Skip to content
Permalink
Browse files
Add JSC support for delivering mutations when the outermost script co…
…ntext exits

https://bugs.webkit.org/show_bug.cgi?id=70289

Reviewed by Eric Seidel.

Source/WebCore:

The meat of this change is in JSMainThreadExecState, where a counter
is incremented every time WebCore calls into JSC and decremented every
time it returns. When the counter reaches zero, any pending mutations
are delivered (this mirrors very similar code in V8Proxy and V8RecursionScope).

The rest of the changes are of two sorts: compilation/logic fixes for
JSC code when ENABLE(MUTATION_OBSERVERS) is true, and additional
usages of JSMainThreadExecState so as to trigger the above
increment/decrements at the appropriate times.

* bindings/js/JSCustomXPathNSResolver.cpp:
(WebCore::JSCustomXPathNSResolver::lookupNamespaceURI):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSDictionary.cpp:
(WebCore::JSDictionary::convertValue): Add support
for tryGetProperty with a HashMap<AtomicString>.
* bindings/js/JSDictionary.h:
* bindings/js/JSErrorHandler.cpp:
(WebCore::JSErrorHandler::handleEvent):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSHTMLDocumentCustom.cpp:
(WebCore::JSHTMLDocument::open):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSMainThreadExecState.cpp:
(WebCore::JSMainThreadExecState::didLeaveScriptContext):
* bindings/js/JSMainThreadExecState.h:
(WebCore::JSMainThreadExecState::JSMainThreadExecState):
Increment a static recursion level counter.
(WebCore::JSMainThreadExecState::~JSMainThreadExecState):
Decrement a static recursion level counter and, if we are
at zero (the outermost script invocation), deliver any
outstanding mutation records.
* bindings/js/JSNodeFilterCondition.cpp:
(WebCore::JSNodeFilterCondition::acceptNode):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSWebKitMutationObserverCustom.cpp:
(WebCore::JSWebKitMutationObserver::observe):
Fix JSDictionary logic, add support for attributeFilter.

Tools:

* DumpRenderTree/mac/EventSendingController.mm: Add support for
eventSender.scheduleAsynchronousKeyDown.
(+[EventSendingController isSelectorExcludedFromWebScript:]):
(+[EventSendingController webScriptNameForSelector:]):
(-[EventSendingController keyDownWrapper:withModifiers:withLocation:]):
(-[EventSendingController scheduleAsynchronousKeyDown:withModifiers:withLocation:]):
* Scripts/build-webkit: Properly alphabetize --mutation-observers in the --help output.

LayoutTests:

With the various fixes in this change, 8/10 tests in fast/mutation
pass under WebKit/Mac. Of the failing tests, only one is due to
a deficiency in the Mac port's code (end-of-task-delivery.html);
the other is due to lack of support for a feature (FILE_SYSTEM)
exercised by the test (non-event-delivery.html).

* fast/mutation/non-event-delivery.html: Made it fail fast if FileSystem support isn't available.
* fast/mutation/observe-attributes.html: Fixed calls to removeEventListener.
* fast/mutation/observe-characterdata.html: ditto.

Canonical link: https://commits.webkit.org/94922@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@107008 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
ajklein committed Feb 8, 2012
1 parent 42a41af commit 8a198044274856e1615567651b3385f4eec89f55
Showing 17 changed files with 203 additions and 26 deletions.
@@ -1,3 +1,20 @@
2012-02-07 Adam Klein <adamk@chromium.org>

Add JSC support for delivering mutations when the outermost script context exits
https://bugs.webkit.org/show_bug.cgi?id=70289

Reviewed by Eric Seidel.

With the various fixes in this change, 8/10 tests in fast/mutation
pass under WebKit/Mac. Of the failing tests, only one is due to
a deficiency in the Mac port's code (end-of-task-delivery.html);
the other is due to lack of support for a feature (FILE_SYSTEM)
exercised by the test (non-event-delivery.html).

* fast/mutation/non-event-delivery.html: Made it fail fast if FileSystem support isn't available.
* fast/mutation/observe-attributes.html: Fixed calls to removeEventListener.
* fast/mutation/observe-characterdata.html: ditto.

2012-02-07 Levi Weintraub <leviw@chromium.org>

[SVG] Use element disappears after scripted change
@@ -116,9 +116,13 @@

description('Test that Mutation Records are delivered following non-event async callbacks.');

