1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim: ft=cpp tw=78 sw=2 et ts=2
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Boris Zbarsky <bzbarsky@mit.edu>.
20 : * Portions created by the Initial Developer are Copyright (C) 2003
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Christian Biesinger <cbiesinger@web.de>
25 : * Bobby Holley <bobbyholley@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * A base class which implements nsIImageLoadingContent and can be
43 : * subclassed by various content nodes that want to provide image
44 : * loading functionality (eg <img>, <object>, etc).
45 : */
46 :
47 : #include "nsImageLoadingContent.h"
48 : #include "nsAutoPtr.h"
49 : #include "nsContentErrors.h"
50 : #include "nsIContent.h"
51 : #include "nsIDocument.h"
52 : #include "nsIScriptGlobalObject.h"
53 : #include "nsIDOMWindow.h"
54 : #include "nsServiceManagerUtils.h"
55 : #include "nsContentPolicyUtils.h"
56 : #include "nsIURI.h"
57 : #include "nsILoadGroup.h"
58 : #include "imgIContainer.h"
59 : #include "imgILoader.h"
60 : #include "nsThreadUtils.h"
61 : #include "nsNetUtil.h"
62 : #include "nsAsyncDOMEvent.h"
63 : #include "nsGenericElement.h"
64 :
65 : #include "nsIPresShell.h"
66 : #include "nsEventStates.h"
67 : #include "nsGUIEvent.h"
68 :
69 : #include "nsIChannel.h"
70 : #include "nsIStreamListener.h"
71 :
72 : #include "nsIFrame.h"
73 : #include "nsIDOMNode.h"
74 :
75 : #include "nsContentUtils.h"
76 : #include "nsLayoutUtils.h"
77 : #include "nsIContentPolicy.h"
78 : #include "nsContentPolicyUtils.h"
79 : #include "nsEventDispatcher.h"
80 : #include "nsSVGEffects.h"
81 :
82 : #include "mozAutoDocUpdate.h"
83 : #include "mozilla/dom/Element.h"
84 :
85 : using namespace mozilla;
86 :
87 : #ifdef DEBUG_chb
88 : static void PrintReqURL(imgIRequest* req) {
89 : if (!req) {
90 : printf("(null req)\n");
91 : return;
92 : }
93 :
94 : nsCOMPtr<nsIURI> uri;
95 : req->GetURI(getter_AddRefs(uri));
96 : if (!uri) {
97 : printf("(null uri)\n");
98 : return;
99 : }
100 :
101 : nsCAutoString spec;
102 : uri->GetSpec(spec);
103 : printf("spec='%s'\n", spec.get());
104 : }
105 : #endif /* DEBUG_chb */
106 :
107 :
108 1 : nsImageLoadingContent::nsImageLoadingContent()
109 : : mObserverList(nsnull),
110 : mImageBlockingStatus(nsIContentPolicy::ACCEPT),
111 : mLoadingEnabled(true),
112 : mIsImageStateForced(false),
113 : mLoading(false),
114 : // mBroken starts out true, since an image without a URI is broken....
115 : mBroken(true),
116 : mUserDisabled(false),
117 : mSuppressed(false),
118 : mBlockingOnload(false),
119 : mNewRequestsWillNeedAnimationReset(false),
120 : mPendingRequestNeedsResetAnimation(false),
121 : mCurrentRequestNeedsResetAnimation(false),
122 : mStateChangerDepth(0),
123 : mCurrentRequestRegistered(false),
124 1 : mPendingRequestRegistered(false)
125 : {
126 1 : if (!nsContentUtils::GetImgLoader()) {
127 0 : mLoadingEnabled = false;
128 : }
129 1 : }
130 :
131 : void
132 1 : nsImageLoadingContent::DestroyImageLoadingContent()
133 : {
134 : // Cancel our requests so they won't hold stale refs to us
135 1 : ClearCurrentRequest(NS_BINDING_ABORTED);
136 1 : ClearPendingRequest(NS_BINDING_ABORTED);
137 1 : }
138 :
139 2 : nsImageLoadingContent::~nsImageLoadingContent()
140 : {
141 1 : NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
142 : "DestroyImageLoadingContent not called");
143 1 : NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
144 : "Observers still registered?");
145 2 : }
146 :
147 : // Macro to call some func on each observer. This handles observers
148 : // removing themselves.
149 : #define LOOP_OVER_OBSERVERS(func_) \
150 : PR_BEGIN_MACRO \
151 : for (ImageObserver* observer = &mObserverList, *next; observer; \
152 : observer = next) { \
153 : next = observer->mNext; \
154 : if (observer->mObserver) { \
155 : observer->mObserver->func_; \
156 : } \
157 : } \
158 : PR_END_MACRO
159 :
160 :
161 : /*
162 : * imgIContainerObserver impl
163 : */
164 : NS_IMETHODIMP
165 0 : nsImageLoadingContent::FrameChanged(imgIRequest* aRequest,
166 : imgIContainer* aContainer,
167 : const nsIntRect* aDirtyRect)
168 : {
169 0 : LOOP_OVER_OBSERVERS(FrameChanged(aRequest, aContainer, aDirtyRect));
170 0 : return NS_OK;
171 : }
172 :
173 : /*
174 : * imgIDecoderObserver impl
175 : */
176 : NS_IMETHODIMP
177 0 : nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest)
178 : {
179 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
180 :
181 0 : LOOP_OVER_OBSERVERS(OnStartRequest(aRequest));
182 0 : return NS_OK;
183 : }
184 :
185 : NS_IMETHODIMP
186 0 : nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
187 : {
188 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
189 :
190 : // Onload blocking. This only applies for the current request.
191 0 : if (aRequest == mCurrentRequest) {
192 :
193 : // Determine whether this is a background request (this can be the case
194 : // with multipart/x-mixed-replace images, for example).
195 : PRUint32 loadFlags;
196 0 : nsresult rv = aRequest->GetLoadFlags(&loadFlags);
197 : bool background =
198 0 : (NS_SUCCEEDED(rv) && (loadFlags & nsIRequest::LOAD_BACKGROUND));
199 :
200 : // Block onload for non-background requests
201 0 : if (!background) {
202 0 : NS_ABORT_IF_FALSE(!mBlockingOnload, "Shouldn't already be blocking");
203 0 : SetBlockingOnload(true);
204 : }
205 : }
206 :
207 0 : LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));
208 0 : return NS_OK;
209 : }
210 :
211 : NS_IMETHODIMP
212 0 : nsImageLoadingContent::OnStartContainer(imgIRequest* aRequest,
213 : imgIContainer* aContainer)
214 : {
215 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
216 :
217 0 : LOOP_OVER_OBSERVERS(OnStartContainer(aRequest, aContainer));
218 :
219 : // Have to check for state changes here, since we might have been in
220 : // the LOADING state before.
221 0 : UpdateImageState(true);
222 0 : return NS_OK;
223 : }
224 :
225 : NS_IMETHODIMP
226 0 : nsImageLoadingContent::OnStartFrame(imgIRequest* aRequest,
227 : PRUint32 aFrame)
228 : {
229 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
230 :
231 0 : LOOP_OVER_OBSERVERS(OnStartFrame(aRequest, aFrame));
232 0 : return NS_OK;
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : nsImageLoadingContent::OnDataAvailable(imgIRequest* aRequest,
237 : bool aCurrentFrame,
238 : const nsIntRect* aRect)
239 : {
240 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
241 :
242 0 : LOOP_OVER_OBSERVERS(OnDataAvailable(aRequest, aCurrentFrame, aRect));
243 0 : return NS_OK;
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : nsImageLoadingContent::OnStopFrame(imgIRequest* aRequest,
248 : PRUint32 aFrame)
249 : {
250 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
251 :
252 : // If we're blocking a load, one frame is enough
253 0 : if (aRequest == mCurrentRequest)
254 0 : SetBlockingOnload(false);
255 :
256 0 : LOOP_OVER_OBSERVERS(OnStopFrame(aRequest, aFrame));
257 0 : return NS_OK;
258 : }
259 :
260 : NS_IMETHODIMP
261 0 : nsImageLoadingContent::OnStopContainer(imgIRequest* aRequest,
262 : imgIContainer* aContainer)
263 : {
264 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
265 :
266 : // This is really hacky. We need to handle the case where we start decoding,
267 : // block onload, but then hit an error before we get to our first frame. In
268 : // theory we would just hook in at OnStopDecode, but OnStopDecode is broken
269 : // until we fix bug 505385. OnStopContainer is actually going away at that
270 : // point. So for now we take advantage of the fact that OnStopContainer is
271 : // always fired in the decoders at the same time as OnStopDecode.
272 0 : if (aRequest == mCurrentRequest)
273 0 : SetBlockingOnload(false);
274 :
275 0 : LOOP_OVER_OBSERVERS(OnStopContainer(aRequest, aContainer));
276 0 : return NS_OK;
277 : }
278 :
279 : // Warning - This isn't actually fired when decode is complete. Rather, it's
280 : // fired when load is complete. See bug 505385, and in the mean time use
281 : // OnStopContainer.
282 : NS_IMETHODIMP
283 0 : nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
284 : nsresult aStatus,
285 : const PRUnichar* aStatusArg)
286 : {
287 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
288 :
289 : // We should definitely have a request here
290 0 : NS_ABORT_IF_FALSE(aRequest, "no request?");
291 :
292 0 : NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
293 : "Unknown request");
294 0 : LOOP_OVER_OBSERVERS(OnStopDecode(aRequest, aStatus, aStatusArg));
295 :
296 : // XXXbholley - When we fix bug 505385, everything here should go in
297 : // OnStopRequest.
298 :
299 : // Our state may change. Watch it.
300 0 : AutoStateChanger changer(this, true);
301 :
302 : // If the pending request is loaded, switch to it.
303 0 : if (aRequest == mPendingRequest) {
304 0 : PrepareCurrentRequest() = mPendingRequest;
305 0 : mPendingRequest = nsnull;
306 0 : mCurrentRequestNeedsResetAnimation = mPendingRequestNeedsResetAnimation;
307 0 : mPendingRequestNeedsResetAnimation = false;
308 : }
309 0 : NS_ABORT_IF_FALSE(aRequest == mCurrentRequest,
310 : "One way or another, we should be current by now");
311 :
312 0 : if (mCurrentRequestNeedsResetAnimation) {
313 0 : nsCOMPtr<imgIContainer> container;
314 0 : mCurrentRequest->GetImage(getter_AddRefs(container));
315 0 : if (container)
316 0 : container->ResetAnimation();
317 0 : mCurrentRequestNeedsResetAnimation = false;
318 : }
319 :
320 : // We just loaded all the data we're going to get. If we haven't done an
321 : // initial paint, we want to make sure the image starts decoding for 2
322 : // reasons:
323 : //
324 : // 1) This image is sitting idle but might need to be decoded as soon as we
325 : // start painting, in which case we've wasted time.
326 : //
327 : // 2) We want to block onload until all visible images are decoded. We do this
328 : // by blocking onload until all in progress decodes get at least one frame
329 : // decoded. However, if all the data comes in while painting is suppressed
330 : // (ie, before the initial paint delay is finished), we fire onload without
331 : // doing a paint first. This means that decode-on-draw images don't start
332 : // decoding, so we can't wait for them to finish. See bug 512435.
333 :
334 : // We can only do this if we have a presshell
335 0 : nsIDocument* doc = GetOurDocument();
336 0 : nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
337 0 : if (shell) {
338 : // We need to figure out whether to kick off decoding
339 0 : bool doRequestDecode = false;
340 :
341 : // If we haven't got the initial reflow yet, IsPaintingSuppressed actually
342 : // returns false
343 0 : if (!shell->DidInitialReflow())
344 0 : doRequestDecode = true;
345 :
346 : // Figure out if painting is suppressed. Note that it's possible for painting
347 : // to be suppressed for reasons other than the initial paint delay (for
348 : // example - being in the bfcache), but we probably aren't loading images in
349 : // those situations.
350 0 : if (shell->IsPaintingSuppressed())
351 0 : doRequestDecode = true;
352 :
353 : // If we're requesting a decode, do it
354 0 : if (doRequestDecode)
355 0 : mCurrentRequest->RequestDecode();
356 : }
357 :
358 : // Fire the appropriate DOM event.
359 0 : if (NS_SUCCEEDED(aStatus)) {
360 0 : FireEvent(NS_LITERAL_STRING("load"));
361 : } else {
362 0 : FireEvent(NS_LITERAL_STRING("error"));
363 : }
364 :
365 0 : nsCOMPtr<nsINode> thisNode = do_QueryInterface(this);
366 0 : nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
367 :
368 0 : return NS_OK;
369 : }
370 :
371 : NS_IMETHODIMP
372 0 : nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, bool aLastPart)
373 : {
374 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
375 :
376 0 : LOOP_OVER_OBSERVERS(OnStopRequest(aRequest, aLastPart));
377 :
378 0 : return NS_OK;
379 : }
380 :
381 : NS_IMETHODIMP
382 0 : nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
383 : {
384 0 : bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
385 0 : if (requestFlag) {
386 : nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
387 0 : aRequest, requestFlag);
388 : }
389 :
390 0 : return NS_OK;
391 : }
392 :
393 : NS_IMETHODIMP
394 0 : nsImageLoadingContent::OnDiscard(imgIRequest *aRequest)
395 : {
396 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
397 :
398 0 : LOOP_OVER_OBSERVERS(OnDiscard(aRequest));
399 :
400 0 : return NS_OK;
401 : }
402 :
403 : /*
404 : * nsIImageLoadingContent impl
405 : */
406 :
407 : NS_IMETHODIMP
408 0 : nsImageLoadingContent::GetLoadingEnabled(bool *aLoadingEnabled)
409 : {
410 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
411 :
412 0 : *aLoadingEnabled = mLoadingEnabled;
413 0 : return NS_OK;
414 : }
415 :
416 : NS_IMETHODIMP
417 0 : nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled)
418 : {
419 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
420 :
421 0 : if (nsContentUtils::GetImgLoader()) {
422 0 : mLoadingEnabled = aLoadingEnabled;
423 : }
424 0 : return NS_OK;
425 : }
426 :
427 : NS_IMETHODIMP
428 0 : nsImageLoadingContent::GetImageBlockingStatus(PRInt16* aStatus)
429 : {
430 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
431 :
432 0 : NS_PRECONDITION(aStatus, "Null out param");
433 0 : *aStatus = mImageBlockingStatus;
434 0 : return NS_OK;
435 : }
436 :
437 : NS_IMETHODIMP
438 0 : nsImageLoadingContent::AddObserver(imgIDecoderObserver* aObserver)
439 : {
440 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
441 :
442 0 : NS_ENSURE_ARG_POINTER(aObserver);
443 :
444 0 : if (!mObserverList.mObserver) {
445 0 : mObserverList.mObserver = aObserver;
446 : // Don't touch the linking of the list!
447 0 : return NS_OK;
448 : }
449 :
450 : // otherwise we have to create a new entry
451 :
452 0 : ImageObserver* observer = &mObserverList;
453 0 : while (observer->mNext) {
454 0 : observer = observer->mNext;
455 : }
456 :
457 0 : observer->mNext = new ImageObserver(aObserver);
458 0 : if (! observer->mNext) {
459 0 : return NS_ERROR_OUT_OF_MEMORY;
460 : }
461 :
462 0 : return NS_OK;
463 : }
464 :
465 : NS_IMETHODIMP
466 0 : nsImageLoadingContent::RemoveObserver(imgIDecoderObserver* aObserver)
467 : {
468 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
469 :
470 0 : NS_ENSURE_ARG_POINTER(aObserver);
471 :
472 0 : if (mObserverList.mObserver == aObserver) {
473 0 : mObserverList.mObserver = nsnull;
474 : // Don't touch the linking of the list!
475 0 : return NS_OK;
476 : }
477 :
478 : // otherwise have to find it and splice it out
479 0 : ImageObserver* observer = &mObserverList;
480 0 : while (observer->mNext && observer->mNext->mObserver != aObserver) {
481 0 : observer = observer->mNext;
482 : }
483 :
484 : // At this point, we are pointing to the list element whose mNext is
485 : // the right observer (assuming of course that mNext is not null)
486 0 : if (observer->mNext) {
487 : // splice it out
488 0 : ImageObserver* oldObserver = observer->mNext;
489 0 : observer->mNext = oldObserver->mNext;
490 0 : oldObserver->mNext = nsnull; // so we don't destroy them all
491 0 : delete oldObserver;
492 : }
493 : #ifdef DEBUG
494 : else {
495 0 : NS_WARNING("Asked to remove nonexistent observer");
496 : }
497 : #endif
498 0 : return NS_OK;
499 : }
500 :
501 : NS_IMETHODIMP
502 0 : nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
503 : imgIRequest** aRequest)
504 : {
505 0 : switch(aRequestType) {
506 : case CURRENT_REQUEST:
507 0 : *aRequest = mCurrentRequest;
508 0 : break;
509 : case PENDING_REQUEST:
510 0 : *aRequest = mPendingRequest;
511 0 : break;
512 : default:
513 0 : NS_ERROR("Unknown request type");
514 0 : *aRequest = nsnull;
515 0 : return NS_ERROR_UNEXPECTED;
516 : }
517 :
518 0 : NS_IF_ADDREF(*aRequest);
519 0 : return NS_OK;
520 : }
521 :
522 : NS_IMETHODIMP_(void)
523 0 : nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
524 : {
525 0 : NS_ASSERTION(aFrame, "aFrame is null");
526 :
527 : // We need to make sure that our image request is registered, if it should
528 : // be registered.
529 0 : nsPresContext* presContext = aFrame->PresContext();
530 :
531 0 : if (mCurrentRequest) {
532 : nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
533 0 : &mCurrentRequestRegistered);
534 : }
535 :
536 0 : if (mPendingRequest) {
537 : nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest,
538 0 : &mPendingRequestRegistered);
539 : }
540 0 : }
541 :
542 : NS_IMETHODIMP_(void)
543 0 : nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
544 : {
545 0 : NS_ASSERTION(aFrame, "aFrame is null");
546 :
547 : // We need to make sure that our image request is deregistered.
548 0 : if (mCurrentRequest) {
549 : nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
550 : mCurrentRequest,
551 0 : &mCurrentRequestRegistered);
552 : }
553 :
554 0 : if (mPendingRequest) {
555 : nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
556 : mPendingRequest,
557 0 : &mPendingRequestRegistered);
558 : }
559 0 : }
560 :
561 : NS_IMETHODIMP
562 0 : nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
563 : PRInt32* aRequestType)
564 : {
565 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
566 :
567 0 : NS_PRECONDITION(aRequestType, "Null out param");
568 :
569 0 : if (aRequest == mCurrentRequest) {
570 0 : *aRequestType = CURRENT_REQUEST;
571 0 : return NS_OK;
572 : }
573 :
574 0 : if (aRequest == mPendingRequest) {
575 0 : *aRequestType = PENDING_REQUEST;
576 0 : return NS_OK;
577 : }
578 :
579 0 : *aRequestType = UNKNOWN_REQUEST;
580 0 : NS_ERROR("Unknown request");
581 0 : return NS_ERROR_UNEXPECTED;
582 : }
583 :
584 : NS_IMETHODIMP
585 0 : nsImageLoadingContent::GetCurrentURI(nsIURI** aURI)
586 : {
587 0 : if (mCurrentRequest) {
588 0 : return mCurrentRequest->GetURI(aURI);
589 : }
590 :
591 0 : if (!mCurrentURI) {
592 0 : *aURI = nsnull;
593 0 : return NS_OK;
594 : }
595 :
596 0 : return NS_EnsureSafeToReturn(mCurrentURI, aURI);
597 : }
598 :
599 : NS_IMETHODIMP
600 0 : nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
601 : nsIStreamListener** aListener)
602 : {
603 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
604 :
605 0 : if (!nsContentUtils::GetImgLoader()) {
606 0 : return NS_ERROR_NULL_POINTER;
607 : }
608 :
609 0 : nsCOMPtr<nsIDocument> doc = GetOurDocument();
610 0 : if (!doc) {
611 : // Don't bother
612 0 : return NS_OK;
613 : }
614 :
615 : // XXX what should we do with content policies here, if anything?
616 : // Shouldn't that be done before the start of the load?
617 : // XXX what about shouldProcess?
618 :
619 : // Our state might change. Watch it.
620 0 : AutoStateChanger changer(this, true);
621 :
622 : // Do the load.
623 0 : nsCOMPtr<imgIRequest>& req = PrepareNextRequest();
624 0 : nsresult rv = nsContentUtils::GetImgLoader()->
625 : LoadImageWithChannel(aChannel, this, doc, aListener,
626 0 : getter_AddRefs(req));
627 0 : if (NS_SUCCEEDED(rv)) {
628 0 : TrackImage(req);
629 : } else {
630 : // If we don't have a current URI, we might as well store this URI so people
631 : // know what we tried (and failed) to load.
632 0 : if (!mCurrentRequest)
633 0 : aChannel->GetURI(getter_AddRefs(mCurrentURI));
634 0 : FireEvent(NS_LITERAL_STRING("error"));
635 0 : return rv;
636 : }
637 0 : return NS_OK;;
638 : }
639 :
640 0 : NS_IMETHODIMP nsImageLoadingContent::ForceReload()
641 : {
642 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
643 :
644 0 : nsCOMPtr<nsIURI> currentURI;
645 0 : GetCurrentURI(getter_AddRefs(currentURI));
646 0 : if (!currentURI) {
647 0 : return NS_ERROR_NOT_AVAILABLE;
648 : }
649 :
650 0 : return LoadImage(currentURI, true, true, nsnull, nsIRequest::VALIDATE_ALWAYS);
651 : }
652 :
653 : /*
654 : * Non-interface methods
655 : */
656 :
657 : void
658 0 : nsImageLoadingContent::NotifyOwnerDocumentChanged(nsIDocument *aOldDoc)
659 : {
660 : // If we had a document before, unregister ourselves with it.
661 0 : if (aOldDoc) {
662 0 : if (mCurrentRequest)
663 0 : aOldDoc->RemoveImage(mCurrentRequest);
664 0 : if (mPendingRequest)
665 0 : aOldDoc->RemoveImage(mPendingRequest);
666 : }
667 :
668 : // Re-track the images
669 0 : TrackImage(mCurrentRequest);
670 0 : TrackImage(mPendingRequest);
671 0 : }
672 :
673 : nsresult
674 0 : nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
675 : bool aForce,
676 : bool aNotify)
677 : {
678 : // First, get a document (needed for security checks and the like)
679 0 : nsIDocument* doc = GetOurDocument();
680 0 : if (!doc) {
681 : // No reason to bother, I think...
682 0 : return NS_OK;
683 : }
684 :
685 0 : nsCOMPtr<nsIURI> imageURI;
686 0 : nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
687 0 : NS_ENSURE_SUCCESS(rv, rv);
688 : // XXXbiesi fire onerror if that failed?
689 :
690 : bool equal;
691 :
692 0 : if (aNewURI.IsEmpty() &&
693 0 : doc->GetDocumentURI() &&
694 0 : NS_SUCCEEDED(doc->GetDocumentURI()->Equals(imageURI, &equal)) &&
695 : equal) {
696 :
697 : // Loading an embedded img from the same URI as the document URI will not work
698 : // as a resource cannot recursively embed itself. Attempting to do so generally
699 : // results in having to pre-emptively close down an in-flight HTTP transaction
700 : // and then incurring the significant cost of establishing a new TCP channel.
701 : // This is generally triggered from <img src="">
702 : // In light of that, just skip loading it..
703 : // Do make sure to drop our existing image, if any
704 0 : CancelImageRequests(aNotify);
705 0 : return NS_OK;
706 : }
707 :
708 0 : NS_TryToSetImmutable(imageURI);
709 :
710 0 : return LoadImage(imageURI, aForce, aNotify, doc);
711 : }
712 :
713 : nsresult
714 0 : nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
715 : bool aForce,
716 : bool aNotify,
717 : nsIDocument* aDocument,
718 : nsLoadFlags aLoadFlags)
719 : {
720 0 : if (!mLoadingEnabled) {
721 : // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
722 : // don't want/need it.
723 0 : FireEvent(NS_LITERAL_STRING("error"));
724 0 : return NS_OK;
725 : }
726 :
727 0 : NS_ASSERTION(!aDocument || aDocument == GetOurDocument(),
728 : "Bogus document passed in");
729 : // First, get a document (needed for security checks and the like)
730 0 : if (!aDocument) {
731 0 : aDocument = GetOurDocument();
732 0 : if (!aDocument) {
733 : // No reason to bother, I think...
734 0 : return NS_OK;
735 : }
736 : }
737 :
738 : // URI equality check.
739 : //
740 : // We skip the equality check if our current image was blocked, since in that
741 : // case we really do want to try loading again.
742 0 : if (!aForce && NS_CP_ACCEPTED(mImageBlockingStatus)) {
743 0 : nsCOMPtr<nsIURI> currentURI;
744 0 : GetCurrentURI(getter_AddRefs(currentURI));
745 : bool equal;
746 0 : if (currentURI &&
747 0 : NS_SUCCEEDED(currentURI->Equals(aNewURI, &equal)) &&
748 : equal) {
749 : // Nothing to do here.
750 0 : return NS_OK;
751 : }
752 : }
753 :
754 : // From this point on, our image state could change. Watch it.
755 0 : AutoStateChanger changer(this, aNotify);
756 :
757 : // Sanity check.
758 : //
759 : // We use the principal of aDocument to avoid having to QI |this| an extra
760 : // time. It should always be the same as the principal of this node.
761 : #ifdef DEBUG
762 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
763 0 : NS_ABORT_IF_FALSE(thisContent &&
764 : thisContent->NodePrincipal() == aDocument->NodePrincipal(),
765 : "Principal mismatch?");
766 : #endif
767 :
768 : // Are we blocked?
769 0 : PRInt16 cpDecision = nsIContentPolicy::REJECT_REQUEST;
770 : nsContentUtils::CanLoadImage(aNewURI, this, aDocument,
771 0 : aDocument->NodePrincipal(), &cpDecision);
772 0 : if (!NS_CP_ACCEPTED(cpDecision)) {
773 0 : FireEvent(NS_LITERAL_STRING("error"));
774 0 : SetBlockedRequest(aNewURI, cpDecision);
775 0 : return NS_OK;
776 : }
777 :
778 0 : nsLoadFlags loadFlags = aLoadFlags;
779 0 : PRInt32 corsmode = GetCORSMode();
780 0 : if (corsmode == CORS_ANONYMOUS) {
781 0 : loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
782 0 : } else if (corsmode == CORS_USE_CREDENTIALS) {
783 0 : loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
784 : }
785 :
786 : // Not blocked. Do the load.
787 0 : nsCOMPtr<imgIRequest>& req = PrepareNextRequest();
788 : nsresult rv;
789 : rv = nsContentUtils::LoadImage(aNewURI, aDocument,
790 : aDocument->NodePrincipal(),
791 : aDocument->GetDocumentURI(),
792 : this, loadFlags,
793 0 : getter_AddRefs(req));
794 0 : if (NS_SUCCEEDED(rv)) {
795 0 : TrackImage(req);
796 : } else {
797 : // If we don't have a current URI, we might as well store this URI so people
798 : // know what we tried (and failed) to load.
799 0 : if (!mCurrentRequest)
800 0 : mCurrentURI = aNewURI;
801 0 : FireEvent(NS_LITERAL_STRING("error"));
802 0 : return NS_OK;
803 : }
804 :
805 0 : return NS_OK;
806 : }
807 :
808 : nsresult
809 0 : nsImageLoadingContent::ForceImageState(bool aForce, nsEventStates::InternalType aState)
810 : {
811 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
812 :
813 0 : mIsImageStateForced = aForce;
814 0 : mForcedImageState = nsEventStates(aState);
815 0 : return NS_OK;
816 : }
817 :
818 : nsEventStates
819 0 : nsImageLoadingContent::ImageState() const
820 : {
821 0 : if (mIsImageStateForced) {
822 0 : return mForcedImageState;
823 : }
824 :
825 0 : nsEventStates states;
826 :
827 0 : if (mBroken) {
828 0 : states |= NS_EVENT_STATE_BROKEN;
829 : }
830 0 : if (mUserDisabled) {
831 0 : states |= NS_EVENT_STATE_USERDISABLED;
832 : }
833 0 : if (mSuppressed) {
834 0 : states |= NS_EVENT_STATE_SUPPRESSED;
835 : }
836 0 : if (mLoading) {
837 0 : states |= NS_EVENT_STATE_LOADING;
838 : }
839 :
840 0 : return states;
841 : }
842 :
843 : void
844 0 : nsImageLoadingContent::UpdateImageState(bool aNotify)
845 : {
846 0 : if (mStateChangerDepth > 0) {
847 : // Ignore this call; we'll update our state when the outermost state
848 : // changer is destroyed. Need this to work around the fact that some libpr0n
849 : // stuff is actually sync and hence we can get OnStopDecode called while
850 : // we're still under LoadImage, and OnStopDecode doesn't know anything about
851 : // aNotify.
852 : // XXX - This machinery should be removed after bug 521604.
853 0 : return;
854 : }
855 :
856 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
857 0 : if (!thisContent) {
858 : return;
859 : }
860 :
861 0 : mLoading = mBroken = mUserDisabled = mSuppressed = false;
862 :
863 : // If we were blocked by server-based content policy, we claim to be
864 : // suppressed. If we were blocked by type-based content policy, we claim to
865 : // be user-disabled. Otherwise, claim to be broken.
866 0 : if (mImageBlockingStatus == nsIContentPolicy::REJECT_SERVER) {
867 0 : mSuppressed = true;
868 0 : } else if (mImageBlockingStatus == nsIContentPolicy::REJECT_TYPE) {
869 0 : mUserDisabled = true;
870 0 : } else if (!mCurrentRequest) {
871 : // No current request means error, since we weren't disabled or suppressed
872 0 : mBroken = true;
873 : } else {
874 : PRUint32 currentLoadStatus;
875 0 : nsresult rv = mCurrentRequest->GetImageStatus(¤tLoadStatus);
876 0 : if (NS_FAILED(rv) || (currentLoadStatus & imgIRequest::STATUS_ERROR)) {
877 0 : mBroken = true;
878 0 : } else if (!(currentLoadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
879 0 : mLoading = true;
880 : }
881 : }
882 :
883 0 : NS_ASSERTION(thisContent->IsElement(), "Not an element?");
884 0 : thisContent->AsElement()->UpdateState(aNotify);
885 : }
886 :
887 : void
888 0 : nsImageLoadingContent::CancelImageRequests(bool aNotify)
889 : {
890 0 : AutoStateChanger changer(this, aNotify);
891 0 : ClearPendingRequest(NS_BINDING_ABORTED);
892 0 : ClearCurrentRequest(NS_BINDING_ABORTED);
893 0 : }
894 :
895 : nsresult
896 0 : nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest,
897 : bool aNotify)
898 : {
899 : // Our state will change. Watch it.
900 0 : AutoStateChanger changer(this, aNotify);
901 :
902 : // Get rid if our existing images
903 0 : ClearPendingRequest(NS_BINDING_ABORTED);
904 0 : ClearCurrentRequest(NS_BINDING_ABORTED);
905 :
906 : // Clone the request we were given.
907 0 : nsCOMPtr<imgIRequest>& req = PrepareNextRequest();;
908 0 : nsresult rv = aRequest->Clone(this, getter_AddRefs(req));
909 0 : if (NS_SUCCEEDED(rv))
910 0 : TrackImage(req);
911 : else
912 0 : return rv;
913 :
914 0 : return NS_OK;
915 : }
916 :
917 : nsIDocument*
918 0 : nsImageLoadingContent::GetOurDocument()
919 : {
920 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
921 0 : NS_ENSURE_TRUE(thisContent, nsnull);
922 :
923 0 : return thisContent->OwnerDoc();
924 : }
925 :
926 : nsIFrame*
927 0 : nsImageLoadingContent::GetOurPrimaryFrame()
928 : {
929 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
930 0 : return thisContent->GetPrimaryFrame();
931 : }
932 :
933 0 : nsPresContext* nsImageLoadingContent::GetFramePresContext()
934 : {
935 0 : nsIFrame* frame = GetOurPrimaryFrame();
936 0 : if (!frame) {
937 0 : return nsnull;
938 : }
939 :
940 0 : return frame->PresContext();
941 : }
942 :
943 : nsresult
944 0 : nsImageLoadingContent::StringToURI(const nsAString& aSpec,
945 : nsIDocument* aDocument,
946 : nsIURI** aURI)
947 : {
948 0 : NS_PRECONDITION(aDocument, "Must have a document");
949 0 : NS_PRECONDITION(aURI, "Null out param");
950 :
951 : // (1) Get the base URI
952 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
953 0 : NS_ASSERTION(thisContent, "An image loading content must be an nsIContent");
954 0 : nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI();
955 :
956 : // (2) Get the charset
957 0 : const nsAFlatCString &charset = aDocument->GetDocumentCharacterSet();
958 :
959 : // (3) Construct the silly thing
960 : return NS_NewURI(aURI,
961 : aSpec,
962 0 : charset.IsEmpty() ? nsnull : charset.get(),
963 : baseURL,
964 0 : nsContentUtils::GetIOService());
965 : }
966 :
967 : nsresult
968 0 : nsImageLoadingContent::FireEvent(const nsAString& aEventType)
969 : {
970 : // We have to fire the event asynchronously so that we won't go into infinite
971 : // loops in cases when onLoad handlers reset the src and the new src is in
972 : // cache.
973 :
974 0 : nsCOMPtr<nsINode> thisNode = do_QueryInterface(this);
975 :
976 : nsRefPtr<nsAsyncDOMEvent> event =
977 0 : new nsLoadBlockingAsyncDOMEvent(thisNode, aEventType, false, false);
978 0 : event->PostDOMEvent();
979 :
980 0 : return NS_OK;
981 : }
982 :
983 : nsCOMPtr<imgIRequest>&
984 0 : nsImageLoadingContent::PrepareNextRequest()
985 : {
986 : // If we don't have a usable current request, get rid of any half-baked
987 : // request that might be sitting there and make this one current.
988 0 : if (!HaveSize(mCurrentRequest))
989 0 : return PrepareCurrentRequest();
990 :
991 : // Otherwise, make it pending.
992 0 : return PreparePendingRequest();
993 : }
994 :
995 : void
996 0 : nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, PRInt16 aContentDecision)
997 : {
998 : // Sanity
999 0 : NS_ABORT_IF_FALSE(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?");
1000 :
1001 : // We do some slightly illogical stuff here to maintain consistency with
1002 : // old behavior that people probably depend on. Even in the case where the
1003 : // new image is blocked, the old one should really be canceled with the
1004 : // reason "image source changed". However, apparently there's some abuse
1005 : // over in nsImageFrame where the displaying of the "broken" icon for the
1006 : // next image depends on the cancel reason of the previous image. ugh.
1007 0 : ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED);
1008 :
1009 : // For the blocked case, we only want to cancel the existing current request
1010 : // if size is not available. bz says the web depends on this behavior.
1011 0 : if (!HaveSize(mCurrentRequest)) {
1012 :
1013 0 : mImageBlockingStatus = aContentDecision;
1014 0 : ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED);
1015 :
1016 : // We still want to remember what URI we were despite not having an actual
1017 : // request.
1018 0 : mCurrentURI = aURI;
1019 : }
1020 0 : }
1021 :
1022 : nsCOMPtr<imgIRequest>&
1023 0 : nsImageLoadingContent::PrepareCurrentRequest()
1024 : {
1025 : // Blocked images go through SetBlockedRequest, which is a separate path. For
1026 : // everything else, we're unblocked.
1027 0 : mImageBlockingStatus = nsIContentPolicy::ACCEPT;
1028 :
1029 : // Get rid of anything that was there previously.
1030 0 : ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED);
1031 :
1032 0 : mCurrentRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset;
1033 :
1034 : // Return a reference.
1035 0 : return mCurrentRequest;
1036 : }
1037 :
1038 : nsCOMPtr<imgIRequest>&
1039 0 : nsImageLoadingContent::PreparePendingRequest()
1040 : {
1041 : // Get rid of anything that was there previously.
1042 0 : ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED);
1043 :
1044 0 : mPendingRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset;
1045 :
1046 : // Return a reference.
1047 0 : return mPendingRequest;
1048 : }
1049 :
1050 : void
1051 1 : nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
1052 : {
1053 1 : if (!mCurrentRequest) {
1054 : // Even if we didn't have a current request, we might have been keeping
1055 : // a URI as a placeholder for a failed load. Clear that now.
1056 1 : mCurrentURI = nsnull;
1057 1 : return;
1058 : }
1059 0 : NS_ABORT_IF_FALSE(!mCurrentURI,
1060 : "Shouldn't have both mCurrentRequest and mCurrentURI!");
1061 :
1062 : // Deregister this image from the refresh driver so it no longer receives
1063 : // notifications.
1064 : nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
1065 0 : &mCurrentRequestRegistered);
1066 :
1067 : // Clean up the request.
1068 0 : UntrackImage(mCurrentRequest);
1069 0 : mCurrentRequest->CancelAndForgetObserver(aReason);
1070 0 : mCurrentRequest = nsnull;
1071 0 : mCurrentRequestNeedsResetAnimation = false;
1072 :
1073 : // We only block onload during the decoding of "current" images. This one is
1074 : // going away, so we should unblock unconditionally here.
1075 0 : SetBlockingOnload(false);
1076 : }
1077 :
1078 : void
1079 1 : nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
1080 : {
1081 1 : if (!mPendingRequest)
1082 1 : return;
1083 :
1084 : // Push a null JSContext on the stack so that code that runs within
1085 : // the below code doesn't think it's being called by JS. See bug
1086 : // 604262.
1087 0 : nsCxPusher pusher;
1088 0 : pusher.PushNull();
1089 :
1090 : // Deregister this image from the refresh driver so it no longer receives
1091 : // notifications.
1092 : nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
1093 0 : &mPendingRequestRegistered);
1094 :
1095 0 : UntrackImage(mPendingRequest);
1096 0 : mPendingRequest->CancelAndForgetObserver(aReason);
1097 0 : mPendingRequest = nsnull;
1098 0 : mPendingRequestNeedsResetAnimation = false;
1099 : }
1100 :
1101 : bool*
1102 0 : nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
1103 : {
1104 0 : if (aRequest == mCurrentRequest) {
1105 0 : return &mCurrentRequestRegistered;
1106 0 : } else if (aRequest == mPendingRequest) {
1107 0 : return &mPendingRequestRegistered;
1108 : } else {
1109 0 : return nsnull;
1110 : }
1111 : }
1112 :
1113 : bool
1114 0 : nsImageLoadingContent::HaveSize(imgIRequest *aImage)
1115 : {
1116 : // Handle the null case
1117 0 : if (!aImage)
1118 0 : return false;
1119 :
1120 : // Query the image
1121 : PRUint32 status;
1122 0 : nsresult rv = aImage->GetImageStatus(&status);
1123 0 : return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE));
1124 : }
1125 :
1126 : void
1127 0 : nsImageLoadingContent::SetBlockingOnload(bool aBlocking)
1128 : {
1129 : // If we're already in the desired state, we have nothing to do
1130 0 : if (mBlockingOnload == aBlocking)
1131 0 : return;
1132 :
1133 : // Get the document
1134 0 : nsIDocument* doc = GetOurDocument();
1135 :
1136 0 : if (doc) {
1137 : // Take the appropriate action
1138 0 : if (aBlocking)
1139 0 : doc->BlockOnload();
1140 : else
1141 0 : doc->UnblockOnload(false);
1142 :
1143 : // Update our state
1144 0 : mBlockingOnload = aBlocking;
1145 : }
1146 : }
1147 :
1148 : nsresult
1149 0 : nsImageLoadingContent::TrackImage(imgIRequest* aImage)
1150 : {
1151 0 : if (!aImage)
1152 0 : return NS_OK;
1153 :
1154 0 : nsIDocument* doc = GetOurDocument();
1155 0 : if (doc)
1156 0 : return doc->AddImage(aImage);
1157 0 : return NS_OK;
1158 : }
1159 :
1160 : nsresult
1161 0 : nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
1162 : {
1163 0 : if (!aImage)
1164 0 : return NS_OK;
1165 :
1166 : // If GetOurDocument() returns null here, we've outlived our document.
1167 : // That's fine, because the document empties out the tracker and unlocks
1168 : // all locked images on destruction.
1169 0 : nsIDocument* doc = GetOurDocument();
1170 0 : if (doc)
1171 0 : return doc->RemoveImage(aImage);
1172 0 : return NS_OK;
1173 : }
1174 :
1175 :
1176 : void
1177 0 : nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const
1178 : {
1179 0 : aDest->mCurrentRequest = nsContentUtils::GetStaticRequest(mCurrentRequest);
1180 0 : aDest->TrackImage(aDest->mCurrentRequest);
1181 0 : aDest->mForcedImageState = mForcedImageState;
1182 0 : aDest->mImageBlockingStatus = mImageBlockingStatus;
1183 0 : aDest->mLoadingEnabled = mLoadingEnabled;
1184 0 : aDest->mStateChangerDepth = mStateChangerDepth;
1185 0 : aDest->mIsImageStateForced = mIsImageStateForced;
1186 0 : aDest->mLoading = mLoading;
1187 0 : aDest->mBroken = mBroken;
1188 0 : aDest->mUserDisabled = mUserDisabled;
1189 0 : aDest->mSuppressed = mSuppressed;
1190 0 : }
1191 :
1192 : CORSMode
1193 0 : nsImageLoadingContent::GetCORSMode()
1194 : {
1195 0 : return CORS_NONE;
1196 : }
|