Upgrade to Pro — share decks privately, control downloads, hide ads and more …

One Process At A TIme, e10s

One Process At A TIme, e10s

From converting page info to e10s to remove unsafe CPOWs, making light weight web themes work again and general struggles in the land of e10s.

Jimmy Wang

July 30, 2015
Tweet

Other Decks in Technology

Transcript

  1. One Process
    Cause it’s multiprocess. Get it eh?
    Jimmy Wang
    At A Time,
    e10s

    View Slide

  2. Intro
    Hi I'm Jimmy,
    I'm an intern on the desktop
    firefox e10s team.

    I enjoy long walks on the beach,
    and reliable & fast wifi
    connections.
    My mentor is Neil Deakin.
    My manger is Dave Townsend

    View Slide

  3. Frontend Firefox Code
    1. Browser UI (chrome)
    Ex. address bar, bookmarks, page info
    2. Web Content code
    Ex. Query web page to fetch pageinfo data

    View Slide

  4. View Slide

  5. What is Electrolysis?
    (“e10s” for short)
    •Separate chrome code from content code
    • chrome process [parent process]
    • content process [child process]
    • First iteration
    •all browser tabs run in the same content process
    • Future iteration
    •We expect to have more than one content process.

    View Slide

  6. Benefits of e10s
    Show me the money
    •Benefits:
    •UI doesn't hang.
    •Better performance
    •2 processes can do more since they're
    asynchronous and operations don't block.
    •If a tab crashes it doesn't crash the browser

    View Slide

  7. Message Manager
    One Tab
    Web page
    Browser Window

    View Slide

  8. What does this mean for
    firefox code?

    View Slide

  9. So why does it break
    everything?
    •Existing code behaviour:
    •Browser UI accessing web page content and using
    it like it was in the same process.

    •Some code just doesn't work in the content process.
    •Ex. setting prefs, downloading files

    View Slide

  10. Migrating to e10s
    1. CPOWs (Cross-process object wrappers)
    •A way to allow code in the parent process to access
    objects in the child process synchronously.
    •Created using the message manager.
    •The parent waits for a response from the child.
    gWindow =
    window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
    gDocument = gWindow.document; //also a CPOW. CPOWs can only
    beget more CPOWs
    gDocument.title
    if (gDocument instanceof ImageDocument)
    imageText = item.title || item.alt;
    In Chrome process

    View Slide

  11. Migrating to e10s
    2. Eventually removing CPOWs and writing content &
    chrome scripts. And having them communicate via
    messages.
    Because every property access sends a message, CPOWs
    can be slow to use. There is no caching, so 1,000
    accesses to the same property will send 1,000 messages.
    - Bill McCloskey
    CPOWs are easier than converting existing
    code to be asynchronous

    View Slide

  12. Talos (Windows)
    Test Win or Loss
    Tp5 Responsiveness 90% improvement
    Tp5 Scroll 13% improvement
    SVG 22% improvement
    SVG Transparent 54% improvement
    Simple Paint (ts_paint) 55% improvement
    Tpaint, tart, canvasmark, webgl 2-3% improvement
    A11y 8% regression
    Tp5 Optimized 9% regression
    Other tests Within margin of error or no data*
    SEE META BUG 1144120 FOR CURRENT DATA

    *kraken, v8_7, dromaeo_css, dromaeo_dom, sessionrestore,
    sessionrestore_no_auto_restore
    Credit: Jim Mathies (IRC: jimm)

    View Slide

  13. Bug 1040947
    [e10s] Opening page info from a remote tab is sluggish

    View Slide

  14. Why so slow?
    • Due to unsafe CPOW usage

    View Slide

  15. Culprit
    browser/base/content/pageinfo/pageInfo.js
    function onLoadPageInfo()
    {
    gWindow = window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
    gDocument = gWindow.document;
    ... //call other functions. Do stuff
    }
    function goThroughFrames(gDocument, gWindow)
    {
    gFrameList.push(gDocument);
    if (gWindow && gWindow.frames.length > 0) {
    var num = aWindow.frames.length;
    for (var i = 0; i < num; i++)
    goThroughFrames(gWindow.frames[i].document, gWindow.frames[i]);
    }
    }
    function processFrames()
    {
    if (gFrameList.length) {
    var doc = gFrameList[0];
    onProcessFrame.forEach(function(func) { func(doc); });
    var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
    gFrameList.shift();
    }
    }

    View Slide

  16. Culprit
    browser/base/content/pageinfo/pageInfo.js
    function onLoadPageInfo()
    {
    gWindow = window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
    gDocument = gWindow.document;
    ... //call other functions. Do stuff
    }
    function goThroughFrames(gDocument, gWindow)
    {
    gFrameList.push(gDocument);
    if (gWindow && gWindow.frames.length > 0) {
    var num = aWindow.frames.length;
    for (var i = 0; i < num; i++)
    goThroughFrames(gWindow.frames[i].document, gWindow.frames[i]);
    }
    }
    function processFrames()
    {
    if (gFrameList.length) {
    var doc = gFrameList[0];
    onProcessFrame.forEach(function(func) { func(doc); });
    var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
    gFrameList.shift();
    }
    }

    View Slide

  17. Culprit
    It's attempting to walk the DOM tree of the
    contentDocument CPOW to get information
    about various scripts, images, etc.
    unsafe usage of CPOWs.
    var iterator = doc.createTreeWalker(doc,
    NodeFilter.SHOW_ELEMENT, grabAll);
    190 unsafe CPOW warnings are from this alone!

    View Slide

  18. Why are some CPOW usage unsafe?
    Why are they slower?
    Both chrome process get blocked by CPOW usage.
    Especially noticeable for dom walker
    var iterator = doc.createTreeWalker(doc,
    NodeFilter.SHOW_ELEMENT, grabAll);
    Instead of asking for the whole book,
    you’re asking for page by page - Mike
    Conley

    View Slide

  19. Read more about Unsafe CPOWs
    http:/
    /mikeconley.ca/blog/2015/02/17/on-
    unsafe-cpow-usage-in-firefox-desktop-and-
    why-is-my-nightly-so-sluggish-with-e10s-
    enabled/
    http:/
    /blog.lassey.us/2015/01/10/unsafe-cpow-
    usage/

    View Slide

  20. How Do We Fix This?
    Break pageinfo.js into a content process
    script and chrome process script.
    •Do the fetching of the web page
    •metadata,
    •media elements,
    •feeds,
    •info for permissions + security tabs
    in the content script.
    Send the info back to the parent process.

    View Slide

  21. Chrome process
    browser/base/content/pageinfo/pageInfo.js
    Uses:
    browser/base/content/pageinfo/feeds.js
    browser/base/content/pageinfo/security.js
    browser/base/content/pageinfo/permissions.js
    Content process
    browser/base/content/content.js
    (already loaded on content page whenever a tab is opened)

    View Slide

  22. browser/base/content/pageinfo/pageInfo.js
    function loadPageInfo(frameOuterWindowID)
    {
    let mm = window.opener.gBrowser.selectedBrowser.messageManager;
    mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
    frameOuterWindowID: frameOuterWindowID});
    mm.addMessageListener("PageInfo:data", function onmessage(message) {
    mm.removeMessageListener("PageInfo:data", onmessage);
    pageInfoData = message.data;
    let docInfo = pageInfoData.docInfo;
    let windowInfo = pageInfoData.windowInfo;
    let uri = makeURI(docInfo.documentURIObject.spec,
    docInfo.documentURIObject.originCharset);
    gDocInfo = docInfo;
    var titleFormat = windowInfo.isTopWindow ? "pageInfo.frame.title"
    : "pageInfo.page.title";
    document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
    document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
    makeGeneralTab(pageInfoData.metaViewRows, docInfo);
    initFeedTab(pageInfoData.feeds);
    onLoadPermission(uri);
    securityOnLoad(uri, windowInfo);
    });
    }

    View Slide

  23. browser/base/content/pageinfo/pageInfo.js
    function loadPageInfo(frameOuterWindowID)
    {
    let mm = window.opener.gBrowser.selectedBrowser.messageManager;
    mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
    frameOuterWindowID: frameOuterWindowID});
    mm.addMessageListener("PageInfo:data", function onmessage(message) {
    mm.removeMessageListener("PageInfo:data", onmessage);
    pageInfoData = message.data;
    let docInfo = pageInfoData.docInfo;
    let windowInfo = pageInfoData.windowInfo;
    let uri = makeURI(docInfo.documentURIObject.spec,
    docInfo.documentURIObject.originCharset);
    gDocInfo = docInfo;
    var titleFormat = windowInfo.isTopWindow ? "pageInfo.frame.title"
    : "pageInfo.page.title";
    document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
    document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
    makeGeneralTab(pageInfoData.metaViewRows, docInfo);
    initFeedTab(pageInfoData.feeds);
    onLoadPermission(uri);
    securityOnLoad(uri, windowInfo);
    });
    }

    View Slide

  24. browser/base/content/content.js
    let pageInfoListener = {
    init: function() {
    addMessageListener("PageInfo:getData", this, false, true);
    },
    receiveMessage: function(message) {
    let frameOuterWindowID = message.data.frameOuterWindowID;
    // If inside frame then get the frame's window and document.
    if (frameOuterWindowID) {
    this.window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
    this.document = this.window.document;
    }
    else {
    this.document = content.document;
    this.window = content.window;
    }
    let pageInfoData = {metaViewRows: this.getMetaInfo(), docInfo: this.getDocumentInfo(),
    feeds: this.getFeedsInfo(), windowInfo: this.getWindowInfo()};
    sendAsyncMessage("PageInfo:data", pageInfoData);
    // Separate step so page info dialog isn't blank while waiting for this to finish.
    this.getMediaInfo();
    // Send the message after all the media elements have been walked through.
    let pageInfoMediaData = {imageViewRows: this.imageViewRows};
    sendAsyncMessage("PageInfo:mediaData", pageInfoMediaData);
    },
    }
    pageInfoListener.init();

    View Slide

  25. browser/base/content/content.js
    let pageInfoListener = {
    init: function() {
    addMessageListener("PageInfo:getData", this, false, true);
    },
    receiveMessage: function(message) {
    let frameOuterWindowID = message.data.frameOuterWindowID;
    // If inside frame then get the frame's window and document.
    if (frameOuterWindowID) {
    this.window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
    this.document = this.window.document;
    }
    else {
    this.document = content.document;
    this.window = content.window;
    }
    let pageInfoData = {metaViewRows: this.getMetaInfo(), docInfo: this.getDocumentInfo(),
    feeds: this.getFeedsInfo(), windowInfo: this.getWindowInfo()};
    sendAsyncMessage("PageInfo:data", pageInfoData);
    // Separate step so page info dialog isn't blank while waiting for this to finish.
    this.getMediaInfo();
    // Send the message after all the media elements have been walked through.
    let pageInfoMediaData = {imageViewRows: this.imageViewRows};
    sendAsyncMessage("PageInfo:mediaData", pageInfoMediaData);
    },
    }
    pageInfoListener.init();

    View Slide

  26. •Separate chrome process code from content process
    code
    •Build up data and send it to parent in a message.
    •Retains same functionality. but does it safely now.
    Takeaway

    View Slide

  27. Bug 653065
    Make the lightweight theme web installer ready for e10s

    View Slide

  28. Why doesn’t it work?
    Light Weight Web Themes
    were installed by having our browser listen in the
    chrome process
    for these event from the theme web page
    “InstallBrowserTheme"
    "PreviewBrowserTheme"
    "ResetBrowserThemePreview"

    View Slide

  29. How Do We Fix This?
    The chrome process will not have access to the content
    process so it can't listen for events.
    Instead you should:
    1. Listen for the event in the content process
    2. Send a message to the chrome process letting them
    know about the type of event

    View Slide

  30. How can help?
    You can make a difference.
    Fill the void
    you
    you

    View Slide

  31. Let’s ship e10s faster
    Test addons
    arewee10syet.com
    (blue untested) Report their status in e10s
    so we know which ones still don't work with e10s
    When filing a new bug
    •please add the flag
    •tracking-e10s:?
    •place 'e10s' in the bug title
    •so that it shows up in the team's weekly triage bug list.
    Work on e10s Bugs.
    Frontend bugs: https:/
    /bugzilla.mozilla.org/show_bug.cgi?id=core-e10s
    Backend bugs: https:/
    /bugzilla.mozilla.org/show_bug.cgi?id=fxe10s
    IRC: #e10s on irc.mozilla.org
    People involved in e10s: https:/
    /wiki.mozilla.org/Electrolysis#People

    View Slide

  32. Learn more about e10s
    https:/
    /wiki.mozilla.org/Electrolysis
    Blog posts. Higher level overview (last updated 2013)
    https:/
    /billmccloskey.wordpress.com/2013/12/05/multiprocess-
    firefox/

    https:/
    /dutherenverseauborddelatable.wordpress.com/2013/10/09/
    making-firefox-feel-as-fast-as-its-benchmarks-part-2-towards-
    multi-process/
    documentation on e10s
    https:/
    /developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox

    View Slide

  33. Closing Remarks
    Neil
    (my mentor)
    Jimmy
    (e10s intern)

    View Slide

  34. Toronto Interns!
    E10s Team
    (The dream team that’s
    making e10s a reality)

    View Slide

  35. jimicy.com
    @jim_icy
    Questions?
    lvl 100 axe master/e10s intern
    Tweet me @jim_icy
    #airmozilla on irc.mozilla.org.
    Include jimicy: in msg
    IRC: jimicy

    View Slide