if (!window.WebKitMutationObserver)
if (!window.WebKitMutationObserver) {
testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
else
finishJSTest();
} else if (!window.webkitRequestFileSystem) {
testFailed('This test requires ENABLE(FILE_SYSTEM)');
finishJSTest();
} else
runNextTest();

</script>
@@ -285,7 +285,7 @@
shouldBe('mutations[0].attributeName', '"foo"');
shouldBe('mutations[1].type', '"attributes"');
shouldBe('mutations[1].attributeName', '"baz"');
div.removeEventListener(listener);
div.removeEventListener('DOMSubtreeModified', listener);
document.body.removeChild(div);
observer.disconnect();
debug('');
@@ -182,7 +182,7 @@
shouldBe('mutations.length', '2');
shouldBe('mutations[0].type', '"characterData"');
shouldBe('mutations[1].type', '"attributes"');
div.removeEventListener(listener);
div.removeEventListener('DOMSubtreeModified', listener);
document.body.removeChild(div);
observer.disconnect();
debug('');
@@ -1,3 +1,49 @@
2012-02-07 Adam Klein <adamk@chromium.org>

Add JSC support for delivering mutations when the outermost script context exits
https://bugs.webkit.org/show_bug.cgi?id=70289

Reviewed by Eric Seidel.

The meat of this change is in JSMainThreadExecState, where a counter
is incremented every time WebCore calls into JSC and decremented every
time it returns. When the counter reaches zero, any pending mutations
are delivered (this mirrors very similar code in V8Proxy and V8RecursionScope).

The rest of the changes are of two sorts: compilation/logic fixes for
JSC code when ENABLE(MUTATION_OBSERVERS) is true, and additional
usages of JSMainThreadExecState so as to trigger the above
increment/decrements at the appropriate times.

* bindings/js/JSCustomXPathNSResolver.cpp:
(WebCore::JSCustomXPathNSResolver::lookupNamespaceURI):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSDictionary.cpp:
(WebCore::JSDictionary::convertValue): Add support
for tryGetProperty with a HashMap<AtomicString>.
* bindings/js/JSDictionary.h:
* bindings/js/JSErrorHandler.cpp:
(WebCore::JSErrorHandler::handleEvent):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSHTMLDocumentCustom.cpp:
(WebCore::JSHTMLDocument::open):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSMainThreadExecState.cpp:
(WebCore::JSMainThreadExecState::didLeaveScriptContext):
* bindings/js/JSMainThreadExecState.h:
(WebCore::JSMainThreadExecState::JSMainThreadExecState):
Increment a static recursion level counter.
(WebCore::JSMainThreadExecState::~JSMainThreadExecState):
Decrement a static recursion level counter and, if we are
at zero (the outermost script invocation), deliver any
outstanding mutation records.
* bindings/js/JSNodeFilterCondition.cpp:
(WebCore::JSNodeFilterCondition::acceptNode):
Use JSMainThreadExecState instead of JSC::call.
* bindings/js/JSWebKitMutationObserverCustom.cpp:
(WebCore::JSWebKitMutationObserver::observe):
Fix JSDictionary logic, add support for attributeFilter.

2012-02-07 Anders Carlsson <andersca@apple.com>

Fix build.
@@ -31,6 +31,7 @@
#include "ExceptionCode.h"
#include "Frame.h"
#include "JSDOMWindowCustom.h"
#include "JSMainThreadExecState.h"
#include "SecurityOrigin.h"
#include <runtime/JSLock.h>

@@ -89,7 +90,7 @@ String JSCustomXPathNSResolver::lookupNamespaceURI(const String& prefix)
args.append(jsString(exec, prefix));

m_globalObject->globalData().timeoutChecker.start();
JSValue retval = JSC::call(exec, function, callType, callData, m_customResolver, args);
JSValue retval = JSMainThreadExecState::call(exec, function, callType, callData, m_customResolver, args);
m_globalObject->globalData().timeoutChecker.stop();

String result;
@@ -34,7 +34,9 @@
#include "JSTrackCustom.h"
#include "SerializedScriptValue.h"
#include "ScriptValue.h"
#include <wtf/HashMap.h>
#include <wtf/MathExtras.h>
#include <wtf/text/AtomicString.h>

using namespace JSC;

