Merge mozilla-central to inbound. a=merge CLOSED TREE

--HG--
rename : dom/media/ipc/RemoteVideoDecoder.cpp => dom/media/ipc/GpuDecoderModule.cpp
rename : dom/media/ipc/RemoteVideoDecoder.h => dom/media/ipc/GpuDecoderModule.h
extra : rebase_source : 0503e2d45fffafb1e8dd1ddcd2115af2778a5c66
This commit is contained in:
Gurzau Raul 2018-11-09 07:46:56 +02:00
commit ff7bbb9936
252 changed files with 4616 additions and 1788 deletions

View File

@ -246,7 +246,7 @@ tasks:
notify:
email:
subject: "Thank you for your try submission of ${push.revision}. It's the best!"
content: "Your try push has been submitted. Use the link to view the status of your jobs."
content: "Your try push has been submitted. It's the best! Use the link to view the status of your jobs."
link:
text: "Treeherder Jobs"
href: "https://treeherder.mozilla.org/#/jobs?repo=${repository.project}&revision=${push.revision}"

View File

@ -43,10 +43,6 @@
#include "FuzzerDefs.h"
#endif
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
#ifdef MOZ_LINUX_32_SSE2_STARTUP_ERROR
#include <cpuid.h>
#include "mozilla/Unused.h"
@ -267,16 +263,6 @@ int main(int argc, char* argv[], char* envp[])
{
mozilla::TimeStamp start = mozilla::TimeStamp::Now();
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
if (argc > 1 && IsArg(argv[1], "contentproc")) {
std::string err;
if (!mozilla::EarlyStartMacSandboxIfEnabled(argc, argv, err)) {
Output("Sandbox error: %s\n", err.c_str());
MOZ_CRASH("Sandbox initialization failed");
}
}
#endif
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
// We are launching as a content process, delegate to the appropriate
// main

View File

@ -338,11 +338,6 @@ var FullScreen = {
// This is needed if they use the context menu to quit fullscreen
this._isPopupOpen = false;
this.cleanup();
// TabsInTitlebar skips appearance updates on resize events for exiting
// fullscreen, since that happens before we change the UI here in the
// "fullscreen" event. Hence we need to call it here to ensure the
// appearance is properly updated. See bug 1173768.
TabsInTitlebar.update();
}
if (enterFS && !document.fullscreenElement) {

View File

@ -10,18 +10,18 @@ var TabsInTitlebar = {
gDragSpaceObserver.init();
this._initialized = true;
this.update();
this._update();
},
allowedBy(condition, allow) {
if (allow) {
if (condition in this._disallowed) {
delete this._disallowed[condition];
this.update();
this._update();
}
} else if (!(condition in this._disallowed)) {
this._disallowed[condition] = null;
this.update();
this._update();
}
},
@ -58,12 +58,13 @@ var TabsInTitlebar = {
Services.prefs.getBoolPref(this._prefName));
},
update() {
if (!this._initialized || window.fullScreen) {
_update() {
if (!this._initialized) {
return;
}
let allowed = this.systemSupported &&
!window.fullScreen &&
(Object.keys(this._disallowed)).length == 0;
if (allowed) {
document.documentElement.setAttribute("tabsintitlebar", "true");
@ -103,7 +104,7 @@ var gDragSpaceObserver = {
pref: "browser.tabs.extraDragSpace",
init() {
this.update();
this._update();
Services.prefs.addObserver(this.pref, this);
},
@ -116,15 +117,14 @@ var gDragSpaceObserver = {
return;
}
this.update();
this._update();
},
update() {
_update() {
if (Services.prefs.getBoolPref(this.pref)) {
document.documentElement.setAttribute("extradragspace", "true");
} else {
document.documentElement.removeAttribute("extradragspace");
}
TabsInTitlebar.update();
},
};

View File

@ -7395,14 +7395,12 @@ const gAccessibilityServiceIndicator = {
[...document.querySelectorAll(".accessibility-indicator")].forEach(
indicator => ["click", "keypress"].forEach(type =>
indicator.addEventListener(type, this)));
TabsInTitlebar.update();
} else if (this._active) {
this._active = false;
document.documentElement.removeAttribute("accessibilitymode");
[...document.querySelectorAll(".accessibility-indicator")].forEach(
indicator => ["click", "keypress"].forEach(type =>
indicator.removeEventListener(type, this)));
TabsInTitlebar.update();
}
},

View File

@ -144,8 +144,10 @@ var Policies = {
let platform = AppConstants.platform;
if (platform == "win") {
dirs = [
// Ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
// Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
// Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
];
} else if (platform == "macosx" || platform == "linux") {
dirs = [

View File

@ -2,12 +2,6 @@
const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
function waitForTransition(element, propertyName) {
return BrowserTestUtils.waitForEvent(element, "transitionend", false, event => {
return event.target == element && event.propertyName == propertyName;
});
}
add_task(async function test_management_install() {
await SpecialPowers.pushPrefEnv({set: [
["xpinstall.signatures.required", false],
@ -57,14 +51,12 @@ add_task(async function test_management_install() {
await extension.awaitMessage("ready");
// Test installing a static WE theme.
let transitionDone = waitForTransition(document.documentElement, "background-color");
clickBrowserAction(extension);
let {id, type} = await extension.awaitMessage("installed");
is(id, "tiger@persona.beard", "Static web extension theme installed");
is(type, "theme", "Extension type is correct");
await transitionDone;
let style = window.getComputedStyle(document.documentElement);
is(style.backgroundColor, "rgb(255, 165, 0)", "Background is the new black");

View File

@ -252,6 +252,14 @@ class Query {
return;
}
// Filter out javascript results for safety. The provider is supposed to do
// it, but we don't want to risk leaking these out.
if (match.url.startsWith("javascript:") &&
!this.context.searchString.startsWith("javascript:") &&
UrlbarPrefs.get("filter.javascript")) {
return;
}
this.context.results.push(match);
let notifyResults = () => {

View File

@ -3,7 +3,7 @@
"use strict";
add_task(async function test_providers() {
add_task(async function test_filtering() {
let match = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH,
UrlbarUtils.MATCH_SOURCE.TABS,
{ url: "http://mozilla.org/foo/" });
@ -57,3 +57,43 @@ add_task(async function test_providers() {
await promise;
Assert.deepEqual(context.results, [match]);
});
add_task(async function test_filter_javascript() {
let context = createContext();
let controller = new UrlbarController({
browserWindow: {
location: {
href: AppConstants.BROWSER_CHROME_URL,
},
},
});
let match = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH,
UrlbarUtils.MATCH_SOURCE.TABS,
{ url: "http://mozilla.org/foo/" });
let jsMatch = new UrlbarMatch(UrlbarUtils.MATCH_TYPE.TAB_SWITCH,
UrlbarUtils.MATCH_SOURCE.HISTORY,
{ url: "javascript:foo" });
registerBasicTestProvider([match, jsMatch]);
info("By default javascript should be filtered out");
let promise = promiseControllerNotification(controller, "onQueryResults");
await controller.startQuery(context, controller);
await promise;
Assert.deepEqual(context.results, [match]);
info("Except when the user explicitly starts the search with javascript:");
context = createContext(`javascript: ${UrlbarTokenizer.RESTRICT.HISTORY}`);
promise = promiseControllerNotification(controller, "onQueryResults");
await controller.startQuery(context, controller);
await promise;
Assert.deepEqual(context.results, [jsMatch]);
info("Disable javascript filtering");
Services.prefs.setBoolPref("browser.urlbar.filter.javascript", false);
context = createContext();
promise = promiseControllerNotification(controller, "onQueryResults");
await controller.startQuery(context, controller);
await promise;
Assert.deepEqual(context.results, [match, jsMatch]);
Services.prefs.clearUserPref("browser.urlbar.filter.javascript");
});

View File

@ -3,5 +3,8 @@ MOZ_AUTOMATION_PACKAGE_TESTS=0
MOZ_AUTOMATION_L10N_CHECK=0
ac_add_options --enable-rust-tests
mk_add_options MOZ_MAKE_FLAGS=--output-sync=line
. "$topsrcdir/browser/config/mozconfigs/linux32/nightly"
unset ENABLE_CLANG_PLUGIN

View File

@ -3,5 +3,8 @@ MOZ_AUTOMATION_PACKAGE_TESTS=0
MOZ_AUTOMATION_L10N_CHECK=0
ac_add_options --enable-rust-tests
mk_add_options MOZ_MAKE_FLAGS=--output-sync=line
. "$topsrcdir/browser/config/mozconfigs/linux32/debug"
unset ENABLE_CLANG_PLUGIN

View File

@ -3,5 +3,8 @@ MOZ_AUTOMATION_PACKAGE_TESTS=0
MOZ_AUTOMATION_L10N_CHECK=0
ac_add_options --enable-rust-tests
mk_add_options MOZ_MAKE_FLAGS=--output-sync=line
. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
unset ENABLE_CLANG_PLUGIN

View File

@ -3,5 +3,8 @@ MOZ_AUTOMATION_PACKAGE_TESTS=0
MOZ_AUTOMATION_L10N_CHECK=0
ac_add_options --enable-rust-tests
mk_add_options MOZ_MAKE_FLAGS=--output-sync=line
. "$topsrcdir/browser/config/mozconfigs/linux64/debug"
unset ENABLE_CLANG_PLUGIN

View File

@ -240,8 +240,6 @@
@RESPATH@/components/ContentProcessSingleton.js
@RESPATH@/components/nsURLFormatter.manifest
@RESPATH@/components/nsURLFormatter.js
@RESPATH@/components/txEXSLTRegExFunctions.manifest
@RESPATH@/components/txEXSLTRegExFunctions.js
@RESPATH@/components/toolkitplaces.manifest
@RESPATH@/components/nsLivemarkService.js
@RESPATH@/components/nsTaggingService.js

View File

@ -19,10 +19,6 @@
--space-above-tabbar: 8px;
}
:root[sessionrestored]:-moz-lwtheme {
transition: @themeTransition@;
}
/* Increase contrast of UI links on dark themes */
:root[lwt-popup-brighttext] panel .text-link {

View File

@ -136,8 +136,12 @@ size_t dummy;
void end_test() {
static size_t count = 0;
/* Only exit when both constructors have been called */
if (++count == 2)
if (++count == 2) {
ret = 0;
// Avoid the dummy variable being stripped out at link time because
// it's unused.
dummy = 1;
}
}
void test() {

View File

@ -93,6 +93,13 @@ async function getRuntimeInfo(runtime, client) {
};
}
function onUSBDebuggerClientClosed() {
// After scanUSBRuntimes action, updateUSBRuntimes action is called.
// The closed runtime will be unwatched and disconnected explicitly in the action
// if needed.
window.AboutDebugging.store.dispatch(Actions.scanUSBRuntimes());
}
function connectRuntime(id) {
return async (dispatch, getState) => {
dispatch({ type: CONNECT_RUNTIME_START });
@ -105,6 +112,12 @@ function connectRuntime(id) {
await preferenceFront.getBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT);
const runtimeDetails = { connectionPromptEnabled, client, info, transportDetails };
if (runtime.type === RUNTIMES.USB) {
// `closed` event will be emitted when disabling remote debugging
// on the connected USB runtime.
client.addOneTimeListener("closed", onUSBDebuggerClientClosed);
}
dispatch({
type: CONNECT_RUNTIME_SUCCESS,
runtime: {
@ -126,8 +139,15 @@ function disconnectRuntime(id) {
const runtime = findRuntimeById(id, getState().runtimes);
const client = runtime.runtimeDetails.client;
if (runtime.type === RUNTIMES.USB) {
client.removeListener("closed", onUSBDebuggerClientClosed);
}
await client.close();
DebuggerServer.destroy();
if (runtime.type === RUNTIMES.THIS_FIREFOX) {
DebuggerServer.destroy();
}
dispatch({
type: DISCONNECT_RUNTIME_SUCCESS,
@ -232,6 +252,13 @@ function updateUSBRuntimes(runtimes) {
await dispatch(Actions.selectPage(RUNTIMES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX));
}
// Disconnect runtimes that were no longer valid
const invalidRuntimes =
getState().runtimes.usbRuntimes.filter(r => !runtimes.includes(r));
for (const invalidRuntime of invalidRuntimes) {
await dispatch(disconnectRuntime(invalidRuntime.id));
}
dispatch({ type: USB_RUNTIMES_UPDATED, runtimes });
};
}

View File

@ -54,8 +54,8 @@ class FlexItemSizingOutline extends PureComponent {
);
}
renderPoint(name) {
return dom.div({ className: `flex-outline-point ${name}`, "data-label": name });
renderPoint(className, label = className) {
return dom.div({ className: `flex-outline-point ${className}`, "data-label": label });
}
render() {
@ -138,6 +138,16 @@ class FlexItemSizingOutline extends PureComponent {
}
gridTemplateColumns += "]";
// Check the final and basis points to see if they are the same and if so, combine
// them into a single rendered point.
const renderedBaseAndFinalPoints = [];
if (mainFinalSize === mainBaseSize) {
renderedBaseAndFinalPoints.push(this.renderPoint("basisfinal", "basis/final"));
} else {
renderedBaseAndFinalPoints.push(this.renderPoint("basis"));
renderedBaseAndFinalPoints.push(this.renderPoint("final"));
}
return (
dom.div({ className: "flex-outline-container" },
dom.div(
@ -150,8 +160,7 @@ class FlexItemSizingOutline extends PureComponent {
gridTemplateColumns,
},
},
this.renderPoint("basis"),
this.renderPoint("final"),
renderedBaseAndFinalPoints,
showMin ? this.renderPoint("min") : null,
showMax ? this.renderPoint("max") : null,
this.renderBasisOutline(mainBaseSize),

View File

@ -22,6 +22,7 @@ support-files =
[browser_flexbox_item_outline_exists.js]
[browser_flexbox_item_outline_has_correct_layout.js]
[browser_flexbox_item_outline_hidden_when_useless.js]
[browser_flexbox_item_outline_renders_basisfinal_points_correctly.js]
[browser_flexbox_item_outline_rotates_for_column.js]
[browser_flexbox_pseudo_elements_are_listed.js]
[browser_flexbox_sizing_flexibility_not_displayed_when_useless.js]

View File

@ -25,10 +25,4 @@ add_task(async function() {
ok(basis, "The basis outline exists");
ok(final, "The final outline exists");
const [basisPoint, finalPoint] = [...flexOutlineContainer.querySelectorAll(
".flex-outline-point.basis, .flex-outline-point.final")];
ok(basisPoint, "The basis point exists");
ok(finalPoint, "The final point exists");
});

View File

@ -0,0 +1,37 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the flex item outline renders the basis and final points as a single point
// if their sizes are equal. If not, then render as separate points.
const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
add_task(async function() {
await addTab(TEST_URI);
const { inspector, flexboxInspector } = await openLayoutView();
const { document: doc } = flexboxInspector;
info("Select a flex item whose basis size matches its final size.");
let onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
await selectNode(".item", inspector);
let [flexOutlineContainer] = await onFlexItemOutlineRendered;
const [basisFinalPoint] = [...flexOutlineContainer.querySelectorAll(
".flex-outline-point.basisfinal")];
ok(basisFinalPoint, "The basis/final point exists");
info("Select a flex item whose basis size is different than its final size.");
onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
await selectNode(".shrinking .item", inspector);
[flexOutlineContainer] = await onFlexItemOutlineRendered;
const [basis, final] = [...flexOutlineContainer.querySelectorAll(
".flex-outline-point.basis, .flex-outline-point.final")];
ok(basis, "The basis point exists");
ok(final, "The final point exists");
});

View File

@ -118,12 +118,6 @@
<!-- Runtime Details -->
<!ENTITY runtimedetails_title "Runtime Info">
<!ENTITY runtimedetails_adbIsRoot "ADB is root: ">
<!ENTITY runtimedetails_summonADBRoot "root device">
<!ENTITY runtimedetails_ADBRootWarning "(requires unlocked bootloader)">
<!ENTITY runtimedetails_unrestrictedPrivileges "Unrestricted DevTools privileges: ">
<!ENTITY runtimedetails_requestPrivileges "request higher privileges">
<!ENTITY runtimedetails_privilegesWarning "(Will reboot device. Requires root access.)">
<!-- Device Preferences and Settings -->
<!ENTITY device_typeboolean "Boolean">

View File

@ -304,7 +304,8 @@
grid-row: 1;
}
.flex-outline-point.basis {
.flex-outline-point.basis,
.flex-outline-point.basisfinal {
grid-column-end: basis-end;
justify-self: end;
}
@ -357,7 +358,8 @@
}
.flex-outline-point.basis::before,
.flex-outline-point.final::before {
.flex-outline-point.final::before,
.flex-outline-point.basisfinal::before {
top: -12px;
}
@ -366,7 +368,7 @@
bottom: -12px;
}
.flex-outline.column .flex-outline-point.min::before,
.flex-outline.column .flex-outline-point.max::before,
.flex-outline.column .flex-outline-point.min::before {
text-indent: -12px;
}
@ -374,13 +376,15 @@
.flex-outline-point.final::before,
.flex-outline.shrinking .flex-outline-point.min::before,
.flex-outline-point.max::before,
.flex-outline.shrinking .flex-outline-point.basis::before {
.flex-outline.shrinking .flex-outline-point.basis::before,
.flex-outline.column .flex-outline-point.basisfinal::before {
border-width: 0 0 0 1px;
}
.flex-outline-point.basis::before,
.flex-outline-point.min::before,
.flex-outline.shrinking .flex-outline-point.final::before {
.flex-outline.shrinking .flex-outline-point.final::before,
.flex-outline.row .flex-outline-point.basisfinal::before {
border-width: 0 1px 0 0;
}

View File

@ -3,24 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const {Connection} = require("devtools/shared/client/connection-manager");
const {RuntimeTypes} = require("devtools/client/webide/modules/runtime-types");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29";
window.addEventListener("load", function() {
document.querySelector("#close").onclick = CloseUI;
document.querySelector("#devtools-check button").onclick = EnableCertApps;
document.querySelector("#adb-check button").onclick = RootADB;
document.querySelector("#unrestricted-privileges").onclick = function() {
window.parent.UI.openInBrowser(UNRESTRICTED_HELP_URL);
};
AppManager.on("app-manager-update", OnAppManagerUpdate);
BuildUI();
CheckLockState();
}, {capture: true, once: true});
window.addEventListener("unload", function() {
@ -34,7 +23,6 @@ function CloseUI() {
function OnAppManagerUpdate(what) {
if (what == "connection" || what == "runtime-global-actors") {
BuildUI();
CheckLockState();
}
}
@ -67,79 +55,3 @@ function BuildUI() {
CloseUI();
}
}
function CheckLockState() {
const adbCheckResult = document.querySelector("#adb-check > .yesno");
const devtoolsCheckResult = document.querySelector("#devtools-check > .yesno");
const flipCertPerfButton = document.querySelector("#devtools-check button");
const flipCertPerfAction = document.querySelector("#devtools-check > .action");
const adbRootAction = document.querySelector("#adb-check > .action");
const sYes = Strings.GetStringFromName("runtimedetails_checkyes");
const sNo = Strings.GetStringFromName("runtimedetails_checkno");
const sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice");
flipCertPerfButton.setAttribute("disabled", "true");
flipCertPerfAction.setAttribute("hidden", "true");
adbRootAction.setAttribute("hidden", "true");
adbCheckResult.textContent = "";
devtoolsCheckResult.textContent = "";
if (AppManager.connection &&
AppManager.connection.status == Connection.Status.CONNECTED) {
// ADB check
if (AppManager.selectedRuntime.type === RuntimeTypes.USB) {
const device = AppManager.selectedRuntime.device;
if (device && device.summonRoot) {
device.isRoot().then(isRoot => {
if (isRoot) {
adbCheckResult.textContent = sYes;
flipCertPerfButton.removeAttribute("disabled");
} else {
adbCheckResult.textContent = sNo;
adbRootAction.removeAttribute("hidden");
}
}, console.error);
}
} else {
adbCheckResult.textContent = sNotUSB;
}
// forbid-certified-apps check
try {
const prefFront = AppManager.preferenceFront;
prefFront.getBoolPref("devtools.debugger.forbid-certified-apps").then(isForbidden => {
if (isForbidden) {
devtoolsCheckResult.textContent = sNo;
flipCertPerfAction.removeAttribute("hidden");
} else {
devtoolsCheckResult.textContent = sYes;
}
}, console.error);
} catch (e) {
// Exception. pref actor is only accessible if forbird-certified-apps is false
devtoolsCheckResult.textContent = sNo;
flipCertPerfAction.removeAttribute("hidden");
}
}
}
function EnableCertApps() {
const device = AppManager.selectedRuntime.device;
// TODO: Remove `network.disable.ipc.security` once bug 1125916 is fixed.
device.shell(
"stop b2g && " +
"cd /data/b2g/mozilla/*.default/ && " +
"echo 'user_pref(\"devtools.debugger.forbid-certified-apps\", false);' >> prefs.js && " +
"echo 'user_pref(\"dom.apps.developer_mode\", true);' >> prefs.js && " +
"echo 'user_pref(\"network.disable.ipc.security\", true);' >> prefs.js && " +
"echo 'user_pref(\"dom.webcomponents.shadowdom.enabled\", true);' >> prefs.js && " +
"start b2g"
);
}
function RootADB() {
const device = AppManager.selectedRuntime.device;
device.summonRoot().then(CheckLockState, console.error);
}

View File

@ -24,23 +24,6 @@
<h1>&runtimedetails_title;</h1>
<div id="devicePrivileges">
<p id="adb-check">
&runtimedetails_adbIsRoot;<span class="yesno"></span>
<div class="action">
<button>&runtimedetails_summonADBRoot;</button>
<em>&runtimedetails_ADBRootWarning;</em>
</div>
</p>
<p id="devtools-check">
<a id="unrestricted-privileges">&runtimedetails_unrestrictedPrivileges;</a><span class="yesno"></span>
<div class="action">
<button>&runtimedetails_requestPrivileges;</button>
<em>&runtimedetails_privilegesWarning;</em>
</div>
</p>
</div>
<table></table>
</body>
</html>

View File

@ -5,21 +5,3 @@
html, body {
background: white;
}
#devicePrivileges {
font-family: monospace;
padding-left: 6px;
}
#devtools-check > a {
color: #4C9ED9;
cursor: pointer;
}
.action {
display: inline;
}
.action[hidden] {
display: none;
}

View File

@ -102,7 +102,6 @@ function runTests() {
window.onload = function() {
SpecialPowers.pushPrefEnv({
"set": [
["devtools.debugger.forbid-certified-apps", false],
["test.all.bool", true],
["test.all.int", 0x4321],
["test.all.string", "allizom"],

View File

@ -10,11 +10,8 @@
var gClient;
var gRegistryFront;
var gActorFront;
var gOldPref;
function run_test() {
gOldPref = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", false);
initTestDebuggerServer();
DebuggerServer.registerAllActors();
gClient = new DebuggerClient(DebuggerServer.connectPipe());
@ -71,7 +68,6 @@ function testActorIsUnregistered() {
gClient.listTabs().then(({ helloActor }) => {
Assert.ok(!helloActor);
Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", gOldPref);
finishClient(gClient);
});
}

View File

@ -176,13 +176,18 @@ exports.defineLazyPrototypeGetter = function(object, key, callback) {
* Safely get the property value from a Debugger.Object for a given key. Walks
* the prototype chain until the property is found.
*
* @param Debugger.Object object
* @param {Debugger.Object} object
* The Debugger.Object to get the value from.
* @param String key
* @param {String} key
* The key to look for.
* @param {Boolean} invokeUnsafeGetter (defaults to false).
* Optional boolean to indicate if the function should execute unsafe getter
* in order to retrieve its result's properties.
* This should be set to true *ONLY* on user action as it may cause side-effects
* in the content page
* @return Any
*/
exports.getProperty = function(object, key) {
exports.getProperty = function(object, key, invokeUnsafeGetters = false) {
const root = object;
while (object && exports.isSafeDebuggerObject(object)) {
let desc;
@ -198,7 +203,7 @@ exports.getProperty = function(object, key) {
return desc.value;
}
// Call the getter if it's safe.
if (exports.hasSafeGetter(desc)) {
if (exports.hasSafeGetter(desc) || invokeUnsafeGetters === true) {
try {
return desc.get.call(root).return;
} catch (e) {
@ -305,6 +310,40 @@ exports.hasSafeGetter = function(desc) {
return fn && fn.callable && fn.class == "Function" && fn.script === undefined;
};
/**
* Check that the property value from a Debugger.Object for a given key is an unsafe
* getter or not. Walks the prototype chain until the property is found.
*
* @param {Debugger.Object} object
* The Debugger.Object to check on.
* @param {String} key
* The key to look for.
* @param {Boolean} invokeUnsafeGetter (defaults to false).
* Optional boolean to indicate if the function should execute unsafe getter
* in order to retrieve its result's properties.
* @return Boolean
*/
exports.isUnsafeGetter = function(object, key) {
while (object && exports.isSafeDebuggerObject(object)) {
let desc;
try {
desc = object.getOwnPropertyDescriptor(key);
} catch (e) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
return false;
}
if (desc) {
if (Object.getOwnPropertyNames(desc).includes("get")) {
return !exports.hasSafeGetter(desc);
}
}
object = object.proto;
}
return false;
};
/**
* Check if it is safe to read properties and execute methods from the given JS
* object. Safety is defined as being protected from unintended code execution

View File

@ -7580,6 +7580,7 @@ exports.CSS_PROPERTIES = {
],
"supports": [],
"values": [
"anywhere",
"break-word",
"inherit",
"initial",
@ -9206,6 +9207,7 @@ exports.CSS_PROPERTIES = {
],
"supports": [],
"values": [
"anywhere",
"break-word",
"inherit",
"initial",

View File

@ -37,8 +37,6 @@ pref("devtools.debugger.remote-port", 6000);
pref("devtools.debugger.remote-websocket", false);
// Force debugger server binding on the loopback interface
pref("devtools.debugger.force-local", true);
// Block tools from seeing / interacting with certified apps
pref("devtools.debugger.forbid-certified-apps", true);
// Limit for intercepted response bodies (1 MB)
// Possible values:

View File

@ -194,23 +194,38 @@ function analyzeInputString(str) {
* Provides a list of properties, that are possible matches based on the passed
* Debugger.Environment/Debugger.Object and inputValue.
*
* @param object dbgObject
* @param {Object} dbgObject
* When the debugger is not paused this Debugger.Object wraps
* the scope for autocompletion.
* It is null if the debugger is paused.
* @param object anEnvironment
* @param {Object} anEnvironment
* When the debugger is paused this Debugger.Environment is the
* scope for autocompletion.
* It is null if the debugger is not paused.
* @param string inputValue
* @param {String} inputValue
* Value that should be completed.
* @param number [cursor=inputValue.length]
* @param {Number} cursor (defaults to inputValue.length).
* Optional offset in the input where the cursor is located. If this is
* omitted then the cursor is assumed to be at the end of the input
* value.
* @param {Boolean} invokeUnsafeGetter (defaults to false).
* Optional boolean to indicate if the function should execute unsafe getter
* in order to retrieve its result's properties.
* This should be set to true *ONLY* on user action as it may cause side-effects
* in the content page
* @returns null or object
* If no completion valued could be computed, null is returned,
* otherwise a object with the following form is returned:
* If the inputValue is an unsafe getter and invokeUnsafeGetter is false, the
* following form is returned:
*
* {
* isUnsafeGetter: true,
* getterName: {String} The name of the unsafe getter
* }
*
* If no completion valued could be computed, and the input is not an unsafe
* getter, null is returned.
*
* Otherwise an object with the following form is returned:
* {
* matches: Set<string>
* matchProp: Last part of the inputValue that was used to find
@ -219,7 +234,13 @@ function analyzeInputString(str) {
* access (e.g. `window["addEvent`).
* }
*/
function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
function JSPropertyProvider(
dbgObject,
anEnvironment,
inputValue,
cursor,
invokeUnsafeGetter = false
) {
if (cursor === undefined) {
cursor = inputValue.length;
}
@ -367,18 +388,33 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
// We get the rest of the properties recursively starting from the
// Debugger.Object that wraps the first property
for (let prop of properties) {
for (let [index, prop] of properties.entries()) {
prop = prop.trim();
if (!prop) {
return null;
}
if (!invokeUnsafeGetter && DevToolsUtils.isUnsafeGetter(obj, prop)) {
// If the unsafe getter is not the last property access of the input, bail out as
// things might get complex.
if (index !== properties.length - 1) {
return null;
}
// If we try to access an unsafe getter, return its name so we can consume that
// on the frontend.
return {
isUnsafeGetter: true,
getterName: prop,
};
}
if (hasArrayIndex(prop)) {
// The property to autocomplete is a member of array. For example
// list[i][j]..[n]. Traverse the array to get the actual element.
obj = getArrayMemberProperty(obj, null, prop);
} else {
obj = DevToolsUtils.getProperty(obj, prop);
obj = DevToolsUtils.getProperty(obj, prop, invokeUnsafeGetter);
}
if (!isObjectUsable(obj)) {

View File

@ -45,6 +45,24 @@ function run_test() {
}
})();`;
const testGetters = `
var testGetters = {
get x() {
return Object.create(null, Object.getOwnPropertyDescriptors({
hello: "",
world: "",
}));
},
get y() {
return Object.create(null, Object.getOwnPropertyDescriptors({
get y() {
return "plop";
},
}));
}
};
`;
const sandbox = Cu.Sandbox("http://example.com");
const dbg = new Debugger();
const dbgObject = dbg.addDebuggee(sandbox);
@ -54,6 +72,7 @@ function run_test() {
Cu.evalInSandbox(testHyphenated, sandbox);
Cu.evalInSandbox(testLet, sandbox);
Cu.evalInSandbox(testGenerators, sandbox);
Cu.evalInSandbox(testGetters, sandbox);
info("Running tests with dbgObject");
runChecks(dbgObject, null, sandbox);
@ -63,124 +82,126 @@ function run_test() {
}
function runChecks(dbgObject, dbgEnv, sandbox) {
const propertyProvider = (...args) => JSPropertyProvider(dbgObject, dbgEnv, ...args);
info("Test that suggestions are given for 'this'");
let results = JSPropertyProvider(dbgObject, dbgEnv, "t");
let results = propertyProvider("t");
test_has_result(results, "this");
if (dbgObject != null) {
info("Test that suggestions are given for 'this.'");
results = JSPropertyProvider(dbgObject, dbgEnv, "this.");
results = propertyProvider("this.");
test_has_result(results, "testObject");
info("Test that no suggestions are given for 'this.this'");
results = JSPropertyProvider(dbgObject, dbgEnv, "this.this");
results = propertyProvider("this.this");
test_has_no_results(results);
}
info("Testing lexical scope issues (Bug 1207868)");
results = JSPropertyProvider(dbgObject, dbgEnv, "foobar");
results = propertyProvider("foobar");
test_has_result(results, "foobar");
results = JSPropertyProvider(dbgObject, dbgEnv, "foobar.");
results = propertyProvider("foobar.");
test_has_result(results, "a");
results = JSPropertyProvider(dbgObject, dbgEnv, "blargh");
results = propertyProvider("blargh");
test_has_result(results, "blargh");
results = JSPropertyProvider(dbgObject, dbgEnv, "blargh.");
results = propertyProvider("blargh.");
test_has_result(results, "a");
info("Test that suggestions are given for 'foo[n]' where n is an integer.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0].");
results = propertyProvider("testArray[0].");
test_has_result(results, "propA");
info("Test that suggestions are given for multidimensional arrays.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[2][0].");
results = propertyProvider("testArray[2][0].");
test_has_result(results, "propE");
info("Test that suggestions are given for nested arrays.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[1].propC[0].");
results = propertyProvider("testArray[1].propC[0].");
test_has_result(results, "indexOf");
info("Test that suggestions are given for literal arrays.");
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3].");
results = propertyProvider("[1,2,3].");
test_has_result(results, "indexOf");
info("Test that suggestions are given for literal arrays with newlines.");
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3,\n4\n].");
results = propertyProvider("[1,2,3,\n4\n].");
test_has_result(results, "indexOf");
info("Test that suggestions are given for literal strings.");
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'.");
results = propertyProvider("'foo'.");
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, '"foo".');
results = propertyProvider('"foo".');
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo`.");
results = propertyProvider("`foo`.");
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo doc`.");
results = propertyProvider("`foo doc`.");
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo \" doc`.");
results = propertyProvider("`foo \" doc`.");
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo \' doc`.");
results = propertyProvider("`foo \' doc`.");
test_has_result(results, "charAt");
results = JSPropertyProvider(dbgObject, dbgEnv, "'[1,2,3]'.");
results = propertyProvider("'[1,2,3]'.");
test_has_result(results, "charAt");
info("Test that suggestions are not given for syntax errors.");
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo\"");
results = propertyProvider("'foo\"");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo d");
results = propertyProvider("'foo d");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, `"foo d`);
results = propertyProvider(`"foo d`);
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo d");
results = propertyProvider("`foo d");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,',2]");
results = propertyProvider("[1,',2]");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "'[1,2].");
results = propertyProvider("'[1,2].");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'..");
results = propertyProvider("'foo'..");
Assert.equal(null, results);
info("Test that suggestions are not given without a dot.");
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'");
results = propertyProvider("'foo'");
test_has_no_results(results);
results = JSPropertyProvider(dbgObject, dbgEnv, "`foo`");
results = propertyProvider("`foo`");
test_has_no_results(results);
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3]");
results = propertyProvider("[1,2,3]");
test_has_no_results(results);
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3].\n'foo'");
results = propertyProvider("[1,2,3].\n'foo'");
test_has_no_results(results);
info("Test that suggestions are not given for numeric literals.");
results = JSPropertyProvider(dbgObject, dbgEnv, "1.");
results = propertyProvider("1.");
Assert.equal(null, results);
info("Test that suggestions are not given for index that's out of bounds.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[10].");
results = propertyProvider("testArray[10].");
Assert.equal(null, results);
info("Test that no suggestions are given if an index is not numerical "
+ "somewhere in the chain.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0]['propC'][0].");
results = propertyProvider("testArray[0]['propC'][0].");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "testObject['propA'][0].");
results = propertyProvider("testObject['propA'][0].");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[0]['propC'].");
results = propertyProvider("testArray[0]['propC'].");
Assert.equal(null, results);
results = JSPropertyProvider(dbgObject, dbgEnv, "testArray[][1].");
results = propertyProvider("testArray[][1].");
Assert.equal(null, results);
info("Test that suggestions are not given if there is an hyphen in the chain.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testHyphenated['prop-A'].");
results = propertyProvider("testHyphenated['prop-A'].");
Assert.equal(null, results);
info("Test that we have suggestions for generators.");
const gen1Result = Cu.evalInSandbox("gen1.next().value", sandbox);
results = JSPropertyProvider(dbgObject, dbgEnv, "gen1.");
results = propertyProvider("gen1.");
test_has_result(results, "next");
info("Test that the generator next() was not executed");
const gen1NextResult = Cu.evalInSandbox("gen1.next().value", sandbox);
@ -188,10 +209,53 @@ function runChecks(dbgObject, dbgEnv, sandbox) {
info("Test with an anonymous generator.");
const gen2Result = Cu.evalInSandbox("gen2.next().value", sandbox);
results = JSPropertyProvider(dbgObject, dbgEnv, "gen2.");
results = propertyProvider("gen2.");
test_has_result(results, "next");
const gen2NextResult = Cu.evalInSandbox("gen2.next().value", sandbox);
Assert.equal(gen2Result + 1, gen2NextResult);
info("Test that getters are not executed if invokeUnsafeGetter is undefined");
results = propertyProvider("testGetters.x.");
Assert.deepEqual(results, {isUnsafeGetter: true, getterName: "x"});
results = propertyProvider("testGetters.x[");
Assert.deepEqual(results, {isUnsafeGetter: true, getterName: "x"});
results = propertyProvider("testGetters.x.hell");
Assert.deepEqual(results, {isUnsafeGetter: true, getterName: "x"});
results = propertyProvider("testGetters.x['hell");
Assert.deepEqual(results, {isUnsafeGetter: true, getterName: "x"});
info("Test that deep getter property access does not return intermediate getters");
results = propertyProvider("testGetters.y.y.");
Assert.ok(results === null);
results = propertyProvider("testGetters['y'].y.");
Assert.ok(results === null);
results = propertyProvider("testGetters['y']['y'].");
Assert.ok(results === null);
results = propertyProvider("testGetters.y['y'].");
Assert.ok(results === null);
info("Test that getters are executed if invokeUnsafeGetter is true");
results = propertyProvider("testGetters.x.", undefined, true);
test_has_exact_results(results, ["hello", "world"]);
Assert.ok(Object.keys(results).includes("isUnsafeGetter") === false);
Assert.ok(Object.keys(results).includes("getterName") === false);
info("Test that executing getters filters with provided string");
results = propertyProvider("testGetters.x.hell", undefined, true);
test_has_exact_results(results, ["hello"]);
results = propertyProvider("testGetters.x['hell", undefined, true);
test_has_exact_results(results, ["'hello'"]);
info("Test that children getters are executed if invokeUnsafeGetter is true");
results = propertyProvider("testGetters.y.y.", undefined, true);
test_has_result(results, "trim");
}
/**
@ -215,3 +279,14 @@ function test_has_result(results, requiredSuggestion) {
Assert.ok(results.matches.size > 0);
Assert.ok(results.matches.has(requiredSuggestion));
}
/**
* A helper that ensures results are the expected ones.
* @param Object results
* The results returned by JSPropertyProvider.
* @param Array expectedMatches
* An array of the properties that should be returned by JsPropertyProvider.
*/
function test_has_exact_results(results, expectedMatches) {
Assert.deepEqual([...results.matches], expectedMatches);
}

View File

@ -311,9 +311,10 @@ DecreasePrivateDocShellCount()
}
}
nsDocShell::nsDocShell()
nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext)
: nsDocLoader()
, mContentWindowID(NextWindowID())
, mBrowsingContext(aBrowsingContext)
, mForcedCharset(nullptr)
, mParentCharset(nullptr)
, mTreeOwner(nullptr)
@ -324,7 +325,7 @@ nsDocShell::nsDocShell()
, mParentCharsetSource(0)
, mMarginWidth(-1)
, mMarginHeight(-1)
, mItemType(typeContent)
, mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome)
, mPreviousEntryIndex(-1)
, mLoadedEntryIndex(-1)
, mChildOffset(0)
@ -361,7 +362,7 @@ nsDocShell::nsDocShell()
, mUseStrictSecurityChecks(false)
, mObserveErrorPages(true)
, mCSSErrorReportingEnabled(false)
, mAllowAuth(true)
, mAllowAuth(mItemType == typeContent)
, mAllowKeywordFixup(false)
, mIsOffScreenBrowser(false)
, mIsActive(true)
@ -460,8 +461,7 @@ nsDocShell::Create(BrowsingContext* aBrowsingContext)
MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
nsresult rv;
RefPtr<nsDocShell> ds = new nsDocShell();
ds->mBrowsingContext = aBrowsingContext;
RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext);
// Initialize the underlying nsDocLoader.
rv = ds->nsDocLoader::Init();
@ -506,9 +506,6 @@ nsDocShell::Create(BrowsingContext* aBrowsingContext)
return nullptr;
}
// Set our DocShellTreeItem type based on our BrowsingContext
ds->SetItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome);
// If our parent is present in this process, set up our parent now.
RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
if (parent && parent->GetDocShell()) {
@ -2597,34 +2594,8 @@ nsDocShell::GetItemType(int32_t* aItemType)
{
NS_ENSURE_ARG_POINTER(aItemType);
*aItemType = ItemType();
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetItemType(int32_t aItemType)
{
NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
// Only allow setting the type on root docshells. Those would be the ones
// that have the docloader service as mParent or have no mParent at all.
nsCOMPtr<nsIDocumentLoader> docLoaderService =
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
mItemType = aItemType;
// disable auth prompting for anything but content
mAllowAuth = mItemType == typeContent;
RefPtr<nsPresContext> presContext = nullptr;
GetPresContext(getter_AddRefs(presContext));
if (presContext) {
presContext->UpdateIsChrome();
}
MOZ_DIAGNOSTIC_ASSERT((mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
*aItemType = mItemType;
return NS_OK;
}

View File

@ -409,7 +409,7 @@ private: // member functions
friend void mozilla::TimelineConsumers::PopMarkers(nsDocShell*,
JSContext*, nsTArray<dom::ProfileTimelineMarker>&);
nsDocShell();
explicit nsDocShell(mozilla::dom::BrowsingContext* aBrowsingContext);
// Security checks to prevent frameset spoofing. See comments at
// implementation sites.
@ -1043,9 +1043,8 @@ private: // data members
int32_t mMarginWidth;
int32_t mMarginHeight;
// This can either be a content docshell or a chrome docshell. After
// Create() is called, the type is not expected to change.
int32_t mItemType;
// This can either be a content docshell or a chrome docshell.
const int32_t mItemType;
// Index into the nsISHEntry array, indicating the previous and current
// entry at the time that this DocShell begins to load. Consequently

View File

@ -47,7 +47,7 @@ interface nsIDocShellTreeItem : nsISupports
/*
The type this item is.
*/
attribute long itemType;
readonly attribute long itemType;
[noscript,notxpcom,nostdcall] long ItemType();
/*

View File

@ -1329,25 +1329,24 @@ Element::AttachShadowWithoutNameChecks(ShadowRootMode aMode)
void
Element::UnattachShadow()
{
RefPtr<ShadowRoot> shadowRoot = GetShadowRoot();
ShadowRoot* shadowRoot = GetShadowRoot();
if (!shadowRoot) {
return;
}
nsAutoScriptBlocker scriptBlocker;
nsIDocument* doc = GetComposedDoc();
if (doc) {
if (nsIDocument* doc = GetComposedDoc()) {
if (nsIPresShell* shell = doc->GetShell()) {
shell->DestroyFramesForAndRestyle(this);
}
}
MOZ_ASSERT(!GetPrimaryFrame());
// Simply unhook the shadow root from the element.
MOZ_ASSERT(!shadowRoot->HasSlots(), "Won't work when shadow root has slots!");
shadowRoot->Unbind();
shadowRoot->Unattach();
SetShadowRoot(nullptr);
// Beware shadowRoot could be dead after this call.
}
void

View File

@ -178,6 +178,16 @@ ShadowRoot::Unbind()
}
}
void
ShadowRoot::Unattach()
{
MOZ_ASSERT(!HasSlots(), "Won't work!");
MOZ_ASSERT(GetHost());
Unbind();
GetHost()->RemoveMutationObserver(this);
SetHost(nullptr);
}
void
ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement)
{

View File

@ -98,6 +98,10 @@ public:
// being connected.
void Unbind();
// Only intended for UA widgets / special shadow roots.
// Forgets our shadow host and unbinds all our kids.
void Unattach();
// Calls BindToTree on each of our kids, and also maybe flags us as being
// connected.
nsresult Bind();

View File

@ -793,6 +793,7 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
for (RefPtr<Timeout> timeout = mTimeouts.GetFirst();
timeout != nullptr;
timeout = next) {
next = timeout->getNext();
// We should only execute callbacks for the set of expired Timeout
// objects we computed above.
if (timeout->mFiringId != firingId) {

View File

@ -0,0 +1,10 @@
<script>
function go() {
var r = document.getSelection().getRangeAt(0).cloneRange();
a.type = "";
r.insertNode(b);
}
</script>
<body onload=go()>
<input id="a" autofocus type="date">
<summary id="b">x</summary>

View File

@ -245,3 +245,4 @@ load 1445670.html
load 1458016.html
pref(dom.webcomponents.shadowdom.enabled,true) load 1459688.html
load 1460794.html
pref(dom.webcomponents.shadowdom.enabled,true) load 1505875.html

View File

@ -548,7 +548,7 @@ TabChild::Init()
// Directly create our web browser object and store it, so we can start
// eliminating QIs.
mWebBrowser = new nsWebBrowser();
mWebBrowser = new nsWebBrowser(nsIDocShellTreeItem::typeContentWrapper);
nsIWebBrowser* webBrowser = mWebBrowser;
webBrowser->SetContainerWindow(this);
@ -556,9 +556,6 @@ TabChild::Init()
mWebNav = do_QueryInterface(webBrowser);
NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(WebNavigation()));
docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper);
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
if (!baseWindow) {
NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");

View File

@ -0,0 +1,98 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "GpuDecoderModule.h"
#include "base/thread.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/StaticPrefs.h"
#include "RemoteMediaDataDecoder.h"
#include "VideoDecoderChild.h"
#include "VideoDecoderManagerChild.h"
namespace mozilla {
using base::Thread;
using dom::VideoDecoderChild;
using dom::VideoDecoderManagerChild;
using namespace ipc;
using namespace layers;
using namespace gfx;
nsresult
GpuDecoderModule::Startup()
{
if (!VideoDecoderManagerChild::GetManagerThread()) {
return NS_ERROR_FAILURE;
}
return mWrapped->Startup();
}
bool
GpuDecoderModule::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
return mWrapped->SupportsMimeType(aMimeType, aDiagnostics);
}
bool
GpuDecoderModule::Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const
{
return mWrapped->Supports(aTrackInfo, aDiagnostics);
}
static inline bool
IsRemoteAcceleratedCompositor(KnowsCompositor* aKnows)
{
TextureFactoryIdentifier ident = aKnows->GetTextureFactoryIdentifier();
return ident.mParentBackend != LayersBackend::LAYERS_BASIC &&
ident.mParentProcessType == GeckoProcessType_GPU;
}
already_AddRefed<MediaDataDecoder>
GpuDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
{
if (!StaticPrefs::MediaGpuProcessDecoder() ||
!aParams.mKnowsCompositor ||
!IsRemoteAcceleratedCompositor(aParams.mKnowsCompositor))
{
return mWrapped->CreateVideoDecoder(aParams);
}
RefPtr<VideoDecoderChild> child = new VideoDecoderChild();
RefPtr<RemoteMediaDataDecoder> object = new RemoteMediaDataDecoder(
child,
VideoDecoderManagerChild::GetManagerThread(),
VideoDecoderManagerChild::GetManagerAbstractThread());
SynchronousTask task("InitIPDL");
MediaResult result(NS_OK);
VideoDecoderManagerChild::GetManagerThread()->Dispatch(
NS_NewRunnableFunction(
"dom::GpuDecoderModule::CreateVideoDecoder",
[&]() {
AutoCompleteTask complete(&task);
result = child->InitIPDL(
aParams.VideoConfig(),
aParams.mRate.mValue,
aParams.mOptions,
aParams.mKnowsCompositor->GetTextureFactoryIdentifier());
}),
NS_DISPATCH_NORMAL);
task.Wait();
if (NS_FAILED(result)) {
if (aParams.mError) {
*aParams.mError = result;
}
return nullptr;
}
return object.forget();
}
} // namespace mozilla

View File

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 include_dom_media_ipc_GpuDecoderModule_h
#define include_dom_media_ipc_GpuDecoderModule_h
#include "PlatformDecoderModule.h"
#include "MediaData.h"
namespace mozilla {
// A PDM implementation that creates a RemoteMediaDataDecoder (a
// MediaDataDecoder) that proxies to a VideoDecoderChild. The
// VideoDecoderChild will talk to a VideoDecoderParent running on the
// GPU process.
// We currently require a 'wrapped' PDM in order to be able to answer
// SupportsMimeType and DecoderNeedsConversion. Ideally we'd check these
// over IPDL using the manager protocol
class GpuDecoderModule : public PlatformDecoderModule
{
public:
explicit GpuDecoderModule(PlatformDecoderModule* aWrapped)
: mWrapped(aWrapped)
{}
nsresult Startup() override;
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const override;
bool Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const override;
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
const CreateDecoderParams& aParams) override;
already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
const CreateDecoderParams& aParams) override
{
return nullptr;
}
private:
RefPtr<PlatformDecoderModule> mWrapped;
};
} // namespace mozilla
#endif // include_dom_media_ipc_GpuDecoderModule_h

View File

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 include_dom_media_ipc_IRemoteDecoderChild_h
#define include_dom_media_ipc_IRemoteDecoderChild_h
#include "PlatformDecoderModule.h"
namespace mozilla {
// This interface mirrors the MediaDataDecoder plus a bit (DestroyIPDL)
// to allow proxying to a remote decoder in RemoteDecoderModule or
// GpuDecoderModule. RemoteAudioDecoderChild, RemoteVideoDecoderChild,
// and VideoDecoderChild (for GPU) implement this interface.
class IRemoteDecoderChild
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IRemoteDecoderChild);
virtual RefPtr<MediaDataDecoder::InitPromise> Init() = 0;
virtual RefPtr<MediaDataDecoder::DecodePromise> Decode(
MediaRawData* aSample) = 0;
virtual RefPtr<MediaDataDecoder::DecodePromise> Drain() = 0;
virtual RefPtr<MediaDataDecoder::FlushPromise> Flush() = 0;
virtual void Shutdown() = 0;
virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const
{
return false;
}
virtual nsCString GetDescriptionName() const = 0;
virtual void SetSeekThreshold(const media::TimeUnit& aTime) {}
virtual MediaDataDecoder::ConversionRequired NeedsConversion() const
{
return MediaDataDecoder::ConversionRequired::kNeedNone;
}
virtual void DestroyIPDL() = 0;
protected:
virtual ~IRemoteDecoderChild() {}
};
} // namespace mozilla
#endif // include_dom_media_ipc_IRemoteDecoderChild_h

View File

@ -0,0 +1,23 @@
/* 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 {
struct MediaDataIPDL
{
int64_t offset;
int64_t time;
int64_t timecode;
int64_t duration;
uint32_t frames;
bool keyframe;
};
struct MediaRawDataIPDL
{
MediaDataIPDL base;
Shmem buffer;
};
} // namespace mozilla

View File

@ -6,22 +6,13 @@
include "mozilla/dom/MediaIPCUtils.h";
include protocol PVideoDecoderManager;
include PMediaDecoderParams;
include LayersSurfaces;
using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
namespace mozilla {
namespace dom {
struct MediaDataIPDL
{
int64_t offset;
int64_t time;
int64_t timecode;
int64_t duration;
uint32_t frames;
bool keyframe;
};
struct VideoDataIPDL
{
MediaDataIPDL base;
@ -31,12 +22,6 @@ struct VideoDataIPDL
int32_t frameID;
};
struct MediaRawDataIPDL
{
MediaDataIPDL base;
Shmem buffer;
};
// This protocol provides a way to use MediaDataDecoder across processes.
// The parent side currently is only implemented to work with
// Window Media Foundation, but can be extended easily to support other backends.

View File

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "RemoteMediaDataDecoder.h"
#include "base/thread.h"
#include "IRemoteDecoderChild.h"
namespace mozilla {
using base::Thread;
RemoteMediaDataDecoder::RemoteMediaDataDecoder(
IRemoteDecoderChild* aChild,
nsIThread* aManagerThread,
AbstractThread* aAbstractManagerThread)
: mChild(aChild)
, mManagerThread(aManagerThread)
, mAbstractManagerThread(aAbstractManagerThread)
{
}
RemoteMediaDataDecoder::~RemoteMediaDataDecoder()
{
// We're about to be destroyed and drop our ref to
// *DecoderChild. Make sure we put a ref into the
// task queue for the *DecoderChild thread to keep
// it alive until we send the delete message.
RefPtr<IRemoteDecoderChild> child = mChild.forget();
RefPtr<Runnable> task = NS_NewRunnableFunction(
"dom::RemoteMediaDataDecoder::~RemoteMediaDataDecoder", [child]() {
MOZ_ASSERT(child);
child->DestroyIPDL();
});
// Drop our references to the child so that the last ref
// always gets released on the manager thread.
child = nullptr;
mManagerThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
}
RefPtr<MediaDataDecoder::InitPromise>
RemoteMediaDataDecoder::Init()
{
RefPtr<RemoteMediaDataDecoder> self = this;
return InvokeAsync(mAbstractManagerThread,
__func__,
[self]() { return self->mChild->Init(); })
->Then(mAbstractManagerThread,
__func__,
[self, this](TrackType aTrack) {
mDescription =
mChild->GetDescriptionName() + NS_LITERAL_CSTRING(" (remote)");
mIsHardwareAccelerated =
mChild->IsHardwareAccelerated(mHardwareAcceleratedReason);
mConversion = mChild->NeedsConversion();
return InitPromise::CreateAndResolve(aTrack, __func__);
},
[self](const MediaResult& aError) {
return InitPromise::CreateAndReject(aError, __func__);
});
}
RefPtr<MediaDataDecoder::DecodePromise>
RemoteMediaDataDecoder::Decode(MediaRawData* aSample)
{
RefPtr<RemoteMediaDataDecoder> self = this;
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(mAbstractManagerThread, __func__, [self, sample]() {
return self->mChild->Decode(sample);
});
}
RefPtr<MediaDataDecoder::FlushPromise>
RemoteMediaDataDecoder::Flush()
{
RefPtr<RemoteMediaDataDecoder> self = this;
return InvokeAsync(mAbstractManagerThread, __func__, [self]() {
return self->mChild->Flush();
});
}
RefPtr<MediaDataDecoder::DecodePromise>
RemoteMediaDataDecoder::Drain()
{
RefPtr<RemoteMediaDataDecoder> self = this;
return InvokeAsync(mAbstractManagerThread, __func__, [self]() {
return self->mChild->Drain();
});
}
RefPtr<ShutdownPromise>
RemoteMediaDataDecoder::Shutdown()
{
RefPtr<RemoteMediaDataDecoder> self = this;
return InvokeAsync(mAbstractManagerThread, __func__, [self]() {
self->mChild->Shutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}
bool
RemoteMediaDataDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const
{
aFailureReason = mHardwareAcceleratedReason;
return mIsHardwareAccelerated;
}
void
RemoteMediaDataDecoder::SetSeekThreshold(const media::TimeUnit& aTime)
{
RefPtr<RemoteMediaDataDecoder> self = this;
media::TimeUnit time = aTime;
mManagerThread->Dispatch(
NS_NewRunnableFunction("dom::RemoteMediaDataDecoder::SetSeekThreshold",
[=]() {
MOZ_ASSERT(self->mChild);
self->mChild->SetSeekThreshold(time);
}),
NS_DISPATCH_NORMAL);
}
MediaDataDecoder::ConversionRequired
RemoteMediaDataDecoder::NeedsConversion() const
{
return mConversion;
}
nsCString
RemoteMediaDataDecoder::GetDescriptionName() const
{
return mDescription;
}
} // namespace mozilla

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 include_dom_media_ipc_RemoteMediaDataDecoder_h
#define include_dom_media_ipc_RemoteMediaDataDecoder_h
#include "PlatformDecoderModule.h"
#include "MediaData.h"
namespace mozilla {
class GpuDecoderModule;
class IRemoteDecoderChild;
class RemoteMediaDataDecoder;
DDLoggedTypeCustomNameAndBase(RemoteMediaDataDecoder,
RemoteMediaDataDecoder,
MediaDataDecoder);
// A MediaDataDecoder implementation that proxies through IPDL
// to a 'real' decoder in the GPU or RDD process.
// All requests get forwarded to a *DecoderChild instance that
// operates solely on the provided manager and abstract manager threads.
class RemoteMediaDataDecoder
: public MediaDataDecoder
, public DecoderDoctorLifeLogger<RemoteMediaDataDecoder>
{
public:
friend class GpuDecoderModule;
// MediaDataDecoder
RefPtr<InitPromise> Init() override;
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
RefPtr<DecodePromise> Drain() override;
RefPtr<FlushPromise> Flush() override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
void SetSeekThreshold(const media::TimeUnit& aTime) override;
nsCString GetDescriptionName() const override;
ConversionRequired NeedsConversion() const override;
private:
RemoteMediaDataDecoder(IRemoteDecoderChild* aChild,
nsIThread* aManagerThread,
AbstractThread* aAbstractManagerThread);
~RemoteMediaDataDecoder();
// Only ever written to from the reader task queue (during the constructor and
// destructor when we can guarantee no other threads are accessing it). Only
// read from the manager thread.
RefPtr<IRemoteDecoderChild> mChild;
nsIThread* mManagerThread;
AbstractThread* mAbstractManagerThread;
// Only ever written/modified during decoder initialisation.
// As such can be accessed from any threads after that.
nsCString mDescription = NS_LITERAL_CSTRING("RemoteMediaDataDecoder");
bool mIsHardwareAccelerated = false;
nsCString mHardwareAcceleratedReason;
ConversionRequired mConversion = ConversionRequired::kNeedNone;
};
} // namespace mozilla
#endif // include_dom_media_ipc_RemoteMediaDataDecoder_h

View File

@ -1,218 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "RemoteVideoDecoder.h"
#include "VideoDecoderChild.h"
#include "VideoDecoderManagerChild.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/StaticPrefs.h"
#include "base/thread.h"
#include "MediaInfo.h"
#include "ImageContainer.h"
#include "mozilla/layers/SynchronousTask.h"
namespace mozilla {
namespace dom {
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
RemoteVideoDecoder::RemoteVideoDecoder()
: mActor(new VideoDecoderChild())
, mDescription("RemoteVideoDecoder")
, mIsHardwareAccelerated(false)
, mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
{
}
RemoteVideoDecoder::~RemoteVideoDecoder()
{
// We're about to be destroyed and drop our ref to
// VideoDecoderChild. Make sure we put a ref into the
// task queue for the VideoDecoderChild thread to keep
// it alive until we send the delete message.
RefPtr<VideoDecoderChild> actor = mActor;
RefPtr<Runnable> task = NS_NewRunnableFunction(
"dom::RemoteVideoDecoder::~RemoteVideoDecoder", [actor]() {
MOZ_ASSERT(actor);
actor->DestroyIPDL();
});
// Drop out references to the actor so that the last ref
// always gets released on the manager thread.
actor = nullptr;
mActor = nullptr;
VideoDecoderManagerChild::GetManagerThread()->Dispatch(task.forget(),
NS_DISPATCH_NORMAL);
}
RefPtr<MediaDataDecoder::InitPromise>
RemoteVideoDecoder::Init()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self]() { return self->mActor->Init(); })
->Then(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self, this](TrackType aTrack) {
mDescription =
mActor->GetDescriptionName() + NS_LITERAL_CSTRING(" (remote)");
mIsHardwareAccelerated =
mActor->IsHardwareAccelerated(mHardwareAcceleratedReason);
mConversion = mActor->NeedsConversion();
return InitPromise::CreateAndResolve(aTrack, __func__);
},
[self](const MediaResult& aError) {
return InitPromise::CreateAndReject(aError, __func__);
});
}
RefPtr<MediaDataDecoder::DecodePromise>
RemoteVideoDecoder::Decode(MediaRawData* aSample)
{
RefPtr<RemoteVideoDecoder> self = this;
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__,
[self, sample]() { return self->mActor->Decode(sample); });
}
RefPtr<MediaDataDecoder::FlushPromise>
RemoteVideoDecoder::Flush()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self]() { return self->mActor->Flush(); });
}
RefPtr<MediaDataDecoder::DecodePromise>
RemoteVideoDecoder::Drain()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self]() { return self->mActor->Drain(); });
}
RefPtr<ShutdownPromise>
RemoteVideoDecoder::Shutdown()
{
RefPtr<RemoteVideoDecoder> self = this;
return InvokeAsync(VideoDecoderManagerChild::GetManagerAbstractThread(),
__func__, [self]() {
self->mActor->Shutdown();
return ShutdownPromise::CreateAndResolve(true, __func__);
});
}
bool
RemoteVideoDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const
{
aFailureReason = mHardwareAcceleratedReason;
return mIsHardwareAccelerated;
}
void
RemoteVideoDecoder::SetSeekThreshold(const media::TimeUnit& aTime)
{
RefPtr<RemoteVideoDecoder> self = this;
media::TimeUnit time = aTime;
VideoDecoderManagerChild::GetManagerThread()->Dispatch(
NS_NewRunnableFunction("dom::RemoteVideoDecoder::SetSeekThreshold",
[=]() {
MOZ_ASSERT(self->mActor);
self->mActor->SetSeekThreshold(time);
}),
NS_DISPATCH_NORMAL);
}
MediaDataDecoder::ConversionRequired
RemoteVideoDecoder::NeedsConversion() const
{
return mConversion;
}
nsresult
RemoteDecoderModule::Startup()
{
if (!VideoDecoderManagerChild::GetManagerThread()) {
return NS_ERROR_FAILURE;
}
return mWrapped->Startup();
}
bool
RemoteDecoderModule::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
return mWrapped->SupportsMimeType(aMimeType, aDiagnostics);
}
bool
RemoteDecoderModule::Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const
{
return mWrapped->Supports(aTrackInfo, aDiagnostics);
}
static inline bool
IsRemoteAcceleratedCompositor(KnowsCompositor* aKnows)
{
TextureFactoryIdentifier ident = aKnows->GetTextureFactoryIdentifier();
return ident.mParentBackend != LayersBackend::LAYERS_BASIC &&
ident.mParentProcessType == GeckoProcessType_GPU;
}
already_AddRefed<MediaDataDecoder>
RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
{
if (!StaticPrefs::MediaGpuProcessDecoder() ||
!aParams.mKnowsCompositor ||
!IsRemoteAcceleratedCompositor(aParams.mKnowsCompositor))
{
return mWrapped->CreateVideoDecoder(aParams);
}
RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder();
SynchronousTask task("InitIPDL");
MediaResult result(NS_OK);
VideoDecoderManagerChild::GetManagerThread()->Dispatch(
NS_NewRunnableFunction(
"dom::RemoteDecoderModule::CreateVideoDecoder",
[&]() {
AutoCompleteTask complete(&task);
result = object->mActor->InitIPDL(
aParams.VideoConfig(),
aParams.mRate.mValue,
aParams.mOptions,
aParams.mKnowsCompositor->GetTextureFactoryIdentifier());
}),
NS_DISPATCH_NORMAL);
task.Wait();
if (NS_FAILED(result)) {
if (aParams.mError) {
*aParams.mError = result;
}
return nullptr;
}
return object.forget();
}
nsCString
RemoteVideoDecoder::GetDescriptionName() const
{
return mDescription;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,100 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 include_dom_ipc_RemoteVideoDecoder_h
#define include_dom_ipc_RemoteVideoDecoder_h
#include "mozilla/RefPtr.h"
#include "mozilla/DebugOnly.h"
#include "MediaData.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
namespace dom {
class RemoteVideoDecoder;
}
DDLoggedTypeCustomNameAndBase(dom::RemoteVideoDecoder,
RemoteVideoDecoder,
MediaDataDecoder);
namespace dom {
class VideoDecoderChild;
class RemoteDecoderModule;
// A MediaDataDecoder implementation that proxies through IPDL
// to a 'real' decoder in the GPU process.
// All requests get forwarded to a VideoDecoderChild instance that
// operates solely on the VideoDecoderManagerChild thread.
class RemoteVideoDecoder
: public MediaDataDecoder
, public DecoderDoctorLifeLogger<RemoteVideoDecoder>
{
public:
friend class RemoteDecoderModule;
// MediaDataDecoder
RefPtr<InitPromise> Init() override;
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
RefPtr<DecodePromise> Drain() override;
RefPtr<FlushPromise> Flush() override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
void SetSeekThreshold(const media::TimeUnit& aTime) override;
nsCString GetDescriptionName() const override;
ConversionRequired NeedsConversion() const override;
private:
RemoteVideoDecoder();
~RemoteVideoDecoder();
// Only ever written to from the reader task queue (during the constructor and
// destructor when we can guarantee no other threads are accessing it). Only
// read from the manager thread.
RefPtr<VideoDecoderChild> mActor;
// Only ever written/modified during decoder initialisation.
// As such can be accessed from any threads after that.
nsCString mDescription;
bool mIsHardwareAccelerated;
nsCString mHardwareAcceleratedReason;
MediaDataDecoder::ConversionRequired mConversion;
};
// A PDM implementation that creates RemoteVideoDecoders.
// We currently require a 'wrapped' PDM in order to be able to answer SupportsMimeType
// and DecoderNeedsConversion. Ideally we'd check these over IPDL using the manager
// protocol
class RemoteDecoderModule : public PlatformDecoderModule
{
public:
explicit RemoteDecoderModule(PlatformDecoderModule* aWrapped)
: mWrapped(aWrapped)
{}
nsresult Startup() override;
bool SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const override;
bool Supports(const TrackInfo& aTrackInfo,
DecoderDoctorDiagnostics* aDiagnostics) const override;
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
const CreateDecoderParams& aParams) override;
already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
const CreateDecoderParams& aParams) override
{
return nullptr;
}
private:
RefPtr<PlatformDecoderModule> mWrapped;
};
} // namespace dom
} // namespace mozilla
#endif // include_dom_ipc_RemoteVideoDecoder_h

View File

@ -9,6 +9,7 @@
#include "MediaResult.h"
#include "PlatformDecoderModule.h"
#include "mozilla/dom/PVideoDecoderChild.h"
#include "IRemoteDecoderChild.h"
namespace mozilla {
namespace dom {
@ -18,12 +19,11 @@ class RemoteDecoderModule;
class VideoDecoderManagerChild;
class VideoDecoderChild final : public PVideoDecoderChild
, public IRemoteDecoderChild
{
public:
explicit VideoDecoderChild();
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderChild)
// PVideoDecoderChild
mozilla::ipc::IPCResult RecvOutput(const VideoDataIPDL& aData) override;
mozilla::ipc::IPCResult RecvInputExhausted() override;
@ -38,22 +38,22 @@ public:
void ActorDestroy(ActorDestroyReason aWhy) override;
RefPtr<MediaDataDecoder::InitPromise> Init();
RefPtr<MediaDataDecoder::DecodePromise> Decode(MediaRawData* aSample);
RefPtr<MediaDataDecoder::DecodePromise> Drain();
RefPtr<MediaDataDecoder::FlushPromise> Flush();
void Shutdown();
bool IsHardwareAccelerated(nsACString& aFailureReason) const;
nsCString GetDescriptionName() const;
void SetSeekThreshold(const media::TimeUnit& aTime);
MediaDataDecoder::ConversionRequired NeedsConversion() const;
RefPtr<MediaDataDecoder::InitPromise> Init() override;
RefPtr<MediaDataDecoder::DecodePromise> Decode(MediaRawData* aSample) override;
RefPtr<MediaDataDecoder::DecodePromise> Drain() override;
RefPtr<MediaDataDecoder::FlushPromise> Flush() override;
void Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
nsCString GetDescriptionName() const override;
void SetSeekThreshold(const media::TimeUnit& aTime) override;
MediaDataDecoder::ConversionRequired NeedsConversion() const override;
void DestroyIPDL() override;
MOZ_IS_CLASS_INIT
MediaResult InitIPDL(const VideoInfo& aVideoInfo,
float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier);
void DestroyIPDL();
// Called from IPDL when our actor has been destroyed
void IPDLActorDestroyed();

View File

@ -6,19 +6,25 @@
IPDL_SOURCES += [
'PMediaDecoderParams.ipdlh',
'PVideoDecoder.ipdl',
'PVideoDecoderManager.ipdl',
]
EXPORTS.mozilla += [
'GpuDecoderModule.h',
'RemoteMediaDataDecoder.h',
]
EXPORTS.mozilla.dom += [
'MediaIPCUtils.h',
'RemoteVideoDecoder.h',
'VideoDecoderManagerChild.h',
'VideoDecoderManagerParent.h',
]
SOURCES += [
'RemoteVideoDecoder.cpp',
'GpuDecoderModule.cpp',
'RemoteMediaDataDecoder.cpp',
'VideoDecoderChild.cpp',
'VideoDecoderManagerChild.cpp',
'VideoDecoderManagerParent.cpp',

View File

@ -45,7 +45,7 @@
#include "MP4Decoder.h"
#include "VPXDecoder.h"
#include "mozilla/dom/RemoteVideoDecoder.h"
#include "mozilla/GpuDecoderModule.h"
#include "H264.h"
@ -355,7 +355,7 @@ PDMFactory::CreatePDMs()
#ifdef XP_WIN
if (StaticPrefs::MediaWmfEnabled() && !IsWin7AndPre2000Compatible()) {
m = new WMFDecoderModule();
RefPtr<PlatformDecoderModule> remote = new dom::RemoteDecoderModule(m);
RefPtr<PlatformDecoderModule> remote = new GpuDecoderModule(m);
StartupPDM(remote);
mWMFFailedToLoad = !StartupPDM(m);
} else {

View File

@ -33,10 +33,7 @@ namespace layers {
class ImageContainer;
} // namespace layers
namespace dom {
class RemoteDecoderModule;
}
class GpuDecoderModule;
class MediaDataDecoder;
class TaskQueue;
class CDMProxy;
@ -214,7 +211,7 @@ protected:
friend class MediaChangeMonitor;
friend class PDMFactory;
friend class dom::RemoteDecoderModule;
friend class GpuDecoderModule;
friend class EMEDecoderModule;
// Indicates if the PlatformDecoderModule supports decoding of aColorDepth.

View File

@ -11,6 +11,7 @@
#include "AppleDecoderModule.h"
#include "AppleUtils.h"
#include "AppleVTLinker.h"
#include "MacIOSurfaceImage.h"
#include "MediaData.h"
#include "mozilla/ArrayUtils.h"
#include "H264.h"
@ -436,7 +437,7 @@ AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage,
RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
RefPtr<layers::Image> image = new MacIOSurfaceImage(macSurface);
RefPtr<layers::Image> image = new layers::MacIOSurfaceImage(macSurface);
data =
VideoData::CreateFromImage(info.mDisplay,

View File

@ -20,6 +20,7 @@ support-files = PushServiceHandler.js PushServiceHandler.manifest
[test_notification_version_string.js]
[test_observer_data.js]
[test_observer_remoting.js]
skip-if = serviceworker_e10s
[test_permissions.js]
run-sequentially = This will delete all existing push subscriptions.

View File

@ -7,12 +7,6 @@
with Files("**"):
BUG_COMPONENT = ("Core", "XSLT")
XPIDL_SOURCES += [
'txIEXSLTRegExFunctions.idl',
]
XPIDL_MODULE = 'content_xslt'
EXPORTS += [
'nsIDocumentTransformer.h',
]

View File

@ -1,21 +0,0 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
webidl Document;
webidl DocumentFragment;
[scriptable, uuid(c180e993-aced-4839-95a0-ecd5ff138be9)]
interface txIEXSLTRegExFunctions : nsISupports
{
DocumentFragment match(in AString aString, in AString aRegEx,
in AString aFlags,
in Document aResultDocument);
AString replace(in AString aString, in AString aRegEx,
in AString aFlags, in AString aReplace);
boolean test(in AString aString, in AString aRegEx,
in AString aFlags);
};

View File

@ -41,19 +41,16 @@ UNIFIED_SOURCES += [
'txXSLTProcessor.cpp',
]
EXTRA_COMPONENTS += [
'txEXSLTRegExFunctions.js',
'txEXSLTRegExFunctions.manifest',
EXTRA_JS_MODULES += [
'txEXSLTRegExFunctions.jsm',
]
# For nsAutoJSString
LOCAL_INCLUDES += ["/dom/base"]
LOCAL_INCLUDES += [
'../base',
'../xml',
'../xpath',
'/dom/base',
'/js/xpconnect/src',
]
FINAL_LIBRARY = 'xul'

View File

@ -26,9 +26,9 @@
#include "nsIContent.h"
#include "txMozillaXMLOutput.h"
#include "nsTextNode.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentFragmentBinding.h"
#include "prtime.h"
#include "txIEXSLTRegExFunctions.h"
#include "xpcprivate.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -217,19 +217,13 @@ private:
class txEXSLTRegExFunctionCall : public FunctionCall
{
public:
txEXSLTRegExFunctionCall(txEXSLTType aType, txIEXSLTRegExFunctions* aRegExService)
explicit txEXSLTRegExFunctionCall(txEXSLTType aType)
: mType(aType)
, mRegExService(aRegExService)
{}
static bool Create(txEXSLTType aType, FunctionCall** aFunction)
{
nsCOMPtr<txIEXSLTRegExFunctions> regExService =
do_GetService("@mozilla.org/exslt/regexp;1");
if (!regExService) {
return false;
}
*aFunction = new txEXSLTRegExFunctionCall(aType, regExService);
*aFunction = new txEXSLTRegExFunctionCall(aType);
return true;
}
@ -237,7 +231,6 @@ public:
private:
txEXSLTType mType;
nsCOMPtr<txIEXSLTRegExFunctions> mRegExService;
};
nsresult
@ -734,15 +727,56 @@ txEXSLTRegExFunctionCall::evaluate(txIEvalContext* aContext,
NS_ENSURE_SUCCESS(rv, rv);
}
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::PrivilegedJunkScope())) {
return NS_ERROR_FAILURE;
}
JSContext* cx = jsapi.cx();
xpc::SandboxOptions options;
options.sandboxName.AssignLiteral("txEXSLTRegExFunctionCall sandbox");
options.invisibleToDebugger = true;
JS::RootedValue v(cx);
rv = CreateSandboxObject(cx, &v, nsXPConnect::SystemPrincipal(), options);
MOZ_ALWAYS_SUCCEEDS(rv);
JS::RootedObject sandbox(cx, js::UncheckedUnwrap(&v.toObject()));
JSAutoRealm ar(cx, sandbox);
ErrorResult er;
GlobalObject global(cx, sandbox);
JS::RootedObject obj(cx);
ChromeUtils::Import(global,
NS_LITERAL_STRING("resource://gre/modules/txEXSLTRegExFunctions.jsm"),
Optional<JS::HandleObject>(), &obj, er);
MOZ_ALWAYS_TRUE(!er.Failed());
JS::RootedString str(cx, JS_NewUCStringCopyZ(cx, PromiseFlatString(string).get()));
JS::RootedString re(cx, JS_NewUCStringCopyZ(cx, PromiseFlatString(regex).get()));
JS::RootedString fl(cx, JS_NewUCStringCopyZ(cx, PromiseFlatString(flags).get()));
switch (mType) {
case txEXSLTType::MATCH:
{
nsCOMPtr<nsIDocument> sourceDoc = getSourceDocument(aContext);
NS_ENSURE_STATE(sourceDoc);
JS::RootedValue doc(cx);
if (!GetOrCreateDOMReflector(cx, sourceDoc, &doc)) {
return NS_ERROR_FAILURE;
}
JS::AutoValueArray<4> args(cx);
args[0].setString(str);
args[1].setString(re);
args[2].setString(fl);
args[3].setObject(doc.toObject());
JS::RootedValue rval(cx);
if (!JS_CallFunctionName(cx, sandbox, "match", args, &rval)) {
return NS_ERROR_FAILURE;
}
RefPtr<DocumentFragment> docFrag;
rv = mRegExService->Match(string, regex, flags, sourceDoc,
getter_AddRefs(docFrag));
rv = UNWRAP_OBJECT(DocumentFragment, &rval, docFrag);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(docFrag);
@ -768,9 +802,22 @@ txEXSLTRegExFunctionCall::evaluate(txIEvalContext* aContext,
rv = mParams[3]->evaluateToString(aContext, replace);
NS_ENSURE_SUCCESS(rv, rv);
JS::RootedString repl(cx, JS_NewUCStringCopyZ(cx, PromiseFlatString(replace).get()));
JS::AutoValueArray<4> args(cx);
args[0].setString(str);
args[1].setString(re);
args[2].setString(fl);
args[3].setString(repl);
JS::RootedValue rval(cx);
if (!JS_CallFunctionName(cx, sandbox, "replace", args, &rval)) {
return NS_ERROR_FAILURE;
}
nsString result;
rv = mRegExService->Replace(string, regex, flags, replace, result);
NS_ENSURE_SUCCESS(rv, rv);
if (!ConvertJSValueToString(cx, rval, result)) {
return NS_ERROR_FAILURE;
}
rv = aContext->recycler()->getStringResult(result, aResult);
NS_ENSURE_SUCCESS(rv, rv);
@ -779,9 +826,17 @@ txEXSLTRegExFunctionCall::evaluate(txIEvalContext* aContext,
}
case txEXSLTType::TEST:
{
bool result;
rv = mRegExService->Test(string, regex, flags, &result);
NS_ENSURE_SUCCESS(rv, rv);
JS::AutoValueArray<3> args(cx);
args[0].setString(str);
args[1].setString(re);
args[2].setString(fl);
JS::RootedValue rval(cx);
if (!JS_CallFunctionName(cx, sandbox, "test", args, &rval)) {
return NS_ERROR_FAILURE;
}
bool result = rval.toBoolean();
aContext->recycler()->getBoolResult(result, aResult);

View File

@ -1,51 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* 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/. */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const EXSLT_REGEXP_CID = Components.ID("{18a03189-067b-4978-b4f1-bafe35292ed6}");
function txEXSLTRegExFunctions()
{
}
var SingletonInstance = null;
txEXSLTRegExFunctions.prototype = {
classID: EXSLT_REGEXP_CID,
QueryInterface: ChromeUtils.generateQI([Ci.txIEXSLTRegExFunctions]),
// txIEXSLTRegExFunctions
match: function(str, regex, flags, doc) {
var docFrag = doc.createDocumentFragment();
var re = new RegExp(regex, flags);
var matches = str.match(re);
if (matches != null) {
for (var i = 0; i < matches.length; ++i) {
var match = matches[i];
var elem = doc.createElementNS(null, "match");
var text = doc.createTextNode(match ? match : '');
elem.appendChild(text);
docFrag.appendChild(elem);
}
}
return docFrag;
},
replace: function(str, regex, flags, replace) {
var re = new RegExp(regex, flags);
return str.replace(re, replace);
},
test: function(str, regex, flags) {
var re = new RegExp(regex, flags);
return re.test(str);
}
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([txEXSLTRegExFunctions]);

View File

@ -0,0 +1,40 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
/* 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/. */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
function match(str, regex, flags, doc) {
var docFrag = doc.createDocumentFragment();
var re = new RegExp(regex, flags);
var matches = str.match(re);
if (matches != null) {
for (var i = 0; i < matches.length; ++i) {
var match = matches[i];
var elem = doc.createElementNS(null, "match");
var text = doc.createTextNode(match ? match : '');
elem.appendChild(text);
docFrag.appendChild(elem);
}
}
return docFrag;
}
function replace(str, regex, flags, replace) {
var re = new RegExp(regex, flags);
return str.replace(re, replace);
}
function test(str, regex, flags) {
var re = new RegExp(regex, flags);
return re.test(str);
}
var EXPORTED_SYMBOLS = [
"match",
"replace",
"test",
];

View File

@ -1,2 +0,0 @@
component {18a03189-067b-4978-b4f1-bafe35292ed6} txEXSLTRegExFunctions.js
contract @mozilla.org/exslt/regexp;1 {18a03189-067b-4978-b4f1-bafe35292ed6}

View File

@ -2058,6 +2058,30 @@ GLContext::MarkDestroyed()
mSymbols = {};
}
// -
GLenum
GLContext::RawGetErrorAndClear() const
{
const GLenum ret = mSymbols.fGetError();
auto flushedErr = ret;
uint32_t i = 1;
while (flushedErr && flushedErr != LOCAL_GL_CONTEXT_LOST) {
if (i == 100) {
gfxCriticalError() << "Flushing glGetError still " << gfx::hexa(flushedErr)
<< " after " << i << " calls.";
break;
}
flushedErr = mSymbols.fGetError();
i += 1;
}
return ret;
}
// -
#ifdef MOZ_GL_DEBUG
/* static */ void
GLContext::AssertNotPassingStackBufferToTheGL(const void* ptr)

View File

@ -579,18 +579,7 @@ public:
private:
mutable GLenum mTopError = 0;
GLenum RawGetError() const {
return mSymbols.fGetError();
}
GLenum RawGetErrorAndClear() const {
GLenum err = RawGetError();
if (err)
while (RawGetError()) {}
return err;
}
GLenum RawGetErrorAndClear() const;
GLenum FlushErrors() const {
GLenum err = RawGetErrorAndClear();

View File

@ -4,8 +4,12 @@
* 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 "VsyncBridgeParent.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
using mozilla::layers::CompositorBridgeParent;
using mozilla::layers::CompositorThreadHolder;
namespace mozilla {
namespace gfx {

View File

@ -1,5 +1,11 @@
// Utilities for synthesizing of native events.
function getResolution() {
let resolution = { value: -1 }; // bogus value in case DWU fails us
SpecialPowers.getDOMWindowUtils(window).getResolution(resolution);
return resolution.value;
}
function getPlatform() {
if (navigator.platform.indexOf("Win") == 0) {
return "windows";
@ -78,15 +84,45 @@ function nativeMouseUpEventMsg() {
throw "Native mouse-up events not supported on platform " + getPlatform();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
// to device pixels relative to the screen.
function coordinatesRelativeToScreen(aX, aY, aElement) {
var targetWindow = aElement.ownerDocument.defaultView;
var scale = targetWindow.devicePixelRatio;
// Given an event target which may be a window or an element, get the associated window.
function windowForTarget(aTarget) {
if (aTarget instanceof Window) {
return aTarget;
}
return aTarget.ownerDocument.defaultView;
}
function getBoundingClientRectRelativeToVisualViewport(aElement) {
let utils = SpecialPowers.getDOMWindowUtils(window);
var rect = aElement.getBoundingClientRect();
var offsetX = {}, offsetY = {};
utils.getVisualViewportOffsetRelativeToLayoutViewport(offsetX, offsetY);
rect.x -= offsetX.value;
rect.y -= offsetY.value;
return rect;
}
// Several event sythesization functions below (and their helpers) take a "target"
// parameter which may be either an element or a window. For such functions,
// the target's "bounding rect" refers to the bounding client rect for an element,
// and the window's origin for a window.
// Not all functions have been "upgraded" to allow a window argument yet; feel
// free to upgrade others as necessary.
// Convert (aX, aY), in CSS pixels relative to aTarget's bounding rect
// to device pixels relative to the screen.
function coordinatesRelativeToScreen(aX, aY, aTarget) {
var targetWindow = windowForTarget(aTarget);
var deviceScale = targetWindow.devicePixelRatio;
var resolution = getResolution();
var rect = (aTarget instanceof Window)
? {left: 0, top: 0} /* we don't use the width or height */
: getBoundingClientRectRelativeToVisualViewport(aTarget);
// moxInnerScreen{X,Y} are in CSS coordinates of the browser chrome.
// The device scale applies to them, but the resolution only zooms the content.
return {
x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale,
y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale
x: (targetWindow.mozInnerScreenX + ((rect.left + aX) * resolution)) * deviceScale,
y: (targetWindow.mozInnerScreenY + ((rect.top + aY) * resolution)) * deviceScale
};
}
@ -186,16 +222,16 @@ function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallbac
}
// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
// relative to the top-left of |aElement|'s bounding rect.
function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
// relative to the top-left of |aTarget|'s bounding rect.
function synthesizeNativeTouch(aTarget, aX, aY, aType, aObserver = null, aTouchId = 0) {
var pt = coordinatesRelativeToScreen(aX, aY, aTarget);
var utils = SpecialPowers.getDOMWindowUtils(windowForTarget(aTarget));
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
return true;
}
// Function to generate native touch events for a multi-touch sequence.
// aElement is the element whose bounding rect the coordinates are relative to.
// aTarget is the element or window whose bounding rect the coordinates are relative to.
// aPositions is a 2D array of position data. It is indexed as [row][column],
// where advancing the row counter moves forward in time, and each column
// represents a single "finger" (or touch input). Each row must have exactly
@ -212,7 +248,7 @@ function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouch
// aObserver is the observer that will get registered on the very last
// synthesizeNativeTouch call this function makes.
// aTouchIds is an array holding the touch ID values of each "finger".
function* synthesizeNativeTouchSequences(aElement, aPositions, aObserver = null, aTouchIds = [0]) {
function* synthesizeNativeTouchSequences(aTarget, aPositions, aObserver = null, aTouchIds = [0]) {
// We use lastNonNullValue to figure out which synthesizeNativeTouch call
// will be the last one we make, so that we can register aObserver on it.
var lastNonNullValue = -1;
@ -268,11 +304,11 @@ function* synthesizeNativeTouchSequences(aElement, aPositions, aObserver = null,
// make, pass the observer as well
var thisIndex = ((i - yields) * aTouchIds.length) + j;
var observer = (lastSynthesizeCall == thisIndex) ? aObserver : null;
synthesizeNativeTouch(aElement, currentPositions[j].x, currentPositions[j].y, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, observer, aTouchIds[j]);
synthesizeNativeTouch(aTarget, currentPositions[j].x, currentPositions[j].y, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, observer, aTouchIds[j]);
currentPositions[j] = null;
}
} else {
synthesizeNativeTouch(aElement, aPositions[i][j].x, aPositions[i][j].y, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchIds[j]);
synthesizeNativeTouch(aTarget, aPositions[i][j].x, aPositions[i][j].y, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchIds[j]);
currentPositions[j] = aPositions[i][j];
}
}
@ -283,7 +319,7 @@ function* synthesizeNativeTouchSequences(aElement, aPositions, aObserver = null,
// Note that when calling this function you'll want to make sure that the pref
// "apz.touch_start_tolerance" is set to 0, or some of the touchmove will get
// consumed to overcome the panning threshold.
function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
function synthesizeNativeTouchDrag(aTarget, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY));
var positions = new Array();
positions.push([{ x: aX, y: aY }]);
@ -294,7 +330,7 @@ function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver
positions.push([pos]);
}
positions.push([{ x: aX + aDeltaX, y: aY + aDeltaY }]);
var continuation = synthesizeNativeTouchSequences(aElement, positions, aObserver, [aTouchId]);
var continuation = synthesizeNativeTouchSequences(aTarget, positions, aObserver, [aTouchId]);
var yielded = continuation.next();
while (!yielded.done) {
yielded = continuation.next();

View File

@ -438,7 +438,11 @@ function runContinuation(testFunction) {
}
}
driveTest();
try {
driveTest();
} catch (ex) {
SimpleTest.ok(false, "APZ test continuation failed with exception: " + ex);
}
});
};
}

View File

@ -8,11 +8,6 @@
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function getResolution() {
let resolution = { value: -1 }; // bogus value in case DWU fails us
SpecialPowers.getDOMWindowUtils(window).getResolution(resolution);
return resolution.value;
}
function* test(testDriver) {
var initial_resolution = getResolution();

View File

@ -8,11 +8,6 @@
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function getResolution() {
let resolution = { value: -1 }; // bogus value in case DWU fails us
SpecialPowers.getDOMWindowUtils(window).getResolution(resolution);
return resolution.value;
}
function* test(testDriver) {
var initial_resolution = getResolution();

View File

@ -23,9 +23,9 @@ function* test(testDriver) {
// notification) resumes the test.
// Scroll down to the iframe. Do it in two drags instead of one in case the
// device screen is short
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -350);
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -350);
// device screen is short.
yield synthesizeNativeTouchDrag(window, 10, 400, 0, -350);
yield synthesizeNativeTouchDrag(window, 10, 400, 0, -350);
// Now the top of the visible area should be at y=700 of the top-level page,
// so if the screen is >= 500px tall, the entire iframe should be visible, at
// least vertically.
@ -37,11 +37,9 @@ function* test(testDriver) {
yield flushApzRepaints(testDriver);
is(window.scrollY, 0, "Main-thread scroll position is still at 0");
// Scroll the iframe by 300px. Note that since the main-thread scroll position
// is still 0, the subframe's getBoundingClientRect is going to be off by
// 700 pixels, so we compensate for that here.
// Scroll the iframe by 300px.
var subframe = document.getElementById('subframe');
yield synthesizeNativeTouchDrag(subframe, 10, 200 - 700, 0, -300);
yield synthesizeNativeTouchDrag(subframe, 10, 200, 0, -300);
// Remove the observer, we don't need it any more.
SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);

View File

@ -40,7 +40,7 @@
// decreases the rate of intermittents that we see. We should figure out
// why and/or rewrite this test entirely.
SpecialPowers.Services.obs.addObserver(testDriver, "mouseevent");
yield synthesizeNativeClick(input, 0, 0, testDriver);
yield synthesizeNativeClick(input, 10, 10, testDriver);
SpecialPowers.Services.obs.removeObserver(testDriver, "mouseevent", false);
}
window.addEventListener("click", (e) => {

View File

@ -34,12 +34,11 @@
// Compute the distance from the right/bottom edge of the visual
// viewport to the same edge of the layout viewport and add the desired
// offset to that.
const layout = (visual + scrollbarWidth) * RESOLUTION;
return layout - visual + OFFSET_SCREEN_PX;
const layout = (visual + scrollbarWidth);
return layout - (visual / RESOLUTION) + OFFSET_CSS_PX;
}
function* test(testDriver) {
const target = document.getElementById("content");
const cases = [
{
x: 0,
@ -52,7 +51,7 @@
},
},
{
x: OFFSET_SCREEN_PX,
x: OFFSET_CSS_PX,
y: 0,
dx: (width) => 0,
dy: (height) => -computeDelta(height, horizontalScrollbarWidth),
@ -64,7 +63,7 @@
];
for (let c of cases) {
yield synthesizeNativeTouchDrag(target,
yield synthesizeNativeTouchDrag(window,
c.x,
c.y,
c.dx(document.documentElement.clientWidth),

View File

@ -11,7 +11,7 @@ use app_units::Au;
use border::{ensure_no_corner_overlap, BorderRadiusAu};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use box_shadow::get_max_scale_for_box_shadow;
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
use ellipse::Ellipse;
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode};
@ -97,13 +97,14 @@ use util::{extract_inner_rect_safe, project_rect, ScaleOffset};
// Type definitions for interning clip nodes.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ClipDataMarker;
pub type ClipDataStore = intern::DataStore<ClipItemKey, ClipNode, ClipDataMarker>;
pub type ClipDataHandle = intern::Handle<ClipDataMarker>;
pub type ClipDataUpdateList = intern::UpdateList<ClipItemKey>;
pub type ClipDataInterner = intern::Interner<ClipItemKey, ClipItemSceneData, ClipDataMarker>;
pub type ClipUid = intern::ItemUid<ClipDataMarker>;
// Result of comparing a clip node instance against a local rect.
#[derive(Debug)]
@ -256,7 +257,6 @@ struct ClipNodeInfo {
conversion: ClipSpaceConversion,
handle: ClipDataHandle,
spatial_node_index: SpatialNodeIndex,
has_non_root_coord_system: bool,
}
impl ClipNode {
@ -423,7 +423,6 @@ pub struct ClipChainInstance {
// Combined clip rect for clips that are in the
// same coordinate system as the primitive.
pub local_clip_rect: LayoutRect,
pub has_non_root_coord_system: bool,
pub has_non_local_clips: bool,
// If true, this clip chain requires allocation
// of a clip mask.
@ -578,7 +577,6 @@ impl ClipStore {
// Run through the clip nodes, and see which ones affect this prim region.
let first_clip_node_index = self.clip_node_instances.len() as u32;
let mut has_non_root_coord_system = false;
let mut has_non_local_clips = false;
let mut needs_mask = false;
@ -664,8 +662,6 @@ impl ClipStore {
spatial_node_index: node_info.spatial_node_index,
};
self.clip_node_instances.push(instance);
has_non_root_coord_system |= node_info.has_non_root_coord_system;
}
}
}
@ -679,7 +675,6 @@ impl ClipStore {
// Return a valid clip chain instance
Some(ClipChainInstance {
clips_range,
has_non_root_coord_system,
has_non_local_clips,
local_clip_rect,
pic_clip_rect,
@ -1322,7 +1317,6 @@ fn add_clip_node_to_current_chain(
conversion,
handle,
spatial_node_index: clip_spatial_node_index,
has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(),
})
}

View File

@ -41,7 +41,7 @@ impl CoordinateSystem {
}
}
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd)]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SpatialNodeIndex(pub usize);

View File

@ -173,7 +173,6 @@ impl<'a> DisplayListFlattener<'a> {
output_pipelines: &FastHashSet<PipelineId>,
frame_builder_config: &FrameBuilderConfig,
new_scene: &mut Scene,
scene_id: u64,
picture_id_generator: &mut PictureIdGenerator,
resources: &mut DocumentResources,
) -> FrameBuilder {
@ -221,7 +220,6 @@ impl<'a> DisplayListFlattener<'a> {
view.inner_rect,
background_color,
view.window_size,
scene_id,
flattener,
)
}
@ -1007,6 +1005,7 @@ impl<'a> DisplayListFlattener<'a> {
&mut self.picture_id_generator,
&mut self.prim_store,
&self.resources.prim_interner,
&self.clip_store,
);
(sc.is_3d(), extra_instance)
},
@ -1141,6 +1140,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_list,
stacking_context.spatial_node_index,
max_clip,
&self.clip_store,
);
let leaf_pic_index = self.prim_store.create_picture(leaf_picture);
@ -1185,6 +1185,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_list,
stacking_context.spatial_node_index,
max_clip,
&self.clip_store,
);
current_pic_index = self.prim_store.create_picture(container_picture);
@ -1211,6 +1212,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_list,
stacking_context.spatial_node_index,
max_clip,
&self.clip_store,
);
let filter_pic_index = self.prim_store.create_picture(filter_picture);
current_pic_index = filter_pic_index;
@ -1244,6 +1246,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_list,
stacking_context.spatial_node_index,
max_clip,
&self.clip_store,
);
let blend_pic_index = self.prim_store.create_picture(blend_picture);
current_pic_index = blend_pic_index;
@ -1585,6 +1588,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_list,
pending_shadow.clip_and_scroll.spatial_node_index,
max_clip,
&self.clip_store,
);
// Create the primitive to draw the shadow picture into the scene.
@ -2307,6 +2311,7 @@ impl FlattenedStackingContext {
picture_id_generator: &mut PictureIdGenerator,
prim_store: &mut PrimitiveStore,
prim_interner: &PrimitiveDataInterner,
clip_store: &ClipStore,
) -> Option<PrimitiveInstance> {
if !self.is_3d() || self.primitives.is_empty() {
return None
@ -2335,6 +2340,7 @@ impl FlattenedStackingContext {
prim_list,
self.spatial_node_index,
LayoutRect::max_rect(),
clip_store,
);
let pic_index = prim_store.create_picture(container_picture);

View File

@ -57,7 +57,6 @@ pub struct FrameBuilder {
screen_rect: DeviceUintRect,
background_color: Option<ColorF>,
window_size: DeviceUintSize,
scene_id: u64,
root_pic_index: PictureIndex,
pub prim_store: PrimitiveStore,
pub clip_store: ClipStore,
@ -66,7 +65,6 @@ pub struct FrameBuilder {
}
pub struct FrameBuildingContext<'a> {
pub scene_id: u64,
pub device_pixel_scale: DevicePixelScale,
pub scene_properties: &'a SceneProperties,
pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
@ -107,7 +105,6 @@ pub struct PictureContext {
/// Mutable state of a picture that gets modified when
/// the children are processed.
pub struct PictureState {
pub has_non_root_coord_system: bool,
pub is_cacheable: bool,
pub local_rect_changed: bool,
pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
@ -146,7 +143,6 @@ impl FrameBuilder {
screen_rect: DeviceUintRect::zero(),
window_size: DeviceUintSize::zero(),
background_color: None,
scene_id: 0,
root_pic_index: PictureIndex(0),
config: FrameBuilderConfig {
default_font_render_mode: FontRenderMode::Mono,
@ -161,7 +157,6 @@ impl FrameBuilder {
screen_rect: DeviceUintRect,
background_color: Option<ColorF>,
window_size: DeviceUintSize,
scene_id: u64,
flattener: DisplayListFlattener,
) -> Self {
FrameBuilder {
@ -172,7 +167,6 @@ impl FrameBuilder {
screen_rect,
background_color,
window_size,
scene_id,
config: flattener.config,
}
}
@ -207,7 +201,6 @@ impl FrameBuilder {
let world_rect = (self.screen_rect.to_f32() / device_pixel_scale).round_out();
let frame_context = FrameBuildingContext {
scene_id: self.scene_id,
device_pixel_scale,
scene_properties,
pipelines,

View File

@ -20,7 +20,7 @@
//! TODO(gw): Add an occupied list head, for fast iteration of the occupied list
//! to implement retain() style functionality.
use std::fmt;
use std::{fmt, u32};
use std::marker::PhantomData;
#[derive(Debug, Copy, Clone, PartialEq)]
@ -68,6 +68,14 @@ impl<M> FreeListHandle<M> {
_marker: PhantomData,
}
}
pub fn invalid() -> Self {
Self {
index: 0,
epoch: Epoch::invalid(),
_marker: PhantomData,
}
}
}
impl<M> Clone for WeakFreeListHandle<M> {

View File

@ -63,15 +63,30 @@ pub struct UpdateList<S> {
updates: Vec<Update<S>>,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct ItemUid<T> {
uid: usize,
_marker: PhantomData<T>,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone)]
pub struct Handle<T> {
index: usize,
index: u32,
epoch: Epoch,
uid: ItemUid<T>,
_marker: PhantomData<T>,
}
impl <T> Handle<T> where T: Copy {
pub fn uid(&self) -> ItemUid<T> {
self.uid
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum UpdateKind<S> {
@ -150,7 +165,7 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
type Output = T;
fn index(&self, handle: Handle<M>) -> &T {
let item = &self.items[handle.index];
let item = &self.items[handle.index as usize];
assert_eq!(item.epoch, handle.epoch);
&item.data
}
@ -160,7 +175,7 @@ impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
/// Retrieve an item from the store via handle
impl<S, T, M> ops::IndexMut<Handle<M>> for DataStore<S, T, M> {
fn index_mut(&mut self, handle: Handle<M>) -> &mut T {
let item = &mut self.items[handle.index];
let item = &mut self.items[handle.index as usize];
assert_eq!(item.epoch, handle.epoch);
&mut item.data
}
@ -182,6 +197,8 @@ pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
updates: Vec<Update<S>>,
/// The current epoch for the interner.
current_epoch: Epoch,
/// Incrementing counter for identifying stable values.
next_uid: usize,
/// The information associated with each interned
/// item that can be accessed by the interner.
local_data: Vec<Item<D>>,
@ -195,6 +212,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
free_list: Vec::new(),
updates: Vec::new(),
current_epoch: Epoch(1),
next_uid: 0,
local_data: Vec::new(),
}
}
@ -221,10 +239,10 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
// via valid handles.
if handle.epoch != self.current_epoch {
self.updates.push(Update {
index: handle.index,
index: handle.index as usize,
kind: UpdateKind::UpdateEpoch,
});
self.local_data[handle.index].epoch = self.current_epoch;
self.local_data[handle.index as usize].epoch = self.current_epoch;
}
handle.epoch = self.current_epoch;
return *handle;
@ -246,14 +264,19 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
// Generate a handle for access via the data store.
let handle = Handle {
index,
index: index as u32,
epoch: self.current_epoch,
uid: ItemUid {
uid: self.next_uid,
_marker: PhantomData,
},
_marker: PhantomData,
};
// Store this handle so the next time it is
// interned, it gets re-used.
self.map.insert(data.clone(), handle);
self.next_uid += 1;
// Create the local data for this item that is
// being interned.
@ -291,9 +314,9 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
// - Add index to the free-list for re-use.
// - Add an update to the data store to invalidate this slow.
// - Remove from the hash map.
free_list.push(handle.index);
free_list.push(handle.index as usize);
updates.push(Update {
index: handle.index,
index: handle.index as usize,
kind: UpdateKind::Remove,
});
return false;
@ -318,7 +341,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
impl<S, D, M> ops::Index<Handle<M>> for Interner<S, D, M> where S: Eq + Clone + Hash + Debug, M: Copy + Debug {
type Output = D;
fn index(&self, handle: Handle<M>) -> &D {
let item = &self.local_data[handle.index];
let item = &self.local_data[handle.index as usize];
assert_eq!(item.epoch, handle.epoch);
&item.data
}

View File

@ -109,6 +109,7 @@ mod scene_builder;
mod segment;
mod shade;
mod spatial_node;
mod surface;
mod texture_allocator;
mod texture_cache;
mod tiling;

View File

@ -3,11 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect, PicturePoint};
use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel};
use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize, RasterRect, RasterSpace};
use api::{DeviceIntRect, DevicePoint, LayoutRect, PictureToRasterTransform, LayoutPixel};
use api::{DevicePixelScale, RasterRect, RasterSpace};
use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip::ClipNodeCollector;
use clip::{ClipNodeCollector, ClipStore};
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
use euclid::{TypedScale, vec3, TypedRect};
use internal_types::{FastHashMap, PlaneSplitter};
@ -21,6 +21,7 @@ use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle};
use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use scene::{FilterOpHelpers, SceneProperties};
use smallvec::SmallVec;
use surface::SurfaceDescriptor;
use std::{mem, ops};
use tiling::RenderTargetKind;
use util::{TransformedRectKind, MatrixHelpers, MaxRect};
@ -194,59 +195,6 @@ impl PictureIdGenerator {
}
}
// Cache key that determines whether a pre-existing
// picture in the texture cache matches the content
// of the current picture.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PictureCacheKey {
// NOTE: We specifically want to ensure that we
// don't include the device space origin
// of this picture in the cache key, because
// we want the cache to remain valid as it
// is scrolled and/or translated by animation.
// This is valid while we have the restriction
// in place that only pictures that use the
// root coordinate system are cached - once
// we relax that, we'll need to consider some
// extra parameters, depending on transform.
// This is a globally unique id of the scene this picture
// is associated with, to avoid picture id collisions.
scene_id: u64,
// The unique (for the scene_id) identifier for this picture.
// TODO(gw): Currently, these will not be
// shared across new display lists,
// so will only remain valid during
// scrolling. Next step will be to
// allow deep comparisons on pictures
// between display lists, allowing
// pictures that are the same to be
// cached across display lists!
picture_id: PictureId,
// Store the rect within the unclipped device
// rect that we are actually rendering. This ensures
// that if the 'clipped' rect changes, we will see
// that the cache is invalid and re-draw the picture.
// TODO(gw): To reduce the number of invalidations that
// happen as a cached picture scrolls off-screen,
// we could round up the size of the off-screen
// targets we draw (e.g. 512 pixels). This may
// also simplify other parts of the code that
// deal with clipped/unclipped rects, such as
// the code to inflate the device rect for blurs.
pic_relative_render_rect: PictureIntRect,
// Ensure that if the overall size of the picture
// changes, the cache key will not match. This can
// happen, for example, during zooming or changes
// in device-pixel-ratio.
unclipped_size: DeviceIntSize,
}
/// Enum value describing the place of a picture in a 3D context.
#[derive(Clone, Debug)]
pub enum Picture3DContext<C> {
@ -491,6 +439,9 @@ pub struct PicturePrimitive {
/// Local clip rect for this picture.
pub local_clip_rect: LayoutRect,
/// A descriptor for this surface that can be used as a cache key.
surface_desc: Option<SurfaceDescriptor>,
}
impl PicturePrimitive {
@ -530,8 +481,32 @@ impl PicturePrimitive {
prim_list: PrimitiveList,
spatial_node_index: SpatialNodeIndex,
local_clip_rect: LayoutRect,
clip_store: &ClipStore,
) -> Self {
// For now, only create a cache descriptor for blur filters (which
// includes text shadows). We can incrementally expand this to
// handle more composite modes.
let create_cache_descriptor = match requested_composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
blur_radius > 0.0
}
Some(_) | None => {
false
}
};
let surface_desc = if create_cache_descriptor {
SurfaceDescriptor::new(
&prim_list.prim_instances,
spatial_node_index,
clip_store,
)
} else {
None
};
PicturePrimitive {
surface_desc,
prim_list,
state: None,
secondary_render_task_id: None,
@ -617,7 +592,6 @@ impl PicturePrimitive {
};
let state = PictureState {
has_non_root_coord_system: false,
is_cacheable: true,
local_rect_changed: false,
map_local_to_pic,
@ -845,6 +819,12 @@ impl PicturePrimitive {
// local_scale.is_some();
let establishes_raster_root = xf.has_perspective_component();
// TODO(gw): For now, we always raster in screen space. Soon,
// we will be able to respect the requested raster
// space, and/or override the requested raster root
// if it makes sense to.
let raster_space = RasterSpace::Screen;
let raster_spatial_node_index = if establishes_raster_root {
surface_spatial_node_index
} else {
@ -865,6 +845,17 @@ impl PicturePrimitive {
surface_index,
});
// If we have a cache key / descriptor for this surface,
// update any transforms it cares about.
if let Some(ref mut surface_desc) = self.surface_desc {
surface_desc.update(
surface_spatial_node_index,
raster_spatial_node_index,
frame_context.clip_scroll_tree,
raster_space,
);
}
surface_index
}
None => {
@ -1054,34 +1045,53 @@ impl PicturePrimitive {
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
// The clipped field is the part of the picture that is visible
// on screen. The unclipped field is the screen-space rect of
// the complete picture, if no screen / clip-chain was applied
// (this includes the extra space for blur region). To ensure
// that we draw a large enough part of the picture to get correct
// blur results, inflate that clipped area by the blur range, and
// then intersect with the total screen rect, to minimize the
// allocation size.
let device_rect = clipped
.inflate(blur_range, blur_range)
.intersection(&unclipped.to_i32())
.unwrap();
// We need to choose whether to cache this picture, or draw
// it into a temporary render target each frame. If we draw
// it into a persistently cached texture, then we want to
// draw the whole picture, without clipping it to the screen
// dimensions, so that it can be reused as it scrolls into
// view etc. However, if the unclipped size of the surface is
// too big, then it will be very expensive to draw, and may
// even be bigger than the maximum hardware render target
// size. In these cases, it's probably best to not cache the
// picture, and just draw a minimal portion of the picture
// (clipped to screen bounds) to a temporary target each frame.
let uv_rect_kind = calculate_uv_rect_kind(
&pic_rect,
&transform,
&device_rect,
frame_context.device_pixel_scale,
);
// TODO(gw): This size is quite arbitrary - we should do some
// profiling / telemetry to see when it makes sense
// to cache a picture.
const MAX_CACHE_SIZE: f32 = 2048.0;
let too_big_to_cache = unclipped.size.width > MAX_CACHE_SIZE ||
unclipped.size.height > MAX_CACHE_SIZE;
// If we are drawing a blur that has primitives or clips that contain
// a complex coordinate system, don't bother caching them (for now).
// It's likely that they are animating and caching may not help here
// anyway. In the future we should relax this a bit, so that we can
// cache tasks with complex coordinate systems if we detect the
// relevant transforms haven't changed from frame to frame.
if pic_state_for_children.has_non_root_coord_system ||
// If we can't create a valid cache key for this descriptor (e.g.
// due to it referencing old non-interned style primitives), then
// don't try to cache it.
let has_valid_cache_key = self.surface_desc.is_some();
if !has_valid_cache_key ||
too_big_to_cache ||
!pic_state_for_children.is_cacheable {
// The clipped field is the part of the picture that is visible
// on screen. The unclipped field is the screen-space rect of
// the complete picture, if no screen / clip-chain was applied
// (this includes the extra space for blur region). To ensure
// that we draw a large enough part of the picture to get correct
// blur results, inflate that clipped area by the blur range, and
// then intersect with the total screen rect, to minimize the
// allocation size.
let device_rect = clipped
.inflate(blur_range, blur_range)
.intersection(&unclipped.to_i32())
.unwrap();
let uv_rect_kind = calculate_uv_rect_kind(
&pic_rect,
&transform,
&device_rect,
frame_context.device_pixel_scale,
);
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, device_rect.size),
unclipped.size,
@ -1108,30 +1118,29 @@ impl PicturePrimitive {
PictureSurface::RenderTask(render_task_id)
} else {
// Get the relative clipped rect within the overall prim rect, that
// forms part of the cache key.
let pic_relative_render_rect = PictureIntRect::new(
PictureIntPoint::new(
device_rect.origin.x - unclipped.origin.x as i32,
device_rect.origin.y - unclipped.origin.y as i32,
),
PictureIntSize::new(
device_rect.size.width,
device_rect.size.height,
),
);
// Request a render task that will cache the output in the
// texture cache.
let device_rect = unclipped.to_i32();
let uv_rect_kind = calculate_uv_rect_kind(
&pic_rect,
&transform,
&device_rect,
frame_context.device_pixel_scale,
);
// TODO(gw): Probably worth changing the render task caching API
// so that we don't need to always clone the key.
let cache_key = self.surface_desc
.as_ref()
.expect("bug: no cache key for surface")
.cache_key
.clone();
let cache_item = frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: device_rect.size,
kind: RenderTaskCacheKeyKind::Picture(PictureCacheKey {
scene_id: frame_context.scene_id,
picture_id: self.id,
unclipped_size: unclipped.size.to_i32(),
pic_relative_render_rect,
}),
kind: RenderTaskCacheKeyKind::Picture(cache_key),
},
frame_state.gpu_cache,
frame_state.render_tasks,

View File

@ -11,7 +11,7 @@ use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, Wor
use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers, LayoutVector2DAu};
use app_units::Au;
use border::{get_max_scale_for_border, build_border_instances, create_normal_border_prim};
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex};
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
use euclid::{TypedTransform3D, TypedRect, TypedScale};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
@ -121,6 +121,39 @@ pub enum CoordinateSpaceMapping<F, T> {
Transform(TypedTransform3D<f32, F, T>),
}
impl<F, T> CoordinateSpaceMapping<F, T> {
pub fn new(
ref_spatial_node_index: SpatialNodeIndex,
target_node_index: SpatialNodeIndex,
clip_scroll_tree: &ClipScrollTree,
) -> Self {
let spatial_nodes = &clip_scroll_tree.spatial_nodes;
let ref_spatial_node = &spatial_nodes[ref_spatial_node_index.0];
let target_spatial_node = &spatial_nodes[target_node_index.0];
if ref_spatial_node_index == target_node_index {
CoordinateSpaceMapping::Local
} else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
CoordinateSpaceMapping::ScaleOffset(
ref_spatial_node.coordinate_system_relative_scale_offset
.inverse()
.accumulate(
&target_spatial_node.coordinate_system_relative_scale_offset
)
)
} else {
let transform = clip_scroll_tree.get_relative_transform(
target_node_index,
ref_spatial_node_index,
).expect("bug: should have already been culled");
CoordinateSpaceMapping::Transform(
transform.with_source::<F>().with_destination::<T>()
)
}
}
}
#[derive(Debug)]
pub struct SpaceMapper<F, T> {
kind: CoordinateSpaceMapping<F, T>,
@ -159,31 +192,13 @@ impl<F, T> SpaceMapper<F, T> where F: fmt::Debug {
clip_scroll_tree: &ClipScrollTree,
) {
if target_node_index != self.current_target_spatial_node_index {
let spatial_nodes = &clip_scroll_tree.spatial_nodes;
let ref_spatial_node = &spatial_nodes[self.ref_spatial_node_index.0];
let target_spatial_node = &spatial_nodes[target_node_index.0];
self.current_target_spatial_node_index = target_node_index;
self.kind = if self.ref_spatial_node_index == target_node_index {
CoordinateSpaceMapping::Local
} else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
CoordinateSpaceMapping::ScaleOffset(
ref_spatial_node.coordinate_system_relative_scale_offset
.inverse()
.accumulate(
&target_spatial_node.coordinate_system_relative_scale_offset
)
)
} else {
let transform = clip_scroll_tree.get_relative_transform(
target_node_index,
self.ref_spatial_node_index,
).expect("bug: should have already been culled");
CoordinateSpaceMapping::Transform(
transform.with_source::<F>().with_destination::<T>()
)
};
self.kind = CoordinateSpaceMapping::new(
self.ref_spatial_node_index,
target_node_index,
clip_scroll_tree,
);
}
}
@ -580,13 +595,14 @@ impl PrimitiveTemplate {
// Type definitions for interning primitives.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct PrimitiveDataMarker;
pub type PrimitiveDataStore = intern::DataStore<PrimitiveKey, PrimitiveTemplate, PrimitiveDataMarker>;
pub type PrimitiveDataHandle = intern::Handle<PrimitiveDataMarker>;
pub type PrimitiveDataUpdateList = intern::UpdateList<PrimitiveKey>;
pub type PrimitiveDataInterner = intern::Interner<PrimitiveKey, PrimitiveSceneData, PrimitiveDataMarker>;
pub type PrimitiveUid = intern::ItemUid<PrimitiveDataMarker>;
// Maintains a list of opacity bindings that have been collapsed into
// the color of a single primitive. This is an important optimization
@ -2137,8 +2153,6 @@ impl PrimitiveStore {
Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => {
// Mark whether this picture has a complex coordinate system.
let is_passthrough = pic_context_for_children.is_passthrough;
pic_state_for_children.has_non_root_coord_system |=
prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root();
self.prepare_primitives(
&mut prim_list,
@ -2259,8 +2273,6 @@ impl PrimitiveStore {
);
}
pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system;
prim_instance.combined_local_clip_rect = if pic_context.apply_local_clip_rect {
clip_chain.local_clip_rect
} else {
@ -2455,11 +2467,6 @@ impl PrimitiveStore {
prim_instance.spatial_node_index,
);
// Mark whether this picture contains any complex coordinate
// systems, due to either the scroll node or the clip-chain.
pic_state.has_non_root_coord_system |=
spatial_node.coordinate_system_id != CoordinateSystemId::root();
pic_state.map_local_to_pic.set_target_spatial_node(
prim_instance.spatial_node_index,
frame_context.clip_scroll_tree,

View File

@ -477,7 +477,6 @@ struct PlainRenderBackend {
frame_config: FrameBuilderConfig,
documents: FastHashMap<DocumentId, DocumentView>,
resources: PlainResources,
last_scene_id: u64,
}
/// The render backend is responsible for transforming high level display lists into
@ -507,8 +506,6 @@ pub struct RenderBackend {
sampler: Option<Box<AsyncPropertySampler + Send>>,
size_of_op: Option<VoidPtrToSizeFn>,
namespace_alloc_by_client: bool,
last_scene_id: u64,
}
impl RenderBackend {
@ -545,7 +542,6 @@ impl RenderBackend {
recorder,
sampler,
size_of_op,
last_scene_id: 0,
namespace_alloc_by_client,
}
}
@ -658,12 +654,6 @@ impl RenderBackend {
IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
}
pub fn make_unique_scene_id(&mut self) -> u64 {
// 2^64 scenes ought to be enough for anybody!
self.last_scene_id += 1;
self.last_scene_id
}
pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
let mut frame_counter: u32 = 0;
let mut keep_going = true;
@ -1046,7 +1036,6 @@ impl RenderBackend {
return;
}
let scene_id = self.make_unique_scene_id();
let doc = self.documents.get_mut(&document_id).unwrap();
if txn.should_build_scene() {
@ -1054,7 +1043,6 @@ impl RenderBackend {
view: doc.view.clone(),
font_instances: self.resource_cache.get_font_instances(),
output_pipelines: doc.output_pipelines.clone(),
scene_id,
});
}
@ -1448,7 +1436,6 @@ impl RenderBackend {
.map(|(id, doc)| (*id, doc.view.clone()))
.collect(),
resources,
last_scene_id: self.last_scene_id,
};
config.serialize(&backend, "backend");
@ -1509,7 +1496,6 @@ impl RenderBackend {
let mut scenes_to_build = Vec::new();
let mut last_scene_id = backend.last_scene_id;
for (id, view) in backend.documents {
debug!("\tdocument {:?}", id);
let scene_name = format!("scene-{}-{}", (id.0).0, id.1);
@ -1568,8 +1554,6 @@ impl RenderBackend {
None => true,
};
last_scene_id += 1;
scenes_to_build.push(LoadScene {
document_id: id,
scene: doc.scene.clone(),
@ -1577,7 +1561,6 @@ impl RenderBackend {
config: self.frame_config.clone(),
output_pipelines: doc.output_pipelines.clone(),
font_instances: self.resource_cache.get_font_instances(),
scene_id: last_scene_id,
build_frame,
doc_resources,
});

View File

@ -21,13 +21,13 @@ use gpu_types::{BorderInstance, ImageSource, UvRectKind};
use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
use picture::PictureCacheKey;
use prim_store::{PictureIndex, ImageCacheKey, LineDecorationCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use render_backend::FrameId;
use resource_cache::{CacheItem, ResourceCache};
use std::{cmp, ops, usize, f32, i32};
use surface::SurfaceCacheKey;
use std::{cmp, ops, mem, usize, f32, i32};
use texture_cache::{TextureCache, TextureCacheHandle, Eviction};
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind};
@ -1076,7 +1076,7 @@ pub enum RenderTaskCacheKeyKind {
Image(ImageCacheKey),
#[allow(dead_code)]
Glyph(GpuGlyphCacheKey),
Picture(PictureCacheKey),
Picture(SurfaceCacheKey),
BorderEdge(BorderEdgeCacheKey),
BorderCorner(BorderCornerCacheKey),
LineDecoration(LineDecorationCacheKey),
@ -1145,19 +1145,18 @@ impl RenderTaskCache {
// Nonetheless, we should remove stale entries
// from here so that this hash map doesn't
// grow indefinitely!
let mut keys_to_remove = Vec::new();
let cache_entries = &mut self.cache_entries;
for (key, handle) in &self.map {
let entry = self.cache_entries.get(handle);
if !texture_cache.is_allocated(&entry.handle) {
keys_to_remove.push(key.clone())
self.map.retain(|_, handle| {
let retain = texture_cache.is_allocated(
&cache_entries.get(handle).handle,
);
if !retain {
let handle = mem::replace(handle, FreeListHandle::invalid());
cache_entries.free(handle);
}
}
for key in &keys_to_remove {
let handle = self.map.remove(key).unwrap();
self.cache_entries.free(handle);
}
retain
});
}
pub fn update(

View File

@ -97,14 +97,12 @@ pub struct SceneRequest {
pub view: DocumentView,
pub font_instances: FontInstanceMap,
pub output_pipelines: FastHashSet<PipelineId>,
pub scene_id: u64,
}
#[cfg(feature = "replay")]
pub struct LoadScene {
pub document_id: DocumentId,
pub scene: Scene,
pub scene_id: u64,
pub output_pipelines: FastHashSet<PipelineId>,
pub font_instances: FontInstanceMap,
pub view: DocumentView,
@ -330,7 +328,6 @@ impl SceneBuilder {
&item.output_pipelines,
&self.config,
&mut new_scene,
item.scene_id,
&mut self.picture_id_generator,
&mut item.doc_resources,
);
@ -435,7 +432,6 @@ impl SceneBuilder {
&request.output_pipelines,
&self.config,
&mut new_scene,
request.scene_id,
&mut self.picture_id_generator,
&mut doc.resources,
);

View File

@ -0,0 +1,331 @@
/* 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/. */
use api::{LayoutPixel, PicturePixel, RasterSpace};
use clip::{ClipChainId, ClipStore, ClipUid};
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
use euclid::TypedTransform3D;
use internal_types::FastHashSet;
use prim_store::{CoordinateSpaceMapping, PrimitiveUid, PrimitiveInstance, PrimitiveInstanceKind};
use std::hash;
use util::ScaleOffset;
/*
Notes for future implementation work on surface caching:
State that can affect the contents of a cached surface:
Primitives
These are handled by the PrimitiveUid value. The structure interning
code during scene building guarantees that each PrimitiveUid will
represent a unique identifier for the content of this primitive.
Clip chains
Similarly, the ClipUid value uniquely identifies the contents of
a clip node.
Transforms
Each picture contains a list of transforms that affect the content
of the picture itself. The value of the surface transform relative
to the raster root transform is only relevant if the picture is
being rasterized in screen-space.
External images
An external image (e.g. video) can change the contents of a picture
without a scene build occurring. We don't need to handle this yet,
but once images support interning and caching, we'll need to include
a list of external image dependencies in the cache key.
Property animation
Transform animations are handled by the transforms case above. We don't
need to handle opacity animations yet, since the interning and picture
caching doesn't support images and / or solid rects. Once those
primitives are ported, we'll need a list of property animation keys
that a surface depends on.
*/
// Matches the definition of SK_ScalarNearlyZero in Skia.
// TODO(gw): Some testing to see what's reasonable for this value
// to avoid invalidating the cache for minor changes.
const QUANTIZE_SCALE: f32 = 4096.0;
fn quantize(value: f32) -> f32 {
(value * QUANTIZE_SCALE).round() / QUANTIZE_SCALE
}
/// A quantized, hashable version of util::ScaleOffset that
/// can be used as a cache key.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct ScaleOffsetKey {
scale_x: f32,
scale_y: f32,
offset_x: f32,
offset_y: f32,
}
impl ScaleOffsetKey {
fn new(scale_offset: &ScaleOffset) -> Self {
// TODO(gw): Since these are quantized, it might make sense in the future to
// convert these to ints to remove the need for custom hash impl.
ScaleOffsetKey {
scale_x: quantize(scale_offset.scale.x),
scale_y: quantize(scale_offset.scale.y),
offset_x: quantize(scale_offset.offset.x),
offset_y: quantize(scale_offset.offset.y),
}
}
}
impl Eq for ScaleOffsetKey {}
impl hash::Hash for ScaleOffsetKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.scale_x.to_bits().hash(state);
self.scale_y.to_bits().hash(state);
self.offset_x.to_bits().hash(state);
self.offset_y.to_bits().hash(state);
}
}
/// A quantized, hashable version of PictureTransform that
/// can be used as a cache key.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct MatrixKey {
values: [f32; 16],
}
impl MatrixKey {
fn new<Src, Dst>(transform: &TypedTransform3D<f32, Src, Dst>) -> Self {
let mut values = transform.to_row_major_array();
// TODO(gw): Since these are quantized, it might make sense in the future to
// convert these to ints to remove the need for custom hash impl.
for value in &mut values {
*value = quantize(*value);
}
MatrixKey {
values,
}
}
}
impl Eq for MatrixKey {}
impl hash::Hash for MatrixKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
for value in &self.values {
value.to_bits().hash(state);
}
}
}
/// A quantized, hashable version of CoordinateSpaceMapping that
/// can be used as a cache key.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub enum TransformKey {
Local,
ScaleOffset(ScaleOffsetKey),
Transform(MatrixKey),
}
impl TransformKey {
pub fn local() -> Self {
TransformKey::Local
}
}
impl<F, T> From<CoordinateSpaceMapping<F, T>> for TransformKey {
/// Construct a transform cache key from a coordinate space mapping.
fn from(mapping: CoordinateSpaceMapping<F, T>) -> TransformKey {
match mapping {
CoordinateSpaceMapping::Local => {
TransformKey::Local
}
CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
TransformKey::ScaleOffset(ScaleOffsetKey::new(scale_offset))
}
CoordinateSpaceMapping::Transform(ref transform) => {
TransformKey::Transform(MatrixKey::new(transform))
}
}
}
}
/// This key uniquely identifies the contents of a cached
/// picture surface.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct SurfaceCacheKey {
/// The list of primitives that are part of this surface.
/// The uid uniquely identifies the content of the primitive.
pub primitive_ids: Vec<PrimitiveUid>,
/// The list of clips that affect the primitives on this surface.
/// The uid uniquely identifies the content of the clip.
pub clip_ids: Vec<ClipUid>,
/// A list of transforms that can affect the contents of primitives
/// and/or clips on this picture surface.
pub transforms: Vec<TransformKey>,
/// Information about the transform of the picture surface itself. If we are
/// drawing in screen-space, then the value of this affects the contents
/// of the cached surface. If we're drawing in local space, then the transform
/// of the surface in its parent is not relevant to the contents.
pub raster_transform: TransformKey,
}
/// A descriptor for the contents of a picture surface.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SurfaceDescriptor {
/// The cache key identifies the contents or primitives, clips and the current
/// state of relevant transforms.
pub cache_key: SurfaceCacheKey,
/// The spatial nodes array is used to update the cache key each frame, without
/// relying on the value of a spatial node index (these may change, if other parts of the
/// display list result in a different clip-scroll tree).
pub spatial_nodes: Vec<SpatialNodeIndex>,
}
impl SurfaceDescriptor {
/// Construct a new surface descriptor for this list of primitives.
/// This method is fallible - it will return None if this picture
/// contains primitives that can't currently be cached safely.
pub fn new(
prim_instances: &[PrimitiveInstance],
pic_spatial_node_index: SpatialNodeIndex,
clip_store: &ClipStore,
) -> Option<Self> {
let mut relevant_spatial_nodes = FastHashSet::default();
let mut primitive_ids = Vec::new();
let mut clip_ids = Vec::new();
for prim_instance in prim_instances {
// If the prim has the same spatial node as the surface,
// then the content can't move relative to it, so we don't
// care if the transform changes.
if pic_spatial_node_index != prim_instance.spatial_node_index {
relevant_spatial_nodes.insert(prim_instance.spatial_node_index);
}
// Collect clip node transforms that we care about.
let mut clip_chain_id = prim_instance.clip_chain_id;
while clip_chain_id != ClipChainId::NONE {
let clip_chain_node = &clip_store.clip_chain_nodes[clip_chain_id.0 as usize];
// TODO(gw): This needs to be a bit more careful once we create
// descriptors for pictures that might be pass-through.
// Ignore clip chain nodes that will be handled by the clip node collector.
if clip_chain_node.spatial_node_index > pic_spatial_node_index {
relevant_spatial_nodes.insert(prim_instance.spatial_node_index);
clip_ids.push(clip_chain_node.handle.uid());
}
clip_chain_id = clip_chain_node.parent_clip_chain_id;
}
// For now, we only handle interned primitives. If we encounter
// a legacy primitive or picture, then fail to create a cache
// descriptor.
match prim_instance.kind {
PrimitiveInstanceKind::Picture { .. } |
PrimitiveInstanceKind::LegacyPrimitive { .. } => {
return None;
}
PrimitiveInstanceKind::LineDecoration { .. } |
PrimitiveInstanceKind::TextRun { .. } |
PrimitiveInstanceKind::Clear => {}
}
// Record the unique identifier for the content represented
// by this primitive.
primitive_ids.push(prim_instance.prim_data_handle.uid());
}
// Get a list of spatial nodes that are relevant for the contents
// of this picture. Sort them to ensure that for a given clip-scroll
// tree, we end up with the same transform ordering.
let mut spatial_nodes: Vec<SpatialNodeIndex> = relevant_spatial_nodes
.into_iter()
.collect();
spatial_nodes.sort();
// Create the array of transform values that gets built each
// frame during update.
let transforms = vec![TransformKey::local(); spatial_nodes.len()];
let cache_key = SurfaceCacheKey {
primitive_ids,
clip_ids,
transforms,
raster_transform: TransformKey::local(),
};
Some(SurfaceDescriptor {
cache_key,
spatial_nodes,
})
}
/// Update the transforms for this cache key, by extracting the current
/// values from the clip-scroll tree state.
pub fn update(
&mut self,
surface_spatial_node_index: SpatialNodeIndex,
raster_spatial_node_index: SpatialNodeIndex,
clip_scroll_tree: &ClipScrollTree,
raster_space: RasterSpace,
) {
// Update the state of the transform for compositing this picture.
self.cache_key.raster_transform = match raster_space {
RasterSpace::Screen => {
// In general cases, if we're rasterizing a picture in screen space, then the
// value of the surface spatial node will affect the contents of the picture
// itself. However, if the surface and raster spatial nodes are in the same
// coordinate system (which is the common case!) then we are effectively drawing
// in a local space anyway, so don't care about that transform for the purposes
// of validating the surface cache contents.
let raster_spatial_node = &clip_scroll_tree.spatial_nodes[raster_spatial_node_index.0];
let surface_spatial_node = &clip_scroll_tree.spatial_nodes[surface_spatial_node_index.0];
let mut key = CoordinateSpaceMapping::<LayoutPixel, PicturePixel>::new(
raster_spatial_node_index,
surface_spatial_node_index,
clip_scroll_tree,
).into();
if let TransformKey::ScaleOffset(ref mut key) = key {
if raster_spatial_node.coordinate_system_id == surface_spatial_node.coordinate_system_id {
key.offset_x = 0.0;
key.offset_y = 0.0;
}
}
key
}
RasterSpace::Local(..) => {
TransformKey::local()
}
};
// Update the state of any relevant transforms for this picture.
for (spatial_node_index, transform) in self.spatial_nodes
.iter()
.zip(self.cache_key.transforms.iter_mut())
{
*transform = CoordinateSpaceMapping::<LayoutPixel, PicturePixel>::new(
raster_spatial_node_index,
*spatial_node_index,
clip_scroll_tree,
).into();
}
}
}

View File

@ -1 +1 @@
6e445b0422075f66be4a2009745cad3fefe3429f
b8829189cfc1769550c9ab4a4bb994e28621f009

View File

@ -9,23 +9,11 @@
#include "mozilla/Bootstrap.h"
#include "mozilla/WindowsDllBlocklist.h"
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
using namespace mozilla;
int
main(int argc, char *argv[])
{
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
std::string err;
if (!mozilla::EarlyStartMacSandboxIfEnabled(argc, argv, err)) {
fprintf(stderr, "Sandbox error: %s\n", err.c_str());
MOZ_CRASH("Sandbox initialization failed");
}
#endif
#ifdef HAS_DLL_BLOCKLIST
DllBlocklist_Initialize(eDllBlocklistInitFlagIsChildProcess);
#endif

View File

@ -2190,7 +2190,7 @@ BytecodeEmitter::isRunOnceLambda()
}
bool
BytecodeEmitter::allocateResumeIndexForCurrentOffset(uint32_t* resumeIndex)
BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset, uint32_t* resumeIndex)
{
static constexpr uint32_t MaxResumeIndex = JS_BITMASK(24);
@ -2203,7 +2203,26 @@ BytecodeEmitter::allocateResumeIndexForCurrentOffset(uint32_t* resumeIndex)
return false;
}
return resumeOffsetList.append(offset());
return resumeOffsetList.append(offset);
}
bool
BytecodeEmitter::allocateResumeIndexRange(mozilla::Span<ptrdiff_t> offsets,
uint32_t* firstResumeIndex)
{
*firstResumeIndex = 0;
for (size_t i = 0, len = offsets.size(); i < len; i++) {
uint32_t resumeIndex;
if (!allocateResumeIndex(offsets[i], &resumeIndex)) {
return false;
}
if (i == 0) {
*firstResumeIndex = resumeIndex;
}
}
return true;
}
bool
@ -2225,7 +2244,7 @@ BytecodeEmitter::emitYieldOp(JSOp op)
}
uint32_t resumeIndex;
if (!allocateResumeIndexForCurrentOffset(&resumeIndex)) {
if (!allocateResumeIndex(offset(), &resumeIndex)) {
return false;
}
@ -4458,7 +4477,7 @@ BytecodeEmitter::emitGoSub(JumpList* jump)
}
uint32_t resumeIndex;
if (!allocateResumeIndexForCurrentOffset(&resumeIndex)) {
if (!allocateResumeIndex(offset(), &resumeIndex)) {
return false;
}

View File

@ -617,7 +617,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
}
MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope);
MOZ_MUST_USE bool allocateResumeIndexForCurrentOffset(uint32_t* resumeIndex);
MOZ_MUST_USE bool allocateResumeIndex(ptrdiff_t offset, uint32_t* resumeIndex);
MOZ_MUST_USE bool allocateResumeIndexRange(mozilla::Span<ptrdiff_t> offsets,
uint32_t* firstResumeIndex);
MOZ_MUST_USE bool emitInitialYield(UnaryNode* yieldNode);
MOZ_MUST_USE bool emitYield(UnaryNode* yieldNode);

View File

@ -6,6 +6,8 @@
#include "frontend/SwitchEmitter.h"
#include "mozilla/Span.h"
#include "jsutil.h"
#include "frontend/BytecodeEmitter.h"
@ -202,9 +204,6 @@ SwitchEmitter::emitTable(const TableGenerator& tableGen)
top_ = bce_->offset();
// The note has one offset that tells total switch code length.
// 3 offsets (len, low, high) before the table, 1 per entry.
size_t switchSize = size_t(JUMP_OFFSET_LEN * (3 + tableGen.tableLength()));
if (!bce_->newSrcNote2(SRC_TABLESWITCH, 0, &noteIndex_)) {
return false;
}
@ -215,7 +214,7 @@ SwitchEmitter::emitTable(const TableGenerator& tableGen)
}
MOZ_ASSERT(top_ == bce_->offset());
if (!bce_->emitN(JSOP_TABLESWITCH, switchSize)) {
if (!bce_->emitN(JSOP_TABLESWITCH, JSOP_TABLESWITCH_LENGTH - sizeof(jsbytecode))) {
return false;
}
@ -452,12 +451,21 @@ SwitchEmitter::emitEnd()
// Skip over the already-initialized switch bounds.
pc += 2 * JUMP_OFFSET_LEN;
// Fill in the jump table, if there is one.
// Use the 'default' offset for missing cases.
for (uint32_t i = 0, length = caseOffsets_.length(); i < length; i++) {
ptrdiff_t off = caseOffsets_[i];
SET_JUMP_OFFSET(pc, off == 0 ? 0 : off - top_);
pc += JUMP_OFFSET_LEN;
if (caseOffsets_[i] == 0) {
caseOffsets_[i] = defaultJumpTargetOffset_.offset;
}
}
// Allocate resume index range.
uint32_t firstResumeIndex = 0;
mozilla::Span<ptrdiff_t> offsets = mozilla::MakeSpan(caseOffsets_.begin(),
caseOffsets_.end());
if (!bce_->allocateResumeIndexRange(offsets, &firstResumeIndex)) {
return false;
}
SET_RESUMEINDEX(pc, firstResumeIndex);
}
// Patch breaks before leaving the scope, as all breaks are under the

View File

@ -353,7 +353,7 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1 //BRDA:$,0,2,0
case 3:
l.push('1'); //DA:$,0
case 5:
@ -501,7 +501,7 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,0 //BRDA:$,0,3,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
case 0:
l.push('0'); //DA:$,0
case 1:

View File

@ -4489,7 +4489,7 @@ BaselineCompiler::emit_JSOP_TABLESWITCH()
frame.popRegsAndSync(1);
// Call IC.
ICTableSwitch::Compiler compiler(cx, pc);
ICTableSwitch::Compiler compiler(cx, script, pc);
return emitOpIC(compiler.getStub(&stubSpace_));
}

View File

@ -5208,13 +5208,7 @@ ICTableSwitch::Compiler::getStub(ICStubSpace* space)
jsbytecode* defaultpc = pc_ + GET_JUMP_OFFSET(pc_);
for (int32_t i = 0; i < length; i++) {
int32_t off = GET_JUMP_OFFSET(pc);
if (off) {
table[i] = pc_ + off;
} else {
table[i] = defaultpc;
}
pc += JUMP_OFFSET_LEN;
table[i] = script_->tableSwitchCasePC(pc_, i);
}
return newStub<ICTableSwitch>(space, code, table, low, length, defaultpc);

View File

@ -2638,11 +2638,12 @@ class ICTableSwitch : public ICStub
class Compiler : public ICStubCompiler {
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
JSScript* script_;
jsbytecode* pc_;
public:
Compiler(JSContext* cx, jsbytecode* pc)
: ICStubCompiler(cx, ICStub::TableSwitch), pc_(pc)
Compiler(JSContext* cx, JSScript* script, jsbytecode* pc)
: ICStubCompiler(cx, ICStub::TableSwitch), script_(script), pc_(pc)
{}
ICStub* getStub(ICStubSpace* space) override;

View File

@ -105,13 +105,14 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
infos_[defaultOffset].init(stackDepth);
infos_[defaultOffset].jumpTarget = true;
for (int32_t i = low; i <= high; i++) {
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
if (targetOffset != offset) {
uint32_t ncases = high - low + 1;
for (uint32_t i = 0; i < ncases; i++) {
unsigned targetOffset = script_->tableSwitchCaseOffset(pc, i);
if (targetOffset != defaultOffset) {
infos_[targetOffset].init(stackDepth);
infos_[targetOffset].jumpTarget = true;
}
pc2 += JUMP_OFFSET_LEN;
}
break;
}

View File

@ -1941,21 +1941,18 @@ ControlFlowGenerator::processTableSwitch(JSOp op, jssrcnote* sn)
}
// Create cases
jsbytecode* casepc = nullptr;
for (int i = 0; i < high-low+1; i++) {
if (!alloc().ensureBallast()) {
return ControlStatus::Error;
}
casepc = pc + GET_JUMP_OFFSET(pc2);
jsbytecode* casepc = script->tableSwitchCasePC(pc, i);
MOZ_ASSERT(casepc >= pc && casepc <= exitpc);
CFGBlock* caseBlock;
if (casepc == pc) {
// If the casepc equals the current pc, it is not a written case,
// but a filled gap. That way we can use a tableswitch instead of
// condswitch, even if not all numbers are consecutive.
// In that case this block goes to the default case
if (casepc == defaultpc) {
// This is a missing case. Jump to the 'default' target.
caseBlock = CFGBlock::New(alloc(), defaultpc);
caseBlock->setStopIns(CFGGoto::New(alloc(), defaultcase));
} else {

View File

@ -333,7 +333,7 @@ MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 0, JSEXN_SYNTAXERR, "too many constructor
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 0, JSEXN_SYNTAXERR, "more than one switch default")
MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 0, JSEXN_SYNTAXERR, "too many function arguments")
MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local variables")
MSG_DEF(JSMSG_TOO_MANY_RESUME_INDEXES, 0, JSEXN_SYNTAXERR, "too many yield/await/finally locations")
MSG_DEF(JSMSG_TOO_MANY_RESUME_INDEXES, 0, JSEXN_SYNTAXERR, "too many yield/await/finally/case locations")
MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}")
MSG_DEF(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, 1, JSEXN_SYNTAXERR, "unexpected token: {0}")

Some files were not shown because too many files have changed in this diff Show More