mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 04:15:43 +00:00
Merge m-c to inbound, a=merge CLOSED TREE
This commit is contained in:
commit
0adc72695e
@ -65,6 +65,7 @@ const URLBAR_SELECTED_RESULT_TYPES = {
|
||||
visiturl: 8,
|
||||
remotetab: 9,
|
||||
extension: 10,
|
||||
"preloaded-top-site": 11,
|
||||
};
|
||||
|
||||
function getOpenTabsAndWinsCounts() {
|
||||
@ -250,7 +251,7 @@ let urlbarListener = {
|
||||
}
|
||||
if (!actionType) {
|
||||
let styles = new Set(controller.getStyleAt(idx).split(/\s+/));
|
||||
let style = ["autofill", "tag", "bookmark"].find(s => styles.has(s));
|
||||
let style = ["preloaded-top-site", "autofill", "tag", "bookmark"].find(s => styles.has(s));
|
||||
actionType = style || "history";
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,7 @@
|
||||
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/netmonitor.css"/>
|
||||
<script src="chrome://devtools/content/shared/theme-switching.js">
|
||||
</script>
|
||||
<script src="chrome://devtools/content/shared/theme-switching.js"></script>
|
||||
</head>
|
||||
<body class="theme-sidebar" role="application">
|
||||
<div id="mount"></div>
|
||||
|
@ -12,10 +12,10 @@
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
const { bootstrap } = require("devtools-launchpad");
|
||||
const { EventEmitter } = require("devtools-sham-modules");
|
||||
const { EventEmitter } = require("devtools-modules");
|
||||
const { configureStore } = require("./src/utils/create-store");
|
||||
|
||||
// require("./src/assets/styles/netmonitor.css");
|
||||
require("./src/assets/styles/netmonitor.css");
|
||||
|
||||
EventEmitter.decorate(window);
|
||||
|
||||
|
@ -151,7 +151,7 @@ body,
|
||||
}
|
||||
|
||||
.requests-list-perf-notice-button::before {
|
||||
background-image: url(images/profiler-stopwatch.svg);
|
||||
background-image: url(chrome://devtools/skin/images/profiler-stopwatch.svg);
|
||||
}
|
||||
|
||||
.requests-list-reload-notice-button {
|
||||
@ -702,7 +702,8 @@ body,
|
||||
.properties-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.properties-view {
|
||||
@ -1018,7 +1019,7 @@ body,
|
||||
}
|
||||
|
||||
.security-warning-icon {
|
||||
background-image: url(images/alerticon-warning.png);
|
||||
background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
|
||||
background-size: 13px 12px;
|
||||
margin-inline-start: 5px;
|
||||
vertical-align: top;
|
||||
@ -1028,7 +1029,7 @@ body,
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.security-warning-icon {
|
||||
background-image: url(images/alerticon-warning@2x.png);
|
||||
background-image: url(chrome://devtools/skin/images/alerticon-warning@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1097,7 +1098,7 @@ body,
|
||||
}
|
||||
|
||||
.requests-list-network-summary-button > .summary-info-icon {
|
||||
background-image: url(images/profiler-stopwatch.svg);
|
||||
background-image: url(chrome://devtools/skin/images/profiler-stopwatch.svg);
|
||||
filter: var(--icon-filter);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
@ -35,6 +35,7 @@ class RequestListHeaderContextMenu {
|
||||
|
||||
for (let [column, shown] of this.columns) {
|
||||
menu.append(new MenuItem({
|
||||
id: `request-list-header-${column}-toggle`,
|
||||
label: L10N.getStr(`netmonitor.toolbar.${stringMap[column] || column}`),
|
||||
type: "checkbox",
|
||||
checked: shown,
|
||||
@ -47,6 +48,7 @@ class RequestListHeaderContextMenu {
|
||||
menu.append(new MenuItem({ type: "separator" }));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
id: "request-list-header-reset-columns",
|
||||
label: L10N.getStr("netmonitor.toolbar.resetColumns"),
|
||||
click: () => this.resetColumns(),
|
||||
}));
|
||||
|
@ -68,6 +68,10 @@ skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
|
||||
[browser_net_charts-06.js]
|
||||
[browser_net_charts-07.js]
|
||||
[browser_net_clear.js]
|
||||
[browser_net_columns_last_column.js]
|
||||
[browser_net_columns_pref.js]
|
||||
[browser_net_columns_reset.js]
|
||||
[browser_net_columns_showhide.js]
|
||||
[browser_net_complex-params.js]
|
||||
[browser_net_content-type.js]
|
||||
[browser_net_brotli.js]
|
||||
|
@ -0,0 +1,53 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that last visible column can't be hidden
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, gStore, parent } = monitor.panelWin;
|
||||
|
||||
for (let [column, shown] of gStore.getState().ui.columns) {
|
||||
let visibleColumns = [...gStore.getState().ui.columns]
|
||||
.filter(([_, visible]) => visible);
|
||||
|
||||
if (visibleColumns.length === 1) {
|
||||
yield testLastMenuItem(column);
|
||||
break;
|
||||
}
|
||||
|
||||
if (shown) {
|
||||
yield hideColumn(column);
|
||||
}
|
||||
}
|
||||
|
||||
yield teardown(monitor);
|
||||
|
||||
function* hideColumn(column) {
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-status-button") ||
|
||||
document.querySelector("#requests-list-waterfall-button"));
|
||||
|
||||
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
|
||||
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
|
||||
|
||||
yield onHeaderRemoved;
|
||||
ok(!document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be hidden`);
|
||||
}
|
||||
|
||||
function* testLastMenuItem(column) {
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector(`#requests-list-${column}-button`));
|
||||
|
||||
let menuItem = parent.document.querySelector(`#request-list-header-${column}-toggle`);
|
||||
ok(menuItem.disabled, "Last visible column menu item should be disabled.");
|
||||
}
|
||||
});
|
66
devtools/client/netmonitor/test/browser_net_columns_pref.js
Normal file
66
devtools/client/netmonitor/test/browser_net_columns_pref.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests if hidden columns are properly saved
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
Services.prefs.setCharPref("devtools.netmonitor.hiddenColumns",
|
||||
'["status", "contentSize"]');
|
||||
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, windowRequire, parent } = monitor.panelWin;
|
||||
|
||||
ok(!document.querySelector("#requests-list-status-button"),
|
||||
"Status column should be hidden");
|
||||
ok(!document.querySelector("#requests-list-contentSize-button"),
|
||||
"Content size column should be hidden");
|
||||
|
||||
let { Prefs } = windowRequire("devtools/client/netmonitor/src/utils/prefs");
|
||||
|
||||
yield showColumn("status");
|
||||
yield showColumn("contentSize");
|
||||
|
||||
ok(!Prefs.hiddenColumns.includes("status"), "Pref should be synced for status");
|
||||
ok(!Prefs.hiddenColumns.includes("contentSize"),
|
||||
"Pref should be synced for contentSize");
|
||||
|
||||
yield hideColumn("status");
|
||||
|
||||
ok(Prefs.hiddenColumns.includes("status"), "Pref should be synced for status");
|
||||
|
||||
yield showColumn("status");
|
||||
|
||||
function* hideColumn(column) {
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-status-button") ||
|
||||
document.querySelector("#requests-list-waterfall-button"));
|
||||
|
||||
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
|
||||
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
|
||||
|
||||
yield onHeaderRemoved;
|
||||
ok(!document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be hidden`);
|
||||
}
|
||||
|
||||
function* showColumn(column) {
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-status-button") ||
|
||||
document.querySelector("#requests-list-waterfall-button"));
|
||||
|
||||
let onHeaderAdded = waitForDOM(document, `#requests-list-${column}-button`, 1);
|
||||
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
|
||||
|
||||
yield onHeaderAdded;
|
||||
ok(document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be visible`);
|
||||
}
|
||||
});
|
42
devtools/client/netmonitor/test/browser_net_columns_reset.js
Normal file
42
devtools/client/netmonitor/test/browser_net_columns_reset.js
Normal file
@ -0,0 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests reset column menu item
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, parent, windowRequire } = monitor.panelWin;
|
||||
let { Prefs } = windowRequire("devtools/client/netmonitor/src/utils/prefs");
|
||||
|
||||
let prefBefore = Prefs.hiddenColumns;
|
||||
|
||||
hideColumn("status");
|
||||
hideColumn("waterfall");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-contentSize-button"));
|
||||
|
||||
parent.document.querySelector("#request-list-header-reset-columns").click();
|
||||
|
||||
is(JSON.stringify(prefBefore), JSON.stringify(Prefs.hiddenColumns),
|
||||
"Reset columns item should reset columns pref");
|
||||
|
||||
function* hideColumn(column) {
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-contentSize-button"));
|
||||
|
||||
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
|
||||
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
|
||||
|
||||
yield onHeaderRemoved;
|
||||
ok(!document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be hidden`);
|
||||
}
|
||||
});
|
@ -0,0 +1,77 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test showing/hiding columns.
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, gStore, parent } = monitor.panelWin;
|
||||
|
||||
for (let [column, shown] of gStore.getState().ui.columns) {
|
||||
if (shown) {
|
||||
yield testVisibleColumnContextMenuItem(column, document, parent);
|
||||
yield testHiddenColumnContextMenuItem(column, document, parent);
|
||||
} else {
|
||||
yield testHiddenColumnContextMenuItem(column, document, parent);
|
||||
yield testVisibleColumnContextMenuItem(column, document, parent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function* testVisibleColumnContextMenuItem(column, document, parent) {
|
||||
ok(document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be visible`);
|
||||
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-status-button") ||
|
||||
document.querySelector("#requests-list-waterfall-button"));
|
||||
|
||||
let menuItem = parent.document.querySelector(`#request-list-header-${column}-toggle`);
|
||||
|
||||
is(menuItem.getAttribute("type"), "checkbox",
|
||||
`${column} menu item should have type="checkbox" attribute`);
|
||||
is(menuItem.getAttribute("checked"), "true",
|
||||
`checked state of ${column} menu item should be correct`);
|
||||
ok(!menuItem.disabled, `disabled state of ${column} menu item should be correct`);
|
||||
|
||||
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
|
||||
menuItem.click();
|
||||
|
||||
yield onHeaderRemoved;
|
||||
|
||||
ok(!document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be hidden`);
|
||||
}
|
||||
|
||||
function* testHiddenColumnContextMenuItem(column, document, parent) {
|
||||
ok(!document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be hidden`);
|
||||
|
||||
info(`Clicking context-menu item for ${column}`);
|
||||
EventUtils.sendMouseEvent({ type: "contextmenu" },
|
||||
document.querySelector("#requests-list-status-button") ||
|
||||
document.querySelector("#requests-list-waterfall-button"));
|
||||
|
||||
let menuItem = parent.document.querySelector(`#request-list-header-${column}-toggle`);
|
||||
|
||||
is(menuItem.getAttribute("type"), "checkbox",
|
||||
`${column} menu item should have type="checkbox" attribute`);
|
||||
ok(!menuItem.getAttribute("checked"),
|
||||
`checked state of ${column} menu item should be correct`);
|
||||
ok(!menuItem.disabled, `disabled state of ${column} menu item should be correct`);
|
||||
|
||||
let onHeaderAdded = waitForDOM(document, `#requests-list-${column}-button`, 1);
|
||||
menuItem.click();
|
||||
|
||||
yield onHeaderAdded;
|
||||
|
||||
ok(document.querySelector(`#requests-list-${column}-button`),
|
||||
`Column ${column} should be visible`);
|
||||
}
|
@ -8,7 +8,6 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const { isDevelopment } = require("devtools-config");
|
||||
const { NormalModuleReplacementPlugin } = require("webpack");
|
||||
const { toolboxConfig } = require("./node_modules/devtools-launchpad/index");
|
||||
const { getConfig } = require("./bin/configure");
|
||||
@ -33,7 +32,7 @@ let webpackConfig = {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.(png|svg)$/,
|
||||
loader: "file-loader?name=[name].[ext]",
|
||||
loader: "file-loader?name=[path][name].[ext]",
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -57,6 +56,7 @@ let webpackConfig = {
|
||||
"devtools/client/shared/components/reps/reps": "devtools-reps",
|
||||
"devtools/client/shared/components/search-box": "devtools-modules",
|
||||
"devtools/client/shared/components/splitter/split-box": "devtools-modules",
|
||||
"devtools/client/shared/components/stack-trace": "devtools-modules",
|
||||
"devtools/client/shared/components/tabs/tabbar": "devtools-modules",
|
||||
"devtools/client/shared/components/tabs/tabs": "devtools-modules",
|
||||
"devtools/client/shared/components/tree/tree-view": "devtools-modules",
|
||||
@ -85,23 +85,31 @@ let webpackConfig = {
|
||||
"toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"),
|
||||
"Services": "devtools-modules",
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (!isDevelopment()) {
|
||||
webpackConfig.output.libraryTarget = "umd";
|
||||
webpackConfig.plugins = [];
|
||||
const mappings = [
|
||||
[
|
||||
/chrome:\/\/devtools\/skin/,
|
||||
(result) => {
|
||||
result.request = result.request
|
||||
.replace("./chrome://devtools/skin", path.join(__dirname, "../themes"));
|
||||
}
|
||||
],
|
||||
[
|
||||
/resource:\/\/devtools/,
|
||||
(result) => {
|
||||
result.request = result.request
|
||||
.replace("./resource://devtools/client", path.join(__dirname, ".."));
|
||||
}
|
||||
],
|
||||
[/\.\/mocha/, "./mochitest"],
|
||||
[/\.\.\/utils\/mocha/, "../utils/mochitest"],
|
||||
[/\.\/utils\/mocha/, "./utils/mochitest"],
|
||||
];
|
||||
|
||||
const mappings = [
|
||||
[/\.\/mocha/, "./mochitest"],
|
||||
[/\.\.\/utils\/mocha/, "../utils/mochitest"],
|
||||
[/\.\/utils\/mocha/, "./utils/mochitest"],
|
||||
];
|
||||
|
||||
mappings.forEach(([regex, res]) => {
|
||||
webpackConfig.plugins.push(new NormalModuleReplacementPlugin(regex, res));
|
||||
});
|
||||
}
|
||||
webpackConfig.plugins = mappings.map(([regex, res]) =>
|
||||
new NormalModuleReplacementPlugin(regex, res));
|
||||
|
||||
let config = toolboxConfig(webpackConfig, getConfig());
|
||||
|
||||
|
@ -50,7 +50,8 @@
|
||||
|
||||
.tabs .tab-panel {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tabs .tabs-navigation,
|
||||
|
@ -6,14 +6,11 @@ In multi-process environments, most devtools actors are created and initialized
|
||||
|
||||
Some actors need to exchange messages between the parent and the child process (typically when some components aren't available in the child process).
|
||||
|
||||
E.g. the **director-manager** needs to ask the list of installed **director scripts** from
|
||||
the **director-registry** running in the parent process.
|
||||
|
||||
To that end, there's a parent/child setup mechanism at `DebuggerServer` level that can be used.
|
||||
|
||||
When the actor is loaded for the first time in the `DebuggerServer` running in the child process, it may decide to run a setup procedure to load a module in the parent process with which to communicate.
|
||||
|
||||
E.g. in the **director-registry**:
|
||||
Example code for the actor running in the child process:
|
||||
|
||||
```
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
@ -26,18 +23,18 @@ E.g. in the **director-registry**:
|
||||
|
||||
function setupChildProcess() {
|
||||
// `setupInParent` is defined on DebuggerServerConnection,
|
||||
// your actor receive a reference to one instance in its constructor.
|
||||
// your actor receives a reference to one instance in its constructor.
|
||||
conn.setupInParent({
|
||||
module: "devtools/server/actors/director-registry",
|
||||
module: "devtools/server/actors/module-name",
|
||||
setupParent: "setupParentProcess"
|
||||
});
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `setupChildProcess` helper defined and used in the previous example uses the `DebuggerServerConnection.setupInParent` to run a given setup function in the parent process Debugger Server, e.g. in the **director-registry** module.
|
||||
The `setupChildProcess` helper defined and used in the previous example uses the `DebuggerServerConnection.setupInParent` to run a given setup function in the parent process Debugger Server.
|
||||
|
||||
With this, the `DebuggerServer` running in the parent process will require the requested module (**director-registry**) and call its `setupParentProcess` function (which should be exported on the module).
|
||||
With this, the `DebuggerServer` running in the parent process will require the requested module and call its `setupParentProcess` function (which should be exported on the module).
|
||||
|
||||
The `setupParentProcess` function will receive a parameter that contains a reference to the **MessageManager** and a prefix that should be used to send/receive messages between the child and parent processes.
|
||||
|
||||
|
@ -1,631 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
const events = require("sdk/event/core");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
|
||||
const { Cu, Ci } = require("chrome");
|
||||
|
||||
const { on, off, emit } = events;
|
||||
|
||||
const { sandbox, evaluate } = require("sdk/loader/sandbox");
|
||||
const { Class } = require("sdk/core/heritage");
|
||||
|
||||
const { PlainTextConsole } = require("sdk/console/plain-text");
|
||||
|
||||
const { DirectorRegistry } = require("./director-registry");
|
||||
|
||||
const {
|
||||
messagePortSpec,
|
||||
directorManagerSpec,
|
||||
directorScriptSpec,
|
||||
} = require("devtools/shared/specs/director-manager");
|
||||
|
||||
/**
|
||||
* Error Messages
|
||||
*/
|
||||
|
||||
const ERR_MESSAGEPORT_FINALIZED = "message port finalized";
|
||||
|
||||
const ERR_DIRECTOR_UNKNOWN_SCRIPTID = "unkown director-script id";
|
||||
const ERR_DIRECTOR_UNINSTALLED_SCRIPTID = "uninstalled director-script id";
|
||||
|
||||
/**
|
||||
* A MessagePort Actor allowing communication through messageport events
|
||||
* over the remote debugging protocol.
|
||||
*/
|
||||
var MessagePortActor = protocol.ActorClassWithSpec(messagePortSpec, {
|
||||
/**
|
||||
* Create a MessagePort actor.
|
||||
*
|
||||
* @param DebuggerServerConnection conn
|
||||
* The server connection.
|
||||
* @param MessagePort port
|
||||
* The wrapped MessagePort.
|
||||
*/
|
||||
initialize: function (conn, port) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
|
||||
// NOTE: can't get a weak reference because we need to subscribe events
|
||||
// using port.onmessage or addEventListener
|
||||
this.port = port;
|
||||
},
|
||||
|
||||
destroy: function (conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message on the wrapped message port.
|
||||
*
|
||||
* @param Object msg
|
||||
* The JSON serializable message event payload
|
||||
*/
|
||||
postMessage: function (msg) {
|
||||
if (!this.port) {
|
||||
console.error(ERR_MESSAGEPORT_FINALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
this.port.postMessage(msg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts to receive and send queued messages on this message port.
|
||||
*/
|
||||
start: function () {
|
||||
if (!this.port) {
|
||||
console.error(ERR_MESSAGEPORT_FINALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: set port.onmessage to a function is an implicit start
|
||||
// and starts to send queued messages.
|
||||
// On the client side we should set MessagePortClient.onmessage
|
||||
// to a setter which register an handler to the message event
|
||||
// and call the actor start method to start receiving messages
|
||||
// from the MessagePort's queue.
|
||||
this.port.onmessage = (evt) => {
|
||||
let ports;
|
||||
|
||||
// TODO: test these wrapped ports
|
||||
if (Array.isArray(evt.ports)) {
|
||||
ports = evt.ports.map((port) => {
|
||||
let actor = new MessagePortActor(this.conn, port);
|
||||
this.manage(actor);
|
||||
return actor;
|
||||
});
|
||||
}
|
||||
|
||||
emit(this, "message", {
|
||||
isTrusted: evt.isTrusted,
|
||||
data: evt.data,
|
||||
origin: evt.origin,
|
||||
lastEventId: evt.lastEventId,
|
||||
source: this,
|
||||
ports: ports
|
||||
});
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts to receive and send queued messages on this message port, or
|
||||
* raise an exception if the port is null
|
||||
*/
|
||||
close: function () {
|
||||
if (!this.port) {
|
||||
console.error(ERR_MESSAGEPORT_FINALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.port.onmessage = null;
|
||||
this.port.close();
|
||||
} catch (e) {
|
||||
// The port might be a dead object
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
|
||||
finalize: function () {
|
||||
this.close();
|
||||
this.port = null;
|
||||
},
|
||||
});
|
||||
|
||||
exports.MessagePortActor = MessagePortActor;
|
||||
|
||||
/**
|
||||
* The Director Script Actor manage javascript code running in a non-privileged sandbox
|
||||
* with the same privileges of the target global (browser tab or a firefox os app).
|
||||
*
|
||||
* After retrieving an instance of this actor (from the tab director actor), you'll
|
||||
* need to set it up by calling setup().
|
||||
*
|
||||
* After the setup, this actor will automatically attach/detach the content script
|
||||
* (and optionally a directly connect the debugger client and the content script using
|
||||
* a MessageChannel) on tab navigation.
|
||||
*/
|
||||
var DirectorScriptActor = protocol.ActorClassWithSpec(directorScriptSpec, {
|
||||
/**
|
||||
* Creates the director script actor
|
||||
*
|
||||
* @param DebuggerServerConnection conn
|
||||
* The server connection.
|
||||
* @param Actor tabActor
|
||||
* The tab (or root) actor.
|
||||
* @param String scriptId
|
||||
* The director-script id.
|
||||
* @param String scriptCode
|
||||
* The director-script javascript source.
|
||||
* @param Object scriptOptions
|
||||
* The director-script options object.
|
||||
*/
|
||||
initialize: function (conn, tabActor, { scriptId, scriptCode, scriptOptions }) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn, tabActor);
|
||||
|
||||
this.tabActor = tabActor;
|
||||
|
||||
this._scriptId = scriptId;
|
||||
this._scriptCode = scriptCode;
|
||||
this._scriptOptions = scriptOptions;
|
||||
this._setupCalled = false;
|
||||
|
||||
this._onGlobalCreated = this._onGlobalCreated.bind(this);
|
||||
this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
|
||||
},
|
||||
destroy: function (conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts listening to the tab global created, in order to create the director-script
|
||||
* sandbox using the configured scriptCode, attached/detached automatically to the tab
|
||||
* window on tab navigation.
|
||||
*
|
||||
* @param Boolean reload
|
||||
* attach the page immediately or reload it first.
|
||||
* @param Boolean skipAttach
|
||||
* skip the attach
|
||||
*/
|
||||
setup: function ({ reload, skipAttach }) {
|
||||
if (this._setupCalled) {
|
||||
// do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
this._setupCalled = true;
|
||||
|
||||
on(this.tabActor, "window-ready", this._onGlobalCreated);
|
||||
on(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
|
||||
|
||||
// optional skip attach (needed by director-manager for director scripts
|
||||
// bulk activation)
|
||||
if (skipAttach) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
this.window.location.reload();
|
||||
} else {
|
||||
// fake a global created event to attach without reload
|
||||
this._onGlobalCreated({
|
||||
id: getWindowID(this.window),
|
||||
window: this.window,
|
||||
isTopLevel: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the attached MessagePort actor if any
|
||||
*/
|
||||
getMessagePort: function () {
|
||||
return this._messagePortActor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop listening for document global changes, destroy the content worker and puts
|
||||
* this actor to hibernation.
|
||||
*/
|
||||
finalize: function () {
|
||||
if (!this._setupCalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
off(this.tabActor, "window-ready", this._onGlobalCreated);
|
||||
off(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
|
||||
|
||||
this._onGlobalDestroyed({ id: this._lastAttachedWinId });
|
||||
|
||||
this._setupCalled = false;
|
||||
},
|
||||
|
||||
// local helpers
|
||||
get window() {
|
||||
return this.tabActor.window;
|
||||
},
|
||||
|
||||
/* event handlers */
|
||||
_onGlobalCreated: function ({ id, window, isTopLevel }) {
|
||||
if (!isTopLevel) {
|
||||
// filter iframes
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this._lastAttachedWinId) {
|
||||
// if we have received a global created without a previous global destroyed,
|
||||
// it's time to cleanup the previous state
|
||||
this._onGlobalDestroyed(this._lastAttachedWinId);
|
||||
}
|
||||
|
||||
// TODO: check if we want to share a single sandbox per global
|
||||
// for multiple debugger clients
|
||||
|
||||
// create & attach the new sandbox
|
||||
this._scriptSandbox = new DirectorScriptSandbox({
|
||||
scriptId: this._scriptId,
|
||||
scriptCode: this._scriptCode,
|
||||
scriptOptions: this._scriptOptions
|
||||
});
|
||||
|
||||
// attach the global window
|
||||
this._lastAttachedWinId = id;
|
||||
let port = this._scriptSandbox.attach(window, id);
|
||||
this._onDirectorScriptAttach(window, port);
|
||||
} catch (e) {
|
||||
this._onDirectorScriptError(e);
|
||||
}
|
||||
},
|
||||
_onGlobalDestroyed: function ({ id }) {
|
||||
if (id !== this._lastAttachedWinId) {
|
||||
// filter destroyed globals
|
||||
return;
|
||||
}
|
||||
|
||||
// unmanage and cleanup the messageport actor
|
||||
if (this._messagePortActor) {
|
||||
this.unmanage(this._messagePortActor);
|
||||
this._messagePortActor = null;
|
||||
}
|
||||
|
||||
// NOTE: destroy here the old worker
|
||||
if (this._scriptSandbox) {
|
||||
this._scriptSandbox.destroy(this._onDirectorScriptError.bind(this));
|
||||
|
||||
// send a detach event to the debugger client
|
||||
emit(this, "detach", {
|
||||
directorScriptId: this._scriptId,
|
||||
innerId: this._lastAttachedWinId
|
||||
});
|
||||
|
||||
this._lastAttachedWinId = null;
|
||||
this._scriptSandbox = null;
|
||||
}
|
||||
},
|
||||
_onDirectorScriptError: function (error) {
|
||||
// route the content script error to the debugger client
|
||||
if (error) {
|
||||
// prevents silent director-script-errors
|
||||
console.error("director-script-error", error);
|
||||
// route errors to debugger server clients
|
||||
emit(this, "error", {
|
||||
directorScriptId: this._scriptId,
|
||||
message: error.toString(),
|
||||
stack: error.stack,
|
||||
fileName: error.fileName,
|
||||
lineNumber: error.lineNumber,
|
||||
columnNumber: error.columnNumber
|
||||
});
|
||||
}
|
||||
},
|
||||
_onDirectorScriptAttach: function (window, port) {
|
||||
let portActor = new MessagePortActor(this.conn, port);
|
||||
this.manage(portActor);
|
||||
this._messagePortActor = portActor;
|
||||
|
||||
emit(this, "attach", {
|
||||
directorScriptId: this._scriptId,
|
||||
url: (window && window.location) ? window.location.toString() : "",
|
||||
innerId: this._lastAttachedWinId,
|
||||
port: this._messagePortActor
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
exports.DirectorScriptActor = DirectorScriptActor;
|
||||
|
||||
/**
|
||||
* The DirectorManager Actor is a tab actor which manages enabling/disabling
|
||||
* director scripts.
|
||||
*/
|
||||
exports.DirectorManagerActor = protocol.ActorClassWithSpec(directorManagerSpec, {
|
||||
/* init & destroy methods */
|
||||
initialize: function (conn, tabActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.tabActor = tabActor;
|
||||
this._directorScriptActorsMap = new Map();
|
||||
},
|
||||
destroy: function (conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the list of installed director-scripts.
|
||||
*/
|
||||
list: function () {
|
||||
let enabledScriptIds = [...this._directorScriptActorsMap.keys()];
|
||||
|
||||
return {
|
||||
installed: DirectorRegistry.list(),
|
||||
enabled: enabledScriptIds
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Bulk enabling director-scripts.
|
||||
*
|
||||
* @param Array[String] selectedIds
|
||||
* The list of director-script ids to be enabled,
|
||||
* ["*"] will activate all the installed director-scripts
|
||||
* @param Boolean reload
|
||||
* optionally reload the target window
|
||||
*/
|
||||
enableByScriptIds: function (selectedIds, { reload }) {
|
||||
if (selectedIds && selectedIds.length === 0) {
|
||||
// filtered all director scripts ids
|
||||
return;
|
||||
}
|
||||
|
||||
for (let scriptId of DirectorRegistry.list()) {
|
||||
// filter director script ids
|
||||
if (selectedIds.indexOf("*") < 0 &&
|
||||
selectedIds.indexOf(scriptId) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let actor = this.getByScriptId(scriptId);
|
||||
|
||||
// skip attach if reload is true (activated director scripts
|
||||
// will be automatically attached on the final reload)
|
||||
actor.setup({ reload: false, skipAttach: reload });
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
this.tabActor.window.location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bulk disabling director-scripts.
|
||||
*
|
||||
* @param Array[String] selectedIds
|
||||
* The list of director-script ids to be disable,
|
||||
* ["*"] will de-activate all the enable director-scripts
|
||||
* @param Boolean reload
|
||||
* optionally reload the target window
|
||||
*/
|
||||
disableByScriptIds: function (selectedIds, { reload }) {
|
||||
if (selectedIds && selectedIds.length === 0) {
|
||||
// filtered all director scripts ids
|
||||
return;
|
||||
}
|
||||
|
||||
for (let scriptId of this._directorScriptActorsMap.keys()) {
|
||||
// filter director script ids
|
||||
if (selectedIds.indexOf("*") < 0 &&
|
||||
selectedIds.indexOf(scriptId) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let actor = this._directorScriptActorsMap.get(scriptId);
|
||||
this._directorScriptActorsMap.delete(scriptId);
|
||||
|
||||
// finalize the actor (which will produce director-script-detach event)
|
||||
actor.finalize();
|
||||
// unsubscribe event handlers on the disabled actor
|
||||
off(actor);
|
||||
|
||||
this.unmanage(actor);
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
this.tabActor.window.location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the actor instance of an installed director-script
|
||||
* (and create the actor instance if it doesn't exists yet).
|
||||
*/
|
||||
getByScriptId: function (scriptId) {
|
||||
let id = scriptId;
|
||||
// raise an unknown director-script id exception
|
||||
if (!DirectorRegistry.checkInstalled(id)) {
|
||||
console.error(ERR_DIRECTOR_UNKNOWN_SCRIPTID, id);
|
||||
throw Error(ERR_DIRECTOR_UNKNOWN_SCRIPTID);
|
||||
}
|
||||
|
||||
// get a previous created actor instance
|
||||
let actor = this._directorScriptActorsMap.get(id);
|
||||
|
||||
// create a new actor instance
|
||||
if (!actor) {
|
||||
let directorScriptDefinition = DirectorRegistry.get(id);
|
||||
|
||||
// test lazy director-script (e.g. uninstalled in the parent process)
|
||||
if (!directorScriptDefinition) {
|
||||
console.error(ERR_DIRECTOR_UNINSTALLED_SCRIPTID, id);
|
||||
throw Error(ERR_DIRECTOR_UNINSTALLED_SCRIPTID);
|
||||
}
|
||||
|
||||
actor = new DirectorScriptActor(this.conn, this.tabActor, directorScriptDefinition);
|
||||
this._directorScriptActorsMap.set(id, actor);
|
||||
|
||||
on(actor, "error", emit.bind(null, this, "director-script-error"));
|
||||
on(actor, "attach", emit.bind(null, this, "director-script-attach"));
|
||||
on(actor, "detach", emit.bind(null, this, "director-script-detach"));
|
||||
|
||||
this.manage(actor);
|
||||
}
|
||||
|
||||
return actor;
|
||||
},
|
||||
|
||||
finalize: function () {
|
||||
this.disableByScriptIds(["*"], false);
|
||||
}
|
||||
});
|
||||
|
||||
/* private helpers */
|
||||
|
||||
/**
|
||||
* DirectorScriptSandbox is a private utility class, which attach a non-priviliged sandbox
|
||||
* to a target window.
|
||||
*/
|
||||
const DirectorScriptSandbox = Class({
|
||||
initialize: function ({scriptId, scriptCode, scriptOptions}) {
|
||||
this._scriptId = scriptId;
|
||||
this._scriptCode = scriptCode;
|
||||
this._scriptOptions = scriptOptions;
|
||||
},
|
||||
|
||||
attach: function (window, innerId) {
|
||||
this._innerId = innerId;
|
||||
this._window = window;
|
||||
this._proto = Cu.createObjectIn(this._window);
|
||||
|
||||
let id = this._scriptId;
|
||||
let uri = this._scriptCode;
|
||||
|
||||
this._sandbox = sandbox(window, {
|
||||
sandboxName: uri,
|
||||
sandboxPrototype: this._proto,
|
||||
sameZoneAs: window,
|
||||
wantXrays: true,
|
||||
wantComponents: false,
|
||||
wantExportHelpers: false,
|
||||
metadata: {
|
||||
URI: uri,
|
||||
addonID: id,
|
||||
SDKDirectorScript: true,
|
||||
"inner-window-id": innerId
|
||||
}
|
||||
});
|
||||
|
||||
// create a CommonJS module object which match the interface from addon-sdk
|
||||
// (addon-sdk/sources/lib/toolkit/loader.js#L678-L686)
|
||||
let module = Cu.cloneInto(Object.create(null, {
|
||||
id: { enumerable: true, value: id },
|
||||
uri: { enumerable: true, value: uri },
|
||||
exports: { enumerable: true, value: Cu.createObjectIn(this._sandbox) }
|
||||
}), this._sandbox);
|
||||
|
||||
// create a console API object
|
||||
let directorScriptConsole = new PlainTextConsole(null, this._innerId);
|
||||
|
||||
// inject CommonJS module globals into the sandbox prototype
|
||||
Object.defineProperties(this._proto, {
|
||||
module: { enumerable: true, value: module },
|
||||
exports: { enumerable: true, value: module.exports },
|
||||
console: {
|
||||
enumerable: true,
|
||||
value: Cu.cloneInto(
|
||||
directorScriptConsole,
|
||||
this._sandbox,
|
||||
{ cloneFunctions: true }
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperties(this._sandbox, {
|
||||
require: {
|
||||
enumerable: true,
|
||||
value: Cu.cloneInto(function () {
|
||||
throw Error("NOT IMPLEMENTED");
|
||||
}, this._sandbox, { cloneFunctions: true })
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: if the debugger target is local, the debugger client could pass
|
||||
// to the director actor the resource url instead of the entire javascript
|
||||
// source code.
|
||||
|
||||
// evaluate the director script source in the sandbox
|
||||
evaluate(this._sandbox, this._scriptCode, "javascript:" + this._scriptCode);
|
||||
|
||||
// prepare the messageport connected to the debugger client
|
||||
let { port1, port2 } = new this._window.MessageChannel();
|
||||
|
||||
// prepare the unload callbacks queue
|
||||
let sandboxOnUnloadQueue = this._sandboxOnUnloadQueue = [];
|
||||
|
||||
// create the attach options
|
||||
let attachOptions = this._attachOptions = Cu.createObjectIn(this._sandbox);
|
||||
Object.defineProperties(attachOptions, {
|
||||
port: { enumerable: true, value: port1 },
|
||||
window: { enumerable: true, value: window },
|
||||
scriptOptions: {
|
||||
enumerable: true,
|
||||
value: Cu.cloneInto(this._scriptOptions, this._sandbox)
|
||||
},
|
||||
onUnload: {
|
||||
enumerable: true,
|
||||
value: Cu.cloneInto(function (cb) {
|
||||
// collect unload callbacks
|
||||
if (typeof cb == "function") {
|
||||
sandboxOnUnloadQueue.push(cb);
|
||||
}
|
||||
}, this._sandbox, { cloneFunctions: true })
|
||||
}
|
||||
});
|
||||
|
||||
// select the attach method
|
||||
let exports = this._proto.module.exports;
|
||||
if (this._scriptOptions && "attachMethod" in this._scriptOptions) {
|
||||
this._sandboxOnAttach = exports[this._scriptOptions.attachMethod];
|
||||
} else {
|
||||
this._sandboxOnAttach = exports;
|
||||
}
|
||||
|
||||
if (typeof this._sandboxOnAttach !== "function") {
|
||||
throw Error("the configured attachMethod '" +
|
||||
(this._scriptOptions.attachMethod || "module.exports") +
|
||||
"' is not exported by the directorScript");
|
||||
}
|
||||
|
||||
// call the attach method
|
||||
this._sandboxOnAttach.call(this._sandbox, attachOptions);
|
||||
|
||||
return port2;
|
||||
},
|
||||
destroy: function (onError) {
|
||||
// evaluate queue unload methods if any
|
||||
while (this._sandboxOnUnloadQueue && this._sandboxOnUnloadQueue.length > 0) {
|
||||
let cb = this._sandboxOnUnloadQueue.pop();
|
||||
|
||||
try {
|
||||
cb();
|
||||
} catch (e) {
|
||||
console.error("Exception on DirectorScript Sandbox destroy", e);
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
Cu.nukeSandbox(this._sandbox);
|
||||
}
|
||||
});
|
||||
|
||||
function getWindowID(window) {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.currentInnerWindowID;
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
|
||||
const {directorRegistrySpec} = require("devtools/shared/specs/director-registry");
|
||||
|
||||
/**
|
||||
* Error Messages
|
||||
*/
|
||||
|
||||
const ERR_DIRECTOR_INSTALL_TWICE = "Trying to install a director-script twice";
|
||||
const ERR_DIRECTOR_INSTALL_EMPTY = "Trying to install an empty director-script";
|
||||
const ERR_DIRECTOR_UNINSTALL_UNKNOWN = "Trying to uninstall an unkown director-script";
|
||||
|
||||
const ERR_DIRECTOR_PARENT_UNKNOWN_METHOD = "Unknown parent process method";
|
||||
const ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD =
|
||||
"Unexpected call to notImplemented method";
|
||||
const ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES =
|
||||
"Unexpected multiple replies to called parent method";
|
||||
const ERR_DIRECTOR_CHILD_NO_REPLY = "Unexpected no reply to called parent method";
|
||||
|
||||
/**
|
||||
* Director Registry
|
||||
*/
|
||||
|
||||
// Map of director scripts ids to director script definitions
|
||||
var gDirectorScripts = Object.create(null);
|
||||
|
||||
const DirectorRegistry = exports.DirectorRegistry = {
|
||||
/**
|
||||
* Register a Director Script with the debugger server.
|
||||
* @param id string
|
||||
* The ID of a director script.
|
||||
* @param directorScriptDef object
|
||||
* The definition of a director script.
|
||||
*/
|
||||
install: function (id, scriptDef) {
|
||||
if (id in gDirectorScripts) {
|
||||
console.error(ERR_DIRECTOR_INSTALL_TWICE, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!scriptDef) {
|
||||
console.error(ERR_DIRECTOR_INSTALL_EMPTY, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
gDirectorScripts[id] = scriptDef;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister a Director Script with the debugger server.
|
||||
* @param id string
|
||||
* The ID of a director script.
|
||||
*/
|
||||
uninstall: function (id) {
|
||||
if (id in gDirectorScripts) {
|
||||
delete gDirectorScripts[id];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error(ERR_DIRECTOR_UNINSTALL_UNKNOWN, id);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if a director script id has been registered.
|
||||
* @param id string
|
||||
* The ID of a director script.
|
||||
*/
|
||||
checkInstalled: function (id) {
|
||||
return (this.list().indexOf(id) >= 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a registered director script definition by id.
|
||||
* @param id string
|
||||
* The ID of a director script.
|
||||
*/
|
||||
get: function (id) {
|
||||
return gDirectorScripts[id];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of registered director script ids.
|
||||
*/
|
||||
list: function () {
|
||||
return Object.keys(gDirectorScripts);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all the registered director scripts.
|
||||
*/
|
||||
clear: function () {
|
||||
gDirectorScripts = Object.create(null);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* E10S parent/child setup helpers
|
||||
*/
|
||||
|
||||
exports.setupParentProcess = function setupParentProcess({ mm, prefix }) {
|
||||
// listen for director-script requests from the child process
|
||||
setMessageManager(mm);
|
||||
|
||||
/* parent process helpers */
|
||||
|
||||
function handleChildRequest(msg) {
|
||||
switch (msg.json.method) {
|
||||
case "get":
|
||||
return DirectorRegistry.get(msg.json.args[0]);
|
||||
case "list":
|
||||
return DirectorRegistry.list();
|
||||
default:
|
||||
console.error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD, msg.json.method);
|
||||
throw new Error(ERR_DIRECTOR_PARENT_UNKNOWN_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
function setMessageManager(newMM) {
|
||||
if (mm) {
|
||||
mm.removeMessageListener("debug:director-registry-request", handleChildRequest);
|
||||
}
|
||||
mm = newMM;
|
||||
if (mm) {
|
||||
mm.addMessageListener("debug:director-registry-request", handleChildRequest);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
onBrowserSwap: setMessageManager,
|
||||
onDisconnected: () => setMessageManager(null),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* The DirectorRegistry Actor is a global actor which manages install/uninstall of
|
||||
* director scripts definitions.
|
||||
*/
|
||||
exports.DirectorRegistryActor = protocol.ActorClassWithSpec(directorRegistrySpec, {
|
||||
/* init & destroy methods */
|
||||
initialize: function (conn, parentActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
this.maybeSetupChildProcess(conn);
|
||||
},
|
||||
destroy: function (conn) {
|
||||
protocol.Actor.prototype.destroy.call(this, conn);
|
||||
this.finalize();
|
||||
},
|
||||
|
||||
finalize: function () {
|
||||
// nothing to cleanup
|
||||
},
|
||||
|
||||
maybeSetupChildProcess(conn) {
|
||||
// skip child setup if this actor module is not running in a child process
|
||||
if (!DebuggerServer.isInChildProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { sendSyncMessage } = conn.parentMessageManager;
|
||||
|
||||
conn.setupInParent({
|
||||
module: "devtools/server/actors/director-registry",
|
||||
setupParent: "setupParentProcess"
|
||||
});
|
||||
|
||||
DirectorRegistry.install = notImplemented.bind(null, "install");
|
||||
DirectorRegistry.uninstall = notImplemented.bind(null, "uninstall");
|
||||
DirectorRegistry.clear = notImplemented.bind(null, "clear");
|
||||
|
||||
DirectorRegistry.get = callParentProcess.bind(null, "get");
|
||||
DirectorRegistry.list = callParentProcess.bind(null, "list");
|
||||
|
||||
/* child process helpers */
|
||||
|
||||
function notImplemented(method) {
|
||||
console.error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD, method);
|
||||
throw Error(ERR_DIRECTOR_CHILD_NOTIMPLEMENTED_METHOD);
|
||||
}
|
||||
|
||||
function callParentProcess(method, ...args) {
|
||||
let reply = sendSyncMessage("debug:director-registry-request", {
|
||||
method: method,
|
||||
args: args
|
||||
});
|
||||
|
||||
if (reply.length === 0) {
|
||||
console.error(ERR_DIRECTOR_CHILD_NO_REPLY);
|
||||
throw Error(ERR_DIRECTOR_CHILD_NO_REPLY);
|
||||
} else if (reply.length > 1) {
|
||||
console.error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
|
||||
throw Error(ERR_DIRECTOR_CHILD_MULTIPLE_REPLIES);
|
||||
}
|
||||
|
||||
return reply[0];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Install a new director-script definition.
|
||||
*
|
||||
* @param String id
|
||||
* The director-script definition identifier.
|
||||
* @param String scriptCode
|
||||
* The director-script javascript source.
|
||||
* @param Object scriptOptions
|
||||
* The director-script option object.
|
||||
*/
|
||||
install: function (id, { scriptCode, scriptOptions }) {
|
||||
// TODO: add more checks on id format?
|
||||
if (!id || id.length === 0) {
|
||||
throw Error("director-script id is mandatory");
|
||||
}
|
||||
|
||||
if (!scriptCode) {
|
||||
throw Error("director-script scriptCode is mandatory");
|
||||
}
|
||||
|
||||
return DirectorRegistry.install(id, {
|
||||
scriptId: id,
|
||||
scriptCode: scriptCode,
|
||||
scriptOptions: scriptOptions
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall a director-script definition.
|
||||
*
|
||||
* @param String id
|
||||
* The identifier of the director-script definition to be removed
|
||||
*/
|
||||
uninstall: function (id) {
|
||||
return DirectorRegistry.uninstall(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the list of installed director-scripts.
|
||||
*/
|
||||
list: function () {
|
||||
return DirectorRegistry.list();
|
||||
}
|
||||
});
|
@ -24,8 +24,6 @@ DevToolsModules(
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
'device.js',
|
||||
'director-manager.js',
|
||||
'director-registry.js',
|
||||
'emulation.js',
|
||||
'environment.js',
|
||||
'errordocs.js',
|
||||
@ -71,47 +69,47 @@ DevToolsModules(
|
||||
'worker.js',
|
||||
)
|
||||
|
||||
with Files('animation.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Animation Inspector')
|
||||
|
||||
with Files('breakpoint.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Debugger')
|
||||
|
||||
with Files('css-properties.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: CSS Rules Inspector')
|
||||
|
||||
with Files('csscoverage.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Graphics Commandline and Toolbar')
|
||||
|
||||
with Files('inspector.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Inspector')
|
||||
|
||||
with Files('memory.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Memory')
|
||||
|
||||
with Files('monitor.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools')
|
||||
|
||||
with Files('performance*'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
|
||||
|
||||
with Files('profiler.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
|
||||
|
||||
with Files('source.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Debugger')
|
||||
|
||||
with Files('storage.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Storage Inspector')
|
||||
|
||||
with Files('styleeditor.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Style Editor')
|
||||
|
||||
with Files('webaudio.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Web Audio Editor')
|
||||
|
||||
with Files('webconsole.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Console')
|
||||
|
||||
with Files('webgl.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: WebGL Shader Editor')
|
||||
with Files('animation.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Animation Inspector')
|
||||
|
||||
with Files('breakpoint.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Debugger')
|
||||
|
||||
with Files('css-properties.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: CSS Rules Inspector')
|
||||
|
||||
with Files('csscoverage.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Graphics Commandline and Toolbar')
|
||||
|
||||
with Files('inspector.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Inspector')
|
||||
|
||||
with Files('memory.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Memory')
|
||||
|
||||
with Files('monitor.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools')
|
||||
|
||||
with Files('performance*'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
|
||||
|
||||
with Files('profiler.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
|
||||
|
||||
with Files('source.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Debugger')
|
||||
|
||||
with Files('storage.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Storage Inspector')
|
||||
|
||||
with Files('styleeditor.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Style Editor')
|
||||
|
||||
with Files('webaudio.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Web Audio Editor')
|
||||
|
||||
with Files('webconsole.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: Console')
|
||||
|
||||
with Files('webgl.js'):
|
||||
BUG_COMPONENT = ('Firefox', 'Developer Tools: WebGL Shader Editor')
|
||||
|
@ -275,6 +275,10 @@ RootActor.prototype = {
|
||||
let selected;
|
||||
return tabList.getList().then((tabActors) => {
|
||||
for (let tabActor of tabActors) {
|
||||
if (tabActor.exited) {
|
||||
// Tab actor may have exited while we were gathering the list.
|
||||
continue;
|
||||
}
|
||||
if (tabActor.selected) {
|
||||
selected = tabActorList.length;
|
||||
}
|
||||
|
@ -700,6 +700,7 @@ function BrowserTabActor(connection, browser) {
|
||||
this._conn = connection;
|
||||
this._browser = browser;
|
||||
this._form = null;
|
||||
this.exited = false;
|
||||
}
|
||||
|
||||
BrowserTabActor.prototype = {
|
||||
@ -712,7 +713,7 @@ BrowserTabActor.prototype = {
|
||||
message: "Tab destroyed while performing a BrowserTabActor update"
|
||||
});
|
||||
}
|
||||
this._form = null;
|
||||
this.exit();
|
||||
};
|
||||
let connect = DebuggerServer.connectToChild(this._conn, this._browser, onDestroy);
|
||||
return connect.then(form => {
|
||||
@ -722,7 +723,7 @@ BrowserTabActor.prototype = {
|
||||
},
|
||||
|
||||
get _tabbrowser() {
|
||||
if (typeof this._browser.getTabBrowser == "function") {
|
||||
if (this._browser && typeof this._browser.getTabBrowser == "function") {
|
||||
return this._browser.getTabBrowser();
|
||||
}
|
||||
return null;
|
||||
@ -739,7 +740,7 @@ BrowserTabActor.prototype = {
|
||||
// If the child happens to be crashed/close/detach, it won't have _form set,
|
||||
// so only request form update if some code is still listening on the other
|
||||
// side.
|
||||
if (this._form) {
|
||||
if (!this.exited) {
|
||||
this._deferredUpdate = promise.defer();
|
||||
let onFormUpdate = msg => {
|
||||
// There may be more than just one childtab.js up and running
|
||||
@ -764,7 +765,7 @@ BrowserTabActor.prototype = {
|
||||
*/
|
||||
get title() {
|
||||
// On Fennec, we can check the session store data for zombie tabs
|
||||
if (this._browser.__SS_restore) {
|
||||
if (this._browser && this._browser.__SS_restore) {
|
||||
let sessionStore = this._browser.__SS_data;
|
||||
// Get the last selected entry
|
||||
let entry = sessionStore.entries[sessionStore.index - 1];
|
||||
@ -788,7 +789,7 @@ BrowserTabActor.prototype = {
|
||||
*/
|
||||
get url() {
|
||||
// On Fennec, we can check the session store data for zombie tabs
|
||||
if (this._browser.__SS_restore) {
|
||||
if (this._browser && this._browser.__SS_restore) {
|
||||
let sessionStore = this._browser.__SS_data;
|
||||
// Get the last selected entry
|
||||
let entry = sessionStore.entries[sessionStore.index - 1];
|
||||
@ -813,6 +814,8 @@ BrowserTabActor.prototype = {
|
||||
|
||||
exit() {
|
||||
this._browser = null;
|
||||
this._form = null;
|
||||
this.exited = true;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -443,11 +443,6 @@ var DebuggerServer = {
|
||||
constructor: "DeviceActor",
|
||||
type: { global: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/director-registry", {
|
||||
prefix: "directorRegistry",
|
||||
constructor: "DirectorRegistryActor",
|
||||
type: { global: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/heap-snapshot-file", {
|
||||
prefix: "heapSnapshotFile",
|
||||
constructor: "HeapSnapshotFileActor",
|
||||
@ -549,11 +544,6 @@ var DebuggerServer = {
|
||||
constructor: "TimelineActor",
|
||||
type: { tab: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/director-manager", {
|
||||
prefix: "directorManager",
|
||||
constructor: "DirectorManagerActor",
|
||||
type: { global: false, tab: true }
|
||||
});
|
||||
if ("nsIProfiler" in Ci) {
|
||||
this.registerModule("devtools/server/actors/profiler", {
|
||||
prefix: "profiler",
|
||||
|
@ -21,7 +21,6 @@ support-files =
|
||||
stylesheets-nested-iframes.html
|
||||
timeline-iframe-child.html
|
||||
timeline-iframe-parent.html
|
||||
director-script-target.html
|
||||
storage-helpers.js
|
||||
!/devtools/server/tests/mochitest/hello-actor.js
|
||||
|
||||
@ -91,11 +90,5 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
|
||||
[browser_timeline.js]
|
||||
[browser_timeline_actors.js]
|
||||
[browser_timeline_iframes.js]
|
||||
[browser_directorscript_actors_exports.js]
|
||||
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_directorscript_actors_error_events.js]
|
||||
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_directorscript_actors.js]
|
||||
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_register_actor.js]
|
||||
[browser_webextension_inspected_window.js]
|
@ -1,161 +0,0 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
|
||||
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab(MAIN_DOMAIN + "director-script-target.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
|
||||
DirectorRegistry.clear();
|
||||
let directorManager = DirectorManagerFront(client, form);
|
||||
|
||||
yield testDirectorScriptAttachEventAttributes(directorManager);
|
||||
yield testDirectorScriptMessagePort(directorManager);
|
||||
yield testDirectorScriptWindowEval(directorManager);
|
||||
yield testDirectorScriptUnloadOnDetach(directorManager);
|
||||
|
||||
yield client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
DirectorRegistry.clear();
|
||||
});
|
||||
|
||||
function* testDirectorScriptAttachEventAttributes(directorManager) {
|
||||
let attachEvent = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_attachEventAttributes",
|
||||
scriptCode: "(" + (function () {
|
||||
exports.attach = function () {};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
|
||||
let { directorScriptId, url } = attachEvent;
|
||||
|
||||
is(directorScriptId, "testDirectorScript_attachEventAttributes",
|
||||
"attach event should contains directorScriptId");
|
||||
is(url, MAIN_DOMAIN + "director-script-target.html");
|
||||
}
|
||||
|
||||
function* testDirectorScriptMessagePort(directorManager) {
|
||||
let { port } = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_MessagePort",
|
||||
scriptCode: "(" + (function () {
|
||||
exports.attach = function ({port: messagePort}) {
|
||||
messagePort.onmessage = function (evt) {
|
||||
// echo messages
|
||||
evt.target.postMessage(evt.data);
|
||||
};
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
|
||||
ok(port && port.postMessage, "testDirector_MessagePort port received");
|
||||
|
||||
// exchange messages over the MessagePort
|
||||
let waitForMessagePortEvent = once(port, "message");
|
||||
// needs to explicit start the port
|
||||
port.start();
|
||||
|
||||
let msg = { k1: "v1", k2: [1, 2, 3] };
|
||||
port.postMessage(msg);
|
||||
|
||||
let reply = yield waitForMessagePortEvent;
|
||||
|
||||
is(JSON.stringify(reply.data), JSON.stringify(msg),
|
||||
"echo reply received on the MessagePortClient");
|
||||
}
|
||||
|
||||
function* testDirectorScriptWindowEval(directorManager) {
|
||||
let { port } = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_WindowEval",
|
||||
scriptCode: "(" + (function () {
|
||||
exports.attach = function ({window, port: evalPort}) {
|
||||
let onpageloaded = function () {
|
||||
let globalVarValue = window.eval("globalAccessibleVar;");
|
||||
evalPort.postMessage(globalVarValue);
|
||||
};
|
||||
|
||||
if (window.document && window.document.readyState === "complete") {
|
||||
onpageloaded();
|
||||
} else {
|
||||
window.addEventListener("load", onpageloaded);
|
||||
}
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
|
||||
ok(port, "testDirectorScript_WindowEval port received");
|
||||
|
||||
// exchange messages over the MessagePort
|
||||
let waitForMessagePortEvent = once(port, "message");
|
||||
// needs to explicit start the port
|
||||
port.start();
|
||||
|
||||
let portEvent = yield waitForMessagePortEvent;
|
||||
|
||||
ok(portEvent.data !== "unsecure-eval", "window.eval should be wrapped and safe");
|
||||
is(portEvent.data, "global-value",
|
||||
"globalAccessibleVar should be accessible through window.eval");
|
||||
}
|
||||
|
||||
function* testDirectorScriptUnloadOnDetach(directorManager) {
|
||||
let { port } = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_unloadOnDetach",
|
||||
scriptCode: "(" + (function () {
|
||||
exports.attach = function ({port: unloadPort, onUnload}) {
|
||||
onUnload(function () {
|
||||
unloadPort.postMessage("ONUNLOAD");
|
||||
});
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
|
||||
ok(port, "testDirectorScript_unloadOnDetach port received");
|
||||
port.start();
|
||||
|
||||
let waitForDetach = once(directorManager, "director-script-detach");
|
||||
let waitForMessage = once(port, "message");
|
||||
|
||||
directorManager.disableByScriptIds(["testDirectorScript_unloadOnDetach"],
|
||||
{reload: false});
|
||||
|
||||
let { directorScriptId } = yield waitForDetach;
|
||||
is(directorScriptId, "testDirectorScript_unloadOnDetach",
|
||||
"detach event should contains directorScriptId");
|
||||
|
||||
let portEvent = yield waitForMessage;
|
||||
is(portEvent.data, "ONUNLOAD", "director-script's exports.onUnload called on detach");
|
||||
}
|
||||
|
||||
function* installAndEnableDirectorScript(directorManager, directorScriptDef) {
|
||||
let { scriptId } = directorScriptDef;
|
||||
|
||||
DirectorRegistry.install(scriptId, directorScriptDef);
|
||||
|
||||
let waitForAttach = once(directorManager, "director-script-attach");
|
||||
let waitForError = once(directorManager, "director-script-error");
|
||||
|
||||
directorManager.enableByScriptIds([scriptId], {reload: false});
|
||||
|
||||
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
|
||||
|
||||
return attachOrErrorEvent;
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
|
||||
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab(MAIN_DOMAIN + "director-script-target.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
|
||||
DirectorRegistry.clear();
|
||||
let directorManager = DirectorManagerFront(client, form);
|
||||
|
||||
yield testErrorOnRequire(directorManager);
|
||||
yield testErrorOnEvaluate(directorManager);
|
||||
yield testErrorOnAttach(directorManager);
|
||||
yield testErrorOnDetach(directorManager);
|
||||
|
||||
yield client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
DirectorRegistry.clear();
|
||||
});
|
||||
|
||||
function* testErrorOnRequire(directorManager) {
|
||||
// director script require method should raise a "not implemented" exception
|
||||
let errorOnRequire = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_errorOnRequire",
|
||||
scriptCode: "(" + (function () {
|
||||
// this director script should generate an error event
|
||||
// because require raise a "not implemented" exception
|
||||
require("fake_module");
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
});
|
||||
|
||||
assertIsDirectorScriptError(errorOnRequire);
|
||||
|
||||
let { message } = errorOnRequire;
|
||||
is(message, "Error: NOT IMPLEMENTED",
|
||||
"error.message contains the expected error message");
|
||||
}
|
||||
|
||||
function* testErrorOnEvaluate(directorManager) {
|
||||
// director scripts should send an error events if the director script
|
||||
// raise an exception on evaluation
|
||||
let errorOnEvaluate = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_errorOnEvaluate",
|
||||
scriptCode: "(" + (function () {
|
||||
// this will raise an exception evaluating
|
||||
// the director script
|
||||
raise.an_error.during.content_script.load();
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
});
|
||||
assertIsDirectorScriptError(errorOnEvaluate);
|
||||
}
|
||||
|
||||
function* testErrorOnAttach(directorManager) {
|
||||
// director scripts should send an error events if the director script
|
||||
// raise an exception on evaluation
|
||||
let errorOnAttach = yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_errorOnAttach",
|
||||
scriptCode: "(" + (function () {
|
||||
// this will raise an exception on evaluating
|
||||
// the director script
|
||||
module.exports = function () {
|
||||
raise.an_error.during.content_script.load();
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
});
|
||||
assertIsDirectorScriptError(errorOnAttach);
|
||||
}
|
||||
|
||||
function* testErrorOnDetach(directorManager) {
|
||||
// director scripts should send an error events if the director script
|
||||
// raise an exception on evaluation
|
||||
yield installAndEnableDirectorScript(directorManager, {
|
||||
scriptId: "testDirectorScript_errorOnDetach",
|
||||
scriptCode: "(" + (function () {
|
||||
module.exports = function ({onUnload}) {
|
||||
// this will raise an exception on unload the director script
|
||||
onUnload(function () {
|
||||
raise_an_error_onunload();
|
||||
});
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
});
|
||||
|
||||
let waitForDetach = once(directorManager, "director-script-detach");
|
||||
let waitForError = once(directorManager, "director-script-error");
|
||||
|
||||
directorManager.disableByScriptIds(["testDirectorScript_errorOnDetach"],
|
||||
{reload: false});
|
||||
|
||||
let detach = yield waitForDetach;
|
||||
let error = yield waitForError;
|
||||
ok(detach, "testDirectorScript_errorOnDetach detach event received");
|
||||
ok(error, "testDirectorScript_errorOnDetach detach error received");
|
||||
assertIsDirectorScriptError(error);
|
||||
}
|
||||
|
||||
function* installAndEnableDirectorScript(directorManager, directorScriptDef) {
|
||||
let { scriptId } = directorScriptDef;
|
||||
|
||||
DirectorRegistry.install(scriptId, directorScriptDef);
|
||||
|
||||
let waitForAttach = once(directorManager, "director-script-attach");
|
||||
let waitForError = once(directorManager, "director-script-error");
|
||||
|
||||
directorManager.enableByScriptIds([scriptId], {reload: false});
|
||||
|
||||
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
|
||||
|
||||
return attachOrErrorEvent;
|
||||
}
|
||||
|
||||
function assertIsDirectorScriptError(error) {
|
||||
ok(!!error.message, "errors should contain a message");
|
||||
ok(!!error.stack, "errors should contain a stack trace");
|
||||
ok(!!error.fileName, "errors should contain a fileName");
|
||||
ok(typeof error.columnNumber == "number", "errors should contain a columnNumber");
|
||||
ok(typeof error.lineNumber == "number", "errors should contain a lineNumber");
|
||||
|
||||
ok(!!error.directorScriptId, "errors should contain a directorScriptId");
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {DirectorManagerFront} = require("devtools/shared/fronts/director-manager");
|
||||
const {DirectorRegistry} = require("devtools/server/actors/director-registry");
|
||||
|
||||
DirectorRegistry.clear();
|
||||
|
||||
add_task(function* () {
|
||||
yield addTab(MAIN_DOMAIN + "director-script-target.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
|
||||
DirectorRegistry.clear();
|
||||
let directorManager = DirectorManagerFront(client, form);
|
||||
|
||||
// director scripts attach method defaults to module.exports
|
||||
let attachModuleExports = yield testDirectorScriptExports(directorManager, {
|
||||
scriptId: "testDirectorScript_moduleExports",
|
||||
scriptCode: "(" + (function () {
|
||||
module.exports = function () {};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
});
|
||||
ok(attachModuleExports.port, "testDirectorScript_moduleExports attach event received");
|
||||
|
||||
// director scripts attach method can be configured using the attachMethod scriptOptions
|
||||
let attachExportsAttach = yield testDirectorScriptExports(directorManager, {
|
||||
scriptId: "testDirectorScript_exportsAttach",
|
||||
scriptCode: "(" + (function () {
|
||||
exports.attach = function () {};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
ok(attachExportsAttach.port, "testDirectorScript_exportsAttach attach event received");
|
||||
|
||||
// director scripts without an attach method generates an error event
|
||||
let errorUndefinedAttachMethod = yield testDirectorScriptExports(directorManager, {
|
||||
scriptId: "testDirectorScript_undefinedAttachMethod",
|
||||
scriptCode: "(" + (function () {
|
||||
// this director script should raise an error
|
||||
// because it doesn't export any attach method
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {
|
||||
attachMethod: "attach"
|
||||
}
|
||||
});
|
||||
let { message } = errorUndefinedAttachMethod;
|
||||
ok(!!message, "testDirectorScript_undefinedAttachMethod error event received");
|
||||
assertIsDirectorScriptError(errorUndefinedAttachMethod);
|
||||
|
||||
yield client.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
DirectorRegistry.clear();
|
||||
});
|
||||
|
||||
function assertIsDirectorScriptError(error) {
|
||||
ok(!!error.message, "errors should contain a message");
|
||||
ok(!!error.stack, "errors should contain a stack trace");
|
||||
ok(!!error.fileName, "errors should contain a fileName");
|
||||
ok(typeof error.columnNumber == "number", "errors should contain a columnNumber");
|
||||
ok(typeof error.lineNumber == "number", "errors should contain a lineNumber");
|
||||
|
||||
ok(!!error.directorScriptId, "errors should contain a directorScriptId");
|
||||
}
|
||||
|
||||
function* testDirectorScriptExports(directorManager, directorScriptDef) {
|
||||
let { scriptId } = directorScriptDef;
|
||||
|
||||
DirectorRegistry.install(scriptId, directorScriptDef);
|
||||
|
||||
let waitForAttach = once(directorManager, "director-script-attach");
|
||||
let waitForError = once(directorManager, "director-script-error");
|
||||
directorManager.enableByScriptIds([scriptId], {reload: false});
|
||||
|
||||
let attachOrErrorEvent = yield Promise.race([waitForAttach, waitForError]);
|
||||
|
||||
return attachOrErrorEvent;
|
||||
}
|
@ -6,7 +6,6 @@ support-files =
|
||||
Debugger.Source.prototype.element.js
|
||||
Debugger.Source.prototype.element-2.js
|
||||
Debugger.Source.prototype.element.html
|
||||
director-helpers.js
|
||||
hello-actor.js
|
||||
inspector_css-properties.html
|
||||
inspector_getImageData.html
|
||||
@ -37,8 +36,6 @@ support-files =
|
||||
[test_Debugger.Source.prototype.element.html]
|
||||
[test_Debugger.Script.prototype.global.html]
|
||||
[test_device.html]
|
||||
[test_director.html]
|
||||
[test_director_connectToChild.html]
|
||||
[test_executeInGlobal-outerized_this.html]
|
||||
[test_framerate_01.html]
|
||||
[test_framerate_02.html]
|
||||
|
@ -1,48 +0,0 @@
|
||||
/* exported DirectorRegistryFront, DirectorManagerFront, Task,
|
||||
newConnectedDebuggerClient, purgeInstalledDirectorScripts */
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const {DebuggerClient} = require("devtools/shared/client/main");
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const Services = require("Services");
|
||||
|
||||
// Always log packets when running tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
Services.prefs.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("devtools.debugger.log");
|
||||
Services.prefs.clearUserPref("dom.mozBrowserFramesEnabled");
|
||||
});
|
||||
|
||||
const { DirectorRegistry } = require("devtools/server/actors/director-registry");
|
||||
const { DirectorRegistryFront } = require("devtools/shared/fronts/director-registry");
|
||||
|
||||
const { DirectorManagerFront } = require("devtools/shared/fronts/director-manager");
|
||||
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
/** *********************************
|
||||
* director helpers functions
|
||||
**********************************/
|
||||
|
||||
function* newConnectedDebuggerClient(opts) {
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let client = new DebuggerClient(transport);
|
||||
|
||||
yield client.connect();
|
||||
|
||||
let root = yield client.listTabs();
|
||||
|
||||
return {
|
||||
client: client,
|
||||
root: root,
|
||||
transport: transport
|
||||
};
|
||||
}
|
||||
|
||||
function purgeInstalledDirectorScripts() {
|
||||
DirectorRegistry.clear();
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug </title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript" src="./director-helpers.js"></script>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
window.onload = function () {
|
||||
Task.spawn(function* () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [
|
||||
runDirectorRegistryActorTest
|
||||
].map((testCase) => {
|
||||
return function* () {
|
||||
setup();
|
||||
yield testCase().then(null, (e) => {
|
||||
console.error("Exception during testCase run", e);
|
||||
ok(false, "Exception during testCase run: "
|
||||
+ [e, e.fileName, e.lineNumber].join("\n\t"));
|
||||
});
|
||||
|
||||
teardown();
|
||||
};
|
||||
});
|
||||
|
||||
for (let test of tests) {
|
||||
yield test();
|
||||
}
|
||||
}).then(
|
||||
function success() {
|
||||
SimpleTest.finish();
|
||||
},
|
||||
function error(e) {
|
||||
console.error("Exception during testCase run", e);
|
||||
ok(false, "Exception during testCase run: "
|
||||
+ [e, e.fileName, e.lineNumber].join("\n\t"));
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let targetWin = null;
|
||||
|
||||
function setup() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init(() => true);
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
SimpleTest.registerCleanupFunction(teardown);
|
||||
}
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
purgeInstalledDirectorScripts();
|
||||
|
||||
DebuggerServer.destroy();
|
||||
if (targetWin) {
|
||||
targetWin.close();
|
||||
}
|
||||
}
|
||||
|
||||
function runDirectorRegistryActorTest() {
|
||||
let testDirectorScriptOptions = {
|
||||
scriptCode: "(" + (function () {
|
||||
module.exports = function ({port}) {
|
||||
port.onmessage = function (evt) {
|
||||
// echo messages
|
||||
evt.target.postMessage(evt.data);
|
||||
};
|
||||
};
|
||||
}).toString() + ")();",
|
||||
scriptOptions: {}
|
||||
};
|
||||
|
||||
return Task.spawn(function* () {
|
||||
let { client, root } = yield newConnectedDebuggerClient();
|
||||
|
||||
let directorRegistryClient = new DirectorRegistryFront(client, root);
|
||||
|
||||
let installed = yield directorRegistryClient.install("testDirectorScript",
|
||||
testDirectorScriptOptions);
|
||||
is(installed, true, "DirectorManager.install returns true");
|
||||
|
||||
let list = yield directorRegistryClient.list();
|
||||
is(JSON.stringify(list), JSON.stringify(["testDirectorScript"]),
|
||||
"DirectorManager.list contains the installed director script");
|
||||
|
||||
let uninstalled = yield directorRegistryClient.uninstall("testDirectorScript");
|
||||
is(uninstalled, true, "DirectorManager.uninstall return true");
|
||||
|
||||
yield client.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,97 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug </title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript" src="./director-helpers.js"></script>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
window.onload = function () {
|
||||
Task.spawn(function* () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [
|
||||
runPropagateDirectorScriptsToChildTest,
|
||||
].map((testCase) => {
|
||||
return function* () {
|
||||
setup();
|
||||
yield testCase().then(null, (e) => {
|
||||
ok(false, "Exception during testCase run: "
|
||||
+ [e, e.fileName, e.lineNumber].join("\n\t"));
|
||||
});
|
||||
|
||||
teardown();
|
||||
};
|
||||
});
|
||||
|
||||
for (let test of tests) {
|
||||
yield test();
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
};
|
||||
|
||||
function setup() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init(() => true);
|
||||
DebuggerServer.addBrowserActors();
|
||||
SimpleTest.registerCleanupFunction(function () {
|
||||
DebuggerServer.destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
purgeInstalledDirectorScripts();
|
||||
DebuggerServer.destroy();
|
||||
}
|
||||
|
||||
function runPropagateDirectorScriptsToChildTest() {
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.mozbrowser = true;
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
return Task.spawn(function* () {
|
||||
let { client, root, transport } = yield newConnectedDebuggerClient();
|
||||
|
||||
let directorRegistryClient = new DirectorRegistryFront(client, root);
|
||||
|
||||
// install a director script
|
||||
yield directorRegistryClient.install("testPropagatedDirectorScript", {
|
||||
scriptCode: "console.log('director script test');",
|
||||
scriptOptions: {}
|
||||
});
|
||||
|
||||
let conn = transport._serverConnection;
|
||||
let childActor = yield DebuggerServer.connectToChild(conn, iframe);
|
||||
|
||||
ok(typeof childActor.directorManagerActor !== "undefined",
|
||||
"childActor.directorActor should be defined");
|
||||
|
||||
let childDirectorManagerClient = new DirectorManagerFront(client, childActor);
|
||||
|
||||
let directorScriptList = yield childDirectorManagerClient.list();
|
||||
|
||||
ok(directorScriptList.installed.length === 1 &&
|
||||
directorScriptList.installed[0] === "testPropagatedDirectorScript",
|
||||
"director scripts propagated correctly");
|
||||
|
||||
yield client.close();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,47 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
const {
|
||||
messagePortSpec,
|
||||
directorScriptSpec,
|
||||
directorManagerSpec,
|
||||
} = require("devtools/shared/specs/director-manager");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the MessagePortActor.
|
||||
*/
|
||||
const MessagePortFront = protocol.FrontClassWithSpec(messagePortSpec, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
}
|
||||
});
|
||||
|
||||
exports.MessagePortFront = MessagePortFront;
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the DirectorScriptActor.
|
||||
*/
|
||||
const DirectorScriptFront = protocol.FrontClassWithSpec(directorScriptSpec, {
|
||||
initialize: function (client, form) {
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
}
|
||||
});
|
||||
|
||||
exports.DirectorScriptFront = DirectorScriptFront;
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the DirectorManagerActor.
|
||||
*/
|
||||
const DirectorManagerFront = protocol.FrontClassWithSpec(directorManagerSpec, {
|
||||
initialize: function (client, { directorManagerActor }) {
|
||||
protocol.Front.prototype.initialize.call(this, client, {
|
||||
actor: directorManagerActor
|
||||
});
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
exports.DirectorManagerFront = DirectorManagerFront;
|
@ -1,21 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
const {directorRegistrySpec} = require("devtools/shared/specs/director-registry");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
|
||||
/**
|
||||
* The corresponding Front object for the DirectorRegistryActor.
|
||||
*/
|
||||
const DirectorRegistryFront = protocol.FrontClassWithSpec(directorRegistrySpec, {
|
||||
initialize: function (client, { directorRegistryActor }) {
|
||||
protocol.Front.prototype.initialize.call(this, client, {
|
||||
actor: directorRegistryActor
|
||||
});
|
||||
this.manage(this);
|
||||
}
|
||||
});
|
||||
|
||||
exports.DirectorRegistryFront = DirectorRegistryFront;
|
@ -13,8 +13,6 @@ DevToolsModules(
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
'device.js',
|
||||
'director-manager.js',
|
||||
'director-registry.js',
|
||||
'emulation.js',
|
||||
'eventlooplag.js',
|
||||
'framerate.js',
|
||||
|
@ -1,190 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
const {
|
||||
types,
|
||||
Arg,
|
||||
Option,
|
||||
RetVal,
|
||||
generateActorSpec,
|
||||
} = require("devtools/shared/protocol");
|
||||
|
||||
/**
|
||||
* Type describing a messageport event
|
||||
*/
|
||||
types.addDictType("messageportevent", {
|
||||
isTrusted: "boolean",
|
||||
data: "nullable:primitive",
|
||||
origin: "nullable:string",
|
||||
lastEventId: "nullable:string",
|
||||
source: "messageport",
|
||||
ports: "nullable:array:messageport"
|
||||
});
|
||||
|
||||
const messagePortSpec = generateActorSpec({
|
||||
typeName: "messageport",
|
||||
|
||||
/**
|
||||
* Events emitted by this actor.
|
||||
*/
|
||||
events: {
|
||||
message: {
|
||||
type: "message",
|
||||
msg: Arg(0, "nullable:messageportevent")
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
postMessage: {
|
||||
oneway: true,
|
||||
request: {
|
||||
msg: Arg(0, "nullable:json")
|
||||
}
|
||||
},
|
||||
start: {
|
||||
oneway: true,
|
||||
request: {}
|
||||
},
|
||||
close: {
|
||||
oneway: true,
|
||||
request: {}
|
||||
},
|
||||
finalize: {
|
||||
oneway: true
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.messagePortSpec = messagePortSpec;
|
||||
|
||||
/**
|
||||
* Type describing a director-script error
|
||||
*/
|
||||
types.addDictType("director-script-error", {
|
||||
directorScriptId: "string",
|
||||
message: "nullable:string",
|
||||
stack: "nullable:string",
|
||||
fileName: "nullable:string",
|
||||
lineNumber: "nullable:number",
|
||||
columnNumber: "nullable:number"
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing a director-script attach event
|
||||
*/
|
||||
types.addDictType("director-script-attach", {
|
||||
directorScriptId: "string",
|
||||
url: "string",
|
||||
innerId: "number",
|
||||
port: "nullable:messageport"
|
||||
});
|
||||
|
||||
/**
|
||||
* Type describing a director-script detach event
|
||||
*/
|
||||
types.addDictType("director-script-detach", {
|
||||
directorScriptId: "string",
|
||||
innerId: "number"
|
||||
});
|
||||
|
||||
const directorScriptSpec = generateActorSpec({
|
||||
typeName: "director-script",
|
||||
|
||||
/**
|
||||
* Events emitted by this actor.
|
||||
*/
|
||||
events: {
|
||||
error: {
|
||||
type: "error",
|
||||
data: Arg(0, "director-script-error")
|
||||
},
|
||||
attach: {
|
||||
type: "attach",
|
||||
data: Arg(0, "director-script-attach")
|
||||
},
|
||||
detach: {
|
||||
type: "detach",
|
||||
data: Arg(0, "director-script-detach")
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setup: {
|
||||
request: {
|
||||
reload: Option(0, "boolean"),
|
||||
skipAttach: Option(0, "boolean")
|
||||
},
|
||||
oneway: true
|
||||
},
|
||||
getMessagePort: {
|
||||
request: { },
|
||||
response: {
|
||||
port: RetVal("nullable:messageport")
|
||||
}
|
||||
},
|
||||
finalize: {
|
||||
oneway: true
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.directorScriptSpec = directorScriptSpec;
|
||||
|
||||
const directorManagerSpec = generateActorSpec({
|
||||
typeName: "director-manager",
|
||||
|
||||
/**
|
||||
* Events emitted by this actor.
|
||||
*/
|
||||
events: {
|
||||
"director-script-error": {
|
||||
type: "error",
|
||||
data: Arg(0, "director-script-error")
|
||||
},
|
||||
"director-script-attach": {
|
||||
type: "attach",
|
||||
data: Arg(0, "director-script-attach")
|
||||
},
|
||||
"director-script-detach": {
|
||||
type: "detach",
|
||||
data: Arg(0, "director-script-detach")
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
list: {
|
||||
response: {
|
||||
directorScripts: RetVal("json")
|
||||
}
|
||||
},
|
||||
enableByScriptIds: {
|
||||
oneway: true,
|
||||
request: {
|
||||
selectedIds: Arg(0, "array:string"),
|
||||
reload: Option(1, "boolean")
|
||||
}
|
||||
},
|
||||
disableByScriptIds: {
|
||||
oneway: true,
|
||||
request: {
|
||||
selectedIds: Arg(0, "array:string"),
|
||||
reload: Option(1, "boolean")
|
||||
}
|
||||
},
|
||||
getByScriptId: {
|
||||
request: {
|
||||
scriptId: Arg(0, "string")
|
||||
},
|
||||
response: {
|
||||
directorScript: RetVal("director-script")
|
||||
}
|
||||
},
|
||||
finalize: {
|
||||
oneway: true
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.directorManagerSpec = directorManagerSpec;
|
@ -1,41 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
const {Arg, Option, RetVal, generateActorSpec} = require("devtools/shared/protocol");
|
||||
|
||||
const directorRegistrySpec = generateActorSpec({
|
||||
typeName: "director-registry",
|
||||
|
||||
methods: {
|
||||
finalize: {
|
||||
oneway: true
|
||||
},
|
||||
install: {
|
||||
request: {
|
||||
scriptId: Arg(0, "string"),
|
||||
scriptCode: Option(1, "string"),
|
||||
scriptOptions: Option(1, "nullable:json")
|
||||
},
|
||||
response: {
|
||||
success: RetVal("boolean")
|
||||
}
|
||||
},
|
||||
uninstall: {
|
||||
request: {
|
||||
scritpId: Arg(0, "string")
|
||||
},
|
||||
response: {
|
||||
success: RetVal("boolean")
|
||||
}
|
||||
},
|
||||
list: {
|
||||
response: {
|
||||
directorScripts: RetVal("array:string")
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
exports.directorRegistrySpec = directorRegistrySpec;
|
@ -14,8 +14,6 @@ DevToolsModules(
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
'device.js',
|
||||
'director-manager.js',
|
||||
'director-registry.js',
|
||||
'emulation.js',
|
||||
'environment.js',
|
||||
'eventlooplag.js',
|
||||
|
@ -289,7 +289,12 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
||||
if (!presContext) {
|
||||
break;
|
||||
}
|
||||
rootFrame = presContext->PresShell()->GetRootScrollFrame();
|
||||
nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
|
||||
if (rootScrollFrame) {
|
||||
rootFrame = rootScrollFrame;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
root = rootFrame->GetContent()->AsElement();
|
||||
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
|
||||
|
12
dom/base/crashtests/1353529-inner.html
Normal file
12
dom/base/crashtests/1353529-inner.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body onload="boom()">
|
||||
<div id="target"></div>
|
||||
<script>
|
||||
function boom() {
|
||||
var io = new IntersectionObserver(function () { }, { });
|
||||
io.observe(document.getElementById('target'));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
8
dom/base/crashtests/1353529.xul
Normal file
8
dom/base/crashtests/1353529.xul
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<xhtml:div>
|
||||
<iframe src="1353529-inner.html"></iframe>
|
||||
</xhtml:div>
|
||||
</window>
|
@ -212,4 +212,5 @@ pref(dom.IntersectionObserver.enabled,true) load 1324209.html
|
||||
pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html
|
||||
pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html
|
||||
pref(dom.IntersectionObserver.enabled,true) load 1332939.html
|
||||
pref(dom.IntersectionObserver.enabled,true) load 1353529.xul
|
||||
pref(dom.webcomponents.enabled,true) load 1341693.html
|
||||
|
@ -12,39 +12,24 @@
|
||||
SimpleTest.requestCompleteLog();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[ "dom.disable_open_during_load", false ]
|
||||
]
|
||||
}).then(function() {
|
||||
info("after pref");
|
||||
var sub = encodeURI("data:text/html,<!DOCTYPE html>\n"+
|
||||
"<html><script>"+
|
||||
"var context = new AudioContext();"+
|
||||
"setTimeout(function(){window.close();},1000);\x3C/script></html>");
|
||||
window.onload = function(){
|
||||
info("after onload");
|
||||
var a = window.open(sub);
|
||||
info("after open: " + a);
|
||||
a.onbeforeunload = function(){
|
||||
setTimeout(function(){
|
||||
try {
|
||||
info("before sp");
|
||||
a.context.createScriptProcessor(512, 1, 1);
|
||||
info("after sp");
|
||||
} catch(e) {
|
||||
ok (true,"got exception");
|
||||
}
|
||||
setTimeout(function() {
|
||||
info("finish");
|
||||
ok (true,"no crash");
|
||||
SimpleTest.finish();
|
||||
}, 0);
|
||||
}, 0);
|
||||
};
|
||||
};
|
||||
});
|
||||
var sub = encodeURI("data:text/html,<!DOCTYPE html>\n"+
|
||||
"<html><script>"+
|
||||
"var context = new AudioContext();"+
|
||||
"setTimeout(function(){window.close();},1000);\x3C/script></html>");
|
||||
var a = window.open(sub);
|
||||
a.onbeforeunload = function() {
|
||||
setTimeout(function(){
|
||||
try {
|
||||
a.context.createScriptProcessor(512, 1, 1);
|
||||
} catch(e) {
|
||||
ok (true,"got exception");
|
||||
}
|
||||
setTimeout(function() {
|
||||
ok (true,"no crash");
|
||||
SimpleTest.finish();
|
||||
}, 0);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc
|
||||
the need to run the cargo update command in js/src as well. Hopefully this will
|
||||
be resolved soon.
|
||||
|
||||
Latest Commit: dafe3579e8dc886e6584116dc52a9362b543c169
|
||||
Latest Commit: 7463ae5908ca1d4065763a8753af5d72a6f78b85
|
||||
|
@ -89,6 +89,18 @@ public:
|
||||
const uint64_t mSerial;
|
||||
};
|
||||
|
||||
static bool
|
||||
WrapWithWebRenderTextureHost(LayersBackend aBackend,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
if (!gfxVars::UseWebRender() ||
|
||||
(aFlags & TextureFlags::SNAPSHOT) ||
|
||||
(aBackend != LayersBackend::LAYERS_WR)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
PTextureParent*
|
||||
TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
|
||||
@ -184,52 +196,55 @@ TextureHost::Create(const SurfaceDescriptor& aDesc,
|
||||
LayersBackend aBackend,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
RefPtr<TextureHost> result;
|
||||
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorDIB:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
|
||||
return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
|
||||
result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
|
||||
break;
|
||||
|
||||
case SurfaceDescriptor::TEGLImageDescriptor:
|
||||
case SurfaceDescriptor::TSurfaceTextureDescriptor:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
|
||||
return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
|
||||
result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
|
||||
break;
|
||||
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
|
||||
if (aBackend == LayersBackend::LAYERS_OPENGL ||
|
||||
aBackend == LayersBackend::LAYERS_WR) {
|
||||
return CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
|
||||
result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
|
||||
break;
|
||||
} else {
|
||||
return CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
|
||||
result = CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef MOZ_X11
|
||||
case SurfaceDescriptor::TSurfaceDescriptorX11: {
|
||||
const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
|
||||
return MakeAndAddRef<X11TextureHost>(aFlags, desc);
|
||||
result = MakeAndAddRef<X11TextureHost>(aFlags, desc);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
case SurfaceDescriptor::TSurfaceDescriptorD3D10:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
|
||||
return CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
|
||||
result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
MOZ_CRASH("GFX: Unsupported Surface type host");
|
||||
}
|
||||
}
|
||||
|
||||
bool WrapWithWebRenderTextureHost(LayersBackend aBackend,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
if (!gfxVars::UseWebRender() ||
|
||||
(aFlags & TextureFlags::SNAPSHOT) ||
|
||||
(aBackend != LayersBackend::LAYERS_WR)) {
|
||||
return false;
|
||||
if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
|
||||
result = new WebRenderTextureHost(aDesc, aFlags, result);
|
||||
}
|
||||
return true;
|
||||
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<TextureHost>
|
||||
@ -249,18 +264,12 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
|
||||
bufferDesc.desc(),
|
||||
aDeallocator,
|
||||
aFlags);
|
||||
if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
|
||||
result = new WebRenderTextureHost(aFlags, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MemoryOrShmem::Tuintptr_t: {
|
||||
result = new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
|
||||
bufferDesc.desc(),
|
||||
aFlags);
|
||||
if (WrapWithWebRenderTextureHost(aBackend, aFlags)) {
|
||||
result = new WebRenderTextureHost(aFlags, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -46,6 +46,7 @@ class CompositorBridgeParent;
|
||||
class SurfaceDescriptor;
|
||||
class HostIPCAllocator;
|
||||
class ISurfaceAllocator;
|
||||
class MacIOSurfaceTextureHostOGL;
|
||||
class TextureHostOGL;
|
||||
class TextureReadLock;
|
||||
class TextureSourceOGL;
|
||||
@ -584,7 +585,7 @@ public:
|
||||
TextureReadLock* GetReadLock() { return mReadLock; }
|
||||
|
||||
virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
|
||||
|
||||
virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() { return nullptr; }
|
||||
virtual WebRenderTextureHost* AsWebRenderTextureHost() { return nullptr; }
|
||||
|
||||
protected:
|
||||
|
@ -686,8 +686,11 @@ CompositorBridgeParent::PauseComposition()
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
|
||||
mCompositor->Pause();
|
||||
|
||||
if (!gfxVars::UseWebRender()) {
|
||||
mCompositor->Pause();
|
||||
} else {
|
||||
mWrBridge->Pause();
|
||||
}
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
DidComposite(now, now);
|
||||
}
|
||||
@ -704,7 +707,8 @@ CompositorBridgeParent::ResumeComposition()
|
||||
|
||||
MonitorAutoLock lock(mResumeCompositionMonitor);
|
||||
|
||||
if (!mCompositor->Resume()) {
|
||||
bool resumed = gfxVars::UseWebRender() ? mWrBridge->Resume() : mCompositor->Resume();
|
||||
if (!resumed) {
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// We can't get a surface. This could be because the activity changed between
|
||||
// the time resume was scheduled and now.
|
||||
|
@ -391,7 +391,8 @@ UNIFIED_SOURCES += [
|
||||
'wr/WebRenderLayersLogging.cpp',
|
||||
'wr/WebRenderPaintedLayer.cpp',
|
||||
'wr/WebRenderTextLayer.cpp',
|
||||
'wr/WebRenderTextureHost.cpp',
|
||||
# XXX here are some unified build error.
|
||||
#'wr/WebRenderTextureHost.cpp'
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
@ -400,6 +401,7 @@ SOURCES += [
|
||||
'Layers.cpp',
|
||||
'LayerTreeInvalidation.cpp',
|
||||
'PersistentBufferProvider.cpp',
|
||||
'wr/WebRenderTextureHost.cpp',
|
||||
]
|
||||
|
||||
# Disable RTTI in google protocol buffer
|
||||
|
@ -98,6 +98,13 @@ public:
|
||||
virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
|
||||
#endif
|
||||
|
||||
virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() override { return this; }
|
||||
|
||||
MacIOSurface* GetMacIOSurface()
|
||||
{
|
||||
return mSurface;
|
||||
}
|
||||
|
||||
protected:
|
||||
GLTextureSource* CreateTextureSourceForPlane(size_t aPlane);
|
||||
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
#include "mozilla/widget/CompositorWidget.h"
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
# include "mozilla/widget/AndroidCompositorWidget.h"
|
||||
#endif
|
||||
|
||||
bool is_in_main_thread()
|
||||
{
|
||||
return NS_IsMainThread();
|
||||
@ -115,6 +119,7 @@ WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompos
|
||||
, mParentLayerObserverEpoch(0)
|
||||
, mWrEpoch(0)
|
||||
, mIdNameSpace(++sIdNameSpace)
|
||||
, mPaused(false)
|
||||
, mDestroyed(false)
|
||||
{
|
||||
MOZ_ASSERT(mCompositableHolder);
|
||||
@ -135,6 +140,15 @@ WebRenderBridgeParent::RecvCreate(const gfx::IntSize& aSize)
|
||||
|
||||
MOZ_ASSERT(mApi);
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// XXX temporary hack.
|
||||
// XXX Remove it when APZ is supported.
|
||||
widget::AndroidCompositorWidget* widget = mWidget->AsAndroid();
|
||||
if (widget) {
|
||||
widget->SetFirstPaintViewport(LayerIntPoint(0, 0), CSSToLayerScale(), CSSRect(0, 0, aSize.width, aSize.height));
|
||||
}
|
||||
#endif
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
@ -344,15 +358,27 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
|
||||
}
|
||||
WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
|
||||
if (wrTexture) {
|
||||
// XXX handling YUV
|
||||
gfx::SurfaceFormat format =
|
||||
wrTexture->GetFormat() == SurfaceFormat::YUV ? SurfaceFormat::B8G8R8A8 : wrTexture->GetFormat();
|
||||
wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetRGBStride(), format);
|
||||
mApi->AddExternalImageBuffer(key,
|
||||
descriptor,
|
||||
wrTexture->GetExternalImageKey());
|
||||
mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
|
||||
keysToDelete.push_back(key);
|
||||
if (wrTexture->IsWrappingNativeHandle()) {
|
||||
// XXX only for MacIOSurface right now.
|
||||
// XXX remove the redundant codes for both native handle and yuv case.
|
||||
wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetReadFormat());
|
||||
mApi->AddExternalImageHandle(key,
|
||||
descriptor,
|
||||
wrTexture->GetExternalImageKey());
|
||||
mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
|
||||
keysToDelete.push_back(key);
|
||||
} else {
|
||||
// XXX handling YUV
|
||||
gfx::SurfaceFormat format =
|
||||
wrTexture->GetFormat() == SurfaceFormat::YUV ? SurfaceFormat::B8G8R8A8 : wrTexture->GetFormat();
|
||||
wr::ImageDescriptor descriptor(wrTexture->GetSize(), wrTexture->GetRGBStride(), format);
|
||||
mApi->AddExternalImageBuffer(key,
|
||||
descriptor,
|
||||
wrTexture->GetExternalImageKey());
|
||||
mCompositableHolder->HoldExternalImage(mPipelineId, aEpoch, texture->AsWebRenderTextureHost());
|
||||
keysToDelete.push_back(key);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
|
||||
@ -414,6 +440,7 @@ WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
MOZ_ASSERT(!mPaused);
|
||||
|
||||
RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
|
||||
if (!texture) {
|
||||
@ -544,6 +571,9 @@ WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
void
|
||||
WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
|
||||
{
|
||||
if (mPaused) {
|
||||
return;
|
||||
}
|
||||
mApi->GenerateFrame();
|
||||
}
|
||||
|
||||
@ -618,6 +648,36 @@ WebRenderBridgeParent::ScheduleComposition()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeParent::Pause()
|
||||
{
|
||||
MOZ_ASSERT(mWidget);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (!mWidget || mDestroyed) {
|
||||
return;
|
||||
}
|
||||
mApi->Pause();
|
||||
#endif
|
||||
mPaused = true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderBridgeParent::Resume()
|
||||
{
|
||||
MOZ_ASSERT(mWidget);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (!mWidget || mDestroyed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mApi->Resume()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
mPaused = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeParent::ClearResources()
|
||||
{
|
||||
|
@ -114,6 +114,9 @@ public:
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
void SetWebRenderProfilerEnabled(bool aEnabled);
|
||||
|
||||
void Pause();
|
||||
bool Resume();
|
||||
|
||||
void Destroy();
|
||||
|
||||
// CompositorVsyncSchedulerOwner
|
||||
@ -207,6 +210,7 @@ private:
|
||||
uint32_t mWrEpoch;
|
||||
uint32_t mIdNameSpace;
|
||||
|
||||
bool mPaused;
|
||||
bool mDestroyed;
|
||||
|
||||
static uint32_t sIdNameSpace;
|
||||
|
@ -6,29 +6,32 @@
|
||||
#include "WebRenderTextureHost.h"
|
||||
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/webrender/RenderBufferTextureHost.h"
|
||||
#include "mozilla/webrender/RenderTextureHost.h"
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "mozilla/layers/MacIOSurfaceTextureHostOGL.h"
|
||||
#include "mozilla/webrender/RenderMacIOSurfaceTextureHostOGL.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
uint64_t WebRenderTextureHost::sSerialCounter(0);
|
||||
|
||||
WebRenderTextureHost::WebRenderTextureHost(TextureFlags aFlags,
|
||||
WebRenderTextureHost::WebRenderTextureHost(const SurfaceDescriptor& aDesc,
|
||||
TextureFlags aFlags,
|
||||
TextureHost* aTexture)
|
||||
: TextureHost(aFlags)
|
||||
, mExternalImageId(++sSerialCounter)
|
||||
, mIsWrappingNativeHandle(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebRenderTextureHost);
|
||||
mWrappedTextureHost = aTexture;
|
||||
|
||||
// XXX support only BufferTextureHost for now.
|
||||
BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
|
||||
MOZ_ASSERT(bufferTexture);
|
||||
RefPtr<wr::RenderTextureHost> texture =
|
||||
new wr::RenderTextureHost(bufferTexture->GetBuffer(),
|
||||
bufferTexture->GetBufferDescriptor());
|
||||
wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
|
||||
CreateRenderTextureHost(aDesc, aTexture);
|
||||
}
|
||||
|
||||
WebRenderTextureHost::~WebRenderTextureHost()
|
||||
@ -37,6 +40,37 @@ WebRenderTextureHost::~WebRenderTextureHost()
|
||||
wr::RenderThread::Get()->UnregisterExternalImage(mExternalImageId);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderTextureHost::CreateRenderTextureHost(const layers::SurfaceDescriptor& aDesc,
|
||||
TextureHost* aTexture)
|
||||
{
|
||||
RefPtr<wr::RenderTextureHost> texture;
|
||||
|
||||
switch (aDesc.type()) {
|
||||
case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
|
||||
BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
|
||||
MOZ_ASSERT(bufferTexture);
|
||||
texture = new wr::RenderBufferTextureHost(bufferTexture->GetBuffer(),
|
||||
bufferTexture->GetBufferDescriptor());
|
||||
mIsWrappingNativeHandle = false;
|
||||
break;
|
||||
}
|
||||
#ifdef XP_MACOSX
|
||||
case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
|
||||
MacIOSurfaceTextureHostOGL* macTexture = aTexture->AsMacIOSurfaceTextureHost();
|
||||
MOZ_ASSERT(macTexture);
|
||||
texture = new wr::RenderMacIOSurfaceTextureHostOGL(macTexture->GetMacIOSurface());
|
||||
mIsWrappingNativeHandle = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
gfxCriticalError() << "No WR implement for texture type:" << aDesc.type();
|
||||
}
|
||||
|
||||
wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId, texture);
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderTextureHost::Lock()
|
||||
{
|
||||
@ -99,6 +133,15 @@ WebRenderTextureHost::GetFormat() const
|
||||
return mWrappedTextureHost->GetFormat();
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat
|
||||
WebRenderTextureHost::GetReadFormat() const
|
||||
{
|
||||
if (!mWrappedTextureHost) {
|
||||
return gfx::SurfaceFormat::UNKNOWN;
|
||||
}
|
||||
return mWrappedTextureHost->GetReadFormat();
|
||||
}
|
||||
|
||||
int32_t
|
||||
WebRenderTextureHost::GetRGBStride()
|
||||
{
|
||||
@ -106,10 +149,10 @@ WebRenderTextureHost::GetRGBStride()
|
||||
return 0;
|
||||
}
|
||||
gfx::SurfaceFormat format = GetFormat();
|
||||
if (GetFormat() == SurfaceFormat::YUV) {
|
||||
if (GetFormat() == gfx::SurfaceFormat::YUV) {
|
||||
// XXX this stride is used until yuv image rendering by webrender is used.
|
||||
// Software converted RGB buffers strides are aliened to 16
|
||||
return GetAlignedStride<16>(GetSize().width, BytesPerPixel(SurfaceFormat::B8G8R8A8));
|
||||
return gfx::GetAlignedStride<16>(GetSize().width, BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8));
|
||||
}
|
||||
return ImageDataSerializer::ComputeRGBStride(format, GetSize().width);
|
||||
}
|
||||
|
@ -11,10 +11,19 @@
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class SurfaceDescriptor;
|
||||
|
||||
// This textureHost is specialized for WebRender usage. With WebRender, there is
|
||||
// no Compositor during composition. Instead, we use RendererOGL for composition.
|
||||
// So, there are some UNREACHABLE asserts for the original Compositor related
|
||||
// code path in this class. Furthermore, the RendererOGL runs at RenderThead
|
||||
// instead of Compositor thread. This class is also creating the corresponding
|
||||
// RenderXXXTextureHost used by RendererOGL at RenderThread.
|
||||
class WebRenderTextureHost : public TextureHost
|
||||
{
|
||||
public:
|
||||
WebRenderTextureHost(TextureFlags aFlags,
|
||||
WebRenderTextureHost(const SurfaceDescriptor& aDesc,
|
||||
TextureFlags aFlags,
|
||||
TextureHost* aTexture);
|
||||
virtual ~WebRenderTextureHost();
|
||||
|
||||
@ -28,6 +37,12 @@ public:
|
||||
|
||||
virtual gfx::SurfaceFormat GetFormat() const override;
|
||||
|
||||
// Return the format used for reading the texture. Some hardware specific
|
||||
// textureHosts use their special data representation internally, but we could
|
||||
// treat these textureHost as the read-format when we read them.
|
||||
// Please check TextureHost::GetReadFormat().
|
||||
virtual gfx::SurfaceFormat GetReadFormat() const override;
|
||||
|
||||
virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
|
||||
|
||||
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
|
||||
@ -45,10 +60,17 @@ public:
|
||||
uint64_t GetExternalImageKey() { return mExternalImageId; }
|
||||
|
||||
int32_t GetRGBStride();
|
||||
|
||||
bool IsWrappingNativeHandle() { return mIsWrappingNativeHandle; }
|
||||
|
||||
protected:
|
||||
void CreateRenderTextureHost(const SurfaceDescriptor& aDesc, TextureHost* aTexture);
|
||||
|
||||
RefPtr<TextureHost> mWrappedTextureHost;
|
||||
uint64_t mExternalImageId;
|
||||
|
||||
bool mIsWrappingNativeHandle;
|
||||
|
||||
static uint64_t sSerialCounter;
|
||||
};
|
||||
|
||||
|
@ -490,7 +490,7 @@ private:
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.background-image", LayersAllowBackgroundImage, false);
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.border-layers", LayersAllowBorderLayers, false);
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-inset-layers", LayersAllowInsetBoxShadow, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, false);
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.boxshadow-outer-layers", LayersAllowOuterBoxShadow, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_GFX_PREF(Live, "layers.advanced.bullet-layers", LayersAllowBulletLayers, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, bool, false);
|
||||
@ -498,6 +498,7 @@ private:
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_GFX_PREF(Live, "layers.advanced.image-layers", LayersAllowImageLayers, bool, false);
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.outline-layers", LayersAllowOutlineLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_GFX_PREF(Live, "layers.advanced.solid-color", LayersAllowSolidColorLayers, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.advanced.text-layers", LayersAllowTextLayers, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled", LayersAMDSwitchableGfxEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled", AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "webrender"
|
||||
version = "0.26.0"
|
||||
version = "0.30.0"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/webrender"
|
||||
@ -14,12 +14,12 @@ webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
|
||||
|
||||
[dependencies]
|
||||
app_units = "0.4"
|
||||
bincode = "1.0.0-alpha2"
|
||||
bincode = "1.0.0-alpha6"
|
||||
bit-set = "0.4"
|
||||
byteorder = "1.0"
|
||||
euclid = "0.11"
|
||||
fnv = "1.0"
|
||||
gleam = "0.4.1"
|
||||
gleam = "0.4.2"
|
||||
lazy_static = "0.2"
|
||||
log = "0.3"
|
||||
num-traits = "0.1.32"
|
||||
|
@ -40,5 +40,10 @@ void main(void) {
|
||||
|
||||
float clip_alpha = rounded_rect(local_pos);
|
||||
|
||||
oFragColor = vec4(min(alpha, clip_alpha), 0.0, 0.0, 1.0);
|
||||
float combined_alpha = min(alpha, clip_alpha);
|
||||
|
||||
// Select alpha or inverse alpha depending on clip in/out.
|
||||
float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
|
||||
|
||||
oFragColor = vec4(final_alpha, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
@ -8,3 +8,4 @@ varying vec3 vPos;
|
||||
flat varying vec4 vLocalRect;
|
||||
flat varying vec4 vClipRect;
|
||||
flat varying vec4 vClipRadius;
|
||||
flat varying float vClipMode;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
struct ClipRect {
|
||||
vec4 rect;
|
||||
vec4 dummy;
|
||||
vec4 mode;
|
||||
};
|
||||
|
||||
ClipRect fetch_clip_rect(int index) {
|
||||
@ -14,8 +14,7 @@ ClipRect fetch_clip_rect(int index) {
|
||||
ivec2 uv = get_fetch_uv_2(index);
|
||||
|
||||
rect.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
|
||||
//rect.dummy = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
|
||||
rect.dummy = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
rect.mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
|
||||
|
||||
return rect;
|
||||
}
|
||||
@ -70,6 +69,7 @@ void main(void) {
|
||||
vLocalRect = vi.clipped_local_rect;
|
||||
vPos = vi.local_pos;
|
||||
|
||||
vClipMode = clip.rect.mode.x;
|
||||
vClipRect = vec4(local_rect.xy, local_rect.xy + local_rect.zw);
|
||||
vClipRadius = vec4(clip.top_left.outer_inner_radius.x,
|
||||
clip.top_right.outer_inner_radius.x,
|
||||
|
@ -263,7 +263,7 @@ GradientStop fetch_gradient_stop(int index) {
|
||||
|
||||
struct RadialGradient {
|
||||
vec4 start_end_center;
|
||||
vec4 start_end_radius_extend_mode;
|
||||
vec4 start_end_radius_ratio_xy_extend_mode;
|
||||
};
|
||||
|
||||
RadialGradient fetch_radial_gradient(int index) {
|
||||
@ -272,7 +272,7 @@ RadialGradient fetch_radial_gradient(int index) {
|
||||
ivec2 uv = get_fetch_uv_2(index);
|
||||
|
||||
gradient.start_end_center = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
|
||||
gradient.start_end_radius_extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
|
||||
gradient.start_end_radius_ratio_xy_extend_mode = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
void main(void) {
|
||||
vec4 clip_scale = vec4(1.0, 1.0, 1.0, do_clip());
|
||||
|
||||
// Mirror and stretch the box shadow corner over the entire
|
||||
// primitives.
|
||||
vec2 uv = vMirrorPoint - abs(vUv.xy - vMirrorPoint);
|
||||
@ -16,5 +18,5 @@ void main(void) {
|
||||
uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
|
||||
|
||||
// Modulate the box shadow by the color.
|
||||
oFragColor = dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)));
|
||||
oFragColor = clip_scale * dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)));
|
||||
}
|
||||
|
@ -29,4 +29,6 @@ void main(void) {
|
||||
vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
|
||||
|
||||
vColor = bs.color;
|
||||
|
||||
write_clip(vi.screen_pos, prim.clip_area);
|
||||
}
|
||||
|
@ -171,6 +171,19 @@ void main(void) {
|
||||
vec4 Cb = texture(sCacheRGBA8, vUv0);
|
||||
vec4 Cs = texture(sCacheRGBA8, vUv1);
|
||||
|
||||
if (Cb.a == 0.0) {
|
||||
oFragColor = Cs;
|
||||
return;
|
||||
}
|
||||
if (Cs.a == 0.0) {
|
||||
oFragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// The mix-blend-mode functions assume no premultiplied alpha
|
||||
Cb.rgb /= Cb.a;
|
||||
Cs.rgb /= Cs.a;
|
||||
|
||||
// Return yellow if none of the branches match (shouldn't happen).
|
||||
vec4 result = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
|
||||
|
@ -12,5 +12,9 @@ void main(void) {
|
||||
#endif
|
||||
|
||||
alpha = min(alpha, do_clip());
|
||||
oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
|
||||
|
||||
// TODO(gw): Re-enable the gradient dither once we get the
|
||||
// reftests passing.
|
||||
//oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
|
||||
oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
|
||||
}
|
||||
|
@ -19,18 +19,18 @@ void main(void) {
|
||||
vec2 g01_x = mix(gradient.start_end_point.xx, gradient.start_end_point.zz,
|
||||
vec2(g0.offset.x, g1.offset.x));
|
||||
|
||||
// The start and end point of gradient might exceed the geometry rect. So clamp
|
||||
// it to the geometry rect.
|
||||
g01_x = clamp(g01_x, prim.local_rect.p0.xx, prim.local_rect.p0.xx + prim.local_rect.size.xx);
|
||||
// The gradient stops might exceed the geometry rect so clamp them
|
||||
vec2 g01_x_clamped = clamp(g01_x,
|
||||
prim.local_rect.p0.xx,
|
||||
prim.local_rect.p0.xx + prim.local_rect.size.xx);
|
||||
|
||||
// Calculate the rect using the clamped coords
|
||||
segment_rect.p0 = vec2(g01_x.x, prim.local_rect.p0.y);
|
||||
segment_rect.size = vec2(g01_x.y - g01_x.x, prim.local_rect.size.y);
|
||||
// Calculate the segment rect using the clamped coords
|
||||
segment_rect.p0 = vec2(g01_x_clamped.x, prim.local_rect.p0.y);
|
||||
segment_rect.size = vec2(g01_x_clamped.y - g01_x_clamped.x, prim.local_rect.size.y);
|
||||
axis = vec2(1.0, 0.0);
|
||||
|
||||
// We need to adjust the colors of the stops because they may have been clamped
|
||||
vec2 adjusted_offset =
|
||||
(g01_x - segment_rect.p0.xx) / segment_rect.size.xx;
|
||||
// Adjust the stop colors by how much they were clamped
|
||||
vec2 adjusted_offset = (g01_x_clamped - g01_x.xx) / (g01_x.y - g01_x.x);
|
||||
adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
|
||||
adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
|
||||
} else {
|
||||
@ -38,18 +38,18 @@ void main(void) {
|
||||
vec2 g01_y = mix(gradient.start_end_point.yy, gradient.start_end_point.ww,
|
||||
vec2(g0.offset.x, g1.offset.x));
|
||||
|
||||
// The start and end point of gradient might exceed the geometry rect. So clamp
|
||||
// it to the geometry rect.
|
||||
g01_y = clamp(g01_y, prim.local_rect.p0.yy, prim.local_rect.p0.yy + prim.local_rect.size.yy);
|
||||
// The gradient stops might exceed the geometry rect so clamp them
|
||||
vec2 g01_y_clamped = clamp(g01_y,
|
||||
prim.local_rect.p0.yy,
|
||||
prim.local_rect.p0.yy + prim.local_rect.size.yy);
|
||||
|
||||
// Calculate the rect using the clamped coords
|
||||
segment_rect.p0 = vec2(prim.local_rect.p0.x, g01_y.x);
|
||||
segment_rect.size = vec2(prim.local_rect.size.x, g01_y.y - g01_y.x);
|
||||
// Calculate the segment rect using the clamped coords
|
||||
segment_rect.p0 = vec2(prim.local_rect.p0.x, g01_y_clamped.x);
|
||||
segment_rect.size = vec2(prim.local_rect.size.x, g01_y_clamped.y - g01_y_clamped.x);
|
||||
axis = vec2(0.0, 1.0);
|
||||
|
||||
// We need to adjust the colors of the stops because they may have been clamped
|
||||
vec2 adjusted_offset =
|
||||
(g01_y - segment_rect.p0.yy) / segment_rect.size.yy;
|
||||
// Adjust the stop colors by how much they were clamped
|
||||
vec2 adjusted_offset = (g01_y_clamped - g01_y.xx) / (g01_y.y - g01_y.x);
|
||||
adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
|
||||
adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
|
||||
}
|
||||
|
@ -24,12 +24,15 @@ void main(void) {
|
||||
// account the spacing in between tiles. We only paint if our fragment does
|
||||
// not fall into that spacing.
|
||||
vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
|
||||
// We clamp the texture coordinates to the half-pixel offset from the borders
|
||||
// in order to avoid sampling outside of the texture area.
|
||||
vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
|
||||
st = clamp(st, vStRect.xy, vStRect.zw);
|
||||
|
||||
alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
|
||||
|
||||
#ifdef WR_FEATURE_TEXTURE_RECT
|
||||
// textureLod doesn't support sampler2DRect. Use texture() instead.
|
||||
oFragColor = vec4(alpha) * texture(sColor0, st);
|
||||
#else
|
||||
oFragColor = vec4(alpha) * textureLod(sColor0, st, 0.0);
|
||||
#endif
|
||||
}
|
||||
|
@ -2,10 +2,13 @@
|
||||
* 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/. */
|
||||
|
||||
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use non-normalized
|
||||
// texture coordinates. Otherwise, it uses normalized texture coordinates. Please
|
||||
// check GL_TEXTURE_RECTANGLE.
|
||||
flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
|
||||
flat varying vec2 vTextureSize; // Size of the image in the texture atlas.
|
||||
flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image.
|
||||
flat varying vec4 vStRect; // Rectangle of valid texture rect, in st-space.
|
||||
flat varying vec4 vStRect; // Rectangle of valid texture rect.
|
||||
|
||||
#ifdef WR_FEATURE_TRANSFORM
|
||||
varying vec3 vLocalPos;
|
||||
|
@ -27,16 +27,28 @@ void main(void) {
|
||||
|
||||
write_clip(vi.screen_pos, prim.clip_area);
|
||||
|
||||
vTileSpacing = image.stretch_size_and_tile_spacing.zw;
|
||||
vStretchSize = image.stretch_size_and_tile_spacing.xy;
|
||||
|
||||
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
|
||||
// non-normalized texture coordinates.
|
||||
#ifdef WR_FEATURE_TEXTURE_RECT
|
||||
vec2 texture_size_normalization_factor = vec2(1, 1);
|
||||
#else
|
||||
vec2 texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
|
||||
#endif
|
||||
|
||||
// vUv will contain how many times this image has wrapped around the image size.
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0));
|
||||
vec2 st0 = res.uv_rect.xy / texture_size;
|
||||
vec2 st1 = res.uv_rect.zw / texture_size;
|
||||
vec2 st0 = res.uv_rect.xy / texture_size_normalization_factor;
|
||||
vec2 st1 = res.uv_rect.zw / texture_size_normalization_factor;
|
||||
|
||||
vTextureSize = st1 - st0;
|
||||
vTextureOffset = st0;
|
||||
vTileSpacing = image.stretch_size_and_tile_spacing.zw;
|
||||
vStretchSize = image.stretch_size_and_tile_spacing.xy;
|
||||
|
||||
vec2 half_texel = vec2(0.5) / texture_size;
|
||||
// We clamp the texture coordinates to the half-pixel offset from the borders
|
||||
// in order to avoid sampling outside of the texture area.
|
||||
vec2 half_texel = vec2(0.5) / texture_size_normalization_factor;
|
||||
vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel);
|
||||
}
|
||||
|
@ -23,12 +23,19 @@ void main(void) {
|
||||
// and not snap here?
|
||||
vStartCenter = floor(0.5 + gradient.start_end_center.xy * uDevicePixelRatio) / uDevicePixelRatio;
|
||||
vEndCenter = floor(0.5 + gradient.start_end_center.zw * uDevicePixelRatio) / uDevicePixelRatio;
|
||||
vStartRadius = gradient.start_end_radius_extend_mode.x;
|
||||
vEndRadius = gradient.start_end_radius_extend_mode.y;
|
||||
vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
|
||||
vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
|
||||
|
||||
// Transform all coordinates by the y scale so the
|
||||
// fragment shader can work with circles
|
||||
float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
|
||||
vPos.y *= ratio_xy;
|
||||
vStartCenter.y *= ratio_xy;
|
||||
vEndCenter.y *= ratio_xy;
|
||||
|
||||
// V coordinate of gradient row in lookup texture.
|
||||
vGradientIndex = float(prim.sub_index);
|
||||
|
||||
// Whether to repeat the gradient instead of clamping.
|
||||
vGradientRepeat = float(int(gradient.start_end_radius_extend_mode.z) == EXTEND_MODE_REPEAT);
|
||||
vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) == EXTEND_MODE_REPEAT);
|
||||
}
|
||||
|
@ -33,9 +33,15 @@
|
||||
//======================================================================================
|
||||
// Shared shader uniforms
|
||||
//======================================================================================
|
||||
#ifndef WR_FEATURE_TEXTURE_RECT
|
||||
uniform sampler2D sColor0;
|
||||
uniform sampler2D sColor1;
|
||||
uniform sampler2D sColor2;
|
||||
#else
|
||||
uniform sampler2DRect sColor0;
|
||||
uniform sampler2DRect sColor1;
|
||||
uniform sampler2DRect sColor2;
|
||||
#endif
|
||||
uniform sampler2D sDither;
|
||||
uniform sampler2D sMask;
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use euclid::Point3D;
|
||||
use geometry::ray_intersects_rect;
|
||||
use mask_cache::{ClipSource, MaskCacheInfo};
|
||||
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
|
||||
use prim_store::GpuBlock32;
|
||||
use renderer::VertexDataStore;
|
||||
use spring::{DAMPING, STIFFNESS, Spring};
|
||||
@ -24,7 +24,7 @@ const CAN_OVERSCROLL: bool = false;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClipInfo {
|
||||
/// The ClipSource for this node, which is used to generate mask_cache_info.
|
||||
pub clip_source: ClipSource,
|
||||
pub clip_sources: Vec<ClipSource>,
|
||||
|
||||
/// The MaskCacheInfo for this node, which is produced by processing the
|
||||
/// provided ClipSource.
|
||||
@ -47,10 +47,10 @@ impl ClipInfo {
|
||||
-> ClipInfo {
|
||||
// We pass true here for the MaskCacheInfo because this type of
|
||||
// mask needs an extra clip for the clip rectangle.
|
||||
let clip_source = ClipSource::Region(clip_region.clone());
|
||||
let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
|
||||
ClipInfo {
|
||||
mask_cache_info: MaskCacheInfo::new(&clip_source, true, clip_store),
|
||||
clip_source: clip_source,
|
||||
mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
|
||||
clip_sources: clip_sources,
|
||||
packed_layer_index: packed_layer_index,
|
||||
xf_rect: None,
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ pub struct ClipScrollTree {
|
||||
/// The current reference frame id, used for giving a unique id to all new
|
||||
/// reference frames. The ClipScrollTree increments this by one every time a
|
||||
/// reference frame is created.
|
||||
current_reference_frame_id: usize,
|
||||
current_reference_frame_id: u64,
|
||||
|
||||
/// The root reference frame, which is the true root of the ClipScrollTree. Initially
|
||||
/// this ID is not valid, which is indicated by ```node``` being empty.
|
||||
|
@ -51,6 +51,17 @@ pub enum DepthFunction {
|
||||
pub enum TextureTarget {
|
||||
Default,
|
||||
Array,
|
||||
Rect,
|
||||
}
|
||||
|
||||
impl TextureTarget {
|
||||
pub fn to_gl_target(&self) -> gl::GLuint {
|
||||
match *self {
|
||||
TextureTarget::Default => gl::TEXTURE_2D,
|
||||
TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
|
||||
TextureTarget::Rect => gl::TEXTURE_RECTANGLE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
@ -304,10 +315,10 @@ impl TextureId {
|
||||
gl.bind_texture(self.target, self.name);
|
||||
}
|
||||
|
||||
pub fn new(name: gl::GLuint) -> TextureId {
|
||||
pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
|
||||
TextureId {
|
||||
name: name,
|
||||
target: gl::TEXTURE_2D,
|
||||
target: texture_target.to_gl_target(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1090,15 +1101,10 @@ impl Device {
|
||||
let id_list = self.gl.gen_textures(count);
|
||||
let mut texture_ids = Vec::new();
|
||||
|
||||
let target = match target {
|
||||
TextureTarget::Default => gl::TEXTURE_2D,
|
||||
TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
|
||||
};
|
||||
|
||||
for id in id_list {
|
||||
let texture_id = TextureId {
|
||||
name: id,
|
||||
target: target,
|
||||
target: target.to_gl_target(),
|
||||
};
|
||||
|
||||
let texture = Texture {
|
||||
@ -2049,9 +2055,8 @@ impl Device {
|
||||
}
|
||||
|
||||
pub fn set_blend_mode_alpha(&self) {
|
||||
//self.gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
|
||||
self.gl.blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA,
|
||||
gl::ONE, gl::ONE);
|
||||
gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
|
||||
self.gl.blend_equation(gl::FUNC_ADD);
|
||||
}
|
||||
|
||||
|
@ -441,7 +441,9 @@ impl Frame {
|
||||
level == 0,
|
||||
composition_operations);
|
||||
|
||||
if level == 0 {
|
||||
// For the root pipeline, there's no need to add a full screen rectangle
|
||||
// here, as it's handled by the framebuffer clear.
|
||||
if level == 0 && context.scene.root_pipeline_id.unwrap() != pipeline_id {
|
||||
if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
|
||||
if let Some(bg_color) = pipeline.background_color {
|
||||
// Note: we don't use the original clip region here,
|
||||
@ -656,6 +658,7 @@ impl Frame {
|
||||
info.gradient.start_radius,
|
||||
info.gradient.end_center,
|
||||
info.gradient.end_radius,
|
||||
info.gradient.ratio_xy,
|
||||
info.gradient.stops,
|
||||
info.gradient.extend_mode);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use batch_builder::BorderSideHelpers;
|
||||
use frame::FrameId;
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::{HardwareCompositeOp, SourceTexture};
|
||||
use mask_cache::{ClipSource, MaskCacheInfo};
|
||||
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
|
||||
use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
|
||||
use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
|
||||
use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
|
||||
@ -148,6 +148,7 @@ impl FrameBuilder {
|
||||
scroll_layer_id: ScrollLayerId,
|
||||
rect: &LayerRect,
|
||||
clip_region: &ClipRegion,
|
||||
extra_clip: Option<ClipSource>,
|
||||
container: PrimitiveContainer)
|
||||
-> PrimitiveIndex {
|
||||
let stacking_context_index = *self.stacking_context_stack.last().unwrap();
|
||||
@ -163,17 +164,21 @@ impl FrameBuilder {
|
||||
local_rect: *rect,
|
||||
local_clip_rect: clip_region.main,
|
||||
};
|
||||
let clip_source = if clip_region.is_complex() {
|
||||
ClipSource::Region(clip_region.clone())
|
||||
} else {
|
||||
ClipSource::NoClip
|
||||
};
|
||||
let clip_info = MaskCacheInfo::new(&clip_source,
|
||||
false,
|
||||
let mut clip_sources = Vec::new();
|
||||
if clip_region.is_complex() {
|
||||
clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
|
||||
}
|
||||
// TODO(gw): Perhaps in the future it's worth passing in an array
|
||||
// so that callers can provide an arbitrary number
|
||||
// of clips?
|
||||
if let Some(extra_clip) = extra_clip {
|
||||
clip_sources.push(extra_clip);
|
||||
}
|
||||
let clip_info = MaskCacheInfo::new(&clip_sources,
|
||||
&mut self.prim_store.gpu_data32);
|
||||
|
||||
let prim_index = self.prim_store.add_primitive(geometry,
|
||||
Box::new(clip_source),
|
||||
clip_sources,
|
||||
clip_info,
|
||||
container);
|
||||
|
||||
@ -353,6 +358,7 @@ impl FrameBuilder {
|
||||
let prim_index = self.add_primitive(scroll_layer_id,
|
||||
rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::Rectangle(prim));
|
||||
|
||||
match flags {
|
||||
@ -651,6 +657,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::Border(prim_cpu, prim_gpu));
|
||||
}
|
||||
BorderDetails::Gradient(ref border) => {
|
||||
@ -673,6 +680,7 @@ impl FrameBuilder {
|
||||
border.gradient.start_radius,
|
||||
border.gradient.end_center,
|
||||
border.gradient.end_radius,
|
||||
border.gradient.ratio_xy,
|
||||
border.gradient.stops,
|
||||
border.gradient.extend_mode);
|
||||
}
|
||||
@ -690,8 +698,12 @@ impl FrameBuilder {
|
||||
extend_mode: ExtendMode) {
|
||||
// Fast path for clamped, axis-aligned gradients:
|
||||
let aligned = extend_mode == ExtendMode::Clamp &&
|
||||
(start_point.x == end_point.x ||
|
||||
start_point.y == end_point.y);
|
||||
(start_point.x == end_point.x &&
|
||||
start_point.x.min(end_point.x) <= rect.min_x() &&
|
||||
start_point.x.max(end_point.x) >= rect.max_x()) ||
|
||||
(start_point.y == end_point.y &&
|
||||
start_point.y.min(end_point.y) <= rect.min_y() &&
|
||||
start_point.y.max(end_point.y) >= rect.max_y());
|
||||
// Try to ensure that if the gradient is specified in reverse, then so long as the stops
|
||||
// are also supplied in reverse that the rendered result will be equivalent. To do this,
|
||||
// a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
|
||||
@ -732,7 +744,11 @@ impl FrameBuilder {
|
||||
PrimitiveContainer::AngleGradient(gradient_cpu, gradient_gpu)
|
||||
};
|
||||
|
||||
self.add_primitive(scroll_layer_id, &rect, clip_region, prim);
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
prim);
|
||||
}
|
||||
|
||||
pub fn add_radial_gradient(&mut self,
|
||||
@ -743,6 +759,7 @@ impl FrameBuilder {
|
||||
start_radius: f32,
|
||||
end_center: LayerPoint,
|
||||
end_radius: f32,
|
||||
ratio_xy: f32,
|
||||
stops: ItemRange,
|
||||
extend_mode: ExtendMode) {
|
||||
let radial_gradient_cpu = RadialGradientPrimitiveCpu {
|
||||
@ -756,13 +773,14 @@ impl FrameBuilder {
|
||||
end_center: end_center,
|
||||
start_radius: start_radius,
|
||||
end_radius: end_radius,
|
||||
ratio_xy: ratio_xy,
|
||||
extend_mode: pack_as_float(extend_mode as u32),
|
||||
padding: [0.0],
|
||||
};
|
||||
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::RadialGradient(radial_gradient_cpu, radial_gradient_gpu));
|
||||
}
|
||||
|
||||
@ -832,6 +850,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::TextRun(prim_cpu, prim_gpu));
|
||||
}
|
||||
}
|
||||
@ -888,12 +907,18 @@ impl FrameBuilder {
|
||||
|
||||
let shadow_kind = match clip_mode {
|
||||
BoxShadowClipMode::Outset | BoxShadowClipMode::None => {
|
||||
// If a border radius is set, we need to draw inside
|
||||
// the original box in order to draw where the border
|
||||
// corners are. A clip-out mask applied below will
|
||||
// ensure that we don't draw on the box itself.
|
||||
let inner_box_bounds = box_bounds.inflate(-border_radius,
|
||||
-border_radius);
|
||||
// For outset shadows, subtracting the element rectangle
|
||||
// from the outer rectangle gives the rectangles we need
|
||||
// to draw. In the simple case (no blur radius), we can
|
||||
// just draw these as solid colors.
|
||||
let mut rects = Vec::new();
|
||||
subtract_rect(&outer_rect, box_bounds, &mut rects);
|
||||
subtract_rect(&outer_rect, &inner_box_bounds, &mut rects);
|
||||
if edge_size == 0.0 {
|
||||
BoxShadowKind::Simple(rects)
|
||||
} else {
|
||||
@ -942,6 +967,15 @@ impl FrameBuilder {
|
||||
BoxShadowClipMode::Inset => 1.0,
|
||||
};
|
||||
|
||||
// If we have a border radius, we'll need to apply
|
||||
// a clip-out mask.
|
||||
let extra_clip = if border_radius > 0.0 {
|
||||
Some(ClipSource::Complex(*box_bounds,
|
||||
border_radius,
|
||||
ClipMode::ClipOut))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let prim_gpu = BoxShadowPrimitiveGpu {
|
||||
src_rect: *box_bounds,
|
||||
@ -956,6 +990,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&outer_rect,
|
||||
clip_region,
|
||||
extra_clip,
|
||||
PrimitiveContainer::BoxShadow(prim_gpu, rects));
|
||||
}
|
||||
}
|
||||
@ -981,6 +1016,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::Image(prim_cpu, prim_gpu));
|
||||
}
|
||||
|
||||
@ -1012,6 +1048,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::Image(prim_cpu, prim_gpu));
|
||||
}
|
||||
|
||||
@ -1035,6 +1072,7 @@ impl FrameBuilder {
|
||||
self.add_primitive(scroll_layer_id,
|
||||
&rect,
|
||||
clip_region,
|
||||
None,
|
||||
PrimitiveContainer::YuvImage(prim_cpu, prim_gpu));
|
||||
}
|
||||
|
||||
@ -1092,10 +1130,10 @@ impl FrameBuilder {
|
||||
geom.local_rect.origin.y = util::lerp(min_y, max_y, f);
|
||||
geom.local_clip_rect = geom.local_rect;
|
||||
|
||||
let clip_source = if scrollbar_prim.border_radius == 0.0 {
|
||||
ClipSource::NoClip
|
||||
let clip_source = if scrollbar_prim.border_radius > 0.0 {
|
||||
Some(ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius, ClipMode::Clip))
|
||||
} else {
|
||||
ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius)
|
||||
None
|
||||
};
|
||||
self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source);
|
||||
*self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
|
||||
@ -1165,7 +1203,7 @@ impl FrameBuilder {
|
||||
let mut prev_task = alpha_task_stack.pop().unwrap();
|
||||
let item = AlphaRenderItem::HardwareComposite(stacking_context_index,
|
||||
current_task.id,
|
||||
HardwareCompositeOp::Alpha,
|
||||
HardwareCompositeOp::PremultipliedAlpha,
|
||||
next_z);
|
||||
next_z += 1;
|
||||
prev_task.as_alpha_batch().alpha_items.push(item);
|
||||
@ -1213,7 +1251,10 @@ impl FrameBuilder {
|
||||
let stacking_context_index = *sc_stack.last().unwrap();
|
||||
let group_index = self.stacking_context_store[stacking_context_index.0]
|
||||
.clip_scroll_group(scroll_layer_id);
|
||||
let clip_scroll_group = &self.clip_scroll_group_store[group_index.0];
|
||||
let xf_rect = match &self.clip_scroll_group_store[group_index.0].xf_rect {
|
||||
&Some(ref xf_rect) => xf_rect,
|
||||
&None => continue,
|
||||
};
|
||||
|
||||
for i in 0..prim_count {
|
||||
let prim_index = PrimitiveIndex(first_prim_index.0 + i);
|
||||
@ -1229,9 +1270,8 @@ impl FrameBuilder {
|
||||
current_task.children.push(clip_task.clone());
|
||||
}
|
||||
|
||||
let transform_kind = clip_scroll_group.xf_rect.as_ref().unwrap().kind;
|
||||
let needs_clipping = prim_metadata.clip_task.is_some();
|
||||
let needs_blending = transform_kind == TransformedRectKind::Complex ||
|
||||
let needs_blending = xf_rect.kind == TransformedRectKind::Complex ||
|
||||
!prim_metadata.is_opaque ||
|
||||
needs_clipping;
|
||||
|
||||
@ -1375,7 +1415,7 @@ struct LayerRectCalculationAndCullingPass<'a> {
|
||||
|
||||
/// Information about the cached clip stack, which is used to avoid having
|
||||
/// to recalculate it for every primitive.
|
||||
current_clip_info: Option<(ScrollLayerId, DeviceIntRect)>
|
||||
current_clip_info: Option<(ScrollLayerId, Option<DeviceIntRect>)>
|
||||
}
|
||||
|
||||
impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
@ -1457,16 +1497,18 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
let auxiliary_lists = self.auxiliary_lists_map.get(&node.pipeline_id)
|
||||
.expect("No auxiliary lists?");
|
||||
|
||||
mask_info.update(&node_clip_info.clip_source,
|
||||
mask_info.update(&node_clip_info.clip_sources,
|
||||
&packed_layer.transform,
|
||||
&mut self.frame_builder.prim_store.gpu_data32,
|
||||
self.device_pixel_ratio,
|
||||
auxiliary_lists);
|
||||
|
||||
if let Some(mask) = node_clip_info.clip_source.image_mask() {
|
||||
// We don't add the image mask for resolution, because
|
||||
// layer masks are resolved later.
|
||||
self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
|
||||
for clip_source in &node_clip_info.clip_sources {
|
||||
if let Some(mask) = clip_source.image_mask() {
|
||||
// We don't add the image mask for resolution, because
|
||||
// layer masks are resolved later.
|
||||
self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1553,7 +1595,7 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
stacking_context.bounding_rect = DeviceIntRect::zero();
|
||||
}
|
||||
|
||||
fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> DeviceIntRect {
|
||||
fn rebuild_clip_info_stack_if_necessary(&mut self, id: ScrollLayerId) -> Option<DeviceIntRect> {
|
||||
if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
|
||||
if current_scroll_id == id {
|
||||
return bounding_rect;
|
||||
@ -1576,14 +1618,15 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
};
|
||||
|
||||
if bounding_rect.is_none() {
|
||||
bounding_rect = Some(clip_info.xf_rect.as_ref().unwrap().bounding_rect);
|
||||
bounding_rect =
|
||||
Some(clip_info.xf_rect.as_ref().map_or_else(DeviceIntRect::zero,
|
||||
|x| x.bounding_rect))
|
||||
}
|
||||
self.current_clip_stack.push((clip_info.packed_layer_index,
|
||||
clip_info.mask_cache_info.clone().unwrap()))
|
||||
}
|
||||
self.current_clip_stack.reverse();
|
||||
|
||||
let bounding_rect = bounding_rect.unwrap_or_else(DeviceIntRect::zero);
|
||||
self.current_clip_info = Some((id, bounding_rect));
|
||||
bounding_rect
|
||||
}
|
||||
@ -1607,6 +1650,9 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
};
|
||||
|
||||
let node_clip_bounds = self.rebuild_clip_info_stack_if_necessary(scroll_layer_id);
|
||||
if node_clip_bounds.map_or(false, |bounds| bounds.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let stacking_context =
|
||||
&mut self.frame_builder.stacking_context_store[stacking_context_index.0];
|
||||
@ -1658,6 +1704,7 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
||||
// stacking context. This means that two primitives which are only clipped
|
||||
// by the stacking context stack can share clip masks during render task
|
||||
// assignment to targets.
|
||||
let node_clip_bounds = node_clip_bounds.unwrap_or_else(DeviceIntRect::zero);
|
||||
let (mask_key, mask_rect) = match prim_clip_info {
|
||||
Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
|
||||
None => (MaskCacheKey::ScrollLayer(scroll_layer_id), node_clip_bounds)
|
||||
|
@ -20,6 +20,14 @@ impl Add<i32> for GpuStoreAddress {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for GpuStoreAddress {
|
||||
type Output = GpuStoreAddress;
|
||||
|
||||
fn add(self, other: usize) -> GpuStoreAddress {
|
||||
GpuStoreAddress(self.0 + other as i32)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GpuStoreLayout {
|
||||
fn image_format() -> ImageFormat;
|
||||
|
||||
|
@ -17,7 +17,7 @@ use tiling;
|
||||
use renderer::BlendMode;
|
||||
use webrender_traits::{Epoch, ColorF, PipelineId};
|
||||
use webrender_traits::{ImageFormat, NativeFontHandle};
|
||||
use webrender_traits::{ExternalImageId, ScrollLayerId};
|
||||
use webrender_traits::{ExternalImageData, ExternalImageId, ScrollLayerId};
|
||||
use webrender_traits::{ImageData};
|
||||
use webrender_traits::{DeviceUintRect};
|
||||
|
||||
@ -43,7 +43,7 @@ pub struct CacheTextureId(pub usize);
|
||||
pub enum SourceTexture {
|
||||
Invalid,
|
||||
TextureCache(CacheTextureId),
|
||||
External(ExternalImageId),
|
||||
External(ExternalImageData),
|
||||
#[cfg_attr(not(feature = "webgl"), allow(dead_code))]
|
||||
/// This is actually a gl::GLuint, with the shared texture id between the
|
||||
/// main context and the WebGL context.
|
||||
@ -387,13 +387,13 @@ pub enum LowLevelFilterOp {
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum HardwareCompositeOp {
|
||||
Alpha,
|
||||
PremultipliedAlpha,
|
||||
}
|
||||
|
||||
impl HardwareCompositeOp {
|
||||
pub fn to_blend_mode(&self) -> BlendMode {
|
||||
match self {
|
||||
&HardwareCompositeOp::Alpha => BlendMode::Alpha,
|
||||
&HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
//#![feature(mpsc_select)]
|
||||
|
||||
//! A GPU based Webrender.
|
||||
//!
|
||||
//! It serves as an experimental render backend for [Servo](https://servo.org/),
|
||||
@ -24,23 +22,19 @@
|
||||
//! to make better use of multicore systems.
|
||||
//!
|
||||
//! What is referred to as a `frame`, is the current geometry on the screen.
|
||||
//! A new Frame is created by calling [set_root_stacking_context()][newframe] on the `RenderApi`.
|
||||
//! A new Frame is created by calling [`set_display_list()`][newframe] on the `RenderApi`.
|
||||
//! When the geometry is processed, the application will be informed via a `RenderNotifier`,
|
||||
//! a callback which you employ with [set_render_notifier][notifier] on the `Renderer`
|
||||
//! More information about [stacking contexts][stacking_contexts].
|
||||
//!
|
||||
//! `set_root_stacking_context()` also needs to be supplied with `BuiltDisplayList`s.
|
||||
//! `set_display_list()` also needs to be supplied with `BuiltDisplayList`s.
|
||||
//! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
|
||||
//! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
|
||||
//! they're nestable.
|
||||
//!
|
||||
//! Remember to insert the DisplayListId into the StackingContext as well, as they'll be referenced
|
||||
//! from there. An Id for your DisplayList can be obtained by calling
|
||||
//! `yourRenderApiInstance.next_display_list_id();`
|
||||
//!
|
||||
//! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
|
||||
//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
|
||||
//! [notifier]: struct.Renderer.html#method.set_render_notifier
|
||||
//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_display_list
|
||||
//! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
@ -8,21 +8,40 @@ use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
|
||||
use renderer::VertexDataStore;
|
||||
use util::{MatrixHelpers, TransformedRect};
|
||||
use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask};
|
||||
use webrender_traits::{DeviceIntRect, DeviceIntSize, LayerRect, LayerToWorldTransform};
|
||||
use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
|
||||
use webrender_traits::{LayerRect, LayerPoint, LayerSize};
|
||||
|
||||
const MAX_CLIP: f32 = 1000000.0;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ClipMode {
|
||||
Clip, // Pixels inside the region are visible.
|
||||
ClipOut, // Pixels outside the region are visible.
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum RegionMode {
|
||||
IncludeRect,
|
||||
ExcludeRect,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ClipSource {
|
||||
NoClip,
|
||||
Complex(LayerRect, f32),
|
||||
Region(ClipRegion),
|
||||
Complex(LayerRect, f32, ClipMode),
|
||||
// The RegionMode here specifies whether to consider the rect
|
||||
// from the clip region as part of the mask. This is true
|
||||
// for clip/scroll nodes, but false for primitives, where
|
||||
// the clip rect is handled in local space.
|
||||
Region(ClipRegion, RegionMode),
|
||||
}
|
||||
|
||||
impl ClipSource {
|
||||
pub fn image_mask(&self) -> Option<ImageMask> {
|
||||
match self {
|
||||
&ClipSource::NoClip => None,
|
||||
&ClipSource::Complex(..) => None,
|
||||
&ClipSource::Region(ref region) => region.image_mask,
|
||||
&ClipSource::Region(ref region, _) => region.image_mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,136 +49,230 @@ impl ClipSource {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct ClipAddressRange {
|
||||
pub start: GpuStoreAddress,
|
||||
item_count: u32,
|
||||
item_count: usize,
|
||||
}
|
||||
|
||||
/// Represents a local rect and a device space
|
||||
/// bounding rect that can be updated when the
|
||||
/// transform changes.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Geometry {
|
||||
pub local_rect: LayerRect,
|
||||
pub bounding_rect: DeviceIntRect,
|
||||
}
|
||||
|
||||
impl Geometry {
|
||||
fn new(local_rect: LayerRect) -> Geometry {
|
||||
Geometry {
|
||||
local_rect: local_rect,
|
||||
bounding_rect: DeviceIntRect::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self,
|
||||
transform: &LayerToWorldTransform,
|
||||
device_pixel_ratio: f32) {
|
||||
let transformed = TransformedRect::new(&self.local_rect,
|
||||
&transform,
|
||||
device_pixel_ratio);
|
||||
self.bounding_rect = transformed.bounding_rect;
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on the complexity of the clip, we may either
|
||||
/// know the outer and/or inner rect, or neither or these.
|
||||
/// In the case of a clip-out, we currently set the mask
|
||||
/// bounds to be unknown. This is conservative, but ensures
|
||||
/// correctness. In the future we can make this a lot
|
||||
/// more clever with some proper region handling.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MaskBounds {
|
||||
/// We know both the outer and inner rect. This is the
|
||||
/// fast path for, e.g. a simple rounded rect.
|
||||
OuterInner(Geometry, Geometry),
|
||||
/// We know the outer rect only.
|
||||
Outer(Geometry),
|
||||
/// We can't determine the bounds - draw mask over entire rect.
|
||||
/// This is currently used for clip-out operations on
|
||||
/// box shadows.
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MaskCacheInfo {
|
||||
pub clip_range: ClipAddressRange,
|
||||
pub effective_clip_count: u32,
|
||||
pub effective_clip_count: usize,
|
||||
pub image: Option<(ImageMask, GpuStoreAddress)>,
|
||||
pub local_rect: Option<LayerRect>,
|
||||
pub local_inner: Option<LayerRect>,
|
||||
pub inner_rect: DeviceIntRect,
|
||||
pub outer_rect: DeviceIntRect,
|
||||
pub bounds: Option<MaskBounds>,
|
||||
pub is_aligned: bool,
|
||||
}
|
||||
|
||||
impl MaskCacheInfo {
|
||||
/// Create a new mask cache info. It allocates the GPU store data but leaves
|
||||
/// it unitialized for the following `update()` call to deal with.
|
||||
pub fn new(source: &ClipSource,
|
||||
extra_clip: bool,
|
||||
pub fn new(clips: &[ClipSource],
|
||||
clip_store: &mut VertexDataStore<GpuBlock32>)
|
||||
-> Option<MaskCacheInfo> {
|
||||
let (image, clip_range) = match source {
|
||||
&ClipSource::NoClip => return None,
|
||||
&ClipSource::Complex(..) => {
|
||||
(None,
|
||||
ClipAddressRange {
|
||||
start: clip_store.alloc(CLIP_DATA_GPU_SIZE),
|
||||
item_count: 1,
|
||||
})
|
||||
},
|
||||
&ClipSource::Region(ref region) => {
|
||||
let count = region.complex.length + if extra_clip {1} else {0};
|
||||
(region.image_mask.map(|info|
|
||||
(info, clip_store.alloc(MASK_DATA_GPU_SIZE))),
|
||||
ClipAddressRange {
|
||||
start: if count > 0 {
|
||||
clip_store.alloc(CLIP_DATA_GPU_SIZE * count)
|
||||
} else {
|
||||
GpuStoreAddress(0)
|
||||
},
|
||||
item_count: count as u32,
|
||||
})
|
||||
if clips.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut image = None;
|
||||
let mut clip_count = 0;
|
||||
|
||||
// Work out how much clip data space we need to allocate
|
||||
// and if we have an image mask.
|
||||
for clip in clips {
|
||||
match clip {
|
||||
&ClipSource::Complex(..) => {
|
||||
clip_count += 1;
|
||||
},
|
||||
&ClipSource::Region(ref region, region_mode) => {
|
||||
if let Some(info) = region.image_mask {
|
||||
debug_assert!(image.is_none()); // TODO(gw): Support >1 image mask!
|
||||
image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
|
||||
}
|
||||
|
||||
clip_count += region.complex.length;
|
||||
if region_mode == RegionMode::IncludeRect {
|
||||
clip_count += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let clip_range = ClipAddressRange {
|
||||
start: if clip_count > 0 {
|
||||
clip_store.alloc(CLIP_DATA_GPU_SIZE * clip_count)
|
||||
} else {
|
||||
GpuStoreAddress(0)
|
||||
},
|
||||
item_count: clip_count,
|
||||
};
|
||||
|
||||
Some(MaskCacheInfo {
|
||||
clip_range: clip_range,
|
||||
effective_clip_count: clip_range.item_count,
|
||||
image: image,
|
||||
local_rect: None,
|
||||
local_inner: None,
|
||||
inner_rect: DeviceIntRect::zero(),
|
||||
outer_rect: DeviceIntRect::zero(),
|
||||
bounds: None,
|
||||
is_aligned: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self,
|
||||
source: &ClipSource,
|
||||
sources: &[ClipSource],
|
||||
transform: &LayerToWorldTransform,
|
||||
clip_store: &mut VertexDataStore<GpuBlock32>,
|
||||
device_pixel_ratio: f32,
|
||||
aux_lists: &AuxiliaryLists) {
|
||||
let is_aligned = transform.can_losslessly_transform_and_perspective_project_a_2d_rect();
|
||||
|
||||
self.is_aligned = transform.can_losslessly_transform_and_perspective_project_a_2d_rect();
|
||||
// If we haven't cached this info, or if the transform type has changed
|
||||
// we need to re-calculate the number of clips.
|
||||
if self.bounds.is_none() || self.is_aligned != is_aligned {
|
||||
let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
|
||||
LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
|
||||
let mut local_inner: Option<LayerRect> = None;
|
||||
let mut has_clip_out = false;
|
||||
|
||||
if self.local_rect.is_none() {
|
||||
let mut local_rect;
|
||||
let mut local_inner: Option<LayerRect>;
|
||||
match source {
|
||||
&ClipSource::NoClip => unreachable!(),
|
||||
&ClipSource::Complex(rect, radius) => {
|
||||
let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE);
|
||||
let data = ClipData::uniform(rect, radius);
|
||||
PrimitiveStore::populate_clip_data(slice, data);
|
||||
debug_assert_eq!(self.clip_range.item_count, 1);
|
||||
local_rect = Some(rect);
|
||||
local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
|
||||
.get_inner_rect();
|
||||
}
|
||||
&ClipSource::Region(ref region) => {
|
||||
local_rect = Some(region.main);
|
||||
local_inner = match region.image_mask {
|
||||
Some(ref mask) if !mask.repeat => {
|
||||
local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
|
||||
None
|
||||
},
|
||||
Some(_) => None,
|
||||
None => local_rect,
|
||||
};
|
||||
self.effective_clip_count = 0;
|
||||
self.is_aligned = is_aligned;
|
||||
|
||||
for source in sources {
|
||||
match source {
|
||||
&ClipSource::Complex(rect, radius, mode) => {
|
||||
// Once we encounter a clip-out, we just assume the worst
|
||||
// case clip mask size, for now.
|
||||
if mode == ClipMode::ClipOut {
|
||||
has_clip_out = true;
|
||||
}
|
||||
debug_assert!(self.effective_clip_count < self.clip_range.item_count);
|
||||
let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
|
||||
self.effective_clip_count += 1;
|
||||
|
||||
let clips = aux_lists.complex_clip_regions(®ion.complex);
|
||||
self.effective_clip_count = if !self.is_aligned && self.clip_range.item_count > clips.len() as u32 {
|
||||
// we have an extra clip rect coming from the transformed layer
|
||||
assert_eq!(self.clip_range.item_count, clips.len() as u32 + 1);
|
||||
let address = GpuStoreAddress(self.clip_range.start.0 + (CLIP_DATA_GPU_SIZE * clips.len()) as i32);
|
||||
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
|
||||
PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0));
|
||||
self.clip_range.item_count
|
||||
} else {
|
||||
clips.len() as u32
|
||||
};
|
||||
let data = ClipData::uniform(rect, radius, mode);
|
||||
PrimitiveStore::populate_clip_data(slice, data);
|
||||
local_rect = local_rect.and_then(|r| r.intersection(&rect));
|
||||
local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
|
||||
.get_inner_rect();
|
||||
}
|
||||
&ClipSource::Region(ref region, region_mode) => {
|
||||
local_rect = local_rect.and_then(|r| r.intersection(®ion.main));
|
||||
local_inner = match region.image_mask {
|
||||
Some(ref mask) if !mask.repeat => {
|
||||
local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
|
||||
None
|
||||
},
|
||||
Some(_) => None,
|
||||
None => local_rect,
|
||||
};
|
||||
|
||||
let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE * clips.len());
|
||||
for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
|
||||
let data = ClipData::from_clip_region(clip);
|
||||
PrimitiveStore::populate_clip_data(chunk, data);
|
||||
local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
|
||||
local_inner = local_inner.and_then(|r| clip.get_inner_rect()
|
||||
.and_then(|ref inner| r.intersection(&inner)));
|
||||
let clips = aux_lists.complex_clip_regions(®ion.complex);
|
||||
if !self.is_aligned && region_mode == RegionMode::IncludeRect {
|
||||
// we have an extra clip rect coming from the transformed layer
|
||||
debug_assert!(self.effective_clip_count < self.clip_range.item_count);
|
||||
let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
|
||||
self.effective_clip_count += 1;
|
||||
|
||||
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
|
||||
PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
|
||||
}
|
||||
|
||||
debug_assert!(self.effective_clip_count + clips.len() <= self.clip_range.item_count);
|
||||
let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
|
||||
self.effective_clip_count += clips.len();
|
||||
|
||||
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
|
||||
for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
|
||||
let data = ClipData::from_clip_region(clip);
|
||||
PrimitiveStore::populate_clip_data(chunk, data);
|
||||
local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
|
||||
local_inner = local_inner.and_then(|r| clip.get_inner_rect()
|
||||
.and_then(|ref inner| r.intersection(&inner)));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.local_rect = Some(local_rect.unwrap_or(LayerRect::zero()));
|
||||
self.local_inner = local_inner;
|
||||
}
|
||||
|
||||
// Work out the type of mask geometry we have, based on the
|
||||
// list of clip sources above.
|
||||
if has_clip_out {
|
||||
// For clip-out, the mask rect is not known.
|
||||
self.bounds = Some(MaskBounds::None);
|
||||
} else {
|
||||
// TODO(gw): local inner is only valid if there's a single clip (for now).
|
||||
// This can be improved in the future, with some proper
|
||||
// rectangle region handling.
|
||||
if sources.len() > 1 {
|
||||
local_inner = None;
|
||||
}
|
||||
|
||||
let local_rect = local_rect.unwrap_or(LayerRect::zero());
|
||||
|
||||
self.bounds = match local_inner {
|
||||
Some(local_inner) => {
|
||||
Some(MaskBounds::OuterInner(Geometry::new(local_rect),
|
||||
Geometry::new(local_inner)))
|
||||
}
|
||||
None => {
|
||||
Some(MaskBounds::Outer(Geometry::new(local_rect)))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let transformed = TransformedRect::new(self.local_rect.as_ref().unwrap(),
|
||||
&transform,
|
||||
device_pixel_ratio);
|
||||
self.outer_rect = transformed.bounding_rect;
|
||||
|
||||
self.inner_rect = if let Some(ref inner_rect) = self.local_inner {
|
||||
let transformed = TransformedRect::new(inner_rect,
|
||||
&transform,
|
||||
device_pixel_ratio);
|
||||
transformed.inner_rect
|
||||
} else {
|
||||
DeviceIntRect::new(self.outer_rect.origin, DeviceIntSize::zero())
|
||||
// Update the device space bounding rects of the mask
|
||||
// geometry.
|
||||
match self.bounds.as_mut().unwrap() {
|
||||
&mut MaskBounds::None => {}
|
||||
&mut MaskBounds::Outer(ref mut outer) => {
|
||||
outer.update(transform, device_pixel_ratio);
|
||||
}
|
||||
&mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
|
||||
outer.update(transform, device_pixel_ratio);
|
||||
inner.update(transform, device_pixel_ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,7 @@ impl FontContext {
|
||||
let mut lib: FT_Library = ptr::null_mut();
|
||||
unsafe {
|
||||
let result = FT_Init_FreeType(&mut lib);
|
||||
if !result.succeeded() {
|
||||
panic!("Unable to initialize FreeType library {:?}", result);
|
||||
}
|
||||
assert!(result.succeeded(), "Unable to initialize FreeType library {:?}", result);
|
||||
|
||||
// TODO(gw): Check result of this to determine if freetype build supports subpixel.
|
||||
let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
|
||||
|
@ -6,7 +6,7 @@ use app_units::Au;
|
||||
use euclid::{Point2D, Size2D};
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::{SourceTexture, PackedTexel};
|
||||
use mask_cache::{ClipSource, MaskCacheInfo};
|
||||
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
|
||||
use renderer::{VertexDataStore, GradientDataStore};
|
||||
use render_task::{RenderTask, RenderTaskLocation};
|
||||
use resource_cache::{CacheItem, ImageProperties, ResourceCache};
|
||||
@ -109,7 +109,7 @@ pub enum PrimitiveCacheKey {
|
||||
#[derive(Debug)]
|
||||
pub struct PrimitiveMetadata {
|
||||
pub is_opaque: bool,
|
||||
pub clip_source: Box<ClipSource>,
|
||||
pub clips: Vec<ClipSource>,
|
||||
pub clip_cache_info: Option<MaskCacheInfo>,
|
||||
pub prim_kind: PrimitiveKind,
|
||||
pub cpu_prim_index: SpecificPrimitiveIndex,
|
||||
@ -249,8 +249,8 @@ pub struct RadialGradientPrimitiveGpu {
|
||||
pub end_center: LayerPoint,
|
||||
pub start_radius: f32,
|
||||
pub end_radius: f32,
|
||||
pub ratio_xy: f32,
|
||||
pub extend_mode: f32,
|
||||
pub padding: [f32; 1],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -415,7 +415,8 @@ struct GlyphPrimitive {
|
||||
#[repr(C)]
|
||||
struct ClipRect {
|
||||
rect: LayerRect,
|
||||
padding: [f32; 4],
|
||||
mode: f32,
|
||||
padding: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -461,7 +462,9 @@ impl ClipData {
|
||||
ClipData {
|
||||
rect: ClipRect {
|
||||
rect: clip.rect,
|
||||
padding: [0.0, 0.0, 0.0, 0.0],
|
||||
padding: [0.0; 3],
|
||||
// TODO(gw): Support other clip modes for regions?
|
||||
mode: ClipMode::Clip as u32 as f32,
|
||||
},
|
||||
top_left: ClipCorner {
|
||||
rect: LayerRect::new(
|
||||
@ -503,11 +506,12 @@ impl ClipData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uniform(rect: LayerRect, radius: f32) -> ClipData {
|
||||
pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
|
||||
ClipData {
|
||||
rect: ClipRect {
|
||||
rect: rect,
|
||||
padding: [0.0; 4],
|
||||
padding: [0.0; 3],
|
||||
mode: mode as u32 as f32,
|
||||
},
|
||||
top_left: ClipCorner::uniform(
|
||||
LayerRect::new(
|
||||
@ -604,7 +608,7 @@ impl PrimitiveStore {
|
||||
|
||||
pub fn add_primitive(&mut self,
|
||||
geometry: PrimitiveGeometry,
|
||||
clip_source: Box<ClipSource>,
|
||||
clips: Vec<ClipSource>,
|
||||
clip_info: Option<MaskCacheInfo>,
|
||||
container: PrimitiveContainer) -> PrimitiveIndex {
|
||||
let prim_index = self.cpu_metadata.len();
|
||||
@ -618,7 +622,7 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: is_opaque,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::Rectangle,
|
||||
cpu_prim_index: SpecificPrimitiveIndex::invalid(),
|
||||
@ -638,7 +642,7 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::TextRun,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
|
||||
@ -659,7 +663,7 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::Image,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
|
||||
@ -680,7 +684,7 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: true,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::YuvImage,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
|
||||
@ -699,7 +703,7 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::Border,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
|
||||
@ -720,7 +724,7 @@ impl PrimitiveStore {
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::AlignedGradient,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
|
||||
@ -741,7 +745,7 @@ impl PrimitiveStore {
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::AngleGradient,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
|
||||
@ -762,7 +766,7 @@ impl PrimitiveStore {
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::RadialGradient,
|
||||
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
|
||||
@ -807,8 +811,8 @@ impl PrimitiveStore {
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: false,
|
||||
clip_source: clip_source,
|
||||
clip_cache_info: None,
|
||||
clips: clips,
|
||||
clip_cache_info: clip_info,
|
||||
prim_kind: PrimitiveKind::BoxShadow,
|
||||
cpu_prim_index: SpecificPrimitiveIndex::invalid(),
|
||||
gpu_prim_index: gpu_prim_address,
|
||||
@ -864,8 +868,8 @@ impl PrimitiveStore {
|
||||
|
||||
// Check if an external image that needs to be resolved
|
||||
// by the render thread.
|
||||
match image_properties.external_id {
|
||||
Some(external_id) => {
|
||||
match image_properties.external_image {
|
||||
Some(external_image) => {
|
||||
// This is an external texture - we will add it to
|
||||
// the deferred resolves list to be patched by
|
||||
// the render thread...
|
||||
@ -874,7 +878,7 @@ impl PrimitiveStore {
|
||||
resource_address: image_uv_address,
|
||||
});
|
||||
|
||||
(SourceTexture::External(external_id), None)
|
||||
(SourceTexture::External(external_image), None)
|
||||
}
|
||||
None => {
|
||||
let cache_item = resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
|
||||
@ -994,21 +998,25 @@ impl PrimitiveStore {
|
||||
deferred_resolves
|
||||
}
|
||||
|
||||
pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: ClipSource) {
|
||||
pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: Option<ClipSource>) {
|
||||
let metadata = &mut self.cpu_metadata[index.0];
|
||||
let (rect, is_complex) = match source {
|
||||
ClipSource::NoClip => (None, false),
|
||||
ClipSource::Complex(rect, radius) => (Some(rect), radius > 0.0),
|
||||
ClipSource::Region(ref region) => (Some(region.main), region.is_complex()),
|
||||
};
|
||||
if let Some(rect) = rect {
|
||||
self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
|
||||
.local_clip_rect = rect;
|
||||
if is_complex {
|
||||
metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
|
||||
metadata.clips = match source {
|
||||
Some(source) => {
|
||||
let (rect, is_complex) = match source {
|
||||
ClipSource::Complex(rect, radius, _) => (rect, radius > 0.0),
|
||||
ClipSource::Region(ref region, _) => (region.main, region.is_complex()),
|
||||
};
|
||||
self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
|
||||
.local_clip_rect = rect;
|
||||
if is_complex {
|
||||
metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
|
||||
}
|
||||
vec![source]
|
||||
}
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
*metadata.clip_source.as_mut() = source;
|
||||
}
|
||||
|
||||
pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
|
||||
@ -1054,14 +1062,16 @@ impl PrimitiveStore {
|
||||
let mut rebuild_bounding_rect = false;
|
||||
|
||||
if let Some(ref mut clip_info) = metadata.clip_cache_info {
|
||||
clip_info.update(&metadata.clip_source,
|
||||
clip_info.update(&metadata.clips,
|
||||
layer_transform,
|
||||
&mut self.gpu_data32,
|
||||
device_pixel_ratio,
|
||||
auxiliary_lists);
|
||||
if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() {
|
||||
resource_cache.request_image(mask.image, ImageRendering::Auto, None);
|
||||
prim_needs_resolve = true;
|
||||
for clip in &metadata.clips {
|
||||
if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, _) = clip {
|
||||
resource_cache.request_image(mask.image, ImageRendering::Auto, None);
|
||||
prim_needs_resolve = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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 bincode::{SizeLimit, serialize};
|
||||
use bincode::{Infinite, serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
use std::any::TypeId;
|
||||
@ -50,7 +50,7 @@ impl BinaryRecorder {
|
||||
impl ApiRecordingReceiver for BinaryRecorder {
|
||||
fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
|
||||
if should_record_msg(msg) {
|
||||
let buf = serialize(msg, SizeLimit::Infinite).unwrap();
|
||||
let buf = serialize(msg, Infinite).unwrap();
|
||||
self.write_length_and_data(&buf);
|
||||
}
|
||||
}
|
||||
@ -71,7 +71,7 @@ pub fn should_record_msg(msg: &ApiMsg) -> bool {
|
||||
&ApiMsg::GenerateFrame(..) |
|
||||
&ApiMsg::UpdateImage(..) |
|
||||
&ApiMsg::DeleteImage(..) |
|
||||
&ApiMsg::SetRootDisplayList(..) |
|
||||
&ApiMsg::SetDisplayList(..) |
|
||||
&ApiMsg::SetRootPipeline(..) |
|
||||
&ApiMsg::Scroll(..) |
|
||||
&ApiMsg::TickScrollingBounce |
|
||||
|
@ -2,7 +2,6 @@
|
||||
* 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 byteorder::{LittleEndian, ReadBytesExt};
|
||||
use frame::Frame;
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use internal_types::{FontTemplate, SourceTexture, ResultMsg, RendererFrame};
|
||||
@ -11,7 +10,6 @@ use record::ApiRecordingReceiver;
|
||||
use resource_cache::ResourceCache;
|
||||
use scene::Scene;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::mpsc::Sender;
|
||||
use texture_cache::TextureCache;
|
||||
@ -21,7 +19,7 @@ use webgl_types::{GLContextHandleWrapper, GLContextWrapper};
|
||||
use webrender_traits::{DeviceIntPoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize, LayerPoint};
|
||||
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, ImageData};
|
||||
use webrender_traits::{PipelineId, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId};
|
||||
use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
|
||||
use webrender_traits::channel::{PayloadSenderHelperMethods, PayloadReceiverHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver};
|
||||
use webrender_traits::{BlobImageRenderer, VRCompositorCommand, VRCompositorHandler};
|
||||
#[cfg(feature = "webgl")]
|
||||
use offscreen_gl_context::GLContextDispatcher;
|
||||
@ -177,60 +175,52 @@ impl RenderBackend {
|
||||
|
||||
sender.send(result).unwrap();
|
||||
}
|
||||
ApiMsg::SetRootDisplayList(background_color,
|
||||
epoch,
|
||||
pipeline_id,
|
||||
viewport_size,
|
||||
display_list_descriptor,
|
||||
auxiliary_lists_descriptor,
|
||||
preserve_frame_state) => {
|
||||
profile_scope!("SetRootDisplayList");
|
||||
ApiMsg::SetDisplayList(background_color,
|
||||
epoch,
|
||||
pipeline_id,
|
||||
viewport_size,
|
||||
display_list_descriptor,
|
||||
auxiliary_lists_descriptor,
|
||||
preserve_frame_state) => {
|
||||
profile_scope!("SetDisplayList");
|
||||
let mut leftover_auxiliary_data = vec![];
|
||||
let mut auxiliary_data;
|
||||
loop {
|
||||
auxiliary_data = self.payload_rx.recv().unwrap();
|
||||
auxiliary_data = self.payload_rx.recv_payload().unwrap();
|
||||
{
|
||||
let mut payload_reader = Cursor::new(&auxiliary_data[..]);
|
||||
let payload_epoch =
|
||||
payload_reader.read_u32::<LittleEndian>().unwrap();
|
||||
if payload_epoch == epoch.0 {
|
||||
if auxiliary_data.epoch == epoch &&
|
||||
auxiliary_data.pipeline_id == pipeline_id {
|
||||
break
|
||||
}
|
||||
}
|
||||
leftover_auxiliary_data.push(auxiliary_data)
|
||||
}
|
||||
for leftover_auxiliary_data in leftover_auxiliary_data {
|
||||
self.payload_tx.send_vec(leftover_auxiliary_data).unwrap()
|
||||
self.payload_tx.send_payload(leftover_auxiliary_data).unwrap()
|
||||
}
|
||||
if let Some(ref mut r) = self.recorder {
|
||||
r.write_payload(frame_counter, &auxiliary_data);
|
||||
r.write_payload(frame_counter, &auxiliary_data.to_data());
|
||||
}
|
||||
|
||||
let mut auxiliary_data = Cursor::new(&mut auxiliary_data[4..]);
|
||||
let mut built_display_list_data =
|
||||
vec![0; display_list_descriptor.size()];
|
||||
auxiliary_data.read_exact(&mut built_display_list_data[..]).unwrap();
|
||||
let built_display_list =
|
||||
BuiltDisplayList::from_data(built_display_list_data,
|
||||
BuiltDisplayList::from_data(auxiliary_data.display_list_data,
|
||||
display_list_descriptor);
|
||||
let mut auxiliary_lists_data =
|
||||
vec![0; auxiliary_lists_descriptor.size()];
|
||||
auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
|
||||
let auxiliary_lists =
|
||||
AuxiliaryLists::from_data(auxiliary_lists_data,
|
||||
AuxiliaryLists::from_data(auxiliary_data.auxiliary_lists_data,
|
||||
auxiliary_lists_descriptor);
|
||||
|
||||
if !preserve_frame_state {
|
||||
self.discard_frame_state_for_pipeline(pipeline_id);
|
||||
}
|
||||
|
||||
self.scene.set_root_display_list(pipeline_id,
|
||||
epoch,
|
||||
built_display_list,
|
||||
background_color,
|
||||
viewport_size,
|
||||
auxiliary_lists);
|
||||
self.build_scene();
|
||||
profile_counters.total_time.profile(|| {
|
||||
self.scene.set_display_list(pipeline_id,
|
||||
epoch,
|
||||
built_display_list,
|
||||
background_color,
|
||||
viewport_size,
|
||||
auxiliary_lists);
|
||||
self.build_scene();
|
||||
})
|
||||
}
|
||||
ApiMsg::SetRootPipeline(pipeline_id) => {
|
||||
profile_scope!("SetRootPipeline");
|
||||
@ -240,7 +230,9 @@ impl RenderBackend {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.build_scene();
|
||||
profile_counters.total_time.profile(|| {
|
||||
self.build_scene();
|
||||
})
|
||||
}
|
||||
ApiMsg::Scroll(delta, cursor, move_phase) => {
|
||||
profile_scope!("Scroll");
|
||||
@ -383,7 +375,9 @@ impl RenderBackend {
|
||||
// rebuild of the frame!
|
||||
if let Some(property_bindings) = property_bindings {
|
||||
self.scene.properties.set_properties(property_bindings);
|
||||
self.build_scene();
|
||||
profile_counters.total_time.profile(|| {
|
||||
self.build_scene();
|
||||
});
|
||||
}
|
||||
|
||||
let frame = {
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
|
||||
use mask_cache::MaskCacheInfo;
|
||||
use mask_cache::{MaskBounds, MaskCacheInfo};
|
||||
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
|
||||
use std::{cmp, f32, i32, mem, usize};
|
||||
use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
|
||||
@ -192,19 +192,44 @@ impl RenderTask {
|
||||
// We scan through the clip stack and detect if our actual rectangle
|
||||
// is in the intersection of all of all the outer bounds,
|
||||
// and if it's completely inside the intersection of all of the inner bounds.
|
||||
let result = clips.iter()
|
||||
.fold(Some(actual_rect), |current, clip| {
|
||||
current.and_then(|rect| rect.intersection(&clip.1.outer_rect))
|
||||
});
|
||||
|
||||
// TODO(gw): If we encounter a clip with unknown bounds, we'll just use
|
||||
// the original rect. This is overly conservative, but can
|
||||
// be optimized later.
|
||||
let mut result = Some(actual_rect);
|
||||
for &(_, ref clip) in clips {
|
||||
match clip.bounds.as_ref().unwrap() {
|
||||
&MaskBounds::OuterInner(ref outer, _) |
|
||||
&MaskBounds::Outer(ref outer) => {
|
||||
result = result.and_then(|rect| {
|
||||
rect.intersection(&outer.bounding_rect)
|
||||
});
|
||||
}
|
||||
&MaskBounds::None => {
|
||||
result = Some(actual_rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let task_rect = match result {
|
||||
None => return MaskResult::Outside,
|
||||
Some(rect) => rect,
|
||||
};
|
||||
|
||||
// Accumulate inner rects. As soon as we encounter
|
||||
// a clip mask where we don't have or don't know
|
||||
// the inner rect, this will become None.
|
||||
let inner_rect = clips.iter()
|
||||
.fold(Some(task_rect), |current, clip| {
|
||||
current.and_then(|rect| rect.intersection(&clip.1.inner_rect))
|
||||
current.and_then(|rect| {
|
||||
let inner_rect = match clip.1.bounds.as_ref().unwrap() {
|
||||
&MaskBounds::Outer(..) |
|
||||
&MaskBounds::None => DeviceIntRect::zero(),
|
||||
&MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
|
||||
};
|
||||
rect.intersection(&inner_rect)
|
||||
})
|
||||
});
|
||||
|
||||
// TODO(gw): This optimization is very conservative for now.
|
||||
|
@ -42,14 +42,14 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thread;
|
||||
use texture_cache::TextureCache;
|
||||
use threadpool::ThreadPool;
|
||||
use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData};
|
||||
use tiling::{AlphaBatchKind, BlurCommand, Frame, PrimitiveBatch, PrimitiveBatchData, RenderTarget};
|
||||
use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
|
||||
use time::precise_time_ns;
|
||||
use thread_profiler::{register_thread_with_profiler, write_profile};
|
||||
use util::TransformedRectKind;
|
||||
use webgl_types::GLContextHandleWrapper;
|
||||
use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
|
||||
use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender};
|
||||
use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
|
||||
use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
|
||||
use webrender_traits::{ImageDescriptor, BlobImageRenderer};
|
||||
use webrender_traits::channel;
|
||||
@ -65,6 +65,7 @@ const GPU_TAG_INIT: GpuProfileTag = GpuProfileTag { label: "Init", color: debug_
|
||||
const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "Target", color: debug_colors::SLATEGREY };
|
||||
const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { label: "Rect", color: debug_colors::RED };
|
||||
const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { label: "Image", color: debug_colors::GREEN };
|
||||
const GPU_TAG_PRIM_IMAGE_RECT: GpuProfileTag = GpuProfileTag { label: "ImageRect", color: debug_colors::GREENYELLOW };
|
||||
const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "YuvImage", color: debug_colors::DARKGREEN };
|
||||
const GPU_TAG_PRIM_BLEND: GpuProfileTag = GpuProfileTag { label: "Blend", color: debug_colors::LIGHTBLUE };
|
||||
const GPU_TAG_PRIM_HW_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "HwComposite", color: debug_colors::DODGERBLUE };
|
||||
@ -223,6 +224,7 @@ pub type GradientDataStore = GpuStore<GradientData, GradientDataTextureLayout>;
|
||||
const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
|
||||
const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
|
||||
const CLIP_FEATURE: &'static str = "CLIP";
|
||||
const TEXTURE_RECT_FEATURE: &'static str = "TEXTURE_RECT";
|
||||
|
||||
enum ShaderKind {
|
||||
Primitive,
|
||||
@ -465,6 +467,7 @@ pub struct Renderer {
|
||||
ps_text_run: PrimitiveShader,
|
||||
ps_text_run_subpixel: PrimitiveShader,
|
||||
ps_image: PrimitiveShader,
|
||||
ps_image_rect: PrimitiveShader,
|
||||
ps_yuv_image: PrimitiveShader,
|
||||
ps_border: PrimitiveShader,
|
||||
ps_gradient: PrimitiveShader,
|
||||
@ -555,12 +558,11 @@ impl From<std::io::Error> for InitError {
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
/// Initializes webrender and creates a Renderer and RenderApiSender.
|
||||
/// Initializes webrender and creates a `Renderer` and `RenderApiSender`.
|
||||
///
|
||||
/// # Examples
|
||||
/// Initializes a Renderer with some reasonable values. For more information see
|
||||
/// [RendererOptions][rendereroptions].
|
||||
/// [rendereroptions]: struct.RendererOptions.html
|
||||
/// Initializes a `Renderer` with some reasonable values. For more information see
|
||||
/// [`RendererOptions`][rendereroptions].
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// # use webrender::renderer::Renderer;
|
||||
@ -573,6 +575,7 @@ impl Renderer {
|
||||
/// };
|
||||
/// let (renderer, sender) = Renderer::new(opts);
|
||||
/// ```
|
||||
/// [rendereroptions]: struct.RendererOptions.html
|
||||
pub fn new(gl: Rc<gl::Gl>,
|
||||
mut options: RendererOptions,
|
||||
initial_window_size: DeviceUintSize) -> Result<(Renderer, RenderApiSender), InitError> {
|
||||
@ -670,6 +673,13 @@ impl Renderer {
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let ps_image_rect = try!{
|
||||
PrimitiveShader::new("ps_image",
|
||||
&mut device,
|
||||
&[ TEXTURE_RECT_FEATURE ],
|
||||
options.precache_shaders)
|
||||
};
|
||||
|
||||
let ps_yuv_image = try!{
|
||||
PrimitiveShader::new("ps_yuv_image",
|
||||
&mut device,
|
||||
@ -912,6 +922,7 @@ impl Renderer {
|
||||
ps_text_run: ps_text_run,
|
||||
ps_text_run_subpixel: ps_text_run_subpixel,
|
||||
ps_image: ps_image,
|
||||
ps_image_rect: ps_image_rect,
|
||||
ps_yuv_image: ps_yuv_image,
|
||||
ps_border: ps_border,
|
||||
ps_box_shadow: ps_box_shadow,
|
||||
@ -1039,10 +1050,10 @@ impl Renderer {
|
||||
fn resolve_source_texture(&mut self, texture_id: &SourceTexture) -> TextureId {
|
||||
match *texture_id {
|
||||
SourceTexture::Invalid => TextureId::invalid(),
|
||||
SourceTexture::WebGL(id) => TextureId::new(id),
|
||||
SourceTexture::External(ref key) => {
|
||||
SourceTexture::WebGL(id) => TextureId::new(id, TextureTarget::Default),
|
||||
SourceTexture::External(external_image) => {
|
||||
*self.external_images
|
||||
.get(key)
|
||||
.get(&external_image.id)
|
||||
.expect("BUG: External image should be resolved by now!")
|
||||
}
|
||||
SourceTexture::TextureCache(index) => {
|
||||
@ -1065,8 +1076,8 @@ impl Renderer {
|
||||
|
||||
/// Renders the current frame.
|
||||
///
|
||||
/// A Frame is supplied by calling [set_root_stacking_context()][newframe].
|
||||
/// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_root_stacking_context
|
||||
/// A Frame is supplied by calling [`set_display_list()`][newframe].
|
||||
/// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_display_list
|
||||
pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
|
||||
profile_scope!("render");
|
||||
|
||||
@ -1193,24 +1204,31 @@ impl Renderer {
|
||||
mode,
|
||||
Some(raw.as_slice()));
|
||||
}
|
||||
ImageData::ExternalBuffer(id) => {
|
||||
let handler = self.external_image_handler
|
||||
.as_mut()
|
||||
.expect("Found external image, but no handler set!");
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::ExternalBuffer => {
|
||||
let handler = self.external_image_handler
|
||||
.as_mut()
|
||||
.expect("Found external image, but no handler set!");
|
||||
|
||||
match handler.lock(id).source {
|
||||
ExternalImageSource::RawData(raw) => {
|
||||
self.device.init_texture(texture_id,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
filter,
|
||||
mode,
|
||||
Some(raw));
|
||||
match handler.lock(ext_image.id).source {
|
||||
ExternalImageSource::RawData(raw) => {
|
||||
self.device.init_texture(texture_id,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
filter,
|
||||
mode,
|
||||
Some(raw));
|
||||
}
|
||||
_ => panic!("No external buffer found"),
|
||||
};
|
||||
handler.unlock(ext_image.id);
|
||||
}
|
||||
_ => panic!("No external buffer found"),
|
||||
};
|
||||
handler.unlock(id);
|
||||
_ => {
|
||||
panic!("External texture handle should not use TextureUpdateOp::Create.");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("No suitable image buffer for TextureUpdateOp::Create.");
|
||||
@ -1339,6 +1357,10 @@ impl Renderer {
|
||||
let shader = self.ps_image.get(&mut self.device, transform_kind);
|
||||
(GPU_TAG_PRIM_IMAGE, shader)
|
||||
}
|
||||
AlphaBatchKind::ImageRect => {
|
||||
let shader = self.ps_image_rect.get(&mut self.device, transform_kind);
|
||||
(GPU_TAG_PRIM_IMAGE_RECT, shader)
|
||||
}
|
||||
AlphaBatchKind::YuvImage => {
|
||||
let shader = self.ps_yuv_image.get(&mut self.device, transform_kind);
|
||||
(GPU_TAG_PRIM_YUV_IMAGE, shader)
|
||||
@ -1459,11 +1481,27 @@ impl Renderer {
|
||||
self.device.enable_depth_write();
|
||||
self.device.set_blend(false);
|
||||
self.device.set_blend_mode_alpha();
|
||||
self.device.clear_target(clear_color, Some(1.0));
|
||||
match render_target {
|
||||
Some(..) => {
|
||||
// TODO(gw): Applying a scissor rect and minimal clear here
|
||||
// is a very large performance win on the Intel and nVidia
|
||||
// GPUs that I have tested with. It's possible it may be a
|
||||
// performance penalty on other GPU types - we should test this
|
||||
// and consider different code paths.
|
||||
self.device.clear_target_rect(clear_color,
|
||||
Some(1.0),
|
||||
target.used_rect());
|
||||
}
|
||||
None => {
|
||||
self.device.clear_target(clear_color, Some(1.0));
|
||||
}
|
||||
}
|
||||
|
||||
let isolate_clear_color = Some([0.0, 0.0, 0.0, 0.0]);
|
||||
for isolate_clear in &target.isolate_clears {
|
||||
self.device.clear_target_rect(isolate_clear_color, None, *isolate_clear);
|
||||
self.device.clear_target_rect(isolate_clear_color,
|
||||
None,
|
||||
*isolate_clear);
|
||||
}
|
||||
|
||||
self.device.disable_depth_write();
|
||||
@ -1592,8 +1630,15 @@ impl Renderer {
|
||||
self.device.disable_depth();
|
||||
self.device.disable_depth_write();
|
||||
|
||||
// TODO(gw): Applying a scissor rect and minimal clear here
|
||||
// is a very large performance win on the Intel and nVidia
|
||||
// GPUs that I have tested with. It's possible it may be a
|
||||
// performance penalty on other GPU types - we should test this
|
||||
// and consider different code paths.
|
||||
let clear_color = [1.0, 1.0, 1.0, 0.0];
|
||||
self.device.clear_target(Some(clear_color), None);
|
||||
self.device.clear_target_rect(Some(clear_color),
|
||||
None,
|
||||
target.used_rect());
|
||||
}
|
||||
|
||||
// Draw the clip items into the tiled alpha mask.
|
||||
@ -1642,16 +1687,24 @@ impl Renderer {
|
||||
for deferred_resolve in &frame.deferred_resolves {
|
||||
GpuMarker::fire(self.device.gl(), "deferred resolve");
|
||||
let props = &deferred_resolve.image_properties;
|
||||
let external_id = props.external_id
|
||||
.expect("BUG: Deferred resolves must be external images!");
|
||||
let image = handler.lock(external_id);
|
||||
let ext_image = props.external_image
|
||||
.expect("BUG: Deferred resolves must be external images!");
|
||||
let image = handler.lock(ext_image.id);
|
||||
let texture_target = match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle => TextureTarget::Default,
|
||||
ExternalImageType::TextureRectHandle => TextureTarget::Rect,
|
||||
_ => {
|
||||
panic!("{:?} is not a suitable image type in update_deferred_resolves().",
|
||||
ext_image.image_type);
|
||||
}
|
||||
};
|
||||
|
||||
let texture_id = match image.source {
|
||||
ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id),
|
||||
ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id, texture_target),
|
||||
_ => panic!("No native texture found."),
|
||||
};
|
||||
|
||||
self.external_images.insert(external_id, texture_id);
|
||||
self.external_images.insert(ext_image.id, texture_id);
|
||||
let resource_rect_index = deferred_resolve.resource_address.0 as usize;
|
||||
let resource_rect = &mut frame.gpu_resource_rects[resource_rect_index];
|
||||
resource_rect.uv0 = DevicePoint::new(image.u0, image.v0);
|
||||
|
@ -24,11 +24,14 @@ use thread_profiler::register_thread_with_profiler;
|
||||
use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
|
||||
use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
|
||||
use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
|
||||
use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance, TileOffset, TileSize};
|
||||
use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
|
||||
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError};
|
||||
use webrender_traits::{ExternalImageData, ExternalImageType};
|
||||
use threadpool::ThreadPool;
|
||||
use euclid::Point2D;
|
||||
|
||||
const DEFAULT_TILE_SIZE: TileSize = 512;
|
||||
|
||||
thread_local!(pub static FONT_CONTEXT: RefCell<FontContext> = RefCell::new(FontContext::new()));
|
||||
|
||||
type GlyphCache = ResourceClassCache<RenderedGlyphKey, Option<TextureCacheItemId>>;
|
||||
@ -94,7 +97,7 @@ impl RenderedGlyphKey {
|
||||
|
||||
pub struct ImageProperties {
|
||||
pub descriptor: ImageDescriptor,
|
||||
pub external_id: Option<ExternalImageId>,
|
||||
pub external_image: Option<ExternalImageData>,
|
||||
pub tiling: Option<TileSize>,
|
||||
}
|
||||
|
||||
@ -254,6 +257,20 @@ impl ResourceCache {
|
||||
self.texture_cache.max_texture_size()
|
||||
}
|
||||
|
||||
fn should_tile(&self, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
|
||||
let limit = self.max_texture_size();
|
||||
let size_check = descriptor.width > limit || descriptor.height > limit;
|
||||
return match data {
|
||||
&ImageData::Raw(_) => { size_check }
|
||||
&ImageData::Blob(_) => { size_check }
|
||||
&ImageData::External(info) => {
|
||||
// External handles already represent existing textures so it does
|
||||
// not make sense to tile them into smaller ones.
|
||||
info.image_type == ExternalImageType::ExternalBuffer && size_check
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
|
||||
// Push the new font to the glyph cache thread, and also store
|
||||
// it locally for glyph metric requests.
|
||||
@ -275,10 +292,10 @@ impl ResourceCache {
|
||||
descriptor: ImageDescriptor,
|
||||
data: ImageData,
|
||||
mut tiling: Option<TileSize>) {
|
||||
if descriptor.width > self.max_texture_size() || descriptor.height > self.max_texture_size() {
|
||||
if tiling.is_none() && self.should_tile(&descriptor, &data) {
|
||||
// We aren't going to be able to upload a texture this big, so tile it, even
|
||||
// if tiling was not requested.
|
||||
tiling = Some(512);
|
||||
tiling = Some(DEFAULT_TILE_SIZE);
|
||||
}
|
||||
|
||||
let resource = ImageResource {
|
||||
@ -295,36 +312,33 @@ impl ResourceCache {
|
||||
pub fn update_image_template(&mut self,
|
||||
image_key: ImageKey,
|
||||
descriptor: ImageDescriptor,
|
||||
bytes: Vec<u8>,
|
||||
data: ImageData,
|
||||
dirty_rect: Option<DeviceUintRect>) {
|
||||
let (next_epoch, prev_dirty_rect) = match self.image_templates.get(&image_key) {
|
||||
Some(image) => {
|
||||
// This image should not be an external image.
|
||||
match image.data {
|
||||
ImageData::ExternalHandle(id) => {
|
||||
panic!("Update an external image with buffer, id={} image_key={:?}", id.0, image_key);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
let resource = if let Some(image) = self.image_templates.get(&image_key) {
|
||||
assert!(image.descriptor.width == descriptor.width);
|
||||
assert!(image.descriptor.height == descriptor.height);
|
||||
assert!(image.descriptor.format == descriptor.format);
|
||||
|
||||
let Epoch(current_epoch) = image.epoch;
|
||||
(Epoch(current_epoch + 1), image.dirty_rect)
|
||||
}
|
||||
None => {
|
||||
(Epoch(0), None)
|
||||
}
|
||||
};
|
||||
let next_epoch = Epoch(image.epoch.0 + 1);
|
||||
|
||||
let resource = ImageResource {
|
||||
descriptor: descriptor,
|
||||
data: ImageData::new(bytes),
|
||||
epoch: next_epoch,
|
||||
tiling: None,
|
||||
dirty_rect: match (dirty_rect, prev_dirty_rect) {
|
||||
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
|
||||
(Some(rect), None) => Some(rect),
|
||||
_ => None,
|
||||
},
|
||||
let mut tiling = image.tiling;
|
||||
if tiling.is_none() && self.should_tile(&descriptor, &data) {
|
||||
tiling = Some(DEFAULT_TILE_SIZE);
|
||||
}
|
||||
|
||||
ImageResource {
|
||||
descriptor: descriptor,
|
||||
data: data,
|
||||
epoch: next_epoch,
|
||||
tiling: tiling,
|
||||
dirty_rect: match (dirty_rect, image.dirty_rect) {
|
||||
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
|
||||
(Some(rect), None) => Some(rect),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
panic!("Attempt to update non-existant image (key {:?}).", image_key);
|
||||
};
|
||||
|
||||
self.image_templates.insert(image_key, resource);
|
||||
@ -336,10 +350,16 @@ impl ResourceCache {
|
||||
// If the key is associated to an external image, pass the external id to renderer for cleanup.
|
||||
if let Some(image) = value {
|
||||
match image.data {
|
||||
ImageData::ExternalHandle(id) => {
|
||||
self.pending_external_image_update_list.push(id);
|
||||
},
|
||||
_ => {},
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
ExternalImageType::TextureRectHandle => {
|
||||
self.pending_external_image_update_list.push(ext_image.id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return;
|
||||
@ -394,6 +414,7 @@ impl ResourceCache {
|
||||
// TODO(nical): figure out the scale factor (should change with zoom).
|
||||
scale_factor: 1.0,
|
||||
},
|
||||
template.dirty_rect,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -523,15 +544,24 @@ impl ResourceCache {
|
||||
pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties {
|
||||
let image_template = &self.image_templates[&image_key];
|
||||
|
||||
let external_id = match image_template.data {
|
||||
ImageData::ExternalHandle(id) => Some(id),
|
||||
// raw and externalBuffer are all use resource_cache.
|
||||
ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => None,
|
||||
let external_image = match image_template.data {
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
ExternalImageType::TextureRectHandle => {
|
||||
Some(ext_image)
|
||||
},
|
||||
// external buffer uses resource_cache.
|
||||
ExternalImageType::ExternalBuffer => None,
|
||||
}
|
||||
},
|
||||
// raw and blob image are all using resource_cache.
|
||||
ImageData::Raw(..) | ImageData::Blob(..) => None,
|
||||
};
|
||||
|
||||
ImageProperties {
|
||||
descriptor: image_template.descriptor,
|
||||
external_id: external_id,
|
||||
external_image: external_image,
|
||||
tiling: image_template.tiling,
|
||||
}
|
||||
}
|
||||
@ -652,94 +682,112 @@ impl ResourceCache {
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize_image_request(&mut self,
|
||||
request: ImageRequest,
|
||||
image_data: Option<ImageData>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||
fn update_texture_cache(&mut self,
|
||||
request: &ImageRequest,
|
||||
image_data: Option<ImageData>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||
let image_template = self.image_templates.get_mut(&request.key).unwrap();
|
||||
let image_data = image_data.unwrap_or_else(||{
|
||||
image_template.data.clone()
|
||||
});
|
||||
|
||||
match image_template.data {
|
||||
ImageData::ExternalHandle(..) => {
|
||||
// external handle doesn't need to update the texture_cache.
|
||||
let descriptor = if let Some(tile) = request.tile {
|
||||
let tile_size = image_template.tiling.unwrap() as u32;
|
||||
let image_descriptor = &image_template.descriptor;
|
||||
let stride = image_descriptor.compute_stride();
|
||||
let bpp = image_descriptor.format.bytes_per_pixel().unwrap();
|
||||
|
||||
// Storage for the tiles on the right and bottom edges is shrunk to
|
||||
// fit the image data (See decompose_tiled_image in frame.rs).
|
||||
let actual_width = if (tile.x as u32) < image_descriptor.width / tile_size {
|
||||
tile_size
|
||||
} else {
|
||||
image_descriptor.width % tile_size
|
||||
};
|
||||
|
||||
let actual_height = if (tile.y as u32) < image_descriptor.height / tile_size {
|
||||
tile_size
|
||||
} else {
|
||||
image_descriptor.height % tile_size
|
||||
};
|
||||
|
||||
let offset = image_descriptor.offset + tile.y as u32 * tile_size * stride
|
||||
+ tile.x as u32 * tile_size * bpp;
|
||||
|
||||
ImageDescriptor {
|
||||
width: actual_width,
|
||||
height: actual_height,
|
||||
stride: Some(stride),
|
||||
offset: offset,
|
||||
format: image_descriptor.format,
|
||||
is_opaque: image_descriptor.is_opaque,
|
||||
}
|
||||
ImageData::Raw(..) | ImageData::ExternalBuffer(..) | ImageData::Blob(..) => {
|
||||
let descriptor = if let Some(tile) = request.tile {
|
||||
let tile_size = image_template.tiling.unwrap() as u32;
|
||||
let image_descriptor = &image_template.descriptor;
|
||||
let stride = image_descriptor.compute_stride();
|
||||
let bpp = image_descriptor.format.bytes_per_pixel().unwrap();
|
||||
} else {
|
||||
image_template.descriptor.clone()
|
||||
};
|
||||
|
||||
// Storage for the tiles on the right and bottom edges is shrunk to
|
||||
// fit the image data (See decompose_tiled_image in frame.rs).
|
||||
let actual_width = if (tile.x as u32) < image_descriptor.width / tile_size {
|
||||
tile_size
|
||||
} else {
|
||||
image_descriptor.width % tile_size
|
||||
match self.cached_images.entry(request.clone(), self.current_frame_id) {
|
||||
Occupied(entry) => {
|
||||
let image_id = entry.get().texture_cache_id;
|
||||
|
||||
if entry.get().epoch != image_template.epoch {
|
||||
self.texture_cache.update(image_id,
|
||||
descriptor,
|
||||
image_data,
|
||||
image_template.dirty_rect);
|
||||
|
||||
// Update the cached epoch
|
||||
*entry.into_mut() = CachedImageInfo {
|
||||
texture_cache_id: image_id,
|
||||
epoch: image_template.epoch,
|
||||
};
|
||||
image_template.dirty_rect = None;
|
||||
}
|
||||
}
|
||||
Vacant(entry) => {
|
||||
let image_id = self.texture_cache.new_item_id();
|
||||
|
||||
let actual_height = if (tile.y as u32) < image_descriptor.height / tile_size {
|
||||
tile_size
|
||||
} else {
|
||||
image_descriptor.height % tile_size
|
||||
};
|
||||
|
||||
let offset = image_descriptor.offset + tile.y as u32 * tile_size * stride
|
||||
+ tile.x as u32 * tile_size * bpp;
|
||||
|
||||
ImageDescriptor {
|
||||
width: actual_width,
|
||||
height: actual_height,
|
||||
stride: Some(stride),
|
||||
offset: offset,
|
||||
format: image_descriptor.format,
|
||||
is_opaque: image_descriptor.is_opaque,
|
||||
}
|
||||
} else {
|
||||
image_template.descriptor.clone()
|
||||
let filter = match request.rendering {
|
||||
ImageRendering::Pixelated => TextureFilter::Nearest,
|
||||
ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
|
||||
};
|
||||
|
||||
match self.cached_images.entry(request.clone(), self.current_frame_id) {
|
||||
Occupied(entry) => {
|
||||
let image_id = entry.get().texture_cache_id;
|
||||
self.texture_cache.insert(image_id,
|
||||
descriptor,
|
||||
filter,
|
||||
image_data,
|
||||
texture_cache_profile);
|
||||
|
||||
if entry.get().epoch != image_template.epoch {
|
||||
self.texture_cache.update(image_id,
|
||||
descriptor,
|
||||
image_data,
|
||||
image_template.dirty_rect);
|
||||
|
||||
// Update the cached epoch
|
||||
*entry.into_mut() = CachedImageInfo {
|
||||
texture_cache_id: image_id,
|
||||
epoch: image_template.epoch,
|
||||
};
|
||||
image_template.dirty_rect = None;
|
||||
}
|
||||
entry.insert(CachedImageInfo {
|
||||
texture_cache_id: image_id,
|
||||
epoch: image_template.epoch,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
fn finalize_image_request(&mut self,
|
||||
request: ImageRequest,
|
||||
image_data: Option<ImageData>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||
match self.image_templates.get(&request.key).unwrap().data {
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
ExternalImageType::TextureRectHandle => {
|
||||
// external handle doesn't need to update the texture_cache.
|
||||
}
|
||||
Vacant(entry) => {
|
||||
let image_id = self.texture_cache.new_item_id();
|
||||
|
||||
let filter = match request.rendering {
|
||||
ImageRendering::Pixelated => TextureFilter::Nearest,
|
||||
ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
|
||||
};
|
||||
|
||||
self.texture_cache.insert(image_id,
|
||||
descriptor,
|
||||
filter,
|
||||
ExternalImageType::ExternalBuffer => {
|
||||
self.update_texture_cache(&request,
|
||||
image_data,
|
||||
texture_cache_profile);
|
||||
|
||||
entry.insert(CachedImageInfo {
|
||||
texture_cache_id: image_id,
|
||||
epoch: image_template.epoch,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageData::Raw(..) | ImageData::Blob(..) => {
|
||||
self.update_texture_cache(&request,
|
||||
image_data,
|
||||
texture_cache_profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,15 +113,15 @@ impl Scene {
|
||||
self.root_pipeline_id = Some(pipeline_id);
|
||||
}
|
||||
|
||||
pub fn set_root_display_list(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
epoch: Epoch,
|
||||
built_display_list: BuiltDisplayList,
|
||||
background_color: Option<ColorF>,
|
||||
viewport_size: LayerSize,
|
||||
auxiliary_lists: AuxiliaryLists) {
|
||||
pub fn set_display_list(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
epoch: Epoch,
|
||||
built_display_list: BuiltDisplayList,
|
||||
background_color: Option<ColorF>,
|
||||
viewport_size: LayerSize,
|
||||
auxiliary_lists: AuxiliaryLists) {
|
||||
self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
|
||||
self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());
|
||||
self.display_lists.insert(pipeline_id, built_display_list.into_display_items());
|
||||
|
||||
let new_pipeline = ScenePipeline {
|
||||
pipeline_id: pipeline_id,
|
||||
|
@ -16,7 +16,7 @@ use std::mem;
|
||||
use std::slice::Iter;
|
||||
use time;
|
||||
use util;
|
||||
use webrender_traits::{ImageData, ImageFormat, DevicePixel, DeviceIntPoint};
|
||||
use webrender_traits::{ExternalImageType, ImageData, ImageFormat, DevicePixel, DeviceIntPoint};
|
||||
use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
|
||||
use webrender_traits::ImageDescriptor;
|
||||
|
||||
@ -611,8 +611,8 @@ impl TextureCache {
|
||||
};
|
||||
|
||||
// TODO(gw): Handle this sensibly (support failing to render items that can't fit?)
|
||||
assert!(requested_size.width < self.max_texture_size);
|
||||
assert!(requested_size.height < self.max_texture_size);
|
||||
assert!(requested_size.width <= self.max_texture_size);
|
||||
assert!(requested_size.height <= self.max_texture_size);
|
||||
|
||||
let mut page_id = None; //using ID here to please the borrow checker
|
||||
for (i, page) in page_list.iter_mut().enumerate() {
|
||||
@ -715,34 +715,37 @@ impl TextureCache {
|
||||
debug_assert_eq!(existing_item.allocated_rect.size.height, descriptor.height);
|
||||
|
||||
let op = match data {
|
||||
ImageData::ExternalHandle(..) | ImageData::ExternalBuffer(..)=> {
|
||||
ImageData::External(..) => {
|
||||
panic!("Doesn't support Update() for external image.");
|
||||
}
|
||||
ImageData::Blob(..) => {
|
||||
panic!("The vector image should have been rasterized into a raw image.");
|
||||
}
|
||||
ImageData::Raw(bytes) => {
|
||||
if let Some(dirty) = dirty_rect {
|
||||
let stride = descriptor.compute_stride();
|
||||
let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
|
||||
TextureUpdateOp::Update {
|
||||
page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
|
||||
page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
|
||||
width: dirty.size.width,
|
||||
height: dirty.size.height,
|
||||
data: bytes,
|
||||
stride: Some(stride),
|
||||
offset: offset,
|
||||
match dirty_rect {
|
||||
Some(dirty) => {
|
||||
let stride = descriptor.compute_stride();
|
||||
let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
|
||||
TextureUpdateOp::Update {
|
||||
page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
|
||||
page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
|
||||
width: dirty.size.width,
|
||||
height: dirty.size.height,
|
||||
data: bytes,
|
||||
stride: Some(stride),
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TextureUpdateOp::Update {
|
||||
page_pos_x: existing_item.allocated_rect.origin.x,
|
||||
page_pos_y: existing_item.allocated_rect.origin.y,
|
||||
width: descriptor.width,
|
||||
height: descriptor.height,
|
||||
data: bytes,
|
||||
stride: descriptor.stride,
|
||||
offset: descriptor.offset,
|
||||
None => {
|
||||
TextureUpdateOp::Update {
|
||||
page_pos_x: existing_item.allocated_rect.origin.x,
|
||||
page_pos_y: existing_item.allocated_rect.origin.y,
|
||||
width: descriptor.width,
|
||||
height: descriptor.height,
|
||||
data: bytes,
|
||||
stride: descriptor.stride,
|
||||
offset: descriptor.offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -781,8 +784,25 @@ impl TextureCache {
|
||||
match result.kind {
|
||||
AllocationKind::TexturePage => {
|
||||
match data {
|
||||
ImageData::ExternalHandle(..) => {
|
||||
panic!("External handle should not go through texture_cache.");
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
ExternalImageType::TextureRectHandle => {
|
||||
panic!("External texture handle should not go through texture_cache.");
|
||||
}
|
||||
ExternalImageType::ExternalBuffer => {
|
||||
let update_op = TextureUpdate {
|
||||
id: result.item.texture_id,
|
||||
op: TextureUpdateOp::UpdateForExternalBuffer {
|
||||
rect: result.item.allocated_rect,
|
||||
id: ext_image.id,
|
||||
stride: stride,
|
||||
},
|
||||
};
|
||||
|
||||
self.pending_updates.push(update_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageData::Blob(..) => {
|
||||
panic!("The vector image should have been rasterized.");
|
||||
@ -801,26 +821,34 @@ impl TextureCache {
|
||||
},
|
||||
};
|
||||
|
||||
self.pending_updates.push(update_op);
|
||||
}
|
||||
ImageData::ExternalBuffer(id) => {
|
||||
let update_op = TextureUpdate {
|
||||
id: result.item.texture_id,
|
||||
op: TextureUpdateOp::UpdateForExternalBuffer {
|
||||
rect: result.item.allocated_rect,
|
||||
id: id,
|
||||
stride: stride,
|
||||
},
|
||||
};
|
||||
|
||||
self.pending_updates.push(update_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
AllocationKind::Standalone => {
|
||||
match data {
|
||||
ImageData::ExternalHandle(..) => {
|
||||
panic!("External handle should not go through texture_cache.");
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
ExternalImageType::TextureRectHandle => {
|
||||
panic!("External texture handle should not go through texture_cache.");
|
||||
}
|
||||
ExternalImageType::ExternalBuffer => {
|
||||
let update_op = TextureUpdate {
|
||||
id: result.item.texture_id,
|
||||
op: TextureUpdateOp::Create {
|
||||
width: width,
|
||||
height: height,
|
||||
format: format,
|
||||
filter: filter,
|
||||
mode: RenderTargetMode::None,
|
||||
data: Some(data),
|
||||
},
|
||||
};
|
||||
|
||||
self.pending_updates.push(update_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let update_op = TextureUpdate {
|
||||
|
@ -23,10 +23,12 @@ use std::collections::HashMap;
|
||||
use std::hash::BuildHasherDefault;
|
||||
use texture_cache::TexturePage;
|
||||
use util::{TransformedRect, TransformedRectKind};
|
||||
use webrender_traits::{AuxiliaryLists, ColorF, DeviceIntPoint, DeviceIntRect, DeviceUintPoint};
|
||||
use webrender_traits::{AuxiliaryLists, ColorF, DeviceIntPoint, DeviceIntRect};
|
||||
use webrender_traits::{DeviceIntSize, DeviceUintPoint};
|
||||
use webrender_traits::{DeviceUintSize, FontRenderMode, ImageRendering, LayerPoint, LayerRect};
|
||||
use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, ScrollLayerId};
|
||||
use webrender_traits::{WorldPoint4D, WorldToLayerTransform};
|
||||
use webrender_traits::{ExternalImageType};
|
||||
|
||||
// Special sentinel value recognized by the shader. It is considered to be
|
||||
// a dummy task that doesn't mask out anything.
|
||||
@ -69,7 +71,24 @@ impl AlphaBatchHelpers for PrimitiveStore {
|
||||
let batch_kind = match metadata.prim_kind {
|
||||
PrimitiveKind::Border => AlphaBatchKind::Border,
|
||||
PrimitiveKind::BoxShadow => AlphaBatchKind::BoxShadow,
|
||||
PrimitiveKind::Image => AlphaBatchKind::Image,
|
||||
PrimitiveKind::Image => {
|
||||
let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
|
||||
|
||||
match image_cpu.color_texture_id {
|
||||
SourceTexture::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle => AlphaBatchKind::Image,
|
||||
ExternalImageType::TextureRectHandle => AlphaBatchKind::ImageRect,
|
||||
_ => {
|
||||
panic!("Non-texture handle type should be handled in other way.");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
AlphaBatchKind::Image
|
||||
}
|
||||
}
|
||||
}
|
||||
PrimitiveKind::YuvImage => AlphaBatchKind::YuvImage,
|
||||
PrimitiveKind::Rectangle => AlphaBatchKind::Rectangle,
|
||||
PrimitiveKind::AlignedGradient => AlphaBatchKind::AlignedGradient,
|
||||
@ -273,7 +292,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
|
||||
});
|
||||
}
|
||||
}
|
||||
AlphaBatchKind::Image => {
|
||||
AlphaBatchKind::Image |
|
||||
AlphaBatchKind::ImageRect => {
|
||||
let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
|
||||
|
||||
data.push(PrimitiveInstance {
|
||||
@ -854,6 +874,46 @@ pub struct RenderTargetContext<'a> {
|
||||
pub resource_cache: &'a ResourceCache,
|
||||
}
|
||||
|
||||
struct TextureAllocator {
|
||||
// TODO(gw): Replace this with a simpler allocator for
|
||||
// render target allocation - this use case doesn't need
|
||||
// to deal with coalescing etc that the general texture
|
||||
// cache allocator requires.
|
||||
page_allocator: TexturePage,
|
||||
|
||||
// Track the used rect of the render target, so that
|
||||
// we can set a scissor rect and only clear to the
|
||||
// used portion of the target as an optimization.
|
||||
used_rect: DeviceIntRect,
|
||||
}
|
||||
|
||||
impl TextureAllocator {
|
||||
fn new(size: DeviceUintSize) -> TextureAllocator {
|
||||
TextureAllocator {
|
||||
page_allocator: TexturePage::new(CacheTextureId(0), size),
|
||||
used_rect: DeviceIntRect::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn allocate(&mut self, size: &DeviceUintSize) -> Option<DeviceUintPoint> {
|
||||
let origin = self.page_allocator.allocate(size);
|
||||
|
||||
if let Some(origin) = origin {
|
||||
// TODO(gw): We need to make all the device rects
|
||||
// be consistent in the use of the
|
||||
// DeviceIntRect and DeviceUintRect types!
|
||||
let origin = DeviceIntPoint::new(origin.x as i32,
|
||||
origin.y as i32);
|
||||
let size = DeviceIntSize::new(size.width as i32,
|
||||
size.height as i32);
|
||||
let rect = DeviceIntRect::new(origin, size);
|
||||
self.used_rect = rect.union(&self.used_rect);
|
||||
}
|
||||
|
||||
origin
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RenderTarget {
|
||||
fn new(size: DeviceUintSize) -> Self;
|
||||
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint>;
|
||||
@ -866,6 +926,7 @@ pub trait RenderTarget {
|
||||
ctx: &RenderTargetContext,
|
||||
render_tasks: &RenderTaskCollection,
|
||||
pass_index: RenderPassIndex);
|
||||
fn used_rect(&self) -> DeviceIntRect;
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -953,12 +1014,12 @@ pub struct ColorRenderTarget {
|
||||
pub horizontal_blurs: Vec<BlurCommand>,
|
||||
pub readbacks: Vec<DeviceIntRect>,
|
||||
pub isolate_clears: Vec<DeviceIntRect>,
|
||||
page_allocator: TexturePage,
|
||||
allocator: TextureAllocator,
|
||||
}
|
||||
|
||||
impl RenderTarget for ColorRenderTarget {
|
||||
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
|
||||
self.page_allocator.allocate(&size)
|
||||
self.allocator.allocate(&size)
|
||||
}
|
||||
|
||||
fn new(size: DeviceUintSize) -> ColorRenderTarget {
|
||||
@ -971,10 +1032,14 @@ impl RenderTarget for ColorRenderTarget {
|
||||
horizontal_blurs: Vec::new(),
|
||||
readbacks: Vec::new(),
|
||||
isolate_clears: Vec::new(),
|
||||
page_allocator: TexturePage::new(CacheTextureId(0), size),
|
||||
allocator: TextureAllocator::new(size),
|
||||
}
|
||||
}
|
||||
|
||||
fn used_rect(&self) -> DeviceIntRect {
|
||||
self.allocator.used_rect
|
||||
}
|
||||
|
||||
fn build(&mut self,
|
||||
ctx: &RenderTargetContext,
|
||||
render_tasks: &mut RenderTaskCollection,
|
||||
@ -1099,21 +1164,25 @@ impl RenderTarget for ColorRenderTarget {
|
||||
|
||||
pub struct AlphaRenderTarget {
|
||||
pub clip_batcher: ClipBatcher,
|
||||
page_allocator: TexturePage,
|
||||
allocator: TextureAllocator,
|
||||
}
|
||||
|
||||
impl RenderTarget for AlphaRenderTarget {
|
||||
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
|
||||
self.page_allocator.allocate(&size)
|
||||
self.allocator.allocate(&size)
|
||||
}
|
||||
|
||||
fn new(size: DeviceUintSize) -> AlphaRenderTarget {
|
||||
AlphaRenderTarget {
|
||||
clip_batcher: ClipBatcher::new(),
|
||||
page_allocator: TexturePage::new(CacheTextureId(0), size),
|
||||
allocator: TextureAllocator::new(size),
|
||||
}
|
||||
}
|
||||
|
||||
fn used_rect(&self) -> DeviceIntRect {
|
||||
self.allocator.used_rect
|
||||
}
|
||||
|
||||
fn add_task(&mut self,
|
||||
task: RenderTask,
|
||||
ctx: &RenderTargetContext,
|
||||
@ -1261,6 +1330,7 @@ pub enum AlphaBatchKind {
|
||||
Rectangle,
|
||||
TextRun,
|
||||
Image,
|
||||
ImageRect,
|
||||
YuvImage,
|
||||
Border,
|
||||
AlignedGradient,
|
||||
@ -1391,6 +1461,7 @@ impl PrimitiveBatch {
|
||||
AlphaBatchKind::Rectangle |
|
||||
AlphaBatchKind::TextRun |
|
||||
AlphaBatchKind::Image |
|
||||
AlphaBatchKind::ImageRect |
|
||||
AlphaBatchKind::YuvImage |
|
||||
AlphaBatchKind::Border |
|
||||
AlphaBatchKind::AlignedGradient |
|
||||
|
@ -5,12 +5,12 @@ authors = ["The Mozilla Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
webrender_traits = {path = "../webrender_traits", version = "0.27.0"}
|
||||
webrender_traits = {path = "../webrender_traits", version = "0.31.0"}
|
||||
euclid = "0.11"
|
||||
app_units = "0.4"
|
||||
gleam = "0.4"
|
||||
|
||||
[dependencies.webrender]
|
||||
path = "../webrender"
|
||||
version = "0.26.0"
|
||||
version = "0.30.0"
|
||||
default-features = false
|
||||
|
115
gfx/webrender_bindings/RenderBufferTextureHost.cpp
Normal file
115
gfx/webrender_bindings/RenderBufferTextureHost.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RenderBufferTextureHost.h"
|
||||
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
RenderBufferTextureHost::RenderBufferTextureHost(uint8_t* aBuffer,
|
||||
const layers::BufferDescriptor& aDescriptor)
|
||||
: mBuffer(aBuffer)
|
||||
, mDescriptor(aDescriptor)
|
||||
, mLocked(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
|
||||
|
||||
switch (mDescriptor.type()) {
|
||||
case layers::BufferDescriptor::TYCbCrDescriptor: {
|
||||
const layers::YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
|
||||
mSize = ycbcr.ySize();
|
||||
mFormat = gfx::SurfaceFormat::YUV;
|
||||
break;
|
||||
}
|
||||
case layers::BufferDescriptor::TRGBDescriptor: {
|
||||
const layers::RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
|
||||
mSize = rgb.size();
|
||||
mFormat = rgb.format();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
|
||||
MOZ_CRASH("GFX: Bad descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
RenderBufferTextureHost::~RenderBufferTextureHost()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
RenderBufferTextureHost::GetAsSurface()
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> result;
|
||||
if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
result = layers::ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
|
||||
GetBuffer(), mDescriptor.get_YCbCrDescriptor());
|
||||
if (NS_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
result =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
|
||||
layers::ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
|
||||
mSize, mFormat);
|
||||
}
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
RenderBufferTextureHost::Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
|
||||
// XXX temporal workaround for YUV handling
|
||||
if (!mSurface) {
|
||||
mSurface = GetAsSurface();
|
||||
if (!mSurface) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mSurface->Map(gfx::DataSourceSurface::MapType::READ_WRITE, &mMap))) {
|
||||
mSurface = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RenderBufferTextureHost::Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mLocked = false;
|
||||
if (mSurface) {
|
||||
mSurface->Unmap();
|
||||
}
|
||||
mSurface = nullptr;
|
||||
}
|
||||
|
||||
const uint8_t*
|
||||
RenderBufferTextureHost::GetDataForRender() const
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
MOZ_ASSERT(mSurface);
|
||||
return mMap.mData;
|
||||
}
|
||||
|
||||
size_t
|
||||
RenderBufferTextureHost::GetBufferSizeForRender() const
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
MOZ_ASSERT(mSurface);
|
||||
return mMap.mStride * mSurface->GetSize().height;
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
61
gfx/webrender_bindings/RenderBufferTextureHost.h
Normal file
61
gfx/webrender_bindings/RenderBufferTextureHost.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
|
||||
#define MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
|
||||
|
||||
#include "RenderTextureHost.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
class RenderBufferTextureHost final : public RenderTextureHost
|
||||
{
|
||||
public:
|
||||
RenderBufferTextureHost(uint8_t* aBuffer,
|
||||
const layers::BufferDescriptor& aDescriptor);
|
||||
|
||||
virtual bool Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
virtual gfx::IntSize GetSize() const override
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
virtual gfx::SurfaceFormat GetFormat() const override
|
||||
{
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
virtual RenderBufferTextureHost* AsBufferTextureHost() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
const uint8_t* GetDataForRender() const;
|
||||
size_t GetBufferSizeForRender() const;
|
||||
|
||||
private:
|
||||
virtual ~RenderBufferTextureHost();
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
|
||||
uint8_t* GetBuffer() const
|
||||
{
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
uint8_t* mBuffer;
|
||||
layers::BufferDescriptor mDescriptor;
|
||||
gfx::IntSize mSize;
|
||||
gfx::SurfaceFormat mFormat;
|
||||
RefPtr<gfx::DataSourceSurface> mSurface;
|
||||
gfx::DataSourceSurface::MappedSurface mMap;
|
||||
bool mLocked;
|
||||
};
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
|
103
gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.cpp
Normal file
103
gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RenderMacIOSurfaceTextureHostOGL.h"
|
||||
|
||||
#include "GLContextCGL.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
RenderMacIOSurfaceTextureHostOGL::RenderMacIOSurfaceTextureHostOGL(MacIOSurface* aSurface)
|
||||
: mTextureHandle(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(RenderMacIOSurfaceTextureHostOGL, RenderTextureHostOGL);
|
||||
|
||||
mSurface = aSurface;
|
||||
}
|
||||
|
||||
RenderMacIOSurfaceTextureHostOGL::~RenderMacIOSurfaceTextureHostOGL()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(RenderMacIOSurfaceTextureHostOGL, RenderTextureHostOGL);
|
||||
DeleteTextureHandle();
|
||||
}
|
||||
|
||||
bool
|
||||
RenderMacIOSurfaceTextureHostOGL::Lock()
|
||||
{
|
||||
if (!mSurface || !mGL || !mGL->MakeCurrent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mTextureHandle) {
|
||||
// xxx: should we need to handle the PlaneCount 3 iosurface?
|
||||
MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
|
||||
MOZ_ASSERT(gl::GLContextCGL::Cast(mGL.get())->GetCGLContext());
|
||||
|
||||
mGL->fGenTextures(1, &mTextureHandle);
|
||||
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
|
||||
gl::ScopedBindTexture texture(mGL, mTextureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
|
||||
mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL.get())->GetCGLContext(), 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RenderMacIOSurfaceTextureHostOGL::Unlock()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
RenderMacIOSurfaceTextureHostOGL::SetGLContext(gl::GLContext* aContext)
|
||||
{
|
||||
if (mGL.get() != aContext) {
|
||||
// release the texture handle in the previous gl context
|
||||
DeleteTextureHandle();
|
||||
mGL = aContext;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RenderMacIOSurfaceTextureHostOGL::DeleteTextureHandle()
|
||||
{
|
||||
if (mTextureHandle != 0 && mGL && mGL->MakeCurrent()) {
|
||||
mGL->fDeleteTextures(1, &mTextureHandle);
|
||||
}
|
||||
mTextureHandle = 0;
|
||||
}
|
||||
|
||||
GLuint
|
||||
RenderMacIOSurfaceTextureHostOGL::GetGLHandle()
|
||||
{
|
||||
return mTextureHandle;
|
||||
}
|
||||
|
||||
gfx::IntSize
|
||||
RenderMacIOSurfaceTextureHostOGL::GetSize() const
|
||||
{
|
||||
if (!mSurface) {
|
||||
return gfx::IntSize();
|
||||
}
|
||||
return gfx::IntSize(mSurface->GetDevicePixelWidth(),
|
||||
mSurface->GetDevicePixelHeight());
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat
|
||||
RenderMacIOSurfaceTextureHostOGL::GetFormat() const
|
||||
{
|
||||
if (!mSurface) {
|
||||
return gfx::SurfaceFormat::UNKNOWN;
|
||||
}
|
||||
return mSurface->GetReadFormat();
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
53
gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.h
Normal file
53
gfx/webrender_bindings/RenderMacIOSurfaceTextureHostOGL.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
|
||||
#define MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
|
||||
|
||||
#include "mozilla/gfx/MacIOSurface.h"
|
||||
#include "mozilla/layers/TextureHostOGL.h"
|
||||
#include "RenderTextureHostOGL.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class SurfaceDescriptorMacIOSurface;
|
||||
}
|
||||
|
||||
namespace wr {
|
||||
|
||||
class RenderMacIOSurfaceTextureHostOGL final : public RenderTextureHostOGL
|
||||
{
|
||||
public:
|
||||
explicit RenderMacIOSurfaceTextureHostOGL(MacIOSurface* aSurface);
|
||||
|
||||
virtual bool Lock() override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
virtual gfx::IntSize GetSize() const override;
|
||||
virtual gfx::SurfaceFormat GetFormat() const override;
|
||||
|
||||
virtual RenderMacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHostOGL() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual void SetGLContext(gl::GLContext* aContext) override;
|
||||
|
||||
virtual GLuint GetGLHandle() override;
|
||||
|
||||
private:
|
||||
virtual ~RenderMacIOSurfaceTextureHostOGL();
|
||||
void DeleteTextureHandle();
|
||||
|
||||
RefPtr<MacIOSurface> mSurface;
|
||||
RefPtr<gl::GLContext> mGL;
|
||||
GLuint mTextureHandle;
|
||||
};
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOSTOGL_H
|
@ -5,40 +5,12 @@
|
||||
|
||||
#include "RenderTextureHost.h"
|
||||
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/layers/ImageDataSerializer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
using namespace layers;
|
||||
|
||||
namespace wr {
|
||||
|
||||
RenderTextureHost::RenderTextureHost(uint8_t* aBuffer, const BufferDescriptor& aDescriptor)
|
||||
: mBuffer(aBuffer)
|
||||
, mDescriptor(aDescriptor)
|
||||
, mLocked(false)
|
||||
RenderTextureHost::RenderTextureHost()
|
||||
{
|
||||
MOZ_COUNT_CTOR(RenderTextureHost);
|
||||
|
||||
switch (mDescriptor.type()) {
|
||||
case BufferDescriptor::TYCbCrDescriptor: {
|
||||
const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
|
||||
mSize = ycbcr.ySize();
|
||||
mFormat = gfx::SurfaceFormat::YUV;
|
||||
break;
|
||||
}
|
||||
case BufferDescriptor::TRGBDescriptor: {
|
||||
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
|
||||
mSize = rgb.size();
|
||||
mFormat = rgb.format();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gfxCriticalError() << "Bad buffer host descriptor " << (int)mDescriptor.type();
|
||||
MOZ_CRASH("GFX: Bad descriptor");
|
||||
}
|
||||
}
|
||||
|
||||
RenderTextureHost::~RenderTextureHost()
|
||||
@ -46,73 +18,5 @@ RenderTextureHost::~RenderTextureHost()
|
||||
MOZ_COUNT_DTOR(RenderTextureHost);
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
RenderTextureHost::GetAsSurface()
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> result;
|
||||
if (mFormat == gfx::SurfaceFormat::YUV) {
|
||||
result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
|
||||
GetBuffer(), mDescriptor.get_YCbCrDescriptor());
|
||||
if (NS_WARN_IF(!result)) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
result =
|
||||
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
|
||||
ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
|
||||
mSize, mFormat);
|
||||
}
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
RenderTextureHost::Lock()
|
||||
{
|
||||
MOZ_ASSERT(!mLocked);
|
||||
|
||||
// XXX temporal workaround for YUV handling
|
||||
if (!mSurface) {
|
||||
mSurface = GetAsSurface();
|
||||
if (!mSurface) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mSurface->Map(DataSourceSurface::MapType::READ_WRITE, &mMap))) {
|
||||
mSurface = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
mLocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RenderTextureHost::Unlock()
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
mLocked = false;
|
||||
if (mSurface) {
|
||||
mSurface->Unmap();
|
||||
}
|
||||
mSurface = nullptr;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
RenderTextureHost::GetDataForRender() const
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
MOZ_ASSERT(mSurface);
|
||||
return mMap.mData;
|
||||
}
|
||||
|
||||
size_t
|
||||
RenderTextureHost::GetBufferSizeForRender() const
|
||||
{
|
||||
MOZ_ASSERT(mLocked);
|
||||
MOZ_ASSERT(mSurface);
|
||||
return mMap.mStride * mSurface->GetSize().height;
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
||||
|
@ -14,36 +14,27 @@
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
class RenderBufferTextureHost;
|
||||
class RenderTextureHostOGL;
|
||||
|
||||
class RenderTextureHost
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RenderTextureHost)
|
||||
|
||||
RenderTextureHost(uint8_t* aBuffer, const layers::BufferDescriptor& aDescriptor);
|
||||
public:
|
||||
RenderTextureHost();
|
||||
|
||||
bool Lock();
|
||||
virtual bool Lock() = 0;
|
||||
virtual void Unlock() = 0;
|
||||
|
||||
void Unlock();
|
||||
virtual gfx::IntSize GetSize() const = 0;
|
||||
virtual gfx::SurfaceFormat GetFormat() const = 0;
|
||||
|
||||
gfx::IntSize GetSize() const { return mSize; }
|
||||
|
||||
gfx::SurfaceFormat GetFormat() const { return mFormat; }
|
||||
|
||||
uint8_t* GetDataForRender() const;
|
||||
size_t GetBufferSizeForRender() const;
|
||||
virtual RenderBufferTextureHost* AsBufferTextureHost() { return nullptr; }
|
||||
virtual RenderTextureHostOGL* AsTextureHostOGL() { return nullptr; }
|
||||
|
||||
protected:
|
||||
~RenderTextureHost();
|
||||
already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
|
||||
uint8_t* GetBuffer() const { return mBuffer; }
|
||||
|
||||
uint8_t* mBuffer;
|
||||
layers::BufferDescriptor mDescriptor;
|
||||
gfx::IntSize mSize;
|
||||
gfx::SurfaceFormat mFormat;
|
||||
RefPtr<gfx::DataSourceSurface> mSurface;
|
||||
gfx::DataSourceSurface::MappedSurface mMap;
|
||||
bool mLocked;
|
||||
virtual ~RenderTextureHost();
|
||||
};
|
||||
|
||||
} // namespace wr
|
||||
|
22
gfx/webrender_bindings/RenderTextureHostOGL.cpp
Normal file
22
gfx/webrender_bindings/RenderTextureHostOGL.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RenderTextureHostOGL.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
RenderTextureHostOGL::RenderTextureHostOGL()
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(RenderTextureHostOGL, RenderTextureHost);
|
||||
}
|
||||
|
||||
RenderTextureHostOGL::~RenderTextureHostOGL()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(RenderTextureHostOGL, RenderTextureHost);
|
||||
}
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
35
gfx/webrender_bindings/RenderTextureHostOGL.h
Normal file
35
gfx/webrender_bindings/RenderTextureHostOGL.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
|
||||
#define MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
|
||||
|
||||
#include "RenderTextureHost.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace wr {
|
||||
|
||||
class RenderMacIOSurfaceTextureHostOGL;
|
||||
|
||||
class RenderTextureHostOGL : public RenderTextureHost
|
||||
{
|
||||
public:
|
||||
RenderTextureHostOGL();
|
||||
|
||||
virtual void SetGLContext(gl::GLContext* aContext) = 0;
|
||||
|
||||
virtual GLuint GetGLHandle() = 0;
|
||||
|
||||
virtual RenderTextureHostOGL* AsTextureHostOGL() { return this; }
|
||||
virtual RenderMacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHostOGL() { return nullptr; }
|
||||
|
||||
protected:
|
||||
virtual ~RenderTextureHostOGL();
|
||||
};
|
||||
|
||||
} // namespace wr
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_GFX_RENDERTEXTUREHOSTOGL_H
|
@ -196,6 +196,34 @@ RenderThread::UpdateAndRender(wr::WindowId aWindowId)
|
||||
));
|
||||
}
|
||||
|
||||
void
|
||||
RenderThread::Pause(wr::WindowId aWindowId)
|
||||
{
|
||||
MOZ_ASSERT(IsInRenderThread());
|
||||
|
||||
auto it = mRenderers.find(aWindowId);
|
||||
MOZ_ASSERT(it != mRenderers.end());
|
||||
if (it == mRenderers.end()) {
|
||||
return;
|
||||
}
|
||||
auto& renderer = it->second;
|
||||
renderer->Pause();
|
||||
}
|
||||
|
||||
bool
|
||||
RenderThread::Resume(wr::WindowId aWindowId)
|
||||
{
|
||||
MOZ_ASSERT(IsInRenderThread());
|
||||
|
||||
auto it = mRenderers.find(aWindowId);
|
||||
MOZ_ASSERT(it != mRenderers.end());
|
||||
if (it == mRenderers.end()) {
|
||||
return false;
|
||||
}
|
||||
auto& renderer = it->second;
|
||||
return renderer->Resume();
|
||||
}
|
||||
|
||||
void
|
||||
RenderThread::RegisterExternalImage(uint64_t aExternalImageId, RenderTextureHost* aTexture)
|
||||
{
|
||||
|
@ -102,6 +102,9 @@ public:
|
||||
/// Can only be called from the render thread.
|
||||
void UpdateAndRender(wr::WindowId aWindowId);
|
||||
|
||||
void Pause(wr::WindowId aWindowId);
|
||||
bool Resume(wr::WindowId aWindowId);
|
||||
|
||||
void RegisterExternalImage(uint64_t aExternalImageId, RenderTextureHost* aTexture);
|
||||
|
||||
void UnregisterExternalImage(uint64_t aExternalImageId);
|
||||
|
@ -9,7 +9,8 @@
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/webrender/RenderTextureHost.h"
|
||||
#include "mozilla/webrender/RenderBufferTextureHost.h"
|
||||
#include "mozilla/webrender/RenderTextureHostOGL.h"
|
||||
#include "mozilla/widget/CompositorWidget.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -19,10 +20,26 @@ WrExternalImage LockExternalImage(void* aObj, WrExternalImageId aId)
|
||||
{
|
||||
RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
|
||||
RenderTextureHost* texture = renderer->GetRenderTexture(aId.id);
|
||||
MOZ_ASSERT(texture);
|
||||
texture->Lock();
|
||||
return WrExternalImage { WrExternalImageIdType::RawData, 0.0f, 0.0f, 0.0f, 0.0f, 0,
|
||||
texture->GetDataForRender(), texture->GetBufferSizeForRender() };
|
||||
|
||||
if (texture->AsBufferTextureHost()) {
|
||||
RenderBufferTextureHost* bufferTexture = texture->AsBufferTextureHost();
|
||||
MOZ_ASSERT(bufferTexture);
|
||||
bufferTexture->Lock();
|
||||
|
||||
return RawDataToWrExternalImage(bufferTexture->GetDataForRender(),
|
||||
bufferTexture->GetBufferSizeForRender());
|
||||
} else {
|
||||
// texture handle case
|
||||
RenderTextureHostOGL* textureOGL = texture->AsTextureHostOGL();
|
||||
MOZ_ASSERT(textureOGL);
|
||||
gfx::IntSize size = textureOGL->GetSize();
|
||||
textureOGL->SetGLContext(renderer->mGL);
|
||||
textureOGL->Lock();
|
||||
|
||||
return NativeTextureToWrExternalImage(textureOGL->GetGLHandle(),
|
||||
0, 0,
|
||||
size.width, size.height);
|
||||
}
|
||||
}
|
||||
|
||||
void UnlockExternalImage(void* aObj, WrExternalImageId aId)
|
||||
@ -118,6 +135,32 @@ RendererOGL::Render()
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RendererOGL::Pause()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (!mGL || mGL->IsDestroyed()) {
|
||||
return;
|
||||
}
|
||||
// ReleaseSurface internally calls MakeCurrent.
|
||||
mGL->ReleaseSurface();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
RendererOGL::Resume()
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (!mGL || mGL->IsDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
// RenewSurface internally calls MakeCurrent.
|
||||
return mGL->RenewSurface(mWidget);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
RendererOGL::SetProfilerEnabled(bool aEnabled)
|
||||
{
|
||||
|
@ -70,6 +70,12 @@ public:
|
||||
WrRenderer* aWrRenderer,
|
||||
layers::CompositorBridgeParentBase* aBridge);
|
||||
|
||||
/// This can be called on the render thread only.
|
||||
void Pause();
|
||||
|
||||
/// This can be called on the render thread only.
|
||||
bool Resume();
|
||||
|
||||
layers::CompositorBridgeParentBase* GetCompositorBridge() { return mBridge; }
|
||||
|
||||
WrRenderedEpochs* FlushRenderedEpochs();
|
||||
|
@ -46,10 +46,10 @@ public:
|
||||
layers::AutoCompleteTask complete(mTask);
|
||||
|
||||
RefPtr<gl::GLContext> gl;
|
||||
if (gfxVars::UseWebRenderANGLE()) {
|
||||
if (gfx::gfxVars::UseWebRenderANGLE()) {
|
||||
gl = gl::GLContextProviderEGL::CreateForCompositorWidget(mCompositorWidget, true);
|
||||
if (!gl || !gl->IsANGLE()) {
|
||||
gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << hexa(gl.get());
|
||||
gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << gfx::hexa(gl.get());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ public:
|
||||
gl = gl::GLContextProvider::CreateForCompositorWidget(mCompositorWidget, true);
|
||||
}
|
||||
if (!gl || !gl->MakeCurrent()) {
|
||||
gfxCriticalNote << "Failed GL context creation for WebRender: " << hexa(gl.get());
|
||||
gfxCriticalNote << "Failed GL context creation for WebRender: " << gfx::hexa(gl.get());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -263,6 +263,80 @@ WebRenderAPI::Readback(gfx::IntSize size,
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderAPI::Pause()
|
||||
{
|
||||
class PauseEvent : public RendererEvent
|
||||
{
|
||||
public:
|
||||
explicit PauseEvent(layers::SynchronousTask* aTask)
|
||||
: mTask(aTask)
|
||||
{
|
||||
MOZ_COUNT_CTOR(PauseEvent);
|
||||
}
|
||||
|
||||
~PauseEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(PauseEvent);
|
||||
}
|
||||
|
||||
virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
|
||||
{
|
||||
aRenderThread.Pause(aWindowId);
|
||||
layers::AutoCompleteTask complete(mTask);
|
||||
}
|
||||
|
||||
layers::SynchronousTask* mTask;
|
||||
};
|
||||
|
||||
layers::SynchronousTask task("Pause");
|
||||
auto event = MakeUnique<PauseEvent>(&task);
|
||||
// This event will be passed from wr_backend thread to renderer thread. That
|
||||
// implies that all frame data have been processed when the renderer runs this event.
|
||||
RunOnRenderThread(Move(event));
|
||||
|
||||
task.Wait();
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderAPI::Resume()
|
||||
{
|
||||
class ResumeEvent : public RendererEvent
|
||||
{
|
||||
public:
|
||||
explicit ResumeEvent(layers::SynchronousTask* aTask, bool* aResult)
|
||||
: mTask(aTask)
|
||||
, mResult(aResult)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ResumeEvent);
|
||||
}
|
||||
|
||||
~ResumeEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ResumeEvent);
|
||||
}
|
||||
|
||||
virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
|
||||
{
|
||||
*mResult = aRenderThread.Resume(aWindowId);
|
||||
layers::AutoCompleteTask complete(mTask);
|
||||
}
|
||||
|
||||
layers::SynchronousTask* mTask;
|
||||
bool* mResult;
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
layers::SynchronousTask task("Resume");
|
||||
auto event = MakeUnique<ResumeEvent>(&task, &result);
|
||||
// This event will be passed from wr_backend thread to renderer thread. That
|
||||
// implies that all frame data have been processed when the renderer runs this event.
|
||||
RunOnRenderThread(Move(event));
|
||||
|
||||
task.Wait();
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderAPI::WaitFlushed()
|
||||
{
|
||||
@ -291,9 +365,7 @@ WebRenderAPI::WaitFlushed()
|
||||
layers::SynchronousTask task("WaitFlushed");
|
||||
auto event = MakeUnique<WaitFlushedEvent>(&task);
|
||||
// This event will be passed from wr_backend thread to renderer thread. That
|
||||
// implies that all frame data have been processed when the renderer runs this
|
||||
// read-back event. Then, we could make sure this read-back event gets the
|
||||
// latest result.
|
||||
// implies that all frame data have been processed when the renderer runs this event.
|
||||
RunOnRenderThread(Move(event));
|
||||
|
||||
task.Wait();
|
||||
@ -327,14 +399,12 @@ WebRenderAPI::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor,
|
||||
|
||||
void
|
||||
WebRenderAPI::AddExternalImageHandle(ImageKey key,
|
||||
gfx::IntSize aSize,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
uint64_t aHandle)
|
||||
{
|
||||
auto format = SurfaceFormatToWrImageFormat(aFormat).value();
|
||||
wr_api_add_external_image_handle(mWrApi,
|
||||
key,
|
||||
aSize.width, aSize.height, format,
|
||||
&aDescriptor,
|
||||
aHandle);
|
||||
}
|
||||
|
||||
@ -522,17 +592,14 @@ DisplayListBuilder::PushLinearGradient(const WrRect& aBounds,
|
||||
void
|
||||
DisplayListBuilder::PushRadialGradient(const WrRect& aBounds,
|
||||
const WrClipRegion& aClip,
|
||||
const WrPoint& aStartCenter,
|
||||
const WrPoint& aEndCenter,
|
||||
float aStartRadius,
|
||||
float aEndRadius,
|
||||
const WrPoint& aCenter,
|
||||
const WrSize& aRadius,
|
||||
const nsTArray<WrGradientStop>& aStops,
|
||||
wr::GradientExtendMode aExtendMode)
|
||||
{
|
||||
wr_dp_push_radial_gradient(mWrState,
|
||||
aBounds, aClip,
|
||||
aStartCenter, aEndCenter,
|
||||
aStartRadius, aEndRadius,
|
||||
aCenter, aRadius,
|
||||
aStops.Elements(), aStops.Length(),
|
||||
aExtendMode);
|
||||
}
|
||||
|
@ -72,8 +72,7 @@ public:
|
||||
Range<uint8_t> aBytes);
|
||||
|
||||
void AddExternalImageHandle(ImageKey key,
|
||||
gfx::IntSize aSize,
|
||||
gfx::SurfaceFormat aFormat,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
uint64_t aHandle);
|
||||
|
||||
void AddExternalImageBuffer(ImageKey key,
|
||||
@ -95,6 +94,9 @@ public:
|
||||
void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
|
||||
void Readback(gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize);
|
||||
|
||||
void Pause();
|
||||
bool Resume();
|
||||
|
||||
WrIdNamespace GetNamespace();
|
||||
GLint GetMaxTextureSize() const { return mMaxTextureSize; }
|
||||
bool GetUseANGLE() const { return mUseANGLE; }
|
||||
@ -166,10 +168,8 @@ public:
|
||||
|
||||
void PushRadialGradient(const WrRect& aBounds,
|
||||
const WrClipRegion& aClip,
|
||||
const WrPoint& aStartCenter,
|
||||
const WrPoint& aEndCenter,
|
||||
float aStartRadius,
|
||||
float aEndRadius,
|
||||
const WrPoint& aCenter,
|
||||
const WrSize& aRadius,
|
||||
const nsTArray<WrGradientStop>& aStops,
|
||||
wr::GradientExtendMode aExtendMode);
|
||||
|
||||
|
@ -44,6 +44,9 @@ inline Epoch NewEpoch(uint32_t aEpoch) {
|
||||
inline Maybe<WrImageFormat>
|
||||
SurfaceFormatToWrImageFormat(gfx::SurfaceFormat aFormat) {
|
||||
switch (aFormat) {
|
||||
case gfx::SurfaceFormat::R8G8B8X8:
|
||||
// TODO: use RGBA + opaque flag
|
||||
return Some(WrImageFormat::RGBA8);
|
||||
case gfx::SurfaceFormat::B8G8R8X8:
|
||||
// TODO: WebRender will have a BGRA + opaque flag for this but does not
|
||||
// have it yet (cf. issue #732).
|
||||
@ -350,6 +353,27 @@ static inline WrExternalImageId ToWrExternalImageId(uint64_t aID)
|
||||
return id;
|
||||
}
|
||||
|
||||
static inline WrExternalImage RawDataToWrExternalImage(const uint8_t* aBuff,
|
||||
size_t size)
|
||||
{
|
||||
return WrExternalImage {
|
||||
WrExternalImageIdType::RawData,
|
||||
0, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
aBuff, size
|
||||
};
|
||||
}
|
||||
|
||||
static inline WrExternalImage NativeTextureToWrExternalImage(uint8_t aHandle,
|
||||
float u0, float v0,
|
||||
float u1, float v1)
|
||||
{
|
||||
return WrExternalImage {
|
||||
WrExternalImageIdType::NativeTexture,
|
||||
aHandle, u0, v0, u1, v1,
|
||||
nullptr, 0
|
||||
};
|
||||
}
|
||||
|
||||
struct VecU8 {
|
||||
WrVecU8 inner;
|
||||
VecU8() {
|
||||
|
@ -8,8 +8,10 @@ with Files('**'):
|
||||
BUG_COMPONENT = ('Core', 'Graphics: WebRender')
|
||||
|
||||
EXPORTS.mozilla.webrender += [
|
||||
'RenderBufferTextureHost.h',
|
||||
'RendererOGL.h',
|
||||
'RenderTextureHost.h',
|
||||
'RenderTextureHostOGL.h',
|
||||
'RenderThread.h',
|
||||
'webrender_ffi.h',
|
||||
'WebRenderAPI.h',
|
||||
@ -18,12 +20,22 @@ EXPORTS.mozilla.webrender += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Moz2DImageRenderer.cpp',
|
||||
'RenderBufferTextureHost.cpp',
|
||||
'RendererOGL.cpp',
|
||||
'RenderTextureHost.cpp',
|
||||
'RenderTextureHostOGL.cpp',
|
||||
'RenderThread.cpp',
|
||||
'WebRenderAPI.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
EXPORTS.mozilla.webrender += [
|
||||
'RenderMacIOSurfaceTextureHostOGL.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'RenderMacIOSurfaceTextureHostOGL.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user