Index: Source/core/html/HTMLImageElement.cpp |
diff --git a/Source/core/html/HTMLImageElement.cpp b/Source/core/html/HTMLImageElement.cpp |
index ff90239a68388aead040d64d21e63c905971801b..a759371bb146c40320a204e9c745075e42cbf070 100644 |
--- a/Source/core/html/HTMLImageElement.cpp |
+++ b/Source/core/html/HTMLImageElement.cpp |
@@ -26,8 +26,10 @@ |
#include "CSSPropertyNames.h" |
#include "HTMLNames.h" |
#include "RuntimeEnabledFeatures.h" |
+#include "bindings/v8/DOMWrapperWorld.h" |
#include "bindings/v8/ScriptEventListener.h" |
#include "core/dom/Attribute.h" |
+#include "core/dom/Microtask.h" |
#include "core/fetch/ImageResource.h" |
#include "core/html/HTMLAnchorElement.h" |
#include "core/html/HTMLCanvasElement.h" |
@@ -36,9 +38,20 @@ |
#include "core/html/parser/HTMLParserIdioms.h" |
#include "core/html/parser/HTMLSrcsetParser.h" |
#include "core/rendering/RenderImage.h" |
+#include "wtf/Vector.h" |
+#include <v8.h> |
using namespace std; |
+namespace { |
+typedef Vector<WebCore::HTMLImageElement*> PendingUpdateQueueType; |
+static PendingUpdateQueueType& PendingUpdateQueue() |
+{ |
+ DEFINE_STATIC_LOCAL(Vector<WebCore::HTMLImageElement*>, pendingUpdateFromElementCalls, ()); |
+ return pendingUpdateFromElementCalls; |
+} |
+} |
+ |
namespace WebCore { |
using namespace HTMLNames; |
@@ -49,6 +62,7 @@ HTMLImageElement::HTMLImageElement(Document& document, HTMLFormElement* form) |
, m_compositeOperator(CompositeSourceOver) |
, m_imageDevicePixelRatio(1.0f) |
, m_formWasSetByParser(false) |
+ , m_pendingUpdate(PendingUpdateNone) |
{ |
ScriptWrappable::init(this); |
if (form && form->inDocument()) { |
@@ -73,6 +87,7 @@ HTMLImageElement::~HTMLImageElement() |
{ |
if (m_form) |
m_form->disassociate(*this); |
+ cancelPendingUpdate(); |
abarth-chromium
2014/04/01 23:57:14
This algorithm is O(N^2) when tearing down the doc
|
} |
PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document& document, int width, int height) |
@@ -163,7 +178,7 @@ void HTMLImageElement::parseAttribute(const QualifiedName& name, const AtomicStr |
if (renderer() && renderer()->isImage()) |
toRenderImage(renderer())->setImageDevicePixelRatio(m_imageDevicePixelRatio); |
} |
- m_imageLoader.updateFromElementIgnoringPreviousError(); |
+ enqueueUpdate(PendingUpdateIgnoreError); |
} else if (name == usemapAttr) { |
setIsLink(!value.isNull()); |
} else if (name == compositeAttr) { |
@@ -235,7 +250,7 @@ Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* |
// If we have been inserted from a renderer-less document, |
// our loader may have not fetched the image, so do it now. |
if (insertionPoint->inDocument() && !m_imageLoader.image()) |
- m_imageLoader.updateFromElement(); |
+ enqueueUpdate(PendingUpdateNormal); |
return HTMLElement::insertedInto(insertionPoint); |
} |
@@ -383,13 +398,17 @@ int HTMLImageElement::y() const |
bool HTMLImageElement::complete() const |
{ |
- return m_imageLoader.imageComplete(); |
+ return m_imageLoader.imageComplete() && !hasPendingUpdate(); |
} |
void HTMLImageElement::didMoveToNewDocument(Document& oldDocument) |
{ |
m_imageLoader.elementDidMoveToNewDocument(); |
HTMLElement::didMoveToNewDocument(oldDocument); |
+ if (hasPendingUpdate()) { |
+ document().incrementLoadEventDelayCount(); |
+ oldDocument.decrementLoadEventDelayCount(); |
+ } |
} |
bool HTMLImageElement::isServerMap() const |
@@ -472,4 +491,80 @@ FloatSize HTMLImageElement::defaultDestinationSize() const |
return size; |
} |
+void HTMLImageElement::enqueueUpdate(PendingUpdateType type) |
+{ |
+ ASSERT(type != PendingUpdateNone); |
+ |
+ v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
+ bool isIsolatedWorld = false; |
+ if (isolate && isolate->InContext()) { |
+ if (DOMWrapperWorld::current(isolate).isIsolatedWorld()) |
+ isIsolatedWorld = true; |
+ } |
abarth-chromium
2014/04/01 23:57:14
The HTMLImageElement shouldn't have any knowledge
|
+ |
+ if (m_imageLoader.shouldLoadImmediately() || isIsolatedWorld) { |
abarth-chromium
2014/04/01 23:57:14
It's wrong to make image loading work differently
|
+ if (hasPendingUpdate()) |
+ cancelPendingUpdate(); |
+ if (type == PendingUpdateNormal) |
+ m_imageLoader.updateFromElement(); |
+ else |
+ m_imageLoader.updateFromElementIgnoringPreviousError(); |
+ return; |
+ } |
+ |
+ if (hasPendingUpdate()) { |
+ ASSERT(PendingUpdateQueue().contains(this)); |
+ if (m_pendingUpdate == PendingUpdateNormal && type == PendingUpdateIgnoreError) |
+ m_pendingUpdate = PendingUpdateIgnoreError; |
+ } else { |
+ document().incrementLoadEventDelayCount(); |
+ PendingUpdateQueue().append(this); |
+ m_pendingUpdate = type; |
+ } |
+ if (PendingUpdateQueue().size() == 1) |
+ Microtask::enqueueMicrotask(&processUpdateFromElementQueue); |
+} |
+ |
+void HTMLImageElement::cancelPendingUpdate() |
+{ |
+ if (hasPendingUpdate()) { |
+ size_t pos = PendingUpdateQueue().find(this); |
+ ASSERT(pos != kNotFound); |
+ PendingUpdateQueue().remove(pos); |
+ m_pendingUpdate = PendingUpdateNone; |
+ document().decrementLoadEventDelayCount(); |
+ } |
+} |
+ |
+/* static */ void HTMLImageElement::processUpdateFromElementQueue() |
abarth-chromium
2014/04/01 23:57:14
We don't use /* static */ comments in Blink
|
+{ |
+ PendingUpdateQueueType pending; |
+ pending.swap(PendingUpdateQueue()); |
+ |
+ // We keep this vector separately, because we can only call |
+ // decrementLoadEventDelayCount after we've updated all image elements |
+ // because the load event may destroy image elements. |
+ Vector<RefPtr<Document> > documents; |
+ |
+ { |
+ PendingUpdateQueueType::const_iterator i, end; |
+ for (i = pending.begin(), end = pending.end(); i != end; ++i) { |
+ RefPtr<HTMLImageElement> current = *i; |
+ if (current->m_pendingUpdate == PendingUpdateIgnoreError) { |
+ current->m_pendingUpdate = PendingUpdateNone; |
+ current->m_imageLoader.updateFromElementIgnoringPreviousError(); |
+ documents.append(¤t->document()); |
+ } else { |
+ ASSERT(current->m_pendingUpdate == PendingUpdateNormal); |
+ current->m_pendingUpdate = PendingUpdateNone; |
+ current->m_imageLoader.updateFromElement(); |
+ documents.append(¤t->document()); |
+ } |
+ } |
+ } |
+ Vector<RefPtr<Document> >::const_iterator i, end; |
+ for (i = documents.begin(), end = documents.end(); i != end; ++i) { |
+ (*i)->decrementLoadEventDelayCount(); |
+ } |
+} |
} |