- Revision
- 144522
- Author
- [email protected]
- Date
- 2013-03-01 18:35:53 -0800 (Fri, 01 Mar 2013)
Log Message
Don't leak Documents when using MutationObserver from extensions
https://bugs.webkit.org/show_bug.cgi?id=111234
Patch by Elliott Sprehn <[email protected]> on 2013-03-01
Reviewed by Adam Barth.
.:
* ManualTests/leak-observer-nonmain-world.html: Added.
Source/WebCore:
MutationObserverCallback holds a WorldContextHandle which secretly isn't
a handle to anything when it's for the main world. When it's for a non-main
world though, like those used in extensions, it becomes a strong reference
to the v8::Context which results in leaks by creating cycles:
MutationObserver -> Callback -> World -> Document -> Node -> MutationObserver.
Instead we should keep a RefPtr to a DOMWrapperWorld in the callback and then
get the v8::Context from that inside handleEvent.
Tests: ManualTests/leak-observer-nonmain-world.html
* bindings/v8/V8Binding.cpp:
(WebCore::toV8Context): Added overload that takes a DOMWrapperWorld.
* bindings/v8/V8Binding.h:
* bindings/v8/V8MutationCallback.cpp:
(WebCore::V8MutationCallback::V8MutationCallback):
(WebCore::V8MutationCallback::handleEvent):
* bindings/v8/V8MutationCallback.h:
(V8MutationCallback):
Modified Paths
Added Paths
Diff
Modified: trunk/ChangeLog (144521 => 144522)
--- trunk/ChangeLog 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/ChangeLog 2013-03-02 02:35:53 UTC (rev 144522)
@@ -1,3 +1,12 @@
+2013-03-01 Elliott Sprehn <[email protected]>
+
+ Don't leak Documents when using MutationObserver from extensions
+ https://bugs.webkit.org/show_bug.cgi?id=111234
+
+ Reviewed by Adam Barth.
+
+ * ManualTests/leak-observer-nonmain-world.html: Added.
+
2013-02-27 Zan Dobersek <[email protected]>
[GTK] Disable MathML support in release builds
Added: trunk/ManualTests/leak-observer-nonmain-world.html (0 => 144522)
--- trunk/ManualTests/leak-observer-nonmain-world.html (rev 0)
+++ trunk/ManualTests/leak-observer-nonmain-world.html 2013-03-02 02:35:53 UTC (rev 144522)
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<p>Test that using mutation observers from the non-main world doesn't leak the document.</p>
+<p>Expected output of this test is LEAK: 28 WebCoreNode</p>
+
+<iframe></iframe>
+
+<script>
+testRunner.dumpAsText();
+testRunner.waitUntilDone();
+
+var iframe = document.querySelector('iframe');
+var count = 0;
+var totalRuns = 5;
+
+iframe._onload_ = function() {
+ if (count++ < totalRuns) {
+ testRunner.evaluateScriptInIsolatedWorld(1, 'new MutationObserver(function(){}).observe(document, {childList: true, subtree: true});');
+ iframe.srcdoc = "<body><input autofocus></body>";
+ GCController.collect();
+ } else {
+ GCController.collect();
+ testRunner.notifyDone();
+ }
+};
+
+// Need autofocus since evaluateScriptInIsolatedWorld runs in the focused frame.
+iframe.srcdoc = "<body><input autofocus></body>";
+</script>
+
Modified: trunk/Source/WebCore/ChangeLog (144521 => 144522)
--- trunk/Source/WebCore/ChangeLog 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/Source/WebCore/ChangeLog 2013-03-02 02:35:53 UTC (rev 144522)
@@ -1,3 +1,31 @@
+2013-03-01 Elliott Sprehn <[email protected]>
+
+ Don't leak Documents when using MutationObserver from extensions
+ https://bugs.webkit.org/show_bug.cgi?id=111234
+
+ Reviewed by Adam Barth.
+
+ MutationObserverCallback holds a WorldContextHandle which secretly isn't
+ a handle to anything when it's for the main world. When it's for a non-main
+ world though, like those used in extensions, it becomes a strong reference
+ to the v8::Context which results in leaks by creating cycles:
+
+ MutationObserver -> Callback -> World -> Document -> Node -> MutationObserver.
+
+ Instead we should keep a RefPtr to a DOMWrapperWorld in the callback and then
+ get the v8::Context from that inside handleEvent.
+
+ Tests: ManualTests/leak-observer-nonmain-world.html
+
+ * bindings/v8/V8Binding.cpp:
+ (WebCore::toV8Context): Added overload that takes a DOMWrapperWorld.
+ * bindings/v8/V8Binding.h:
+ * bindings/v8/V8MutationCallback.cpp:
+ (WebCore::V8MutationCallback::V8MutationCallback):
+ (WebCore::V8MutationCallback::handleEvent):
+ * bindings/v8/V8MutationCallback.h:
+ (V8MutationCallback):
+
2013-03-01 Bear Travis <[email protected]>
[css exclusions] Move ExclusionShapeInsideInfo into RenderBlockRareData
Modified: trunk/Source/WebCore/bindings/v8/V8Binding.cpp (144521 => 144522)
--- trunk/Source/WebCore/bindings/v8/V8Binding.cpp 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/Source/WebCore/bindings/v8/V8Binding.cpp 2013-03-02 02:35:53 UTC (rev 144522)
@@ -266,6 +266,26 @@
return v8::Local<v8::Context>();
}
+v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context, DOMWrapperWorld* world)
+{
+ if (context->isDocument()) {
+ if (Frame* frame = static_cast<Document*>(context)->frame()) {
+ // FIXME: Store the DOMWrapperWorld for the main world in the v8::Context so callers
+ // that are looking up their world with DOMWrapperWorld::getWorld(v8::Context::GetCurrent())
+ // won't end up passing null here when later trying to get their v8::Context back.
+ if (!world)
+ return frame->script()->mainWorldContext();
+ return v8::Local<v8::Context>::New(frame->script()->windowShell(world)->context());
+ }
+#if ENABLE(WORKERS)
+ } else if (context->isWorkerContext()) {
+ if (WorkerScriptController* script = static_cast<WorkerContext*>(context)->script())
+ return script->context();
+#endif
+ }
+ return v8::Local<v8::Context>();
+}
+
bool handleOutOfMemory()
{
v8::Local<v8::Context> context = v8::Context::GetCurrent();
Modified: trunk/Source/WebCore/bindings/v8/V8Binding.h (144521 => 144522)
--- trunk/Source/WebCore/bindings/v8/V8Binding.h 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/Source/WebCore/bindings/v8/V8Binding.h 2013-03-02 02:35:53 UTC (rev 144522)
@@ -445,6 +445,7 @@
// Returns the context associated with a ScriptExecutionContext.
v8::Local<v8::Context> toV8Context(ScriptExecutionContext*, const WorldContextHandle&);
+ v8::Local<v8::Context> toV8Context(ScriptExecutionContext*, DOMWrapperWorld*);
// Returns the frame object of the window object associated with
// a context, if the window is currently being displayed in the Frame.
Modified: trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp (144521 => 144522)
--- trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/Source/WebCore/bindings/v8/V8MutationCallback.cpp 2013-03-02 02:35:53 UTC (rev 144522)
@@ -38,7 +38,7 @@
V8MutationCallback::V8MutationCallback(v8::Handle<v8::Object> callback, ScriptExecutionContext* context, v8::Handle<v8::Object> owner, v8::Isolate* isolate)
: ActiveDOMCallback(context)
, m_callback(callback)
- , m_worldContext(UseCurrentWorld)
+ , m_world(DOMWrapperWorld::getWorld(v8::Context::GetCurrent()))
{
owner->SetHiddenValue(V8HiddenPropertyName::callback(), callback);
m_callback.get().MakeWeak(isolate, this, &V8MutationCallback::weakCallback);
@@ -55,7 +55,7 @@
v8::HandleScope handleScope;
- v8::Handle<v8::Context> v8Context = toV8Context(scriptExecutionContext(), m_worldContext);
+ v8::Handle<v8::Context> v8Context = toV8Context(scriptExecutionContext(), m_world.get());
if (v8Context.IsEmpty())
return true;
Modified: trunk/Source/WebCore/bindings/v8/V8MutationCallback.h (144521 => 144522)
--- trunk/Source/WebCore/bindings/v8/V8MutationCallback.h 2013-03-02 01:31:54 UTC (rev 144521)
+++ trunk/Source/WebCore/bindings/v8/V8MutationCallback.h 2013-03-02 02:35:53 UTC (rev 144522)
@@ -27,10 +27,11 @@
#define V8MutationCallback_h
#include "ActiveDOMCallback.h"
+#include "DOMWrapperWorld.h"
#include "MutationCallback.h"
#include "ScopedPersistent.h"
-#include "WorldContextHandle.h"
#include <v8.h>
+#include <wtf/RefPtr.h>
namespace WebCore {
@@ -58,7 +59,7 @@
}
ScopedPersistent<v8::Object> m_callback;
- WorldContextHandle m_worldContext;
+ RefPtr<DOMWrapperWorld> m_world;
};
}