author | Ryan 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 id | 27736 |
push user | ryanvm@gmail.com |
push date | Wed, 29 Oct 2014 20:49:13 +0000 |
treeherder | mozilla-central@80e18ff7c7b2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 36.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
36.0a1
/
20141030030218
/
pushlog to previous
nightly linux64
36.0a1
/
20141030030218
/
pushlog to previous
nightly mac
36.0a1
/
20141030030218
/
pushlog to previous
nightly win32
36.0a1
/
20141030030218
/
pushlog to previous
nightly win64
36.0a1
/
20141030030218
/
pushlog to previous
|
docshell/base/nsDocShell.cpp | file | annotate | diff | comparison | revisions | |
dom/base/Navigator.cpp | file | annotate | diff | comparison | revisions | |
dom/media/test/eme.js | file | annotate | diff | comparison | revisions | |
dom/media/test/test_eme_canvas_blocked.html | file | annotate | diff | comparison | revisions | |
dom/media/test/test_eme_playback.html | file | annotate | diff | comparison | revisions | |
dom/media/test/test_eme_stream_capture_blocked.html | file | annotate | diff | comparison | revisions | |
dom/plugins/test/mochitest/test_npruntime_npnsetexception.html | file | annotate | diff | comparison | revisions | |
testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py | file | annotate | diff | comparison | revisions |
--- 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;