Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 29 Oct 2014 16:49:04 -0400
changeset 212953 80e18ff7c7b2da4caa773eb9931c0bbbcc2d321d
parent 212915 7b6a11203bec910c81d39d5f79e1afd8f662789f (current diff)
parent 212952 a542bd0c39e734ca47df7bf608df2eb8a64c8f4e (diff)
child 212969 111985bd4b217f2e8caf43d6b86052f8dbd10a22
child 213016 8512443e6e4f2c266ade1023c46a66b37c5e5832
child 213081 0746593b7cde2f7e5dc9c82193946f0fab7939fa
push id27736
push userryanvm@gmail.com
push dateWed, 29 Oct 2014 20:49:13 +0000
treeherdermozilla-central@80e18ff7c7b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
80e18ff7c7b2 / 36.0a1 / 20141030030218 / files
nightly linux64
80e18ff7c7b2 / 36.0a1 / 20141030030218 / files
nightly mac
80e18ff7c7b2 / 36.0a1 / 20141030030218 / files
nightly win32
80e18ff7c7b2 / 36.0a1 / 20141030030218 / files
nightly win64
80e18ff7c7b2 / 36.0a1 / 20141030030218 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
docshell/base/nsDocShell.cpp
dom/base/Navigator.cpp
dom/media/test/eme.js
dom/media/test/test_eme_canvas_blocked.html
dom/media/test/test_eme_playback.html
dom/media/test/test_eme_stream_capture_blocked.html
dom/plugins/test/mochitest/test_npruntime_npnsetexception.html
testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -1,26 +1,26 @@
 [DEFAULT]
-skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
 subsuite = devtools
 support-files =
   addon1.xpi
   addon2.xpi
   addon3.xpi
   addon4.xpi
   addon5.xpi
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_blackboxing_blackboxme.js
   code_blackboxing_one.js
   code_blackboxing_three.js
   code_blackboxing_two.js
   code_breakpoints-break-on-last-line-of-script-on-reload.js
   code_breakpoints-other-tabs.js
+  code_frame-script.js
   code_function-search-01.js
   code_function-search-02.js
   code_function-search-03.js
   code_location-changes.js
   code_math.js
   code_math.map
   code_math.min.js
   code_math_bogus_map.js
@@ -93,238 +93,457 @@ support-files =
   doc_watch-expressions.html
   doc_watch-expression-button.html
   doc_with-frame.html
   head.js
   sjs_random-javascript.sjs
   testactors.js
 
 [browser_dbg_aaa_run_first_leaktest.js]
+skip-if = e10s
 [browser_dbg_addonactor.js]
+skip-if = e10s
 [browser_dbg_addon-sources.js]
+skip-if = e10s
 [browser_dbg_addon-modules.js]
+skip-if = e10s
 [browser_dbg_addon-modules-unpacked.js]
+skip-if = e10s
 [browser_dbg_addon-panels.js]
+skip-if = e10s
 [browser_dbg_addon-console.js]
-skip-if = os == 'win' # bug 1005274
+skip-if = e10s || os == 'win' # bug 1005274
 [browser_dbg_auto-pretty-print-01.js]
+skip-if = e10s
 [browser_dbg_auto-pretty-print-02.js]
+skip-if = e10s
 [browser_dbg_bfcache.js]
+skip-if = e10s
 [browser_dbg_blackboxing-01.js]
+skip-if = e10s && debug
 [browser_dbg_blackboxing-02.js]
+skip-if = e10s && debug
 [browser_dbg_blackboxing-03.js]
+skip-if = e10s && debug
 [browser_dbg_blackboxing-04.js]
+skip-if = e10s && debug
 [browser_dbg_blackboxing-05.js]
+skip-if = e10s && debug
 [browser_dbg_blackboxing-06.js]
+skip-if = e10s && debug
 [browser_dbg_breadcrumbs-access.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-01.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-02.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-03.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-04.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-05.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-06.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-07.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-08.js]
+skip-if = e10s
 [browser_dbg_break-on-dom-event-01.js]
-skip-if = os == "mac" || e10s # Bug 895426
+skip-if = e10s || os == "mac" || e10s # Bug 895426
 [browser_dbg_break-on-dom-event-02.js]
+skip-if = e10s
 [browser_dbg_breakpoints-actual-location.js]
+skip-if = e10s
 [browser_dbg_breakpoints-actual-location2.js]
+skip-if = e10s
 [browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
+skip-if = e10s
 [browser_dbg_breakpoints-button-01.js]
+skip-if = e10s
 [browser_dbg_breakpoints-button-02.js]
+skip-if = e10s
 [browser_dbg_breakpoints-contextmenu-add.js]
+skip-if = e10s
 [browser_dbg_breakpoints-contextmenu.js]
+skip-if = e10s
 [browser_dbg_breakpoints-disabled-reload.js]
+skip-if = e10s
 [browser_dbg_breakpoints-editor.js]
+skip-if = e10s
 [browser_dbg_breakpoints-highlight.js]
+skip-if = e10s
 [browser_dbg_breakpoints-new-script.js]
+skip-if = e10s
 [browser_dbg_breakpoints-other-tabs.js]
+skip-if = e10s
 [browser_dbg_breakpoints-pane.js]
+skip-if = e10s
 [browser_dbg_breakpoints-reload.js]
+skip-if = e10s
 [browser_dbg_chrome-create.js]
+skip-if = e10s
 [browser_dbg_chrome-debugging.js]
+skip-if = e10s
 [browser_dbg_clean-exit-window.js]
 skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
+skip-if = e10s
 [browser_dbg_closure-inspection.js]
+skip-if = e10s
 [browser_dbg_cmd-blackbox.js]
+skip-if = e10s
 [browser_dbg_cmd-break.js]
+skip-if = e10s
 [browser_dbg_cmd-dbg.js]
+skip-if = e10s
 [browser_dbg_conditional-breakpoints-01.js]
+skip-if = e10s
 [browser_dbg_conditional-breakpoints-02.js]
+skip-if = e10s
 [browser_dbg_conditional-breakpoints-03.js]
+skip-if = e10s
 [browser_dbg_conditional-breakpoints-04.js]
+skip-if = e10s
 [browser_dbg_server-conditional-bp-01.js]
+skip-if = e10s
 [browser_dbg_server-conditional-bp-02.js]
+skip-if = e10s
 [browser_dbg_server-conditional-bp-03.js]
+skip-if = e10s
 [browser_dbg_server-conditional-bp-04.js]
+skip-if = e10s
 [browser_dbg_controller-evaluate-01.js]
+skip-if = e10s
 [browser_dbg_controller-evaluate-02.js]
+skip-if = e10s
 [browser_dbg_debugger-statement.js]
+skip-if = e10s
 [browser_dbg_editor-contextmenu.js]
+skip-if = e10s
 [browser_dbg_editor-mode.js]
+skip-if = e10s
 [browser_dbg_event-listeners-01.js]
+skip-if = e10s
 [browser_dbg_event-listeners-02.js]
+skip-if = e10s
 [browser_dbg_event-listeners-03.js]
+skip-if = e10s
 [browser_dbg_file-reload.js]
+skip-if = e10s
 [browser_dbg_function-display-name.js]
+skip-if = e10s
 [browser_dbg_global-method-override.js]
+skip-if = e10s
 [browser_dbg_globalactor.js]
+skip-if = e10s
 [browser_dbg_hit-counts-01.js]
+skip-if = e10s
 [browser_dbg_hit-counts-02.js]
+skip-if = e10s
 [browser_dbg_host-layout.js]
+skip-if = e10s
 [browser_dbg_iframes.js]
+skip-if = e10s
 [browser_dbg_instruments-pane-collapse.js]
+skip-if = e10s
 [browser_dbg_interrupts.js]
+skip-if = e10s
 [browser_dbg_listaddons.js]
+skip-if = e10s
 [browser_dbg_listtabs-01.js]
+skip-if = e10s
 [browser_dbg_listtabs-02.js]
+skip-if = e10s
 [browser_dbg_listtabs-03.js]
+skip-if = e10s
 [browser_dbg_location-changes-01-simple.js]
+skip-if = e10s
 [browser_dbg_location-changes-02-blank.js]
+skip-if = e10s
 [browser_dbg_location-changes-03-new.js]
+skip-if = e10s
 [browser_dbg_location-changes-04-breakpoint.js]
+skip-if = e10s
 [browser_dbg_multiple-windows.js]
+skip-if = e10s
 [browser_dbg_navigation.js]
+skip-if = e10s
 [browser_dbg_no-page-sources.js]
+skip-if = e10s
 [browser_dbg_on-pause-highlight.js]
+skip-if = e10s
 [browser_dbg_on-pause-raise.js]
-skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
+skip-if = e10s || os == "linux" || e10s # Bug 888811 & bug 891176
 [browser_dbg_optimized-out-vars.js]
+skip-if = e10s
 [browser_dbg_panel-size.js]
+skip-if = e10s
 [browser_dbg_parser-01.js]
+skip-if = e10s
 [browser_dbg_parser-02.js]
+skip-if = e10s
 [browser_dbg_parser-03.js]
+skip-if = e10s
 [browser_dbg_parser-04.js]
+skip-if = e10s
 [browser_dbg_parser-05.js]
+skip-if = e10s
 [browser_dbg_parser-06.js]
+skip-if = e10s
 [browser_dbg_parser-07.js]
+skip-if = e10s
 [browser_dbg_parser-08.js]
+skip-if = e10s
 [browser_dbg_parser-09.js]
+skip-if = e10s
 [browser_dbg_parser-10.js]
+skip-if = e10s
 [browser_dbg_pause-exceptions-01.js]
+skip-if = e10s
 [browser_dbg_pause-exceptions-02.js]
+skip-if = e10s
 [browser_dbg_pause-resume.js]
+skip-if = e10s
 [browser_dbg_pause-warning.js]
+skip-if = e10s
 [browser_dbg_paused-keybindings.js]
+skip-if = e10s
 [browser_dbg_pretty-print-01.js]
+skip-if = e10s
 [browser_dbg_pretty-print-02.js]
+skip-if = e10s
 [browser_dbg_pretty-print-03.js]
+skip-if = e10s
 [browser_dbg_pretty-print-04.js]
+skip-if = e10s
 [browser_dbg_pretty-print-05.js]
+skip-if = e10s
 [browser_dbg_pretty-print-06.js]
+skip-if = e10s
 [browser_dbg_pretty-print-07.js]
+skip-if = e10s
 [browser_dbg_pretty-print-08.js]
+skip-if = e10s
 [browser_dbg_pretty-print-09.js]
+skip-if = e10s
 [browser_dbg_pretty-print-10.js]
+skip-if = e10s
 [browser_dbg_pretty-print-11.js]
+skip-if = e10s
 [browser_dbg_pretty-print-12.js]
+skip-if = e10s
 [browser_dbg_pretty-print-13.js]
+skip-if = e10s
 [browser_dbg_pretty-print-on-paused.js]
+skip-if = e10s
 [browser_dbg_progress-listener-bug.js]
+skip-if = e10s
 [browser_dbg_reload-preferred-script-01.js]
+skip-if = e10s
 [browser_dbg_reload-preferred-script-02.js]
+skip-if = e10s
 [browser_dbg_reload-preferred-script-03.js]
+skip-if = e10s
 [browser_dbg_reload-same-script.js]
+skip-if = e10s
 [browser_dbg_scripts-switching-01.js]
+skip-if = e10s
 [browser_dbg_scripts-switching-02.js]
+skip-if = e10s
 [browser_dbg_scripts-switching-03.js]
+skip-if = e10s
 [browser_dbg_search-autofill-identifier.js]
+skip-if = e10s
 [browser_dbg_search-basic-01.js]
+skip-if = e10s
 [browser_dbg_search-basic-02.js]
+skip-if = e10s
 [browser_dbg_search-basic-03.js]
+skip-if = e10s
 [browser_dbg_search-basic-04.js]
+skip-if = e10s
 [browser_dbg_search-global-01.js]
+skip-if = e10s
 [browser_dbg_search-global-02.js]
+skip-if = e10s
 [browser_dbg_search-global-03.js]
+skip-if = e10s
 [browser_dbg_search-global-04.js]
+skip-if = e10s
 [browser_dbg_search-global-05.js]
+skip-if = e10s
 [browser_dbg_search-global-06.js]
+skip-if = e10s
 [browser_dbg_search-popup-jank.js]
+skip-if = e10s
 [browser_dbg_search-sources-01.js]
+skip-if = e10s
 [browser_dbg_search-sources-02.js]
+skip-if = e10s
 [browser_dbg_search-sources-03.js]
+skip-if = e10s
 [browser_dbg_search-symbols.js]
+skip-if = e10s
 [browser_dbg_searchbox-help-popup-01.js]
+skip-if = e10s
 [browser_dbg_searchbox-help-popup-02.js]
+skip-if = e10s
 [browser_dbg_searchbox-parse.js]
+skip-if = e10s
 [browser_dbg_source-maps-01.js]
+skip-if = e10s
 [browser_dbg_source-maps-02.js]
+skip-if = e10s
 [browser_dbg_source-maps-03.js]
+skip-if = e10s
 [browser_dbg_source-maps-04.js]
+skip-if = e10s
 [browser_dbg_sources-cache.js]
+skip-if = e10s
 [browser_dbg_sources-labels.js]
+skip-if = e10s
 [browser_dbg_sources-sorting.js]
+skip-if = e10s
 [browser_dbg_split-console-paused-reload.js]
+skip-if = e10s
 [browser_dbg_stack-01.js]
+skip-if = e10s
 [browser_dbg_stack-02.js]
+skip-if = e10s
 [browser_dbg_stack-03.js]
+skip-if = e10s
 [browser_dbg_stack-04.js]
+skip-if = e10s
 [browser_dbg_stack-05.js]
+skip-if = e10s
 [browser_dbg_stack-06.js]
+skip-if = e10s
 [browser_dbg_stack-07.js]
+skip-if = e10s
 [browser_dbg_step-out.js]
+skip-if = e10s
 [browser_dbg_tabactor-01.js]
+skip-if = e10s
 [browser_dbg_tabactor-02.js]
+skip-if = e10s
 [browser_dbg_terminate-on-tab-close.js]
+skip-if = e10s
 [browser_dbg_tracing-01.js]
+skip-if = e10s
 [browser_dbg_tracing-02.js]
+skip-if = e10s
 [browser_dbg_tracing-03.js]
+skip-if = e10s
 [browser_dbg_tracing-04.js]
+skip-if = e10s
 [browser_dbg_tracing-05.js]
+skip-if = e10s
 [browser_dbg_tracing-06.js]
+skip-if = e10s
 [browser_dbg_tracing-07.js]
+skip-if = e10s
 [browser_dbg_tracing-08.js]
+skip-if = e10s
 [browser_dbg_variables-view-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-03.js]
+skip-if = e10s
 [browser_dbg_variables-view-04.js]
+skip-if = e10s
 [browser_dbg_variables-view-05.js]
+skip-if = e10s
 [browser_dbg_variables-view-06.js]
+skip-if = e10s
 [browser_dbg_variables-view-accessibility.js]
+skip-if = e10s
 [browser_dbg_variables-view-data.js]
+skip-if = e10s
 [browser_dbg_variables-view-edit-cancel.js]
+skip-if = e10s
 [browser_dbg_variables-view-edit-click.js]
-skip-if = (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
+skip-if = e10s || (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
 [browser_dbg_variables-view-edit-getset-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-edit-getset-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-edit-value.js]
+skip-if = e10s
 [browser_dbg_variables-view-edit-watch.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-03.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-04.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-05.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-pref.js]
+skip-if = e10s
 [browser_dbg_variables-view-filter-searchbox.js]
+skip-if = e10s
 [browser_dbg_variables-view-frame-parameters-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-frame-parameters-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-frame-parameters-03.js]
+skip-if = e10s
 [browser_dbg_variables-view-frame-with.js]
+skip-if = e10s
 [browser_dbg_variables-view-frozen-sealed-nonext.js]
-skip-if = buildapp == 'mulet'
+skip-if = e10s || buildapp == 'mulet'
 [browser_dbg_variables-view-hide-non-enums.js]
+skip-if = e10s
 [browser_dbg_variables-view-large-array-buffer.js]
+skip-if = e10s
 [browser_dbg_variables-view-override-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-override-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-03.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-04.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-05.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-06.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-07.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-08.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-09.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-10.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-11.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-12.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-13.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-14.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-15.js]
+skip-if = e10s
 [browser_dbg_variables-view-popup-16.js]
+skip-if = e10s
 [browser_dbg_variables-view-reexpand-01.js]
+skip-if = e10s
 [browser_dbg_variables-view-reexpand-02.js]
+skip-if = e10s
 [browser_dbg_variables-view-reexpand-03.js]
+skip-if = e10s
 [browser_dbg_variables-view-webidl.js]
+skip-if = e10s
 [browser_dbg_watch-expressions-01.js]
+skip-if = e10s
 [browser_dbg_watch-expressions-02.js]
+skip-if = e10s
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
@@ -2,22 +2,21 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that if we black box a source and then refresh, it is still black boxed.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
 
     waitForSourceShown(gPanel, ".coffee")
       .then(testBlackBoxSource)
       .then(testBlackBoxReload)
       .then(() => closeDebuggerAndFinish(gPanel))
       .then(null, aError => {
@@ -43,12 +42,11 @@ function testBlackBoxReload() {
     ok(bbButton.checked, "Should still be black boxed.");
     ok(selectedSource.classList.contains("black-boxed"),
       "'black-boxed' class should still be applied");
   });
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
@@ -4,23 +4,22 @@
 /**
  * Test that black boxed frames are compressed into a single frame on the stack
  * view.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
 const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gFrames;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     waitForSourceShown(gPanel, BLACKBOXME_URL)
       .then(testBlackBoxSource)
       .then(testBlackBoxStack)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
@@ -39,21 +38,18 @@ function testBlackBoxSource() {
 function testBlackBoxStack() {
   let finished = waitForSourceAndCaretAndScopes(gPanel, ".html", 21).then(() => {
     is(gFrames.itemCount, 3,
       "Should only get 3 frames.");
     is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
       "And one of them should be the combined black boxed frames.");
   });
 
-  // Spin the event loop before causing the debuggee to pause, to allow
-  // this function to return first.
-  executeSoon(() => gDebuggee.runTest());
+  callInTab(gTab, "runTest");
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gFrames = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
@@ -4,36 +4,35 @@
 /**
  * Test that black boxed frames are compressed into a single frame on the stack
  * view when we are already paused.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
 const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gFrames;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
       .then(testBlackBoxStack)
       .then(testBlackBoxSource)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.runTest();
+    callInTab(gTab, "runTest");
   });
 }
 
 function testBlackBoxStack() {
   is(gFrames.itemCount, 6,
     "Should get 6 frames.");
   is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 0,
     "And none of them are black boxed.");
@@ -47,13 +46,12 @@ function testBlackBoxSource() {
       "Should only get 3 frames.");
     is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
       "And one of them should be the combined black boxed frames.");
   });
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gFrames = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
@@ -4,23 +4,22 @@
 /**
  * Test that we get a stack frame for each black boxed source, not a single one
  * for all of them.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
 const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gFrames;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gFrames = gDebugger.DebuggerView.StackFrames;
 
     waitForSourceShown(gPanel, BLACKBOXME_URL)
       .then(blackBoxSources)
       .then(testBlackBoxStack)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
@@ -41,21 +40,18 @@ function blackBoxSources() {
 function testBlackBoxStack() {
   let finished = waitForSourceAndCaretAndScopes(gPanel, ".html", 21).then(() => {
     is(gFrames.itemCount, 4,
       "Should get 4 frames (one -> two -> three -> doDebuggerStatement).");
     is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 3,
       "And 'one', 'two', and 'three' should each have their own black boxed frame.");
   });
 
-  // Spin the event loop before causing the debuggee to pause, to allow
-  // this function to return first.
-  executeSoon(() => gDebuggee.one());
+  callInTab(gTab, "one");
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gFrames = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-05.js
@@ -3,23 +3,22 @@
 
 /**
  * Test that a "this source is blackboxed" message is shown when necessary
  * and can be properly dismissed.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gDeck;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gDeck = gDebugger.document.getElementById("editor-deck");
 
     waitForSourceShown(gPanel, ".coffee")
       .then(testSourceEditorShown)
       .then(toggleBlackBoxing.bind(null, gPanel))
       .then(testBlackBoxMessageShown)
@@ -59,13 +58,12 @@ function testSourceEditorShownAgain() {
 }
 
 function getEditorBlackboxMessageButton() {
   return gDebugger.document.getElementById("black-boxed-message-button");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gDeck = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-06.js
@@ -3,35 +3,34 @@
 
 /**
  * Test that clicking the black box checkbox when paused doesn't re-select the
  * currently paused frame's source.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gSources;
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
 
     waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
       .then(testBlackBox)
       .then(() => resumeDebuggerThenCloseAndFinish(gPanel))
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
 
-    gDebuggee.runTest();
+    callInTab(gTab, "runTest");
   });
 }
 
 function testBlackBox() {
   const selectedUrl = gSources.selectedValue;
 
   let finished = waitForSourceShown(gPanel, "blackboxme.js").then(() => {
     const newSelectedUrl = gSources.selectedValue;
@@ -45,13 +44,12 @@ function testBlackBox() {
   });
 
   gSources.selectedIndex = 0;
   return finished;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gSources = null;
 });
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_frame-script.js
@@ -0,0 +1,9 @@
+"use strict";
+
+dump("Frame script loaded.\n");
+
+addMessageListener("test:call", function (message) {
+  dump("Calling function with name " + message.data + ".\n");
+
+  XPCNativeWrapper.unwrap(content)[message.data]();
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -23,16 +23,17 @@ let { DebuggerServer } = Cu.import("reso
 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 let EventEmitter = require("devtools/toolkit/event-emitter");
 const { promiseInvoke } = require("devtools/async-utils");
 let TargetFactory = devtools.TargetFactory;
 let Toolbox = devtools.Toolbox;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
+const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
 
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
 });
 
 // All tests are asynchronous.
 waitForExplicitFinish();
@@ -81,16 +82,19 @@ function addTab(aUrl, aWindow) {
   let deferred = promise.defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
   let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
   let linkedBrowser = tab.linkedBrowser;
 
+  info("Loading frame script with url " + FRAME_SCRIPT_URL + ".");
+  linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
+
   linkedBrowser.addEventListener("load", function onLoad() {
     linkedBrowser.removeEventListener("load", onLoad, true);
     info("Tab added and finished loading: " + aUrl);
     deferred.resolve(tab);
   }, true);
 
   return deferred.promise;
 }
@@ -931,9 +935,33 @@ function pushPrefs(...aPrefs) {
   SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
   return deferred.promise;
 }
 
 function popPrefs() {
   let deferred = promise.defer();
   SpecialPowers.popPrefEnv(deferred.resolve);
   return deferred.promise;
-}
\ No newline at end of file
+}
+
+function sendMessageToTab(tab, name, data, objects) {
+  info("Sending message with name " + name + " to tab.");
+
+  tab.linkedBrowser.messageManager.sendAsyncMessage(name, data, objects);
+}
+
+function waitForMessageFromTab(tab, name) {
+  info("Waiting for message with name " + name + " from tab.");
+
+  return new Promise(function (resolve) {
+    let messageManager = tab.linkedBrowser.messageManager;
+    messageManager.addMessageListener(name, function listener(message) {
+      messageManager.removeMessageListener(name, listener);
+      resolve(message);
+    });
+  });
+}
+
+function callInTab(tab, name) {
+  info("Calling function with name " + name + " in tab.");
+
+  sendMessageToTab(tab, "test:call", name);
+}
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -120,16 +120,24 @@ PluginContent.prototype = {
     if (tagMimetype == "") {
       tagMimetype = pluginElement.type;
     }
 
     if (this.isKnownPlugin(pluginElement)) {
       pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
       pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);
 
+      // Convert this from nsIPluginTag so it can be serialized.
+      let properties = ["name", "description", "filename", "version", "enabledState"];
+      let pluginTagCopy = {};
+      for (let prop of properties) {
+        pluginTagCopy[prop] = pluginTag[prop];
+      }
+      pluginTag = pluginTagCopy;
+
       permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
       fallbackType = pluginElement.defaultFallbackType;
       blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
       // Make state-softblocked == state-notblocked for our purposes,
       // they have the same UI. STATE_OUTDATED should not exist for plugin
       // items, but let's alias it anyway, just in case.
       if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
           blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3122,31 +3122,49 @@ nsDocShell::NotifyAsyncPanZoomStarted(co
         nsWeakPtr ref = iter.GetNext();
         nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
         if (obs) {
             obs->AsyncPanZoomStarted(aScrollPos);
         } else {
             mScrollObservers.RemoveElement(ref);
         }
     }
+
+    // Also notify child docshell
+    for (uint32_t i = 0; i < mChildList.Length(); ++i) {
+        nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
+        if (kid) {
+            nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
+            docShell->NotifyAsyncPanZoomStarted(aScrollPos);
+        }
+    }
 }
 
 void
 nsDocShell::NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
 {
     nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
     while (iter.HasMore()) {
         nsWeakPtr ref = iter.GetNext();
         nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
         if (obs) {
             obs->AsyncPanZoomStopped(aScrollPos);
         } else {
             mScrollObservers.RemoveElement(ref);
         }
     }
+
+    // Also notify child docshell
+    for (uint32_t i = 0; i < mChildList.Length(); ++i) {
+        nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
+        if (kid) {
+            nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
+            docShell->NotifyAsyncPanZoomStopped(aScrollPos);
+        }
+    }
 }
 
 NS_IMETHODIMP
 nsDocShell::NotifyScrollObservers()
 {
     nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
     while (iter.HasMore()) {
         nsWeakPtr ref = iter.GetNext();
@@ -10386,18 +10404,18 @@ nsDocShell::DoURILoad(nsIURI * aURI,
       }
     }
 
     //hack
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
     if (httpChannelInternal) {
       if (aForceAllowCookies) {
-        httpChannelInternal->SetForceAllowThirdPartyCookie(true);
-      } 
+        httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+      }
       if (aFirstParty) {
         httpChannelInternal->SetDocumentURI(aURI);
       } else {
         httpChannelInternal->SetDocumentURI(aReferrerURI);
       }
     }
 
     nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1094,17 +1094,20 @@ Navigator::SendBeacon(const nsAString& a
   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   if (!httpChannelInternal) {
     aRv.Throw(NS_ERROR_DOM_BAD_URI);
     return false;
   }
   bool isForeign = true;
   thirdPartyUtil->IsThirdPartyWindow(mWindow, uri, &isForeign);
-  httpChannelInternal->SetForceAllowThirdPartyCookie(!isForeign);
+  uint32_t thirdPartyFlags = isForeign ?
+    0 :
+    nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW;
+  httpChannelInternal->SetThirdPartyFlags(thirdPartyFlags);
 
   nsCString mimeType;
   if (!aData.IsNull()) {
     nsCOMPtr<nsIInputStream> in;
 
     if (aData.Value().IsString()) {
       nsCString stringData = NS_ConvertUTF16toUTF8(aData.Value().GetAsString());
       nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
--- a/dom/base/ThirdPartyUtil.cpp
+++ b/dom/base/ThirdPartyUtil.cpp
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ThirdPartyUtil.h"
 #include "nsNetUtil.h"
 #include "nsIServiceManager.h"
 #include "nsIHttpChannelInternal.h"
@@ -161,30 +163,57 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIC
                                     nsIURI* aURI,
                                     bool* aResult)
 {
   NS_ENSURE_ARG(aChannel);
   NS_ASSERTION(aResult, "null outparam pointer");
 
   nsresult rv;
   bool doForce = false;
+  bool checkWindowChain = true;
+  bool parentIsThird = false;
   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
     do_QueryInterface(aChannel);
   if (httpChannelInternal) {
-    rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
+    uint32_t flags;
+    rv = httpChannelInternal->GetThirdPartyFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    doForce = (flags & nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+
     // If aURI was not supplied, and we're forcing, then we're by definition
     // not foreign. If aURI was supplied, we still want to check whether it's
     // foreign with respect to the channel URI. (The forcing only applies to
     // whatever window hierarchy exists above the channel.)
     if (doForce && !aURI) {
       *aResult = false;
       return NS_OK;
     }
+
+    if (flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_THIRD_PARTY) {
+      // Check that the two PARENT_IS_{THIRD,SAME}_PARTY are mutually exclusive.
+      MOZ_ASSERT(!(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY));
+
+      // If we're not forcing and we know that the window chain of the channel
+      // is third party, then we know now that we're third party.
+      if (!doForce) {
+        *aResult = true;
+        return NS_OK;
+      }
+
+      checkWindowChain = false;
+      parentIsThird = true;
+    } else {
+      // In e10s, we can't check the parent chain in the parent, so we do so
+      // in the child and send the result to the parent.
+      // Note that we only check the window chain if neither
+      // THIRD_PARTY_PARENT_IS_* flag is set.
+      checkWindowChain = !(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY);
+      parentIsThird = false;
+    }
   }
 
   // Obtain the URI from the channel, and its base domain.
   nsCOMPtr<nsIURI> channelURI;
   aChannel->GetURI(getter_AddRefs(channelURI));
   NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
 
   nsCString channelDomain;
@@ -201,16 +230,22 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIC
 
     // If it's foreign, or we're forcing, we're done.
     if (result || doForce) {
       *aResult = result;
       return NS_OK;
     }
   }
 
+  // If we've already computed this in the child process, we're done.
+  if (!checkWindowChain) {
+    *aResult = parentIsThird;
+    return NS_OK;
+  }
+
   // Find the associated window and its parent window.
   nsCOMPtr<nsILoadContext> ctx;
   NS_QueryNotificationCallbacks(aChannel, ctx);
   if (!ctx) return NS_ERROR_INVALID_ARG;
 
   // If there is no window, the consumer kicking off the load didn't provide one
   // to the channel. This is limited to loads of certain types of resources. If
   // those loads require cookies, the forceAllowThirdPartyCookie property should
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -601,21 +601,16 @@ HTMLMediaElement::OnChannelRedirect(nsIC
 
   return NS_OK;
 }
 
 void HTMLMediaElement::ShutdownDecoder()
 {
   RemoveMediaElementFromURITable();
   NS_ASSERTION(mDecoder, "Must have decoder to shut down");
-  // TODO: This should be handled by ChangeNetworkState() so we have only one
-  // place to call StopProgress().
-  if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
-    mDecoder->StopProgress();
-  }
   mDecoder->Shutdown();
   mDecoder = nullptr;
 }
 
 void HTMLMediaElement::AbortExistingLoads()
 {
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
@@ -1787,21 +1782,17 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
 {
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
-#ifdef MOZ_EME
-  if (ContainsRestrictedContent()) {
-    return nullptr;
-  }
-#endif
+
   OutputMediaStream* out = mOutputStreams.AppendElement();
 #ifdef DEBUG
   // Estimate hints based on the type of the media element
   // under the preference media.capturestream_hints for the
   // debug builds only. This allows WebRTC Peer Connection
   // to behave appropriately when media streams generated
   // via mozCaptureStream*() are added to the Peer Connection.
   // This functionality is planned to be used as part of Audio
@@ -3996,31 +3987,20 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
 
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
-bool
-HTMLMediaElement::ContainsRestrictedContent()
-{
-  return GetMediaKeys() != nullptr;
-}
-
 already_AddRefed<Promise>
 HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
                                ErrorResult& aRv)
 {
-  if (MozAudioCaptured()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
@@ -4034,29 +4014,27 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return promise.forget();
   }
   if (mMediaKeys) {
     // Existing MediaKeys object. Shut it down.
     mMediaKeys->Shutdown();
     mMediaKeys = nullptr;
   }
-  
+
   mMediaKeys = aMediaKeys;
   if (mMediaKeys) {
     if (NS_FAILED(mMediaKeys->Bind(this))) {
       promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
-    // Update the same-origin status.
-    NotifyDecoderPrincipalChanged();
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 MediaWaitingFor
 HTMLMediaElement::WaitingFor() const
 {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -545,17 +545,16 @@ public:
 
 
   bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
 
   // Returns the principal of the "top level" document; the origin displayed
   // in the URL bar of the browser window.
   already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
 
-  bool ContainsRestrictedContent();
 #endif // MOZ_EME
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
   already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -32,16 +32,17 @@
 #include "nsITextControlFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsLinebreakConverter.h"
 #include "nsMappedAttributes.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
 #include "nsPresState.h"
 #include "nsReadableUtils.h"
+#include "nsRuleData.h"
 #include "nsStyleConsts.h"
 #include "nsTextEditorState.h"
 #include "nsIController.h"
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 
 #define NS_NO_CONTENT_DISPATCH (1 << 0)
 
@@ -402,16 +403,28 @@ HTMLTextAreaElement::ParseAttribute(int3
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 void
 HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                            nsRuleData* aData)
 {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
+    // wrap=off
+    nsCSSValue* whiteSpace = aData->ValueForWhiteSpace();
+    if (whiteSpace->GetUnit() == eCSSUnit_Null) {
+      const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
+      if (value && value->Type() == nsAttrValue::eString &&
+          value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
+        whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE, eCSSUnit_Enumerated);
+      }
+    }
+  }
+
   nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes, aData);
   nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
 }
 
 nsChangeHint
 HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
                                             int32_t aModType) const
 {
@@ -426,17 +439,23 @@ HTMLTextAreaElement::GetAttributeChangeH
     NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
   }
   return retval;
 }
 
 NS_IMETHODIMP_(bool)
 HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 {
+  static const MappedAttributeEntry attributes[] {
+    { &nsGkAtoms::wrap },
+    { nullptr }
+  };
+
   static const MappedAttributeEntry* const map[] = {
+    attributes,
     sDivAlignAttributeMap,
     sCommonAttributeMap,
   };
 
   return FindAttributeDependence(aAttribute, map);
 }
 
 nsMapRuleToAttributesFunc
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-1-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" wrap="off">
+  0  1  2  3
+  4  5
+
+  6  7  8
+  9
+
+
+  This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" style="white-space: pre">
+  0  1  2  3
+  4  5
+
+  6  7  8
+  9
+
+
+  This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-2-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" wrap="off" style="white-space: normal">
+  0  1  2  3
+  4  5
+
+  6  7  8
+  9
+
+
+  This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-2.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" style="white-space: normal">
+0 1 2 3 4 5 6 7 8 9 This is a long line that could wrap.
+</textarea>
+</body>
+</html>
--- a/dom/html/reftests/reftest.list
+++ b/dom/html/reftests/reftest.list
@@ -1,15 +1,18 @@
 # autofocus attribute (we can't test with mochitests)
 include autofocus/reftest.list
 include toblob-todataurl/reftest.list
 
 skip-if(B2G) == 41464-1a.html 41464-1-ref.html
 skip-if(B2G) == 41464-1b.html 41464-1-ref.html
 == 52019-1.html 52019-1-ref.html
+== 82711-1.html 82711-1-ref.html
+== 82711-2.html 82711-2-ref.html
+!= 82711-1-ref.html 82711-2-ref.html
 != 468263-1a.html about:blank
 != 468263-1b.html about:blank
 != 468263-1c.html about:blank
 != 468263-1d.html about:blank
 == 468263-2.html 468263-2-ref.html
 == 468263-2.html 468263-2-alternate-ref.html
 == 484200-1.html 484200-1-ref.html
 == 485377.html 485377-ref.html
--- a/dom/inputmethod/Keyboard.jsm
+++ b/dom/inputmethod/Keyboard.jsm
@@ -244,24 +244,44 @@ this.Keyboard = {
       case 'Keyboard:Unregister':
         this._keyboardMM = null;
         this._keyboardID = -1;
         break;
     }
   },
 
   forwardEvent: function keyboardForwardEvent(newEventName, msg) {
-    this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
-                            .frameLoader.messageManager;
+    let mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
+                .frameLoader.messageManager;
+    if (newEventName === 'Keyboard:FocusChange' &&
+        msg.data.type === 'blur') {
+      // A blur message can't be sent to the keyboard if the focus has
+      // already taken away at first place.
+      // This check is here to prevent problem caused by out-of-order
+      // ipc messages from two processes.
+      if (mm !== this.formMM) {
+        return false;
+      }
+
+      this.sendToKeyboard(newEventName, msg.data);
+      return true;
+    }
+
+    this.formMM = mm;
 
     this.sendToKeyboard(newEventName, msg.data);
+    return true;
   },
 
   handleFocusChange: function keyboardHandleFocusChange(msg) {
-    this.forwardEvent('Keyboard:FocusChange', msg);
+    let isSent = this.forwardEvent('Keyboard:FocusChange', msg);
+
+    if (!isSent) {
+      return;
+    }
 
     // Chrome event, used also to render value selectors; that's why we need
     // the info about choices / min / max here as well...
     SystemAppProxy.dispatchEvent({
       type: 'inputmethod-contextchange',
       inputType: msg.data.type,
       value: msg.data.value,
       choices: JSON.stringify(msg.data.choices),
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/TestShellChild.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/plugins/PluginModuleParent.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #include "nsDirectoryServiceDefs.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
@@ -927,16 +928,23 @@ ContentChild::DeallocPCycleCollectWithLo
 {
     // ...so when we get here, there's nothing for us to do.
     //
     // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
     // this point, so we shouldn't touch the actor in any case.
     return true;
 }
 
+mozilla::plugins::PPluginModuleParent*
+ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
+                                       base::ProcessId aOtherProcess)
+{
+    return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess);
+}
+
 PContentBridgeChild*
 ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ContentBridgeChild::Create(aTransport, aOtherProcess);
 }
 
 PContentBridgeParent*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -96,16 +96,20 @@ public:
     ContentBridgeParent* GetLastBridge() {
         MOZ_ASSERT(mLastBridge);
         ContentBridgeParent* parent = mLastBridge;
         mLastBridge = nullptr;
         return parent;
     }
     nsRefPtr<ContentBridgeParent> mLastBridge;
 
+    PPluginModuleParent *
+    AllocPPluginModuleParent(mozilla::ipc::Transport* transport,
+                             base::ProcessId otherProcess) MOZ_OVERRIDE;
+
     PContentBridgeParent*
     AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
                               base::ProcessId otherProcess) MOZ_OVERRIDE;
     PContentBridgeChild*
     AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
                              base::ProcessId otherProcess) MOZ_OVERRIDE;
 
     PCompositorChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -65,16 +65,17 @@
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsAppRunner.h"
 #include "nsAutoPtr.h"
 #include "nsCDefaultURIFixup.h"
@@ -917,16 +918,30 @@ ContentParent::AnswerBridgeToChildProces
         return PContentBridge::Bridge(this, cp);
     } else {
         // You can't bridge to a process you didn't open!
         KillHard();
         return false;
     }
 }
 
+bool
+ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId)
+{
+    return mozilla::plugins::SetupBridge(aPluginId, this);
+}
+
+bool
+ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
+                               nsTArray<PluginTag>* aPlugins,
+                               uint32_t* aNewPluginEpoch)
+{
+    return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+}
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -143,16 +143,21 @@ public:
 
     virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
                                         const hal::ProcessPriority& aPriority,
                                         uint64_t* aId,
                                         bool* aIsForApp,
                                         bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE;
 
+    virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
+    virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
+                                 nsTArray<PluginTag>* aPlugins,
+                                 uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;
+
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_NSIOBSERVER
     NS_DECL_NSIDOMGEOPOSITIONCALLBACK
 
     /**
      * MessageManagerCallback methods that we override.
--- a/dom/ipc/CrashReporterChild.cpp
+++ b/dom/ipc/CrashReporterChild.cpp
@@ -20,17 +20,17 @@ CrashReporterChild::GetCrashReporter()
   const InfallibleTArray<PCrashReporterChild*>* reporters = nullptr;
   switch (XRE_GetProcessType()) {
     case GeckoProcessType_Content: {
       ContentChild* child = ContentChild::GetSingleton();
       reporters = &child->ManagedPCrashReporterChild();
       break;
     }
     case GeckoProcessType_Plugin: {
-      PluginModuleChild* child = PluginModuleChild::current();
+      PluginModuleChild* child = PluginModuleChild::GetChrome();
       reporters = &child->ManagedPCrashReporterChild();
       break;
     }
     default:
       break;
   }
   if (reporters && reporters->Length() > 0) {
     return reporters->ElementAt(0);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -20,32 +20,34 @@ include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PImageBridge;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
+include protocol PPluginModule;
 include protocol PPrinting;
 include protocol PScreenManager;
 include protocol PSharedBufferManager;
 include protocol PSms;
 include protocol PSpeechSynthesis;
 include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
 include protocol PVoicemail;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
+include PluginTypes;
 include ProtocolTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
@@ -323,16 +325,18 @@ struct ClipboardCapabilities {
 
 union MaybeFileDesc {
     FileDescriptor;
     void_t;
 };
 
 prio(normal upto high) intr protocol PContent
 {
+    parent spawns PPluginModule;
+
     parent opens PCompositor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     child opens PBackground;
 
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
@@ -527,16 +531,39 @@ parent:
         returns (bool isOffline, nsString[] dictionaries,
                  ClipboardCapabilities clipboardCaps);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority)
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     intr BridgeToChildProcess(uint64_t id);
 
+    /**
+     * This call connects the content process to a plugin process. While this
+     * call runs, a new PluginModuleParent will be created in the ContentChild
+     * via bridging. The corresponding PluginModuleChild will live in the plugin
+     * process. We use intr semantics here to ensure that the PluginModuleParent
+     * allocation message is dispatched before LoadPlugin returns.
+     */
+    intr LoadPlugin(uint32_t pluginId);
+
+    /**
+     * This call returns the set of plugins loaded in the chrome
+     * process. However, in many cases this set will not have changed since the
+     * last FindPlugins message. Consequently, the chrome process increments an
+     * epoch number every time the set of plugins changes. The content process
+     * sends up the last epoch it observed. If the epochs are the same, the
+     * chrome process returns no plugins. Otherwise it returns a complete list.
+     *
+     * |pluginEpoch| is the epoch last observed by the content
+     * process. |newPluginEpoch| is the current epoch in the chrome process. If
+     * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
+     */
+    sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch);
+
     async PJavaScript();
 
     sync PRemoteSpellcheckEngine();
     PDeviceStorageRequest(DeviceStorageParams params);
 
     PFileSystemRequest(FileSystemParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -494,18 +494,19 @@ void MediaDecoder::Shutdown()
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
     mResource->Close();
   }
 
   ChangeState(PLAY_STATE_SHUTDOWN);
 
-  // If we hit this assertion, there might be a bug in network state transition.
-  NS_ASSERTION(!mProgressTimer, "Progress timer should've been stopped.");
+  if (mProgressTimer) {
+    StopProgress();
+  }
   mOwner = nullptr;
 
   MediaShutdownManager::Instance().Unregister(this);
 }
 
 MediaDecoder::~MediaDecoder()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -7,18 +7,18 @@
 #include "gmp-test-storage.h"
 
 #include <string>
 #include <vector>
 #include <iostream>
 #include <istream>
 #include <iterator>
 #include <sstream>
-#include <assert.h>
 
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/NullPtr.h"
 
 using namespace std;
 
 FakeDecryptor* FakeDecryptor::sInstance = nullptr;
 
 static bool sFinishedTruncateTest = false;
@@ -31,30 +31,30 @@ MaybeFinish()
   if (sFinishedTruncateTest && sFinishedReplaceTest && sMultiClientTest) {
     FakeDecryptor::Message("test-storage complete");
   }
 }
 
 FakeDecryptor::FakeDecryptor()
   : mCallback(nullptr)
 {
-  assert(!sInstance);
+  MOZ_ASSERT(!sInstance);
   sInstance = this;
 }
 
 void FakeDecryptor::DecryptingComplete()
 {
   sInstance = nullptr;
   delete this;
 }
 
 void
 FakeDecryptor::Message(const std::string& aMessage)
 {
-  assert(sInstance);
+  MOZ_ASSERT(sInstance);
   const static std::string sid("fake-session-id");
   sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
                                        (const uint8_t*)aMessage.c_str(), aMessage.size(),
                                        nullptr, 0);
 }
 
 std::vector<std::string>
 Tokenize(const std::string& aString)
--- a/dom/media/gmp-plugin/gmp-test-storage.cpp
+++ b/dom/media/gmp-plugin/gmp-test-storage.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gmp-test-storage.h"
 #include <vector>
-#include <assert.h>
+
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 class WriteRecordClient : public GMPRecordClient {
 public:
   GMPErr Init(GMPRecord* aRecord,
               GMPTask* aContinuation,
               const uint8_t* aData,
               uint32_t aDataSize) {
@@ -114,17 +115,17 @@ private:
   GMPRecord* mRecord;
   ReadContinuation* mContinuation;
 };
 
 GMPErr
 ReadRecord(const std::string& aRecordName,
            ReadContinuation* aContinuation)
 {
-  assert(aContinuation);
+  MOZ_ASSERT(aContinuation);
   GMPRecord* record;
   ReadRecordClient* client = new ReadRecordClient();
   auto err = GMPOpenRecord(aRecordName.c_str(),
                            aRecordName.size(),
                            &record,
                            client);
   if (GMP_FAILED(err)) {
     return err;
@@ -135,24 +136,24 @@ ReadRecord(const std::string& aRecordNam
 extern GMPPlatformAPI* g_platform_api; // Defined in gmp-fake.cpp
 
 GMPErr
 GMPOpenRecord(const char* aName,
               uint32_t aNameLength,
               GMPRecord** aOutRecord,
               GMPRecordClient* aClient)
 {
-  assert(g_platform_api);
+  MOZ_ASSERT(g_platform_api);
   return g_platform_api->createrecord(aName, aNameLength, aOutRecord, aClient);
 }
 
 GMPErr
 GMPRunOnMainThread(GMPTask* aTask)
 {
-  assert(g_platform_api);
+  MOZ_ASSERT(g_platform_api);
   return g_platform_api->runonmainthread(aTask);
 }
 
 class OpenRecordClient : public GMPRecordClient {
 public:
   GMPErr Init(GMPRecord* aRecord,
               OpenContinuation* aContinuation) {
     mRecord = aRecord;
@@ -175,17 +176,17 @@ private:
   GMPRecord* mRecord;
   OpenContinuation* mContinuation;
 };
 
 GMPErr
 GMPOpenRecord(const std::string& aRecordName,
               OpenContinuation* aContinuation)
 {
-  assert(aContinuation);
+  MOZ_ASSERT(aContinuation);
   GMPRecord* record;
   OpenRecordClient* client = new OpenRecordClient();
   auto err = GMPOpenRecord(aRecordName.c_str(),
                            aRecordName.size(),
                            &record,
                            client);
   if (GMP_FAILED(err)) {
     return err;
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -79,28 +79,16 @@ public:
   }
 
   void SetTaskQueue(MediaTaskQueue* aTaskQueue)
   {
     MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
     mTaskQueue = aTaskQueue;
   }
 
-  void BreakCycles()
-  {
-    if (mReader) {
-      mReader->BreakCycles();
-      mReader = nullptr;
-    }
-    mTaskQueue = nullptr;
-#ifdef MOZ_EME
-    mCDMProxy = nullptr;
-#endif
-  }
-
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy)
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mCDMProxy = aProxy;
     return NS_OK;
   }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime)
 }
 
 void
 TrackBuffer::BreakCycles()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    mDecoders[i]->BreakCycles();
+    mDecoders[i]->GetReader()->BreakCycles();
   }
   mDecoders.Clear();
 
   // These are cleared in Shutdown()
   MOZ_ASSERT(mInitializedDecoders.IsEmpty());
   MOZ_ASSERT(!mParentDecoder);
 }
 
deleted file mode 100644
--- a/dom/media/test/eme.js
+++ /dev/null
@@ -1,178 +0,0 @@
-const KEYSYSTEM_TYPE = "org.w3.clearkey";
-
-function bail(message)
-{
-  return function(err) {
-    ok(false, message);
-    if (err) {
-      info(err);
-    }
-    SimpleTest.finish();
-  }
-}
-
-function ArrayBufferToString(arr)
-{
-  var str = '';
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < view.length; i++) {
-    str += String.fromCharCode(view[i]);
-  }
-  return str;
-}
-
-function StringToArrayBuffer(str)
-{
-  var arr = new ArrayBuffer(str.length);
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < str.length; i++) {
-    view[i] = str.charCodeAt(i);
-  }
-  return arr;
-}
-
-function Base64ToHex(str)
-{
-  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
-  var res = "";
-  for (var i = 0; i < bin.length; i++) {
-    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
-  }
-  return res;
-}
-
-function HexToBase64(hex)
-{
-  var bin = "";
-  for (var i = 0; i < hex.length; i += 2) {
-    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
-  }
-  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
-}
-
-function UpdateSessionFunc(test) {
-  return function(ev) {
-    var msgStr = ArrayBufferToString(ev.message);
-    var msg = JSON.parse(msgStr);
-
-    info("got message from CDM: " + msgStr);
-    is(msg.type, test.sessionType, "Key session type should match");
-    ok(msg.kids, "message event should contain key ID array");
-
-    var outKeys = [];
-
-    for (var i = 0; i < msg.kids.length; i++) {
-      var id64 = msg.kids[i];
-      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
-      var key = test.keys[idHex];
-
-      if (key) {
-        info("found key " + key + " for key id " + idHex);
-        outKeys.push({
-          "kty":"oct",
-          "alg":"A128KW",
-          "kid":id64,
-          "k":HexToBase64(key)
-        });
-      } else {
-        bail("Couldn't find key for key id " + idHex);
-      }
-    }
-
-    var update = JSON.stringify({
-      "keys" : outKeys,
-      "type" : msg.type
-    });
-    info("sending update message to CDM: " + update);
-
-    ev.target.update(StringToArrayBuffer(update)).then(function() {
-      info("MediaKeySession update ok!");
-    }, bail("MediaKeySession update failed"));
-  }
-}
-
-function PlayFragmented(test, elem)
-{
-  return new Promise(function(resolve, reject) {
-    var ms = new MediaSource();
-    elem.src = URL.createObjectURL(ms);
-
-    var sb;
-    var curFragment = 0;
-
-    function addNextFragment() {
-      if (curFragment >= test.fragments.length) {
-        ms.endOfStream();
-        resolve();
-        return;
-      }
-
-      var fragmentFile = test.fragments[curFragment++];
-
-      var req = new XMLHttpRequest();
-      req.open("GET", fragmentFile);
-      req.responseType = "arraybuffer";
-
-      req.addEventListener("load", function() {
-        sb.appendBuffer(new Uint8Array(req.response));
-      });
-
-      info("fetching resource " + fragmentFile);
-      req.send(null);
-    }
-
-    ms.addEventListener("sourceopen", function () {
-      sb = ms.addSourceBuffer(test.type);
-      sb.addEventListener("updateend", addNextFragment);
-
-      addNextFragment();
-    })
-  });
-}
-
-// Returns a promise that is resovled when the media element is ready to have
-// its play() function called; when it's loaded MSE fragments, or once the load
-// has started for non-MSE video.
-function LoadTest(test, elem)
-{
-  if (test.fragments) {
-    return PlayFragmented(test, elem);
-  }
-
-  // This file isn't fragmented; set the media source normally.
-  return new Promise(function(resolve, reject) {
-    elem.src = test.name;
-    resolve();
-  });
-}
-
-function SetupEME(test, token, params)
-{
-  var v = document.createElement("video");
-
-  var onSetKeysFail = (params && params.onSetKeysFail)
-    ? params.onSetKeysFail
-    : bail(token + " Failed to set MediaKeys on <video> element");
-  
-  v.addEventListener("encrypted", function(ev) {
-    info(token + " got encrypted event");
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      info(token + " created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      info(token + " set MediaKeys on <video> element ok");
-
-      var session = v.mediaKeys.createSession(test.sessionType);
-      if (params && params.onsessioncreated) {
-        params.onsessioncreated(session);
-      }
-      session.addEventListener("message", UpdateSessionFunc(test));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, onSetKeysFail);
-  });
-
-  return v;
-}
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -119,17 +119,17 @@ var gPlayTests = [
 
   // oggz-chop stream
   { name:"bug482461.ogv", type:"video/ogg", duration:4.34 },
   // Theora only oggz-chop stream
   { name:"bug482461-theora.ogv", type:"video/ogg", duration:4.138 },
   // With first frame a "duplicate" (empty) frame.
   { name:"bug500311.ogv", type:"video/ogg", duration:1.96 },
   // Small audio file
-  { name:"small-shot.ogg", type:"video/ogg", duration:0.276 },
+  { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
   // More audio in file than video.
   { name:"short-video.ogv", type:"video/ogg", duration:1.081 },
   // First Theora data packet is zero bytes.
   { name:"bug504613.ogv", type:"video/ogg", duration:Number.NaN },
   // Multiple audio streams.
   { name:"bug516323.ogv", type:"video/ogg", duration:4.208 },
   // oggz-chop with non-keyframe as first frame
   { name:"bug556821.ogv", type:"video/ogg", duration:2.551 },
@@ -674,23 +674,27 @@ function checkMetadata(msg, e, test) {
     ok(Math.abs(e.duration - test.duration) < 0.1,
        msg + " duration (" + e.duration + ") should be around " + test.duration);
   }
 }
 
 // Returns the first test from candidates array which we can play with the
 // installed video backends.
 function getPlayableVideo(candidates) {
-  var v = document.createElement("video");
-  var resources = candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
+  var resources = getPlayableVideos(candidates);
   if (resources.length > 0)
     return resources[0];
   return null;
 }
 
+function getPlayableVideos(candidates) {
+  var v = document.createElement("video");
+  return candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
+}
+
 function getPlayableAudio(candidates) {
   var v = document.createElement("audio");
   var resources = candidates.filter(function(x){return /^audio/.test(x.type) && v.canPlayType(x.type);});
   if (resources.length > 0)
     return resources[0];
   return null;
 }
 
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -135,17 +135,16 @@ support-files =
   detodos.opus
   detodos.opus^headers^
   detodos.webm
   detodos.webm^headers^
   dirac.ogg
   dirac.ogg^headers^
   dynamic_redirect.sjs
   dynamic_resource.sjs
-  eme.js
   gizmo-frag-cenc1.m4s
   gizmo-frag-cenc2.m4s
   gizmo-frag-cencinit.mp4
   file_access_controls.html
   fragment_noplay.js
   fragment_play.js
   gizmo.mp4
   gizmo.mp4^headers^
@@ -356,21 +355,17 @@ skip-if = (toolkit == 'android' && proce
 [test_contentDuration7.html]
 [test_controls.html]
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
-[test_eme_canvas_blocked.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
-[test_eme_playback.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
-[test_eme_stream_capture_blocked.html]
+[test_encryptedMediaExtensions.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
@@ -397,17 +392,16 @@ skip-if = (toolkit == 'android' && proce
 [test_mediarecorder_getencodeddata.html]
 [test_mediarecorder_record_4ch_audiocontext.html]
 [test_mediarecorder_record_audiocontext.html]
 [test_mediarecorder_record_audiocontext_mlk.html]
 [test_mediarecorder_record_audionode.html]
 [test_mediarecorder_record_gum_video_timeslice.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
 [test_mediarecorder_record_immediate_stop.html]
-skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_no_timeslice.html]
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_nosrc.html]
 [test_mediarecorder_record_session.html]
 [test_mediarecorder_record_startstopstart.html]
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_stopms.html]
 [test_mediarecorder_record_timeslice.html]
deleted file mode 100644
--- a/dom/media/test/test_eme_canvas_blocked.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test Encrypted Media Extensions</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="text/javascript" src="manifest.js"></script>
-  <script type="text/javascript" src="eme.js"></script>
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-var manager = new MediaTestManager;
-
-function startTest(test, token)
-{
-  manager.started(token);
-
-  var sessions = [];
-
-  var v = SetupEME(test, token);
-  v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
-
-  v.addEventListener("canplay", function(ev) {
-    var video = ev.target;
-    var canvas = document.createElement("canvas");
-    canvas.width = video.videoWidth;
-    canvas.height = video.videoHeight;
-    document.body.appendChild(canvas);
-    var ctx = canvas.getContext("2d");
-    var threwError = false;
-    try {
-      ctx.drawImage(video, 0, 0);
-    } catch (ex) {
-      threwError = true;
-    }
-    ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
-    manager.finished(token);
-  });
-
-  v.addEventListener("error", bail(token + " got error event"));
-
-  LoadTest(test, v);
-}
-
-function beginTest() {
-  manager.runTests(gEMETests, startTest);
-}
-
-var prefs = [
-  [ "media.mediasource.enabled", true ],
-  [ "media.mediasource.ignore_codecs", true ],
-];
-
-if (/Linux/.test(navigator.userAgent) ||
-    !document.createElement('video').canPlayType("video/mp4")) {
-  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
-  prefs.push([ "media.fragmented-mp4.exposed", true ]);
-  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/media/test/test_eme_stream_capture_blocked.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test Encrypted Media Extensions</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="text/javascript" src="manifest.js"></script>
-  <script type="text/javascript" src="eme.js"></script>
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-var manager = new MediaTestManager;
-
-function startTest(test, token)
-{
-  // Three cases:
-  // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
-  // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
-  // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
-
-  // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
-  var case1token = token + "_case1";
-  var setKeysFailed = function() {
-    ok(true, case1token + " setMediaKeys failed as expected.");
-    manager.finished(case1token);
-  };
-  var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
-  var context = new AudioContext();
-  var node = context.createMediaElementSource(v1);
-  v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
-  v1.addEventListener("error", bail(case1token + " got error event"));
-  v1.addEventListener("canplay", function(ev) {
-    ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
-  });
-  manager.started(case1token);
-  LoadTest(test, v1);
-
-
-  // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
-  var case2token = token + "_case2";
-  var v2 = SetupEME(test, case2token);
-  v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
-  v2.addEventListener("error", bail(case2token + " got error event"));
-  v2.addEventListener("canplay", function(ev) {
-    ok(true, case2token + " should reach canplay");
-    var threw = false;
-    try {
-      var context = new AudioContext();
-      var node = context.createMediaElementSource(v2);
-    } catch (e) {
-      threw = true;
-    }
-    ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
-    manager.finished(case2token);
-  });
-  manager.started(case2token);
-  LoadTest(test, v2);
-
-
-  // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
-  var case3token = token + "_case3";
-  var v3 = SetupEME(test, case3token);
-  v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
-  v3.addEventListener("error", bail(case3token + " got error event"));
-  v3.addEventListener("canplay", function(ev) {
-    ok(true, case3token + " should reach canplay");
-    var threw = false;
-    try {
-      var stream = v3.mozCaptureStreamUntilEnded();
-    } catch (e) {
-      threw = true;
-    }
-    ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
-    manager.finished(case3token);
-  });
-  manager.started(case3token);
-  LoadTest(test, v3);
-}
-
-function beginTest() {
-  manager.runTests(gEMETests, startTest);
-}
-
-var prefs = [
-  [ "media.mediasource.enabled", true ],
-  [ "media.mediasource.ignore_codecs", true ],
-];
-
-if (/Linux/.test(navigator.userAgent) ||
-    !document.createElement('video').canPlayType("video/mp4")) {
-  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
-  prefs.push([ "media.fragmented-mp4.exposed", true ]);
-  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-</script>
-</pre>
-</body>
-</html>
rename from dom/media/test/test_eme_playback.html
rename to dom/media/test/test_encryptedMediaExtensions.html
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_encryptedMediaExtensions.html
@@ -1,96 +1,246 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Encrypted Media Extensions</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
-  <script type="text/javascript" src="eme.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var manager = new MediaTestManager;
 
+const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
-function KeysChangeFunc(session, keys, token) {
+function bail(message)
+{
+  return function(err) {
+    ok(false, message);
+    if (err) {
+      info(err);
+    }
+    SimpleTest.finish();
+  }
+}
+
+function ArrayBufferToString(arr)
+{
+  var str = '';
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < view.length; i++) {
+    str += String.fromCharCode(view[i]);
+  }
+  return str;
+}
+
+function StringToArrayBuffer(str)
+{
+  var arr = new ArrayBuffer(str.length);
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < str.length; i++) {
+    view[i] = str.charCodeAt(i);
+  }
+  return arr;
+}
+
+function Base64ToHex(str)
+{
+  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+  var res = "";
+  for (var i = 0; i < bin.length; i++) {
+    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+  }
+  return res;
+}
+
+function HexToBase64(hex)
+{
+  var bin = "";
+  for (var i = 0; i < hex.length; i += 2) {
+    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+  }
+  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function UpdateSessionFunc(test) {
+  return function(ev) {
+    var msgStr = ArrayBufferToString(ev.message);
+    var msg = JSON.parse(msgStr);
+
+    info("got message from CDM: " + msgStr);
+    is(msg.type, test.sessionType, "Key session type should match");
+    ok(msg.kids, "message event should contain key ID array");
+
+    var outKeys = [];
+
+    for (var i = 0; i < msg.kids.length; i++) {
+      var id64 = msg.kids[i];
+      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
+      var key = test.keys[idHex];
+
+      if (key) {
+        info("found key " + key + " for key id " + idHex);
+        outKeys.push({
+          "kty":"oct",
+          "alg":"A128KW",
+          "kid":id64,
+          "k":HexToBase64(key)
+        });
+      } else {
+        bail("Couldn't find key for key id " + idHex);
+      }
+    }
+
+    var update = JSON.stringify({
+      "keys" : outKeys,
+      "type" : msg.type
+    });
+    info("sending update message to CDM: " + update);
+
+    ev.target.update(StringToArrayBuffer(update)).then(function() {
+      info("MediaKeySession update ok!");
+    }, bail("MediaKeySession update failed"));
+  }
+}
+
+function PlayFragmented(test, elem)
+{
+  var ms = new MediaSource();
+  elem.src = URL.createObjectURL(ms);
+
+  var sb;
+  var curFragment = 0;
+
+  function addNextFragment() {
+    if (curFragment >= test.fragments.length) {
+      ms.endOfStream();
+      elem.play();
+      return;
+    }
+
+    var fragmentFile = test.fragments[curFragment++];
+
+    var req = new XMLHttpRequest();
+    req.open("GET", fragmentFile);
+    req.responseType = "arraybuffer";
+
+    req.addEventListener("load", function() {
+      sb.appendBuffer(new Uint8Array(req.response));
+    });
+
+    info("fetching resource " + fragmentFile);
+    req.send(null);
+  }
+
+  ms.addEventListener("sourceopen", function () {
+    sb = ms.addSourceBuffer(test.type);
+    sb.addEventListener("updateend", addNextFragment);
+
+    addNextFragment();
+  });
+}
+
+function PlayTest(test, elem)
+{
+  if (test.fragments) {
+    PlayFragmented(test, elem);
+    return;
+  }
+
+  // This file isn't fragmented; set the media source normally.
+  elem.src = test.name;
+  elem.play();
+}
+
+function KeysChangeFunc(session, keys) {
   session.keyIdsReceived = [];
   for (var keyid in keys) {
     info("Set " + keyid + " to false in session.keyIdsReceived");
     session.keyIdsReceived[keyid] = false;
   }
   return function(ev) {
     var session = ev.target;
     session.gotKeysChanged = true;
     session.getUsableKeyIds().then(function(keyIds) {
       for (var k = 0; k < keyIds.length; k++) {
         var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
-        ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
+        ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
         session.keyIdsReceived[kid] = true;
       }
     }, bail("Failed to get keyIds"));
   }
 }
 
 function startTest(test, token)
 {
-  manager.started(token);
-
-  var sessions = [];
+  manager.started(test._token);
 
-  var v = SetupEME(test, token,
-    {
-      onsessioncreated: function(session) {
-        sessions.push(session);
-        session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
-      }
-    }
-  );
-
+  var v = document.createElement("video");
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
+    gotEncrypted = true;
+
+    info(token + " got encrypted event");
     ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
        token + " MediaKeys should support this keysystem");
-    gotEncrypted = true;
+
+    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
+      info(token + " created MediaKeys object ok");
+      mediaKeys.sessions = [];
+      return v.setMediaKeys(mediaKeys);
+    }, bail("failed to create MediaKeys object")).then(function() {
+      info(token + " set MediaKeys on <video> element ok");
+
+      ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
+         "MediaKeys should still support keysystem after CDM created...");
+
+      var session = v.mediaKeys.createSession(test.sessionType);
+      v.mediaKeys.sessions.push(session);
+      session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
+      session.addEventListener("message", UpdateSessionFunc(test));
+      session.generateRequest(ev.initDataType, ev.initData).then(function() {
+      }, bail(token + " Failed to initialise MediaKeySession"));
+
+    }, bail(token + " Failed to set MediaKeys on <video> element"));
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, token + " got ended event");
-    manager.finished(token);
+    manager.finished(test._token);
 
     ok(gotEncrypted, token + " encrypted event should have fired");
     ok(gotPlaying, token + " playing event should have fired");
 
     ok(Math.abs(test.duration - v.duration) < 0.1,
        token + " Duration of video should be corrrect");
     ok(Math.abs(test.duration - v.currentTime) < 0.1,
        token + " Current time should be same as duration");
-
     // Verify all sessions had all keys went sent the to the CDM usable, and thus
     // that we received keyschange event(s).
+    var sessions = v.mediaKeys.sessions;
     is(sessions.length, 1, "should have 1 session");
     for (var i = 0; i < sessions.length; i++) {
       var session = sessions[i];
-      ok(session.gotKeysChanged, token + " should have received at least one keychange event");
+      ok(session.gotKeysChanged, "Should have received at least one keychange event");
       for (var kid in session.keyIdsReceived) {
-        ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
+        ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
       }
     }
-
-   });
+  });
 
   v.addEventListener("error", bail(token + " got error event"));
 
-  LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
+  PlayTest(test, v);
 }
 
 function testIsTypeSupported()
 {
   var t = MediaKeys.isTypeSupported;
   const clearkey = "org.w3.clearkey";
   ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
   ok(t(clearkey), "ClearKey supported.");
--- a/dom/media/test/test_reset_src.html
+++ b/dom/media/test/test_reset_src.html
@@ -9,78 +9,87 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="manifest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank"
 href="https://bugzilla.mozilla.org/show_bug.cgi?id=804875">Mozilla Bug 804875</a>
 
-<video style="border: 4px solid red" controls></video>
 <canvas></canvas>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+var manager = new MediaTestManager;
+
+function finish(v) {
+  removeNodeAndSource(v);
+  manager.finished(v.token);
+}
+
 function onLoadedMetadata_Audio(e) {
   var t = e.target;
-  is(t.videoHeight, 0, "videoHeight should be zero when there is no video.");
-  is(t.videoWidth, 0, "videoWidth should be zero when there is no video.");
-  is(t.mozPaintedFrames, 0, "mozPaintedFrames should be zero when there is no video.");
-  is(t.mozFrameDelay, 0, "mozFrameDelay should be zero when there is no video.");
+  is(t.videoHeight, 0, t.name + ": videoHeight should be zero when there is no video.");
+  is(t.videoWidth, 0, t.name + ": videoWidth should be zero when there is no video.");
+  is(t.mozPaintedFrames, 0, t.name + ": mozPaintedFrames should be zero when there is no video.");
+  is(t.mozFrameDelay, 0, t.name + ": mozFrameDelay should be zero when there is no video.");
   var c = document.getElementsByTagName("canvas")[0].getContext("2d");
   try {
     c.drawImage(t, 0, 0, t.videoHeight, t.videoWidth);
   } catch (e) {
-    ok(true, "Trying to draw to a canvas should throw, since we don't have a frame anymore");
-    SimpleTest.finish();
+    ok(true, t.name + ": Trying to draw to a canvas should throw, since we don't have a frame anymore");
+    finish(t);
     return;
   }
-  ok(false, "We should not succeed to draw a video frame on the canvas.");
+  ok(false, t.name + ": We should not succeed to draw a video frame on the canvas.");
 }
 
 function onTimeUpdate_Video(e) {
   var t = e.target;
   if (t.currentTime < t.duration / 4) {
     return;
   }
   t.removeEventListener("timeupdate", onTimeUpdate_Video);
-  ok(t.mozPaintedFrames > 0, "mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
-  ok(t.mozFrameDelay >= 0, "mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
+  ok(t.mozPaintedFrames > 0, t.name + ": mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
+  ok(t.mozFrameDelay >= 0, t.name + ": mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
 
-  if (v._firstTime) {
+  if (t._firstTime) {
     t.src = t.src;
-    v._firstTime = false;
+    t._firstTime = false;
   } else {
     var source = getPlayableAudio(gPlayTests);
     if (!source) {
       todo("No audio file available.")
-      SimpleTest.finish();
+      finish(t);
     } else {
       t.removeEventListener("loadedmetadata", onLoadedMetadata_Video);
       t.addEventListener("loadedmetadata", onLoadedMetadata_Audio);
       t.src = source.name;
     }
   }
 }
 
 function onLoadedMetadata_Video(e) {
   var t = e.target;
-  ok(t.videoHeight != 0, "We should have a videoHeight.");
-  ok(t.videoWidth != 0, "We should have a videoWidth.");
+  isnot(t.videoHeight, 0, t.name + ": We should have a videoHeight.");
+  isnot(t.videoWidth, 0, t.name + ": We should have a videoWidth.");
   t.addEventListener("timeupdate", onTimeUpdate_Video);
   t.play();
 }
 
-var v = document.getElementsByTagName("video")[0];
-v._firstTime = true;
-var source = getPlayableVideo(gPlayTests);
-if (!source) {
-  todo("No video file available.");
-} else {
+function startTest(test, token) {
+  var v = document.createElement('video');
+  document.body.appendChild(v);
+  v.preload = "metadata";
+  v._firstTime = true;
   v.addEventListener("loadedmetadata", onLoadedMetadata_Video);
-  v.src = source.name;
-  SimpleTest.waitForExplicitFinish();
+  v.src = test.name;
+  v.token = token;
+  v.name = test.name;
+  manager.started(token);
 }
+
+manager.runTests(getPlayableVideos(gSmallTests.concat(gSeekTests)), startTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -280,22 +280,16 @@ AudioContext::CreateAnalyser()
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
-#ifdef MOZ_EME
-  if (aMediaElement.ContainsRestrictedContent()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
-  }
-#endif
   nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
     new MediaElementAudioSourceNode(this, stream);
   return mediaElementAudioSourceNode.forget();
 }
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -75,17 +75,18 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/PluginLibrary.h"
 using mozilla::PluginLibrary;
 
 #include "mozilla/PluginPRLibrary.h"
 using mozilla::PluginPRLibrary;
 
 #include "mozilla/plugins/PluginModuleParent.h"
-using mozilla::plugins::PluginModuleParent;
+using mozilla::plugins::PluginModuleChromeParent;
+using mozilla::plugins::PluginModuleContentParent;
 
 #ifdef MOZ_X11
 #include "mozilla/X11Util.h"
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 #include "mozilla/WindowsVersion.h"
@@ -242,16 +243,20 @@ nsNPAPIPlugin::PluginCrashed(const nsASt
 {
   nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
   host->PluginCrashed(this, pluginDumpID, browserDumpID);
 }
 
 bool
 nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
 {
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return true;
+  }
+
 #if (MOZ_WIDGET_GTK == 3)
   // We force OOP on Linux/GTK3 because some plugins use GTK2 and both GTK
   // libraries can't be loaded in the same process.
   return true;
 #else
   if (PR_GetEnv("MOZ_DISABLE_OOP_PLUGINS")) {
     return false;
   }
@@ -383,18 +388,22 @@ nsNPAPIPlugin::RunPluginOOP(const nsPlug
 
 inline PluginLibrary*
 GetNewPluginLibrary(nsPluginTag *aPluginTag)
 {
   if (!aPluginTag) {
     return nullptr;
   }
 
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return PluginModuleContentParent::LoadModule(aPluginTag->mId);
+  }
+
   if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
-    return PluginModuleParent::LoadModule(aPluginTag->mFullPath.get());
+    return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId);
   }
   return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
 }
 
 // Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
 nsresult
 nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
 {
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -43,17 +43,20 @@
 #include "nsPluginLogging.h"
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/LoadInfo.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsISupportsPrimitives.h"
 
 #include "nsXULAppAPI.h"
@@ -101,16 +104,17 @@
 #endif
 
 #if MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla;
 using mozilla::TimeStamp;
+using mozilla::plugins::PluginTag;
 
 // Null out a strong ref to a linked list iteratively to avoid
 // exhausting the stack (bug 486349).
 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_)                \
   {                                                                  \
     while (list_) {                                                  \
       type_ temp = list_->mNext_;                                    \
       list_->mNext_ = nullptr;                                        \
@@ -304,16 +308,20 @@ nsPluginHost::GetInst()
 }
 
 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
 {
   if (!aPluginTag || !aPluginTag->mPlugin) {
     return false;
   }
 
+  if (aPluginTag->mContentProcessRunningCount) {
+    return true;
+  }
+
   for (uint32_t i = 0; i < mInstances.Length(); i++) {
     nsNPAPIPluginInstance *instance = mInstances[i].get();
     if (instance &&
         instance->GetPlugin() == aPluginTag->mPlugin &&
         instance->IsRunning()) {
       return true;
     }
   }
@@ -1267,16 +1275,86 @@ nsresult nsPluginHost::EnsurePluginLoade
     if (NS_FAILED(rv)) {
       return rv;
     }
     aPluginTag->mPlugin = plugin;
   }
   return NS_OK;
 }
 
+nsresult
+nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // If plugins haven't been scanned yet, do so now
+  LoadPlugins();
+
+  nsPluginTag* pluginTag = PluginWithId(aPluginId);
+  if (pluginTag) {
+    nsresult rv = EnsurePluginLoaded(pluginTag);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // We only get here if a content process doesn't have a PluginModuleParent
+    // for the given plugin already. Therefore, this counter is counting the
+    // number of outstanding PluginModuleParents for the plugin, excluding the
+    // one from the chrome process.
+    pluginTag->mContentProcessRunningCount++;
+    NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+class nsPluginUnloadRunnable : public nsRunnable
+{
+public:
+  explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
+
+  NS_IMETHOD Run()
+  {
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    if (!host) {
+      return NS_OK;
+    }
+    nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
+    if (!pluginTag) {
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
+    pluginTag->mContentProcessRunningCount--;
+
+    if (!pluginTag->mContentProcessRunningCount) {
+      if (!host->IsRunningPlugin(pluginTag)) {
+        pluginTag->TryUnloadPlugin(false);
+      }
+    }
+    return NS_OK;
+  }
+
+protected:
+  uint32_t mPluginId;
+};
+
+void
+nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // This is called in response to a message from the plugin. Don't unload the
+  // plugin until the message handler is off the stack.
+  nsRefPtr<nsPluginUnloadRunnable> runnable =
+    new nsPluginUnloadRunnable(aPluginId);
+  NS_DispatchToMainThread(runnable);
+}
+
 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
 {
   nsresult rv = NS_ERROR_FAILURE;
   *aPlugin = nullptr;
 
   if (!aMimeType)
     return NS_ERROR_ILLEGAL_VALUE;
 
@@ -1609,16 +1687,27 @@ nsPluginHost::FirstPluginWithPath(const 
   for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
     if (tag->mFullPath.Equals(path)) {
       return tag;
     }
   }
   return nullptr;
 }
 
+nsPluginTag*
+nsPluginHost::PluginWithId(uint32_t aId)
+{
+  for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+    if (tag->mId == aId) {
+      return tag;
+    }
+  }
+  return nullptr;
+}
+
 namespace {
 
 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
 {
   PRTime fileModTime = 0;
 
 #if defined(XP_MACOSX)
   // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
@@ -1695,22 +1784,42 @@ struct CompareFilesByTime
   Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
   {
     return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
   }
 };
 
 } // anonymous namespace
 
+void
+nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
+{
+  aPluginTag->mNext = mPlugins;
+  mPlugins = aPluginTag;
+
+  if (aPluginTag->IsActive()) {
+    nsAdoptingCString disableFullPage =
+      Preferences::GetCString(kPrefDisableFullPage);
+    for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
+      if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
+        RegisterWithCategoryManager(aPluginTag->mMimeTypes[i],
+                                    ePluginRegister);
+      }
+    }
+  }
+}
+
 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
 
 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
                                             bool aCreatePluginList,
                                             bool *aPluginsChanged)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   NS_ENSURE_ARG_POINTER(aPluginsChanged);
   nsresult rv;
 
   *aPluginsChanged = false;
 
 #ifdef PLUGIN_LOGGING
   nsAutoCString dirPath;
   pluginsDir->GetNativePath(dirPath);
@@ -1890,65 +1999,32 @@ nsresult nsPluginHost::ScanPluginsDirect
     }
 
     // If we're not creating a plugin list, simply looking for changes,
     // then we're done.
     if (!aCreatePluginList) {
       return NS_OK;
     }
 
-    // Add plugin tags such that the list is ordered by modification date,
-    // newest to oldest. This is ugly, it'd be easier with just about anything
-    // other than a single-directional linked list.
-    if (mPlugins) {
-      nsPluginTag *prev = nullptr;
-      nsPluginTag *next = mPlugins;
-      while (next) {
-        if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
-          pluginTag->mNext = next;
-          if (prev) {
-            prev->mNext = pluginTag;
-          } else {
-            mPlugins = pluginTag;
-          }
-          break;
-        }
-        prev = next;
-        next = prev->mNext;
-        if (!next) {
-          prev->mNext = pluginTag;
-        }
-      }
-    } else {
-      mPlugins = pluginTag;
-    }
-
-    if (pluginTag->IsActive()) {
-      nsAdoptingCString disableFullPage =
-        Preferences::GetCString(kPrefDisableFullPage);
-      for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
-        if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
-          RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
-                                      ePluginRegister);
-        }
-      }
-    }
+    AddPluginTag(pluginTag);
   }
 
   if (warnOutdated) {
     Preferences::SetBool("plugins.update.notifyUser", true);
   }
 
   return NS_OK;
 }
 
 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                                                 bool aCreatePluginList,
                                                 bool *aPluginsChanged)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
     bool hasMore;
     while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
       nsCOMPtr<nsISupports> supports;
       nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
       if (NS_FAILED(rv))
         continue;
       nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
       if (NS_FAILED(rv))
@@ -1963,16 +2039,44 @@ nsresult nsPluginHost::ScanPluginsDirect
 
       // if changes are detected and we are not creating the list, do not proceed
       if (!aCreatePluginList && *aPluginsChanged)
         break;
     }
     return NS_OK;
 }
 
+void
+nsPluginHost::IncrementChromeEpoch()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  mPluginEpoch++;
+}
+
+uint32_t
+nsPluginHost::ChromeEpoch()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  return mPluginEpoch;
+}
+
+uint32_t
+nsPluginHost::ChromeEpochForContent()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+  return mPluginEpoch;
+}
+
+void
+nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+  mPluginEpoch = aEpoch;
+}
+
 nsresult nsPluginHost::LoadPlugins()
 {
 #ifdef ANDROID
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     return NS_OK;
   }
 #endif
   // do not do anything if it is already done
@@ -1985,35 +2089,92 @@ nsresult nsPluginHost::LoadPlugins()
 
   bool pluginschanged;
   nsresult rv = FindPlugins(true, &pluginschanged);
   if (NS_FAILED(rv))
     return rv;
 
   // only if plugins have changed will we notify plugin-change observers
   if (pluginschanged) {
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      IncrementChromeEpoch();
+    }
+
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService)
       obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
   }
 
   return NS_OK;
 }
 
+nsresult
+nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+  dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+  nsTArray<PluginTag> plugins;
+  uint32_t parentEpoch;
+  if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (parentEpoch != ChromeEpochForContent()) {
+    SetChromeEpochForContent(parentEpoch);
+    *aPluginsChanged = true;
+    if (!aCreatePluginList) {
+      return NS_OK;
+    }
+
+    for (size_t i = 0; i < plugins.Length(); i++) {
+      PluginTag& tag = plugins[i];
+
+      // Don't add the same plugin again.
+      if (PluginWithId(tag.id())) {
+        continue;
+      }
+
+      nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
+                                               tag.name().get(),
+                                               tag.description().get(),
+                                               tag.filename().get(),
+                                               "", // aFullPath
+                                               tag.version().get(),
+                                               nsTArray<nsCString>(tag.mimeTypes()),
+                                               nsTArray<nsCString>(tag.mimeDescriptions()),
+                                               nsTArray<nsCString>(tag.extensions()),
+                                               tag.isJavaPlugin(),
+                                               tag.isFlashPlugin(),
+                                               tag.lastModifiedTime(),
+                                               tag.isFromExtension());
+      AddPluginTag(pluginTag);
+    }
+  }
+
+  mPluginsLoaded = true;
+  return NS_OK;
+}
+
 // if aCreatePluginList is false we will just scan for plugins
 // and see if any changes have been made to the plugins.
 // This is needed in ReloadPlugins to prevent possible recursive reloads
 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
 {
   Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
 
   NS_ENSURE_ARG_POINTER(aPluginsChanged);
 
   *aPluginsChanged = false;
+
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
+  }
+
   nsresult rv;
 
   // Read cached plugins info. If the profile isn't yet available then don't
   // scan for plugins
   if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
     return NS_OK;
 
 #ifdef XP_WIN
@@ -2162,19 +2323,68 @@ nsresult nsPluginHost::FindPlugins(bool 
 
   // No more need for cached plugins. Clear it up.
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   return NS_OK;
 }
 
+bool
+mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
+                                        nsTArray<PluginTag>* aPlugins,
+                                        uint32_t* aNewPluginEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+  host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+  return true;
+}
+
+void
+nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
+                                    nsTArray<PluginTag>* aPlugins,
+                                    uint32_t* aNewPluginEpoch)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  // Load plugins so that the epoch is correct.
+  LoadPlugins();
+
+  *aNewPluginEpoch = ChromeEpoch();
+  if (aPluginEpoch == ChromeEpoch()) {
+    return;
+  }
+
+  nsTArray<nsRefPtr<nsPluginTag>> plugins;
+  GetPlugins(plugins);
+
+  for (size_t i = 0; i < plugins.Length(); i++) {
+    nsRefPtr<nsPluginTag> tag = plugins[i];
+    aPlugins->AppendElement(PluginTag(tag->mId,
+                                      tag->mName,
+                                      tag->mDescription,
+                                      tag->mMimeTypes,
+                                      tag->mMimeDescriptions,
+                                      tag->mExtensions,
+                                      tag->mIsJavaPlugin,
+                                      tag->mIsFlashPlugin,
+                                      tag->mFileName,
+                                      tag->mVersion,
+                                      tag->mLastModifiedTime,
+                                      tag->IsFromExtension()));
+  }
+}
+
 nsresult
 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   ReadPluginInfo();
   WritePluginInfo();
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   if (!aPluginTag) {
     return NS_OK;
   }
@@ -2256,16 +2466,17 @@ nsPluginHost::RegisterWithCategoryManage
                                   true);
     }
   }
 }
 
 nsresult
 nsPluginHost::WritePluginInfo()
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
   nsresult rv = NS_OK;
   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
   if (NS_FAILED(rv))
     return rv;
 
   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                         getter_AddRefs(mPluginRegFile));
@@ -2400,16 +2611,18 @@ nsPluginHost::WritePluginInfo()
   NS_ENSURE_SUCCESS(rv, rv);
   rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
   return rv;
 }
 
 nsresult
 nsPluginHost::ReadPluginInfo()
 {
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
   const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
   const long PLUGIN_REG_MAX_MIMETYPES = 1000;
 
   // we need to import the legacy flags from the plugin registry once
   const bool pluginStateImported =
     Preferences::GetDefaultBool("plugin.importedState", false);
 
   nsresult rv;
@@ -3537,16 +3750,17 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugi
     }
   }
 
   // Only after all instances have been invalidated is it safe to null
   // out nsPluginTag.mPlugin. The next time we try to create an
   // instance of this plugin we reload it (launch a new plugin process).
 
   crashedPluginTag->mPlugin = nullptr;
+  crashedPluginTag->mContentProcessRunningCount = 0;
 
 #ifdef XP_WIN
   CheckForDisabledWindows();
 #endif
 }
 
 nsNPAPIPluginInstance*
 nsPluginHost::FindInstance(const char *mimetype)
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -23,24 +23,26 @@
 #include "nsTArray.h"
 #include "nsTObserverArray.h"
 #include "nsITimer.h"
 #include "nsPluginTags.h"
 #include "nsPluginPlayPreviewInfo.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
 #include "nsCRT.h"
+#include "mozilla/plugins/PluginTypes.h"
 
 class nsNPAPIPlugin;
 class nsIComponentManager;
 class nsIFile;
 class nsIChannel;
 class nsPluginNativeWindow;
 class nsObjectLoadingContent;
 class nsPluginInstanceOwner;
+class nsPluginUnloadRunnable;
 class nsNPAPIPluginInstance;
 class nsNPAPIPluginStreamListener;
 class nsIPluginInstanceOwner;
 class nsIInputStream;
 class nsIStreamListener;
 
 class nsInvalidPluginTag : public nsISupports
 {
@@ -83,16 +85,19 @@ public:
   nsresult SetUpPluginInstance(const char *aMimeType,
                                nsIURI *aURL,
                                nsPluginInstanceOwner *aOwner);
   bool PluginExistsForType(const char* aMimeType);
 
   nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
   void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray);
+  void FindPluginsForContent(uint32_t aPluginEpoch,
+                             nsTArray<mozilla::plugins::PluginTag>* aPlugins,
+                             uint32_t* aNewPluginEpoch);
 
   nsresult GetURL(nsISupports* pluginInst,
                   const char* url,
                   const char* target,
                   nsNPAPIPluginStreamListener* streamListener,
                   const char* altHost,
                   const char* referrer,
                   bool forceJSEnabled);
@@ -186,22 +191,26 @@ public:
   nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
                                      nsObjectLoadingContent *aContent,
                                      nsPluginInstanceOwner** aOwner);
 
   // Does not accept nullptr and should never fail.
   nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
 
   nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
+  nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
+  void NotifyContentModuleDestroyed(uint32_t aPluginId);
 
   nsresult NewPluginStreamListener(nsIURI* aURL,
                                    nsNPAPIPluginInstance* aInstance,
                                    nsIStreamListener **aStreamListener);
 
 private:
+  friend class nsPluginUnloadRunnable;
+
   nsresult
   TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
 
   nsPluginTag*
   FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
 
   // Return an nsPluginTag for this type, if any.  If aCheckEnabled is
   // true, only enabled plugins will be returned.
@@ -209,24 +218,28 @@ private:
   FindPluginForType(const char* aMimeType, bool aCheckEnabled);
 
   nsPluginTag*
   FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
+  nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
+
   nsresult
   FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
 
   // Registers or unregisters the given mime type with the category manager
   // (performs no checks - see UpdateCategoryManager)
   enum nsRegisterType { ePluginRegister, ePluginUnregister };
   void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType);
 
+  void AddPluginTag(nsPluginTag* aPluginTag);
+
   nsresult
   ScanPluginsDirectory(nsIFile *pluginsDir,
                        bool aCreatePluginList,
                        bool *aPluginsChanged);
 
   nsresult
   ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                            bool aCreatePluginList,
@@ -250,21 +263,33 @@ private:
   // Checks to see if a tag object is in our list of live tags.
   bool IsLiveTag(nsIPluginTag* tag);
   
   // Checks our list of live tags for an equivalent tag.
   nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag);
     
   // Returns the first plugin at |path|
   nsPluginTag* FirstPluginWithPath(const nsCString& path);
+  nsPluginTag* PluginWithId(uint32_t aId);
 
   nsresult EnsurePrivateDirServiceProvider();
 
   void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
 
+  // To be used by the chrome process whenever the set of plugins changes.
+  void IncrementChromeEpoch();
+
+  // To be used by the chrome process; returns the current epoch.
+  uint32_t ChromeEpoch();
+
+  // To be used by the content process to get/set the last observed epoch value
+  // from the chrome process.
+  uint32_t ChromeEpochForContent();
+  void SetChromeEpochForContent(uint32_t aEpoch);
+
   nsRefPtr<nsPluginTag> mPlugins;
   nsRefPtr<nsPluginTag> mCachedPlugins;
   nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
   nsTArray< nsRefPtr<nsPluginPlayPreviewInfo> > mPlayPreviewMimeTypes;
   bool mPluginsLoaded;
 
   // set by pref plugin.override_internal_types
   bool mOverrideInternalTypes;
@@ -290,16 +315,22 @@ private:
   nsresult NormalizeHostname(nsCString& host);
   nsresult EnumerateSiteData(const nsACString& domain,
                              const InfallibleTArray<nsCString>& sites,
                              InfallibleTArray<nsCString>& result,
                              bool firstMatchOnly);
 
   nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
 
+  // This epoch increases each time we load the list of plugins from disk.
+  // In the chrome process, this stores the actual epoch.
+  // In the content process, this stores the last epoch value observed
+  // when reading plugins from chrome.
+  uint32_t mPluginEpoch;
+
   static nsIFile *sPluginTempDir;
 
   // We need to hold a global ptr to ourselves because we register for
   // two different CIDs for some reason...
   static nsPluginHost* sInst;
 };
 
 class MOZ_STACK_CLASS PluginDestructionGuard : protected PRCList
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -61,20 +61,24 @@ MakePrefNameForPlugin(const char* const 
 static nsCString
 GetStatePrefNameForPlugin(nsPluginTag* aTag)
 {
   return MakePrefNameForPlugin("state", aTag);
 }
 
 /* nsPluginTag */
 
+uint32_t nsPluginTag::sNextId;
+
 nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
                          int64_t aLastModifiedTime,
                          bool fromExtension)
-  : mName(aPluginInfo->fName),
+  : mId(sNextId++),
+    mContentProcessRunningCount(0),
+    mName(aPluginInfo->fName),
     mDescription(aPluginInfo->fDescription),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
     mFileName(aPluginInfo->fFileName),
     mFullPath(aPluginInfo->fFullPath),
     mVersion(aPluginInfo->fVersion),
     mLastModifiedTime(aLastModifiedTime),
@@ -98,17 +102,19 @@ nsPluginTag::nsPluginTag(const char* aNa
                          const char* aVersion,
                          const char* const* aMimeTypes,
                          const char* const* aMimeDescriptions,
                          const char* const* aExtensions,
                          int32_t aVariants,
                          int64_t aLastModifiedTime,
                          bool fromExtension,
                          bool aArgsAreUTF8)
-  : mName(aName),
+  : mId(sNextId++),
+    mContentProcessRunningCount(0),
+    mName(aName),
     mDescription(aDescription),
     mLibrary(nullptr),
     mIsJavaPlugin(false),
     mIsFlashPlugin(false),
     mFileName(aFileName),
     mFullPath(aFullPath),
     mVersion(aVersion),
     mLastModifiedTime(aLastModifiedTime),
@@ -119,16 +125,49 @@ nsPluginTag::nsPluginTag(const char* aNa
 {
   InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
            static_cast<uint32_t>(aVariants));
   if (!aArgsAreUTF8)
     EnsureMembersAreUTF8();
   FixupVersion();
 }
 
+nsPluginTag::nsPluginTag(uint32_t aId,
+                         const char* aName,
+                         const char* aDescription,
+                         const char* aFileName,
+                         const char* aFullPath,
+                         const char* aVersion,
+                         nsTArray<nsCString> aMimeTypes,
+                         nsTArray<nsCString> aMimeDescriptions,
+                         nsTArray<nsCString> aExtensions,
+                         bool aIsJavaPlugin,
+                         bool aIsFlashPlugin,
+                         int64_t aLastModifiedTime,
+                         bool aFromExtension)
+  : mId(aId),
+    mContentProcessRunningCount(0),
+    mName(aName),
+    mDescription(aDescription),
+    mMimeTypes(aMimeTypes),
+    mMimeDescriptions(aMimeDescriptions),
+    mExtensions(aExtensions),
+    mLibrary(nullptr),
+    mIsJavaPlugin(aIsJavaPlugin),
+    mIsFlashPlugin(aIsFlashPlugin),
+    mFileName(aFileName),
+    mVersion(aVersion),
+    mLastModifiedTime(aLastModifiedTime),
+    mNiceFileName(),
+    mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+    mCachedBlocklistStateValid(false),
+    mIsFromExtension(aFromExtension)
+{
+}
+
 nsPluginTag::~nsPluginTag()
 {
   NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
 }
 
 NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag)
 
 void nsPluginTag::InitMime(const char* const* aMimeTypes,
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -46,16 +46,29 @@ public:
               const char* aVersion,
               const char* const* aMimeTypes,
               const char* const* aMimeDescriptions,
               const char* const* aExtensions,
               int32_t aVariants,
               int64_t aLastModifiedTime,
               bool fromExtension,
               bool aArgsAreUTF8 = false);
+  nsPluginTag(uint32_t aId,
+              const char* aName,
+              const char* aDescription,
+              const char* aFileName,
+              const char* aFullPath,
+              const char* aVersion,
+              nsTArray<nsCString> aMimeTypes,
+              nsTArray<nsCString> aMimeDescriptions,
+              nsTArray<nsCString> aExtensions,
+              bool aIsJavaPlugin,
+              bool aIsFlashPlugin,
+              int64_t aLastModifiedTime,
+              bool aFromExtension);
 
   void TryUnloadPlugin(bool inShutdown);
 
   // plugin is enabled and not blocklisted
   bool IsActive();
 
   bool IsEnabled();
   void SetEnabled(bool enabled);
@@ -69,16 +82,20 @@ public:
   void ImportFlagsToPrefs(uint32_t flag);
 
   bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
   nsCString GetNiceFileName();
 
   bool IsFromExtension() const;
 
   nsRefPtr<nsPluginTag> mNext;
+  uint32_t      mId;
+
+  // Number of PluginModuleParents living in all content processes.
+  size_t        mContentProcessRunningCount;
   nsCString     mName; // UTF-8
   nsCString     mDescription; // UTF-8
   nsTArray<nsCString> mMimeTypes; // UTF-8
   nsTArray<nsCString> mMimeDescriptions; // UTF-8
   nsTArray<nsCString> mExtensions; // UTF-8
   PRLibrary     *mLibrary;
   nsRefPtr<nsNPAPIPlugin> mPlugin;
   bool          mIsJavaPlugin;
@@ -101,11 +118,13 @@ private:
   bool          mIsFromExtension;
 
   void InitMime(const char* const* aMimeTypes,
                 const char* const* aMimeDescriptions,
                 const char* const* aExtensions,
                 uint32_t aVariantCount);
   nsresult EnsureMembersAreUTF8();
   void FixupVersion();
+
+  static uint32_t sNextId;
 };
 
 #endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -1,28 +1,31 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PPluginInstance;
 include protocol PPluginScriptableObject;
 include protocol PCrashReporter;
+include protocol PContent;
 
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace plugins {
 
 intr protocol PPluginModule
 {
+  bridges PContent, PPluginModule;
+
   manages PPluginInstance;
   manages PCrashReporter;
 
 both:
   // Window-specific message which instructs the interrupt mechanism to enter
   // a nested event loop for the current interrupt call.
   async ProcessNativeEventsInInterruptCall();
 
@@ -102,16 +105,20 @@ parent:
 
   // OS X Specific calls to allow the plugin to manage the cursor.
   async SetCursor(NSCursorInfo cursorInfo);
   async ShowCursor(bool show);
   async PushCursor(NSCursorInfo cursorInfo);
   async PopCursor();
   sync GetNativeCursorsSupported() returns (bool supported);
 
-  sync NPN_SetException(nullable PPluginScriptableObject actor,
-                        nsCString message);
+  sync NPN_SetException(nsCString message);
 
   async NPN_ReloadPlugins(bool aReloadPages);
+
+  // Notifies the chrome process that a PluginModuleChild linked to a content
+  // process was destroyed. The chrome process may choose to asynchronously shut
+  // down the plugin process in response.
+  async NotifyContentModuleDestroyed();
 };
 
 } // namespace plugins
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_PluginBridge_h
+#define mozilla_plugins_PluginBridge_h
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+}
+
+namespace plugins {
+
+bool
+SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent);
+
+bool
+FindPluginsForContent(uint32_t aPluginEpoch,
+                      nsTArray<PluginTag>* aPlugins,
+                      uint32_t* aNewPluginEpoch);
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginBridge_h
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -64,17 +64,17 @@ private:
   uint32_t mResponseTimeMs;
   uint32_t mTimeoutMs;
 };
 } // anonymous namespace
 
 namespace mozilla {
 namespace plugins {
 
-PluginHangUIParent::PluginHangUIParent(PluginModuleParent* aModule,
+PluginHangUIParent::PluginHangUIParent(PluginModuleChromeParent* aModule,
                                        const int32_t aHangUITimeoutPref,
                                        const int32_t aChildTimeoutPref)
   : mMutex("mozilla::plugins::PluginHangUIParent::mMutex"),
     mModule(aModule),
     mTimeoutPrefMs(static_cast<uint32_t>(aHangUITimeoutPref) * 1000U),
     mIPCTimeoutMs(static_cast<uint32_t>(aChildTimeoutPref) * 1000U),
     mMainThreadMessageLoop(MessageLoop::current()),
     mIsShowing(false),
--- a/dom/plugins/ipc/PluginHangUIParent.h
+++ b/dom/plugins/ipc/PluginHangUIParent.h
@@ -15,32 +15,32 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 
 #include "MiniShmParent.h"
 
 namespace mozilla {
 namespace plugins {
 
-class PluginModuleParent;
+class PluginModuleChromeParent;
 
 /**
  * This class is responsible for launching and communicating with the 
  * plugin-hang-ui process.
  *
  * NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent" 
  *       is describing the fact that firefox is the parent process to the 
  *       plugin-hang-ui process, which is the PluginHangUIChild.
  *       PluginHangUIParent and PluginHangUIChild are a matched pair.
  * @see PluginHangUIChild
  */
 class PluginHangUIParent : public MiniShmObserver
 {
 public:
-  PluginHangUIParent(PluginModuleParent* aModule,
+  PluginHangUIParent(PluginModuleChromeParent* aModule,
                      const int32_t aHangUITimeoutPref,
                      const int32_t aChildTimeoutPref);
   virtual ~PluginHangUIParent();
 
   /**
    * Spawn the plugin-hang-ui.exe child process and terminate the given 
    * plugin container process if the user elects to stop the hung plugin.
    *
@@ -130,17 +130,17 @@ private:
   bool
   UnwatchHangUIChildProcess(bool aWait);
 
   static
   VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer);
 
 private:
   Mutex mMutex;
-  PluginModuleParent* mModule;
+  PluginModuleChromeParent* mModule;
   const uint32_t mTimeoutPrefMs;
   const uint32_t mIPCTimeoutMs;
   MessageLoop* mMainThreadMessageLoop;
   bool mIsShowing;
   unsigned int mLastUserResponse;
   base::ProcessHandle mHangUIProcessHandle;
   NativeWindowHandle mMainWindowHandle;
   HANDLE mRegWait;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -160,16 +160,17 @@ PluginInstanceChild::PluginInstanceChild
     , mIsTransparent(false)
     , mSurfaceType(gfxSurfaceType::Max)
     , mCurrentInvalidateTask(nullptr)
     , mCurrentAsyncSetWindowTask(nullptr)
     , mPendingPluginCall(false)
     , mDoAlphaExtraction(false)
     , mHasPainted(false)
     , mSurfaceDifferenceRect(0,0,0,0)
+    , mDestroyed(false)
 {
     memset(&mWindow, 0, sizeof(mWindow));
     mWindow.type = NPWindowTypeWindow;
     mData.ndata = (void*) this;
     mData.pdata = nullptr;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     mWindow.ws_info = &mWsInfo;
     memset(&mWsInfo, 0, sizeof(mWsInfo));
@@ -207,17 +208,17 @@ PluginInstanceChild::~PluginInstanceChil
         UnscheduleTimer(mCARefreshTimer);
     }
 #endif
 }
 
 int
 PluginInstanceChild::GetQuirks()
 {
-    return PluginModuleChild::current()->GetQuirks();
+    return PluginModuleChild::GetChrome()->GetQuirks();
 }
 
 NPError
 PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
                                                  NPObject** aObject)
 {
     PluginScriptableObjectChild* actor = nullptr;
     NPError result = NPERR_NO_ERROR;
@@ -2388,17 +2389,17 @@ PluginInstanceChild::GetActorForNPObject
     if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
         // One of ours! It's a browser-provided object.
         ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
         NS_ASSERTION(object->parent, "Null actor!");
         return object->parent;
     }
 
     PluginScriptableObjectChild* actor =
-        PluginModuleChild::current()->GetActorForNPObject(aObject);
+        PluginScriptableObjectChild::GetActorForNPObject(aObject);
     if (actor) {
         // Plugin-provided object that we've previously wrapped.
         return actor;
     }
 
     actor = new PluginScriptableObjectChild(LocalObject);
     if (!SendPPluginScriptableObjectConstructor(actor)) {
         NS_ERROR("Failed to send constructor message!");
@@ -3271,17 +3272,17 @@ PluginInstanceChild::ShowPluginFrame()
         XSync(mWsInfo.display, False);
     } else
 #endif
 #ifdef XP_WIN
     if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
         SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
         if (!mCurrentSurfaceActor) {
             base::SharedMemoryHandle handle = nullptr;
-            s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
+            s->ShareToProcess(OtherProcess(), &handle);
 
             mCurrentSurfaceActor =
                 SendPPluginSurfaceConstructor(handle,
                                               mCurrentSurface->GetSize(),
                                               haveTransparentPixels);
         }
         currSurf = mCurrentSurfaceActor;
         s->Flush();
@@ -3673,22 +3674,23 @@ PluginInstanceChild::ClearAllSurfaces()
         mCGLayer = nullptr;
     }
 
     mDoubleBufferCARenderer.ClearFrontSurface();
     mDoubleBufferCARenderer.ClearBackSurface();
 #endif
 }
 
-bool
-PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+void
+PluginInstanceChild::Destroy()
 {
-    PLUGIN_LOG_DEBUG_METHOD;
-    AssertPluginThread();
-    *aResult = NPERR_NO_ERROR;
+    if (mDestroyed) {
+        return;
+    }
+    mDestroyed = true;
 
 #if defined(OS_WIN)
     SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
 #endif
 
     InfallibleTArray<PBrowserStreamChild*> streams;
     ManagedPBrowserStreamChild(streams);
 
@@ -3702,17 +3704,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N
     for (uint32_t i = 0; i < streams.Length(); ++i)
         static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
 
     mTimers.Clear();
 
     // NPP_Destroy() should be a synchronization point for plugin threads
     // calling NPN_AsyncCall: after this function returns, they are no longer
     // allowed to make async calls on this instance.
-    PluginModuleChild::current()->NPP_Destroy(this);
+    static_cast<PluginModuleChild *>(Manager())->NPP_Destroy(this);
     mData.ndata = 0;
 
     if (mCurrentInvalidateTask) {
         mCurrentInvalidateTask->Cancel();
         mCurrentInvalidateTask = nullptr;
     }
     if (mCurrentAsyncSetWindowTask) {
         mCurrentAsyncSetWindowTask->Cancel();
@@ -3724,17 +3726,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N
             mAsyncInvalidateTask->Cancel();
             mAsyncInvalidateTask = nullptr;
         }
     }
 
     ClearAllSurfaces();
 
     mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
-    PluginModuleChild::current()->FindNPObjectsForInstance(this);
+    PluginScriptableObjectChild::NotifyOfInstanceShutdown(this);
 
     mDeletingHash->EnumerateEntries(InvalidateObject, nullptr);
     mDeletingHash->EnumerateEntries(DeleteObject, nullptr);
 
     // Null out our cached actors as they should have been killed in the
     // PluginInstanceDestroyed call above.
     mCachedWindowActor = nullptr;
     mCachedElementActor = nullptr;
@@ -3756,11 +3758,27 @@ PluginInstanceChild::AnswerNPP_Destroy(N
 #ifdef MOZ_WIDGET_GTK
     if (mWindow.type == NPWindowTypeWindow && !mXEmbed) {
       xt_client_xloop_destroy();
     }
 #endif
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     DeleteWindow();
 #endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+    PLUGIN_LOG_DEBUG_METHOD;
+    AssertPluginThread();
+    *aResult = NPERR_NO_ERROR;
+
+    Destroy();
 
     return true;
 }
+
+void
+PluginInstanceChild::ActorDestroy(ActorDestroyReason why)
+{
+    Destroy();
+}
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -50,17 +50,18 @@ namespace plugins {
 class PBrowserStreamChild;
 class BrowserStreamChild;
 class StreamNotifyChild;
 
 class PluginInstanceChild : public PPluginInstanceChild
 {
     friend class BrowserStreamChild;
     friend class PluginStreamChild;
-    friend class StreamNotifyChild; 
+    friend class StreamNotifyChild;
+    friend class PluginScriptableObjectChild;
 
 #ifdef OS_WIN
     friend LRESULT CALLBACK PluginWindowProc(HWND hWnd,
                                              UINT message,
                                              WPARAM wParam,
                                              LPARAM lParam);
     static LRESULT CALLBACK PluginWindowProcInternal(HWND hWnd,
                                                      UINT message,
@@ -519,16 +520,20 @@ private:
     void ClearCurrentSurface();
 
     // Swap mCurrentSurface/mBackSurface and their associated actors
     void SwapSurfaces();
 
     // Clear all surfaces in response to NPP_Destroy
     void ClearAllSurfaces();
 
+    void Destroy();
+
+    void ActorDestroy(ActorDestroyReason why);
+
     // Set as true when SetupLayer called
     // and go with different path in InvalidateRect function
     bool mLayersRendering;
 
     // Current surface available for rendering
     nsRefPtr<gfxASurface> mCurrentSurface;
 
     // Back surface, just keeping reference to
@@ -592,14 +597,17 @@ private:
     // that we ask a plugin to paint at least once even if it's invisible;
     // some plugin (instances) rely on this in order to work properly.
     bool mHasPainted;
 
     // Cached rectangle rendered to previous surface(mBackSurface)
     // Used for reading back to current surface and syncing data,
     // in plugin coordinates.
     nsIntRect mSurfaceDifferenceRect;
+
+    // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
+    bool mDestroyed;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1773,17 +1773,17 @@ PluginInstanceParent::SharedSurfaceSetWi
     if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
                                            newPort.width, newPort.height, false)))
       return false;
 
     // save the new shared surface size we just allocated
     mSharedSize = newPort;
 
     base::SharedMemoryHandle handle;
-    if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
+    if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherProcess(), &handle)))
       return false;
 
     aRemoteWindow.surfaceHandle = handle;
 
     return true;
 }
 
 void
--- a/dom/plugins/ipc/PluginInterposeOSX.mm
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -540,17 +540,17 @@ void NSCursorInfo::SetCustomImageData(ui
 }
 
 // This should never be called from the browser process -- only from the
 // plugin process.
 bool NSCursorInfo::GetNativeCursorsSupported()
 {
   if (mNativeCursorsSupported == -1) {
     AssertPluginThread();
-    PluginModuleChild *pmc = PluginModuleChild::current();
+    PluginModuleChild *pmc = PluginModuleChild::GetChrome();
     if (pmc) {
       bool result = pmc->GetNativeCursorsSupported();
       if (result) {
         mNativeCursorsSupported = 1;
       } else {
         mNativeCursorsSupported = 0;
       }
     }
@@ -686,60 +686,60 @@ void FocusPluginProcess() {
     SetFrontProcess(&this_process);
   }
 }
 
 void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
                                      bool modal) {
   AssertPluginThread();
 
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc)
     pmc->PluginShowWindow(window_id, modal, bounds);
 }
 
 void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
   AssertPluginThread();
 
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc)
     pmc->PluginHideWindow(window_id);
 }
 
 void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->SetCursor(aCursorInfo);
   }
 }
 
 void NotifyBrowserOfShowCursor(bool show)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->ShowCursor(show);
   }
 }
 
 void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->PushCursor(aCursorInfo);
   }
 }
 
 void NotifyBrowserOfPopCursor()
 {
   AssertPluginThread();
-  PluginModuleChild *pmc = PluginModuleChild::current();
+  PluginModuleChild *pmc = PluginModuleChild::GetChrome();
   if (pmc) {
     pmc->PopCursor();
   }
 }
 
 struct WindowInfo {
   uint32_t window_id;
   CGRect bounds;
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -60,17 +60,18 @@ using mozilla::dom::CrashReporterChild;
 using mozilla::dom::PCrashReporterChild;
 
 #if defined(XP_WIN)
 const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
 #endif
 
 namespace {
-PluginModuleChild* gInstance = nullptr;
+PluginModuleChild* gChromeInstance = nullptr;
+nsTArray<PluginModuleChild*>* gAllInstances;
 }
 
 #ifdef MOZ_WIDGET_QT
 typedef void (*_gtk_init_fn)(int argc, char **argv);
 static _gtk_init_fn s_gtk_init = nullptr;
 static PRLibrary *sGtkLib = nullptr;
 #endif
 
@@ -79,80 +80,156 @@ static PRLibrary *sGtkLib = nullptr;
 static bool gDelayFlashFocusReplyUntilEval = false;
 // Used to fix GetWindowInfo problems with internal flash settings dialogs
 static WindowsDllInterceptor sUser32Intercept;
 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
 static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
 static HWND sBrowserHwnd = nullptr;
 #endif
 
-PluginModuleChild::PluginModuleChild()
+/* static */
+PluginModuleChild*
+PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+                                           base::ProcessId aOtherProcess)
+{
+    PluginModuleChild* child = new PluginModuleChild(false);
+    ProcessHandle handle;
+    if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+        // XXX need to kill |aOtherProcess|, it's boned
+        return nullptr;
+    }
+
+    if (!child->InitForContent(handle, XRE_GetIOMessageLoop(), aTransport)) {
+        return nullptr;
+    }
+
+    return child;
+}
+
+PluginModuleChild::PluginModuleChild(bool aIsChrome)
   : mLibrary(0)
   , mPluginFilename("")
   , mQuirks(QUIRKS_NOT_INITIALIZED)
+  , mIsChrome(aIsChrome)
+  , mTransport(nullptr)
   , mShutdownFunc(0)
   , mInitializeFunc(0)
 #if defined(OS_WIN) || defined(OS_MACOSX)
   , mGetEntryPointsFunc(0)
 #elif defined(MOZ_WIDGET_GTK)
   , mNestedLoopTimerId(0)
 #elif defined(MOZ_WIDGET_QT)
   , mNestedLoopTimerObject(0)
 #endif
 #ifdef OS_WIN
   , mNestedEventHook(nullptr)
   , mGlobalCallWndProcHook(nullptr)
 #endif
 {
-    NS_ASSERTION(!gInstance, "Something terribly wrong here!");
+    if (!gAllInstances) {
+        gAllInstances = new nsTArray<PluginModuleChild*>(1);
+    }
+    gAllInstances->AppendElement(this);
+
     memset(&mFunctions, 0, sizeof(mFunctions));
-    memset(&mSavedData, 0, sizeof(mSavedData));
-    gInstance = this;
+    if (mIsChrome) {
+        MOZ_ASSERT(!gChromeInstance);
+        gChromeInstance = this;
+    }
     mUserAgent.SetIsVoid(true);
 #ifdef XP_MACOSX
     mac_plugin_interposing::child::SetUpCocoaInterposing();
 #endif
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
-    NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
-
-    // We don't unload the plugin library in case it uses atexit handlers or
-    // other similar hooks.
+    if (mTransport) {
+        // For some reason IPDL doesn't autmatically delete the channel for a
+        // bridged protocol (bug 1090570). So we have to do it ourselves. This
+        // code is only invoked for PluginModuleChild instances created via
+        // bridging; otherwise mTransport is null.
+        XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+    }
 
-    DeinitGraphics();
-    PluginScriptableObjectChild::ClearIdentifiers();
+    gAllInstances->RemoveElement(this);
+    MOZ_ASSERT_IF(mIsChrome, gAllInstances->Length() == 0);
+    if (gAllInstances->IsEmpty()) {
+        delete gAllInstances;
+        gAllInstances = nullptr;
+    }
 
-    gInstance = nullptr;
+    if (mIsChrome) {
+        MOZ_ASSERT(gChromeInstance == this);
+
+        // We don't unload the plugin library in case it uses atexit handlers or
+        // other similar hooks.
+
+        DeinitGraphics();
+        PluginScriptableObjectChild::ClearIdentifiers();
+
+        gChromeInstance = nullptr;
+    }
 }
 
 // static
 PluginModuleChild*
-PluginModuleChild::current()
+PluginModuleChild::GetChrome()
 {
-    NS_ASSERTION(gInstance, "Null instance!");
-    return gInstance;
+    MOZ_ASSERT(gChromeInstance);
+    return gChromeInstance;
 }
 
 bool
-PluginModuleChild::Init(const std::string& aPluginFilename,
-                        base::ProcessHandle aParentProcessHandle,
-                        MessageLoop* aIOLoop,
-                        IPC::Channel* aChannel)
+PluginModuleChild::CommonInit(base::ProcessHandle aParentProcessHandle,
+                              MessageLoop* aIOLoop,
+                              IPC::Channel* aChannel)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
-    GetIPCChannel()->SetAbortOnError(true);
-
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
+    // Bug 1090573 - Don't do this for connections to content processes.
     GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 
+    if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+        return false;
+
+    memset((void*) &mFunctions, 0, sizeof(mFunctions));
+    mFunctions.size = sizeof(mFunctions);
+    mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+    return true;
+}
+
+bool
+PluginModuleChild::InitForContent(base::ProcessHandle aParentProcessHandle,
+                                  MessageLoop* aIOLoop,
+                                  IPC::Channel* aChannel)
+{
+    if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
+        return false;
+    }
+
+    mTransport = aChannel;
+
+    mLibrary = GetChrome()->mLibrary;
+    mQuirks = GetChrome()->mQuirks;
+    mFunctions = GetChrome()->mFunctions;
+
+    return true;
+}
+
+bool
+PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
+                                 base::ProcessHandle aParentProcessHandle,
+                                 MessageLoop* aIOLoop,
+                                 IPC::Channel* aChannel)
+{
 #ifdef XP_WIN
     COMMessageFilter::Initialize(this);
 #endif
 
     NS_ASSERTION(aChannel, "need a channel");
 
     if (!InitGraphics())
         return false;
@@ -196,22 +273,21 @@ PluginModuleChild::Init(const std::strin
 #endif
     {
         nsresult rv = pluginFile.LoadPlugin(&mLibrary);
         if (NS_FAILED(rv))
             return false;
     }
     NS_ASSERTION(mLibrary, "couldn't open shared object");
 
-    if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+    if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
         return false;
+    }
 
-    memset((void*) &mFunctions, 0, sizeof(mFunctions));
-    mFunctions.size = sizeof(mFunctions);
-    mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+    GetIPCChannel()->SetAbortOnError(true);
 
     // TODO: use PluginPRLibrary here
 
 #if defined(OS_LINUX) || defined(OS_BSD)
     mShutdownFunc =
         (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
 
     // create the new plugin handler
@@ -586,16 +662,17 @@ PluginModuleChild::DeinitGraphics()
     XCloseDisplay(DefaultXDisplay());
 #endif
 }
 
 bool
 PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
 {
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #if defined XP_WIN
     mozilla::widget::StopAudioSession();
 #endif
 
     // the PluginModuleParent shuts down this process after this interrupt
     // call pops off its stack
 
@@ -673,16 +750,23 @@ PluginModuleChild::RecvSetAudioSessionDa
 
 void
 PluginModuleChild::QuickExit()
 {
     NS_WARNING("plugin process _exit()ing");
     _exit(0);
 }
 
+PPluginModuleChild*
+PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+                                           base::ProcessId aOtherProcess)
+{
+    return PluginModuleChild::CreateForContentProcess(aTransport, aOtherProcess);
+}
+
 PCrashReporterChild*
 PluginModuleChild::AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id,
                                             uint32_t* processType)
 {
     return new CrashReporterChild();
 }
 
 bool
@@ -703,16 +787,27 @@ PluginModuleChild::AnswerPCrashReporterC
     *processType = XRE_GetProcessType();
 #endif
     return true;
 }
 
 void
 PluginModuleChild::ActorDestroy(ActorDestroyReason why)
 {
+    if (!mIsChrome) {
+        PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+        if (chromeInstance) {
+            chromeInstance->SendNotifyContentModuleDestroyed();
+        }
+
+        // Destroy ourselves once we finish other teardown activities.
+        MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this));
+        return;
+    }
+
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
         QuickExit();
     }
 
     // doesn't matter why we're being destroyed; it's up to us to
     // initiate (clean) shutdown
     XRE_ShutdownChildProcess();
@@ -727,69 +822,16 @@ const char*
 PluginModuleChild::GetUserAgent()
 {
     if (mUserAgent.IsVoid() && !CallNPN_UserAgent(&mUserAgent))
         return nullptr;
 
     return NullableStringGet(mUserAgent);
 }
 
-bool
-PluginModuleChild::RegisterActorForNPObject(NPObject* aObject,
-                                            PluginScriptableObjectChild* aActor)
-{
-    AssertPluginThread();
-    NS_ASSERTION(aObject && aActor, "Null pointer!");
-
-    NPObjectData* d = mObjectMap.GetEntry(aObject);
-    if (!d) {
-        NS_ERROR("NPObject not in object table");
-        return false;
-    }
-
-    d->actor = aActor;
-    return true;
-}
-
-void
-PluginModuleChild::UnregisterActorForNPObject(NPObject* aObject)
-{
-    AssertPluginThread();
-    NS_ASSERTION(aObject, "Null pointer!");
-
-    NPObjectData* d = mObjectMap.GetEntry(aObject);
-    NS_ASSERTION(d, "NPObject not in object table");
-    if (d) {
-        d->actor = nullptr;
-    }
-}
-
-PluginScriptableObjectChild*
-PluginModuleChild::GetActorForNPObject(NPObject* aObject)
-{
-    AssertPluginThread();
-    NS_ASSERTION(aObject, "Null pointer!");
-
-    NPObjectData* d = mObjectMap.GetEntry(aObject);
-    if (!d) {
-        NS_ERROR("Plugin using object not created with NPN_CreateObject?");
-        return nullptr;
-    }
-
-    return d->actor;
-}
-
-#ifdef DEBUG
-bool
-PluginModuleChild::NPObjectIsRegistered(NPObject* aObject)
-{
-    return !!mObjectMap.GetEntry(aObject);
-}
-#endif
-
 //-----------------------------------------------------------------------------
 // FIXME/cjones: just getting this out of the way for the moment ...
 
 namespace mozilla {
 namespace plugins {
 namespace child {
 
 static NPError
@@ -1080,17 +1122,17 @@ NPError
 
         case NPNVjavascriptEnabledBool: // Intentional fall-through
         case NPNVasdEnabledBool: // Intentional fall-through
         case NPNVisOfflineBool: // Intentional fall-through
         case NPNVSupportsXEmbedBool: // Intentional fall-through
         case NPNVSupportsWindowless: { // Intentional fall-through
             NPError result;
             bool value;
-            PluginModuleChild::current()->
+            PluginModuleChild::GetChrome()->
                 CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value);
             *(NPBool*)aValue = value ? true : false;
             return result;
         }
 #if defined(MOZ_WIDGET_GTK)
         case NPNVxDisplay: {
             if (aNPP) {
                 return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
@@ -1271,17 +1313,20 @@ uint32_t
 }
 
 void
 _reloadplugins(NPBool aReloadPages)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
 
-    PluginModuleChild::current()->SendNPN_ReloadPlugins(!!aReloadPages);
+    // Send the reload message to all modules. Chrome will need to reload from
+    // disk and content will need to request a new list of plugin tags from
+    // chrome.
+    PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages);
 }
 
 void
 _invalidaterect(NPP aNPP,
                 NPRect* aInvalidRect)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
@@ -1310,17 +1355,17 @@ void
     // never be necessary.
 }
 
 const char*
 _useragent(NPP aNPP)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD(nullptr);
-    return PluginModuleChild::current()->GetUserAgent();
+    return PluginModuleChild::GetChrome()->GetUserAgent();
 }
 
 void*
 _memalloc(uint32_t aSize)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     // Only assert plugin thread here for consistency with in-process plugins.
     AssertPluginThread();
@@ -1541,28 +1586,17 @@ void
 
 void
 _setexception(NPObject* aNPObj,
               const NPUTF8* aMessage)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
 
-    PluginModuleChild* self = PluginModuleChild::current();
-    PluginScriptableObjectChild* actor = nullptr;
-    if (aNPObj) {
-        actor = self->GetActorForNPObject(aNPObj);
-        if (!actor) {
-            NS_ERROR("Failed to get actor!");
-            return;
-        }
-    }
-
-    self->SendNPN_SetException(static_cast<PPluginScriptableObjectChild*>(actor),
-                               NullableString(aMessage));
+    // Do nothing. We no longer support this API.
 }
 
 void
 _pushpopupsenabledstate(NPP aNPP,
                         NPBool aEnabled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     ENSURE_PLUGIN_THREAD_VOID();
@@ -1742,17 +1776,17 @@ NPError
 
     NPBool success = _convertpoint(instance, 
                                   pluginX,  pluginY, NPCoordinateSpacePlugin, 
                                  &screenX, &screenY, NPCoordinateSpaceScreen);
 
     if (success) {
         return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
                                     screenX, screenY,
-                                    PluginModuleChild::current(),
+                                    PluginModuleChild::GetChrome(),
                                     ProcessBrowserEvents);
     } else {
         NS_WARNING("Convertpoint failed, could not created contextmenu.");
         return NPERR_GENERIC_ERROR;
     }
 
 #else
     NS_WARNING("Not supported on this platform!");
@@ -1800,32 +1834,34 @@ void
 
 //-----------------------------------------------------------------------------
 
 bool
 PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #if defined(OS_LINUX) || defined(OS_BSD)
     return true;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     *_retval = mGetEntryPointsFunc(&mFunctions);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
 bool
 PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
+    MOZ_ASSERT(mIsChrome);
 
 #ifdef OS_WIN
     SetEventHooks();
 #endif
 
 #ifdef MOZ_X11
     // Send the parent our X socket to act as a proxy reference for our X
     // resources.
@@ -2045,20 +2081,17 @@ PluginModuleChild::NPN_CreateObject(NPP 
     }
 
     if (newObject) {
         newObject->_class = aClass;
         newObject->referenceCount = 1;
         NS_LOG_ADDREF(newObject, 1, "NPObject", sizeof(NPObject));
     }
 
-    NPObjectData* d = static_cast<PluginModuleChild*>(i->Manager())
-        ->mObjectMap.PutEntry(newObject);
-    NS_ASSERTION(!d->instance, "New NPObject already mapped?");
-    d->instance = i;
+    PluginScriptableObjectChild::RegisterObject(newObject, i);
 
     return newObject;
 }
 
 NPObject*
 PluginModuleChild::NPN_RetainObject(NPObject* aNPObj)
 {
     AssertPluginThread();
@@ -2072,25 +2105,25 @@ PluginModuleChild::NPN_RetainObject(NPOb
     return aNPObj;
 }
 
 void
 PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj)
 {
     AssertPluginThread();
 
-    NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj);
-    if (!d) {
+    PluginInstanceChild* instance = PluginScriptableObjectChild::GetInstanceForNPObject(aNPObj);
+    if (!instance) {
         NS_ERROR("Releasing object not in mObjectMap?");
         return;
     }
 
     DeletingObjectEntry* doe = nullptr;
-    if (d->instance->mDeletingHash) {
-        doe = d->instance->mDeletingHash->GetEntry(aNPObj);
+    if (instance->mDeletingHash) {
+        doe = instance->mDeletingHash->GetEntry(aNPObj);
         if (!doe) {
             NS_ERROR("An object for a destroyed instance isn't in the instance deletion hash");
             return;
         }
         if (doe->mDeleted)
             return;
     }
 
@@ -2109,39 +2142,21 @@ void
 PluginModuleChild::DeallocNPObject(NPObject* aNPObj)
 {
     if (aNPObj->_class && aNPObj->_class->deallocate) {
         aNPObj->_class->deallocate(aNPObj);
     } else {
         child::_memfree(aNPObj);
     }
 
-    NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj);
-    if (d->actor)
-        d->actor->NPObjectDestroyed();
-
-    current()->mObjectMap.RemoveEntry(aNPObj);
-}
+    PluginScriptableObjectChild* actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj);
+    if (actor)
+        actor->NPObjectDestroyed();
 
-void
-PluginModuleChild::FindNPObjectsForInstance(PluginInstanceChild* instance)
-{
-    NS_ASSERTION(instance->mDeletingHash, "filling null mDeletingHash?");
-    mObjectMap.EnumerateEntries(CollectForInstance, instance);
-}
-
-PLDHashOperator
-PluginModuleChild::CollectForInstance(NPObjectData* d, void* userArg)
-{
-    PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(userArg);
-    if (d->instance == instance) {
-        NPObject* o = d->GetKey();
-        instance->mDeletingHash->PutEntry(o);
-    }
-    return PL_DHASH_NEXT;
+    PluginScriptableObjectChild::UnregisterObject(aNPObj);
 }
 
 NPIdentifier
 PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
@@ -2267,17 +2282,17 @@ PluginModuleChild::CallWindowProcHook(in
     }
 
     return CallNextHookEx(nullptr, nCode, wParam, lParam);
 }
 
 LRESULT CALLBACK
 PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam)
 {
-    PluginModuleChild* self = current();
+    PluginModuleChild* self = GetChrome();
     uint32_t len = self->mIncallPumpingStack.Length();
     if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) {
         MessageLoop* loop = MessageLoop::current();
         self->SendProcessNativeEventsInInterruptCall();
         IncallFrame& f = self->mIncallPumpingStack[len - 1];
         f._spinning = true;
         f._savedNestableTasksAllowed = loop->NestableTasksAllowed();
         loop->SetNestableTasksAllowed(true);
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -71,16 +71,20 @@ protected:
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
     virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
     virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
 
+    virtual PPluginModuleChild*
+    AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+                            base::ProcessId aOtherProcess) MOZ_OVERRIDE;
+
     virtual PPluginInstanceChild*
     AllocPPluginInstanceChild(const nsCString& aMimeType,
                               const uint16_t& aMode,
                               const InfallibleTArray<nsCString>& aNames,
                               const InfallibleTArray<nsCString>& aValues,
                               NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
@@ -135,43 +139,44 @@ protected:
 
     virtual bool
     RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE;
 
     virtual bool
     AnswerGeckoGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
 
 public:
-    PluginModuleChild();
+    PluginModuleChild(bool aIsChrome);
     virtual ~PluginModuleChild();
 
+    bool CommonInit(base::ProcessHandle aParentProcessHandle,
+                    MessageLoop* aIOLoop,
+                    IPC::Channel* aChannel);
+
     // aPluginFilename is UTF8, not native-charset!
-    bool Init(const std::string& aPluginFilename,
-              base::ProcessHandle aParentProcessHandle,
-              MessageLoop* aIOLoop,
-              IPC::Channel* aChannel);
+    bool InitForChrome(const std::string& aPluginFilename,
+                       base::ProcessHandle aParentProcessHandle,
+                       MessageLoop* aIOLoop,
+                       IPC::Channel* aChannel);
+
+    bool InitForContent(base::ProcessHandle aParentProcessHandle,
+                        MessageLoop* aIOLoop,
+                        IPC::Channel* aChannel);
+
+    static PluginModuleChild*
+    CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+                            base::ProcessId aOtherProcess);
 
     void CleanUp();
 
     const char* GetUserAgent();
 
     static const NPNetscapeFuncs sBrowserFuncs;
 
-    static PluginModuleChild* current();
-
-    bool RegisterActorForNPObject(NPObject* aObject,
-                                  PluginScriptableObjectChild* aActor);
-
-    void UnregisterActorForNPObject(NPObject* aObject);
-
-    PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
-
-#ifdef DEBUG
-    bool NPObjectIsRegistered(NPObject* aObject);
-#endif
+    static PluginModuleChild* GetChrome();
 
     /**
      * The child implementation of NPN_CreateObject.
      */
     static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass);
     /**
      * The child implementation of NPN_RetainObject.
      */
@@ -294,27 +299,29 @@ private:
     virtual void ExitedCxxStack() MOZ_OVERRIDE;
 #endif
 
     PRLibrary* mLibrary;
     nsCString mPluginFilename; // UTF8
     nsCString mUserAgent;
     int mQuirks;
 
+    bool mIsChrome;
+    Transport* mTransport;
+
     // we get this from the plugin
     NP_PLUGINSHUTDOWN mShutdownFunc;
 #if defined(OS_LINUX) || defined(OS_BSD)
     NP_PLUGINUNIXINIT mInitializeFunc;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     NP_PLUGININIT mInitializeFunc;
     NP_GETENTRYPOINTS mGetEntryPointsFunc;
 #endif
 
     NPPluginFuncs mFunctions;
-    NPSavedData mSavedData;
 
 #if defined(MOZ_WIDGET_GTK)
     // If a plugin spins a nested glib event loop in response to a
     // synchronous IPC message from the browser, the loop might break
     // only after the browser responds to a request sent by the
     // plugin.  This can happen if a plugin uses gtk's synchronous
     // copy/paste, for example.  But because the browser is blocked on
     // a condvar, it can't respond to the request.  This situation
@@ -348,56 +355,28 @@ private:
     // g_main_context_iteration, or 0 when dispatched directly from
     // MessagePumpForUI.
     int mTopLoopDepth;
 #  endif
 #elif defined (MOZ_WIDGET_QT)
     NestedLoopTimer *mNestedLoopTimerObject;
 #endif
 
-    struct NPObjectData : public nsPtrHashKey<NPObject>
-    {
-        explicit NPObjectData(const NPObject* key)
-            : nsPtrHashKey<NPObject>(key)
-            , instance(nullptr)
-            , actor(nullptr)
-        { }
-
-        // never nullptr
-        PluginInstanceChild* instance;
-
-        // sometimes nullptr (no actor associated with an NPObject)
-        PluginScriptableObjectChild* actor;
-    };
-    /**
-     * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
-     * final release/dealloc, whether or not an actor is currently associated with the object.
-     */
-    nsTHashtable<NPObjectData> mObjectMap;
-
 public: // called by PluginInstanceChild
     /**
      * Dealloc an NPObject after last-release or when the associated instance
      * is destroyed. This function will remove the object from mObjectMap.
      */
     static void DeallocNPObject(NPObject* o);
 
     NPError NPP_Destroy(PluginInstanceChild* instance) {
         return mFunctions.destroy(instance->GetNPP(), 0);
     }
 
-    /**
-     * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects
-     * associated with that instance.
-     */
-    void FindNPObjectsForInstance(PluginInstanceChild* instance);
-
 private:
-    static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg);
-
 #if defined(OS_WIN)
     virtual void EnteredCall() MOZ_OVERRIDE;
     virtual void ExitedCall() MOZ_OVERRIDE;
 
     // Entered/ExitedCall notifications keep track of whether the plugin has
     // entered a nested event loop within this interrupt call.
     struct IncallFrame
     {
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -10,19 +10,22 @@
 #include <QtCore/QEventLoop>
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PCrashReporterParent.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
 #include "nsIObserverService.h"
@@ -78,26 +81,92 @@ static const char kHangUIMinDisplayPref[
 template<>
 struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent>
 {
     typedef mozilla::plugins::PluginModuleParent Class;
     static void RetainCallee(Class* obj) { }
     static void ReleaseCallee(Class* obj) { }
 };
 
+bool
+mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent)
+{
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    nsRefPtr<nsNPAPIPlugin> plugin;
+    nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
+    if (NS_FAILED(rv)) {
+        return false;
+    }
+    PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary());
+    return PPluginModule::Bridge(aContentParent, chromeParent);
+}
+
+PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent;
+
+/* static */ PluginLibrary*
+PluginModuleContentParent::LoadModule(uint32_t aPluginId)
+{
+    MOZ_ASSERT(!sSavedModuleParent);
+    MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+    /*
+     * We send a LoadPlugin message to the chrome process using an intr
+     * message. Before it sends its response, it sends a message to create
+     * PluginModuleParent instance. That message is handled by
+     * PluginModuleContentParent::Create, which saves the instance in
+     * sSavedModuleParent. We fetch it from there after LoadPlugin finishes.
+     */
+    dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+    if (!cp->CallLoadPlugin(aPluginId)) {
+        return nullptr;
+    }
+
+    PluginModuleContentParent* parent = sSavedModuleParent;
+    MOZ_ASSERT(parent);
+    sSavedModuleParent = nullptr;
+
+    return parent;
+}
+
+/* static */ PluginModuleContentParent*
+PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport,
+                                  base::ProcessId aOtherProcess)
+{
+    nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent());
+    ProcessHandle handle;
+    if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+        // Bug 1090578 - need to kill |aOtherProcess|, it's boned.
+        return nullptr;
+    }
+
+    MOZ_ASSERT(!sSavedModuleParent);
+    sSavedModuleParent = parent;
+
+    DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(),
+                                      mozilla::ipc::ParentSide);
+    MOZ_ASSERT(ok);
+
+    // Request Windows message deferral behavior on our channel. This
+    // applies to the top level and all sub plugin protocols since they
+    // all share the same channel.
+    parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+    return parent.forget();
+}
+
 // static
 PluginLibrary*
-PluginModuleParent::LoadModule(const char* aFilePath)
+PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
 
     int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
 
     // Block on the child process being launched and initialized.
-    nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath));
+    nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
     bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
     if (!launched) {
         // We never reached open
         parent->mShutdown = true;
         return nullptr;
     }
     parent->Open(parent->mSubprocess->GetChannel(),
                  parent->mSubprocess->GetChildProcessHandle());
@@ -119,25 +188,50 @@ PluginModuleParent::LoadModule(const cha
     mozilla::MutexAutoLock lock(parent->mCrashReporterMutex);
     parent->mCrashReporter = parent->CrashReporter();
 #endif
 #endif
 
     return parent.forget();
 }
 
-
-PluginModuleParent::PluginModuleParent(const char* aFilePath)
-    : mSubprocess(new PluginProcessParent(aFilePath))
+PluginModuleParent::PluginModuleParent(bool aIsChrome)
+    : mIsChrome(aIsChrome)
     , mShutdown(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(nullptr)
     , mPlugin(nullptr)
     , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+}
+
+PluginModuleParent::~PluginModuleParent()
+{
+    if (!OkToCleanup()) {
+        NS_RUNTIMEABORT("unsafe destruction");
+    }
+
+    if (!mShutdown) {
+        NS_WARNING("Plugin host deleted the module without shutting down.");
+        NPError err;
+        NP_Shutdown(&err);
+    }
+}
+
+PluginModuleContentParent::PluginModuleContentParent()
+    : PluginModuleParent(false)
+{
+}
+
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId)
+    : PluginModuleParent(true)
+    , mSubprocess(new PluginProcessParent(aFilePath))
+    , mPluginId(aPluginId)
+    , mChromeTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
     , mHangAnnotationFlags(0)
 #ifdef XP_WIN
     , mPluginCpuUsageOnHang()
     , mHangUIParent(nullptr)
     , mHangUIEnabled(true)
     , mIsTimerReset(true)
 #ifdef MOZ_CRASHREPORTER
     , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex")
@@ -160,17 +254,17 @@ PluginModuleParent::PluginModuleParent(c
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
-PluginModuleParent::~PluginModuleParent()
+PluginModuleChromeParent::~PluginModuleChromeParent()
 {
     if (!OkToCleanup()) {
         NS_RUNTIMEABORT("unsafe destruction");
     }
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     ShutdownPluginProfiling();
 #endif
@@ -207,17 +301,17 @@ PluginModuleParent::~PluginModuleParent(
     }
 #endif
 
     mozilla::HangMonitor::UnregisterAnnotator(*this);
 }
 
 #ifdef MOZ_CRASHREPORTER
 void
-PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
+PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes)
 {
 #ifdef XP_WIN
     // mCrashReporterMutex is already held by the caller
     mCrashReporterMutex.AssertCurrentThreadOwns();
 #endif
     typedef nsDependentCString CS;
 
     // Get the plugin filename, try to get just the file leafname
@@ -252,58 +346,58 @@ PluginModuleParent::WriteExtraDataForMin
 #endif
         }
 #endif
     }
 }
 #endif  // MOZ_CRASHREPORTER
 
 void
-PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
+PluginModuleChromeParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
                       MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 void
-PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+PluginModuleChromeParent::TimeoutChanged(const char* aPref, void* aModule)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
     if (!strcmp(aPref, kChildTimeoutPref)) {
       // The timeout value used by the parent for children
       int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
-      static_cast<PluginModuleParent*>(aModule)->SetChildTimeout(timeoutSecs);
+      static_cast<PluginModuleChromeParent*>(aModule)->SetChildTimeout(timeoutSecs);
 #else
     if (!strcmp(aPref, kChildTimeoutPref) ||
         !strcmp(aPref, kHangUIMinDisplayPref) ||
         !strcmp(aPref, kHangUITimeoutPref)) {
-      static_cast<PluginModuleParent*>(aModule)->EvaluateHangUIState(true);
+      static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true);
 #endif // XP_WIN
     } else if (!strcmp(aPref, kParentTimeoutPref)) {
       // The timeout value used by the child for its parent
       int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
-      unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
+      unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
     }
 }
 
 void
-PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI)
+PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
 {
     if (mShutdown) {
       return;
     }
 
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack, try again
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
-            mTaskFactory.NewRunnableMethod(
-                &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10);
+            mChromeTaskFactory.NewRunnableMethod(
+                &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10);
         return;
     }
 
     /* If the plugin container was terminated by the Plugin Hang UI, 
        then either the I/O thread detects a channel error, or the 
        main thread must set the error (whomever gets there first).
        OTOH, if we terminate and return false from 
        ShouldContinueFromReplyTimeout, then the channel state has 
@@ -380,35 +474,35 @@ GetProcessCpuUsage(const InfallibleTArra
   return true;
 }
 
 } // anonymous namespace
 
 #endif // #ifdef XP_WIN
 
 void
-PluginModuleParent::EnteredCxxStack()
+PluginModuleChromeParent::EnteredCxxStack()
 {
     mHangAnnotationFlags |= kInPluginCall;
 }
 
 void
-PluginModuleParent::ExitedCxxStack()
+PluginModuleChromeParent::ExitedCxxStack()
 {
     mHangAnnotationFlags = 0;
 #ifdef XP_WIN
     FinishHangUI();
 #endif
 }
 
 /**
  * This function is always called by the HangMonitor thread.
  */
 void
-PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
+PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
 {
     uint32_t flags = mHangAnnotationFlags;
     if (flags) {
         /* We don't actually annotate anything specifically for kInPluginCall;
            we use it to determine whether to annotate other things. It will
            be pretty obvious from the ChromeHang stack that we're in a plugin
            call when the hang occurred. */
         if (flags & kHangUIShown) {
@@ -446,32 +540,32 @@ CreateFlashMinidump(DWORD processId, Thr
   bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
   base::CloseProcessHandle(handle);
 
   return res;
 }
 #endif
 
 bool
-PluginModuleParent::ShouldContinueFromReplyTimeout()
+PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
 {
 #ifdef XP_WIN
     if (LaunchHangUI()) {
         return true;
     }
     // If LaunchHangUI returned false then we should proceed with the 
     // original plugin hang behaviour and kill the plugin container.
     FinishHangUI();
 #endif // XP_WIN
     TerminateChildProcess(MessageLoop::current());
     return false;
 }
 
 void
-PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop)
+PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop)
 {
 #ifdef MOZ_CRASHREPORTER
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
     CrashReporterParent* crashReporter = mCrashReporter;
     if (!crashReporter) {
         // If mCrashReporter is null then the hang has ended, the plugin module
         // is shutting down. There's nothing to do here.
@@ -551,18 +645,18 @@ PluginModuleParent::TerminateChildProces
     }
 #endif
 
     // this must run before the error notification from the channel,
     // or not at all
     bool isFromHangUI = aMsgLoop != MessageLoop::current();
     aMsgLoop->PostTask(
         FROM_HERE,
-        mTaskFactory.NewRunnableMethod(
-            &PluginModuleParent::CleanupFromTimeout, isFromHangUI));
+        mChromeTaskFactory.NewRunnableMethod(
+            &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
 
     if (!KillProcess(OtherProcess(), 1, false))
         NS_WARNING("failed to kill subprocess!");
 }
 
 bool
 PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
                                      nsACString& aPluginVersion)
@@ -577,17 +671,17 @@ PluginModuleParent::GetPluginDetails(nsA
     }
     aPluginName = pluginTag->mName;
     aPluginVersion = pluginTag->mVersion;
     return true;
 }
 
 #ifdef XP_WIN
 void
-PluginModuleParent::EvaluateHangUIState(const bool aReset)
+PluginModuleChromeParent::EvaluateHangUIState(const bool aReset)
 {
     int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10);
     int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0);
     int32_t timeoutSecs = 0;
     if (autoStopSecs > 0 && autoStopSecs < minDispSecs) {
         /* If we're going to automatically terminate the plugin within a 
            time frame shorter than minDispSecs, there's no point in 
            showing the hang UI; it would just flash briefly on the screen. */
@@ -611,17 +705,17 @@ PluginModuleParent::EvaluateHangUIState(
             autoStopSecs *= 2;
         }
     }
     mIsTimerReset = false;
     SetChildTimeout(autoStopSecs);
 }
 
 bool
-PluginModuleParent::LaunchHangUI()
+PluginModuleChromeParent::LaunchHangUI()
 {
     if (!mHangUIEnabled) {
         return false;
     }
     if (mHangUIParent) {
         if (mHangUIParent->IsShowing()) {
             // We've already shown the UI but the timeout has expired again.
             return false;
@@ -648,17 +742,17 @@ PluginModuleParent::LaunchHangUI()
            after kChildTimeoutPref seconds if the user doesn't respond to 
            the hang UI. */
         EvaluateHangUIState(false);
     }
     return retval;
 }
 
 void
-PluginModuleParent::FinishHangUI()
+PluginModuleChromeParent::FinishHangUI()
 {
     if (mHangUIEnabled && mHangUIParent) {
         bool needsCancel = mHangUIParent->IsShowing();
         // If we're still showing, send a Cancel notification
         if (needsCancel) {
             mHangUIParent->Cancel();
         }
         /* If we cancelled the UI or if the user issued a response,
@@ -669,25 +763,25 @@ PluginModuleParent::FinishHangUI()
                UI was displayed. Now that we're finishing the UI, we need to 
                switch it back to kHangUITimeoutPref. */
             EvaluateHangUIState(true);
         }
     }
 }
 
 void
-PluginModuleParent::OnHangUIContinue()
+PluginModuleChromeParent::OnHangUIContinue()
 {
     mHangAnnotationFlags |= kHangUIContinued;
 }
 #endif // XP_WIN
 
 #ifdef MOZ_CRASHREPORTER
 CrashReporterParent*
-PluginModuleParent::CrashReporter()
+PluginModuleChromeParent::CrashReporter()
 {
     return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 static void
 RemoveMinidump(nsIFile* minidump)
 {
@@ -699,17 +793,17 @@ RemoveMinidump(nsIFile* minidump)
     if (GetExtraFileForMinidump(minidump,
                                 getter_AddRefs(extraFile))) {
         extraFile->Remove(true);
     }
 }
 #endif // MOZ_CRASHREPORTER_INJECTOR
 
 void
-PluginModuleParent::ProcessFirstMinidump()
+PluginModuleChromeParent::ProcessFirstMinidump()
 {
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
 #endif
     CrashReporterParent* crashReporter = CrashReporter();
     if (!crashReporter)
         return;
 
@@ -776,20 +870,16 @@ PluginModuleParent::ProcessFirstMinidump
 }
 #endif
 
 void
 PluginModuleParent::ActorDestroy(ActorDestroyReason why)
 {
     switch (why) {
     case AbnormalShutdown: {
-#ifdef MOZ_CRASHREPORTER
-        ProcessFirstMinidump();
-#endif
-
         mShutdown = true;
         // Defer the PluginCrashed method so that we don't re-enter
         // and potentially modify the actor child list while enumerating it.
         if (mPlugin)
             MessageLoop::current()->PostTask(
                 FROM_HERE,
                 mTaskFactory.NewRunnableMethod(
                     &PluginModuleParent::NotifyPluginCrashed));
@@ -800,16 +890,28 @@ PluginModuleParent::ActorDestroy(ActorDe
         break;
 
     default:
         NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor.");
     }
 }
 
 void
+PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
+{
+    if (why == AbnormalShutdown) {
+#ifdef MOZ_CRASHREPORTER
+        ProcessFirstMinidump();
+#endif
+    }
+
+    PluginModuleParent::ActorDestroy(why);
+}
+
+void
 PluginModuleParent::NotifyPluginCrashed()
 {
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack.  try again
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
             mTaskFactory.NewRunnableMethod(
                 &PluginModuleParent::NotifyPluginCrashed), 10);
@@ -1168,23 +1270,26 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    if (!CallNP_Initialize(error)) {
-        Close();
-        return NS_ERROR_FAILURE;
-    }
-    else if (*error != NPERR_NO_ERROR) {
-        Close();
-        return NS_OK;
+    *error = NPERR_NO_ERROR;
+    if (IsChrome()) {
+        if (!CallNP_Initialize(error)) {
+            Close();
+            return NS_ERROR_FAILURE;
+        }
+        else if (*error != NPERR_NO_ERROR) {
+            Close();
+            return NS_OK;
+        }
     }
 
     SetPluginFuncs(pFuncs);
 
     return NS_OK;
 }
 #else
 nsresult
@@ -1194,16 +1299,27 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
+    *error = NPERR_NO_ERROR;
+    return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+    nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+    if (NS_FAILED(rv))
+        return rv;
+
     if (!CallNP_Initialize(error)) {
         Close();
         return NS_ERROR_FAILURE;
     }
     if (*error != NPERR_NO_ERROR) {
         Close();
         return NS_OK;
     }
@@ -1233,17 +1349,20 @@ PluginModuleParent::NP_Shutdown(NPError*
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    bool ok = CallNP_Shutdown(error);
+    bool ok = true;
+    if (IsChrome()) {
+        ok = CallNP_Shutdown(error);
+    }
 
     // if NP_Shutdown() is nested within another interrupt call, this will
     // break things.  but lord help us if we're doing that anyway; the
     // plugin dso will have been unloaded on the other side by the
     // CallNP_Shutdown() message
     Close();
 
     return ok ? NS_OK : NS_ERROR_FAILURE;
@@ -1271,26 +1390,31 @@ PluginModuleParent::NP_GetValue(void *fu
 }
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
 {
     NS_ASSERTION(pFuncs, "Null pointer!");
 
-    // We need to have the child process update its function table
-    // here by actually calling NP_GetEntryPoints since the parent's
-    // function table can reflect nullptr entries in the child's table.
-    if (!CallNP_GetEntryPoints(error)) {
-        return NS_ERROR_FAILURE;
-    }
-    else if (*error != NPERR_NO_ERROR) {
-        return NS_OK;
+    // We need to have the plugin process update its function table here by
+    // actually calling NP_GetEntryPoints. The parent's function table will
+    // reflect nullptr entries in the child's table once SetPluginFuncs is
+    // called.
+
+    if (IsChrome()) {
+        if (!CallNP_GetEntryPoints(error)) {
+            return NS_ERROR_FAILURE;
+        }
+        else if (*error != NPERR_NO_ERROR) {
+            return NS_OK;
+        }
     }
 
+    *error = NPERR_NO_ERROR;
     SetPluginFuncs(pFuncs);
 
     return NS_OK;
 }
 #endif
 
 nsresult
 PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
@@ -1342,19 +1466,25 @@ PluginModuleParent::NPP_New(NPMIMEType p
         return NS_ERROR_FAILURE;
     }
 
     if (*error != NPERR_NO_ERROR) {
         NPP_Destroy(instance, 0);
         return NS_ERROR_FAILURE;
     }
 
+    UpdatePluginTimeout();
+
+    return NS_OK;
+}
+
+void
+PluginModuleChromeParent::UpdatePluginTimeout()
+{
     TimeoutChanged(kParentTimeoutPref, this);
-    
-    return NS_OK;
 }
 
 nsresult
 PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
                                       uint64_t maxAge)
 {
     if (!mClearSiteDataSupported)
         return NS_ERROR_NOT_AVAILABLE;
@@ -1523,25 +1653,38 @@ PluginModuleParent::RecvPluginHideWindow
     return false;
 #endif
 }
 
 PCrashReporterParent*
 PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
                                               uint32_t* processType)
 {
+    MOZ_CRASH("unreachable");
+}
+
+bool
+PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+{
+    MOZ_CRASH("unreachable");
+}
+
+PCrashReporterParent*
+PluginModuleChromeParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+                                                    uint32_t* processType)
+{
 #ifdef MOZ_CRASHREPORTER
     return new CrashReporterParent();
 #else
     return nullptr;
 #endif
 }
 
 bool
-PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
 {
 #ifdef MOZ_CRASHREPORTER
 #ifdef XP_WIN
     mozilla::MutexAutoLock lock(mCrashReporterMutex);
     if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) {
         mCrashReporter = nullptr;
     }
 #endif
@@ -1617,42 +1760,44 @@ PluginModuleParent::RecvGetNativeCursors
 #else
     NS_NOTREACHED(
         "PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
     return false;
 #endif
 }
 
 bool
-PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
-                                         const nsCString& aMessage)
+PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
-    NPObject* aNPObj = nullptr;
-    if (aActor) {
-        aNPObj = static_cast<PluginScriptableObjectParent*>(aActor)->GetObject(true);
-        if (!aNPObj) {
-            NS_ERROR("Failed to get object!");
-            return false;
-        }
-    }
-    mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage));
+    // This function ignores its first argument.
+    mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage));
     return true;
 }
 
 bool
 PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
     mozilla::plugins::parent::_reloadplugins(aReloadPages);
     return true;
 }
 
+bool
+PluginModuleChromeParent::RecvNotifyContentModuleDestroyed()
+{
+    nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+    if (host) {
+        host->NotifyContentModuleDestroyed(mPluginId);
+    }
+    return true;
+}
+
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 // We only add the crash reporter to subprocess which have the filename
 // FlashPlayerPlugin*
 #define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
 
 static DWORD
 GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
@@ -1673,17 +1818,17 @@ GetFlashChildOfPID(DWORD pid, HANDLE sna
     }
     return 0;
 }
 
 // We only look for child processes of the Flash plugin, NPSWF*
 #define FLASH_PLUGIN_PREFIX "NPSWF"
 
 void
-PluginModuleParent::InitializeInjector()
+PluginModuleChromeParent::InitializeInjector()
 {
     if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false))
         return;
 
     nsCString path(Process()->GetPluginFilePath().c_str());
     ToUpperCase(path);
     int32_t lastSlash = path.RFindCharInSet("\\/");
     if (kNotFound == lastSlash)
@@ -1705,17 +1850,17 @@ PluginModuleParent::InitializeInjector()
         mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
         if (mFlashProcess2) {
             InjectCrashReporterIntoProcess(mFlashProcess2, this);
         }
     }
 }
 
 void
-PluginModuleParent::OnCrash(DWORD processID)
+PluginModuleChromeParent::OnCrash(DWORD processID)
 {
     if (!mShutdown) {
         GetIPCChannel()->CloseWithError();
         KillProcess(OtherProcess(), 1, false);
     }
 }
 
 #endif // MOZ_CRASHREPORTER_INJECTOR
@@ -1751,26 +1896,26 @@ PluginProfilerObserver::Observe(nsISuppo
       if (success && !result.IsEmpty()) {
           pse->AddSubProfile(result.get());
       }
     }
     return NS_OK;
 }
 
 void
-PluginModuleParent::InitPluginProfiling()
+PluginModuleChromeParent::InitPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         mProfilerObserver = new PluginProfilerObserver(this);
         observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
     }
 }
 
 void
-PluginModuleParent::ShutdownPluginProfiling()
+PluginModuleChromeParent::ShutdownPluginProfiling()
 {
     nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
     if (observerService) {
         observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
     }
 }
 #endif
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -48,104 +48,75 @@ class PluginHangUIParent;
  *
  * This class implements the NPP API from the perspective of the rest
  * of Gecko, forwarding NPP calls along to the child process that is
  * actually running the plugin.
  *
  * This class /also/ implements a version of the NPN API, because the
  * child process needs to make these calls back into Gecko proper.
  * This class is responsible for "actually" making those function calls.
+ *
+ * If a plugin is running, there will always be one PluginModuleParent for it in
+ * the chrome process. In addition, any content process using the plugin will
+ * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and
+ * PluginModuleContentParent implement functionality that is specific to one
+ * case or the other.
  */
 class PluginModuleParent
     : public PPluginModuleParent
     , public PluginLibrary
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , public CrashReporter::InjectorCrashCallback
 #endif
-    , public mozilla::HangMonitor::Annotator
 {
-private:
+protected:
     typedef mozilla::PluginLibrary PluginLibrary;
     typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
     typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
-protected:
-
     PPluginInstanceParent*
     AllocPPluginInstanceParent(const nsCString& aMimeType,
                                const uint16_t& aMode,
                                const InfallibleTArray<nsCString>& aNames,
                                const InfallibleTArray<nsCString>& aValues,
                                NPError* rv) MOZ_OVERRIDE;
 
     virtual bool
     DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) MOZ_OVERRIDE;
 
 public:
-    // aFilePath is UTF8, not native!
-    explicit PluginModuleParent(const char* aFilePath);
+    explicit PluginModuleParent(bool aIsChrome);
     virtual ~PluginModuleParent();
 
+    bool IsChrome() const { return mIsChrome; }
+
     virtual void SetPlugin(nsNPAPIPlugin* plugin) MOZ_OVERRIDE
     {
         mPlugin = plugin;
     }
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
-    /**
-     * LoadModule
-     *
-     * This may or may not launch a plugin child process,
-     * and may or may not be very expensive.
-     */
-    static PluginLibrary* LoadModule(const char* aFilePath);
-
     const NPNetscapeFuncs* GetNetscapeFuncs() {
         return mNPNIface;
     }
 
-    PluginProcessParent* Process() const { return mSubprocess; }
-    base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
-
     bool OkToCleanup() const {
         return !IsOnCxxStack();
     }
 
     void ProcessRemoteNativeEventsInInterruptCall();
 
-    void TerminateChildProcess(MessageLoop* aMsgLoop);
-
-    virtual void
-    EnteredCxxStack() MOZ_OVERRIDE;
-
-    virtual void
-    ExitedCxxStack() MOZ_OVERRIDE;
-
-    virtual void
-    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
-
-#ifdef XP_WIN
-    /**
-     * Called by Plugin Hang UI to notify that the user has clicked continue.
-     * Used for chrome hang annotations.
-     */
-    void
-    OnHangUIContinue();
-#endif // XP_WIN
-
 protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
-    virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
-
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
 
     virtual bool
     AnswerNPN_UserAgent(nsCString* userAgent) MOZ_OVERRIDE;
 
     virtual bool
     AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
@@ -182,37 +153,30 @@ protected:
 
     virtual bool
     RecvPopCursor() MOZ_OVERRIDE;
 
     virtual bool
     RecvGetNativeCursorsSupported(bool* supported) MOZ_OVERRIDE;
 
     virtual bool
-    RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
-                         const nsCString& aMessage) MOZ_OVERRIDE;
+    RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE;
 
     virtual bool
     RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE;
 
     static PluginInstanceParent* InstCast(NPP instance);
     static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
 
-private:
-    void SetPluginFuncs(NPPluginFuncs* aFuncs);
+protected:
+    virtual void UpdatePluginTimeout() {}
 
-    // Implement the module-level functions from NPAPI; these are
-    // normally resolved directly from the DSO.
-#ifdef OS_LINUX
-    NPError NP_Initialize(const NPNetscapeFuncs* npnIface,
-                          NPPluginFuncs* nppIface);
-#else
-    NPError NP_Initialize(const NPNetscapeFuncs* npnIface);
-    NPError NP_GetEntryPoints(NPPluginFuncs* nppIface);
-#endif
+    virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; }
+
+    void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
     // NPP-like API that Gecko calls are trampolined into.  These 
     // messages then get forwarded along to the plugin instance,
     // and then eventually the child process.
 
     static NPError NPP_Destroy(NPP instance, NPSavedData** save);
 
     static NPError NPP_SetWindow(NPP instance, NPWindow* window);
@@ -250,16 +214,17 @@ private:
                                          const nsIntRect& aRect) MOZ_OVERRIDE;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
 #endif
     virtual nsresult NP_Shutdown(NPError* error);
+
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc);
     virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                  void *aValue, NPError* error);
 #if defined(XP_WIN) || defined(XP_MACOSX)
     virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error);
 #endif
     virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
                              uint16_t mode, int16_t argc, char* argn[],
@@ -269,54 +234,152 @@ private:
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
     virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
 
-private:
-    CrashReporterParent* CrashReporter();
-
-#ifdef MOZ_CRASHREPORTER
-    void ProcessFirstMinidump();
-    void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
-#endif
-    void CleanupFromTimeout(const bool aByHangUI);
-    void SetChildTimeout(const int32_t aChildTimeout);
-    static void TimeoutChanged(const char* aPref, void* aModule);
+protected:
     void NotifyPluginCrashed();
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-    void InitPluginProfiling();
-    void ShutdownPluginProfiling();
-#endif
-
-    PluginProcessParent* mSubprocess;
+    bool mIsChrome;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
     const NPNetscapeFuncs* mNPNIface;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
     nsRefPtr<nsIObserver> mProfilerObserver;
+    nsCString mPluginName;
+    nsCString mPluginVersion;
+
+#ifdef MOZ_X11
+    // Dup of plugin's X socket, used to scope its resources to this
+    // object instead of the plugin process's lifetime
+    ScopedClose mPluginXSocketFdDup;
+#endif
+
+    bool
+    GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
+
+    friend class mozilla::dom::CrashReporterParent;
+};
+
+class PluginModuleContentParent : public PluginModuleParent
+{
+  public:
+    static PluginLibrary* LoadModule(uint32_t aPluginId);
+
+    static PluginModuleContentParent* Create(mozilla::ipc::Transport* aTransport,
+                                             base::ProcessId aOtherProcess);
+
+  private:
+    explicit PluginModuleContentParent();
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+    void OnCrash(DWORD processID) MOZ_OVERRIDE {}
+#endif
+
+    static PluginModuleContentParent* sSavedModuleParent;
+};
+
+class PluginModuleChromeParent
+    : public PluginModuleParent
+    , public mozilla::HangMonitor::Annotator
+{
+  public:
+    /**
+     * LoadModule
+     *
+     * This may or may not launch a plugin child process,
+     * and may or may not be very expensive.
+     */
+    static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId);
+
+    virtual ~PluginModuleChromeParent();
+
+    void TerminateChildProcess(MessageLoop* aMsgLoop);
+
+#ifdef XP_WIN
+    /**
+     * Called by Plugin Hang UI to notify that the user has clicked continue.
+     * Used for chrome hang annotations.
+     */
+    void
+    OnHangUIContinue();
+#endif // XP_WIN
+
+private:
+    virtual void
+    EnteredCxxStack() MOZ_OVERRIDE;
+
+    void
+    ExitedCxxStack() MOZ_OVERRIDE;
+
+    virtual void
+    AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
+
+    virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_CRASHREPORTER
+    void ProcessFirstMinidump();
+    void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+#endif
+
+    virtual PCrashReporterParent*
+    AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+                              uint32_t* processType) MOZ_OVERRIDE;
+    virtual bool
+    DeallocPCrashReporterParent(PCrashReporterParent* actor) MOZ_OVERRIDE;
+
+    PluginProcessParent* Process() const { return mSubprocess; }
+    base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
+
+#if !defined(XP_UNIX) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
+#endif
+
+    virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+    // aFilePath is UTF8, not native!
+    explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId);
+
+    CrashReporterParent* CrashReporter();
+
+    void CleanupFromTimeout(const bool aByHangUI);
+    void SetChildTimeout(const int32_t aChildTimeout);
+    static void TimeoutChanged(const char* aPref, void* aModule);
+
+    virtual void UpdatePluginTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    void InitPluginProfiling();
+    void ShutdownPluginProfiling();
+#endif
+
+    virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE;
+
+    PluginProcessParent* mSubprocess;
+    uint32_t mPluginId;
+
+    ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
+
     enum HangAnnotationFlags
     {
         kInPluginCall = (1u << 0),
         kHangUIShown = (1u << 1),
         kHangUIContinued = (1u << 2),
         kHangUIDontShow = (1u << 3)
     };
     Atomic<uint32_t> mHangAnnotationFlags;
-    nsCString mPluginName;
-    nsCString mPluginVersion;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
     PluginHangUIParent *mHangUIParent;
     bool mHangUIEnabled;
     bool mIsTimerReset;
 #ifdef MOZ_CRASHREPORTER
     /**
      * This mutex protects the crash reporter when the Plugin Hang UI event
@@ -344,25 +407,16 @@ private:
 
     /**
      * Finishes the Plugin Hang UI and cancels if it is being shown to the user.
      */
     void
     FinishHangUI();
 #endif
 
-    bool
-    GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
-
-#ifdef MOZ_X11
-    // Dup of plugin's X socket, used to scope its resources to this
-    // object instead of the plugin process's lifetime
-    ScopedClose mPluginXSocketFdDup;
-#endif
-
     friend class mozilla::dom::CrashReporterParent;
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     void InitializeInjector();
     
     void OnCrash(DWORD processID) MOZ_OVERRIDE;
 
     DWORD mFlashProcess1;
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -114,19 +114,19 @@ PluginProcessChild::Init()
 #  error Sorry
 #endif
 
     if (NS_FAILED(nsRegion::InitStatic())) {
       NS_ERROR("Could not initialize nsRegion");
       return false;
     }
 
-    return mPlugin.Init(pluginFilename, ParentHandle(),
-                        IOThreadChild::message_loop(),
-                        IOThreadChild::channel());
+    return mPlugin.InitForChrome(pluginFilename, ParentHandle(),
+                                 IOThreadChild::message_loop(),
+                                 IOThreadChild::channel());
 }
 
 void
 PluginProcessChild::CleanUp()
 {
 #ifdef XP_WIN
     ::OleUninitialize();
 #endif
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -14,17 +14,18 @@ namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class PluginProcessChild : public mozilla::ipc::ProcessChild {
 protected:
     typedef mozilla::ipc::ProcessChild ProcessChild;
 
 public:
-    explicit PluginProcessChild(ProcessHandle aParentHandle) : ProcessChild(aParentHandle)
+    explicit PluginProcessChild(ProcessHandle aParentHandle)
+      : ProcessChild(aParentHandle), mPlugin(true)
     { }
 
     virtual ~PluginProcessChild()
     { }
 
     virtual bool Init() MOZ_OVERRIDE;
     virtual void CleanUp() MOZ_OVERRIDE;
 
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -534,17 +534,17 @@ PluginScriptableObjectChild::PluginScrip
   AssertPluginThread();
 }
 
 PluginScriptableObjectChild::~PluginScriptableObjectChild()
 {
   AssertPluginThread();
 
   if (mObject) {
-    PluginModuleChild::current()->UnregisterActorForNPObject(mObject);
+    UnregisterActor(mObject);
 
     if (mObject->_class == GetClass()) {
       NS_ASSERTION(mType == Proxy, "Wrong type!");
       static_cast<ChildNPObject*>(mObject)->parent = nullptr;
     }
     else {
       NS_ASSERTION(mType == LocalObject, "Wrong type!");
       PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
@@ -564,18 +564,18 @@ PluginScriptableObjectChild::InitializeP
   NS_ASSERTION(mInstance, "Null manager?!");
 
   NPObject* object = CreateProxyObject();
   if (!object) {
     NS_ERROR("Failed to create object!");
     return false;
   }
 
-  if (!PluginModuleChild::current()->RegisterActorForNPObject(object, this)) {
-    NS_ERROR("RegisterActorForNPObject failed");
+  if (!RegisterActor(object)) {
+    NS_ERROR("RegisterActor failed");
     return false;
   }
 
   mObject = object;
   return true;
 }
 
 void
@@ -589,18 +589,18 @@ PluginScriptableObjectChild::InitializeL
   mInstance = static_cast<PluginInstanceChild*>(Manager());
   NS_ASSERTION(mInstance, "Null manager?!");
 
   PluginModuleChild::sBrowserFuncs.retainobject(aObject);
 
   NS_ASSERTION(!mProtectCount, "Should be zero!");
   mProtectCount++;
 
-  if (!PluginModuleChild::current()->RegisterActorForNPObject(aObject, this)) {
-    NS_ERROR("RegisterActorForNPObject failed");
+  if (!RegisterActor(aObject)) {
+    NS_ERROR("RegisterActor failed");
   }
 
   mObject = aObject;
 }
 
 NPObject*
 PluginScriptableObjectChild::CreateProxyObject()
 {
@@ -683,17 +683,17 @@ void
 PluginScriptableObjectChild::DropNPObject()
 {
   NS_ASSERTION(mObject, "Invalidated object!");
   NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!");
   NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
 
   // We think we're about to be deleted, but we could be racing with the other
   // process.
-  PluginModuleChild::current()->UnregisterActorForNPObject(mObject);
+  UnregisterActor(mObject);
   mObject = nullptr;
 
   SendUnprotect();
 }
 
 void
 PluginScriptableObjectChild::NPObjectDestroyed()
 {
@@ -1176,8 +1176,110 @@ PluginScriptableObjectChild::Evaluate(NP
 
   if (!success) {
     return false;
   }
 
   ConvertToVariant(result, *aResult);
   return true;
 }
+
+nsTHashtable<PluginScriptableObjectChild::NPObjectData>* PluginScriptableObjectChild::sObjectMap;
+
+bool
+PluginScriptableObjectChild::RegisterActor(NPObject* aObject)
+{
+  AssertPluginThread();
+  MOZ_ASSERT(aObject, "Null pointer!");
+
+  NPObjectData* d = sObjectMap->GetEntry(aObject);
+  if (!d) {
+    NS_ERROR("NPObject not in object table");
+    return false;
+  }
+
+  d->actor = this;
+  return true;
+}
+
+void
+PluginScriptableObjectChild::UnregisterActor(NPObject* aObject)
+{
+  AssertPluginThread();
+  MOZ_ASSERT(aObject, "Null pointer!");
+
+  NPObjectData* d = sObjectMap->GetEntry(aObject);
+  MOZ_ASSERT(d, "NPObject not in object table");
+  if (d) {
+    d->actor = nullptr;
+  }
+}
+
+/* static */ PluginScriptableObjectChild*
+PluginScriptableObjectChild::GetActorForNPObject(NPObject* aObject)
+{
+  AssertPluginThread();
+  MOZ_ASSERT(aObject, "Null pointer!");
+
+  NPObjectData* d = sObjectMap->GetEntry(aObject);
+  if (!d) {
+    NS_ERROR("Plugin using object not created with NPN_CreateObject?");
+    return nullptr;
+  }
+
+  return d->actor;
+}
+
+/* static */ void
+PluginScriptableObjectChild::RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance)
+{
+  AssertPluginThread();
+
+  if (!sObjectMap) {
+    sObjectMap = new nsTHashtable<PluginScriptableObjectChild::NPObjectData>();
+  }
+
+  NPObjectData* d = sObjectMap->PutEntry(aObject);
+  MOZ_ASSERT(!d->instance, "New NPObject already mapped?");
+  d->instance = aInstance;
+}
+
+/* static */ void
+PluginScriptableObjectChild::UnregisterObject(NPObject* aObject)
+{
+  AssertPluginThread();
+
+  sObjectMap->RemoveEntry(aObject);
+
+  if (!sObjectMap->Count()) {
+    delete sObjectMap;
+    sObjectMap = nullptr;
+  }
+}
+
+/* static */ PluginInstanceChild*
+PluginScriptableObjectChild::GetInstanceForNPObject(NPObject* aObject)
+{
+  AssertPluginThread();
+  NPObjectData* d = sObjectMap->GetEntry(aObject);
+  if (!d) {
+    return nullptr;
+  }
+  return d->instance;
+}
+
+/* static */ PLDHashOperator
+PluginScriptableObjectChild::CollectForInstance(NPObjectData* d, void* userArg)
+{
+    PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(userArg);
+    if (d->instance == instance) {
+        NPObject* o = d->GetKey();
+        instance->mDeletingHash->PutEntry(o);
+    }
+    return PL_DHASH_NEXT;
+}
+
+/* static */ void
+PluginScriptableObjectChild::NotifyOfInstanceShutdown(PluginInstanceChild* aInstance)
+{
+  AssertPluginThread();
+  sObjectMap->EnumerateEntries(CollectForInstance, aInstance);
+}
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -212,16 +212,32 @@ public:
     DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
 
     PluginIdentifier mIdentifier;
     nsRefPtr<StoredIdentifier> mStored;
   };
 
   static void ClearIdentifiers();
 
+  bool RegisterActor(NPObject* aObject);
+  void UnregisterActor(NPObject* aObject);
+
+  static PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
+
+  static void RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance);
+  static void UnregisterObject(NPObject* aObject);
+
+  static PluginInstanceChild* GetInstanceForNPObject(NPObject* aObject);
+
+  /**
+   * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects
+   * associated with that instance.
+   */
+  static void NotifyOfInstanceShutdown(PluginInstanceChild* aInstance);
+
 private:
   static NPObject*
   ScriptableAllocate(NPP aInstance,
                      NPClass* aClass);
 
   static void
   ScriptableInvalidate(NPObject* aObject);
 
@@ -292,14 +308,37 @@ private:
 
   static const NPClass sNPClass;
 
   static StoredIdentifier* HashIdentifier(const nsCString& aIdentifier);
   static void UnhashIdentifier(StoredIdentifier* aIdentifier);
 
   typedef nsDataHashtable<nsCStringHashKey, nsRefPtr<StoredIdentifier>> IdentifierTable;
   static IdentifierTable sIdentifiers;
+
+  struct NPObjectData : public nsPtrHashKey<NPObject>
+  {
+    explicit NPObjectData(const NPObject* key)
+    : nsPtrHashKey<NPObject>(key),
+      instance(nullptr),
+      actor(nullptr)
+    { }
+
+    // never nullptr
+    PluginInstanceChild* instance;
+
+    // sometimes nullptr (no actor associated with an NPObject)
+    PluginScriptableObjectChild* actor;
+  };
+
+  static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg);
+
+  /**
+   * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
+   * final release/dealloc, whether or not an actor is currently associated with the object.
+   */
+  static nsTHashtable<NPObjectData>* sObjectMap;
 };
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
 #endif /* dom_plugins_PluginScriptableObjectChild_h */
--- a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -85,17 +85,17 @@ mozilla::plugins::ConvertToVariant(const
 
       npn->retainobject(object);
       OBJECT_TO_NPVARIANT(object, aVariant);
       break;
     }
 
     case Variant::TPPluginScriptableObjectChild: {
       NS_ASSERTION(!aInstance, "No instance should be given!");
-      NS_ASSERTION(PluginModuleChild::current(),
+      NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
                    "Should be running on child only!");
 
       NPObject* object = NPObjectFromVariant(aRemoteVariant);
       NS_ASSERTION(object, "Null object?!");
 
       PluginModuleChild::sBrowserFuncs.retainobject(object);
       OBJECT_TO_NPVARIANT(object, aVariant);
       break;
--- a/dom/plugins/ipc/PluginScriptableObjectUtils.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h
@@ -107,17 +107,17 @@ ReleaseRemoteVariant(Variant& aVariant)
         const_cast<PluginScriptableObjectParent*>(
           reinterpret_cast<const PluginScriptableObjectParent*>(
             aVariant.get_PPluginScriptableObjectParent()));
       actor->Unprotect();
       break;
     }
 
     case Variant::TPPluginScriptableObjectChild: {
-      NS_ASSERTION(PluginModuleChild::current(),
+      NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
                    "Should only be running in the child!");
       PluginScriptableObjectChild* actor =
         const_cast<PluginScriptableObjectChild*>(
           reinterpret_cast<const PluginScriptableObjectChild*>(
             aVariant.get_PPluginScriptableObjectChild()));
       actor->Unprotect();
       break;
     }
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -1,16 +1,32 @@
 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace mozilla {
 namespace plugins {
 
+struct PluginTag
+{
+  uint32_t id;
+  nsCString name;
+  nsCString description;
+  nsCString[] mimeTypes;
+  nsCString[] mimeDescriptions;
+  nsCString[] extensions;
+  bool isJavaPlugin;
+  bool isFlashPlugin;
+  nsCString filename;
+  nsCString version;
+  int64_t lastModifiedTime;
+  bool isFromExtension;
+};
+
 union PluginIdentifier
 {
   nsCString;
   int32_t;
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -16,16 +16,17 @@ EXPORTS.mozilla.plugins += [
     'BrowserStreamChild.h',
     'BrowserStreamParent.h',
     'ChildAsyncCall.h',
     'ChildTimer.h',
     'NPEventAndroid.h',
     'NPEventOSX.h',
     'NPEventUnix.h',
     'NPEventWindows.h',
+    'PluginBridge.h',
     'PluginInstanceChild.h',
     'PluginInstanceParent.h',
     'PluginMessageUtils.h',
     'PluginModuleChild.h',
     'PluginModuleParent.h',
     'PluginProcessChild.h',
     'PluginProcessParent.h',
     'PluginScriptableObjectChild.h',
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || e10s #b2g-desktop(tests that use plugins)
+skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || (e10s && debug) #b2g-desktop(tests that use plugins)
 support-files =
   307-xo-redirect.sjs
   crashing_subpage.html
   file_bug738396.html
   file_bug771202.html
   file_bug863792.html
   large-pic.jpg
   loremipsum.txt
@@ -58,35 +58,35 @@ skip-if = (!crashreporter) || true # Bug
 [test_CrashService_crash.html]
 skip-if = !crashreporter || e10s
 [test_CrashService_hang.html]
 skip-if = !crashreporter || e10s
 [test_defaultValue.html]
 [test_enumerate.html]
 [test_fullpage.html]
 [test_getauthenticationinfo.html]
+skip-if = e10s
 [test_hanging.html]
-skip-if = !crashreporter
+skip-if = !crashreporter || e10s
 [test_instance_re-parent.html]
 [test_instance_unparent1.html]
 [test_instance_unparent2.html]
 [test_instance_unparent3.html]
 [test_instantiation.html]
 [test_mixed_case_mime.html]
 [test_multipleinstanceobjects.html]
 [test_newstreamondestroy.html]
 [test_npn_asynccall.html]
 [test_npn_timers.html]
 [test_npobject_getters.html]
 [test_npruntime_construct.html]
 [test_npruntime_identifiers.html]
 [test_npruntime_npnevaluate.html]
 [test_npruntime_npninvoke.html]
 [test_npruntime_npninvokedefault.html]
-[test_npruntime_npnsetexception.html]
 [test_painting.html]
 [test_plugin_scroll_painting.html]
 skip-if = true # Bug 596491
 [test_pluginstream_asfile.html]
 [test_pluginstream_asfileonly.html]
 [test_pluginstream_err.html]
 [test_pluginstream_geturl.html]
 [test_pluginstream_geturlnotify.html]
@@ -103,15 +103,16 @@ skip-if = true # Bug 596491
 skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
 [test_propertyAndMethod.html]
 [test_queryContentsScaleFactor.html]
 skip-if = toolkit != "cocoa"
 [test_redirect_handling.html]
 [test_secondPlugin.html]
 [test_src_url_change.html]
 [test_streamNotify.html]
+skip-if = e10s
 [test_streamatclose.html]
 [test_twostreams.html]
 [test_windowed_invalidate.html]
 skip-if = os != "win"
 [test_visibility.html]
 skip-if = toolkit == "cocoa"
 [test_zero_opacity.html]
--- a/dom/plugins/test/mochitest/test_instance_re-parent.html
+++ b/dom/plugins/test/mochitest/test_instance_re-parent.html
@@ -5,17 +5,17 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="utils.js"></script>
 </head>
 <body onload="begin()">
   <script type="application/javascript;version=1.8">
   SimpleTest.waitForExplicitFinish();
-  getTestPlugin().enabledState = SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED;
+  setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var exceptionThrown = false;
   var p = null;
   var d1 = null;
   var d2 = null;
 
   var destroyed = false;
 
deleted file mode 100644
--- a/dom/plugins/test/mochitest/test_npruntime_npnsetexception.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<html>
-<head>
-  <title>NPN_SetException Tests</title>
-  <script type="text/javascript" 
-          src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="utils.js"></script>
-  <link rel="stylesheet" type="text/css" 
-        href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTests()">
-  <script class="testbody" type="application/javascript">
-  SimpleTest.waitForExplicitFinish();
-  setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
-
-  function runTests() {
-    // test a single exception thrown in scriptable invoke
-    var plugin = document.getElementById("plugin1");
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin.npnInvokeTest("badFunction");
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e, "badFunction", "wrong exception thrown");
-    }
-    
-    // test multiple exceptions thrown in scriptable invokedefault
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin("first exception", "second exception");
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e, "second exception", "wrong exception thrown");
-    }    
-
-    // test calling exception with NULL message
-    plugin.throwExceptionNextInvoke();
-    try {
-      plugin();
-      ok(false, "exception not thrown");
-    }
-    catch (e) {
-      is(e.message, "Error calling method on NPObject!", "wrong exception thrown");
-    }    
-
-    SimpleTest.finish();
-  }
-  </script>
-
-  <p id="display"></p>
-
-  <embed id="plugin1" type="application/x-test" width="400" height="100">
-  </embed>
-
-  <div id="verbose">
-  </div>
- </body>
- </html>
--- a/dom/plugins/test/mochitest/test_secondPlugin.html
+++ b/dom/plugins/test/mochitest/test_secondPlugin.html
@@ -38,35 +38,36 @@
       function run() {
         // Add "Test Plug-in" (but not "Second Test Plug-in") to the list of
         // unhidden plugins. This test must modify the "plugins.enumerable_names"
         // pref BEFORE accessing the navigator.plugins or navigator.mimeTypes
         // arrays because they only read the pref when they first initialize
         // their internal arrays!
         var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch);
         var defaultEnumerableNamesPref = prefs.getCharPref("plugins.enumerable_names");
-        prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in");
-
+        SpecialPowers.pushPrefEnv(
+          {'set': [["plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in"]]},
+          finishRun
+        );
+      }
+      function finishRun() {
         var pluginElement = document.getElementById("plugin");
         is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
 
         ok(navigator.plugins["Test Plug-in"], "Should have queried a non-hidden plugin named 'Test Plug-in'");
         ok(navigator.plugins["Second Test Plug-in"], "Should have queried a hidden plugin named 'Test Plug-in'");
 
         ok(findPlugin("Test Plug-in"), "Should have found a non-hidden plugin named 'Test Plug-in'");
         ok(!findPlugin("Second Test Plug-in"), "Should NOT found a hidden plugin named 'Test Plug-in'");
 
         ok(navigator.mimeTypes["application/x-test"], "Should have queried a non-hidden MIME type named 'application/x-test'");
         ok(navigator.mimeTypes["application/x-second-test"], "Should have queried a MIME type named 'application/x-second-test'");
 
         ok(findMimeType("application/x-test"), "Should have found a non-hidden MIME type named 'application/x-test'");
         ok(!findMimeType("application/x-second-test"), "Should NOT have found a MIME type named 'application/x-second-test'");
 
-        // Restore original pref to hide "Test Plug-in" and "Second Test Plug-in".
-        prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref);
-
         SimpleTest.finish();
       }
     </script>
 
     <object id="plugin" type="application/x-second-test" width=200 height=200></object>
   </body>
 </html>
--- a/dom/plugins/test/mochitest/utils.js
+++ b/dom/plugins/test/mochitest/utils.js
@@ -30,21 +30,29 @@ function getTestPlugin(pluginName) {
   ok(false, "Could not find plugin tag with plugin name '" + name + "'");
   return null;
 }
 
 // call this to set the test plugin(s) initially expected enabled state.
 // it will automatically be reset to it's previous value after the test
 // ends
 function setTestPluginEnabledState(newEnabledState, pluginName) {
+  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+  if (!oldEnabledState) {
+    ok(false, "Cannot find plugin '" + plugin + "'");
+    return;
+  }
   var plugin = getTestPlugin(pluginName);
-  var oldEnabledState = plugin.enabledState;
-  plugin.enabledState = newEnabledState;
+  while (plugin.enabledState != newEnabledState) {
+    // Run a nested event loop to wait for the preference change to
+    // propagate to the child. Yuck!
+    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+  }
   SimpleTest.registerCleanupFunction(function() {
-    getTestPlugin(pluginName).enabledState = oldEnabledState;
+    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
   });
 }
 
 function crashAndGetCrashServiceRecord(crashMethodName, callback) {
   var crashMan =
     SpecialPowers.Cu.import("resource://gre/modules/Services.jsm").
     Services.crashmanager;
 
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -1980,16 +1980,20 @@ scriptableSetProperty(NPObject* npobj, N
 }
 
 bool
 scriptableRemoveProperty(NPObject* npobj, NPIdentifier name)
 {
   for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
     if (name == sPluginPropertyIdentifiers[i]) {
       NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
+
+      // Avoid double frees (see test_propertyAndMethod.html, which deletes a
+      // property that doesn't exist).
+      VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
       return true;
     }
   }
   return false;
 }
 
 bool
 scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier, uint32_t* count)
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -1231,17 +1231,17 @@ nsresult nsWebBrowserPersist::SaveURIInt
         }
     }
 
     if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
     {
         nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
                 do_QueryInterface(inputChannel);
         if (httpChannelInternal)
-            httpChannelInternal->SetForceAllowThirdPartyCookie(true);
+            httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
     }
 
     // Set the referrer, post data and headers if any
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
     if (httpChannel)
     {
         // Referrer
         if (aReferrer)
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -131,27 +131,16 @@ class LayerManagerComposite;
 
 enum SurfaceInitMode
 {
   INIT_MODE_NONE,
   INIT_MODE_CLEAR
 };
 
 /**
- * A base class for a platform-dependent helper for use by TextureHost.
- */
-class CompositorBackendSpecificData
-{
-  NS_INLINE_DECL_REFCOUNTING(CompositorBackendSpecificData)
-
-protected:
-  virtual ~CompositorBackendSpecificData() {}
-};
-
-/**
  * Common interface for compositor backends.
  *
  * Compositor provides a cross-platform interface to a set of operations for
  * compositing quads. Compositor knows nothing about the layer tree. It must be
  * told everything about each composited quad - contents, location, transform,
  * opacity, etc.
  *
  * In theory it should be possible for different widgets to use the same
@@ -476,20 +465,16 @@ public:
       fillRatio = 100.0f * float(mPixelsFilled) / float(mPixelsPerFrame);
       if (fillRatio > 999.0f) {
         fillRatio = 999.0f;
       }
     }
     return fillRatio;
   }
 
-  virtual CompositorBackendSpecificData* GetCompositorBackendSpecificData() {
-    return nullptr;
-  }
-
   ScreenRotation GetScreenRotation() const {
     return mScreenRotation;
   }
 
   void SetScreenRotation(ScreenRotation aRotation) {
     mScreenRotation = aRotation;
   }
 
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -19,24 +19,16 @@
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "mozilla/layers/PCompositableParent.h"
 
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 
-CompositableBackendSpecificData::CompositableBackendSpecificData()
-  : mAllowSharingTextureHost(false)
-{
-  static uint64_t sNextID = 1;
-  ++sNextID;
-  mId = sNextID;
-}
-
 /**
  * IPDL actor used by CompositableHost to match with its corresponding
  * CompositableClient on the content side.
  *
  * CompositableParent is owned by the IPDL system. It's deletion is triggered
  * by either the CompositableChild's deletion, or by the IPDL communication
  * goind down.
  */
@@ -82,19 +74,16 @@ CompositableHost::CompositableHost(const
   , mKeepAttached(false)
 {
   MOZ_COUNT_CTOR(CompositableHost);
 }
 
 CompositableHost::~CompositableHost()
 {
   MOZ_COUNT_DTOR(CompositableHost);
-  if (mBackendData) {
-    mBackendData->ClearData();
-  }
 }
 
 PCompositableParent*
 CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
                                   const TextureInfo& aTextureInfo,
                                   uint64_t aID)
 {
   return new CompositableParent(aMgr, aTextureInfo, aID);
@@ -116,69 +105,61 @@ CompositableHost::FromIPDLActor(PComposi
 
 void
 CompositableHost::UseTextureHost(TextureHost* aTexture)
 {
   if (!aTexture) {
     return;
   }
   aTexture->SetCompositor(GetCompositor());
-  aTexture->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
 }
 
 void
 CompositableHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                             TextureHost* aTextureOnWhite)
 {
   MOZ_ASSERT(aTextureOnBlack && aTextureOnWhite);
   aTextureOnBlack->SetCompositor(GetCompositor());
-  aTextureOnBlack->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
   aTextureOnWhite->SetCompositor(GetCompositor());
-  aTextureOnWhite->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
 }
 
 void
 CompositableHost::RemoveTextureHost(TextureHost* aTexture)
-{
-  // Clear strong refrence to CompositableBackendSpecificData
-  aTexture->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
-}
+{}
 
 void
 CompositableHost::SetCompositor(Compositor* aCompositor)
 {
   mCompositor = aCompositor;
 }
 
 bool
 CompositableHost::AddMaskEffect(EffectChain& aEffects,
                                 const gfx::Matrix4x4& aTransform,
                                 bool aIs3D)
 {
-  RefPtr<TextureSource> source;
+  CompositableTextureSourceRef source;
   RefPtr<TextureHost> host = GetAsTextureHost();
 
   if (!host) {
     NS_WARNING("Using compositable with no valid TextureHost as mask");
     return false;
   }
 
   if (!host->Lock()) {
     NS_WARNING("Failed to lock the mask texture");
     return false;
   }
 
-  source = host->GetTextureSources();
-  MOZ_ASSERT(source);
-
-  if (!source) {
+  if (!host->BindTextureSource(source)) {
     NS_WARNING("The TextureHost was successfully locked but can't provide a TextureSource");
     host->Unlock();
     return false;
   }
+  MOZ_ASSERT(source);
 
   RefPtr<EffectMask> effect = new EffectMask(source,
                                              source->GetSize(),
                                              aTransform);
   effect->mIs3D = aIs3D;
   aEffects.mSecondaryEffects[EffectTypes::MASK] = effect;
   return true;
 }
@@ -187,19 +168,16 @@ void
 CompositableHost::RemoveMaskEffect()
 {
   RefPtr<TextureHost> host = GetAsTextureHost();
   if (host) {
     host->Unlock();
   }
 }
 
-// implemented in TextureHostOGL.cpp
-TemporaryRef<CompositableBackendSpecificData> CreateCompositableBackendSpecificDataOGL();
-
 /* static */ TemporaryRef<CompositableHost>
 CompositableHost::Create(const TextureInfo& aTextureInfo)
 {
   RefPtr<CompositableHost> result;
   switch (aTextureInfo.mCompositableType) {
   case CompositableType::BUFFER_BRIDGE:
     NS_ERROR("Cannot create an image bridge compositable this way");
     break;
@@ -222,22 +200,16 @@ CompositableHost::Create(const TextureIn
     result = new ContentHostSingleBuffered(aTextureInfo);
     break;
   case CompositableType::CONTENT_DOUBLE:
     result = new ContentHostDoubleBuffered(aTextureInfo);
     break;
   default:
     NS_ERROR("Unknown CompositableType");
   }
-  // We know that Tiled buffers don't use the compositable backend-specific
-  // data, so don't bother creating it.
-  if (result && aTextureInfo.mCompositableType != CompositableType::BUFFER_TILED) {
-    RefPtr<CompositableBackendSpecificData> data = CreateCompositableBackendSpecificDataOGL();
-    result->SetCompositableBackendSpecificData(data);
-  }
   return result;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 CompositableHost::DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture)
 {
   if (!aTexture) {
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -45,52 +45,16 @@ class Compositor;
 class ISurfaceAllocator;
 class ThebesBufferData;
 class TiledLayerComposer;
 class CompositableParentManager;
 class PCompositableParent;
 struct EffectChain;
 
 /**
- * A base class for doing CompositableHost and platform dependent task on TextureHost.
- */
-class CompositableBackendSpecificData
-{
-protected:
-  virtual ~CompositableBackendSpecificData() {}
-
-public:
-  NS_INLINE_DECL_REFCOUNTING(CompositableBackendSpecificData)
-
-  CompositableBackendSpecificData();
-
-  virtual void ClearData() {}
-  virtual void SetCompositor(Compositor* aCompositor) {}
-
-  bool IsAllowingSharingTextureHost()
-  {
-    return mAllowSharingTextureHost;
-  }
-
-  void SetAllowSharingTextureHost(bool aAllow)
-  {
-    mAllowSharingTextureHost = aAllow;
-  }
-
-  uint64_t GetId()
-  {
-    return mId;
-  }
-
-public:
-  bool mAllowSharingTextureHost;
-  uint64_t mId;
-};
-
-/**
  * The compositor-side counterpart to CompositableClient. Responsible for
  * updating textures and data about textures from IPC and how textures are
  * composited (tiling, double buffering, etc.).
  *
  * Update (for images/canvases) and UpdateThebes (for Thebes) are called during
  * the layers transaction to update the Compositbale's textures from the
  * content side. The actual update (and any syncronous upload) is done by the
  * TextureHost, but it is coordinated by the CompositableHost.
@@ -107,26 +71,16 @@ protected:
 public:
   NS_INLINE_DECL_REFCOUNTING(CompositableHost)
   explicit CompositableHost(const TextureInfo& aTextureInfo);
 
   static TemporaryRef<CompositableHost> Create(const TextureInfo& aTextureInfo);
 
   virtual CompositableType GetType() = 0;
 
-  virtual CompositableBackendSpecificData* GetCompositableBackendSpecificData()
-  {
-    return mBackendData;
-  }
-
-  virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
-  {
-    mBackendData = aBackendData;
-  }
-
   // If base class overrides, it should still call the parent implementation
   virtual void SetCompositor(Compositor* aCompositor);
 
   // composite the contents of this buffer host to the compositor's surface
   virtual void Composite(EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
@@ -247,19 +201,16 @@ public:
   virtual void Detach(Layer* aLayer = nullptr, AttachFlags aFlags = NO_FLAGS)
   {
     if (!mKeepAttached ||
         aLayer == mLayer ||
         aFlags & FORCE_DETACH) {
       SetLayer(nullptr);
       mAttached = false;
       mKeepAttached = false;
-      if (mBackendData) {
-        mBackendData->ClearData();
-      }
     }
   }
   bool IsAttached() { return mAttached; }
 
 #ifdef MOZ_DUMP_PAINTING
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) { }
@@ -309,17 +260,16 @@ public:
   }
 
 protected:
   TextureInfo mTextureInfo;
   uint64_t mAsyncID;
   uint64_t mCompositorID;
   RefPtr<Compositor> mCompositor;
   Layer* mLayer;
-  RefPtr<CompositableBackendSpecificData> mBackendData;
   uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
   bool mAttached;
   bool mKeepAttached;
 };
 
 class AutoLockCompositableHost MOZ_FINAL
 {
 public:
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -30,38 +30,45 @@ ContentHostBase::ContentHostBase(const T
   , mInitialised(false)
 {}
 
 ContentHostBase::~ContentHostBase()
 {
 }
 
 void
-ContentHostBase::Composite(EffectChain& aEffectChain,
-                           float aOpacity,
-                           const gfx::Matrix4x4& aTransform,
-                           const Filter& aFilter,
-                           const Rect& aClipRect,
-                           const nsIntRegion* aVisibleRegion)
+ContentHostTexture::Composite(EffectChain& aEffectChain,
+                              float aOpacity,
+                              const gfx::Matrix4x4& aTransform,
+                              const Filter& aFilter,
+                              const Rect& aClipRect,
+                              const nsIntRegion* aVisibleRegion)
 {
   NS_ASSERTION(aVisibleRegion, "Requires a visible region");
 
   AutoLockCompositableHost lock(this);
   if (lock.Failed()) {
     return;
   }
 
-  RefPtr<TextureSource> source = GetTextureSource();
-  RefPtr<TextureSource> sourceOnWhite = GetTextureSourceOnWhite();
+  if (!mTextureHost->BindTextureSource(mTextureSource)) {
+    return;
+  }
+  MOZ_ASSERT(mTextureSource.get());
 
-  if (!source) {
+  if (!mTextureHostOnWhite) {
+    mTextureSourceOnWhite = nullptr;
+  }
+  if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
     return;
   }
 
-  RefPtr<TexturedEffect> effect = GenEffect(aFilter);
+  RefPtr<TexturedEffect> effect = CreateTexturedEffect(mTextureSource.get(),
+                                                       mTextureSourceOnWhite.get(),
+                                                       aFilter, true);
   if (!effect) {
     return;
   }
 
   aEffectChain.mPrimaryEffect = effect;
 
   nsIntRegion tmpRegion;
   const nsIntRegion* renderRegion;
@@ -76,17 +83,17 @@ ContentHostBase::Composite(EffectChain& 
   }
 
   nsIntRegion region(*renderRegion);
   nsIntPoint origin = GetOriginOffset();
   // translate into TexImage space, buffer origin might not be at texture (0,0)
   region.MoveBy(-origin);
 
   // Figure out the intersecting draw region
-  gfx::IntSize texSize = source->GetSize();
+  gfx::IntSize texSize = mTextureSource->GetSize();
   nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
   textureRect.MoveBy(region.GetBounds().TopLeft());
   nsIntRegion subregion;
   subregion.And(region, textureRect);
   if (subregion.IsEmpty()) {
     // Region is empty, nothing to draw
     return;
   }
@@ -100,24 +107,24 @@ ContentHostBase::Composite(EffectChain& 
     nsIntRect regionRect = *iterRect;
     nsIntRect screenRect = regionRect;
     screenRect.MoveBy(origin);
 
     screenRects.Or(screenRects, screenRect);
     regionRects.Or(regionRects, regionRect);
   }
 
-  BigImageIterator* bigImgIter = source->AsBigImageIterator();
+  BigImageIterator* bigImgIter = mTextureSource->AsBigImageIterator();
   BigImageIterator* iterOnWhite = nullptr;
   if (bigImgIter) {
     bigImgIter->BeginBigImageIteration();
   }
 
-  if (sourceOnWhite) {
-    iterOnWhite = sourceOnWhite->AsBigImageIterator();
+  if (mTextureSourceOnWhite) {
+    iterOnWhite = mTextureSourceOnWhite->AsBigImageIterator();
     MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
                "Tile count mismatch on component alpha texture");
     if (iterOnWhite) {
       iterOnWhite->BeginBigImageIteration();
     }
   }
 
   bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
@@ -200,42 +207,50 @@ ContentHostBase::Composite(EffectChain& 
   DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
   if (iterOnWhite) {
     diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
                                    aTransform, mFlashCounter);
 }
 
-TemporaryRef<TexturedEffect>
-ContentHostBase::GenEffect(const gfx::Filter& aFilter)
-{
-  RefPtr<TextureSource> source = GetTextureSource();
-  RefPtr<TextureSource> sourceOnWhite = GetTextureSourceOnWhite();
-  if (!source) {
-    return nullptr;
-  }
-  return CreateTexturedEffect(source, sourceOnWhite, aFilter, true);
-}
-
 void
 ContentHostTexture::UseTextureHost(TextureHost* aTexture)
 {
+  if (mTextureHost && mTextureHost != aTexture) {
+    mTextureHost->UnbindTextureSource();
+  }
   ContentHostBase::UseTextureHost(aTexture);
   mTextureHost = aTexture;
   mTextureHostOnWhite = nullptr;
+  mTextureSourceOnWhite = nullptr;
+  if (mTextureHost) {
+    mTextureHost->PrepareTextureSource(mTextureSource);
+  }
 }
 
 void
 ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                               TextureHost* aTextureOnWhite)
 {
+  if (mTextureHost && mTextureHost != aTextureOnBlack) {
+    mTextureHost->UnbindTextureSource();
+  }
+  if (mTextureHostOnWhite && mTextureHostOnWhite != aTextureOnWhite) {
+    mTextureHostOnWhite->UnbindTextureSource();
+  }
   ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite);
   mTextureHost = aTextureOnBlack;
   mTextureHostOnWhite = aTextureOnWhite;
+  if (mTextureHost) {
+    mTextureHost->PrepareTextureSource(mTextureSource);
+  }
+  if (mTextureHostOnWhite) {
+    mTextureHostOnWhite->PrepareTextureSource(mTextureSourceOnWhite);
+  }
 }
 
 void
 ContentHostTexture::SetCompositor(Compositor* aCompositor)
 {
   ContentHostBase::SetCompositor(aCompositor);
   if (mTextureHost) {
     mTextureHost->SetCompositor(aCompositor);
@@ -413,16 +428,186 @@ ContentHostIncremental::UpdateIncrementa
                                                      aSurface,
                                                      aUpdated,
                                                      aBufferRect,
                                                      aBufferRotation));
   FlushUpdateQueue();
 }
 
 void
+ContentHostIncremental::Composite(EffectChain& aEffectChain,
+                                  float aOpacity,
+                                  const gfx::Matrix4x4& aTransform,
+                                  const Filter& aFilter,
+                                  const Rect& aClipRect,
+                                  const nsIntRegion* aVisibleRegion)
+{
+  NS_ASSERTION(aVisibleRegion, "Requires a visible region");
+
+  AutoLockCompositableHost lock(this);
+  if (lock.Failed()) {
+    return;
+  }
+
+  if (!mSource) {
+    return;
+  }
+
+  RefPtr<TexturedEffect> effect = CreateTexturedEffect(mSource.get(),
+                                                       mSourceOnWhite.get(),
+                                                       aFilter, true);
+  if (!effect) {
+    return;
+  }
+
+  aEffectChain.mPrimaryEffect = effect;
+
+  nsIntRegion tmpRegion;
+  const nsIntRegion* renderRegion;
+  if (PaintWillResample()) {
+    // If we're resampling, then the texture image will contain exactly the
+    // entire visible region's bounds, and we should draw it all in one quad
+    // to avoid unexpected aliasing.
+    tmpRegion = aVisibleRegion->GetBounds();
+    renderRegion = &tmpRegion;
+  } else {
+    renderRegion = aVisibleRegion;
+  }
+
+  nsIntRegion region(*renderRegion);
+  nsIntPoint origin = GetOriginOffset();
+  // translate into TexImage space, buffer origin might not be at texture (0,0)
+  region.MoveBy(-origin);
+
+  // Figure out the intersecting draw region
+  gfx::IntSize texSize = mSource->GetSize();
+  nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height);
+  textureRect.MoveBy(region.GetBounds().TopLeft());
+  nsIntRegion subregion;
+  subregion.And(region, textureRect);
+  if (subregion.IsEmpty()) {
+    // Region is empty, nothing to draw
+    return;
+  }
+
+  nsIntRegion screenRects;
+  nsIntRegion regionRects;
+
+  // Collect texture/screen coordinates for drawing
+  nsIntRegionRectIterator iter(subregion);
+  while (const nsIntRect* iterRect = iter.Next()) {
+    nsIntRect regionRect = *iterRect;
+    nsIntRect screenRect = regionRect;
+    screenRect.MoveBy(origin);
+
+    screenRects.Or(screenRects, screenRect);
+    regionRects.Or(regionRects, regionRect);
+  }
+
+  BigImageIterator* bigImgIter = mSource->AsBigImageIterator();
+  BigImageIterator* iterOnWhite = nullptr;
+  if (bigImgIter) {
+    bigImgIter->BeginBigImageIteration();
+  }
+
+  if (mSourceOnWhite) {
+    iterOnWhite = mSourceOnWhite->AsBigImageIterator();
+    MOZ_ASSERT(!bigImgIter || bigImgIter->GetTileCount() == iterOnWhite->GetTileCount(),
+               "Tile count mismatch on component alpha texture");
+    if (iterOnWhite) {
+      iterOnWhite->BeginBigImageIteration();
+    }
+  }
+
+  bool usingTiles = (bigImgIter && bigImgIter->GetTileCount() > 1);
+  do {
+    if (iterOnWhite) {
+      MOZ_ASSERT(iterOnWhite->GetTileRect() == bigImgIter->GetTileRect(),
+                 "component alpha textures should be the same size.");
+    }
+
+    nsIntRect texRect = bigImgIter ? bigImgIter->GetTileRect()
+                                   : nsIntRect(0, 0,
+                                               texSize.width,
+                                               texSize.height);
+
+    // Draw texture. If we're using tiles, we do repeating manually, as texture
+    // repeat would cause each individual tile to repeat instead of the
+    // compound texture as a whole. This involves drawing at most 4 sections,
+    // 2 for each axis that has texture repeat.
+    for (int y = 0; y < (usingTiles ? 2 : 1); y++) {
+      for (int x = 0; x < (usingTiles ? 2 : 1); x++) {
+        nsIntRect currentTileRect(texRect);
+        currentTileRect.MoveBy(x * texSize.width, y * texSize.height);
+
+        nsIntRegionRectIterator screenIter(screenRects);
+        nsIntRegionRectIterator regionIter(regionRects);
+
+        const nsIntRect* screenRect;
+        const nsIntRect* regionRect;
+        while ((screenRect = screenIter.Next()) &&
+               (regionRect = regionIter.Next())) {
+          nsIntRect tileScreenRect(*screenRect);
+          nsIntRect tileRegionRect(*regionRect);
+
+          // When we're using tiles, find the intersection between the tile
+          // rect and this region rect. Tiling is then handled by the
+          // outer for-loops and modifying the tile rect.
+          if (usingTiles) {
+            tileScreenRect.MoveBy(-origin);
+            tileScreenRect = tileScreenRect.Intersect(currentTileRect);
+            tileScreenRect.MoveBy(origin);
+
+            if (tileScreenRect.IsEmpty())
+              continue;
+
+            tileRegionRect = regionRect->Intersect(currentTileRect);
+            tileRegionRect.MoveBy(-currentTileRect.TopLeft());
+          }
+          gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
+                         tileScreenRect.width, tileScreenRect.height);
+
+          effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
+                                        Float(tileRegionRect.y) / texRect.height,
+                                        Float(tileRegionRect.width) / texRect.width,
+                                        Float(tileRegionRect.height) / texRect.height);
+          GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+          if (usingTiles) {
+            DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
+            if (iterOnWhite) {
+              diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+            }
+            GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect,
+                                             aTransform, mFlashCounter);
+          }
+        }
+      }
+    }
+
+    if (iterOnWhite) {
+      iterOnWhite->NextTile();
+    }
+  } while (usingTiles && bigImgIter->NextTile());
+
+  if (bigImgIter) {
+    bigImgIter->EndBigImageIteration();
+  }
+  if (iterOnWhite) {
+    iterOnWhite->EndBigImageIteration();
+  }
+
+  DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT;
+  if (iterOnWhite) {
+    diagnostics |= DiagnosticFlags::COMPONENT_ALPHA;
+  }
+  GetCompositor()->DrawDiagnostics(diagnostics, nsIntRegion(mBufferRect), aClipRect,
+                                   aTransform, mFlashCounter);
+}
+
+void
 ContentHostIncremental::FlushUpdateQueue()
 {
   // If we're not compositing for some reason (the window being minimized
   // is one example), then we never process these updates and it can consume
   // huge amounts of memory. Instead we forcibly process the updates (during the
   // transaction) if the list gets too long.
   static const uint32_t kMaxUpdateCount = 6;
   if (mUpdateList.Length() >= kMaxUpdateCount) {
@@ -434,30 +619,16 @@ void
 ContentHostIncremental::ProcessTextureUpdates()
 {
   for (uint32_t i = 0; i < mUpdateList.Length(); i++) {
     mUpdateList[i]->Execute(this);
   }
   mUpdateList.Clear();
 }
 
-TextureSource*
-ContentHostIncremental::GetTextureSource()
-{
-  MOZ_ASSERT(mLocked);
-  return mSource;
-}
-
-TextureSource*
-ContentHostIncremental::GetTextureSourceOnWhite()
-{
-  MOZ_ASSERT(mLocked);
-  return mSourceOnWhite;
-}
-
 void
 ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost)
 {
   Compositor* compositor = aHost->GetCompositor();
   MOZ_ASSERT(compositor);
 
   RefPtr<DataTextureSource> temp =
     compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags);
@@ -678,16 +849,45 @@ ContentHostTexture::GetRenderState()
 
   if (mBufferRotation != nsIntPoint()) {
     result.mFlags |= LayerRenderStateFlags::BUFFER_ROTATION;
   }
   result.SetOffset(GetOriginOffset());
   return result;
 }
 
+TemporaryRef<TexturedEffect>
+ContentHostTexture::GenEffect(const gfx::Filter& aFilter)
+{
+  if (!mTextureHost) {
+    return nullptr;
+  }
+  if (!mTextureHost->BindTextureSource(mTextureSource)) {
+    return nullptr;
+  }
+  if (!mTextureHostOnWhite) {
+    mTextureSourceOnWhite = nullptr;
+  }
+  if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
+    return nullptr;
+  }
+  return CreateTexturedEffect(mTextureSource.get(),
+                              mTextureSourceOnWhite.get(),
+                              aFilter, true);
+}
+
+TemporaryRef<TexturedEffect>
+ContentHostIncremental::GenEffect(const gfx::Filter& aFilter)
+{
+  if (!mSource) {
+    return nullptr;
+  }
+  return CreateTexturedEffect(mSource, mSourceOnWhite, aFilter, true);
+}
+
 #ifdef MOZ_DUMP_PAINTING
 TemporaryRef<gfx::DataSourceSurface>
 ContentHostTexture::GetAsSurface()
 {
   if (!mTextureHost) {
     return nullptr;
   }
 
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -91,28 +91,16 @@ class ContentHostBase : public ContentHo
 {
 public:
   typedef RotatedContentBuffer::ContentType ContentType;
   typedef RotatedContentBuffer::PaintState PaintState;
 
   explicit ContentHostBase(const TextureInfo& aTextureInfo);
   virtual ~ContentHostBase();
 
-  virtual void Composite(EffectChain& aEffectChain,
-                         float aOpacity,
-                         const gfx::Matrix4x4& aTransform,
-                         const gfx::Filter& aFilter,
-                         const gfx::Rect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr);
-
-  virtual TextureSource* GetTextureSource() = 0;
-  virtual TextureSource* GetTextureSourceOnWhite() = 0;
-
-  virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
-
 protected:
   virtual nsIntPoint GetOriginOffset()
   {
     return mBufferRect.TopLeft() - mBufferRotation;
   }
 
 
   nsIntRect mBufferRect;
@@ -127,16 +115,23 @@ protected:
 class ContentHostTexture : public ContentHostBase
 {
 public:
   explicit ContentHostTexture(const TextureInfo& aTextureInfo)
     : ContentHostBase(aTextureInfo)
     , mLocked(false)
   { }
 
+  virtual void Composite(EffectChain& aEffectChain,
+                         float aOpacity,
+                         const gfx::Matrix4x4& aTransform,
+                         const gfx::Filter& aFilter,
+                         const gfx::Rect& aClipRect,
+                         const nsIntRegion* aVisibleRegion = nullptr);
+
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
 #ifdef MOZ_DUMP_PAINTING
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) MOZ_OVERRIDE;
@@ -168,33 +163,25 @@ public:
     MOZ_ASSERT(mLocked);
     mTextureHost->Unlock();
     if (mTextureHostOnWhite) {
       mTextureHostOnWhite->Unlock();
     }
     mLocked = false;
   }
 
-  virtual TextureSource* GetTextureSource() MOZ_OVERRIDE {
-    MOZ_ASSERT(mLocked);
-    return mTextureHost->GetTextureSources();
-  }
-  virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE {
-    MOZ_ASSERT(mLocked);
-    if (mTextureHostOnWhite) {
-      return mTextureHostOnWhite->GetTextureSources();
-    }
-    return nullptr;
-  }
+  LayerRenderState GetRenderState();
 
-  LayerRenderState GetRenderState();
+  virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
 
 protected:
   RefPtr<TextureHost> mTextureHost;
   RefPtr<TextureHost> mTextureHostOnWhite;
+  CompositableTextureSourceRef mTextureSource;
+  CompositableTextureSourceRef mTextureSourceOnWhite;
   bool mLocked;
 };
 
 /**
  * Double buffering is implemented by swapping the front and back TextureHosts.
  * We assume that whenever we use double buffering, then we have
  * render-to-texture and thus no texture upload to do.
  */
@@ -272,32 +259,39 @@ public:
                             const nsIntRegion& aUpdated,
                             const nsIntRegion& aOldValidRegionBack,
                             nsIntRegion* aUpdatedRegionBack)
   {
     NS_ERROR("Shouldn't call this");
     return false;
   }
 
+  virtual void Composite(EffectChain& aEffectChain,
+                         float aOpacity,
+                         const gfx::Matrix4x4& aTransform,
+                         const gfx::Filter& aFilter,
+                         const gfx::Rect& aClipRect,
+                         const nsIntRegion* aVisibleRegion = nullptr);
+
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) MOZ_OVERRIDE;
 
   virtual bool Lock() MOZ_OVERRIDE {
     MOZ_ASSERT(!mLocked);
     ProcessTextureUpdates();
     mLocked = true;
     return true;
   }
 
   virtual void Unlock() MOZ_OVERRIDE {
     MOZ_ASSERT(mLocked);
     mLocked = false;
   }
 
-  virtual TextureSource* GetTextureSource() MOZ_OVERRIDE;
-  virtual TextureSource* GetTextureSourceOnWhite() MOZ_OVERRIDE;
+  virtual TemporaryRef<TexturedEffect>
+  GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
 
 private:
 
   void FlushUpdateQueue();
   void ProcessTextureUpdates();
 
   class Request
   {
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -32,46 +32,40 @@ ImageHost::ImageHost(const TextureInfo& 
   , mFrontBuffer(nullptr)
   , mHasPictureRect(false)
   , mLocked(false)
 {}
 
 ImageHost::~ImageHost()
 {
   if (mFrontBuffer) {
-    mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
-  }
-}
-
-void
-ImageHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
-{
-  CompositableHost::SetCompositableBackendSpecificData(aBackendData);
-  // ImageHost allows TextureHost sharing among ImageHosts.
-  if (aBackendData) {
-    aBackendData->SetAllowSharingTextureHost(true);
+    mFrontBuffer->UnbindTextureSource();
   }
 }
 
 void
 ImageHost::UseTextureHost(TextureHost* aTexture)
 {
+  if (mFrontBuffer && mFrontBuffer != aTexture) {
+    mFrontBuffer->UnbindTextureSource();
+  }
   CompositableHost::UseTextureHost(aTexture);
+  mFrontBuffer = aTexture;
   if (mFrontBuffer) {
-    mFrontBuffer->UnsetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
+    mFrontBuffer->PrepareTextureSource(mTextureSource);
   }
-  mFrontBuffer = aTexture;
 }
 
 void
 ImageHost::RemoveTextureHost(TextureHost* aTexture)
 {
   CompositableHost::RemoveTextureHost(aTexture);
   if (aTexture && mFrontBuffer == aTexture) {
-    aTexture->SetCompositableBackendSpecificData(nullptr);
+    mFrontBuffer->UnbindTextureSource();
+    mTextureSource = nullptr;
     mFrontBuffer = nullptr;
   }
 }
 
 TextureHost*
 ImageHost::GetAsTextureHost()
 {
   return mFrontBuffer;
@@ -92,59 +86,68 @@ ImageHost::Composite(EffectChain& aEffec
     return;
   }
   if (!mFrontBuffer) {
     return;
   }
 
   // Make sure the front buffer has a compositor
   mFrontBuffer->SetCompositor(GetCompositor());
-  mFrontBuffer->SetCompositableBackendSpecificData(GetCompositableBackendSpecificData());
 
   AutoLockCompositableHost autoLock(this);
   if (autoLock.Failed()) {
     NS_WARNING("failed to lock front buffer");
     return;
   }
-  RefPtr<TextureSource> source = GetTextureSource();
-  if (!source) {
+
+  if (!mFrontBuffer->BindTextureSource(mTextureSource)) {
     return;
   }
 
-  RefPtr<TexturedEffect> effect = GenEffect(aFilter);
+  if (!mTextureSource) {
+    // BindTextureSource above should have returned false!
+    MOZ_ASSERT(false);
+    return;
+  }
+
+  bool isAlphaPremultiplied = !(mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+  RefPtr<TexturedEffect> effect = CreateTexturedEffect(mFrontBuffer->GetFormat(),
+                                                       mTextureSource.get(),
+                                                       aFilter,
+                                                       isAlphaPremultiplied);
   if (!effect) {
     return;
   }
 
   aEffectChain.mPrimaryEffect = effect;
-  IntSize textureSize = source->GetSize();
+  IntSize textureSize = mTextureSource->GetSize();
   gfx::Rect gfxPictureRect
     = mHasPictureRect ? gfx::Rect(0, 0, mPictureRect.width, mPictureRect.height)
                       : gfx::Rect(0, 0, textureSize.width, textureSize.height);
 
   gfx::Rect pictureRect(0, 0,
                         mPictureRect.width,
                         mPictureRect.height);
-  BigImageIterator* it = source->AsBigImageIterator();
+  BigImageIterator* it = mTextureSource->AsBigImageIterator();
   if (it) {
 
     // This iteration does not work if we have multiple texture sources here
     // (e.g. 3 YCbCr textures). There's nothing preventing the different
     // planes from having different resolutions or tile sizes. For example, a
     // YCbCr frame could have Cb and Cr planes that are half the resolution of
     // the Y plane, in such a way that the Y plane overflows the maximum
     // texture size and the Cb and Cr planes do not. Then the Y plane would be
     // split into multiple tiles and the Cb and Cr planes would just be one
     // tile each.
     // To handle the general case correctly, we'd have to create a grid of
     // intersected tiles over all planes, and then draw each grid tile using
     // the corresponding source tiles from all planes, with appropriate
     // per-plane per-tile texture coords.
     // DrawQuad currently assumes that all planes use the same texture coords.
-    MOZ_ASSERT(it->GetTileCount() == 1 || !source->GetNextSibling(),
+    MOZ_ASSERT(it->GetTileCount() == 1 || !mTextureSource->GetNextSibling(),
                "Can't handle multi-plane BigImages");
 
     it->BeginBigImageIteration();
     do {
       nsIntRect tileRect = it->GetTileRect();
       gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
       if (mHasPictureRect) {
         rect = rect.Intersect(pictureRect);
@@ -165,17 +168,17 @@ ImageHost::Composite(EffectChain& aEffec
                                        rect, aClipRect, aTransform, mFlashCounter);
     } while (it->NextTile());
     it->EndBigImageIteration();
     // layer border
     GetCompositor()->DrawDiagnostics(DiagnosticFlags::IMAGE,
                                      gfxPictureRect, aClipRect,
                                      aTransform, mFlashCounter);
   } else {
-    IntSize textureSize = source->GetSize();
+    IntSize textureSize = mTextureSource->GetSize();
     gfx::Rect rect;
     if (mHasPictureRect) {
       effect->mTextureCoords = Rect(Float(mPictureRect.x) / textureSize.width,
                                     Float(mPictureRect.y) / textureSize.height,
                                     Float(mPictureRect.width) / textureSize.width,
                                     Float(mPictureRect.height) / textureSize.height);
       rect = pictureRect;
     } else {
@@ -268,36 +271,28 @@ ImageHost::Lock()
 void
 ImageHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
   mFrontBuffer->Unlock();
   mLocked = false;
 }
 
-TemporaryRef<TextureSource>
-ImageHost::GetTextureSource()
-{
-  MOZ_ASSERT(mLocked);
-  return mFrontBuffer->GetTextureSources();
-}
-
 TemporaryRef<TexturedEffect>
 ImageHost::GenEffect(const gfx::Filter& aFilter)
 {
-  RefPtr<TextureSource> source = GetTextureSource();
-  if (!source) {
+  if (!mFrontBuffer->BindTextureSource(mTextureSource)) {
     return nullptr;
   }
   bool isAlphaPremultiplied = true;
   if (mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED)
     isAlphaPremultiplied = false;
 
   return CreateTexturedEffect(mFrontBuffer->GetFormat(),
-                              source,
+                              mTextureSource,
                               aFilter,
                               isAlphaPremultiplied);
 }
 
 #ifdef MOZ_WIDGET_GONK
 ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
   , mHasPictureRect(false)
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -40,18 +40,16 @@ struct EffectChain;
 class ImageHost : public CompositableHost
 {
 public:
   explicit ImageHost(const TextureInfo& aTextureInfo);
   ~ImageHost();
 
   virtual CompositableType GetType() { return mTextureInfo.mCompositableType; }
 
-  virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
-
   virtual void Composite(EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) MOZ_OVERRIDE;
 
   virtual void UseTextureHost(TextureHost* aTexture) MOZ_OVERRIDE;
@@ -79,23 +77,22 @@ public:
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 #endif
 
   virtual bool Lock() MOZ_OVERRIDE;
 
   virtual void Unlock() MOZ_OVERRIDE;
 
-  virtual TemporaryRef<TextureSource> GetTextureSource();
-
   virtual TemporaryRef<TexturedEffect> GenEffect(const gfx::Filter& aFilter) MOZ_OVERRIDE;
 
 protected:
 
   RefPtr<TextureHost> mFrontBuffer;
+  CompositableTextureSourceRef mTextureSource;
   nsIntRect mPictureRect;
   bool mHasPictureRect;
   bool mLocked;
 };
 
 #ifdef MOZ_WIDGET_GONK
 
 /**
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -139,16 +139,23 @@ TextureHost::AsTextureHost(PTextureParen
 }
 
 PTextureParent*
 TextureHost::GetIPDLActor()
 {
   return mActor;
 }
 
+bool
+TextureHost::BindTextureSource(CompositableTextureSourceRef& texture)
+{
+  texture = GetTextureSources();
+  return !!texture;
+}
+
 FenceHandle
 TextureHost::GetAndResetReleaseFenceHandle()
 {
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   TextureHostOGL* hostOGL = this->AsHostOGL();
   if (!hostOGL) {
     return FenceHandle();
   }
@@ -272,28 +279,16 @@ void
 TextureHost::CompositorRecycle()
 {
   if (!mActor) {
     return;
   }
   static_cast<TextureParent*>(mActor)->CompositorRecycle();
 }
 
-void
-TextureHost::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
-{
-  mCompositableBackendData = aBackendData;
-}
-
-void
-TextureHost::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
-{
-  mCompositableBackendData = nullptr;
-}
-
 TextureHost::TextureHost(TextureFlags aFlags)
     : mActor(nullptr)
     , mFlags(aFlags)
 {}
 
 TextureHost::~TextureHost()
 {
 }
@@ -317,19 +312,21 @@ TextureHost::PrintInfo(std::stringstream
     AppendToString(aStream, GetSize(), " [size=", "]");
     AppendToString(aStream, GetFormat(), " [format=", "]");
     Unlock();
   }
   AppendToString(aStream, mFlags, " [flags=", "]");
 }
 
 TextureSource::TextureSource()
+: mCompositableCount(0)
 {
     MOZ_COUNT_CTOR(TextureSource);
 }
+
 TextureSource::~TextureSource()
 {
     MOZ_COUNT_DTOR(TextureSource);
 }
 
 BufferTextureHost::BufferTextureHost(gfx::SurfaceFormat aFormat,
                                      TextureFlags aFlags)
 : TextureHost(aFlags)
@@ -839,34 +836,36 @@ SharedSurfaceToTexSource(gl::SharedSurfa
       auto surf = gl::SharedSurface_GLTexture::Cast(abstractSurf);
 
       MOZ_ASSERT(compositor->GetBackendType() == LayersBackend::LAYERS_OPENGL);
       CompositorOGL* compositorOGL = static_cast<CompositorOGL*>(compositor);
       gl::GLContext* gl = compositorOGL->gl();
 
       GLenum target = surf->ConsTextureTarget();
       GLuint tex = surf->ConsTexture(gl);
-      texSource = new GLTextureSource(compositorOGL, tex, format, target,
-                                      surf->mSize);
+      texSource = new GLTextureSource(compositorOGL, tex, target,
+                                      surf->mSize, format,
+                                      true/*externally owned*/);
       break;
     }
     case gl::SharedSurfaceType::EGLImageShare: {
       auto surf = gl::SharedSurface_EGLImage::Cast(abstractSurf);
 
       MOZ_ASSERT(compositor->GetBackendType() == LayersBackend::LAYERS_OPENGL);
       CompositorOGL* compositorOGL = static_cast<CompositorOGL*>(compositor);
       gl::GLContext* gl = compositorOGL->gl();
       MOZ_ASSERT(gl->IsCurrent());
 
       GLenum target = 0;
       GLuint tex = 0;
       surf->AcquireConsumerTexture(gl, &tex, &target);
 
-      texSource = new GLTextureSource(compositorOGL, tex, format, target,
-                                      surf->mSize);
+      texSource = new GLTextureSource(compositorOGL, tex, target,
+                                      surf->mSize, format,
+                                      true/*externally owned*/);
       break;
     }
 #ifdef XP_MACOSX
     case gl::SharedSurfaceType::IOSurface: {
       auto surf = gl::SharedSurface_IOSurface::Cast(abstractSurf);
       MacIOSurface* ioSurf = surf->GetIOSurface();
 
       MOZ_ASSERT(compositor->GetBackendType() == LayersBackend::LAYERS_OPENGL);
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -41,17 +41,16 @@ class SharedSurface;
 namespace ipc {
 class Shmem;
 }
 
 namespace layers {
 
 class Compositor;
 class CompositableHost;
-class CompositableBackendSpecificData;
 class CompositableParentManager;
 class SurfaceDescriptor;
 class SharedSurfaceDescriptor;
 class ISurfaceAllocator;
 class TextureHostOGL;
 class TextureSourceOGL;
 class TextureSourceD3D9;
 class TextureSourceD3D11;
@@ -145,20 +144,79 @@ public:
     switch (index) {
       case 0: return this;
       case 1: return GetNextSibling();
       case 2: return GetNextSibling() ? GetNextSibling()->GetNextSibling() : nullptr;
     }
     return nullptr;
   }
 
+  void AddCompositableRef() { ++mCompositableCount; }
+
+  void ReleaseCompositableRef() {
+    --mCompositableCount;
+    MOZ_ASSERT(mCompositableCount >= 0);
+  }
+
+  int NumCompositableRefs() const { return mCompositableCount; }
+
 protected:
   virtual ~TextureSource();
 
   RefPtr<TextureSource> mNextSibling;
+  int mCompositableCount;
+};
+
+/**
+ * equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
+ * ReleaseCompositableRef in addition to the usual AddRef and Release.
+ */
+class CompositableTextureSourceRef {
+public:
+  CompositableTextureSourceRef() {}
+
+  ~CompositableTextureSourceRef()
+  {
+    if (mRef) {
+      mRef->ReleaseCompositableRef();
+    }
+  }
+
+  CompositableTextureSourceRef& operator=(const TemporaryRef<TextureSource>& aOther)
+  {
+    RefPtr<TextureSource> temp = aOther;
+    if (temp) {
+      temp->AddCompositableRef();
+    }
+    if (mRef) {
+      mRef->ReleaseCompositableRef();
+    }
+    mRef = temp;
+    return *this;
+  }
+
+  CompositableTextureSourceRef& operator=(TextureSource* aOther)
+  {
+    if (aOther) {
+      aOther->AddCompositableRef();
+    }
+    if (mRef) {
+      mRef->ReleaseCompositableRef();
+    }
+    mRef = aOther;
+    return *this;
+  }
+
+  TextureSource* get() const { return mRef; }
+  operator TextureSource*() const { return mRef; }
+  TextureSource* operator->() const { return mRef; }
+  TextureSource& operator*() const { return *mRef; }
+
+private:
+  RefPtr<TextureSource> mRef;
 };
 
 /**
  * Interface for TextureSources that can be updated from a DataSourceSurface.
  *
  * All backend should implement at least one DataTextureSource.
  */
 class DataTextureSource : public TextureSource
@@ -298,16 +356,35 @@ public:
    *
    * This can trigger texture uploads, so do not call it inside transactions
    * so as to not upload textures while the main thread is blocked.
    * Must not be called while this TextureHost is not sucessfully Locked.
    */
   virtual TextureSource* GetTextureSources() = 0;
 
   /**
+   * Called during the transaction. The TextureSource may or may not be composited.
+   *
+   * Note that this is called outside of lock/unlock.
+   */
+  virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) {}
+
+  /**
+   * Called at composition time, just before compositing the TextureSource composited.
+   *
+   * Note that this is called only withing lock/unlock.
+   */
+  virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture);
+
+  /**
+   * Called when another TextureHost will take over.
+   */
+  virtual void UnbindTextureSource() {}
+
+  /**
    * Is called before compositing if the shared data has changed since last
    * composition.
    * This method should be overload in cases like when we need to do a texture
    * upload for example.
    *
    * @param aRegion The region that has been changed, if nil, it means that the
    * entire surface should be updated.
    */
@@ -401,20 +478,16 @@ public:
    */
   virtual LayerRenderState GetRenderState()
   {
     // By default we return an empty render state, this should be overridden
     // by the TextureHost implementations that are used on B2G with Composer2D
     return LayerRenderState();
   }
 
-  virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData);
-
-  virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData);
-
   // If a texture host holds a reference to shmem, it should override this method
   // to forget about the shmem _without_ releasing it.
   virtual void OnShutdown() {}
 
   // Forget buffer actor. Used only for hacky fix for bug 966446.
   virtual void ForgetBufferActor() {}
 
   virtual const char *Name() { return "TextureHost"; }
@@ -430,17 +503,16 @@ public:
   /**
    * Cast to a TextureHost for each backend.
    */
   virtual TextureHostOGL* AsHostOGL() { return nullptr; }
 
 protected:
   PTextureParent* mActor;
   TextureFlags mFlags;
-  RefPtr<CompositableBackendSpecificData> mCompositableBackendData;
 
   friend class TextureParent;
 };
 
 /**
  * TextureHost that wraps a random access buffer such as a Shmem or some raw
  * memory.
  *
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -131,43 +131,28 @@ GrallocTextureSourceOGL::BindTexture(GLe
   }
 
   GLuint tex = GetGLTexture();
   GLuint textureTarget = GetTextureTarget();
 
   gl()->fActiveTexture(aTextureUnit);
   gl()->fBindTexture(textureTarget, tex);
 
-  if (mTextureBackendSpecificData) {
-    // There are two paths for locking/unlocking - if mTextureBackendSpecificData is
-    // set, we use the texture on there, otherwise we use
-    // CompositorBackendSpecificData from the compositor and bind the EGLImage
-    // only in Lock().
-    if (!mEGLImage) {
-      mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
-    }
-    BindEGLImage();
-  }
-
   ApplyFilterToBoundTexture(gl(), aFilter, textureTarget);
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   if (mTextureHost) {
     // Wait until it's ready.
     mTextureHost->WaitAcquireFenceSyncComplete();
   }
 #endif
 }
 
 bool GrallocTextureSourceOGL::Lock()
 {
-  if (mTextureBackendSpecificData) {
-    return true;
-  }
-
   MOZ_ASSERT(IsValid());
   if (!IsValid()) {
     return false;
   }
   if (!gl()->MakeCurrent()) {
     NS_WARNING("Failed to make the gl context current");
     return false;
   }
@@ -183,17 +168,17 @@ bool GrallocTextureSourceOGL::Lock()
   }
   gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
   return true;
 }
 
 bool
 GrallocTextureSourceOGL::IsValid() const
 {
-  return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mTextureBackendSpecificData);
+  return !!gl() && !!mGraphicBuffer.get() && !!mCompositor;
 }
 
 gl::GLContext*
 GrallocTextureSourceOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
@@ -225,72 +210,16 @@ GrallocTextureSourceOGL::GetTextureTarge
   if (gl()->Renderer() == gl::GLRenderer::SGX530 ||
       gl()->Renderer() == gl::GLRenderer::SGX540) {
     return LOCAL_GL_TEXTURE_EXTERNAL;
   }
 
   return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat());
 }
 
-void
-GrallocTextureSourceOGL::SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData)
-{
-  if (!aBackendData) {
-    DeallocateDeviceData();
-    // Update mTextureBackendSpecificData after calling DeallocateDeviceData().
-    mTextureBackendSpecificData = nullptr;
-    return;
-  }
-
-  if (mTextureBackendSpecificData != aBackendData) {
-    mNeedsReset = true;
-  }
-
-  if (!gl() || !gl()->MakeCurrent()) {
-    NS_WARNING("Failed to make the context current");
-    return;
-  }
-
-  if (!mNeedsReset) {
-    // Update binding to the EGLImage
-    GLuint tex = GetGLTexture();
-    GLuint textureTarget = GetTextureTarget();
-    gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
-    gl()->fBindTexture(textureTarget, tex);
-    BindEGLImage();
-    return;
-  }
-
-  if (!mCompositor) {
-    mTextureBackendSpecificData = aBackendData;
-    return;
-  }
-
-  // delete old EGLImage
-  DeallocateDeviceData();
-
-  // Update mTextureBackendSpecificData after calling DeallocateDeviceData().
-  mTextureBackendSpecificData = aBackendData;
-
-  GLuint tex = GetGLTexture();
-  GLuint textureTarget = GetTextureTarget();
-
-  gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
-  gl()->fBindTexture(textureTarget, tex);
-
-  // Setup texure parameters at the first binding.
-  gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, GetWrapMode());
-  gl()->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, GetWrapMode());
-
-  // create new EGLImage
-  mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer());
-  BindEGLImage();
-  mNeedsReset = false;
-}
-
 gfx::IntSize
 GrallocTextureSourceOGL::GetSize() const
 {
   if (!IsValid()) {
     NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL");
     return gfx::IntSize(0, 0);
   }
   return gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight());
@@ -299,103 +228,100 @@ GrallocTextureSourceOGL::GetSize() const
 void
 GrallocTextureSourceOGL::DeallocateDeviceData()
 {
   if (mEGLImage) {
     MOZ_ASSERT(mCompositor);
     if (!gl() || !gl()->MakeCurrent()) {
       return;
     }
-    if (mTextureBackendSpecificData) {
-      mTextureBackendSpecificData->ClearBoundEGLImage(mEGLImage);
-    }
     EGLImageDestroy(gl(), mEGLImage);
     mEGLImage = EGL_NO_IMAGE;
   }
 }
 
 GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags,
                                              const NewSurfaceDescriptorGralloc& aDescriptor)
   : TextureHost(aFlags)
+  , mGrallocHandle(aDescriptor)
+  , mSize(0, 0)
+  , mDescriptorSize(aDescriptor.size())
+  , mFormat(gfx::SurfaceFormat::UNKNOWN)
+  , mEGLImage(EGL_NO_IMAGE)
 {
-  gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
-  mGrallocHandle = aDescriptor;
-
   android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
   MOZ_ASSERT(graphicBuffer);
 
-  mSize = aDescriptor.size();
   if (graphicBuffer) {
-    format =
+    mFormat =
       SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
                                          aFlags & TextureFlags::RB_SWAPPED);
-    mTextureSource = new GrallocTextureSourceOGL(nullptr,
-                                                 this,
-                                                 graphicBuffer,
-                                                 format);
+    mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
   } else {
     printf_stderr("gralloc buffer is nullptr");
   }
 }
 
 GrallocTextureHostOGL::~GrallocTextureHostOGL()
-{
-  MOZ_ASSERT(!mTextureSource || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
-             "Leaking our buffer");
-}
+{}
 
 void
 GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
-  if (mTextureSource) {
-    mTextureSource->SetCompositor(static_cast<CompositorOGL*>(aCompositor));
+  mCompositor = static_cast<CompositorOGL*>(aCompositor);
+  if (mTilingTextureSource) {
+    mTilingTextureSource->SetCompositor(mCompositor);
+  }
+  if (mGLTextureSource) {
+    mGLTextureSource->SetCompositor(mCompositor);
+  }
+
+  if (mCompositor && aCompositor != mCompositor) {
+    DestroyEGLImage();
   }
 }
 
 bool
 GrallocTextureHostOGL::Lock()
 {
-  if (IsValid()) {
-    mTextureSource->Lock();
-    return true;
-  }
-  return false;
+  return IsValid();
 }
 
 void
 GrallocTextureHostOGL::Unlock()
 {
   // Unlock is done internally by binding the texture to another gralloc buffer
 }
 
 bool
 GrallocTextureHostOGL::IsValid() const
 {
-  if (!mTextureSource) {
-    return false;
-  }
-  return mTextureSource->IsValid();
+  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+  return graphicBuffer != nullptr;
 }
 
 gfx::SurfaceFormat
 GrallocTextureHostOGL::GetFormat() const
 {
-  if (!mTextureSource) {
-    return gfx::SurfaceFormat::UNKNOWN;
-  }
-  return mTextureSource->GetFormat();
+  return mFormat;
 }
 
 void
 GrallocTextureHostOGL::DeallocateSharedData()
 {
-  if (mTextureSource) {
-    mTextureSource->ForgetBuffer();
-    mTextureSource = nullptr;
+  if (mTilingTextureSource) {
+    mTilingTextureSource->ForgetBuffer();
+    mTilingTextureSource = nullptr;
   }
+  if (mGLTextureSource) {
+    mGLTextureSource = nullptr;
+  }
+
+  DestroyEGLImage();
+
   if (mGrallocHandle.buffer().type() != SurfaceDescriptor::Tnull_t) {
     MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer();
     base::ProcessId owner;
     if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
       owner = handle.get_GrallocBufferRef().mOwner;
     }
     else {
       owner = handle.get_MagicGrallocBufferHandle().mRef.mOwner;
@@ -403,54 +329,63 @@ GrallocTextureHostOGL::DeallocateSharedD
 
     SharedBufferManagerParent::DropGrallocBuffer(owner, mGrallocHandle);
   }
 }
 
 void
 GrallocTextureHostOGL::ForgetSharedData()
 {
-  if (mTextureSource) {
-    mTextureSource->ForgetBuffer();
-    mTextureSource = nullptr;
+  if (mTilingTextureSource) {
+    mTilingTextureSource->ForgetBuffer();
+    mTilingTextureSource = nullptr;
+  }
+  if (mGLTextureSource) {
+    mGLTextureSource = nullptr;
   }
 }
 
 void
 GrallocTextureHostOGL::DeallocateDeviceData()
 {
-  if (mTextureSource) {
-    mTextureSource->DeallocateDeviceData();
+  if (mTilingTextureSource) {
+    mTilingTextureSource->DeallocateDeviceData();
   }
+  if (mGLTextureSource) {
+    mGLTextureSource = nullptr;
+  }
+  DestroyEGLImage();
 }
 
 LayerRenderState
 GrallocTextureHostOGL::GetRenderState()
 {
-  if (IsValid()) {
+  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+
+  if (graphicBuffer) {
     LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT;
     if (mFlags & TextureFlags::NEEDS_Y_FLIP) {
       flags |= LayerRenderStateFlags::Y_FLIPPED;
     }
     if (mFlags & TextureFlags::RB_SWAPPED) {
       flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
     }
-    return LayerRenderState(mTextureSource->mGraphicBuffer.get(),
-                            gfx::ThebesIntSize(mSize),
+    return LayerRenderState(graphicBuffer,
+                            gfx::ThebesIntSize(mDescriptorSize),
                             flags,
                             this);
   }
 
   return LayerRenderState();
 }
 
 TemporaryRef<gfx::DataSourceSurface>
 GrallocTextureHostOGL::GetAsSurface() {
-  return mTextureSource ? mTextureSource->GetAsSurface()
-                        : nullptr;
+  return mTilingTextureSource ? mTilingTextureSource->GetAsSurface()
+                              : nullptr;
 }
 
 TemporaryRef<gfx::DataSourceSurface>
 GrallocTextureSourceOGL::GetAsSurface() {
   if (!IsValid() || !gl()->MakeCurrent()) {
     return nullptr;
   }
 
@@ -468,109 +403,192 @@ GrallocTextureSourceOGL::GetAsSurface() 
 
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   return surf.forget();
 }
 
 GLuint
 GrallocTextureSourceOGL::GetGLTexture()
 {
-  if (mTextureBackendSpecificData) {
-    mTextureBackendSpecificData->SetCompositor(mCompositor);
-    return mTextureBackendSpecificData->GetTexture();
-  }
-
   return mTexture;
 }
 
 void
 GrallocTextureSourceOGL::BindEGLImage()
 {
-  if (mTextureBackendSpecificData) {
-    mTextureBackendSpecificData->BindEGLImage(GetTextureTarget(), mEGLImage);
+  gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage);
+}
+
+TextureSource*
+GrallocTextureHostOGL::GetTextureSources()
+{
+  // This is now only used with tiled layers, and will eventually be removed.
+  // Other layer types use BindTextureSource instead.
+  MOZ_ASSERT(!mGLTextureSource);
+  if (!mTilingTextureSource) {
+    android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+    MOZ_ASSERT(graphicBuffer);
+    if (!graphicBuffer) {
+      return nullptr;
+    }
+    mTilingTextureSource = new GrallocTextureSourceOGL(mCompositor, this,
+                                                 graphicBuffer, mFormat);
+  }
+  mTilingTextureSource->Lock();
+  return mTilingTextureSource;
+}
+
+void
+GrallocTextureHostOGL::UnbindTextureSource()
+{
+  // Clear the reference to the TextureSource (if any), because we know that
+  // another TextureHost is being bound to the TextureSource. This means that
+  // we will have to re-do gl->fEGLImageTargetTexture2D next time we go through
+  // BindTextureSource (otherwise we would have skipped it).
+  // Note that this doesn't "unlock" the gralloc buffer or force it to be
+  // detached, Although decreasing the refcount of the TextureSource may lead
+  // to the gl handle being destroyed, which would unlock the gralloc buffer.
+  // That said, this method is called before another TextureHost attaches to the
+  // TextureSource, which has the effect of unlocking the gralloc buffer. So when
+  // this is called we know we are going to be unlocked soon.
+  mGLTextureSource = nullptr;
+}
+
+GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) {
+  MOZ_ASSERT(aGL);
+  if (aGL->Renderer() == gl::GLRenderer::SGX530 ||
+      aGL->Renderer() == gl::GLRenderer::SGX540) {
+    // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will
+    // result in black pixels when trying to draw from bound textures.
+    // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on
+    // performance.
+    // See Bug 950050.
+    return LOCAL_GL_TEXTURE_EXTERNAL;
   } else {
-    gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage);
+    return TextureTargetForAndroidPixelFormat(aFormat);
+  }
+}
+
+void
+GrallocTextureHostOGL::DestroyEGLImage()
+{
+  // Only called when we want to get rid of the gralloc buffer, usually
+  // around the end of life of the TextureHost.
+  if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) {
+    EGLImageDestroy(GetGLContext(), mEGLImage);
+    mEGLImage = EGL_NO_IMAGE;
   }
 }
 
 void
-GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
+GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource)
 {
-  if(!aBackendData) {
+  // This happens during the layers transaction.
+  // All of the gralloc magic goes here. The only thing that happens externally
+  // and that is good to keep in mind is that when the TextureSource is deleted,
+  // it destroys its gl texture handle which is important for genlock.
+
+  // If this TextureHost's mGLTextureSource member is non-null, it means we are
+  // still bound to the TextureSource, in which case we can skip the driver
+  // overhead of binding the texture again (fEGLImageTargetTexture2D)
+  // As a result, if the TextureHost is used with several CompositableHosts,
+  // it will be bound to only one TextureSource, and we'll do the driver work
+  // only once, which is great. This means that all of the compositables that
+  // use this TextureHost will keep a reference to this TextureSource at least
+  // for the duration of this frame.
+
+  // If the compositable already has a TextureSource (the aTextureSource parameter),
+  // that is compatible and is not in use by several compositable, we try to
+  // attach to it. This has the effect of unlocking the previous TextureHost that
+  // we attached to the TextureSource (the previous frame)
+
+  // If the TextureSource used by the compositable is also used by other
+  // compositables (see NumCompositableRefs), we have to create a new TextureSource,
+  // because otherwise we would be modifying the content of every layer that uses
+  // the TextureSource in question, even thoug they don't use this TextureHost.
+
+  MOZ_ASSERT(!mTilingTextureSource);
+
+  android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+
+  MOZ_ASSERT(graphicBuffer);
+  if (!graphicBuffer) {
+    mGLTextureSource = nullptr;
+    return;
+  }
+
+  if (mGLTextureSource && !mGLTextureSource->IsValid()) {
+    mGLTextureSource = nullptr;
+  }
+
+  if (mGLTextureSource) {
+    // We are already attached to a TextureSource, nothing to do except tell
+    // the compositable to use it.
+    aTextureSource = mGLTextureSource.get();
+    return;
+  }
+
+  gl::GLContext* gl = GetGLContext();
+  if (!gl || !gl->MakeCurrent()) {
+    mGLTextureSource = nullptr;
     return;
   }
 
-  // Update mTextureBackendSpecificData if it is not set yet.
-  if (!mTextureBackendSpecificData) {
-    MOZ_ASSERT(!mCompositableBackendData);
-    mCompositableBackendData = aBackendData;
-    CompositableDataGonkOGL* backend = static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get());
-    mTextureBackendSpecificData = backend->GetTextureBackendSpecificData();
-  }
-
-  // If TextureHost sharing by multiple CompositableHosts are detected,
-  // enable mBackendDatas usage.
-  if (!mBackendDatas &&
-      mCompositableBackendData &&
-      mCompositableBackendData != aBackendData &&
-      mTextureBackendSpecificData->IsAllowingSharingTextureHost())
-  {
-    mBackendDatas = MakeUnique<std::map<uint64_t, RefPtr<CompositableBackendSpecificData> > >();
-    (*mBackendDatas)[mCompositableBackendData->GetId()] = mCompositableBackendData;
-    mCompositableBackendData = nullptr;
-
-    // Get new mTextureBackendSpecificData
-    mTextureBackendSpecificData =
-      mTextureBackendSpecificData->GetNewTextureBackendSpecificData(mTextureSource->GetEGLImage());
-    mTextureBackendSpecificData->SetOwnedByTextureHost();
+  if (mEGLImage == EGL_NO_IMAGE) {
+    // Should only happen the first time.
+    mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer());
   }
 
-  // Update mCompositableBackendData.
-  if (mBackendDatas)
-  {
-    // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL.
-    MOZ_ASSERT(aBackendData->IsAllowingSharingTextureHost());
-    (*mBackendDatas)[aBackendData->GetId()] = aBackendData;
-    if (mBackendDatas->size() > 200) {
-      NS_WARNING("Too many CompositableBackends");
-    }
+  GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat());
+
+  GLTextureSource* glSource = aTextureSource.get() ?
+    aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr;
+
+  bool shouldCreateTextureSource = !glSource  || !glSource->IsValid()
+                                 || glSource->NumCompositableRefs() > 1
+                                 || glSource->GetTextureTarget() != textureTarget;
+
+  if (shouldCreateTextureSource) {
+    GLuint textureHandle;
+    gl->fGenTextures(1, &textureHandle);
+    gl->fBindTexture(textureTarget, textureHandle);
+    gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
+
+    mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget,
+                                           mSize, mFormat);
+    aTextureSource = mGLTextureSource.get();
   } else {
-    // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL.
-    mCompositableBackendData = aBackendData;
-    CompositableDataGonkOGL* backend = static_cast<CompositableDataGonkOGL*>(mCompositableBackendData.get());
-    mTextureBackendSpecificData = backend->GetTextureBackendSpecificData();
+    gl->fBindTexture(textureTarget, glSource->GetTextureHandle());
+
+    gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
+    glSource->SetSize(mSize);
+    glSource->SetFormat(mFormat);
+    mGLTextureSource = glSource;
   }
-
-  if (mTextureSource) {
-    mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData);
-  }
-
 }
 
-void
-GrallocTextureHostOGL::UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData)
+bool
+GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource)
 {
-  if(!aBackendData ||
-     !mTextureBackendSpecificData) {
-    return;
+  // This happens at composition time.
+
+  // If mGLTextureSource is null it means PrepareTextureSource failed.
+  if (!mGLTextureSource) {
+    return false;
   }
 
-  if (mBackendDatas)
-  {
-    // Handle a case that TextureHost has ownership of TextureSharedDataGonkOGL.
-    mBackendDatas->erase(aBackendData->GetId());
-    if (mBackendDatas->size() == 0) {
-      mCompositableBackendData = nullptr;
-      mTextureBackendSpecificData = nullptr;
-    }
-  } else {
-    // Handle a case that CompositableHost has ownership of TextureSharedDataGonkOGL.
-    mCompositableBackendData = nullptr;
-    mTextureBackendSpecificData = nullptr;
-  }
+  // If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource,
+  // otherwise it means something has fiddled with the TextureSource between Prepare and
+  // now.
+  MOZ_ASSERT(mGLTextureSource == aTextureSource);
+  aTextureSource = mGLTextureSource.get();
 
-  if (mTextureSource) {
-    mTextureSource->SetTextureBackendSpecificData(mTextureBackendSpecificData);
-  }
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+  // Wait until it's ready.
+  WaitAcquireFenceSyncComplete();
+#endif
+  return true;
 }
 
 } // namepsace layers
 } // namepsace mozilla
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -12,16 +12,17 @@
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
 #include <ui/GraphicBuffer.h>
 
 namespace mozilla {
 namespace layers {
 
 class GrallocTextureHostOGL;
 
+// Progressively getting replaced by GLTextureSource
 class GrallocTextureSourceOGL : public TextureSource
                               , public TextureSourceOGL
 {
 public:
   friend class GrallocTextureHostOGL;
 
   GrallocTextureSourceOGL(CompositorOGL* aCompositor,
                           GrallocTextureHostOGL* aTextureHost,
@@ -42,18 +43,16 @@ public:
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual GLenum GetWrapMode() const MOZ_OVERRIDE
   {
     return LOCAL_GL_CLAMP_TO_EDGE;
   }
 
-  virtual void SetTextureBackendSpecificData(TextureSharedDataGonkOGL* aBackendData);
-
   void DeallocateDeviceData();
 
   gl::GLContext* gl() const;
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   void ForgetBuffer()
   {
@@ -70,17 +69,16 @@ public:
   EGLImage GetEGLImage()
   {
     return mEGLImage;
   }
 
   bool Lock();
 
 protected:
-  RefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
   RefPtr<CompositorOGL> mCompositor;
   GrallocTextureHostOGL* mTextureHost;
   android::sp<android::GraphicBuffer> mGraphicBuffer;
   EGLImage mEGLImage;
   GLuint mTexture;
   gfx::SurfaceFormat mFormat;
   bool mNeedsReset;
 };
@@ -108,48 +106,57 @@ public:
   virtual void DeallocateSharedData() MOZ_OVERRIDE;
 
   virtual void ForgetSharedData() MOZ_OVERRIDE;
 
   virtual void DeallocateDeviceData() MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const;
 
-  virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
+  virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mDescriptorSize; }
 
   virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
-  virtual TextureSource* GetTextureSources() MOZ_OVERRIDE
-  {
-    return mTextureSource;
-  }
+  virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE;
+
+  virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) MOZ_OVERRIDE;
+
+  virtual void UnbindTextureSource() MOZ_OVERRIDE;
+
+  virtual TextureSource* GetTextureSources() MOZ_OVERRIDE;
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   virtual TextureHostOGL* AsHostOGL() MOZ_OVERRIDE
   {
     return this;
   }
 #endif
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE;
 
-  virtual void SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
-
-  virtual void UnsetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) MOZ_OVERRIDE;
-
   bool IsValid() const;
 
   virtual const char* Name() MOZ_OVERRIDE { return "GrallocTextureHostOGL"; }
 
+  gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; }
+
 private:
+  void DestroyEGLImage();
+
   NewSurfaceDescriptorGralloc mGrallocHandle;
-  RefPtr<GrallocTextureSourceOGL> mTextureSource;
-  gfx::IntSize mSize; // See comment in textureClientOGL.h
-
-  RefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
-  UniquePtr<std::map<uint64_t, RefPtr<CompositableBackendSpecificData> > > mBackendDatas;
+  RefPtr<GLTextureSource> mGLTextureSource;
+  RefPtr<CompositorOGL> mCompositor;
+  // only used for tiling, will be removed.
+  RefPtr<GrallocTextureSourceOGL> mTilingTextureSource;
+  // Size reported by the GraphicBuffer
+  gfx::IntSize mSize;
+  // Size reported by TextureClient, can be different in some cases (video?),
+  // used by LayerRenderState.
+  gfx::IntSize mDescriptorSize;
+  gfx::SurfaceFormat mFormat;
+  EGLImage mEGLImage;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
 #endif
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -35,26 +35,16 @@
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 
-TemporaryRef<CompositableBackendSpecificData>
-CreateCompositableBackendSpecificDataOGL()
-{
-#ifdef MOZ_WIDGET_GONK
-  return new CompositableDataGonkOGL();
-#else
-  return nullptr;
-#endif
-}
-
 TemporaryRef<TextureHost>
 CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
                      ISurfaceAllocator* aDeallocator,
                      TextureFlags aFlags)
 {
   RefPtr<TextureHost> result;
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorShmem:
@@ -114,184 +104,16 @@ FlagsToGLFlags(TextureFlags aFlags)
   if (aFlags & TextureFlags::NEEDS_Y_FLIP)
     result |= TextureImage::NeedsYFlip;
   if (aFlags & TextureFlags::DISALLOW_BIGIMAGE)
     result |= TextureImage::DisallowBigImage;
 
   return static_cast<gl::TextureImage::Flags>(result);
 }
 
-CompositableDataGonkOGL::CompositableDataGonkOGL()
-{
-}
-
-CompositableDataGonkOGL::~CompositableDataGonkOGL()
-{
-   ClearData();
-}
-
-void
-CompositableDataGonkOGL::ClearData()
-{
-  CompositableBackendSpecificData::ClearData();
-  mTextureBackendSpecificData = nullptr;
-  mCompositor = nullptr;
-}
-
-void
-CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor)
-{
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
-  if (mTextureBackendSpecificData) {
-    mTextureBackendSpecificData->SetCompositor(aCompositor);
-  }
-}
-
-TextureSharedDataGonkOGL*
-CompositableDataGonkOGL::GetTextureBackendSpecificData()
-{
-  if (!mTextureBackendSpecificData) {
-    mTextureBackendSpecificData = new TextureSharedDataGonkOGL();
-    mTextureBackendSpecificData->SetCompositor(mCompositor);
-    mTextureBackendSpecificData->SetAllowSharingTextureHost(IsAllowingSharingTextureHost());
-  }
-  return mTextureBackendSpecificData;
-}
-
-TextureSharedDataGonkOGL::TextureSharedDataGonkOGL()
- : mOwnedByCompositableHost(true)
- , mAllowSharingTextureHost(false)
- , mTexture(0)
- , mBoundEGLImage(EGL_NO_IMAGE)
-{
-}
-
-TextureSharedDataGonkOGL::TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor)
- : mOwnedByCompositableHost(true)
- , mAllowSharingTextureHost(false)
- , mCompositor(aCompositor)
- , mTexture(aTexture)
- , mBoundEGLImage(aImage)
-{
-}
-
-TextureSharedDataGonkOGL::~TextureSharedDataGonkOGL()
-{
-  DeleteTextureIfPresent();
-}
-
-gl::GLContext*
-TextureSharedDataGonkOGL::gl() const
-{
-  return mCompositor ? mCompositor->gl() : nullptr;
-}
-
-void
-TextureSharedDataGonkOGL::SetCompositor(Compositor* aCompositor)
-{
-  if (gl() && mCompositor != aCompositor) {
-    DeleteTextureIfPresent();
-  }
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
-}
-
-void
-TextureSharedDataGonkOGL::ClearData()
-{
-  DeleteTextureIfPresent();
-}
-
-TemporaryRef<TextureSharedDataGonkOGL>
-TextureSharedDataGonkOGL::GetNewTextureBackendSpecificData(EGLImage aImage)
-{
-  MOZ_ASSERT(IsAllowingSharingTextureHost());
-
-  if (IsEGLImageBound(aImage))
-  {
-    // If EGLImage is already bound to OpenGL Texture,
-    // handover the OpenGL Texture to caller
-    GLuint textureId = GetAndResetGLTextureOwnership();
-    RefPtr<TextureSharedDataGonkOGL> data = new TextureSharedDataGonkOGL(textureId, aImage, mCompositor);
-    data->SetCompositor(mCompositor);
-    data->SetAllowSharingTextureHost(true);
-    return data;
-  }
-
-  // Create brand new TextureSharedDataGonkOGL
-  RefPtr<TextureSharedDataGonkOGL> data = new TextureSharedDataGonkOGL();
-  data->SetCompositor(mCompositor);
-  data->SetAllowSharingTextureHost(true);
-  return data;
-}
-
-GLuint
-TextureSharedDataGonkOGL::GetTexture()
-{
-  if (!mTexture) {
-    if (gl() && gl()->MakeCurrent()) {
-      gl()->fGenTextures(1, &mTexture);
-    }
-  }
-  return mTexture;
-}
-
-GLuint
-TextureSharedDataGonkOGL::GetAndResetGLTextureOwnership()
-{
-  GLuint texture = mTexture;
-  mTexture = 0;
-  mBoundEGLImage = EGL_NO_IMAGE;
-  return texture;
-}
-
-void
-TextureSharedDataGonkOGL::DeleteTextureIfPresent()
-{
-  if (mTexture) {
-    MOZ_ASSERT(mCompositor);
-    if (gl() && gl()->MakeCurrent()) {
-      gl()->fDeleteTextures(1, &mTexture);
-    }
-    mTexture = 0;
-    mBoundEGLImage = EGL_NO_IMAGE;
-  }
-}
-
-void
-TextureSharedDataGonkOGL::BindEGLImage(GLuint aTarget, EGLImage aImage)
-{
-  if (mBoundEGLImage != aImage) {
-    MOZ_ASSERT(gl());
-    if (gl()) {
-      gl()->fEGLImageTargetTexture2D(aTarget, aImage);
-    }
-    mBoundEGLImage = aImage;
-  }
-}
-
-void
-TextureSharedDataGonkOGL::ClearBoundEGLImage(EGLImage aImage)
-{
-  if (mBoundEGLImage == aImage) {
-    DeleteTextureIfPresent();
-    mBoundEGLImage = EGL_NO_IMAGE;
-  }
-}
-
-bool
-TextureSharedDataGonkOGL::IsEGLImageBound(EGLImage aImage)
-{
-  if (mTexture != 0 &&
-      aImage != EGL_NO_IMAGE &&
-      aImage == mBoundEGLImage) {
-    return true;
-  }
-  return false;
-}
-
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 bool
 TextureHostOGL::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
 {
   if (!aReleaseFence.get() || !aReleaseFence->isValid()) {
     // HWC might not provide Fence.
     // In this case, HWC implicitly handles buffer's fence.
     return false;
@@ -524,50 +346,79 @@ TextureImageTextureSourceOGL::BindTextur
   mTexImage->BindTexture(aTextureUnit);
   SetFilter(mGL, aFilter);
 }
 
 ////////////////////////////////////////////////////////////////////////
 // GLTextureSource
 
 GLTextureSource::GLTextureSource(CompositorOGL* aCompositor,
-                                 GLuint aTex,
-                                 gfx::SurfaceFormat aFormat,
+                                 GLuint aTextureHandle,
                                  GLenum aTarget,
-                                 gfx::IntSize aSize)
-  : mSize(aSize)
-  , mCompositor(aCompositor)
-  , mTex(aTex)
+                                 gfx::IntSize aSize,
+                                 gfx::SurfaceFormat aFormat,
+                                 bool aExternallyOwned)
+  : mCompositor(aCompositor)
+  , mTextureHandle(aTextureHandle)
+  , mTextureTarget(aTarget)
+  , mSize(aSize)
   , mFormat(aFormat)
-  , mTextureTarget(aTarget)
+  , mExternallyOwned(aExternallyOwned)
+{
+  MOZ_COUNT_CTOR(GLTextureSource);
+}
+
+GLTextureSource::~GLTextureSource()
 {
+  MOZ_COUNT_DTOR(GLTextureSource);
+  if (!mExternallyOwned) {
+    DeleteTextureHandle();
+  }
+}
+
+void
+GLTextureSource::DeallocateDeviceData()
+{
+  if (!mExternallyOwned) {
+    DeleteTextureHandle();
+  }
+}
+
+void
+GLTextureSource::DeleteTextureHandle()
+{
+  if (mTextureHandle != 0 && gl() && gl()->MakeCurrent()) {
+    gl()->fDeleteTextures(1, &mTextureHandle);
+  }
+  mTextureHandle = 0;
 }
 
 void
 GLTextureSource::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter)
 {
+  MOZ_ASSERT(gl());
+  MOZ_ASSERT(mTextureHandle != 0);
   if (!gl()) {
-    NS_WARNING("Trying to bind a texture without a GLContext");
     return;
   }
   gl()->fActiveTexture(aTextureUnit);
-  gl()->fBindTexture(mTextureTarget, mTex);
+  gl()->fBindTexture(mTextureTarget, mTextureHandle);
   ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget);
 }
 
 void
 GLTextureSource::SetCompositor(Compositor* aCompositor)
 {
   mCompositor = static_cast<CompositorOGL*>(aCompositor);
 }
 
 bool
 GLTextureSource::IsValid() const
 {
-  return !!gl();
+  return !!gl() && mTextureHandle != 0;
 }
 
 gl::GLContext*
 GLTextureSource::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
@@ -609,16 +460,20 @@ SurfaceTextureSource::BindTexture(GLenum
   mSurfTex->UpdateTexImage();
 
   ApplyFilterToBoundTexture(gl(), aFilter, mTextureTarget);
 }
 
 void
 SurfaceTextureSource::SetCompositor(Compositor* aCompositor)
 {
+  if (mCompositor != aCompositor) {
+    DeallocateDeviceData();
+  }
+
   mCompositor = static_cast<CompositorOGL*>(aCompositor);
 }
 
 bool
 SurfaceTextureSource::IsValid() const
 {
   return !!gl();
 }
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -53,109 +53,17 @@ class AndroidSurfaceTexture;
 }
 
 namespace layers {
 
 class Compositor;
 class CompositorOGL;
 class TextureImageTextureSourceOGL;
 class TextureSharedDataGonkOGL;
-
-/**
- * CompositableBackendSpecificData implementation for the Gonk OpenGL backend.
- * Share a same texture between TextureHosts in the same CompositableHost.
- * By shareing the texture among the TextureHosts, number of texture allocations
- * can be reduced than texture allocation in every TextureHosts.
- * From Bug 912134, use only one texture among all TextureHosts degrade
- * the rendering performance.
- * CompositableDataGonkOGL chooses in a middile of them.
- */
-class CompositableDataGonkOGL : public CompositableBackendSpecificData
-{
-protected:
-  virtual ~CompositableDataGonkOGL();
-
-public:
-  CompositableDataGonkOGL();
-  virtual void ClearData() MOZ_OVERRIDE;
-  virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
-
-  TextureSharedDataGonkOGL* GetTextureBackendSpecificData();
-protected:
-  nsRefPtr<TextureSharedDataGonkOGL> mTextureBackendSpecificData;
-  RefPtr<CompositorOGL> mCompositor;
-};
-
-/**
- * Manage actual shared resources of CompositableDataGonkOGL.
- * The resources are split from CompositableDataGonkOGL to handle two use cases.
- * Normally TextureHost is used from one CompositableHost at the same time.
- * In this case, performance is good if the resources are owned by CompositableDataGonkOGL.
- * But TextureHost could be shared among multiple ImageHosts.
- * If it happens, performance is good if the resource is owned by TextureHost.
- * The resources ownership is carryed over from CompositableDataGonkOGL to TextureHost.
- * See Bug 1017351.
- */
-class TextureSharedDataGonkOGL
-{
-protected:
-  virtual ~TextureSharedDataGonkOGL();
-
-public:
-  NS_INLINE_DECL_REFCOUNTING(TextureSharedDataGonkOGL)
-
-  TextureSharedDataGonkOGL();
-  TextureSharedDataGonkOGL(GLuint aTexture, EGLImage aImage, CompositorOGL* aCompositor);
-
-  void SetCompositor(Compositor* aCompositor);
-  void ClearData();
-
-  // Mark TextureSharedDataGonkOGL as owned by TextureHost.
-  void SetOwnedByTextureHost()
-  {
-    mOwnedByCompositableHost = false;
-  }
-
-  // Check if this is owned by CompositableHost or TextureHost.
-  bool IsOwnedByCompositableHost()
-  {
-    return mOwnedByCompositableHost;
-  }
-
-  bool IsAllowingSharingTextureHost()
-  {
-    return mAllowSharingTextureHost;
-  }
-
-  void SetAllowSharingTextureHost(bool aAllow)
-  {
-    mAllowSharingTextureHost = aAllow;
-  }
-
-  // Create new TextureSharedDataGonkOGL.
-  // If aImage is already bound to OpenGL texture, the OpenGL textre is carried over
-  // to a new object. It could reduce calling fEGLImageTargetTexture2D()
-  // during resources ownership carry over from CompositableHost to TextureHost.
-  TemporaryRef<TextureSharedDataGonkOGL> GetNewTextureBackendSpecificData(EGLImage aImage);
-
-  GLuint GetTexture();
-  void DeleteTextureIfPresent();
-  gl::GLContext* gl() const;
-  void BindEGLImage(GLuint aTarget, EGLImage aImage);
-  void ClearBoundEGLImage(EGLImage aImage);
-  bool IsEGLImageBound(EGLImage aImage);
-protected:
-  GLuint GetAndResetGLTextureOwnership();
-
-  bool mOwnedByCompositableHost;
-  bool mAllowSharingTextureHost;
-  RefPtr<CompositorOGL> mCompositor;
-  GLuint mTexture;
-  EGLImage mBoundEGLImage;
-};
+class GLTextureSource;
 
 inline void ApplyFilterToBoundTexture(gl::GLContext* aGL,
                                       gfx::Filter aFilter,
                                       GLuint aTarget = LOCAL_GL_TEXTURE_2D)
 {
   GLenum filter =
     (aFilter == gfx::Filter::POINT ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR);
 
@@ -200,16 +108,18 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const = 0;
 
   virtual GLenum GetWrapMode() const = 0;
 
   virtual gfx::Matrix4x4 GetTextureTransform() { return gfx::Matrix4x4(); }
 
   virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() { return nullptr; }
 
+  virtual GLTextureSource* AsGLTextureSource() { return nullptr; }
+
   void SetFilter(gl::GLContext* aGL, gfx::Filter aFilter)
   {
     if (mHasCachedFilter &&
         mCachedFilter == aFilter) {
       return;
     }
     mHasCachedFilter = true;
     mCachedFilter = aFilter;
@@ -368,47 +278,63 @@ protected:
  *
  * The shared texture handle is owned by the TextureHost.
  */
 class GLTextureSource : public TextureSource
                       , public TextureSourceOGL
 {
 public:
   GLTextureSource(CompositorOGL* aCompositor,
-                  GLuint aTex,
+                  GLuint aTextureHandle,
+                  GLenum aTarget,
+                  gfx::IntSize aSize,
                   gfx::SurfaceFormat aFormat,
-                  GLenum aTarget,
-                  gfx::IntSize aSize);
+                  bool aExternallyOwned = false);
+
+  ~GLTextureSource();
+
+  virtual GLTextureSource* AsGLTextureSource() MOZ_OVERRIDE { return this; }
 
   virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; }
 
   virtual void BindTexture(GLenum activetex, gfx::Filter aFilter) MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual GLenum GetTextureTarget() const { return mTextureTarget; }
 
   virtual GLenum GetWrapMode() const MOZ_OVERRIDE { return LOCAL_GL_CLAMP_TO_EDGE; }
 
-  virtual void DeallocateDeviceData() MOZ_OVERRIDE {}
+  virtual void DeallocateDeviceData() MOZ_OVERRIDE;
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
+  void SetSize(gfx::IntSize aSize) { mSize = aSize; }
+
+  void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; }
+
+  GLuint GetTextureHandle() const { return mTextureHandle; }
+
   gl::GLContext* gl() const;
 
 protected:
-  const gfx::IntSize mSize;
+  void DeleteTextureHandle();
+
   RefPtr<CompositorOGL> mCompositor;
-  const GLuint mTex;
-  const gfx::SurfaceFormat mFormat;
-  const GLenum mTextureTarget;
+  GLuint mTextureHandle;
+  GLenum mTextureTarget;
+  gfx::IntSize mSize;
+  gfx::SurfaceFormat mFormat;
+  // If the texture is externally owned, the gl handle will not be deleted
+  // in the destructor.
+  bool mExternallyOwned;
 };
 
 ////////////////////////////////////////////////////////////////////////
 // SurfaceTexture
 
 #ifdef MOZ_WIDGET_ANDROID
 
 class SurfaceTextureSource : public TextureSource
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2642,17 +2642,19 @@ class _GenerateProtocolActorCode(ipdl.as
                 CppDirective('endif')
             ])
 
         cppheaders = [CppDirective('include', '"%s"' % filename)
                       for filename in ipdl.builtin.CppIncludes]
 
         cf.addthings((
             [ Whitespace.NL ]
-            + self.protocolCxxIncludes
+            + [ CppDirective(
+                'include',
+                '"%s.h"' % (inc)) for inc in self.protocolCxxIncludes ]
             + [ Whitespace.NL ]
             + cppheaders
             + [ Whitespace.NL ]))
 
         cppns = makeNamespace(self.protocol, cf)
         cppns.addstmts([
             Whitespace.NL,
             Whitespace.NL,
@@ -2683,27 +2685,29 @@ class _GenerateProtocolActorCode(ipdl.as
 
     def visitInclude(self, inc):
         ip = inc.tu.protocol
         if not ip:
             return
 
         self.actorForwardDecls.extend([
             _makeForwardDeclForActor(ip.decl.type, self.side),
+            _makeForwardDeclForActor(ip.decl.type, _otherSide(self.side)),
             Whitespace.NL
         ])
-        self.protocolCxxIncludes.append(
-            CppDirective(
-                'include',
-                '"%s.h"'% (_protocolHeaderName(ip, self.side))))
+        self.protocolCxxIncludes.append(_protocolHeaderName(ip, self.side))
 
         if ip.decl.fullname is not None:
             self.includedActorTypedefs.append(Typedef(
-                Type(_actorName(ip.decl.fullname, self.prettyside)),
-                _actorName(ip.decl.shortname, self.prettyside)))
+                Type(_actorName(ip.decl.fullname, self.side.title())),
+                _actorName(ip.decl.shortname, self.side.title())))
+
+            self.includedActorTypedefs.append(Typedef(
+                Type(_actorName(ip.decl.fullname, _otherSide(self.side).title())),
+                _actorName(ip.decl.shortname, _otherSide(self.side).title())))
 
 
     def visitProtocol(self, p):
         self.hdrfile.addthings([
             CppDirective('ifdef', 'DEBUG'),
             CppDirective('include', '"prenv.h"'),
             CppDirective('endif', '// DEBUG')
         ])
@@ -4220,16 +4224,19 @@ class _GenerateProtocolActorCode(ipdl.as
             StmtDecl(Decl(Type('TransportDescriptor'), tdvar.name)),
             StmtDecl(Decl(Type('ProcessId'), pidvar.name)),
             StmtDecl(Decl(Type('ProtocolId'), pvar.name)),
             iffail,
             Whitespace.NL
         ])
 
         def makeHandlerCase(actor):
+            self.protocolCxxIncludes.append(_protocolHeaderName(actor.ptype._ast,
+                                                                actor.side))
+
             case = StmtBlock()
             modevar = _sideToTransportMode(actor.side)
             tvar = ExprVar('t')
             iffailopen = StmtIf(ExprNot(ExprAssn(
                 tvar,
                 ExprCall(ExprVar('mozilla::ipc::OpenDescriptor'),
                          args=[ tdvar, modevar ]))))
             iffailopen.addifstmt(StmtReturn(_Result.ValuError))
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -381,16 +381,19 @@ frontend::CompileScript(ExclusiveContext
         }
 
         if (!FoldConstants(cx, &pn, &parser))
             return nullptr;
 
         if (!NameFunctions(cx, pn))
             return nullptr;
 
+        if (!bce.updateLocalsToFrameSlots())
+            return nullptr;
+
         if (!EmitTree(cx, &bce, pn))
             return nullptr;
 
         parser.handler.freeTree(pn);
     }
 
     if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, *pc))
         return nullptr;
@@ -418,17 +421,17 @@ frontend::CompileScript(ExclusiveContext
         return nullptr;
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).  They may have block-scoped
     // locals, however, which are allocated to the fixed part of the stack
     // frame.
     InternalHandle<Bindings*> bindings(script, &script->bindings);
     if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0,
-                                            pc->blockScopeDepth, nullptr))
+                                            pc->blockScopeDepth, 0, 0, nullptr))
     {
         return nullptr;
     }
 
     if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
         return nullptr;
 
     // Note that this marking must happen before we tell Debugger
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -126,16 +126,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     current(&main),
     parser(parser),
     evalCaller(evalCaller),
     topStmt(nullptr),
     topScopeStmt(nullptr),
     staticScope(sc->context),
     atomIndices(sc->context),
     firstLine(lineNum),
+    localsToFrameSlots_(sc->context),
     stackDepth(0), maxStackDepth(0),
     arrayCompDepth(0),
     emitLevel(0),
     constList(sc->context),
     tryNoteList(sc->context),
     blockScopeList(sc->context),
     typesetCount(0),
     hasSingletons(false),
@@ -150,16 +151,51 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
 }
 
 bool
 BytecodeEmitter::init()
 {
     return atomIndices.ensureMap(sc->context);
 }
 
+bool
+BytecodeEmitter::updateLocalsToFrameSlots()
+{
+    // Assign stack slots to unaliased locals (aliased locals are stored in the
+    // call object and don't need their own stack slots). We do this by filling
+    // a Vector that can be used to map a local to its stack slot.
+
+    if (localsToFrameSlots_.length() == script->bindings.numLocals()) {
+        // CompileScript calls updateNumBlockScoped to update the block scope
+        // depth. Do nothing if the depth didn't change.
+        return true;
+    }
+
+    localsToFrameSlots_.clear();
+
+    if (!localsToFrameSlots_.reserve(script->bindings.numLocals()))
+        return false;
+
+    uint32_t slot = 0;
+    for (BindingIter bi(script); !bi.done(); bi++) {
+        if (bi->kind() == Binding::ARGUMENT)
+            continue;
+
+        if (bi->aliased())
+            localsToFrameSlots_.infallibleAppend(UINT32_MAX);
+        else
+            localsToFrameSlots_.infallibleAppend(slot++);
+    }
+
+    for (size_t i = 0; i < script->bindings.numBlockScoped(); i++)
+        localsToFrameSlots_.infallibleAppend(slot++);
+
+    return true;
+}
+
 static ptrdiff_t
 EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
 {
     ptrdiff_t offset = bce->code().length();
 
     // Start it off moderately large to avoid repeated resizings early on.
     // ~98% of cases fit within 1024 bytes.
     if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
@@ -766,22 +802,28 @@ AllLocalsAliased(StaticBlockObject &obj)
             return false;
     return true;
 }
 #endif
 
 static bool
 ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
 {
+    uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals();
+
     for (unsigned i = 0; i < blockObj->numVariables(); i++) {
         Definition *dn = blockObj->definitionParseNode(i);
 
         MOZ_ASSERT(dn->isDefn());
+
+        // blockIndexToLocalIndex returns the frame slot following the unaliased
+        // locals. We add numAliased so that the cookie's slot value comes after
+        // all (aliased and unaliased) body level locals.
         if (!dn->pn_cookie.set(bce->parser->tokenStream, dn->pn_cookie.level(),
-                               blockObj->blockIndexToLocalIndex(dn->frameSlot())))
+                               numAliased + blockObj->blockIndexToLocalIndex(dn->frameSlot())))
         {
             return false;
         }
 
 #ifdef DEBUG
         for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             MOZ_ASSERT(pnu->pn_lexdef == dn);
             MOZ_ASSERT(!(pnu->pn_dflags & PND_BOUND));
@@ -802,17 +844,19 @@ EmitInternedObjectOp(ExclusiveContext *c
 
 // In a function, block-scoped locals go after the vars, and form part of the
 // fixed part of a stack frame.  Outside a function, there are no fixed vars,
 // but block-scoped locals still form part of the fixed part of a stack frame
 // and are thus addressable via GETLOCAL and friends.
 static void
 ComputeLocalOffset(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlockObject *> blockObj)
 {
-    unsigned nbodyfixed = bce->sc->isFunctionBox() ? bce->script->bindings.numBodyLevelLocals() : 0;
+    unsigned nbodyfixed = bce->sc->isFunctionBox()
+                          ? bce->script->bindings.numUnaliasedBodyLevelLocals()
+                          : 0;
     unsigned localOffset = nbodyfixed;
 
     if (bce->staticScope) {
         Rooted<NestedScopeObject *> outer(cx, bce->staticScope);
         for (; outer; outer = outer->enclosingNestedScope()) {
             if (outer->is<StaticBlockObject>()) {
                 StaticBlockObject &outerBlock = outer->as<StaticBlockObject>();
                 localOffset = outerBlock.localOffset() + outerBlock.numVariables();
@@ -1088,16 +1132,22 @@ EmitLocalOp(ExclusiveContext *cx, Byteco
 
 static bool
 EmitUnaliasedVarOp(ExclusiveContext *cx, JSOp op, uint32_t slot, MaybeCheckLexical checkLexical,
                    BytecodeEmitter *bce)
 {
     MOZ_ASSERT(JOF_OPTYPE(op) != JOF_SCOPECOORD);
 
     if (IsLocalOp(op)) {
+        // Only unaliased locals have stack slots assigned to them. Convert the
+        // var index (which includes unaliased and aliased locals) to the stack
+        // slot index.
+        MOZ_ASSERT(bce->localsToFrameSlots_[slot] <= slot);
+        slot = bce->localsToFrameSlots_[slot];
+
         if (checkLexical) {
             MOZ_ASSERT(op != JSOP_INITLEXICAL);
             if (!EmitLocalOp(cx, bce, JSOP_CHECKLEXICAL, slot))
                 return false;
         }
 
         return EmitLocalOp(cx, bce, op, slot);
     }
@@ -1295,16 +1345,17 @@ EmitAliasedVarOp(ExclusiveContext *cx, J
         if (local < bceOfDef->script->bindings.numBodyLevelLocals()) {
             if (!AssignHops(bce, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
                 return false;
             JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef, bceOfDef->script, pn->name(), &sc));
         } else {
             MOZ_ASSERT_IF(bce->sc->isFunctionBox(), local <= bceOfDef->script->bindings.numLocals());
             MOZ_ASSERT(bceOfDef->staticScope->is<StaticBlockObject>());
             Rooted<StaticBlockObject*> b(cx, &bceOfDef->staticScope->as<StaticBlockObject>());
+            local = bceOfDef->localsToFrameSlots_[local];
             while (local < b->localOffset()) {
                 if (b->needsClone())
                     skippedScopes++;
                 b = &b->enclosingNestedScope()->as<StaticBlockObject>();
             }
             if (!AssignHops(bce, pn, skippedScopes, &sc))
                 return false;
             sc.setSlot(b->localIndexToSlot(local));
@@ -2564,17 +2615,22 @@ InitializeBlockScopedLocalsFromStack(Exc
     for (unsigned i = blockObj->numVariables(); i > 0; --i) {
         if (blockObj->isAliased(i - 1)) {
             ScopeCoordinate sc;
             sc.setHops(0);
             sc.setSlot(BlockObject::RESERVED_SLOTS + i - 1);
             if (!EmitAliasedVarOp(cx, JSOP_INITALIASEDLEXICAL, sc, DontCheckLexical, bce))
                 return false;
         } else {
-            unsigned local = blockObj->blockIndexToLocalIndex(i - 1);
+            // blockIndexToLocalIndex returns the slot index after the unaliased
+            // locals stored in the frame. EmitUnaliasedVarOp expects the slot index
+            // to include both unaliased and aliased locals, so we have to add the
+            // number of aliased locals.
+            uint32_t numAliased = bce->script->bindings.numAliasedBodyLevelLocals();
+            unsigned local = blockObj->blockIndexToLocalIndex(i - 1) + numAliased;
             if (!EmitUnaliasedVarOp(cx, JSOP_INITLEXICAL, local, DontCheckLexical, bce))
                 return false;
         }
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
     }
     return true;
 }
@@ -2944,16 +3000,19 @@ BytecodeEmitter::isRunOnceLambda()
     return !funbox->argumentsHasLocalBinding() &&
            !funbox->isGenerator() &&
            !funbox->function()->name();
 }
 
 bool
 frontend::EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body)
 {
+    if (!bce->updateLocalsToFrameSlots())
+        return false;
+
     /*
      * IonBuilder has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
     FunctionBox *funbox = bce->sc->asFunctionBox();
@@ -5258,17 +5317,17 @@ EmitFunc(ExclusiveContext *cx, BytecodeE
         bce->switchToMain();
     } else {
 #ifdef DEBUG
         BindingIter bi(bce->script);
         while (bi->name() != fun->atom())
             bi++;
         MOZ_ASSERT(bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT ||
                    bi->kind() == Binding::ARGUMENT);
-        MOZ_ASSERT(bi.frameIndex() < JS_BIT(20));
+        MOZ_ASSERT(bi.argOrLocalIndex() < JS_BIT(20));
 #endif
         pn->pn_index = index;
         if (!EmitIndexOp(cx, JSOP_LAMBDA, index, bce))
             return false;
         MOZ_ASSERT(pn->getOp() == JSOP_GETLOCAL || pn->getOp() == JSOP_GETARG);
         JSOp setOp = pn->getOp() == JSOP_GETLOCAL ? JSOP_SETLOCAL : JSOP_SETARG;
         if (!EmitVarOp(cx, pn, setOp, bce))
             return false;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -111,16 +111,23 @@ struct BytecodeEmitter
     StmtInfoBCE     *topStmt;       /* top of statement info stack */
     StmtInfoBCE     *topScopeStmt;  /* top lexical scope statement */
     Rooted<NestedScopeObject *> staticScope;
                                     /* compile time scope chain */
 
     OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
 
+    /*
+     * Only unaliased locals have stack slots assigned to them. This vector is
+     * used to map a local index (which includes unaliased and aliased locals)
+     * to its stack slot index.
+     */
+    Vector<uint32_t, 16> localsToFrameSlots_;
+
     int32_t         stackDepth;     /* current stack depth in script frame */
     uint32_t        maxStackDepth;  /* maximum stack depth so far */
 
     uint32_t        arrayCompDepth; /* stack depth of array in comprehension */
 
     unsigned        emitLevel;      /* js::frontend::EmitTree recursion level */
 
     CGConstList     constList;      /* constants to be included with the script */
@@ -173,16 +180,17 @@ struct BytecodeEmitter
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
     BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
                     HandleScript script, Handle<LazyScript *> lazyScript,
                     bool insideEval, HandleScript evalCaller, bool hasGlobalScope,
                     uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
+    bool updateLocalsToFrameSlots();
 
     bool isAliasedName(ParseNode *pn);
 
     MOZ_ALWAYS_INLINE
     bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
         AtomIndexAddPtr p = atomIndices->lookupForAdd(atom);
         if (p) {
             *indexp = p.value();
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -315,17 +315,18 @@ void
 ParseContext<ParseHandler>::popLetDecl(JSAtom *atom)
 {
     MOZ_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET);
     decls_.remove(atom);
 }
 
 template <typename ParseHandler>
 static void
-AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst)
+AppendPackedBindings(const ParseContext<ParseHandler> *pc, const DeclVector &vec, Binding *dst,
+                     uint32_t *numUnaliased = nullptr)
 {
     for (size_t i = 0; i < vec.length(); ++i, ++dst) {
         Definition *dn = vec[i];
         PropertyName *name = dn->name();
 
         Binding::Kind kind;
         switch (dn->kind()) {
           case Definition::LET:
@@ -352,16 +353,18 @@ AppendPackedBindings(const ParseContext<
          * maintains the canonical definition for each name, so use that.
          */
         MOZ_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
         bool aliased = dn->isClosed() ||
                        (pc->sc->allLocalsAliased() &&
                         pc->decls().lookupFirst(name) == dn);
 
         *dst = Binding(name, kind, aliased);
+        if (!aliased && numUnaliased)
+            ++*numUnaliased;
     }
 }
 
 template <typename ParseHandler>
 bool
 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext *cx, TokenStream &ts,
                                                      LifoAlloc &alloc,
                                                      InternalHandle<Bindings*> bindings) const
@@ -387,23 +390,27 @@ ParseContext<ParseHandler>::generateFunc
 
     uint32_t count = args_.length() + vars_.length() + bodyLevelLexicals_.length();
     Binding *packedBindings = alloc.newArrayUninitialized<Binding>(count);
     if (!packedBindings) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
+    uint32_t numUnaliasedVars = 0;
+    uint32_t numUnaliasedBodyLevelLexicals = 0;
+
     AppendPackedBindings(this, args_, packedBindings);
-    AppendPackedBindings(this, vars_, packedBindings + args_.length());
+    AppendPackedBindings(this, vars_, packedBindings + args_.length(), &numUnaliasedVars);
     AppendPackedBindings(this, bodyLevelLexicals_,
-                         packedBindings + args_.length() + vars_.length());
+                         packedBindings + args_.length() + vars_.length(), &numUnaliasedBodyLevelLexicals);
 
     return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
                                               bodyLevelLexicals_.length(), blockScopeDepth,
+                                              numUnaliasedVars, numUnaliasedBodyLevelLexicals,
                                               packedBindings);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
                                    unsigned errorNumber, va_list args)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-24.js
@@ -0,0 +1,24 @@
+// Make sure the getVariable/setVariable/eval functions work correctly with
+// unaliased locals.
+var g = newGlobal();
+g.eval('\
+function g() { debugger; };\
+function f(arg) {\
+    var y = arg - 3;\
+    var a1 = 1;\
+    var a2 = 1;\
+    var b = arg + 9;\
+    var z = function() { return a1 + a2; };\
+    g();\
+};');
+
+var dbg = new Debugger(g);
+
+dbg.onDebuggerStatement = function handleDebugger(frame) {
+    assertEq(frame.older.eval("y + b").return, 26);
+    assertEq(frame.older.environment.getVariable("y"), 7);
+    frame.older.environment.setVariable("b", 4);
+    assertEq(frame.older.eval("y + b").return, 11);
+};
+
+g.f(10);
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -88,17 +88,17 @@ BaselineFrame::trace(JSTracer *trc, JitF
         // All locals are live.
         MarkLocals(this, trc, 0, numValueSlots());
     } else {
         // Mark operand stack.
         MarkLocals(this, trc, nfixed, numValueSlots());
 
         // Clear dead block-scoped locals.
         while (nfixed > nlivefixed)
-            unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setMagic(JS_UNINITIALIZED_LEXICAL);
+            unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL);
 
         // Mark live locals.
         MarkLocals(this, trc, 0, nlivefixed);
     }
 }
 
 bool
 BaselineFrame::copyRawFrameSlots(AutoValueVector *vec) const
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -183,21 +183,18 @@ class BaselineFrame
 
     Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
         MOZ_ASSERT(i < numActualArgs());
         MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
         MOZ_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
         return argv()[i];
     }
 
-    Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) const {
+    Value &unaliasedLocal(uint32_t i) const {
         MOZ_ASSERT(i < script()->nfixed());
-#ifdef DEBUG
-        CheckLocalUnaliased(checkAliasing, script(), i);
-#endif
         return *valueSlot(i);
     }
 
     unsigned numActualArgs() const {
         return *(size_t *)(reinterpret_cast<const uint8_t *>(this) +
                              BaselineFrame::Size() +
                              offsetOfNumActualArgs());
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7708,17 +7708,17 @@ IonBuilder::getElemTryCache(bool *emitte
     // the cache can attach stubs for particular properties.
     if (index->mightBeType(MIRType_String) || index->mightBeType(MIRType_Symbol))
         barrier = BarrierKind::TypeSet;
 
     // See note about always needing a barrier in jsop_getprop.
     if (needsToMonitorMissingProperties(types))
         barrier = BarrierKind::TypeSet;
 
-    MInstruction *ins = MGetElementCache::New(alloc(), obj, index, barrier != BarrierKind::NoBarrier);
+    MInstruction *ins = MGetElementCache::New(alloc(), obj, index, barrier == BarrierKind::TypeSet);
 
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
     // Spice up type information.
@@ -9639,21 +9639,27 @@ IonBuilder::getPropTryCache(bool *emitte
     if (inspector->hasSeenAccessedGetter(pc))
         barrier = BarrierKind::TypeSet;
 
     if (needsToMonitorMissingProperties(types))
         barrier = BarrierKind::TypeSet;
 
     // Caches can read values from prototypes, so update the barrier to
     // reflect such possible values.
-    if (barrier == BarrierKind::NoBarrier)
-        barrier = PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
+    if (barrier != BarrierKind::TypeSet) {
+        BarrierKind protoBarrier =
+            PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
+        if (protoBarrier != BarrierKind::NoBarrier) {
+            MOZ_ASSERT(barrier <= protoBarrier);
+            barrier = protoBarrier;
+        }
+    }
 
     MGetPropertyCache *load = MGetPropertyCache::New(alloc(), obj, name,
-                                                     barrier != BarrierKind::NoBarrier);
+                                                     barrier == BarrierKind::TypeSet);
 
     // Try to mark the cache as idempotent.
     //
     // In parallel execution, idempotency of caches is ignored, since we
     // repeat the entire ForkJoin workload if we bail out. Note that it's
     // overly restrictive to mark everything as idempotent, because we can
     // treat non-idempotent caches in parallel as repeatable.
     if (obj->type() == MIRType_Object && !invalidatedIdempotentCache() &&
--- a/js/src/jit/RematerializedFrame.h
+++ b/js/src/jit/RematerializedFrame.h
@@ -144,21 +144,18 @@ class RematerializedFrame
         return slots_ + numActualArgs_;
     }
 
     Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
         MOZ_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
         MOZ_ASSERT(i < script()->nfixed());
         return locals()[i];
     }
-    Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+    Value &unaliasedLocal(unsigned i) {
         MOZ_ASSERT(i < script()->nfixed());
-#ifdef DEBUG
-        CheckLocalUnaliased(checkAliasing, script(), i);
-#endif
         return locals()[i];
     }
     Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
         MOZ_ASSERT(i < numFormalArgs());
         MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
                                      !script()->formalIsAliased(i));
         return argv()[i];
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -59,49 +59,60 @@ using namespace js::frontend;
 
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::RotateLeft;
 
 typedef Rooted<GlobalObject *> RootedGlobalObject;
 
 /* static */ uint32_t
-Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings)
+Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings,
+                            uint32_t *unaliasedSlot)
 {
     HandlePropertyName arguments = cx->names().arguments;
     BindingIter bi(bindings);
     while (bi->name() != arguments)
         bi++;
-    return bi.frameIndex();
+
+    if (unaliasedSlot)
+        *unaliasedSlot = bi->aliased() ? UINT32_MAX : bi.frameIndex();
+
+    return bi.localIndex();
 }
 
 bool
 Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self,
                                    uint32_t numArgs, uint32_t numVars,
                                    uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
+                                   uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
                                    Binding *bindingArray)
 {
     MOZ_ASSERT(!self->callObjShape_);
     MOZ_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
     MOZ_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
     MOZ_ASSERT(numArgs <= ARGC_LIMIT);
     MOZ_ASSERT(numVars <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBlockScoped <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBodyLevelLexicals <= LOCALNO_LIMIT);
     uint64_t totalSlots = uint64_t(numVars) +
                           uint64_t(numBodyLevelLexicals) +
                           uint64_t(numBlockScoped);
     MOZ_ASSERT(totalSlots <= LOCALNO_LIMIT);
     MOZ_ASSERT(UINT32_MAX - numArgs >= totalSlots);
 
+    MOZ_ASSERT(numUnaliasedVars <= numVars);
+    MOZ_ASSERT(numUnaliasedBodyLevelLexicals <= numBodyLevelLexicals);
+
     self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
     self->numArgs_ = numArgs;
     self->numVars_ = numVars;
     self->numBodyLevelLexicals_ = numBodyLevelLexicals;
     self->numBlockScoped_ = numBlockScoped;
+    self->numUnaliasedVars_ = numUnaliasedVars;
+    self->numUnaliasedBodyLevelLexicals_ = numUnaliasedBodyLevelLexicals;
 
     // Get the initial shape to use when creating CallObjects for this script.
     // After creation, a CallObject's shape may change completely (via direct eval() or
     // other operations that mutate the lexical scope). However, since the
     // lexical bindings added to the initial shape are permanent and the
     // allocKind/nfixed of a CallObject cannot change, one may assume that the
     // slot location (whether in the fixed or dynamic slots) of a variable is
     // the same as in the initial shape. (This is assumed by the interpreter and
@@ -118,17 +129,17 @@ Bindings::initWithTemporaryStorage(Exclu
         if (bi->aliased()) {
             // Per ES6, lexical bindings cannot be accessed until
             // initialized. Remember the first aliased slot that is a
             // body-level let, so that they may be initialized to sentinel
             // magic values.
             if (numBodyLevelLexicals > 0 &&
                 nslots < aliasedBodyLevelLexicalBegin &&
                 bi->kind() == Binding::VARIABLE &&
-                bi.frameIndex() >= numVars)
+                bi.localIndex() >= numVars)
             {
                 aliasedBodyLevelLexicalBegin = nslots;
             }
 
             nslots++;
         }
     }
     self->aliasedBodyLevelLexicalBegin_ = aliasedBodyLevelLexicalBegin;
@@ -210,17 +221,20 @@ Bindings::clone(JSContext *cx, InternalB
     MOZ_ASSERT(size_t(off) <= srcScript->dataSize());
     Binding *dstPackedBindings = (Binding *)(dstScriptData + off);
 
     /*
      * Since atoms are shareable throughout the runtime, we can simply copy
      * the source's bindingArray directly.
      */
     if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(),
-                                  src.numBodyLevelLexicals(), src.numBlockScoped(),
+                                  src.numBodyLevelLexicals(),
+                                  src.numBlockScoped(),
+                                  src.numUnaliasedVars(),
+                                  src.numUnaliasedBodyLevelLexicals(),
                                   src.bindingArray()))
     {
         return false;
     }
 
     self->switchToScriptStorage(dstPackedBindings);
     return true;
 }
@@ -229,17 +243,19 @@ Bindings::clone(JSContext *cx, InternalB
 GCMethods<Bindings>::initial()
 {
     return Bindings();
 }
 
 template<XDRMode mode>
 static bool
 XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, uint16_t numArgs, uint32_t numVars,
-                  uint16_t numBodyLevelLexicals, uint16_t numBlockScoped, HandleScript script)
+                  uint16_t numBodyLevelLexicals, uint16_t numBlockScoped,
+                  uint32_t numUnaliasedVars, uint16_t numUnaliasedBodyLevelLexicals,
+                  HandleScript script)
 {
     JSContext *cx = xdr->cx();
 
     if (mode == XDR_ENCODE) {
         for (BindingIter bi(script); bi; bi++) {
             RootedAtom atom(cx, bi->name());
             if (!XDRAtom(xdr, &atom))
                 return false;
@@ -276,16 +292,17 @@ XDRScriptBindings(XDRState<mode> *xdr, L
             bool aliased = bool(u8 & 1);
 
             bindingArray[i] = Binding(name, kind, aliased);
         }
 
         InternalBindingsHandle bindings(script, &script->bindings);
         if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars,
                                                 numBodyLevelLexicals, numBlockScoped,
+                                                numUnaliasedVars, numUnaliasedBodyLevelLexicals,
                                                 bindingArray))
         {
             return false;
         }
     }
 
     return true;
 }
@@ -594,33 +611,41 @@ js::XDRScript(XDRState<mode> *xdr, Handl
     natoms = nsrcnotes = 0;
     nconsts = nobjects = nregexps = ntrynotes = nblockscopes = 0;
 
     /* XDR arguments and vars. */
     uint16_t nargs = 0;
     uint16_t nblocklocals = 0;
     uint16_t nbodylevellexicals = 0;
     uint32_t nvars = 0;
+    uint32_t nunaliasedvars = 0;
+    uint16_t nunaliasedbodylevellexicals = 0;
     if (mode == XDR_ENCODE) {
         script = scriptp.get();
         MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
 
         nargs = script->bindings.numArgs();
         nblocklocals = script->bindings.numBlockScoped();
         nbodylevellexicals = script->bindings.numBodyLevelLexicals();
         nvars = script->bindings.numVars();
+        nunaliasedvars = script->bindings.numUnaliasedVars();
+        nunaliasedbodylevellexicals = script->bindings.numUnaliasedBodyLevelLexicals();
     }
     if (!xdr->codeUint16(&nargs))
         return false;
     if (!xdr->codeUint16(&nblocklocals))
         return false;
     if (!xdr->codeUint16(&nbodylevellexicals))
         return false;
     if (!xdr->codeUint32(&nvars))
         return false;
+    if (!xdr->codeUint32(&nunaliasedvars))
+        return false;
+    if (!xdr->codeUint16(&nunaliasedbodylevellexicals))
+        return false;
 
     if (mode == XDR_ENCODE)
         length = script->length();
     if (!xdr->codeUint32(&length))
         return false;
 
     if (mode == XDR_ENCODE) {
         prologLength = script->mainOffset();
@@ -754,17 +779,18 @@ js::XDRScript(XDRState<mode> *xdr, Handl
         script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
                                   options, /* staticLevel = */ 0, sourceObject, 0, 0);
         if (!script)
             return false;
     }
 
     /* JSScript::partiallyInit assumes script->bindings is fully initialized. */
     LifoAllocScope las(&cx->tempLifoAlloc());
-    if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals, script))
+    if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals,
+                           nunaliasedvars, nunaliasedbodylevellexicals, script))
         return false;
 
     if (mode == XDR_DECODE) {
         if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes,
                                      nblockscopes, nTypeSets))
         {
             return false;
         }
@@ -3513,17 +3539,18 @@ js::SetFrameArgumentsObject(JSContext *c
                             HandleScript script, JSObject *argsobj)
 {
     /*
      * Replace any optimized arguments in the frame with an explicit arguments
      * object. Note that 'arguments' may have already been overwritten.
      */
 
     InternalBindingsHandle bindings(script, &script->bindings);
-    const uint32_t var = Bindings::argumentsVarIndex(cx, bindings);
+    uint32_t unaliasedSlot;
+    const uint32_t var = Bindings::argumentsVarIndex(cx, bindings, &unaliasedSlot);
 
     if (script->varIsAliased(var)) {
         /*
          * Scan the script to find the slot in the call object that 'arguments'
          * is assigned to.
          */
         jsbytecode *pc = script->code();
         while (*pc != JSOP_ARGUMENTS)
@@ -3532,18 +3559,18 @@ js::SetFrameArgumentsObject(JSContext *c
         MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
 
         // Note that here and below, it is insufficient to only check for
         // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
         // arguments slot.
         if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(ScopeCoordinate(pc))))
             frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj));
     } else {
-        if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var)))
-            frame.unaliasedLocal(var) = ObjectValue(*argsobj);
+        if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(unaliasedSlot)))
+            frame.unaliasedLocal(unaliasedSlot) = ObjectValue(*argsobj);
     }
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
 {
     MOZ_ASSERT(script->functionNonDelazifying());
     MOZ_ASSERT(script->analyzedArgsUsage());
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -182,17 +182,19 @@ class Bindings
     friend class AliasedFormalIter;
 
     HeapPtrShape callObjShape_;
     uintptr_t bindingArrayAndFlag_;
     uint16_t numArgs_;
     uint16_t numBlockScoped_;
     uint16_t numBodyLevelLexicals_;
     uint16_t aliasedBodyLevelLexicalBegin_;
+    uint16_t numUnaliasedBodyLevelLexicals_;
     uint32_t numVars_;
+    uint32_t numUnaliasedVars_;
 
 #if JS_BITS_PER_WORD == 32
     // Bindings is allocated inline inside JSScript, which needs to be
     // gc::Cell aligned.
     uint32_t padding_;
 #endif
 
     /*
@@ -222,16 +224,17 @@ class Bindings
      * bindingArray must have length numArgs + numVars +
      * numBodyLevelLexicals. Before the temporary storage is release,
      * switchToScriptStorage must be called, providing a pointer into the
      * Binding array stored in script->data.
      */
     static bool initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self,
                                          uint32_t numArgs, uint32_t numVars,
                                          uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
+                                         uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
                                          Binding *bindingArray);
 
     // CompileScript parses and compiles one statement at a time, but the result
     // is one Script object.  There will be no vars or bindings, because those
     // go on the global, but there may be block-scoped locals, and the number of
     // block-scoped locals may increase as we parse more expressions.  This
     // helper updates the number of block scoped variables in a script as it is
     // being parsed.
@@ -252,28 +255,35 @@ class Bindings
     static bool clone(JSContext *cx, InternalBindingsHandle self, uint8_t *dstScriptData,
                       HandleScript srcScript);
 
     uint32_t numArgs() const { return numArgs_; }
     uint32_t numVars() const { return numVars_; }
     uint32_t numBodyLevelLexicals() const { return numBodyLevelLexicals_; }
     uint32_t numBlockScoped() const { return numBlockScoped_; }
     uint32_t numBodyLevelLocals() const { return numVars_ + numBodyLevelLexicals_; }
+    uint32_t numUnaliasedBodyLevelLocals() const { return numUnaliasedVars_ + numUnaliasedBodyLevelLexicals_; }
+    uint32_t numAliasedBodyLevelLocals() const { return numBodyLevelLocals() - numUnaliasedBodyLevelLocals(); }
     uint32_t numLocals() const { return numVars() + numBodyLevelLexicals() + numBlockScoped(); }
+    uint32_t numUnaliasedLocals() const { return numUnaliasedVars() + numUnaliasedBodyLevelLexicals() + numBlockScoped(); }
     uint32_t lexicalBegin() const { return numArgs() + numVars(); }
     uint32_t aliasedBodyLevelLexicalBegin() const { return aliasedBodyLevelLexicalBegin_; }
 
+    uint32_t numUnaliasedVars() const { return numUnaliasedVars_; }
+    uint32_t numUnaliasedBodyLevelLexicals() const { return numUnaliasedBodyLevelLexicals_; }
+
     // Return the size of the bindingArray.
     uint32_t count() const { return numArgs() + numVars() + numBodyLevelLexicals(); }
 
     /* Return the initial shape of call objects created for this scope. */
     Shape *callObjShape() const { return callObjShape_; }
 
     /* Convenience method to get the var index of 'arguments'. */
-    static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle);
+    static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle,
+                                      uint32_t *unaliasedSlot = nullptr);
 
     /* Return whether the binding at bindingIndex is aliased. */
     bool bindingIsAliased(uint32_t bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
     bool hasAnyAliasedBindings() const {
         if (!callObjShape_)
             return false;
@@ -1048,30 +1058,30 @@ class JSScript : public js::gc::TenuredC
         return column_;
     }
 
     void setColumn(size_t column) { column_ = column; }
 
     // The fixed part of a stack frame is comprised of vars (in function code)
     // and block-scoped locals (in all kinds of code).
     size_t nfixed() const {
-        return function_ ? bindings.numLocals() : bindings.numBlockScoped();
+        return function_ ? bindings.numUnaliasedLocals() : bindings.numBlockScoped();
     }
 
     // Number of fixed slots reserved for vars.  Only nonzero for function
     // code.
     size_t nfixedvars() const {
-        return function_ ? bindings.numVars() : 0;
+        return function_ ? bindings.numUnaliasedVars() : 0;
     }
 
     // Number of fixed slots reserved for body-level lexicals and vars. This
     // value minus nfixedvars() is the number of body-level lexicals. Only
     // nonzero for function code.
     size_t nbodyfixed() const {
-        return function_ ? bindings.numBodyLevelLocals() : 0;
+        return function_ ? bindings.numUnaliasedBodyLevelLocals() : 0;
     }
 
     // Aliases for clarity when dealing with lexical slots.
     size_t fixedLexicalBegin() const {
         return nfixedvars();
     }
 
     size_t fixedLexicalEnd() const {
@@ -1679,32 +1689,62 @@ namespace js {
  * The order of iteration is:
  *  - first, formal arguments, from index 0 to numArgs
  *  - next, variables, from index 0 to numLocals
  */
 class BindingIter
 {
     const InternalBindingsHandle bindings_;
     uint32_t i_;
+    uint32_t unaliasedLocal_;
 
     friend class Bindings;
 
   public:
-    explicit BindingIter(const InternalBindingsHandle &bindings) : bindings_(bindings), i_(0) {}
-    explicit BindingIter(const HandleScript &script) : bindings_(script, &script->bindings), i_(0) {}
+    explicit BindingIter(const InternalBindingsHandle &bindings)
+      : bindings_(bindings), i_(0), unaliasedLocal_(0) {}
+    explicit BindingIter(const HandleScript &script)
+      : bindings_(script, &script->bindings), i_(0), unaliasedLocal_(0) {}
 
     bool done() const { return i_ == bindings_->count(); }
     operator bool() const { return !done(); }
-    void operator++(int) { MOZ_ASSERT(!done()); i_++; }
     BindingIter &operator++() { (*this)++; return *this; }
 
+    void operator++(int) {
+        MOZ_ASSERT(!done());
+        const Binding &binding = **this;
+        if (binding.kind() != Binding::ARGUMENT && !binding.aliased())
+            unaliasedLocal_++;
+        i_++;
+    }
+
+    // Stack slots are assigned to arguments and unaliased locals. frameIndex()
+    // returns the slot index. It's invalid to call this method when the
+    // iterator is stopped on an aliased local, as it has no stack slot.
     uint32_t frameIndex() const {
         MOZ_ASSERT(!done());
+        if (i_ < bindings_->numArgs())
+            return i_;
+        MOZ_ASSERT(!(*this)->aliased());
+        return unaliasedLocal_;
+    }
+    uint32_t argIndex() const {
+        MOZ_ASSERT(!done());
+        MOZ_ASSERT(i_ < bindings_->numArgs());
+        return i_;
+    }
+    uint32_t argOrLocalIndex() const {
+        MOZ_ASSERT(!done());
         return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs();
     }
+    uint32_t localIndex() const {
+        MOZ_ASSERT(!done());
+        MOZ_ASSERT(i_ >= bindings_->numArgs());
+        return i_ - bindings_->numArgs();
+    }
 
     const Binding &operator*() const { MOZ_ASSERT(!done()); return bindings_->bindingArray()[i_]; }
     const Binding *operator->() const { MOZ_ASSERT(!done()); return &bindings_->bindingArray()[i_]; }
 };
 
 /*
  * This helper function fills the given BindingVector with the sequential
  * values of BindingIter.
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -18,17 +18,19 @@
 
 #include "vm/Shape-inl.h"
 
 namespace js {
 
 inline
 Bindings::Bindings()
     : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
-      numArgs_(0), numBlockScoped_(0), numVars_(0)
+      numArgs_(0), numBlockScoped_(0),
+      numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0),
+      numVars_(0), numUnaliasedVars_(0)
 {}
 
 inline
 AliasedFormalIter::AliasedFormalIter(JSScript *script)
   : begin_(script->bindingArray()),
     p_(begin_),
     end_(begin_ + (script->funHasAnyAliasedFormal() ? script->numArgs() : 0)),
     slot_(CallObject::RESERVED_SLOTS)
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -1125,17 +1125,17 @@ ForkJoinOperation::reportBailoutWarnings
                         arg = frame->callObj().getSlot(scopeSlot);
                         scopeSlot++;
                     } else if (i < frame->numFormalArgs()) {
                         if (script->argsObjAliasesFormals() && frame->hasArgsObj())
                             arg = frame->argsObj().arg(i);
                         else
                             arg = frame->unaliasedActual(i, DONT_CHECK_ALIASING);
                     } else {
-                        arg = frame->unaliasedLocal(i - frame->numFormalArgs(), DONT_CHECK_ALIASING);
+                        arg = frame->unaliasedLocal(i - frame->numFormalArgs());
                     }
 
                     JSAutoByteString valueBytes;
                     const char *valueChars = ValueToChar(cx_, arg, valueBytes);
                     if (!valueChars)
                         return false;
 
                     sp.printf("\n      %s %s = %s",
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1344,20 +1344,20 @@ class DebugScopeProxy : public BaseProxy
             Bindings &bindings = script->bindings;
             BindingIter bi(script);
             while (bi && NameToId(bi->name()) != id)
                 bi++;
             if (!bi)
                 return true;
 
             if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) {
-                uint32_t i = bi.frameIndex();
-                if (script->bodyLevelLocalIsAliased(i))
+                if (script->bodyLevelLocalIsAliased(bi.localIndex()))
                     return true;
 
+                uint32_t i = bi.frameIndex();
                 if (maybeLiveScope) {
                     AbstractFramePtr frame = maybeLiveScope->frame();
                     if (action == GET)
                         vp.set(frame.unaliasedLocal(i));
                     else
                         frame.unaliasedLocal(i) = vp;
                 } else if (NativeObject *snapshot = debugScope->maybeSnapshot()) {
                     if (action == GET)
@@ -1368,17 +1368,17 @@ class DebugScopeProxy : public BaseProxy
                     /* The unaliased value has been lost to the debugger. */
                     if (action == GET) {
                         *accessResult = ACCESS_LOST;
                         return true;
                     }
                 }
             } else {
                 MOZ_ASSERT(bi->kind() == Binding::ARGUMENT);
-                unsigned i = bi.frameIndex();
+                unsigned i = bi.argIndex();
                 if (script->formalIsAliased(i))
                     return true;
 
                 if (maybeLiveScope) {
                     AbstractFramePtr frame = maybeLiveScope->frame();
                     if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
                         if (action == GET)
                             vp.set(frame.argsObj().arg(i));
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -113,22 +113,19 @@ inline Value &
 InterpreterFrame::unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing)
 {
     MOZ_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
     MOZ_ASSERT(i < script()->nfixedvars());
     return slots()[i];
 }
 
 inline Value &
-InterpreterFrame::unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing)
+InterpreterFrame::unaliasedLocal(uint32_t i)
 {
     MOZ_ASSERT(i < script()->nfixed());
-#ifdef DEBUG
-    CheckLocalUnaliased(checkAliasing, script(), i);
-#endif
     return slots()[i];
 }
 
 inline Value &
 InterpreterFrame::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     MOZ_ASSERT(i < numFormalArgs());
     MOZ_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
@@ -497,23 +494,23 @@ AbstractFramePtr::unaliasedVar(uint32_t 
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedVar(i, checkAliasing);
     if (isBaselineFrame())
         return asBaselineFrame()->unaliasedVar(i, checkAliasing);
     return asRematerializedFrame()->unaliasedVar(i, checkAliasing);
 }
 
 inline Value &
-AbstractFramePtr::unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing)
+AbstractFramePtr::unaliasedLocal(uint32_t i)
 {
     if (isInterpreterFrame())
-        return asInterpreterFrame()->unaliasedLocal(i, checkAliasing);
+        return asInterpreterFrame()->unaliasedLocal(i);
     if (isBaselineFrame())
-        return asBaselineFrame()->unaliasedLocal(i, checkAliasing);
-    return asRematerializedFrame()->unaliasedLocal(i, checkAliasing);
+        return asBaselineFrame()->unaliasedLocal(i);
+    return asRematerializedFrame()->unaliasedLocal(i);
 }
 
 inline Value &
 AbstractFramePtr::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->unaliasedFormal(i, checkAliasing);
     if (isBaselineFrame())
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -363,17 +363,17 @@ InterpreterFrame::markValues(JSTracer *t
         // All locals are live.
         markValues(trc, 0, sp - slots());
     } else {
         // Mark operand stack.
         markValues(trc, nfixed, sp - slots());
 
         // Clear dead block-scoped locals.
         while (nfixed > nlivefixed)
-            unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setMagic(JS_UNINITIALIZED_LEXICAL);
+            unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL);
 
         // Mark live locals.
         markValues(trc, 0, nlivefixed);
     }
 
     if (hasArgs()) {
         // Mark callee, |this| and arguments.
         unsigned argc = Max(numActualArgs(), numFormalArgs());
@@ -1343,33 +1343,16 @@ AbstractFramePtr::evalPrevScopeChain(JSC
 bool
 AbstractFramePtr::hasPushedSPSFrame() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->hasPushedSPSFrame();
     return asBaselineFrame()->hasPushedSPSFrame();
 }
 
-#ifdef DEBUG
-void
-js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i)
-{
-    if (!checkAliasing)
-        return;
-
-    MOZ_ASSERT(i < script->nfixed());
-    if (i < script->bindings.numVars()) {
-        MOZ_ASSERT(!script->varIsAliased(i));
-    } else {
-        // FIXME: The callers of this function do not easily have the PC of the
-        // current frame, and so they do not know the block scope.
-    }
-}
-#endif
-
 jit::JitActivation::JitActivation(JSContext *cx, bool active)
   : Activation(cx, Jit),
     active_(active),
     rematerializedFrames_(nullptr),
     ionRecovery_(cx),
     bailoutData_(nullptr)
 {
     if (active) {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -63,21 +63,16 @@ class ScopeCoordinate;
 // from the stack and the InterpreterRegs struct (pointed to by the
 // InterpreterActivation) is a local var of js::Interpret.
 
 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
 enum MaybeCheckLexical { CheckLexical = true, DontCheckLexical = false };
 
 /*****************************************************************************/
 
-#ifdef DEBUG
-extern void
-CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i);
-#endif
-
 namespace jit {
     class BaselineFrame;
     class RematerializedFrame;
 }
 
 /*
  * Pointer to either a ScriptFrameIter::Data, an InterpreterFrame, or a Baseline
  * JIT frame.
@@ -212,17 +207,17 @@ class AbstractFramePtr
     inline bool hasArgsObj() const;
     inline ArgumentsObject &argsObj() const;
     inline void initArgsObj(ArgumentsObject &argsobj) const;
     inline bool useNewType() const;
 
     inline bool copyRawFrameSlots(AutoValueVector *vec) const;
 
     inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
-    inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+    inline Value &unaliasedLocal(uint32_t i);
     inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
     inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
     template <class Op> inline void unaliasedForEachActual(JSContext *cx, Op op);
 
     inline bool prevUpToDate() const;
     inline void setPrevUpToDate() const;
 
     JSObject *evalPrevScopeChain(JSContext *cx) const;
@@ -512,17 +507,17 @@ class InterpreterFrame
      * Currently, all variables are given slots in *both* the stack frame and
      * heap objects, even though, as just described, only one should ever be
      * accessed. Thus, it is up to the code performing an access to access the
      * correct value. These functions assert that accesses to stack values are
      * unaliased. For more about canonical values locations.
      */
 
     inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING);
-    inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING);
+    inline Value &unaliasedLocal(uint32_t i);
 
     bool hasArgs() const { return isNonEvalFunctionFrame(); }
     inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING);
     inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING);
     template <class Op> inline void unaliasedForEachActual(Op op);
 
     bool copyRawFrameSlots(AutoValueVector *v);
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -23,17 +23,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 187);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 188);
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "prlog.h"
 #include "SelectionCarets.h"
 
 #include "gfxPrefs.h"
 #include "nsBidiPresUtils.h"
 #include "nsCanvasFrame.h"
 #include "nsCaret.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
@@ -31,16 +32,36 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/TouchEvents.h"
 #include "TouchCaret.h"
 #include "nsFrameSelection.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gSelectionCaretsLog;
+static const char* kSelectionCaretsLogModuleName = "SelectionCarets";
+
+// To enable all the SELECTIONCARETS_LOG print statements, set the environment
+// variable NSPR_LOG_MODULES=SelectionCarets:5
+#define SELECTIONCARETS_LOG(message, ...)                                      \
+  PR_LOG(gSelectionCaretsLog, PR_LOG_DEBUG,                                    \
+         ("SelectionCarets (%p): %s:%d : " message "\n", this, __FUNCTION__,   \
+          __LINE__, ##__VA_ARGS__));
+
+#define SELECTIONCARETS_LOG_STATIC(message, ...)                               \
+  PR_LOG(gSelectionCaretsLog, PR_LOG_DEBUG,                                    \
+         ("SelectionCarets: %s:%d : " message "\n", __FUNCTION__, __LINE__,    \
+          ##__VA_ARGS__));
+#else
+#define SELECTIONCARETS_LOG(message, ...)
+#define SELECTIONCARETS_LOG_STATIC(message, ...)
+#endif // #ifdef PR_LOGGING
+
 // We treat mouse/touch move as "REAL" move event once its move distance
 // exceed this value, in CSS pixel.
 static const int32_t kMoveStartTolerancePx = 5;
 // Time for trigger scroll end event, in miliseconds.
 static const int32_t kScrollEndTimerDelay = 300;
 // Read from preference "selectioncaret.noneditable". Indicate whether support
 // non-editable fields selection or not. We have stable state for editable
 // fields selection now. And we don't want to break this stable state when
@@ -51,41 +72,49 @@ static bool kSupportNonEditableFields = 
 
 NS_IMPL_ISUPPORTS(SelectionCarets,
                   nsISelectionListener,
                   nsIScrollObserver,
                   nsISupportsWeakReference)
 
 /*static*/ int32_t SelectionCarets::sSelectionCaretsInflateSize = 0;
 
-SelectionCarets::SelectionCarets(nsIPresShell *aPresShell)
-  : mActiveTouchId(-1)
+SelectionCarets::SelectionCarets(nsIPresShell* aPresShell)
+  : mPresShell(aPresShell)
+  , mActiveTouchId(-1)
   , mCaretCenterToDownPointOffsetY(0)
   , mDragMode(NONE)
   , mAPZenabled(false)
   , mEndCaretVisible(false)
   , mStartCaretVisible(false)
   , mVisible(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+#ifdef PR_LOGGING
+  if (!gSelectionCaretsLog) {
+    gSelectionCaretsLog = PR_NewLogModule(kSelectionCaretsLogModuleName);
+  }
+#endif
+
+  SELECTIONCARETS_LOG("Constructor, PresShell=%p", mPresShell);
+
   static bool addedPref = false;
   if (!addedPref) {
     Preferences::AddIntVarCache(&sSelectionCaretsInflateSize,
                                 "selectioncaret.inflatesize.threshold");
     Preferences::AddBoolVarCache(&kSupportNonEditableFields,
                                  "selectioncaret.noneditable");
     addedPref = true;
   }
-
-  mPresShell = aPresShell;
 }
 
 SelectionCarets::~SelectionCarets()
 {
+  SELECTIONCARETS_LOG("Destructor");
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mLongTapDetectorTimer) {
     mLongTapDetectorTimer->Cancel();
     mLongTapDetectorTimer = nullptr;
   }
 
   if (mScrollEndDetectorTimer) {
@@ -125,43 +154,43 @@ SelectionCarets::HandleEvent(WidgetEvent
     } else {
       movePoint = touchEvent->touches[0]->mRefPoint;
       nowTouchId = touchEvent->touches[0]->Identifier();
     }
   } else if (mouseEvent) {
     movePoint = LayoutDeviceIntPoint::ToUntyped(mouseEvent->AsGUIEvent()->refPoint);
   }
 
-  // Get event coordinate relative to canvas frame
-  nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
-  if (!canvasFrame) {
+  // Get event coordinate relative to root frame
+  nsIFrame* rootFrame = mPresShell->GetRootFrame();
+  if (!rootFrame) {
     return nsEventStatus_eIgnore;
   }
-  nsPoint ptInCanvas =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, movePoint, canvasFrame);
+  nsPoint ptInRoot =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, movePoint, rootFrame);
 
   if (aEvent->message == NS_TOUCH_START ||
       (aEvent->message == NS_MOUSE_BUTTON_DOWN &&
        mouseEvent->button == WidgetMouseEvent::eLeftButton)) {
     // If having a active touch, ignore other touch down event
     if (aEvent->message == NS_TOUCH_START && mActiveTouchId >= 0) {
       return nsEventStatus_eConsumeNoDefault;
     }
 
     mActiveTouchId = nowTouchId;
-    mDownPoint = ptInCanvas;
-    if (IsOnStartFrame(ptInCanvas)) {
+    mDownPoint = ptInRoot;
+    if (IsOnStartFrame(ptInRoot)) {
       mDragMode = START_FRAME;
-      mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - ptInCanvas.y;
+      mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - ptInRoot.y;
       SetSelectionDirection(false);
       SetSelectionDragState(true);
       return nsEventStatus_eConsumeNoDefault;
-    } else if (IsOnEndFrame(ptInCanvas)) {
+    } else if (IsOnEndFrame(ptInRoot)) {
       mDragMode = END_FRAME;
-      mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - ptInCanvas.y;
+      mCaretCenterToDownPointOffsetY = GetCaretYCenterPosition() - ptInRoot.y;
       SetSelectionDirection(true);
       SetSelectionDragState(true);
       return nsEventStatus_eConsumeNoDefault;
     } else {
       mDragMode = NONE;
       mActiveTouchId = -1;
       SetVisibility(false);
       LaunchLongTapDetector();
@@ -178,30 +207,31 @@ SelectionCarets::HandleEvent(WidgetEvent
         mActiveTouchId = -1;
       }
       return nsEventStatus_eConsumeNoDefault;
     }
   } else if (aEvent->message == NS_TOUCH_MOVE ||
              aEvent->message == NS_MOUSE_MOVE) {
     if (mDragMode == START_FRAME || mDragMode == END_FRAME) {
       if (mActiveTouchId == nowTouchId) {
-        ptInCanvas.y += mCaretCenterToDownPointOffsetY;
-        return DragSelection(ptInCanvas);
+        ptInRoot.y += mCaretCenterToDownPointOffsetY;
+        return DragSelection(ptInRoot);
       }
 
       return nsEventStatus_eConsumeNoDefault;
     }
 
-    nsPoint delta = mDownPoint - ptInCanvas;
+    nsPoint delta = mDownPoint - ptInRoot;
     if (NS_hypot(delta.x, delta.y) >
           nsPresContext::AppUnitsPerCSSPixel() * kMoveStartTolerancePx) {
       CancelLongTapDetector();
     }
   } else if (aEvent->message == NS_MOUSE_MOZLONGTAP) {
     if (!mVisible) {
+      SELECTIONCARETS_LOG("SelectWord from APZ");
       SelectWord();
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 static void
@@ -219,19 +249,23 @@ SetElementVisibility(dom::Element* aElem
 void
 SelectionCarets::SetVisibility(bool aVisible)
 {
   if (!mPresShell) {
     return;
   }
 
   if (mVisible == aVisible) {
+    SELECTIONCARETS_LOG("Set visibility %s, same as the old one",
+                        (aVisible ? "shown" : "hidden"));
     return;
   }
+
   mVisible = aVisible;
+  SELECTIONCARETS_LOG("Set visibility %s", (mVisible ? "shown" : "hidden"));
 
   dom::Element* startElement = mPresShell->GetSelectionCaretsStartElement();
   SetElementVisibility(startElement, mVisible && mStartCaretVisible);
 
   dom::Element* endElement = mPresShell->GetSelectionCaretsEndElement();
   SetElementVisibility(endElement, mVisible && mEndCaretVisible);
 
   // We must call SetHasTouchCaret() in order to get APZC to wait until the
@@ -240,38 +274,47 @@ SelectionCarets::SetVisibility(bool aVis
   // selection caret.
   mPresShell->SetMayHaveTouchCaret(mVisible);
 }
 
 void
 SelectionCarets::SetStartFrameVisibility(bool aVisible)
 {
   mStartCaretVisible = aVisible;
+  SELECTIONCARETS_LOG("Set start frame visibility %s",
+                      (mStartCaretVisible ? "shown" : "hidden"));
+
   dom::Element* element = mPresShell->GetSele