Changeset 254328 in webkit


Ignore:
Timestamp:
Jan 10, 2020 12:42:11 AM (4 years ago)
Author:
Carlos Garcia Campos
Message:

Automation: scripts are executed in the wrong js context after a history navigation
https://bugs.webkit.org/show_bug.cgi?id=204880
<rdar://problem/58413615>

Reviewed by Brian Burg.

After a history navigation we use the script object from the previous frame js context because
didClearWindowObjectForFrame() is not called in that case. We are caching the script object for every frame ID,
and after a history navigation the frame ID is the same, but the frame js context isn't. That also means we might
be leaking the script objects in those cases, because we end up calling JSValueUnprotect with the wrong
context. It would be easier to set the script object as a property of the global object and let JSC handle the
lifetime. Instead of caching the script object and protect/unprotect it, we just check if the global object of
the current js context has the property or not to get or create it. We use a private symbol as the key of the
global object property to ensure it's not visible.

  • WebProcess/Automation/WebAutomationSessionProxy.cpp:

(WebKit::WebAutomationSessionProxy::WebAutomationSessionProxy): Initialize m_scriptObjectIdentifier.
(WebKit::WebAutomationSessionProxy::scriptObject): Helper function to get the script object for the given
JavaScript context.
(WebKit::WebAutomationSessionProxy::setScriptObject): Helper function to set the script object for the given
JavaScript context.
(WebKit::WebAutomationSessionProxy::scriptObjectForFrame): Get or create the script object.
(WebKit::WebAutomationSessionProxy::elementForNodeHandle): Get the script object from global object.
(WebKit::WebAutomationSessionProxy::didClearWindowObjectForFrame): Remove the code to unprotect script objects
of the frame.

  • WebProcess/Automation/WebAutomationSessionProxy.h: Add m_scriptObjectIdentifier and remove m_webFrameScriptObjectMap.