@@ -136,4 +138,26 @@ void JSDictionary::convertValue(ExecState*, JSValue value, RefPtr<TrackBase>& re
}
#endif

#if ENABLE(MUTATION_OBSERVERS)
void JSDictionary::convertValue(ExecState* exec, JSValue value, HashSet<AtomicString>& result)
{
result.clear();

if (value.isUndefinedOrNull())
return;

unsigned length;
JSObject* object = toJSSequence(exec, value, length);
if (exec->hadException())
return;

for (unsigned i = 0 ; i < length; ++i) {
JSValue itemValue = object->get(exec, i);
if (exec->hadException())
return;
result.add(ustringToAtomicString(itemValue.toString(exec)->value(exec)));
}
}
#endif

} // namespace WebCore
@@ -87,6 +87,9 @@ class JSDictionary {
#if ENABLE(VIDEO_TRACK)
static void convertValue(JSC::ExecState*, JSC::JSValue, RefPtr<TrackBase>& result);
#endif
#if ENABLE(MUTATION_OBSERVERS)
static void convertValue(JSC::ExecState*, JSC::JSValue, HashSet<AtomicString>& result);
#endif

JSC::ExecState* m_exec;
JSC::JSObject* m_initializerObject;
@@ -36,6 +36,7 @@
#include "Event.h"
#include "EventNames.h"
#include "JSEvent.h"
#include "JSMainThreadExecState.h"
#include <runtime/JSLock.h>

using namespace JSC;
@@ -94,7 +95,9 @@ void JSErrorHandler::handleEvent(ScriptExecutionContext* scriptExecutionContext,
JSValue thisValue = globalObject->methodTable()->toThisObject(globalObject, exec);

globalData.timeoutChecker.start();
JSValue returnValue = JSC::call(exec, jsFunction, callType, callData, thisValue, args);
JSValue returnValue = scriptExecutionContext->isDocument()
? JSMainThreadExecState::call(exec, jsFunction, callType, callData, thisValue, args)
: JSC::call(exec, jsFunction, callType, callData, thisValue, args);
globalData.timeoutChecker.stop();

globalObject->setCurrentEvent(savedEvent);
@@ -38,6 +38,7 @@
#include "JSDOMWindowCustom.h"
#include "JSDOMWindowShell.h"
#include "JSHTMLCollection.h"
#include "JSMainThreadExecState.h"
#include "SegmentedString.h"
#include "DocumentParser.h"
#include <runtime/Error.h>
@@ -113,7 +114,7 @@ JSValue JSHTMLDocument::open(ExecState* exec)
CallType callType = ::getCallData(function, callData);
if (callType == CallTypeNone)
return throwTypeError(exec);
return JSC::call(exec, function, callType, callData, wrapper, ArgList(exec));
return JSMainThreadExecState::call(exec, function, callType, callData, wrapper, ArgList(exec));
}
}
return jsUndefined();
@@ -25,9 +25,19 @@

#include "config.h"
#include "JSMainThreadExecState.h"
#include "WebKitMutationObserver.h"

namespace WebCore {

JSC::ExecState* JSMainThreadExecState::s_mainThreadState = 0;

#if ENABLE(MUTATION_OBSERVERS)
int JSMainThreadExecState::s_recursionLevel = 0;

void JSMainThreadExecState::didLeaveScriptContext()
{
WebKitMutationObserver::deliverAllMutations();
}
#endif

} // namespace WebCore
@@ -91,17 +91,33 @@ class JSMainThreadExecState {
{
ASSERT(isMainThread());
s_mainThreadState = exec;

#if ENABLE(MUTATION_OBSERVERS)
ASSERT(s_recursionLevel >= 0);
++s_recursionLevel;
#endif
};

~JSMainThreadExecState()
{
ASSERT(isMainThread());
s_mainThreadState = m_previousState;

#if ENABLE(MUTATION_OBSERVERS)
ASSERT(s_recursionLevel > 0);
if (!--s_recursionLevel)
didLeaveScriptContext();
#endif
}

private:
static JSC::ExecState* s_mainThreadState;
JSC::ExecState* m_previousState;

#if ENABLE(MUTATION_OBSERVERS)
static void didLeaveScriptContext();
static int s_recursionLevel;
#endif
};

// Null state prevents origin security checks.
@@ -20,6 +20,7 @@
#include "config.h"
#include "JSNodeFilterCondition.h"

