Note adding new input types

Brian Burg edited this page Mar 29, 2014 · 9 revisions

For Timelapse to handle more sources of nondeterminism, new inputs must be identified and captured/replayed. In general, follow these steps:

  1. Identify where the nondeterminism comes from, and its control flow.
  2. Rewrite the relevant code path to go through UserInputBridge, MemoizedDOMResult, ReplayableTimer, or other nondeterminism shim.
  3. Edit WebInputs.json to create a new input storage class.
  4. Add code paths that store or load the input during capture/replay.

Identifying nondeterminism sources and code paths

User nondeterminism

If the nondeterminism happens because of something the user did, then this task is fairly easy. All user input is routed from UIProcess to WebProcess, so somewhere inside WebKit2 there is a message from UIProcess --> WebProcess that represents the user input. For example, to find out how page reloads start, I searched for "reload" in files named "*.messages.in". (The latter restricts the search to IDL-like protocol definitions that define inter-process communication.) This finds the following result

WebProcess.messages.in:77
    Reload(bool reloadFromOrigin, WebKit::SandboxExtension::Handle sandboxExtensionHandle)

Messages are implemented by C++ classes of the same name. So, I then looked at WebPage.cpp:reload().

void WebPage::reload(bool reloadFromOrigin, const SandboxExtension::Handle& sandboxExtensionHandle)
{
    SendStopResponsivenessTimer stopper(this);

    m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle);
    m_mainFrame->coreFrame()->loader().reload(reloadFromOrigin);
}

In this case, it looks like reloads are triggered by calling FrameLoader::reload() on the main frame's loader. We need to change the last line of the method to go through a proxy object that is aware of the current replay state.

Timer Nondeterminism

TODO

Network Nondeterminism

TODO

Nondeterministic APIs

TODO

Redirect code paths to go through shims

Timelapse's shims are part of the public WebCore API that is called by WebKit2's WebProcess code. So, we need to add a new method to UserInputBridge representing the nondeterministic user signal. In this case, we add the new shim entry point:

in UserInputBridge.h:

    void reloadFrame(Frame* frame, bool endToEndReload, InputSource = InputSource::User);

in UserInputBridge.cpp:

void UserInputBridge::reloadFrame(Frame* frame, bool endToEndReload, InputSource)
{
    frame->loader().reload(endToEndReload);
}

Then call it from WebKit2's WebPage:

void WebPage::reload(bool reloadFromOrigin, const SandboxExtension::Handle& sandboxExtensionHandle)
{
    SendStopResponsivenessTimer stopper(this);

    m_sandboxExtensionTracker.beginLoad(m_mainFrame.get(), sandboxExtensionHandle);
    corePage()->userInputBridge()->reloadFrame(m_mainFrame->coreFrame(), reloadFromOrigin);
}

The input bridge is associated with Page instances, rather than individual frames. Thus, the shim method takes the specific frame as an argument.

As the shim is part of the public WebCore API, one last step is to add the new method's signature to the exported symbols list. You can find the mangled symbol name by compiling and looking at the linker error. For the above message the mangled name is __ZN7WebCore15UserInputBridge11reloadFrameEPNS_5FrameEbb. Once you have found the mangled name, add it to WebCore.exp.in, making sure to maintain sorted order.