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