Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / loader / ImageLoader.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "core/loader/ImageLoader.h"
24
25 #include "bindings/core/v8/ScriptController.h"
26 #include "core/dom/Document.h"
27 #include "core/dom/Element.h"
28 #include "core/dom/IncrementLoadEventDelayCount.h"
29 #include "core/dom/Microtask.h"
30 #include "core/events/Event.h"
31 #include "core/events/EventSender.h"
32 #include "core/fetch/CrossOriginAccessControl.h"
33 #include "core/fetch/FetchRequest.h"
34 #include "core/fetch/MemoryCache.h"
35 #include "core/fetch/ResourceFetcher.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/html/HTMLImageElement.h"
38 #include "core/html/parser/HTMLParserIdioms.h"
39 #include "core/rendering/RenderImage.h"
40 #include "core/rendering/RenderVideo.h"
41 #include "core/rendering/svg/RenderSVGImage.h"
42 #include "platform/weborigin/SecurityOrigin.h"
43 #include "public/platform/WebURLRequest.h"
44
45 namespace blink {
46
47 static ImageEventSender& loadEventSender()
48 {
49     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::load));
50     return sender;
51 }
52
53 static ImageEventSender& errorEventSender()
54 {
55     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::error));
56     return sender;
57 }
58
59 static inline bool pageIsBeingDismissed(Document* document)
60 {
61     return document->pageDismissalEventBeingDispatched() != Document::NoDismissal;
62 }
63
64 static ImageLoader::BypassMainWorldBehavior shouldBypassMainWorldCSP(ImageLoader* loader)
65 {
66     ASSERT(loader);
67     ASSERT(loader->element());
68     ASSERT(loader->element()->document().frame());
69     if (loader->element()->document().frame()->script().shouldBypassMainWorldCSP())
70         return ImageLoader::BypassMainWorldCSP;
71     return ImageLoader::DoNotBypassMainWorldCSP;
72 }
73
74 class ImageLoader::Task : public blink::WebThread::Task {
75 public:
76     static PassOwnPtr<Task> create(ImageLoader* loader, UpdateFromElementBehavior updateBehavior)
77     {
78         return adoptPtr(new Task(loader, updateBehavior));
79     }
80
81     Task(ImageLoader* loader, UpdateFromElementBehavior updateBehavior)
82         : m_loader(loader)
83         , m_shouldBypassMainWorldCSP(shouldBypassMainWorldCSP(loader))
84         , m_weakFactory(this)
85         , m_updateBehavior(updateBehavior)
86     {
87     }
88
89     virtual void run() OVERRIDE
90     {
91         if (m_loader) {
92             m_loader->doUpdateFromElement(m_shouldBypassMainWorldCSP, m_updateBehavior);
93         }
94     }
95
96     void clearLoader()
97     {
98         m_loader = 0;
99     }
100
101     WeakPtr<Task> createWeakPtr()
102     {
103         return m_weakFactory.createWeakPtr();
104     }
105
106 private:
107     ImageLoader* m_loader;
108     BypassMainWorldBehavior m_shouldBypassMainWorldCSP;
109     WeakPtrFactory<Task> m_weakFactory;
110     UpdateFromElementBehavior m_updateBehavior;
111 };
112
113 ImageLoader::ImageLoader(Element* element)
114     : m_element(element)
115     , m_image(0)
116     , m_derefElementTimer(this, &ImageLoader::timerFired)
117     , m_hasPendingLoadEvent(false)
118     , m_hasPendingErrorEvent(false)
119     , m_imageComplete(true)
120     , m_loadingImageDocument(false)
121     , m_elementIsProtected(false)
122     , m_highPriorityClientCount(0)
123 {
124 }
125
126 ImageLoader::~ImageLoader()
127 {
128     if (m_pendingTask)
129         m_pendingTask->clearLoader();
130
131     if (m_image)
132         m_image->removeClient(this);
133
134     ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this));
135     if (m_hasPendingLoadEvent)
136         loadEventSender().cancelEvent(this);
137
138     ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this));
139     if (m_hasPendingErrorEvent)
140         errorEventSender().cancelEvent(this);
141 }
142
143 void ImageLoader::trace(Visitor* visitor)
144 {
145     visitor->trace(m_element);
146 }
147
148 void ImageLoader::setImage(ImageResource* newImage)
149 {
150     setImageWithoutConsideringPendingLoadEvent(newImage);
151
152     // Only consider updating the protection ref-count of the Element immediately before returning
153     // from this function as doing so might result in the destruction of this ImageLoader.
154     updatedHasPendingEvent();
155 }
156
157 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newImage)
158 {
159     ASSERT(m_failedLoadURL.isEmpty());
160     ImageResource* oldImage = m_image.get();
161     if (newImage != oldImage) {
162         sourceImageChanged();
163         m_image = newImage;
164         if (m_hasPendingLoadEvent) {
165             loadEventSender().cancelEvent(this);
166             m_hasPendingLoadEvent = false;
167         }
168         if (m_hasPendingErrorEvent) {
169             errorEventSender().cancelEvent(this);
170             m_hasPendingErrorEvent = false;
171         }
172         m_imageComplete = true;
173         if (newImage)
174             newImage->addClient(this);
175         if (oldImage)
176             oldImage->removeClient(this);
177     }
178
179     if (RenderImageResource* imageResource = renderImageResource())
180         imageResource->resetAnimation();
181 }
182
183 static void configureRequest(FetchRequest& request, ImageLoader::BypassMainWorldBehavior bypassBehavior, Element& element)
184 {
185     if (bypassBehavior == ImageLoader::BypassMainWorldCSP)
186         request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy);
187
188     AtomicString crossOriginMode = element.fastGetAttribute(HTMLNames::crossoriginAttr);
189     if (!crossOriginMode.isNull())
190         request.setCrossOriginAccessControl(element.document().securityOrigin(), crossOriginMode);
191 }
192
193 ResourcePtr<ImageResource> ImageLoader::createImageResourceForImageDocument(Document& document, FetchRequest& request)
194 {
195     bool autoLoadOtherImages = document.fetcher()->autoLoadImages();
196     document.fetcher()->setAutoLoadImages(false);
197     ResourcePtr<ImageResource> newImage = new ImageResource(request.resourceRequest());
198     newImage->setLoading(true);
199     document.fetcher()->m_documentResources.set(newImage->url(), newImage.get());
200     document.fetcher()->setAutoLoadImages(autoLoadOtherImages);
201     return newImage;
202 }
203
204 inline void ImageLoader::crossSiteOrCSPViolationOccured(AtomicString imageSourceURL)
205 {
206     m_failedLoadURL = imageSourceURL;
207     m_hasPendingErrorEvent = true;
208     errorEventSender().dispatchEventSoon(this);
209 }
210
211 inline void ImageLoader::clearFailedLoadURL()
212 {
213     m_failedLoadURL = AtomicString();
214 }
215
216 inline void ImageLoader::enqueueImageLoadingMicroTask(UpdateFromElementBehavior updateBehavior)
217 {
218     OwnPtr<Task> task = Task::create(this, updateBehavior);
219     m_pendingTask = task->createWeakPtr();
220     Microtask::enqueueMicrotask(task.release());
221     m_loadDelayCounter = IncrementLoadEventDelayCount::create(m_element->document());
222 }
223
224 void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, UpdateFromElementBehavior updateBehavior)
225 {
226     // We don't need to call clearLoader here: Either we were called from the
227     // task, or our caller updateFromElement cleared the task's loader (and set
228     // m_pendingTask to null).
229     m_pendingTask.clear();
230     // Make sure to only decrement the count when we exit this function
231     OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter;
232     loadDelayCounter.swap(m_loadDelayCounter);
233
234     Document& document = m_element->document();
235     if (!document.isActive())
236         return;
237
238     AtomicString imageSourceURL = m_element->imageSourceURL();
239     KURL url = imageSourceToKURL(imageSourceURL);
240     ResourcePtr<ImageResource> newImage = 0;
241     if (!url.isNull()) {
242         // Unlike raw <img>, we block mixed content inside of <picture> or <img srcset>.
243         ResourceLoaderOptions resourceLoaderOptions = ResourceFetcher::defaultResourceOptions();
244         ResourceRequest resourceRequest(url);
245         if (isHTMLPictureElement(element()->parentNode()) || !element()->fastGetAttribute(HTMLNames::srcsetAttr).isNull()) {
246             resourceLoaderOptions.mixedContentBlockingTreatment = TreatAsActiveContent;
247             resourceRequest.setRequestContext(WebURLRequest::RequestContextImageSet);
248         }
249         FetchRequest request(ResourceRequest(url), element()->localName(), resourceLoaderOptions);
250         configureRequest(request, bypassBehavior, *m_element);
251
252         if (m_loadingImageDocument)
253             newImage = createImageResourceForImageDocument(document, request);
254         else
255             newImage = document.fetcher()->fetchImage(request);
256
257         if (!newImage && !pageIsBeingDismissed(&document))
258             crossSiteOrCSPViolationOccured(imageSourceURL);
259         else
260             clearFailedLoadURL();
261     } else if (!imageSourceURL.isNull()) {
262         // Fire an error event if the url string is not empty, but the KURL is.
263         m_hasPendingErrorEvent = true;
264         errorEventSender().dispatchEventSoon(this);
265     }
266
267     ImageResource* oldImage = m_image.get();
268     if (newImage != oldImage) {
269         sourceImageChanged();
270
271         if (m_hasPendingLoadEvent) {
272             loadEventSender().cancelEvent(this);
273             m_hasPendingLoadEvent = false;
274         }
275
276         // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute.
277         // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by
278         // this load and we should not cancel the event.
279         // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two.
280         if (m_hasPendingErrorEvent && newImage) {
281             errorEventSender().cancelEvent(this);
282             m_hasPendingErrorEvent = false;
283         }
284
285         m_image = newImage;
286         m_hasPendingLoadEvent = newImage;
287         m_imageComplete = !newImage;
288
289         updateRenderer();
290         // If newImage exists and is cached, addClient() will result in the load event
291         // being queued to fire. Ensure this happens after beforeload is dispatched.
292         if (newImage)
293             newImage->addClient(this);
294
295         if (oldImage)
296             oldImage->removeClient(this);
297     } else if (updateBehavior == UpdateSizeChanged && m_element->renderer() && m_element->renderer()->isImage()) {
298         toRenderImage(m_element->renderer())->intrinsicSizeChanged();
299     }
300
301     if (RenderImageResource* imageResource = renderImageResource())
302         imageResource->resetAnimation();
303
304     // Only consider updating the protection ref-count of the Element immediately before returning
305     // from this function as doing so might result in the destruction of this ImageLoader.
306     updatedHasPendingEvent();
307 }
308
309 void ImageLoader::updateFromElement(UpdateFromElementBehavior updateBehavior, LoadType loadType)
310 {
311     AtomicString imageSourceURL = m_element->imageSourceURL();
312
313     if (updateBehavior == UpdateIgnorePreviousError)
314         clearFailedLoadURL();
315
316     if (!m_failedLoadURL.isEmpty() && imageSourceURL == m_failedLoadURL)
317         return;
318
319     // If we have a pending task, we have to clear it -- either we're
320     // now loading immediately, or we need to reset the task's state.
321     if (m_pendingTask) {
322         m_pendingTask->clearLoader();
323         m_pendingTask.clear();
324     }
325
326     KURL url = imageSourceToKURL(imageSourceURL);
327     if (imageSourceURL.isNull() || url.isNull() || shouldLoadImmediately(url, loadType)) {
328         doUpdateFromElement(DoNotBypassMainWorldCSP, updateBehavior);
329         return;
330     }
331     enqueueImageLoadingMicroTask(updateBehavior);
332 }
333
334 KURL ImageLoader::imageSourceToKURL(AtomicString imageSourceURL) const
335 {
336     KURL url;
337
338     // Don't load images for inactive documents. We don't want to slow down the
339     // raw HTML parsing case by loading images we don't intend to display.
340     Document& document = m_element->document();
341     if (!document.isActive())
342         return url;
343
344     // Do not load any image if the 'src' attribute is missing or if it is
345     // an empty string.
346     if (!imageSourceURL.isNull() && !stripLeadingAndTrailingHTMLSpaces(imageSourceURL).isEmpty())
347         url = document.completeURL(sourceURI(imageSourceURL));
348     return url;
349 }
350
351 bool ImageLoader::shouldLoadImmediately(const KURL& url, LoadType loadType) const
352 {
353     return (m_loadingImageDocument
354         || isHTMLObjectElement(m_element)
355         || isHTMLEmbedElement(m_element)
356         || url.protocolIsData()
357         || memoryCache()->resourceForURL(url)
358         || loadType == ForceLoadImmediately);
359 }
360
361 void ImageLoader::notifyFinished(Resource* resource)
362 {
363     ASSERT(m_failedLoadURL.isEmpty());
364     ASSERT(resource == m_image.get());
365
366     m_imageComplete = true;
367     updateRenderer();
368
369     if (!m_hasPendingLoadEvent)
370         return;
371
372     if (resource->errorOccurred()) {
373         loadEventSender().cancelEvent(this);
374         m_hasPendingLoadEvent = false;
375
376         m_hasPendingErrorEvent = true;
377         errorEventSender().dispatchEventSoon(this);
378
379         // Only consider updating the protection ref-count of the Element immediately before returning
380         // from this function as doing so might result in the destruction of this ImageLoader.
381         updatedHasPendingEvent();
382         return;
383     }
384     if (resource->wasCanceled()) {
385         m_hasPendingLoadEvent = false;
386         // Only consider updating the protection ref-count of the Element immediately before returning
387         // from this function as doing so might result in the destruction of this ImageLoader.
388         updatedHasPendingEvent();
389         return;
390     }
391     loadEventSender().dispatchEventSoon(this);
392 }
393
394 RenderImageResource* ImageLoader::renderImageResource()
395 {
396     RenderObject* renderer = m_element->renderer();
397
398     if (!renderer)
399         return 0;
400
401     // We don't return style generated image because it doesn't belong to the ImageLoader.
402     // See <https://bugs.webkit.org/show_bug.cgi?id=42840>
403     if (renderer->isImage() && !static_cast<RenderImage*>(renderer)->isGeneratedContent())
404         return toRenderImage(renderer)->imageResource();
405
406     if (renderer->isSVGImage())
407         return toRenderSVGImage(renderer)->imageResource();
408
409     if (renderer->isVideo())
410         return toRenderVideo(renderer)->imageResource();
411
412     return 0;
413 }
414
415 void ImageLoader::updateRenderer()
416 {
417     RenderImageResource* imageResource = renderImageResource();
418
419     if (!imageResource)
420         return;
421
422     // Only update the renderer if it doesn't have an image or if what we have
423     // is a complete image.  This prevents flickering in the case where a dynamic
424     // change is happening between two images.
425     ImageResource* cachedImage = imageResource->cachedImage();
426     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
427         imageResource->setImageResource(m_image.get());
428 }
429
430 void ImageLoader::updatedHasPendingEvent()
431 {
432     // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable.
433     // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being
434     // destroyed by DOM manipulation or garbage collection.
435     // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly.
436     bool wasProtected = m_elementIsProtected;
437     m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
438     if (wasProtected == m_elementIsProtected)
439         return;
440
441     if (m_elementIsProtected) {
442         if (m_derefElementTimer.isActive())
443             m_derefElementTimer.stop();
444         else
445             m_keepAlive = m_element;
446     } else {
447         ASSERT(!m_derefElementTimer.isActive());
448         m_derefElementTimer.startOneShot(0, FROM_HERE);
449     }
450 }
451
452 void ImageLoader::timerFired(Timer<ImageLoader>*)
453 {
454     m_keepAlive.clear();
455 }
456
457 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
458 {
459     ASSERT(eventSender == &loadEventSender() || eventSender == &errorEventSender());
460     const AtomicString& eventType = eventSender->eventType();
461     if (eventType == EventTypeNames::load)
462         dispatchPendingLoadEvent();
463     if (eventType == EventTypeNames::error)
464         dispatchPendingErrorEvent();
465 }
466
467 void ImageLoader::dispatchPendingLoadEvent()
468 {
469     if (!m_hasPendingLoadEvent)
470         return;
471     if (!m_image)
472         return;
473     m_hasPendingLoadEvent = false;
474     if (element()->document().frame())
475         dispatchLoadEvent();
476
477     // Only consider updating the protection ref-count of the Element immediately before returning
478     // from this function as doing so might result in the destruction of this ImageLoader.
479     updatedHasPendingEvent();
480 }
481
482 void ImageLoader::dispatchPendingErrorEvent()
483 {
484     if (!m_hasPendingErrorEvent)
485         return;
486     m_hasPendingErrorEvent = false;
487
488     if (element()->document().frame())
489         element()->dispatchEvent(Event::create(EventTypeNames::error));
490
491     // Only consider updating the protection ref-count of the Element immediately before returning
492     // from this function as doing so might result in the destruction of this ImageLoader.
493     updatedHasPendingEvent();
494 }
495
496 void ImageLoader::addClient(ImageLoaderClient* client)
497 {
498     if (client->requestsHighLiveResourceCachePriority()) {
499         if (m_image && !m_highPriorityClientCount++)
500             memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityHigh);
501     }
502 #if ENABLE(OILPAN)
503     m_clients.add(client, adoptPtr(new ImageLoaderClientRemover(*this, *client)));
504 #else
505     m_clients.add(client);
506 #endif
507 }
508
509 void ImageLoader::willRemoveClient(ImageLoaderClient& client)
510 {
511     if (client.requestsHighLiveResourceCachePriority()) {
512         ASSERT(m_highPriorityClientCount);
513         m_highPriorityClientCount--;
514         if (m_image && !m_highPriorityClientCount)
515             memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityLow);
516     }
517 }
518
519 void ImageLoader::removeClient(ImageLoaderClient* client)
520 {
521     willRemoveClient(*client);
522     m_clients.remove(client);
523 }
524
525 void ImageLoader::dispatchPendingLoadEvents()
526 {
527     loadEventSender().dispatchPendingEvents();
528 }
529
530 void ImageLoader::dispatchPendingErrorEvents()
531 {
532     errorEventSender().dispatchPendingEvents();
533 }
534
535 void ImageLoader::elementDidMoveToNewDocument()
536 {
537     if (m_loadDelayCounter)
538         m_loadDelayCounter->documentChanged(m_element->document());
539     clearFailedLoadURL();
540     setImage(0);
541 }
542
543 void ImageLoader::sourceImageChanged()
544 {
545 #if ENABLE(OILPAN)
546     PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator end = m_clients.end();
547     for (PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator it = m_clients.begin(); it != end; ++it) {
548         it->key->notifyImageSourceChanged();
549     }
550 #else
551     HashSet<ImageLoaderClient*>::iterator end = m_clients.end();
552     for (HashSet<ImageLoaderClient*>::iterator it = m_clients.begin(); it != end; ++it) {
553         ImageLoaderClient* handle = *it;
554         handle->notifyImageSourceChanged();
555     }
556 #endif
557 }
558
559 #if ENABLE(OILPAN)
560 ImageLoader::ImageLoaderClientRemover::~ImageLoaderClientRemover()
561 {
562     m_loader.willRemoveClient(m_client);
563 }
564 #endif
565
566 }