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.

33defd886a981c53de771d8bb6b7032f?s=128

Jimmy Wang

July 30, 2015
Tweet

Transcript

  1. One Process Cause it’s multiprocess. Get it eh? Jimmy Wang

    At A Time, e10s
  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
  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
  4. None
  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.
  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
  7. Message Manager One Tab Web page Browser Window

  8. What does this mean for firefox code?

  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
  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
  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
  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)
  13. Bug 1040947 [e10s] Opening page info from a remote tab

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

  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(); } }
  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(); } }
  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!
  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
  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/
  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.
  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)
  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); }); }
  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); }); }
  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();
  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();
  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
  27. Bug 653065 Make the lightweight theme web installer ready for

    e10s
  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"
  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
  30. How can help? You can make a difference. Fill the

    void you you
  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
  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
  33. Closing Remarks Neil (my mentor) Jimmy (e10s intern)

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

    a reality)
  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