mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 16:22:00 +00:00
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:
commit
ff7bbb9936
@ -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}"
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
},
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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 = [
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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 = () => {
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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 });
|
||||
};
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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");
|
||||
});
|
@ -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">
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"],
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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:
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -47,7 +47,7 @@ interface nsIDocShellTreeItem : nsISupports
|
||||
/*
|
||||
The type this item is.
|
||||
*/
|
||||
attribute long itemType;
|
||||
readonly attribute long itemType;
|
||||
[noscript,notxpcom,nostdcall] long ItemType();
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
10
dom/base/crashtests/1505875.html
Normal file
10
dom/base/crashtests/1505875.html
Normal 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>
|
@ -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
|
||||
|
@ -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");
|
||||
|
98
dom/media/ipc/GpuDecoderModule.cpp
Normal file
98
dom/media/ipc/GpuDecoderModule.cpp
Normal 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
|
50
dom/media/ipc/GpuDecoderModule.h
Normal file
50
dom/media/ipc/GpuDecoderModule.h
Normal 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
|
47
dom/media/ipc/IRemoteDecoderChild.h
Normal file
47
dom/media/ipc/IRemoteDecoderChild.h
Normal 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
|
23
dom/media/ipc/PMediaDecoderParams.ipdlh
Normal file
23
dom/media/ipc/PMediaDecoderParams.ipdlh
Normal 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
|
@ -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.
|
||||
|
140
dom/media/ipc/RemoteMediaDataDecoder.cpp
Normal file
140
dom/media/ipc/RemoteMediaDataDecoder.cpp
Normal 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
|
66
dom/media/ipc/RemoteMediaDataDecoder.h
Normal file
66
dom/media/ipc/RemoteMediaDataDecoder.h
Normal 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
|
@ -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
|
@ -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
|
@ -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();
|
||||
|
@ -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',
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -7,12 +7,6 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "XSLT")
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'txIEXSLTRegExFunctions.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'content_xslt'
|
||||
|
||||
EXPORTS += [
|
||||
'nsIDocumentTransformer.h',
|
||||
]
|
||||
|
@ -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);
|
||||
};
|
@ -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'
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]);
|
40
dom/xslt/xslt/txEXSLTRegExFunctions.jsm
Normal file
40
dom/xslt/xslt/txEXSLTRegExFunctions.jsm
Normal 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",
|
||||
];
|
@ -1,2 +0,0 @@
|
||||
component {18a03189-067b-4978-b4f1-bafe35292ed6} txEXSLTRegExFunctions.js
|
||||
contract @mozilla.org/exslt/regexp;1 {18a03189-067b-4978-b4f1-bafe35292ed6}
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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();
|
||||
|
@ -438,7 +438,11 @@ function runContinuation(testFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
driveTest();
|
||||
try {
|
||||
driveTest();
|
||||
} catch (ex) {
|
||||
SimpleTest.ok(false, "APZ test continuation failed with exception: " + ex);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) => {
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ mod scene_builder;
|
||||
mod segment;
|
||||
mod shade;
|
||||
mod spatial_node;
|
||||
mod surface;
|
||||
mod texture_allocator;
|
||||
mod texture_cache;
|
||||
mod tiling;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
);
|
||||
|
331
gfx/webrender/src/surface.rs
Normal file
331
gfx/webrender/src/surface.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
6e445b0422075f66be4a2009745cad3fefe3429f
|
||||
b8829189cfc1769550c9ab4a4bb994e28621f009
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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, ¬eIndex_)) {
|
||||
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
|
||||
|
@ -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:
|
||||
|
@ -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_));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user