Location:
trunk/Source/WebKit
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r254325 r254328  
     12020-01-10  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        Automation: scripts are executed in the wrong js context after a history navigation
     4        https://bugs.webkit.org/show_bug.cgi?id=204880
     5        <rdar://problem/58413615>
     6
     7        Reviewed by Brian Burg.
     8
     9        After a history navigation we use the script object from the previous frame js context because
     10        didClearWindowObjectForFrame() is not called in that case. We are caching the script object for every frame ID,
     11        and after a history navigation the frame ID is the same, but the frame js context isn't. That also means we might
     12        be leaking the script objects in those cases, because we end up calling JSValueUnprotect with the wrong
     13        context. It would be easier to set the script object as a property of the global object and let JSC handle the
     14        lifetime. Instead of caching the script object and protect/unprotect it, we just check if the global object of
     15        the current js context has the property or not to get or create it. We use a private symbol as the key of the
     16        global object property to ensure it's not visible.
     17
     18        * WebProcess/Automation/WebAutomationSessionProxy.cpp:
     19        (WebKit::WebAutomationSessionProxy::WebAutomationSessionProxy): Initialize m_scriptObjectIdentifier.
     20        (WebKit::WebAutomationSessionProxy::scriptObject): Helper function to get the script object for the given
     21        JavaScript context.
     22        (WebKit::WebAutomationSessionProxy::setScriptObject): Helper function to set the script object for the given
     23        JavaScript context.
     24        (WebKit::WebAutomationSessionProxy::scriptObjectForFrame): Get or create the script object.
     25        (WebKit::WebAutomationSessionProxy::elementForNodeHandle): Get the script object from global object.
     26        (WebKit::WebAutomationSessionProxy::didClearWindowObjectForFrame): Remove the code to unprotect script objects
     27        of the frame.
     28        * WebProcess/Automation/WebAutomationSessionProxy.h: Add m_scriptObjectIdentifier and remove m_webFrameScriptObjectMap.
     29
    1302020-01-09  Ross Kirsling  <ross.kirsling@sony.com>
    231
  • trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp

    r254118 r254328  
    109109WebAutomationSessionProxy::WebAutomationSessionProxy(const String& sessionIdentifier)
    110110    : m_sessionIdentifier(sessionIdentifier)
     111    , m_scriptObjectIdentifier(JSC::PrivateName::Description, "automationSessionProxy"_s)
    111112{
    112113    WebProcess::singleton().addMessageReceiver(Messages::WebAutomationSessionProxy::messageReceiverName(), *this);
     
    212213}
    213214
     215JSObjectRef WebAutomationSessionProxy::scriptObject(JSGlobalContextRef context)
     216{
     217    JSC::JSGlobalObject* globalObject = toJS(context);
     218    JSC::VM& vm = globalObject->vm();
     219    JSC::JSLockHolder locker(vm);
     220    auto scriptObjectID = JSC::Identifier::fromUid(m_scriptObjectIdentifier);
     221    if (!globalObject->hasProperty(globalObject, scriptObjectID))
     222        return nullptr;
     223
     224    return const_cast<JSObjectRef>(toRef(globalObject, globalObject->get(globalObject, scriptObjectID)));
     225}
     226
     227void WebAutomationSessionProxy::setScriptObject(JSGlobalContextRef context, JSObjectRef object)
     228{
     229    JSC::JSGlobalObject* globalObject = toJS(context);
     230    JSC::VM& vm = globalObject->vm();
     231    JSC::JSLockHolder locker(vm);
     232    auto scriptObjectID = JSC::Identifier::fromUid(m_scriptObjectIdentifier);
     233    PutPropertySlot slot(globalObject);
     234    globalObject->methodTable(vm)->put(globalObject, globalObject, scriptObjectID, toJS(globalObject, object), slot);
     235}
     236
    214237JSObjectRef WebAutomationSessionProxy::scriptObjectForFrame(WebFrame& frame)
    215238{
    216     if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID()))
     239    JSGlobalContextRef context = frame.jsContext();
     240    if (auto* scriptObject = this->scriptObject(context))
    217241        return scriptObject;
    218242
    219243    JSValueRef exception = nullptr;
    220     JSGlobalContextRef context = frame.jsContext();
     244    String script = StringImpl::createWithoutCopying(WebAutomationSessionProxyScriptSource, sizeof(WebAutomationSessionProxyScriptSource));
     245    JSObjectRef scriptObjectFunction = const_cast<JSObjectRef>(JSEvaluateScript(context, OpaqueJSString::tryCreate(script).get(), nullptr, nullptr, 0, &exception));
     246    ASSERT(JSValueIsObject(context, scriptObjectFunction));
    221247
    222248    JSValueRef sessionIdentifier = toJSValue(context, m_sessionIdentifier);
     
    224250    JSObjectRef createUUIDFunction = JSObjectMakeFunctionWithCallback(context, nullptr, createUUID);
    225251    JSObjectRef isValidNodeIdentifierFunction = JSObjectMakeFunctionWithCallback(context, nullptr, isValidNodeIdentifier);
    226 
    227     String script = StringImpl::createWithoutCopying(WebAutomationSessionProxyScriptSource, sizeof(WebAutomationSessionProxyScriptSource));
    228 
    229     JSObjectRef scriptObjectFunction = const_cast<JSObjectRef>(JSEvaluateScript(context, OpaqueJSString::tryCreate(script).get(), nullptr, nullptr, 0, &exception));
    230     ASSERT(JSValueIsObject(context, scriptObjectFunction));
    231 
    232252    JSValueRef arguments[] = { sessionIdentifier, evaluateFunction, createUUIDFunction, isValidNodeIdentifierFunction };
    233253    JSObjectRef scriptObject = const_cast<JSObjectRef>(JSObjectCallAsFunction(context, scriptObjectFunction, nullptr, WTF_ARRAY_LENGTH(arguments), arguments, &exception));
    234254    ASSERT(JSValueIsObject(context, scriptObject));
    235255
    236     JSValueProtect(context, scriptObject);
    237     m_webFrameScriptObjectMap.add(frame.frameID(), scriptObject);
    238 
     256    setScriptObject(context, scriptObject);
    239257    return scriptObject;
    240258}
     
    245263    // does not exist, there are no nodes mapped to handles. Using scriptObjectForFrame()
    246264    // will make a new script object if it can't find one, preventing us from returning fast.
    247     JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID());
     265    JSGlobalContextRef context = frame.jsContext();
     266    auto* scriptObject = this->scriptObject(context);
    248267    if (!scriptObject)
    249268        return nullptr;
    250 
    251     JSGlobalContextRef context = frame.jsContext();
    252269
    253270    JSValueRef functionArguments[] = {
     
    269286void WebAutomationSessionProxy::didClearWindowObjectForFrame(WebFrame& frame)
    270287{
    271     WebCore::FrameIdentifier frameID = frame.frameID();
    272     if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.take(frameID))
    273         JSValueUnprotect(frame.jsContext(), scriptObject);
    274 
    275288    String errorMessage = "Callback was not called before the unload event."_s;
    276289    String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptError);
    277290
    278     auto pendingFrameCallbacks = m_webFramePendingEvaluateJavaScriptCallbacksMap.take(frameID);
     291    auto pendingFrameCallbacks = m_webFramePendingEvaluateJavaScriptCallbacksMap.take(frame.frameID());
    279292    for (uint64_t callbackID : pendingFrameCallbacks)
    280293        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, errorMessage, errorType), 0);
  • trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.h

    r253883 r254328  
    2929#include "CoordinateSystem.h"
    3030#include <JavaScriptCore/JSBase.h>
     31#include <JavaScriptCore/PrivateName.h>
    3132#include <WebCore/FrameIdentifier.h>
    3233#include <WebCore/IntRect.h>
     
    5758
    5859private:
     60    JSObjectRef scriptObject(JSGlobalContextRef);
     61    void setScriptObject(JSGlobalContextRef, JSObjectRef);
    5962    JSObjectRef scriptObjectForFrame(WebFrame&);
    6063    WebCore::Element* elementForNodeHandle(WebFrame&, const String&);
     
    7881
    7982    String m_sessionIdentifier;
     83    JSC::PrivateName m_scriptObjectIdentifier;
    8084
    81     HashMap<WebCore::FrameIdentifier, JSObjectRef> m_webFrameScriptObjectMap;
    8285    HashMap<WebCore::FrameIdentifier, Vector<uint64_t>> m_webFramePendingEvaluateJavaScriptCallbacksMap;
    8386};
Note: See TracChangeset for help on using the changeset viewer.