Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / loader / ImageLoader.cpp
index 57914ef..86d64ea 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -22,6 +22,7 @@
 #include "config.h"
 #include "ImageLoader.h"
 
+#include "BitmapImage.h"
 #include "CachedImage.h"
 #include "CachedResourceLoader.h"
 #include "CachedResourceRequest.h"
 #include "EventNames.h"
 #include "EventSender.h"
 #include "Frame.h"
+#include "FrameLoader.h"
 #include "HTMLNames.h"
 #include "HTMLObjectElement.h"
 #include "HTMLParserIdioms.h"
+#include "InspectorInstrumentation.h"
 #include "Page.h"
 #include "RenderImage.h"
 #include "RenderSVGImage.h"
@@ -162,7 +165,7 @@ void ImageLoader::updateFromElement()
     if (!document.hasLivingRenderTree())
         return;
 
-    AtomicString attr = element().imageSourceURL();
+    AtomString attr = element().imageSourceURL();
 
     // Avoid loading a URL we already failed to load.
     if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL)
@@ -176,22 +179,25 @@ void ImageLoader::updateFromElement()
         options.contentSecurityPolicyImposition = element().isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
         options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
 
-        CachedResourceRequest request(ResourceRequest(document.completeURL(sourceURI(attr))), options);
-        request.setInitiator(element());
+        auto crossOriginAttribute = element().attributeWithoutSynchronization(HTMLNames::crossoriginAttr);
+
+        ResourceRequest resourceRequest(document.completeURL(sourceURI(attr)));
+        resourceRequest.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(m_element));
 
-        request.setAsPotentiallyCrossOrigin(element().attributeWithoutSynchronization(HTMLNames::crossoriginAttr), document);
+        auto request = createPotentialAccessControlRequest(WTFMove(resourceRequest), document, crossOriginAttribute, WTFMove(options));
+        request.setInitiator(element());
 
         if (m_loadManually) {
             bool autoLoadOtherImages = document.cachedResourceLoader().autoLoadImages();
             document.cachedResourceLoader().setAutoLoadImages(false);
-            newImage = new CachedImage(WTFMove(request), m_element.document().page()->sessionID());
+            auto* page = m_element.document().page();
+            newImage = new CachedImage(WTFMove(request), page->sessionID(), &page->cookieJar());
             newImage->setStatus(CachedResource::Pending);
             newImage->setLoading(true);
-            newImage->setOwningCachedResourceLoader(&document.cachedResourceLoader());
             document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get());
             document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages);
         } else
-            newImage = document.cachedResourceLoader().requestImage(WTFMove(request));
+            newImage = document.cachedResourceLoader().requestImage(WTFMove(request)).value_or(nullptr);
 
         // If we do not have an image here, it means that a cross-site
         // violation occurred, or that the image was blocked via Content
@@ -282,14 +288,19 @@ void ImageLoader::notifyFinished(CachedResource& resource)
         return;
 
     if (m_image->resourceError().isAccessControl()) {
+        URL imageURL = m_image->url();
+
         clearImageWithoutConsideringPendingLoadEvent();
 
         m_hasPendingErrorEvent = true;
         errorEventSender().dispatchEventSoon(*this);
 
-        static NeverDestroyed<String> consoleMessage(ASCIILiteral("Cross-origin image load denied by Cross-Origin Resource Sharing policy."));
-        element().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
+        auto message = makeString("Cannot load image ", imageURL.string(), " due to access control checks.");
+        element().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
 
+        if (hasPendingDecodePromises())
+            decodeError("Access control error.");
+        
         ASSERT(!m_hasPendingLoadEvent);
 
         // Only consider updating the protection ref-count of the Element immediately before returning
@@ -299,6 +310,8 @@ void ImageLoader::notifyFinished(CachedResource& resource)
     }
 
     if (m_image->wasCanceled()) {
+        if (hasPendingDecodePromises())
+            decodeError("Loading was canceled.");
         m_hasPendingLoadEvent = false;
         // Only consider updating the protection ref-count of the Element immediately before returning
         // from this function as doing so might result in the destruction of this ImageLoader.
@@ -306,6 +319,8 @@ void ImageLoader::notifyFinished(CachedResource& resource)
         return;
     }
 