#include "JSMainThreadExecState.h"
#include "JSNode.h"
#include "JSNodeFilter.h"
#include "NodeFilter.h"
@@ -72,7 +73,7 @@ short JSNodeFilterCondition::acceptNode(JSC::ExecState* exec, Node* filterNode)
if (exec->hadException())
return NodeFilter::FILTER_REJECT;

JSValue result = JSC::call(exec, function, callType, callData, m_filter.get(), args);
JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, m_filter.get(), args);
if (exec->hadException())
return NodeFilter::FILTER_REJECT;

@@ -41,6 +41,8 @@
#include "Node.h"
#include "WebKitMutationObserver.h"
#include <runtime/Error.h>
#include <wtf/HashSet.h>
#include <wtf/text/AtomicString.h>

using namespace JSC;

@@ -62,6 +64,22 @@ EncodedJSValue JSC_HOST_CALL JSWebKitMutationObserverConstructor::constructJSWeb
return JSValue::encode(asObject(toJS(exec, jsConstructor->globalObject(), WebKitMutationObserver::create(callback.release()))));
}

struct BooleanOption {
const char* name;
MutationObserverOptions value;
};

static const BooleanOption booleanOptions[] = {
{ "childList", WebKitMutationObserver::ChildList },
{ "attributes", WebKitMutationObserver::Attributes },
{ "characterData", WebKitMutationObserver::CharacterData },
{ "subtree", WebKitMutationObserver::Subtree },
{ "attributeOldValue", WebKitMutationObserver::AttributeOldValue },
{ "characterDataOldValue", WebKitMutationObserver::CharacterDataOldValue }
};

static const size_t numBooleanOptions = sizeof(booleanOptions) / sizeof(BooleanOption);

JSValue JSWebKitMutationObserver::observe(ExecState* exec)
{
if (exec->argumentCount() < 2)
@@ -78,24 +96,22 @@ JSValue JSWebKitMutationObserver::observe(ExecState* exec)

JSDictionary dictionary(exec, optionsObject);
MutationObserverOptions options = 0;
// FIXME: Add support for parsing of the attributeFilter option.
bool option;
if (dictionary.tryGetProperty("childList", option) && option)
options |= WebKitMutationObserver::ChildList;
if (dictionary.tryGetProperty("attributes", option) && option)
options |= WebKitMutationObserver::Attributes;
if (dictionary.tryGetProperty("subtree", option) && option)
options |= WebKitMutationObserver::Subtree;
if (dictionary.tryGetProperty("attributeOldValue", option) && option)
options |= WebKitMutationObserver::AttributeOldValue;
if (dictionary.tryGetProperty("characterDataOldValue", option) && option)
options |= WebKitMutationObserver::CharacterDataOldValue;
for (unsigned i = 0; i < numBooleanOptions; ++i) {
bool option = false;
if (!dictionary.tryGetProperty(booleanOptions[i].name, option))
return jsUndefined();
if (option)
options |= booleanOptions[i].value;
}

if (exec->hadException())
HashSet<AtomicString> attributeFilter;
if (!dictionary.tryGetProperty("attributeFilter", attributeFilter))
return jsUndefined();
if (!attributeFilter.isEmpty())
options |= WebKitMutationObserver::AttributeFilter;

ExceptionCode ec = 0;
impl()->observe(target, options, ec);
impl()->observe(target, options, attributeFilter, ec);
if (ec)
setDOMException(exec, ec);
return jsUndefined();
@@ -1,3 +1,18 @@
2012-02-07 Adam Klein <adamk@chromium.org>

Add JSC support for delivering mutations when the outermost script context exits
https://bugs.webkit.org/show_bug.cgi?id=70289

Reviewed by Eric Seidel.

* DumpRenderTree/mac/EventSendingController.mm: Add support for
eventSender.scheduleAsynchronousKeyDown.
(+[EventSendingController isSelectorExcludedFromWebScript:]):
(+[EventSendingController webScriptNameForSelector:]):
(-[EventSendingController keyDownWrapper:withModifiers:withLocation:]):
(-[EventSendingController scheduleAsynchronousKeyDown:withModifiers:withLocation:]):
* Scripts/build-webkit: Properly alphabetize --mutation-observers in the --help output.

2012-02-07 Chris Rogers <crogers@google.com>

Add Chris Rogers to reviewers section

0 comments on commit 8a19804

Please sign in to comment.