Merge m-c to inbound, a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2017-04-05 14:56:38 -07:00
commit 0adc72695e
154 changed files with 19961 additions and 20187 deletions

View File

@ -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";
}

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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(),
}));

View File

@ -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]

View File

@ -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.");
}
});

View 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`);
}
});

View 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`);
}
});

View File

@ -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`);
}

View File

@ -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());

View File

@ -50,7 +50,8 @@
.tabs .tab-panel {
height: 100%;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
.tabs .tabs-navigation,

View File

@ -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.

View File

@ -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;
}

View File

@ -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();
}
});

View File

@ -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')

View File

@ -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;
}

View File

@ -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;
},
};

View File

@ -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",

View File

@ -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]

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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]

View File

@ -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();
}

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -13,8 +13,6 @@ DevToolsModules(
'css-properties.js',
'csscoverage.js',
'device.js',
'director-manager.js',
'director-registry.js',
'emulation.js',
'eventlooplag.js',
'framerate.js',

View File

@ -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;

View File

@ -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;

View File

@ -14,8 +14,6 @@ DevToolsModules(
'css-properties.js',
'csscoverage.js',
'device.js',
'director-manager.js',
'director-registry.js',
'emulation.js',
'environment.js',
'eventlooplag.js',

View File

@ -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);

View 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>

View 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>

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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()
{

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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"

View File

@ -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);
}

View File

@ -8,3 +8,4 @@ varying vec3 vPos;
flat varying vec4 vLocalRect;
flat varying vec4 vClipRect;
flat varying vec4 vClipRadius;
flat varying float vClipMode;

View File

@ -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,

View File

@ -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;
}

View File

@ -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)));
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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,
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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;

View File

@ -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,
}
}
}

View File

@ -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;

View File

@ -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(&region.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(&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,
};
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(&region.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);
}
}
}

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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 |

View File

@ -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 = {

View File

@ -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.

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -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 {

View File

@ -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 |

View File

@ -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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View 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

View 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

View File

@ -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)
{

View File

@ -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);

View File

@ -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)
{

View File

@ -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();

View File

@ -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);
}

View File

@ -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);

View File

@ -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() {

View File

@ -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