+    if (hasPendingDecodePromises())
+        decode();
     loadEventSender().dispatchEventSoon(*this);
 }
 
@@ -364,10 +379,64 @@ void ImageLoader::updatedHasPendingEvent()
             m_protectedElement = &element();
     } else {
         ASSERT(!m_derefElementTimer.isActive());
-        m_derefElementTimer.startOneShot(0);
+        m_derefElementTimer.startOneShot(0_s);
     }   
 }
 
+void ImageLoader::decode(Ref<DeferredPromise>&& promise)
+{
+    m_decodingPromises.append(WTFMove(promise));
+    
+    if (!element().document().domWindow()) {
+        decodeError("Inactive document.");
+        return;
+    }
+    
+    AtomString attr = element().imageSourceURL();
+    if (stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
+        decodeError("Missing source URL.");
+        return;
+    }
+    
+    if (m_imageComplete)
+        decode();
+}
+
+void ImageLoader::decodeError(const char* message)
+{
+    ASSERT(hasPendingDecodePromises());
+    for (auto& promise : m_decodingPromises)
+        promise->reject(Exception { EncodingError, message });
+    m_decodingPromises.clear();
+}
+
+void ImageLoader::decode()
+{
+    ASSERT(hasPendingDecodePromises());
+    
+    if (!element().document().domWindow()) {
+        decodeError("Inactive document.");
+        return;
+    }
+
+    if (!m_image || !m_image->image() || m_image->errorOccurred()) {
+        decodeError("Loading error.");
+        return;
+    }
+
+    Image* image = m_image->image();
+    if (!is<BitmapImage>(image)) {
+        decodeError("Invalid image type.");
+        return;
+    }
+
+    auto& bitmapImage = downcast<BitmapImage>(*image);
+    bitmapImage.decode([promises = WTFMove(m_decodingPromises)]() mutable {
+        for (auto& promise : promises)
+            promise->resolve();
+    });
+}
+
 void ImageLoader::timerFired()
 {
     m_protectedElement = nullptr;
@@ -376,7 +445,7 @@ void ImageLoader::timerFired()
 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
 {
     ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender());
-    const AtomicString& eventType = eventSender->eventType();
+    const AtomString& eventType = eventSender->eventType();
     if (eventType == eventNames().beforeloadEvent)
         dispatchPendingBeforeLoadEvent();
     if (eventType == eventNames().loadEvent)
@@ -394,7 +463,12 @@ void ImageLoader::dispatchPendingBeforeLoadEvent()
     if (!element().document().hasLivingRenderTree())
         return;
     m_hasPendingBeforeLoadEvent = false;
+    Ref<Document> originalDocument = element().document();
     if (element().dispatchBeforeLoadEvent(m_image->url())) {
+        bool didEventListenerDisconnectThisElement = !element().isConnected() || &element().document() != originalDocument.ptr();
+        if (didEventListenerDisconnectThisElement)
+            return;
+        
         updateRenderer();
         return;
     }
@@ -435,7 +509,7 @@ void ImageLoader::dispatchPendingErrorEvent()
         return;
     m_hasPendingErrorEvent = false;
     if (element().document().hasLivingRenderTree())
-        element().dispatchEvent(Event::create(eventNames().errorEvent, false, false));
+        element().dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
 
     // Only consider updating the protection ref-count of the Element immediately before returning
     // from this function as doing so might result in the destruction of this ImageLoader.
@@ -465,7 +539,7 @@ void ImageLoader::elementDidMoveToNewDocument()
 
 inline void ImageLoader::clearFailedLoadURL()
 {
-    m_failedLoadURL = nullAtom;
+    m_failedLoadURL = nullAtom();
 }
 
 }