Merge autoland to m-c, a=merge

MozReview-Commit-ID: 6ChkHqkFGsA
This commit is contained in:
Phil Ringnalda 2016-11-17 19:14:15 -08:00
commit da94b3af3f
178 changed files with 2976 additions and 2921 deletions

View File

@ -54,11 +54,7 @@
<li>&aboutPrivateBrowsing.info.downloads;</li>
</ul>
</div>
<p>
&aboutPrivateBrowsing.note.before;
<strong>&aboutPrivateBrowsing.note.emphasize;</strong>
&aboutPrivateBrowsing.note.after;
</p>
<p>&aboutPrivateBrowsing.note.before;<strong>&aboutPrivateBrowsing.note.emphasize;</strong>&aboutPrivateBrowsing.note.after;</p>
</section>
<h2 id="tpSubHeader" class="about-subheader">
@ -75,9 +71,7 @@
</section>
<section class="section-main">
<p class="about-info">&aboutPrivateBrowsing.learnMore2;
<a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore2.title;</a>.
</p>
<p class="about-info">&aboutPrivateBrowsing.learnMore3.before;<a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore3.title;</a>&aboutPrivateBrowsing.learnMore3.after;</p>
</section>
</div>

View File

@ -4,7 +4,11 @@ ac_add_options --disable-install-strip
ac_add_options --enable-verify-mar
ac_add_options --enable-profiling
ac_add_options --enable-instruments
ac_add_options --enable-dtrace
# Cross-universal builds fail when dtrace is enabled
if test `uname -s` != Linux; then
ac_add_options --enable-dtrace
fi
if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
ac_add_options --with-macbundlename-prefix=Firefox

View File

@ -23,8 +23,9 @@
<!ENTITY aboutPrivateBrowsing.note.before "Private Browsing ">
<!ENTITY aboutPrivateBrowsing.note.emphasize "doesnt make you anonymous">
<!ENTITY aboutPrivateBrowsing.note.after " on the Internet. Your employer or Internet service provider can still know what page you visit.">
<!ENTITY aboutPrivateBrowsing.learnMore2 "Learn more about">
<!ENTITY aboutPrivateBrowsing.learnMore2.title "Private Browsing">
<!ENTITY aboutPrivateBrowsing.learnMore3.before "Learn more about ">
<!ENTITY aboutPrivateBrowsing.learnMore3.title "Private Browsing">
<!ENTITY aboutPrivateBrowsing.learnMore3.after ".">
<!ENTITY trackingProtection.title "Tracking Protection">
<!ENTITY trackingProtection.description2 "Some websites use trackers that can monitor your activity across the Internet. With Tracking Protection Firefox will block many trackers that can collect information about your browsing behavior.">

View File

@ -132,17 +132,13 @@ PluginContent.prototype = {
switch (aTopic) {
case "decoder-doctor-notification":
let data = JSON.parse(aData);
if (this.haveShownNotification &&
let type = data.type.toLowerCase();
if (type == "cannot-play" &&
this.haveShownNotification &&
aSubject.top.document == this.content.document &&
data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
let principal = this.content.document.nodePrincipal;
let location = this.content.document.location.href;
this.global.content.pluginRequiresReload = true;
this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification",
{ plugins: [...this.pluginData.values()],
showNow: true,
location: location,
}, null, principal);
this.updateNotificationUI(this.content.document);
}
}
},
@ -235,6 +231,12 @@ PluginContent.prototype = {
};
},
/**
* _getPluginInfoForTag is called when iterating the plugins for a document,
* and what we get from nsIDOMWindowUtils is an nsIPluginTag, and not an
* nsIObjectLoadingContent. This only should happen if the plugin is
* click-to-play (see bug 1186948).
*/
_getPluginInfoForTag: function(pluginTag, tagMimetype) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
@ -269,7 +271,11 @@ PluginContent.prototype = {
pluginName: pluginName,
pluginTag: pluginTag,
permissionString: permissionString,
fallbackType: null,
// Since we should only have entered _getPluginInfoForTag when
// examining a click-to-play plugin, we can safely hard-code
// this fallback type, since we don't actually have an
// nsIObjectLoadingContent to check.
fallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
blocklistState: blocklistState,
};
},

View File

@ -83,6 +83,7 @@ AC_CHECK_PROGS(RANLIB, "${TOOLCHAIN_PREFIX}ranlib", :)
AC_CHECK_PROGS(AR, "${TOOLCHAIN_PREFIX}ar", :)
AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :)
AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :)
AC_CHECK_PROGS(LIPO, "${TOOLCHAIN_PREFIX}lipo", :)
AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :)
AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :)
AC_CHECK_PROGS(OTOOL, "${TOOLCHAIN_PREFIX}otool", :)

View File

@ -4,13 +4,16 @@
mk_add_options MOZ_UNIFY_BDATE=1
DARWIN_VERSION=`uname -r`
DARWIN_VERSION=10
ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
ac_add_app_options i386 --with-unify-dist=../x86_64/dist
ac_add_app_options x86_64 --with-unify-dist=../i386/dist
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
if ! test `uname -s` = Linux; then
# Cross-universal builds already do the equivalent of this by setting -isysroot directly
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
fi
. $topsrcdir/build/macosx/mozconfig.common
@ -35,12 +38,12 @@ if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then
CXX="$CXX -arch $TARGET_CPU"
# These must be set for cross builds, and don't hurt straight builds.
RANLIB=ranlib
AR=ar
RANLIB="${TOOLCHAIN_PREFIX}ranlib"
AR="${TOOLCHAIN_PREFIX}ar"
AS=$CC
LD=ld
STRIP="strip"
OTOOL="otool"
OTOOL="${TOOLCHAIN_PREFIX}otool"
# Each per-CPU build should be entirely oblivious to the fact that a
# universal binary will be produced. The exception is packager.mk, which

View File

@ -726,13 +726,14 @@ gDevTools.getToolDefinitionArray()
// and the new ones.
gDevTools.on("tool-registered", function (ev, toolId) {
let toolDefinition = gDevTools._tools.get(toolId);
gDevToolsBrowser._addToolToWindows(toolDefinition);
// If the tool has been registered globally, add to all the
// available windows.
if (toolDefinition) {
gDevToolsBrowser._addToolToWindows(toolDefinition);
}
});
gDevTools.on("tool-unregistered", function (ev, toolId) {
if (typeof toolId != "string") {
toolId = toolId.id;
}
gDevToolsBrowser._removeToolFromWindows(toolId);
});

View File

@ -140,12 +140,16 @@ DevTools.prototype = {
tool = this._tools.get(tool);
}
else {
let {Deprecated} = Cu.import("resource://gre/modules/Deprecated.jsm", {});
Deprecated.warning("Deprecation WARNING: gDevTools.unregisterTool(tool) is deprecated. " +
"You should unregister a tool using its toolId: " +
"gDevTools.unregisterTool(toolId).");
toolId = tool.id;
}
this._tools.delete(toolId);
if (!isQuitApplication) {
this.emit("tool-unregistered", tool);
this.emit("tool-unregistered", toolId);
}
},

View File

@ -78,6 +78,7 @@ skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
[browser_toolbox_toggle.js]
[browser_toolbox_tool_ready.js]
[browser_toolbox_tool_remote_reopen.js]
[browser_toolbox_tools_per_toolbox_registration.js]
[browser_toolbox_transport_events.js]
[browser_toolbox_view_source_01.js]
[browser_toolbox_view_source_02.js]

View File

@ -71,9 +71,8 @@ function testUnregister()
gDevTools.unregisterTool("test-tool");
}
function toolUnregistered(event, toolDefinition)
function toolUnregistered(event, toolId)
{
let toolId = toolDefinition.id;
is(toolId, "test-tool", "tool-unregistered event handler sent tool id");
ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map");

View File

@ -21,6 +21,7 @@ add_task(function* () {
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target);
doc = toolbox.doc;
yield registerNewPerToolboxTool();
yield testSelectTool();
yield testOptionsShortcut();
yield testOptions();
@ -46,6 +47,31 @@ function registerNewTool() {
"The tool is registered");
}
function registerNewPerToolboxTool() {
let toolDefinition = {
id: "test-pertoolbox-tool",
isTargetSupported: () => true,
visibilityswitch: "devtools.test-pertoolbox-tool.enabled",
url: "about:blank",
label: "perToolboxSomeLabel"
};
ok(gDevTools, "gDevTools exists");
ok(!gDevTools.getToolDefinitionMap().has("test-pertoolbox-tool"),
"The per-toolbox tool is not registered globally");
ok(toolbox, "toolbox exists");
ok(!toolbox.hasAdditionalTool("test-pertoolbox-tool"),
"The per-toolbox tool is not yet registered to the toolbox");
toolbox.addAdditionalTool(toolDefinition);
ok(!gDevTools.getToolDefinitionMap().has("test-pertoolbox-tool"),
"The per-toolbox tool is not registered globally");
ok(toolbox.hasAdditionalTool("test-pertoolbox-tool"),
"The per-toolbox tool has been registered to the toolbox");
}
function* testSelectTool() {
info("Checking to make sure that the options panel can be selected.");
@ -168,9 +194,13 @@ function* testToggleTools() {
"#additional-tools-box input[type=checkbox]:not([data-unsupported])");
let enabledTools = [...toolNodes].filter(node => node.checked);
let toggleableTools = gDevTools.getDefaultTools().filter(tool => {
return tool.visibilityswitch;
}).concat(gDevTools.getAdditionalTools());
let toggleableTools = gDevTools.getDefaultTools()
.filter(tool => {
return tool.visibilityswitch;
})
.concat(gDevTools.getAdditionalTools())
.concat(toolbox.getAdditionalTools());
for (let node of toolNodes) {
let id = node.getAttribute("id");
@ -235,7 +265,7 @@ function* toggleTool(node) {
}
function checkUnregistered(toolId, deferred, event, data) {
if (data.id == toolId) {
if (data == toolId) {
ok(true, "Correct tool removed");
// checking tab on the toolbox
ok(!doc.getElementById("toolbox-tab-" + toolId),

View File

@ -0,0 +1,139 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/ */
const TEST_URL = `data:text/html,<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
test for registering and unregistering tools to a specific toolbox
</body>
</html>`;
const TOOL_ID = "test-toolbox-tool";
var toolbox;
var target;
function test() {
addTab(TEST_URL).then(tab => {
target = TargetFactory.forTab(tab);
gDevTools.showToolbox(target)
.then(toolboxRegister)
.then(testToolRegistered);
});
}
var resolveToolInstanceBuild;
var waitForToolInstanceBuild = new Promise((resolve) => {
resolveToolInstanceBuild = resolve;
});
var resolveToolInstanceDestroyed;
var waitForToolInstanceDestroyed = new Promise((resolve) => {
resolveToolInstanceDestroyed = resolve;
});
function toolboxRegister(aToolbox) {
toolbox = aToolbox;
var resolveToolInstanceBuild;
waitForToolInstanceBuild = new Promise((resolve) => {
resolveToolInstanceBuild = resolve;
});
info("add per-toolbox tool in the opened toolbox.");
toolbox.addAdditionalTool({
id: TOOL_ID,
label: "per-toolbox Test Tool",
inMenu: true,
isTargetSupported: () => true,
build: function () {
info("per-toolbox tool has been built.");
resolveToolInstanceBuild();
return {
destroy: () => {
info("per-toolbox tool has been destroyed.");
resolveToolInstanceDestroyed();
},
};
},
key: "t"
});
}
function testToolRegistered() {
ok(!gDevTools.getToolDefinitionMap().has(TOOL_ID), "per-toolbox tool is not registered globally");
ok(toolbox.hasAdditionalTool(TOOL_ID),
"per-toolbox tool registered to the specific toolbox");
// Test that the tool appeared in the UI.
let doc = toolbox.doc;
let tab = doc.getElementById("toolbox-tab-" + TOOL_ID);
ok(tab, "new tool's tab exists in toolbox UI");
let panel = doc.getElementById("toolbox-panel-" + TOOL_ID);
ok(panel, "new tool's panel exists in toolbox UI");
for (let win of getAllBrowserWindows()) {
let key = win.document.getElementById("key_" + TOOL_ID);
if (win.document == doc) {
continue;
}
ok(!key, "key for new tool should not exists in the other browser windows");
let menuitem = win.document.getElementById("menuitem_" + TOOL_ID);
ok(!menuitem, "menu item should not exists in the other browser window");
}
// Test that the tool is built once selected and then test its unregistering.
info("select per-toolbox tool in the opened toolbox.");
gDevTools.showToolbox(target, TOOL_ID)
.then(waitForToolInstanceBuild)
.then(testUnregister);
}
function getAllBrowserWindows() {
let wins = [];
let enumerator = Services.wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
wins.push(enumerator.getNext());
}
return wins;
}
function testUnregister() {
info("remove per-toolbox tool in the opened toolbox.");
toolbox.removeAdditionalTool(TOOL_ID);
Promise.all([
waitForToolInstanceDestroyed
]).then(toolboxToolUnregistered);
}
function toolboxToolUnregistered() {
ok(!toolbox.hasAdditionalTool(TOOL_ID),
"per-toolbox tool unregistered from the specific toolbox");
// test that it disappeared from the UI
let doc = toolbox.doc;
let tab = doc.getElementById("toolbox-tab-" + TOOL_ID);
ok(!tab, "tool's tab was removed from the toolbox UI");
let panel = doc.getElementById("toolbox-panel-" + TOOL_ID);
ok(!panel, "tool's panel was removed from toolbox UI");
cleanup();
}
function cleanup() {
toolbox.destroy();
toolbox = null;
gBrowser.removeCurrentTab();
finish();
}

View File

@ -186,15 +186,15 @@ OptionsPanel.prototype = {
"tools-not-supported-label");
let atleastOneToolNotSupported = false;
const toolbox = this.toolbox;
// Signal tool registering/unregistering globally (for the tools registered
// globally) and per toolbox (for the tools registered to a single toolbox).
let onCheckboxClick = function (id) {
let toolDefinition = gDevTools._tools.get(id);
let toolDefinition = gDevTools._tools.get(id) || toolbox.getToolDefinition(id);
// Set the kill switch pref boolean to true
Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
if (this.checked) {
gDevTools.emit("tool-registered", id);
} else {
gDevTools.emit("tool-unregistered", toolDefinition);
}
gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", id);
};
let createToolCheckbox = tool => {
@ -243,6 +243,13 @@ OptionsPanel.prototype = {
additionalToolsBox.appendChild(createToolCheckbox(tool));
}
// Populating the additional toolbox-specific tools list that came
// from WebExtension add-ons.
for (let tool of this.toolbox.getAdditionalTools()) {
atleastOneAddon = true;
additionalToolsBox.appendChild(createToolCheckbox(tool));
}
if (!atleastOneAddon) {
additionalToolsBox.style.display = "none";
}

View File

@ -895,6 +895,7 @@ Toolbox.prototype = {
* Add tabs to the toolbox UI for registered tools
*/
_buildTabs: function () {
// Build tabs for global registered tools.
for (let definition of gDevTools.getToolDefinitionArray()) {
this._buildTabForTool(definition);
}
@ -1252,6 +1253,81 @@ Toolbox.prototype = {
this._addKeysToWindow();
},
/**
* Lazily created map of the additional tools registered to this toolbox.
*
* @returns {Map<string, object>}
* a map of the tools definitions registered to this
* particular toolbox (the key is the toolId string, the value
* is the tool definition plain javascript object).
*/
get additionalToolDefinitions() {
if (!this._additionalToolDefinitions) {
this._additionalToolDefinitions = new Map();
}
return this._additionalToolDefinitions;
},
/**
* Retrieve the array of the additional tools registered to this toolbox.
*
* @return {Array<object>}
* the array of additional tool definitions registered on this toolbox.
*/
getAdditionalTools() {
return Array.from(this.additionalToolDefinitions.values());
},
/**
* Test the existence of a additional tools registered to this toolbox by tool id.
*
* @param {string} toolId
* the id of the tool to test for existence.
*
* @return {boolean}
*
*/
hasAdditionalTool(toolId) {
return this.additionalToolDefinitions.has(toolId);
},
/**
* Register and load an additional tool on this particular toolbox.
*
* @param {object} definition
* the additional tool definition to register and add to this toolbox.
*/
addAdditionalTool(definition) {
if (!definition.id) {
throw new Error("Tool definition id is missing");
}
if (this.isToolRegistered(definition.id)) {
throw new Error("Tool definition already registered: " +
definition.id);
}
this.additionalToolDefinitions.set(definition.id, definition);
this._buildTabForTool(definition);
},
/**
* Unregister and unload an additional tool from this particular toolbox.
*
* @param {string} toolId
* the id of the additional tool to unregister and remove.
*/
removeAdditionalTool(toolId) {
if (!this.hasAdditionalTool(toolId)) {
throw new Error("Tool definition not registered to this toolbox: " +
toolId);
}
this.unloadTool(toolId);
this.additionalToolDefinitions.delete(toolId);
},
/**
* Ensure the tool with the given id is loaded.
*
@ -1280,7 +1356,9 @@ Toolbox.prototype = {
return deferred.promise;
}
let definition = gDevTools.getToolDefinition(id);
// Retrieve the tool definition (from the global or the per-toolbox tool maps)
let definition = this.getToolDefinition(id);
if (!definition) {
deferred.reject(new Error("no such tool id " + id));
return deferred.promise;
@ -1907,41 +1985,47 @@ Toolbox.prototype = {
},
/**
* Return if the tool is available as a tab (i.e. if it's checked
* in the options panel). This is different from Toolbox.getPanel -
* a tool could be registered but not yet opened in which case
* isToolRegistered would return true but getPanel would return false.
* Test the availability of a tool (both globally registered tools and
* additional tools registered to this toolbox) by tool id.
*
* @param {string} toolId
* Id of the tool definition to search in the per-toolbox or globally
* registered tools.
*
* @returns {bool}
* Returns true if the tool is registered globally or on this toolbox.
*/
isToolRegistered: function (toolId) {
return gDevTools.getToolDefinitionMap().has(toolId);
return !!this.getToolDefinition(toolId);
},
/**
* Handler for the tool-registered event.
* @param {string} event
* Name of the event ("tool-registered")
* Return the tool definition registered globally or additional tools registered
* to this toolbox.
*
* @param {string} toolId
* Id of the tool that was registered
* Id of the tool definition to retrieve for the per-toolbox and globally
* registered tools.
*
* @returns {object}
* The plain javascript object that represents the requested tool definition.
*/
_toolRegistered: function (event, toolId) {
let tool = gDevTools.getToolDefinition(toolId);
this._buildTabForTool(tool);
// Emit the event so tools can listen to it from the toolbox level
// instead of gDevTools
this.emit("tool-registered", toolId);
getToolDefinition: function (toolId) {
return gDevTools.getToolDefinition(toolId) ||
this.additionalToolDefinitions.get(toolId);
},
/**
* Handler for the tool-unregistered event.
* @param {string} event
* Name of the event ("tool-unregistered")
* @param {string|object} toolId
* Definition or id of the tool that was unregistered. Passing the
* tool id should be avoided as it is a temporary measure.
* Internal helper that removes a loaded tool from the toolbox,
* it removes a loaded tool panel and tab from the toolbox without removing
* its definition, so that it can still be listed in options and re-added later.
*
* @param {string} toolId
* Id of the tool to be removed.
*/
_toolUnregistered: function (event, toolId) {
unloadTool: function (toolId) {
if (typeof toolId != "string") {
toolId = toolId.id;
throw new Error("Unexpected non-string toolId received.");
}
if (this._toolPanels.has(toolId)) {
@ -1980,6 +2064,38 @@ Toolbox.prototype = {
key.parentNode.removeChild(key);
}
}
},
/**
* Handler for the tool-registered event.
* @param {string} event
* Name of the event ("tool-registered")
* @param {string} toolId
* Id of the tool that was registered
*/
_toolRegistered: function (event, toolId) {
let tool = this.getToolDefinition(toolId);
if (!tool) {
// Ignore if the tool is not found, when a per-toolbox tool
// has been toggle in the toolbox options view, every toolbox will receive
// the toolbox-register and toolbox-unregister events.
return;
}
this._buildTabForTool(tool);
// Emit the event so tools can listen to it from the toolbox level
// instead of gDevTools.
this.emit("tool-registered", toolId);
},
/**
* Handler for the tool-unregistered event.
* @param {string} event
* Name of the event ("tool-unregistered")
* @param {string} toolId
* id of the tool that was unregistered
*/
_toolUnregistered: function (event, toolId) {
this.unloadTool(toolId);
// Emit the event so tools can listen to it from the toolbox level
// instead of gDevTools
this.emit("tool-unregistered", toolId);

View File

@ -4,45 +4,47 @@
"use strict";
// Test for as-authored styles.
// Test for as-authored color styles.
function* createTestContent(style) {
let html = `<style type="text/css">
${style}
</style>
<div id="testid" class="testclass">Styled Node</div>`;
let tab = yield addTab("data:text/html;charset=utf-8," +
encodeURIComponent(html));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
return {view, tab};
}
/**
* Array of test color objects:
* {String} name: name of the used & expected color format.
* {String} id: id of the element that will be created to test this color.
* {String} color: initial value of the color property applied to the test element.
* {String} result: expected value of the color property after edition.
*/
const colors = [
{name: "hex", id: "test1", color: "#f0c", result: "#0f0"},
{name: "rgb", id: "test2", color: "rgb(0,128,250)", result: "rgb(0, 255, 0)"},
// Test case preservation.
{name: "hex", id: "test3", color: "#F0C", result: "#0F0"},
];
add_task(function* () {
let colors = [
{name: "hex", text: "#f0c", result: "#0f0"},
{name: "rgb", text: "rgb(0,128,250)", result: "rgb(0, 255, 0)"},
// Test case preservation.
{name: "hex", text: "#F0C", result: "#0F0"},
];
Services.prefs.setCharPref("devtools.defaultColorUnit", "authored");
for (let color of colors) {
let {view, tab} = yield createTestContent("#testid {" +
" color: " + color.text + ";" +
"} ");
let html = "";
for (let {color, id} of colors) {
html += `<div id="${id}" style="color: ${color}">Styled Node</div>`;
}
let tab = yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(html));
let {inspector, view} = yield openRuleView();
for (let color of colors) {
let cPicker = view.tooltips.colorPicker;
let swatch = getRuleViewProperty(view, "#testid", "color").valueSpan
let selector = "#" + color.id;
yield selectNode(selector, inspector);
let swatch = getRuleViewProperty(view, "element", "color").valueSpan
.querySelector(".ruleview-colorswatch");
let onColorPickerReady = cPicker.once("ready");
swatch.click();
yield onColorPickerReady;
yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], {
selector: "#testid",
selector,
name: "color",
value: "rgb(0, 255, 0)"
});
@ -55,11 +57,11 @@ add_task(function* () {
yield onHidden;
yield onRuleViewChanged;
is(getRuleViewPropertyValue(view, "#testid", "color"), color.result,
is(getRuleViewPropertyValue(view, "element", "color"), color.result,
"changing the color preserved the unit for " + color.name);
let target = TargetFactory.forTab(tab);
yield gDevTools.closeToolbox(target);
gBrowser.removeCurrentTab();
}
let target = TargetFactory.forTab(tab);
yield gDevTools.closeToolbox(target);
gBrowser.removeCurrentTab();
});

View File

@ -4,11 +4,13 @@
/* globals window, dumpn, gNetwork, $, EVENTS, NetMonitorView */
"use strict";
const {Task} = require("devtools/shared/task");
const {writeHeaderText, getKeyWithEvent} = require("./request-utils");
loader.lazyRequireGetter(this, "NetworkHelper",
"devtools/shared/webconsole/network-helper");
const { Task } = require("devtools/shared/task");
const {
writeHeaderText,
getKeyWithEvent,
getUrlQuery,
parseQueryString,
} = require("./request-utils");
/**
* Functions handling the custom request view.
@ -112,8 +114,7 @@ CustomRequestView.prototype = {
* The URL to extract query string from.
*/
updateCustomQuery: function (url) {
let paramsArray = NetworkHelper.parseQueryString(
NetworkHelper.nsIURL(url).query);
const paramsArray = parseQueryString(getUrlQuery(url));
if (!paramsArray) {
$("#custom-query").hidden = true;
@ -135,7 +136,7 @@ CustomRequestView.prototype = {
let queryString = writeQueryString(params);
let url = $("#custom-url-value").value;
let oldQuery = NetworkHelper.nsIURL(url).query;
let oldQuery = getUrlQuery(url);
let path = url.replace(oldQuery, queryString);
$("#custom-url-value").value = path;

View File

@ -8,9 +8,11 @@ const { LocalizationHelper } = require("devtools/shared/l10n");
const Services = require("Services");
const appInfo = Services.appinfo;
const { CurlUtils } = require("devtools/client/shared/curl");
const { getFormDataSections } = require("devtools/client/netmonitor/request-utils");
loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper");
const {
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("devtools/client/netmonitor/request-utils");
loader.lazyGetter(this, "L10N", () => {
return new LocalizationHelper("devtools/client/locales/har.properties");
@ -170,8 +172,7 @@ HarBuilder.prototype = {
request.headers = this.appendHeadersPostData(request.headers, file);
request.cookies = this.buildCookies(file.requestCookies);
request.queryString = NetworkHelper.parseQueryString(
NetworkHelper.nsIURL(file.url).query) || [];
request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
request.postData = this.buildPostData(file);
@ -280,7 +281,7 @@ HarBuilder.prototype = {
this._options.getString
).then(formDataSections => {
formDataSections.forEach(section => {
let paramsArray = NetworkHelper.parseQueryString(section);
let paramsArray = parseQueryString(section);
if (paramsArray) {
postData.params = [...postData.params, ...paramsArray];
}

View File

@ -56,14 +56,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Chart",
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
Object.defineProperty(this, "NetworkHelper", {
get: function () {
return require("devtools/shared/webconsole/network-helper");
},
configurable: true,
enumerable: true
});
/**
* Object defining the network monitor controller components.
*/

View File

@ -8,10 +8,6 @@
/* exported $, $all */
"use strict";
XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function () {
return require("devtools/shared/webconsole/network-helper");
});
/* eslint-disable mozilla/reject-some-requires */
const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
/* eslint-disable mozilla/reject-some-requires */
@ -20,9 +16,15 @@ const {ToolSidebar} = require("devtools/client/framework/sidebar");
const {testing: isTesting} = require("devtools/shared/flags");
const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers");
const {Filters} = require("./filter-predicates");
const {getFormDataSections,
formDataURI,
getUriHostPort} = require("./request-utils");
const {
formDataURI,
decodeUnicodeUrl,
getFormDataSections,
getUrlBaseName,
getUrlQuery,
getUrlHost,
parseQueryString,
} = require("./request-utils");
const {L10N} = require("./l10n");
const {RequestsMenuView} = require("./requests-menu-view");
const {CustomRequestView} = require("./custom-request-view");
@ -552,7 +554,7 @@ NetworkDetailsView.prototype = {
*/
_setSummary: function (data) {
if (data.url) {
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(data.url));
let unicodeUrl = decodeUnicodeUrl(data.url);
$("#headers-summary-url-value").setAttribute("value", unicodeUrl);
$("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
$("#headers-summary-url").removeAttribute("hidden");
@ -743,7 +745,7 @@ NetworkDetailsView.prototype = {
* The request's url.
*/
_setRequestGetParams: function (url) {
let query = NetworkHelper.nsIURL(url).query;
let query = getUrlQuery(url);
if (query) {
this._addParams(this._paramsQueryString, query);
}
@ -825,7 +827,7 @@ NetworkDetailsView.prototype = {
* A query string of params (e.g. "?foo=bar&baz=42").
*/
_addParams: function (name, queryString) {
let paramsArray = NetworkHelper.parseQueryString(queryString);
let paramsArray = parseQueryString(queryString);
if (!paramsArray) {
return;
}
@ -918,7 +920,7 @@ NetworkDetailsView.prototype = {
// Immediately display additional information about the image:
// file name, mime type and encoding.
$("#response-content-image-name-value").setAttribute("value",
NetworkHelper.nsIURL(url).fileName);
getUrlBaseName(url));
$("#response-content-image-mime-value").setAttribute("value", mimeType);
// Wait for the image to load in order to display the width and height.
@ -1126,7 +1128,7 @@ NetworkDetailsView.prototype = {
setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
// Host header
let domain = getUriHostPort(url);
let domain = getUrlHost(url);
let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader",
domain);
setValue("#security-info-host-header", hostHeader);

View File

@ -13,7 +13,12 @@ const { gDevTools } = require("devtools/client/framework/devtools");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const { L10N } = require("./l10n");
const { formDataURI, getFormDataSections } = require("./request-utils");
const {
formDataURI,
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("./request-utils");
loader.lazyRequireGetter(this, "HarExporter",
"devtools/client/netmonitor/har/har-exporter", true);
@ -21,9 +26,6 @@ loader.lazyRequireGetter(this, "HarExporter",
loader.lazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
loader.lazyRequireGetter(this, "NetworkHelper",
"devtools/shared/webconsole/network-helper");
function RequestListContextMenu() {}
RequestListContextMenu.prototype = {
@ -56,8 +58,7 @@ RequestListContextMenu.prototype = {
id: "request-menu-context-copy-url-params",
label: L10N.getStr("netmonitor.context.copyUrlParams"),
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
visible: !!(selectedItem &&
NetworkHelper.nsIURL(selectedItem.attachment.url).query),
visible: !!(selectedItem && getUrlQuery(selectedItem.attachment.url)),
click: () => this.copyUrlParams(),
}));
@ -203,7 +204,7 @@ RequestListContextMenu.prototype = {
*/
copyUrlParams() {
let { url } = this.selectedItem.attachment;
let params = NetworkHelper.nsIURL(url).query.split("&");
let params = getUrlQuery(url).split("&");
let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
clipboardHelper.copyString(string);
},
@ -224,7 +225,7 @@ RequestListContextMenu.prototype = {
let params = [];
formDataSections.forEach(section => {
let paramsArray = NetworkHelper.parseQueryString(section);
let paramsArray = parseQueryString(section);
if (paramsArray) {
params = [...params, ...paramsArray];
}

View File

@ -1,26 +1,24 @@
"use strict";
/* eslint-disable mozilla/reject-some-requires */
const { Ci } = require("chrome");
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task");
const NetworkHelper = require("devtools/shared/webconsole/network-helper");
/**
* Helper method to get a wrapped function which can be bound to as
* an event listener directly and is executed only when data-key is
* present in event.target.
*
* @param function callback
* Function to execute execute when data-key
* is present in event.target.
* @param bool onlySpaceOrReturn
* Flag to indicate if callback should only be called
when the space or return button is pressed
* @return function
* Wrapped function with the target data-key as the first argument
* and the event as the second argument.
* @param {function} callback - function to execute execute when data-key
* is present in event.target.
* @param {bool} onlySpaceOrReturn - flag to indicate if callback should only
* be called when the space or return button
* is pressed
* @return {function} wrapped function with the target data-key as the first argument
* and the event as the second argument.
*/
exports.getKeyWithEvent = function (callback, onlySpaceOrReturn) {
function getKeyWithEvent(callback, onlySpaceOrReturn) {
return function (event) {
let key = event.target.getAttribute("data-key");
let filterKeyboardEvent = !onlySpaceOrReturn ||
@ -31,24 +29,19 @@ exports.getKeyWithEvent = function (callback, onlySpaceOrReturn) {
callback.call(null, key);
}
};
};
}
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param object headers
* The "requestHeaders".
* @param object uploadHeaders
* The "requestHeadersFromUploadStream".
* @param object postData
* The "requestPostData".
* @param object getString
Callback to retrieve a string from a LongStringGrip.
* @return array
* A promise that is resolved with the extracted form data.
* @param {object} headers - the "requestHeaders".
* @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
* @param {object} postData - the "requestPostData".
* @param {function} getString - callback to retrieve a string from a LongStringGrip.
* @return {array} a promise list that is resolved with the extracted form data.
*/
exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
const getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
getString) {
let formDataSections = [];
@ -83,73 +76,132 @@ exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, post
/**
* Form a data: URI given a mime type, encoding, and some text.
*
* @param {String} mimeType the mime type
* @param {String} encoding the encoding to use; if not set, the
* text will be base64-encoded.
* @param {String} text the text of the URI.
* @return {String} a data: URI
* @param {string} mimeType - mime type
* @param {string} encoding - encoding to use; if not set, the
* text will be base64-encoded.
* @param {string} text - text of the URI.
* @return {string} a data URI
*/
exports.formDataURI = function (mimeType, encoding, text) {
function formDataURI(mimeType, encoding, text) {
if (!encoding) {
encoding = "base64";
text = btoa(text);
}
return "data:" + mimeType + ";" + encoding + "," + text;
};
}
/**
* Write out a list of headers into a chunk of text
*
* @param array headers
* Array of headers info {name, value}
* @return string text
* List of headers in text format
* @param {array} headers - array of headers info { name, value }
* @return {string} list of headers in text format
*/
exports.writeHeaderText = function (headers) {
function writeHeaderText(headers) {
return headers.map(({name, value}) => name + ": " + value).join("\n");
};
}
/**
* Convert a string into unicode if string is valid.
* If there is a malformed URI sequence, it returns input string.
*
* @param {string} url - a string
* @return {string} unicode string
*/
function decodeUnicodeUrl(string) {
try {
return decodeURIComponent(string);
} catch (err) {
// Ignore error and return input string directly.
}
return string;
}
/**
* Helper for getting an abbreviated string for a mime type.
*
* @param string mimeType
* @return string
* @param {string} mimeType - mime type
* @return {string} abbreviated mime type
*/
exports.getAbbreviatedMimeType = function (mimeType) {
function getAbbreviatedMimeType(mimeType) {
if (!mimeType) {
return "";
}
return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
};
}
/**
* Helpers for getting details about an nsIURL.
* Helpers for getting the last portion of a url.
* For example helper returns "basename" from http://domain.com/path/basename
* If basename portion is empty, it returns the url pathname.
*
* @param nsIURL | string url
* @return string
* @param {string} url - url string
* @return {string} unicode basename of a url
*/
exports.getUriNameWithQuery = function (url) {
if (!(url instanceof Ci.nsIURL)) {
url = NetworkHelper.nsIURL(url);
function getUrlBaseName(url) {
const pathname = (new URL(url)).pathname;
return decodeUnicodeUrl(
pathname.replace(/\S*\//, "") || pathname || "/");
}
/**
* Helpers for getting the query portion of a url.
*
* @param {string} url - url string
* @return {string} unicode query of a url
*/
function getUrlQuery(url) {
return decodeUnicodeUrl((new URL(url)).search.replace(/^\?/, ""));
}
/**
* Helpers for getting unicode name and query portions of a url.
*
* @param {string} url - url string
* @return {string} unicode basename and query portions of a url
*/
function getUrlBaseNameWithQuery(url) {
return getUrlBaseName(url) + decodeUnicodeUrl((new URL(url)).search);
}
/**
* Helpers for getting unicode hostname portion of an URL.
*
* @param {string} url - url string
* @return {string} unicode hostname of a url
*/
function getUrlHostName(url) {
return decodeUnicodeUrl((new URL(url)).hostname);
}
/**
* Helpers for getting unicode host portion of an URL.
*
* @param {string} url - url string
* @return {string} unicode host of a url
*/
function getUrlHost(url) {
return decodeUnicodeUrl((new URL(url)).host);
}
/**
* Parse a url's query string into its components
*
* @param {string} query - query string of a url portion
* @return {array} array of query params { name, value }
*/
function parseQueryString(query) {
if (!query) {
return null;
}
let name = NetworkHelper.convertToUnicode(
unescape(url.fileName || url.filePath || "/"));
let query = NetworkHelper.convertToUnicode(unescape(url.query));
return name + (query ? "?" + query : "");
};
exports.getUriHostPort = function (url) {
if (!(url instanceof Ci.nsIURL)) {
url = NetworkHelper.nsIURL(url);
}
return NetworkHelper.convertToUnicode(unescape(url.hostPort));
};
exports.getUriHost = function (url) {
return exports.getUriHostPort(url).replace(/:\d+$/, "");
};
return query.replace(/^[?&]/, "").split("&").map(e => {
let param = e.split("=");
return {
name: param[0] ? decodeUnicodeUrl(param[0]) : "",
value: param[1] ? decodeUnicodeUrl(param[1]) : "",
};
});
}
/**
* Convert a nsIContentPolicy constant to a display string
@ -180,6 +232,22 @@ const LOAD_CAUSE_STRINGS = {
[Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest"
};
exports.loadCauseString = function (causeType) {
function loadCauseString(causeType) {
return LOAD_CAUSE_STRINGS[causeType] || "unknown";
}
module.exports = {
getKeyWithEvent,
getFormDataSections,
formDataURI,
writeHeaderText,
decodeUnicodeUrl,
getAbbreviatedMimeType,
getUrlBaseName,
getUrlQuery,
getUrlBaseNameWithQuery,
getUrlHostName,
getUrlHost,
parseQueryString,
loadCauseString,
};

View File

@ -24,18 +24,16 @@ const {Sorters} = require("./sort-predicates");
const {L10N, WEBCONSOLE_L10N} = require("./l10n");
const {formDataURI,
writeHeaderText,
decodeUnicodeUrl,
getKeyWithEvent,
getAbbreviatedMimeType,
getUriNameWithQuery,
getUriHostPort,
getUriHost,
getUrlBaseNameWithQuery,
getUrlHost,
getUrlHostName,
loadCauseString} = require("./request-utils");
const Actions = require("./actions/index");
const RequestListContextMenu = require("./request-list-context-menu");
loader.lazyRequireGetter(this, "NetworkHelper",
"devtools/shared/webconsole/network-helper");
const HTML_NS = "http://www.w3.org/1999/xhtml";
const EPSILON = 0.001;
// ms
@ -925,17 +923,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
break;
}
case "url": {
let uri;
try {
uri = NetworkHelper.nsIURL(value);
} catch (e) {
// User input may not make a well-formed url yet.
break;
}
let nameWithQuery = getUriNameWithQuery(uri);
let hostPort = getUriHostPort(uri);
let host = getUriHost(uri);
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(uri.spec));
let nameWithQuery = getUrlBaseNameWithQuery(value);
let hostPort = getUrlHost(value);
let host = getUrlHostName(value);
let unicodeUrl = decodeUnicodeUrl(value);
let file = $(".requests-menu-file", target);
file.setAttribute("value", nameWithQuery);

View File

@ -1,8 +1,8 @@
"use strict";
const { getAbbreviatedMimeType,
getUriNameWithQuery,
getUriHostPort,
getUrlBaseNameWithQuery,
getUrlHost,
loadCauseString } = require("./request-utils");
/**
@ -36,8 +36,8 @@ function method(first, second) {
}
function file(first, second) {
let firstUrl = getUriNameWithQuery(first.url).toLowerCase();
let secondUrl = getUriNameWithQuery(second.url).toLowerCase();
let firstUrl = getUrlBaseNameWithQuery(first.url).toLowerCase();
let secondUrl = getUrlBaseNameWithQuery(second.url).toLowerCase();
if (firstUrl == secondUrl) {
return first.startedMillis - second.startedMillis;
}
@ -45,8 +45,8 @@ function file(first, second) {
}
function domain(first, second) {
let firstDomain = getUriHostPort(first.url).toLowerCase();
let secondDomain = getUriHostPort(second.url).toLowerCase();
let firstDomain = getUrlHost(first.url).toLowerCase();
let secondDomain = getUrlHost(second.url).toLowerCase();
if (firstDomain == secondDomain) {
return first.startedMillis - second.startedMillis;
}

View File

@ -9,8 +9,13 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
var NetworkHelper = require("devtools/shared/webconsole/network-helper");
var { Toolbox } = require("devtools/client/framework/toolbox");
const {
decodeUnicodeUrl,
getUrlBaseName,
getUrlQuery,
getUrlHost,
} = require("devtools/client/netmonitor/request-utils");
const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
const HTTPS_EXAMPLE_URL = "https://example.com/browser/devtools/client/netmonitor/test/";
@ -260,11 +265,10 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
transferred, size, time, displayedStatus } = aData;
let { attachment, target } = aRequestItem;
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl));
let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/"));
let query = NetworkHelper.convertToUnicode(unescape(uri.query));
let hostPort = uri.hostPort;
let unicodeUrl = decodeUnicodeUrl(aUrl);
let name = getUrlBaseName(aUrl);
let query = getUrlQuery(aUrl);
let hostPort = getUrlHost(aUrl);
let remoteAddress = attachment.remoteAddress;
if (fuzzyUrl) {

View File

@ -1740,6 +1740,7 @@ WebConsoleActor.prototype =
delete result.ID;
delete result.innerID;
delete result.consoleID;
delete result.originAttributes;
result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
let dbgObj = this.makeDebuggeeValue(aObj, aUseObjectGlobal);

View File

@ -127,6 +127,12 @@ ConsoleAPIStorageService.prototype = {
}
let storage = _consoleStorage.get(aId);
// Clone originAttributes to prevent "TypeError: can't access dead object"
// exceptions when cached console messages are retrieved/filtered
// by the devtools webconsole actor.
aEvent.originAttributes = Cu.cloneInto(aEvent.originAttributes, {});
storage.push(aEvent);
// truncate

View File

@ -407,7 +407,8 @@ EventTargetChainItem::HandleEventTargetChain(
}
aVisitor.mEvent->mFlags.mInBubblingPhase = false;
if (!aVisitor.mEvent->mFlags.mInSystemGroup) {
if (!aVisitor.mEvent->mFlags.mInSystemGroup &&
aVisitor.mEvent->IsAllowedToDispatchInSystemGroup()) {
// Dispatch to the system event group. Make sure to clear the
// STOP_DISPATCH flag since this resets for each event group.
aVisitor.mEvent->mFlags.mPropagationStopped = false;

View File

@ -118,10 +118,8 @@ support-files =
support-files = pointerevent_setpointercapture_to_same_element_twice-manual.html
[test_pointerevent_suppress_compat_events_on_click.html]
support-files = pointerevent_suppress_compat_events_on_click.html
disabled = should be investigated
[test_pointerevent_suppress_compat_events_on_drag_mouse.html]
support-files = pointerevent_suppress_compat_events_on_drag_mouse.html
disabled = should be investigated
[test_touch_action.html]
support-files =
../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js
@ -148,5 +146,7 @@ support-files =
support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
[test_bug1293174_implicit_pointer_capture_for_touch_2.html]
support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
[test_bug1303704.html]
[test_empty_file.html]
disabled = disabled # Bug 1150091 - Issue with support-files
[test_bug1315862.html]

View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1303704
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1303704</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1303704">Mozilla Bug 1303704</a>
<p id="display"></p>
<a id="link1" href="http://www.google.com">Link 1</a>
<script type="text/javascript">
/** Test for Bug 1303704 **/
SimpleTest.waitForExplicitFinish();
function runTests() {
let link1 = window.document.getElementById("link1");
let mouseEvents = ["mousedown", "mouseup", "mousemove"];
link1.addEventListener("pointerdown", (e) => {
e.preventDefault();
is(e.defaultPrevented, true, "defaultPrevented should be true");
}, false);
mouseEvents.forEach((elm, index, arr) => {
link1.addEventListener(elm, () => {
ok(false, "Should not receive " + elm + " after preventDefault on pointerdown");
}, false);
});
link1.addEventListener("click", (e) => {
e.preventDefault();
SimpleTest.finish();
}, false);
synthesizeMouseAtCenter(link1, { type: "mousedown",
inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE });
synthesizeMouseAtCenter(link1, { type: "mousemove",
inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE });
synthesizeMouseAtCenter(link1, { type: "mouseup",
inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE });
}
SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests);
</script>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1315862
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1315862</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="content">
This is a test to check if pointer events are dispatched in the system group
</p>
<script type="text/javascript">
/** Test for Bug 1315862 **/
SimpleTest.waitForExplicitFinish();
function runTests() {
let allPointerEvents = ["pointerdown", "pointerup", "pointercancel",
"pointermove", "pointerover", "pointerout",
"pointerenter", "pointerleave", "gotpointercapture",
"lostpointercapture"
];
let content = document.getElementById('content');
let iframe = document.createElement('iframe');
let receivePointerEvents = false;
iframe.width = 50;
iframe.height = 50;
content.appendChild(iframe);
iframe.contentDocument.body.innerHTML =
"<div style='width: 100%; height: 100%; border: 1px solid black;'></div>";
let target = iframe.contentDocument.body.firstChild;
allPointerEvents.forEach((event, idx, arr) => {
SpecialPowers.addSystemEventListener(target, event, () => {
ok(false, "Shouldn't dispatch " + event + " in the system group");
receivePointerEvents = true;
});
});
target.addEventListener("pointerdown", (e) => {
target.setPointerCapture(e.pointerId);
}, false);
target.addEventListener("pointerup", () => {
is(receivePointerEvents, false, "Shouldn't dispatch pointer events in the system group");
SimpleTest.finish();
}, false);
let source = SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE;
synthesizeMouse(target, 5, 5, { type: "mousemove", inputSource: source },
iframe.contentWindow);
synthesizeMouse(target, 5, 5, { type: "mousedown", inputSource: source },
iframe.contentWindow);
synthesizeMouse(target, 5, 5, { type: "mousemove", inputSource: source },
iframe.contentWindow);
synthesizeMouse(target, 5, 5, { type: "mouseup", inputSource: source },
iframe.contentWindow);
}
SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests);
</script>
</body>
</html>

View File

@ -1042,11 +1042,6 @@ RTCPeerConnection.prototype = {
stream.getTracks().forEach(track => this.addTrack(track, stream));
},
getStreamById: function(id) {
throw new this._win.DOMException("getStreamById not yet implemented",
"NotSupportedError");
},
addTrack: function(track, stream) {
if (stream.currentTime === undefined) {
throw new this._win.DOMException("invalid stream.", "InvalidParameterError");

View File

@ -372,6 +372,13 @@ MediaDrmCDMProxy::IsOnOwnerThread()
}
#endif
const nsString&
MediaDrmCDMProxy::GetMediaDrmStubId() const
{
MOZ_ASSERT(mCDM);
return mCDM->GetMediaDrmStubId();
}
void
MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId)
{
@ -464,4 +471,4 @@ MediaDrmCDMProxy::md_Shutdown()
mCDM = nullptr;
}
} // namespace mozilla
} // namespace mozilla

View File

@ -10,7 +10,6 @@
#include <jni.h>
#include "mozilla/jni/Types.h"
#include "GeneratedJNINatives.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/CDMCaps.h"
#include "mozilla/dom/MediaKeys.h"
@ -118,6 +117,8 @@ public:
bool IsOnOwnerThread() override;
#endif
const nsString& GetMediaDrmStubId() const;
private:
virtual ~MediaDrmCDMProxy();
@ -181,4 +182,4 @@ private:
};
} // namespace mozilla
#endif // MediaDrmCDMProxy_h_
#endif // MediaDrmCDMProxy_h_

View File

@ -204,6 +204,9 @@ MediaDrmProxySupport::MediaDrmProxySupport(const nsAString& aKeySystem)
MediaDrmProxy::Create(mKeySystem,
mJavaCallbacks,
MediaPrefs::PDMAndroidRemoteCodecEnabled());
MOZ_ASSERT(mBridgeProxy, "mBridgeProxy should not be null");
mMediaDrmStubId = mBridgeProxy->GetStubId()->ToString();
}
MediaDrmProxySupport::~MediaDrmProxySupport()
@ -281,4 +284,4 @@ MediaDrmProxySupport::Shutdown()
mDestroyed = true;
}
} // namespace mozilla
} // namespace mozilla

View File

@ -54,14 +54,16 @@ public:
void Shutdown();
const nsString& GetMediaDrmStubId() const { return mMediaDrmStubId; }
private:
const nsString mKeySystem;
java::MediaDrmProxy::GlobalRef mBridgeProxy;
java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::GlobalRef mJavaCallbacks;
DecryptorProxyCallback* mCallback;
bool mDestroyed;
nsString mMediaDrmStubId;
};
} // namespace mozilla
#endif // MediaDrmProxySupport_H
#endif // MediaDrmProxySupport_H

View File

@ -5,6 +5,7 @@
#include <stdint.h>
#include <math.h>
#include <memory>
#include "../AudioPacketizer.h"
#include "gtest/gtest.h"
@ -37,7 +38,7 @@ int16_t Sequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
return aStart + i;
}
void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
void IsSequence(std::unique_ptr<int16_t[]> aBuffer, uint32_t aSize, uint32_t aStart = 0)
{
for (uint32_t i = 0; i < aSize; i++) {
ASSERT_TRUE(aBuffer[i] == static_cast<int64_t>(aStart + i)) <<
@ -46,7 +47,7 @@ void IsSequence(int16_t* aBuffer, uint32_t aSize, uint32_t aStart = 0)
// Buffer is a sequence.
}
void Zero(int16_t* aBuffer, uint32_t aSize)
void Zero(std::unique_ptr<int16_t[]> aBuffer, uint32_t aSize)
{
for (uint32_t i = 0; i < aSize; i++) {
ASSERT_TRUE(aBuffer[i] == 0) <<
@ -65,9 +66,8 @@ TEST(AudioPacketizer, Test)
{
AudioPacketizer<int16_t, int16_t> ap(441, channels);
for (int16_t i = 0; i < 10; i++) {
int16_t* out = ap.Output();
Zero(out, 441);
delete[] out;
std::unique_ptr<int16_t[]> out(ap.Output());
Zero(std::move(out), 441);
}
}
// Simple test, with input/output buffer size aligned on the packet size,
@ -80,9 +80,8 @@ TEST(AudioPacketizer, Test)
int16_t prevEnd = seqEnd;
seqEnd = Sequence(b.Get(), channels * 441, prevEnd);
ap.Input(b.Get(), 441);
int16_t* out = ap.Output();
IsSequence(out, 441 * channels, prevEnd);
delete[] out;
std::unique_ptr<int16_t[]> out(ap.Output());
IsSequence(std::move(out), 441 * channels, prevEnd);
}
}
// Simple test, with input/output buffer size aligned on the packet size,
@ -99,12 +98,10 @@ TEST(AudioPacketizer, Test)
seqEnd = Sequence(b1.Get(), 441 * channels, seqEnd);
ap.Input(b.Get(), 441);
ap.Input(b1.Get(), 441);
int16_t* out = ap.Output();
int16_t* out2 = ap.Output();
IsSequence(out, 441 * channels, prevEnd0);
IsSequence(out2, 441 * channels, prevEnd1);
delete[] out;
delete[] out2;
std::unique_ptr<int16_t[]> out(ap.Output());
std::unique_ptr<int16_t[]> out2(ap.Output());
IsSequence(std::move(out), 441 * channels, prevEnd0);
IsSequence(std::move(out2), 441 * channels, prevEnd1);
}
}
// Input/output buffer size not aligned on the packet size,
@ -120,14 +117,12 @@ TEST(AudioPacketizer, Test)
prevSeq = Sequence(b1.Get(), 480 * channels, prevSeq);
ap.Input(b.Get(), 480);
ap.Input(b1.Get(), 480);
int16_t* out = ap.Output();
int16_t* out2 = ap.Output();
IsSequence(out, 441 * channels, prevEnd);
std::unique_ptr<int16_t[]> out(ap.Output());
std::unique_ptr<int16_t[]> out2(ap.Output());
IsSequence(std::move(out), 441 * channels, prevEnd);
prevEnd += 441 * channels;
IsSequence(out2, 441 * channels, prevEnd);
IsSequence(std::move(out2), 441 * channels, prevEnd);
prevEnd += 441 * channels;
delete[] out;
delete[] out2;
}
printf("Available: %d\n", ap.PacketsAvailable());
}
@ -151,7 +146,7 @@ TEST(AudioPacketizer, Test)
}
ap.Input(b.Get(), 128);
while (ap.PacketsAvailable()) {
int16_t* packet = ap.Output();
std::unique_ptr<int16_t[]> packet(ap.Output());
for (uint32_t k = 0; k < ap.PacketSize(); k++) {
for (int32_t c = 0; c < channels; c++) {
ASSERT_TRUE(packet[k * channels + c] ==
@ -159,7 +154,6 @@ TEST(AudioPacketizer, Test)
}
outPhase++;
}
delete [] packet;
}
}
}

View File

@ -469,6 +469,14 @@ PDMFactory::GetDecoder(const TrackInfo& aTrackInfo,
void
PDMFactory::SetCDMProxy(CDMProxy* aProxy)
{
MOZ_ASSERT(aProxy);
#ifdef MOZ_WIDGET_ANDROID
if (IsWidevineKeySystem(aProxy->KeySystem())) {
mEMEPDM = new AndroidDecoderModule(aProxy);
return;
}
#endif
RefPtr<PDMFactory> m = new PDMFactory();
mEMEPDM = new EMEDecoderModule(aProxy, m);
}

View File

@ -119,6 +119,11 @@ GetCryptoInfoFromSample(const MediaRawData* aSample)
return cryptoInfo;
}
AndroidDecoderModule::AndroidDecoderModule(CDMProxy* aProxy)
{
mProxy = static_cast<MediaDrmCDMProxy*>(aProxy);
}
bool
AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
@ -174,16 +179,24 @@ AndroidDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
config.mDisplay.height,
&format), nullptr);
RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
RemoteDataDecoder::CreateVideoDecoder(config,
format,
aParams.mCallback,
aParams.mImageContainer) :
MediaCodecDataDecoder::CreateVideoDecoder(config,
format,
aParams.mCallback,
aParams.mImageContainer);
nsString drmStubId;
if (mProxy) {
drmStubId = mProxy->GetMediaDrmStubId();
}
RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
RemoteDataDecoder::CreateVideoDecoder(config,
format,
aParams.mCallback,
aParams.mImageContainer,
drmStubId) :
MediaCodecDataDecoder::CreateVideoDecoder(config,
format,
aParams.mCallback,
aParams.mImageContainer,
drmStubId,
mProxy,
aParams.mTaskQueue);
return decoder.forget();
}
@ -204,10 +217,18 @@ AndroidDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
config.mChannels,
&format), nullptr);
nsString drmStubId;
if (mProxy) {
drmStubId = mProxy->GetMediaDrmStubId();
}
RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback) :
MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback, drmStubId) :
MediaCodecDataDecoder::CreateAudioDecoder(config,
format,
aParams.mCallback,
drmStubId,
mProxy,
aParams.mTaskQueue);
return decoder.forget();
}

View File

@ -6,6 +6,7 @@
#define AndroidDecoderModule_h_
#include "PlatformDecoderModule.h"
#include "mozilla/MediaDrmCDMProxy.h"
namespace mozilla {
@ -17,7 +18,7 @@ public:
already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const CreateDecoderParams& aParams) override;
AndroidDecoderModule() {}
AndroidDecoderModule(CDMProxy* aProxy = nullptr);
virtual ~AndroidDecoderModule() {}
bool SupportsMimeType(const nsACString& aMimeType,
@ -25,6 +26,9 @@ public:
ConversionRequired
DecoderNeedsConversion(const TrackInfo& aConfig) const override;
private:
RefPtr<MediaDrmCDMProxy> mProxy;
};
extern LazyLogModule sAndroidDecoderModuleLog;

View File

@ -56,9 +56,10 @@ public:
VideoDataDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
aFormat, aCallback)
aFormat, aCallback, aDrmStubId)
, mImageContainer(aImageContainer)
, mConfig(aConfig)
{
@ -81,7 +82,6 @@ public:
if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
@ -127,15 +127,59 @@ protected:
layers::ImageContainer* mImageContainer;
const VideoInfo& mConfig;
RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
class EMEVideoDataDecoder : public VideoDataDecoder {
public:
EMEVideoDataDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId,
CDMProxy* aProxy,
TaskQueue* aTaskQueue)
: VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
aTaskQueue, aProxy))
{
}
void Input(MediaRawData* aSample) override;
void Shutdown() override;
private:
RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
void
EMEVideoDataDecoder::Input(MediaRawData* aSample)
{
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return;
}
VideoDataDecoder::Input(aSample);
}
void
EMEVideoDataDecoder::Shutdown()
{
VideoDataDecoder::Shutdown();
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
}
class AudioDataDecoder : public MediaCodecDataDecoder
{
public:
AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
aFormat, aCallback)
aFormat, aCallback, aDrmStubId)
{
JNIEnv* const env = jni::GetEnvForThread();
@ -210,27 +254,89 @@ public:
}
};
class EMEAudioDataDecoder : public AudioDataDecoder {
public:
EMEAudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback, const nsString& aDrmStubId,
CDMProxy* aProxy, TaskQueue* aTaskQueue)
: AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
aTaskQueue, aProxy))
{
}
void Input(MediaRawData* aSample) override;
void Shutdown() override;
private:
RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
};
void
EMEAudioDataDecoder::Input(MediaRawData* aSample)
{
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return;
}
AudioDataDecoder::Input(aSample);
}
void
EMEAudioDataDecoder::Shutdown()
{
AudioDataDecoder::Shutdown();
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
}
MediaDataDecoder*
MediaCodecDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId,
CDMProxy* aProxy,
TaskQueue* aTaskQueue)
{
return new AudioDataDecoder(aConfig, aFormat, aCallback);
if (!aProxy) {
return new AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId);
} else {
return new EMEAudioDataDecoder(aConfig,
aFormat,
aCallback,
aDrmStubId,
aProxy,
aTaskQueue);
}
}
MediaDataDecoder*
MediaCodecDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId,
CDMProxy* aProxy,
TaskQueue* aTaskQueue)
{
return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer);
if (!aProxy) {
return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
} else {
return new EMEVideoDataDecoder(aConfig,
aFormat,
aCallback,
aImageContainer,
aDrmStubId,
aProxy,
aTaskQueue);
}
}
MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: mType(aType)
, mMimeType(aMimeType)
, mFormat(aFormat)
@ -239,8 +345,8 @@ MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
, mOutputBuffers(nullptr)
, mMonitor("MediaCodecDataDecoder::mMonitor")
, mState(ModuleState::kDecoding)
, mDrmStubId(aDrmStubId)
{
}
MediaCodecDataDecoder::~MediaCodecDataDecoder()
@ -273,8 +379,11 @@ MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
return NS_ERROR_FAILURE;
}
MediaCrypto::LocalRef crypto = MediaDrmProxy::GetMediaCrypto(mDrmStubId);
bool hascrypto = !!crypto;
LOG("Has(%d) MediaCrypto (%s)", hascrypto, NS_ConvertUTF16toUTF8(mDrmStubId).get());
nsresult rv;
NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, nullptr, 0), rv);
NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, crypto, 0), rv);
NS_ENSURE_SUCCESS(rv = mDecoder->Start(), rv);
NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv);

View File

@ -23,12 +23,18 @@ class MediaCodecDataDecoder : public MediaDataDecoder {
public:
static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback);
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId,
CDMProxy* aProxy,
TaskQueue* aTaskQueue);
static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId,
CDMProxy* aProxy,
TaskQueue* aTaskQueue);
virtual ~MediaCodecDataDecoder();
@ -58,7 +64,8 @@ protected:
MediaCodecDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback);
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId);
static const char* ModuleStateStr(ModuleState aState);
@ -119,6 +126,8 @@ protected:
SampleQueue mQueue;
// Durations are stored in microseconds.
std::deque<media::TimeUnit> mDurations;
nsString mDrmStubId;
};
} // namespace mozilla

View File

@ -185,9 +185,10 @@ public:
RemoteVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId)
: RemoteDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
aFormat, aCallback)
aFormat, aCallback, aDrmStubId)
, mImageContainer(aImageContainer)
, mConfig(aConfig)
{
@ -213,7 +214,10 @@ public:
JavaCallbacksSupport::AttachNative(mJavaCallbacks,
mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
mJavaDecoder = CodecProxy::Create(mFormat, mSurfaceTexture->JavaSurface(), mJavaCallbacks);
mJavaDecoder = CodecProxy::Create(mFormat,
mSurfaceTexture->JavaSurface(),
mJavaCallbacks,
mDrmStubId);
if (mJavaDecoder == nullptr) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
@ -281,10 +285,11 @@ class RemoteAudioDecoder final : public RemoteDataDecoder
{
public:
RemoteAudioDecoder(const AudioInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: RemoteDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
aFormat, aCallback)
aFormat, aCallback, aDrmStubId)
, mConfig(aConfig)
{
JNIEnv* const env = jni::GetEnvForThread();
@ -311,7 +316,7 @@ public:
JavaCallbacksSupport::AttachNative(mJavaCallbacks,
mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks);
mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks, mDrmStubId);
if (mJavaDecoder == nullptr) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
}
@ -410,28 +415,32 @@ private:
MediaDataDecoder*
RemoteDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
{
return new RemoteAudioDecoder(aConfig, aFormat, aCallback);
return new RemoteAudioDecoder(aConfig, aFormat, aCallback, aDrmStubId);
}
MediaDataDecoder*
RemoteDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId)
{
return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer);
return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
}
RemoteDataDecoder::RemoteDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback)
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: mType(aType)
, mMimeType(aMimeType)
, mFormat(aFormat)
, mCallback(aCallback)
, mDrmStubId(aDrmStubId)
{
}

View File

@ -22,12 +22,14 @@ class RemoteDataDecoder : public MediaDataDecoder {
public:
static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback);
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId);
static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId);
virtual ~RemoteDataDecoder() {}
@ -44,7 +46,8 @@ protected:
RemoteDataDecoder(MediaData::Type aType,
const nsACString& aMimeType,
java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback);
MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId);
MediaData::Type mType;
@ -55,6 +58,7 @@ protected:
java::CodecProxy::GlobalRef mJavaDecoder;
java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
nsString mDrmStubId;
};
} // namespace mozilla

View File

@ -222,7 +222,7 @@ public:
Dispatch();
}
const T& ReturnValue() const {
T ReturnValue() const {
if (mSuccess) {
return mSuccessValue;
} else {

View File

@ -54,6 +54,7 @@ public:
}
aValues.ComputeLengthAndData();
aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::SetValueCurve,
aStartTime, 0.0f, 0.0f, aDuration, aValues.Data(),
aValues.Length());
@ -82,6 +83,7 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return this;
}
aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::SetValueAtTime,
aStartTime, aValue);
@ -95,6 +97,7 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return this;
}
aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::LinearRamp, aEndTime, aValue);
return this;
}
@ -106,6 +109,7 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return this;
}
aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::ExponentialRamp,
aEndTime, aValue);
return this;
@ -119,6 +123,7 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return this;
}
aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
EventInsertionHelper(aRv, AudioTimelineEvent::SetTarget,
aStartTime, aTarget,
aTimeConstant);
@ -133,6 +138,8 @@ public:
return this;
}
aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
// Remove some events on the main thread copy.
AudioEventTimeline::CancelScheduledValues(aStartTime);

View File

@ -120,8 +120,6 @@ interface RTCPeerConnection : EventTarget {
sequence<MediaStream> getLocalStreams ();
[UnsafeInPrerendering, Deprecated="RTCPeerConnectionGetStreams"]
sequence<MediaStream> getRemoteStreams ();
[UnsafeInPrerendering]
MediaStream? getStreamById (DOMString streamId);
void addStream (MediaStream stream);
// replaces addStream; fails if already added

View File

@ -6,6 +6,7 @@
#include "GPUProcessManager.h"
#include "GPUProcessHost.h"
#include "GPUProcessListener.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/layers/APZCTreeManager.h"

View File

@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Instruments.h"
#include "mozilla/Attributes.h"
#ifdef __APPLE__
@ -42,7 +43,7 @@ template<typename T>
class AutoReleased
{
public:
AutoReleased(T aTypeRef) : mTypeRef(aTypeRef)
MOZ_IMPLICIT AutoReleased(T aTypeRef) : mTypeRef(aTypeRef)
{
}
~AutoReleased()

View File

@ -507,7 +507,7 @@ class CodeOffsetJump
return jumpTableIndex_;
}
#else
CodeOffsetJump(size_t offset) : offset_(offset) {}
explicit CodeOffsetJump(size_t offset) : offset_(offset) {}
#endif
CodeOffsetJump() {

View File

@ -71,7 +71,7 @@ class RelocationIterator
uint32_t offset_;
public:
RelocationIterator(CompactBufferReader& reader)
explicit RelocationIterator(CompactBufferReader& reader)
: reader_(reader)
{ }

View File

@ -160,14 +160,14 @@ static const uint32_t WasmStackAlignment = SimdMemoryAlignment;
struct ImmTag : public Imm32
{
ImmTag(JSValueTag mask)
explicit ImmTag(JSValueTag mask)
: Imm32(int32_t(mask))
{ }
};
struct ImmType : public ImmTag
{
ImmType(JSValueType type)
explicit ImmType(JSValueType type)
: ImmTag(JSVAL_TYPE_TO_TAG(type))
{ }
};

View File

@ -736,7 +736,7 @@ class OutOfLineTruncate : public OutOfLineCodeBase<CodeGeneratorX86>
LTruncateDToInt32* ins_;
public:
OutOfLineTruncate(LTruncateDToInt32* ins)
explicit OutOfLineTruncate(LTruncateDToInt32* ins)
: ins_(ins)
{ }
@ -753,7 +753,7 @@ class OutOfLineTruncateFloat32 : public OutOfLineCodeBase<CodeGeneratorX86>
LTruncateFToInt32* ins_;
public:
OutOfLineTruncateFloat32(LTruncateFToInt32* ins)
explicit OutOfLineTruncateFloat32(LTruncateFToInt32* ins)
: ins_(ins)
{ }

View File

@ -254,7 +254,7 @@ class BaseCompiler
# ifdef DEBUG
BaseCompiler& bc;
public:
ScratchI32(BaseCompiler& bc) : bc(bc) {
explicit ScratchI32(BaseCompiler& bc) : bc(bc) {
MOZ_ASSERT(!bc.scratchRegisterTaken());
bc.setScratchRegisterTaken(true);
}
@ -264,7 +264,7 @@ class BaseCompiler
}
# else
public:
ScratchI32(BaseCompiler& bc) {}
explicit ScratchI32(BaseCompiler& bc) {}
# endif
operator Register() const {
# ifdef JS_CODEGEN_X86

View File

@ -1567,32 +1567,18 @@ struct MaskLayerUserData : public LayerUserData
struct CSSMaskLayerUserData : public LayerUserData
{
CSSMaskLayerUserData()
: mImageLayers(nsStyleImageLayers::LayerType::Mask)
: mFrame(nullptr)
{ }
CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntRect& aBounds)
: mImageLayers(aFrame->StyleSVGReset()->mMask),
mContentRect(aFrame->GetContentRectRelativeToSelf()),
mPaddingRect(aFrame->GetPaddingRectRelativeToSelf()),
mBorderRect(aFrame->GetRectRelativeToSelf()),
mMarginRect(aFrame->GetMarginRectRelativeToSelf()),
mBounds(aBounds)
{
Hash(aFrame);
}
CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntSize& aMaskSize)
: mFrame(aFrame),
mMaskSize(aMaskSize)
{ }
CSSMaskLayerUserData& operator=(const CSSMaskLayerUserData& aOther)
{
mImageLayers = aOther.mImageLayers;
mContentRect = aOther.mContentRect;
mPaddingRect = aOther.mPaddingRect;
mBorderRect = aOther.mBorderRect;
mMarginRect = aOther.mMarginRect;
mBounds = aOther.mBounds;
mHash = aOther.mHash;
mFrame = aOther.mFrame;
mMaskSize = aOther.mMaskSize;
return *this;
}
@ -1600,22 +1586,16 @@ struct CSSMaskLayerUserData : public LayerUserData
bool
operator==(const CSSMaskLayerUserData& aOther) const
{
if (mHash != aOther.mHash) {
if (mFrame != aOther.mFrame) {
return false;
}
if (mImageLayers.mLayers != aOther.mImageLayers.mLayers) {
return false;
}
if (!mContentRect.IsEqualEdges(aOther.mContentRect) ||
!mPaddingRect.IsEqualEdges(aOther.mPaddingRect) ||
!mBorderRect.IsEqualEdges(aOther.mBorderRect) ||
!mMarginRect.IsEqualEdges(aOther.mMarginRect)) {
return false;
}
if (!mBounds.IsEqualEdges(aOther.mBounds)) {
// Even if the frame is valid, check the size of the display item's
// boundary is still necessary. For example, if we scale the masked frame
// by adding a transform property on it, the masked frame is valid itself
// but we have to regenerate mask according to the new size in device
// space.
if (mMaskSize != aOther.mMaskSize) {
return false;
}
@ -1623,36 +1603,8 @@ struct CSSMaskLayerUserData : public LayerUserData
}
private:
void Hash(nsIFrame* aFrame)
{
uint32_t hash = 0;
const nsStyleImageLayers& imageLayers = aFrame->StyleSVGReset()->mMask;
for (uint32_t i = 0; i < imageLayers.mLayers.Length(); i++) {
const nsStyleImageLayers::Layer& newLayer = imageLayers.mLayers[i];
hash = AddToHash(hash, HashBytes(&newLayer, sizeof(newLayer)));
}
hash = AddToHash(hash, HashBytes(&mContentRect, sizeof(mContentRect)));
hash = AddToHash(hash, HashBytes(&mPaddingRect, sizeof(mPaddingRect)));
hash = AddToHash(hash, HashBytes(&mBorderRect, sizeof(mBorderRect)));
hash = AddToHash(hash, HashBytes(&mMarginRect, sizeof(mMarginRect)));
hash = AddToHash(hash, HashBytes(&mBounds, sizeof(mBounds)));
mHash = hash;
}
nsStyleImageLayers mImageLayers;
nsRect mContentRect;
nsRect mPaddingRect;
nsRect mBorderRect;
nsRect mMarginRect;
nsIntRect mBounds;
uint32_t mHash;
nsIFrame* mFrame;
nsIntSize mMaskSize;
};
/*
@ -3915,8 +3867,9 @@ ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
bool snap;
nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
nsIntRect itemRect = ScaleToOutsidePixels(bounds, snap);
CSSMaskLayerUserData newUserData(aMaskItem->Frame(), itemRect);
if (*oldUserData == newUserData) {
CSSMaskLayerUserData newUserData(aMaskItem->Frame(), itemRect.Size());
nsRect dirtyRect;
if (!aMaskItem->IsInvalid(dirtyRect) && *oldUserData == newUserData) {
aLayer->SetMaskLayer(maskLayer);
return;
}

View File

@ -7140,10 +7140,13 @@ bool nsDisplayMask::ShouldPaintOnMaskLayer(LayerManager* aManager)
nsSVGUtils::MaskUsage maskUsage;
nsSVGUtils::DetermineMaskUsage(mFrame, mHandleOpacity, maskUsage);
if (!maskUsage.shouldGenerateMaskLayer ||
maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath ||
maskUsage.shouldApplyBasicShape ||
maskUsage.shouldGenerateClipMaskLayer) {
if (!maskUsage.shouldGenerateMaskLayer &&
!maskUsage.shouldGenerateClipMaskLayer) {
return false;
}
if (maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath ||
maskUsage.shouldApplyBasicShape) {
return false;
}
@ -7151,20 +7154,6 @@ bool nsDisplayMask::ShouldPaintOnMaskLayer(LayerManager* aManager)
return false;
}
// XXX temporary disable drawing SVG mask onto mask layer before bug 1313877
// been fixed.
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
for (size_t i = 0; i < maskFrames.Length() ; i++) {
nsSVGMaskFrame *maskFrame = maskFrames[i];
if (maskFrame) {
return false; // Found SVG mask.
}
}
if (gfxPrefs::DrawMaskLayer()) {
return false;
}

View File

@ -1277,11 +1277,13 @@ public:
uint16_t mPointerType;
bool mActiveState;
bool mPrimaryState;
bool mPreventMouseEventByContent;
explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
bool aPrimaryState)
: mPointerType(aPointerType)
, mActiveState(aActiveState)
, mPrimaryState(aPrimaryState)
, mPreventMouseEventByContent(false)
{
}
};

View File

@ -6834,6 +6834,82 @@ FlushThrottledStyles(nsIDocument *aDocument, void *aData)
return true;
}
/*
* This function handles the preventDefault behavior of pointerdown. When user
* preventDefault on pointerdown, We have to mark the active pointer to prevent
* sebsequent mouse events (except mouse transition events) and default
* behaviors.
*
* We add mPreventMouseEventByContent flag in PointerInfo to represent the
* active pointer won't firing compatible mouse events. It's set to true when
* content preventDefault on pointerdown
*/
static void
PostHandlePointerEventsPreventDefault(WidgetPointerEvent* aPointerEvent,
WidgetGUIEvent* aMouseOrTouchEvent)
{
if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
!aPointerEvent->DefaultPreventedByContent()) {
return;
}
nsIPresShell::PointerInfo* pointerInfo = nullptr;
if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
!pointerInfo) {
// We already added the PointerInfo for active pointer when
// PresShell::HandleEvent handling pointerdown event.
#ifdef DEBUG
MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
#endif // #ifdef DEBUG
return;
}
// PreventDefault only applied for active pointers.
if (!pointerInfo->mActiveState) {
return;
}
aMouseOrTouchEvent->PreventDefault(false);
pointerInfo->mPreventMouseEventByContent = true;
}
/*
* This function handles the case when content had called preventDefault on the
* active pointer. In that case we have to prevent firing subsequent mouse
* to content. We check the flag PointerInfo::mPreventMouseEventByContent and
* call PreventDefault(false) to stop default behaviors and stop firing mouse
* events to content and chrome.
*
* note: mouse transition events are excluded
* note: we have to clean mPreventMouseEventByContent on pointerup for those
* devices support hover
* note: we don't suppress firing mouse events to chrome and system group
* handlers because they may implement default behaviors
*/
static void
PreHandlePointerEventsPreventDefault(WidgetPointerEvent* aPointerEvent,
WidgetGUIEvent* aMouseOrTouchEvent)
{
if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
return;
}
nsIPresShell::PointerInfo* pointerInfo = nullptr;
if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
!pointerInfo) {
// The PointerInfo for active pointer should be added for normal cases. But
// in some cases, we may receive mouse events before adding PointerInfo in
// sActivePointersIds. (e.g. receive mousemove before eMouseEnterIntoWidget
// or change preference 'dom.w3c_pointer_events.enabled' from off to on).
// In these cases, we could ignore them because they are not the events
// between a DefaultPrevented pointerdown and the corresponding pointerup.
return;
}
if (!pointerInfo->mPreventMouseEventByContent) {
return;
}
aMouseOrTouchEvent->PreventDefault(false);
if (aPointerEvent->mMessage == ePointerUp) {
pointerInfo->mPreventMouseEventByContent = false;
}
}
static nsresult
DispatchPointerFromMouseOrTouch(PresShell* aShell,
nsIFrame* aFrame,
@ -6880,8 +6956,10 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
0.0f;
event.convertToPointer = mouseEvent->convertToPointer = false;
PreHandlePointerEventsPreventDefault(&event, aEvent);
aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
aTargetContent);
PostHandlePointerEventsPreventDefault(&event, aEvent);
} else if (aEvent->mClass == eTouchEventClass) {
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
// loop over all touches and dispatch pointer events on each touch
@ -6926,8 +7004,10 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
event.buttons = WidgetMouseEvent::eLeftButtonFlag;
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
event.convertToPointer = touch->convertToPointer = false;
PreHandlePointerEventsPreventDefault(&event, aEvent);
aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
aTargetContent);
PostHandlePointerEventsPreventDefault(&event, aEvent);
}
}
return NS_OK;

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: clipPath invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 10px;
height: 10px;
transform: scale(20);
transform-origin: top left;
}
div.clipped {
clip-path: url(#cp1);
}
</style>
</head>
<body>
<div id="d1" class="outer clipped"></div>
<script type="text/javascript">
function changeTransform()
{
document.getElementById("d1").style.transform = "scale(10)";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeTransform, false);
</script>
<svg height="0">
<clipPath id="cp1">
<rect x="10" y="10" width="5" height="5"/>
</clipPath>
</svg>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: clipPath invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 10px;
height: 10px;
transform: scale(20);
transform-origin: top left;
}
div.clipped {
clip-path: url(#cp1);
}
div.inner {
width: 5px;
height: 5px;
border: 1px solid transparent;
will-change: transform;
}
</style>
</head>
<body>
<div id="d1" class="outer clipped"><div class="inner"></div></div>
<script type="text/javascript">
function changeTransform()
{
document.getElementById("d1").style.transform = "scale(10)";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeTransform, false);
</script>
<svg height="0">
<clipPath id="cp1">
<rect x="10" y="10" width="5" height="5"/>
</clipPath>
</svg>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: clipPath invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 200px;
height: 200px;
}
div.clipped {
clip-path: url(#cp1);
}
</style>
</head>
<body>
<div id="d1" class="outer clipped"></div>
<script type="text/javascript">
function changeClipPath()
{
document.getElementById("r1").setAttribute("width", "50");
document.getElementById("r1").setAttribute("height", "50");
document.getElementById("r1").setAttribute("x", "100");
document.getElementById("r1").setAttribute("y", "100");
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeClipPath, false);
</script>
<svg height="0">
<clipPath id="cp1">
<rect id="r1" x="50" y="50" width="100" height="100"/>
</clipPath>
</svg>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: clipPath invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 200px;
height: 200px;
}
div.clipped {
clip-path: url(#cp1);
}
div.inner {
width: 5px;
height: 5px;
border: 1px solid transparent;
will-change: transform;
}
</style>
</head>
<body>
<div id="d1" class="outer clipped"><div class="inner"></div></div>
<script type="text/javascript">
function changeClipPath()
{
document.getElementById("r1").setAttribute("width", "50");
document.getElementById("r1").setAttribute("height", "50");
document.getElementById("r1").setAttribute("x", "100");
document.getElementById("r1").setAttribute("y", "100");
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeClipPath, false);
</script>
<svg height="0">
<clipPath id="cp1">
<rect id="r1" x="50" y="50" width="100" height="100"/>
</clipPath>
</svg>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Masking: mask repainting.</title>
<style type="text/css">
div {
background-color: purple;
position: absolute;
margin: 1px 2px 3px 4px;
border: solid purple;
width: 44px;
height: 9px;
}
</style>
</head>
<body>
<div class="outer" style="top: 15px; left: 15px;"></div>
<div class="outer" style="top: 15px; left: 115px;"></div>
<div class="outer" style="top: 15px; left: 215px;"></div>
</body>
</html>

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
background-color: purple;
position: absolute;
margin: 1px 2px 3px 4px;
border: solid purple;
width: 40px;
height: 20px;
}
div.mask {
mask-size: 100% 100%;
mask-origin: border-box;
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="100%" height="50%" fill="blue" fill-opacity="1"/><rect x="0" y="50%" width="100%" height="50%" fill="blue" fill-opacity="0"/></svg>');
}
#d1 {
top: 10px;
left: 10px;
mask-clip: padding-box;
border-width: 10px;
padding: 0px;
}
#d2 {
top: 10px;
left: 110px;
mask-clip: padding-box;
border-width: 0px;
padding: 10px;
}
#d3 {
top: 15px;
left: 215px;
mask-clip: content-box;
border-width: 10px;
padding: 0px;
}
</style>
</head>
<body>
<div id="d1" class="outer mask"></div>
<div id="d2" class="outer mask"></div>
<div id="d3" class="outer mask"></div>
<script type="text/javascript">
function invalidateMaskedElements()
{
// Shrink border area, thicken padding area. Keep ths size of this
// division unchanged.
document.getElementById("d1").style.borderWidth = "5px";
document.getElementById("d1").style.padding = "5px";
// Shrink padding area, thicken border area. Keep ths size of this
// division unchanged.
document.getElementById("d2").style.borderWidth = "5px";
document.getElementById("d2").style.padding = "5px";
// Shrink border area, thicken content area. Keep ths size of this
// division unchanged.
document.getElementById("d3").style.width = "50px";
document.getElementById("d3").style.height = "30px";
document.getElementById("d3").style.borderWidth = "0px";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
invalidateMaskedElements, false);
</script>
</body>
</html>

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
background-color: purple;
position: absolute;
margin: 1px 2px 3px 4px;
border: solid purple;
width: 40px;
height: 20px;
}
div.inner {
width: 10px;
height: 10px;
border: 1px solid transparent;
will-change: transform;
}
div.mask {
mask-size: 100% 100%;
mask-origin: border-box;
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect x="0" y="0" width="100%" height="50%" fill="blue" fill-opacity="1"/><rect x="0" y="50%" width="100%" height="50%" fill="blue" fill-opacity="0"/></svg>');
}
#d1 {
top: 10px;
left: 10px;
mask-clip: padding-box;
border-width: 10px;
padding: 0px;
}
#d2 {
top: 10px;
left: 110px;
mask-clip: padding-box;
border-width: 0px;
padding: 10px;
}
#d3 {
top: 15px;
left: 215px;
mask-clip: content-box;
border-width: 10px;
padding: 0px;
}
</style>
</head>
<body>
<div id="d1" class="outer mask"><div class="inner"></div></div>
<div id="d2" class="outer mask"><div class="inner"></div></div>
<div id="d3" class="outer mask"><div class="inner"></div></div>
<script type="text/javascript">
function invalidateMaskedElements()
{
// Shrink border area, thicken padding area. Keep ths size of this
// division unchanged.
document.getElementById("d1").style.borderWidth = "5px";
document.getElementById("d1").style.padding = "5px";
// Shrink padding area, thicken border area. Keep ths size of this
// division unchanged.
document.getElementById("d2").style.borderWidth = "5px";
document.getElementById("d2").style.padding = "5px";
// Shrink border area, thicken content area. Keep ths size of this
// division unchanged.
document.getElementById("d3").style.width = "50px";
document.getElementById("d3").style.height = "30px";
document.getElementById("d3").style.borderWidth = "0px";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
invalidateMaskedElements, false);
</script>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Masking: mask repainting.</title>
<link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
</head>
<body>
<svg width="200" height="200">
<rect x="100" y="100" width="50" height="50" style="stroke:none; fill: purple;"/>
</svg>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 10px;
height: 10px;
transform: scale(20);
transform-origin: top left;
}
div.mask {
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 10 10"><rect x="10" y="10" width="5" height="5" fill="black"/></svg>');
}
</style>
</head>
<body>
<div id="d1" class="outer mask"></div>
<script type="text/javascript">
function changeTransform()
{
document.getElementById("d1").style.transform = "scale(10)";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeTransform, false);
</script>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 10px;
height: 10px;
transform: scale(20);
transform-origin: top left;
}
div.mask {
mask-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 10 10"><rect x="10" y="10" width="5" height="5" fill="black"/></svg>');
}
div.inner {
width: 5px;
height: 5px;
border: 1px solid transparent;
will-change: transform;
}
</style>
</head>
<body>
<div id="d1" class="outer mask"><div class="inner"></div></div>
<script type="text/javascript">
function changeTransform()
{
document.getElementById("d1").style.transform = "scale(10)";
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeTransform, false);
</script>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 200px;
height: 200px;
}
div.mask {
mask-image: url(#m1);
}
</style>
</head>
<body>
<div id="d1" class="outer mask"></div>
<script type="text/javascript">
function changeMask()
{
document.getElementById("r1").setAttribute("width", "50");
document.getElementById("r1").setAttribute("height", "50");
document.getElementById("r1").setAttribute("x", "100");
document.getElementById("r1").setAttribute("y", "100");
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeMask, false);
</script>
<svg height="0">
<mask id="m1" x="0" y="0" width="1" height="1">
<rect id="r1" x="50" y="50" width="100" height="100" style="stroke:none; fill: #ffffff;"/>
</mask>
</svg>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>CSS Masking: mask invalidation.</title>
<style type="text/css">
div.outer {
position: absolute;
background-color: purple;
border: solid purple;
width: 200px;
height: 200px;
}
div.mask {
mask-image: url(#m1);
}
div.inner {
width: 5px;
height: 5px;
border: 1px solid transparent;
will-change: transform;
}
</style>
</head>
<body>
<div id="d1" class="outer mask"><div class="inner"></div></div>
<script type="text/javascript">
function changeMask()
{
document.getElementById("r1").setAttribute("width", "50");
document.getElementById("r1").setAttribute("height", "50");
document.getElementById("r1").setAttribute("x", "100");
document.getElementById("r1").setAttribute("y", "100");
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate",
changeMask, false);
</script>
<svg height="0">
<mask id="m1" x="0" y="0" width="1" height="1">
<rect id="r1" x="50" y="50" width="100" height="100" style="stroke:none; fill: #ffffff;"/>
</mask>
</svg>
</body>
</html>

View File

@ -78,3 +78,16 @@ pref(layers.single-tile.enabled,false) != fast-scrolling.html about:blank
== zero-opacity-animation.html about:blank
== zero-opacity-text.html about:blank
== negative-w-component.html negative-w-component-ref.html
== mask-invalidation-1a.html mask-invalidation-1-ref.html
== mask-invalidation-1b.html mask-invalidation-1-ref.html
== mask-invalidation-2a.html mask-invalidation-2-ref.html
== mask-invalidation-2b.html mask-invalidation-2-ref.html
== mask-invalidation-2c.html mask-invalidation-2-ref.html
== mask-invalidation-2d.html mask-invalidation-2-ref.html
== clip-path-invalidation-1a.html mask-invalidation-2-ref.html
== clip-path-invalidation-1b.html mask-invalidation-2-ref.html
== clip-path-invalidation-1c.html mask-invalidation-2-ref.html
== clip-path-invalidation-1d.html mask-invalidation-2-ref.html

View File

@ -27,6 +27,8 @@ namespace css {
* typedef ... input_type;
* typedef ... input_array_type;
*
* typedef ... coeff_type;
*
* typedef ... result_type;
*
* // GetUnit(avalue) must return the correct nsCSSUnit for any
@ -40,17 +42,17 @@ namespace css {
*
* result_type
* MergeMultiplicativeL(nsCSSUnit aCalcFunction,
* float aValue1, result_type aValue2);
* coeff_type aValue1, result_type aValue2);
*
* result_type
* MergeMultiplicativeR(nsCSSUnit aCalcFunction,
* result_type aValue1, float aValue2);
* result_type aValue1, coeff_type aValue2);
*
* result_type
* ComputeLeafValue(const input_type& aValue);
*
* float
* ComputeNumber(const input_type& aValue);
* coeff_type
* ComputeCoefficient(const coeff_type& aValue);
*
* The CalcOps methods might compute the calc() expression down to a
* number, reduce some parts of it to a number but replicate other
@ -59,18 +61,21 @@ namespace css {
* values).
*
* For each leaf in the calc() expression, ComputeCalc will call either
* ComputeNumber (when the leaf is the left side of a Times_L or the
* ComputeCoefficient (when the leaf is the left side of a Times_L or the
* right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
* (The CalcOps in the CSS parser that reduces purely numeric
* expressions in turn calls ComputeCalc on numbers; other ops can
* presume that expressions in the number positions have already been
* normalized to a single numeric value and derive from
* NumbersAlreadyNormalizedCalcOps.)
* presume that expressions in the coefficient positions have already been
* normalized to a single numeric value and derive from, if their coefficient
* types are floats, FloatCoeffsAlreadyNormalizedCalcOps.)
*
* coeff_type will be float most of the time, but it's templatized so that
* ParseCalc can be used with <integer>s too.
*
* For non-leaves, one of the Merge functions will be called:
* MergeAdditive for Plus and Minus
* MergeMultiplicativeL for Times_L (number * value)
* MergeMultiplicativeR for Times_R (value * number) and Divided
* MergeMultiplicativeL for Times_L (coeff * value)
* MergeMultiplicativeR for Times_R (value * coeff) and Divided
*/
template <class CalcOps>
static typename CalcOps::result_type
@ -93,7 +98,7 @@ ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
case eCSSUnit_Calc_Times_L: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length");
float lhs = aOps.ComputeNumber(arr->Item(0));
typename CalcOps::coeff_type lhs = aOps.ComputeCoefficient(arr->Item(0));
typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
}
@ -102,7 +107,7 @@ ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length");
typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
float rhs = aOps.ComputeNumber(arr->Item(1));
typename CalcOps::coeff_type rhs = aOps.ComputeCoefficient(arr->Item(1));
return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
}
default: {
@ -135,6 +140,7 @@ struct CSSValueInputCalcOps
struct BasicCoordCalcOps
{
typedef nscoord result_type;
typedef float coeff_type;
result_type
MergeAdditive(nsCSSUnit aCalcFunction,
@ -150,7 +156,7 @@ struct BasicCoordCalcOps
result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2)
coeff_type aValue1, result_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit");
@ -159,7 +165,7 @@ struct BasicCoordCalcOps
result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2)
result_type aValue1, coeff_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R ||
aCalcFunction == eCSSUnit_Calc_Divided,
@ -174,6 +180,7 @@ struct BasicCoordCalcOps
struct BasicFloatCalcOps
{
typedef float result_type;
typedef float coeff_type;
result_type
MergeAdditive(nsCSSUnit aCalcFunction,
@ -189,7 +196,7 @@ struct BasicFloatCalcOps
result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2)
coeff_type aValue1, result_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit");
@ -198,7 +205,7 @@ struct BasicFloatCalcOps
result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2)
result_type aValue1, coeff_type aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Times_R) {
return aValue1 * aValue2;
@ -209,13 +216,55 @@ struct BasicFloatCalcOps
}
};
/**
* A ComputeNumber implementation for callers that can assume numbers
* are already normalized (i.e., anything past the parser).
*/
struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
struct BasicIntegerCalcOps
{
float ComputeNumber(const nsCSSValue& aValue)
typedef int result_type;
typedef int coeff_type;
result_type
MergeAdditive(nsCSSUnit aCalcFunction,
result_type aValue1, result_type aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Plus) {
return aValue1 + aValue2;
}
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
"unexpected unit");
return aValue1 - aValue2;
}
result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction,
coeff_type aValue1, result_type aValue2)
{
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit");
return aValue1 * aValue2;
}
result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, coeff_type aValue2)
{
if (aCalcFunction == eCSSUnit_Calc_Times_R) {
return aValue1 * aValue2;
}
MOZ_ASSERT_UNREACHABLE("We should catch and prevent divisions in integer "
"calc()s in the parser.");
return 1;
}
};
/**
* A ComputeCoefficient implementation for callers that can assume coefficients
* are floats and are already normalized (i.e., anything past the parser except
* pure-integer calcs, whose coefficients are integers).
*/
struct FloatCoeffsAlreadyNormalizedOps : public CSSValueInputCalcOps
{
typedef float coeff_type;
coeff_type ComputeCoefficient(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
return aValue.GetFloatValue();
@ -240,7 +289,11 @@ struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
*
* void Append(const char* aString);
* void AppendLeafValue(const input_type& aValue);
* void AppendNumber(const input_type& aValue);
*
* // AppendCoefficient accepts an input_type value, which represents a
* // value in the coefficient position, not a value of coeff_type,
* // because we're serializing the calc() expression itself.
* void AppendCoefficient(const input_type& aValue);
*
* Data structures given may or may not have a toplevel eCSSUnit_Calc
* node representing a calc whose toplevel is not min() or max().
@ -320,7 +373,7 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
aOps.Append("(");
}
if (unit == eCSSUnit_Calc_Times_L) {
aOps.AppendNumber(array->Item(0));
aOps.AppendCoefficient(array->Item(0));
} else {
SerializeCalcInternal(array->Item(0), aOps);
}
@ -344,7 +397,7 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
if (unit == eCSSUnit_Calc_Times_L) {
SerializeCalcInternal(array->Item(1), aOps);
} else {
aOps.AppendNumber(array->Item(1));
aOps.AppendCoefficient(array->Item(1));
}
if (needParens) {
aOps.Append(")");
@ -354,6 +407,48 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
}
}
/**
* ReduceNumberCalcOps is a CalcOps implementation for pure-number calc()
* (sub-)expressions, input as nsCSSValues.
* For example, nsCSSParser::ParseCalcMultiplicativeExpression uses it to
* simplify numeric sub-expressions in order to check for division-by-zero.
*/
struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
public mozilla::css::CSSValueInputCalcOps
{
result_type ComputeLeafValue(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
return aValue.GetFloatValue();
}
coeff_type ComputeCoefficient(const nsCSSValue& aValue)
{
return mozilla::css::ComputeCalc(aValue, *this);
}
};
/**
* ReduceIntegerCalcOps is a CalcOps implementation for pure-integer calc()
* (sub-)expressions, input as nsCSSValues.
*/
struct ReduceIntegerCalcOps : public mozilla::css::BasicIntegerCalcOps,
public mozilla::css::CSSValueInputCalcOps
{
result_type
ComputeLeafValue(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Integer, "unexpected unit");
return aValue.GetIntValue();
}
coeff_type
ComputeCoefficient(const nsCSSValue& aValue)
{
return mozilla::css::ComputeCalc(aValue, *this);
}
};
} // namespace css
} // namespace mozilla

View File

@ -8148,10 +8148,14 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
}
if ((aVariantMask & VARIANT_CALC) &&
IsCSSTokenCalcFunction(*tk)) {
// calc() currently allows only lengths and percents and number inside it.
// And note that in current implementation, number cannot be mixed with
// length and percent.
if (!ParseCalc(aValue, aVariantMask & VARIANT_LPN)) {
// calc() currently allows only lengths, percents, numbers, and integers.
//
// Note that VARIANT_NUMBER can be mixed with VARIANT_LENGTH and
// VARIANT_PERCENTAGE in the list of allowed types (numbers can be used as
// coefficients).
// However, the the resulting type is not a mixed type with number.
// VARIANT_INTEGER can't be mixed with anything else.
if (!ParseCalc(aValue, aVariantMask & (VARIANT_LPN | VARIANT_INTEGER))) {
return CSSParseResult::Error;
}
return CSSParseResult::Ok;
@ -13638,6 +13642,9 @@ CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask)
// This can be done without lookahead when we assume that the property
// values cannot themselves be numbers.
MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask");
MOZ_ASSERT(!(aVariantMask & VARIANT_LPN) != !(aVariantMask & VARIANT_INTEGER),
"variant mask must intersect with exactly one of VARIANT_LPN "
"or VARIANT_INTEGER");
bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
mUnitlessLengthQuirk = false;
@ -13708,21 +13715,6 @@ CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue,
}
}
struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
public mozilla::css::CSSValueInputCalcOps
{
result_type ComputeLeafValue(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
return aValue.GetFloatValue();
}
float ComputeNumber(const nsCSSValue& aValue)
{
return mozilla::css::ComputeCalc(aValue, *this);
}
};
// * If aVariantMask is VARIANT_NUMBER, this function parses the
// <number-multiplicative-expression> production.
// * If aVariantMask does not contain VARIANT_NUMBER, this function
@ -13748,9 +13740,18 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
nsCSSValue *storage = &aValue;
for (;;) {
uint32_t variantMask;
if (afterDivision || gotValue) {
if (aVariantMask & VARIANT_INTEGER) {
MOZ_ASSERT(aVariantMask == VARIANT_INTEGER,
"integers in calc expressions can't be mixed with anything "
"else.");
variantMask = aVariantMask;
} else if (afterDivision || gotValue) {
// At this point in the calc expression, we expect a coefficient or a
// divisor, which must be a number. (Not a length/%/etc.)
variantMask = VARIANT_NUMBER;
} else {
// At this point in the calc expression, we'll accept a coefficient
// (a number) or a value of whatever type |aVariantMask| specifies.
variantMask = aVariantMask | VARIANT_NUMBER;
}
if (!ParseCalcTerm(*storage, variantMask))
@ -13764,7 +13765,7 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
if (variantMask & VARIANT_NUMBER) {
// Simplify the value immediately so we can check for division by
// zero.
ReduceNumberCalcOps ops;
mozilla::css::ReduceNumberCalcOps ops;
float number = mozilla::css::ComputeCalc(*storage, ops);
if (number == 0.0 && afterDivision)
return false;
@ -13778,9 +13779,15 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
"unexpected relationship to current storage");
nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
ReduceNumberCalcOps ops;
float number = mozilla::css::ComputeCalc(leftValue, ops);
leftValue.SetFloatValue(number, eCSSUnit_Number);
if (variantMask & VARIANT_INTEGER) {
mozilla::css::ReduceIntegerCalcOps ops;
int integer = mozilla::css::ComputeCalc(leftValue, ops);
leftValue.SetIntValue(integer, eCSSUnit_Integer);
} else {
mozilla::css::ReduceNumberCalcOps ops;
float number = mozilla::css::ComputeCalc(leftValue, ops);
leftValue.SetFloatValue(number, eCSSUnit_Number);
}
}
}
@ -13794,6 +13801,19 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
afterDivision = false;
} else if (mToken.IsSymbol('/')) {
if (variantMask & VARIANT_INTEGER) {
// Integers aren't mixed with anything else (see the assert at the top
// of CSSParserImpl::ParseCalc).
// We don't allow division at all in calc()s for expressions where an
// integer is expected, because calc() division can't be resolved to
// an integer, as implied by spec text about '/' here:
// https://drafts.csswg.org/css-values-3/#calc-type-checking
// We've consumed the '/' token, but it doesn't matter as we're in an
// error-handling situation where we've already consumed a lot of
// other tokens (e.g. the token before the '/'). ParseVariant will
// indicate this with CSSParseResult::Error.
return false;
}
unit = eCSSUnit_Calc_Divided;
afterDivision = true;
} else {
@ -13853,15 +13873,24 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
}
// ... or just a value
UngetToken();
// Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
// always gets picked up
if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) !=
CSSParseResult::Ok) {
return false;
}
// ...and do the VARIANT_NUMBER check ourselves.
if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
return false;
if (aVariantMask & VARIANT_INTEGER) {
// Integers aren't mixed with anything else (see the assert at the
// top of CSSParserImpl::ParseCalc).
if (ParseVariant(aValue, aVariantMask, nullptr) != CSSParseResult::Ok) {
return false;
}
} else {
// Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
// always gets picked up (we want to catch unitless zeroes using
// VARIANT_NUMBER and then error out)
if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) !=
CSSParseResult::Ok) {
return false;
}
// ...and do the VARIANT_NUMBER check ourselves.
if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) {
return false;
}
}
// If we did the value parsing, we need to adjust aVariantMask to
// reflect which option we took (see above).

View File

@ -953,7 +953,7 @@ struct CSSValueSerializeCalcOps {
aValue.AppendToString(mProperty, mResult, mValueSerialization);
}
void AppendNumber(const input_type& aValue)
void AppendCoefficient(const input_type& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
aValue.AppendToString(mProperty, mResult, mValueSerialization);

View File

@ -327,8 +327,13 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue,
RuleNodeCacheConditions& aConditions);
struct CalcLengthCalcOps : public css::BasicCoordCalcOps,
public css::NumbersAlreadyNormalizedOps
public css::FloatCoeffsAlreadyNormalizedOps
{
// Declare that we have floats as coefficients so that we unambiguously
// resolve coeff_type (BasicCoordCalcOps and FloatCoeffsAlreadyNormalizedOps
// both have |typedef float coeff_type|).
typedef float coeff_type;
// All of the parameters to CalcLengthWith except aValue.
const nscoord mFontSize;
const nsStyleFont* const mStyleFont;
@ -667,7 +672,7 @@ nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext,
true, false, conditions);
}
struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
struct LengthPercentPairCalcOps : public css::FloatCoeffsAlreadyNormalizedOps
{
typedef nsRuleNode::ComputedCalc result_type;
@ -3318,8 +3323,13 @@ nsRuleNode::FindNextLargerFontSize(nscoord aFontSize, int32_t aBasePointSize,
}
struct SetFontSizeCalcOps : public css::BasicCoordCalcOps,
public css::NumbersAlreadyNormalizedOps
public css::FloatCoeffsAlreadyNormalizedOps
{
// Declare that we have floats as coefficients so that we unambiguously
// resolve coeff_type (BasicCoordCalcOps and FloatCoeffsAlreadyNormalizedOps
// both have |typedef float coeff_type|).
typedef float coeff_type;
// The parameters beyond aValue that we need for CalcLengthWith.
const nscoord mParentSize;
const nsStyleFont* const mParentFont;
@ -4449,7 +4459,7 @@ struct LineHeightCalcObj
bool mIsNumber;
};
struct SetLineHeightCalcOps : public css::NumbersAlreadyNormalizedOps
struct SetLineHeightCalcOps : public css::FloatCoeffsAlreadyNormalizedOps
{
typedef LineHeightCalcObj result_type;
nsStyleContext* const mStyleContext;

View File

@ -478,10 +478,9 @@ PaintMaskSurface(const PaintFramesParams& aParams,
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
Rect drawRect = IntRectToRect(IntRect(IntPoint(0, 0), svgMask->GetSize()));
aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
drawRect.TopLeft(),
DrawOptions(1.0, compositionOp));
Point(0, 0),
DrawOptions(1.0, compositionOp));
}
} else {
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
@ -704,7 +703,8 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
nsSVGUtils::MaskUsage maskUsage;
nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
maskUsage);
MOZ_ASSERT(maskUsage.shouldGenerateMaskLayer);
MOZ_ASSERT(maskUsage.shouldGenerateMaskLayer ||
maskUsage.shouldGenerateClipMaskLayer);
nsIFrame* frame = aParams.frame;
if (!ValidateSVGFrame(frame)) {
@ -716,25 +716,77 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
}
gfxContext& ctx = aParams.ctx;
gfxContextMatrixAutoSaveRestore matSR(&ctx);
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
bool opacityApplied = !HasNonSVGMask(maskFrames);
DrawResult result = DrawResult::SUCCESS;
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
SetupContextMatrix(frame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
gfxContextMatrixAutoSaveRestore matSR;
DrawTarget* target = ctx.GetDrawTarget();
return PaintMaskSurface(aParams, ctx.GetDrawTarget(),
opacityApplied ? maskUsage.opacity : 1.0,
firstFrame->StyleContext(), maskFrames,
ctx.CurrentMatrix(), offsetToUserSpace);
// Paint mask onto ctx.
if (maskUsage.shouldGenerateMaskLayer) {
matSR.SetContext(&ctx);
SetupContextMatrix(frame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
bool opacityApplied = !HasNonSVGMask(maskFrames);
result = PaintMaskSurface(aParams, target,
opacityApplied ? maskUsage.opacity : 1.0,
firstFrame->StyleContext(), maskFrames,
ctx.CurrentMatrix(), offsetToUserSpace);
if (result != DrawResult::SUCCESS) {
return result;
}
}
// Paint clip-path onto ctx.
if (maskUsage.shouldGenerateClipMaskLayer) {
matSR.Restore();
matSR.SetContext(&ctx);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
Matrix clipMaskTransform;
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
bool isOK = true;
nsSVGClipPathFrame *clipPathFrame =
effectProperties.GetClipPathFrame(&isOK);
// XXX Bug 1317636. Split nsSVGClipPathFrame::GetClipMask into two
// functions:
// 1. nsSVGClipPathFrame::CreateClipMask
// Create an A8 surface with right size for painting clip-path mask.
// 2. nsSVGClipPathFrame::PaintClipMask
// Paint the content of clip-path _direct_ onto a given A8 surface.
// With this change, we can skip one extra draw call
// (DrawTarget::MaskSurface) bellow.
RefPtr<SourceSurface> clipMaskSurface =
clipPathFrame->GetClipMask(ctx, frame, cssPxToDevPxMatrix,
&clipMaskTransform, nullptr,
ToMatrix(ctx.CurrentMatrix()), &result);
if (clipMaskSurface) {
gfxContextMatrixAutoSaveRestore matRestore(&ctx);
ctx.Multiply(ThebesMatrix(clipMaskTransform));
CompositionOp op = maskUsage.shouldGenerateMaskLayer
? CompositionOp::OP_IN : CompositionOp::OP_OVER;
target->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
clipMaskSurface,
Point(),
DrawOptions(1.0, op));
} else {
// Either entire surface is clipped out, or gfx buffer allocation
// failure in nsSVGClipPathFrame::GetClipMask.
return result;
}
}
return result;
}
DrawResult
@ -828,15 +880,15 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
Matrix clippedMaskTransform;
Matrix clipMaskTransform;
RefPtr<SourceSurface> clipMaskSurface =
clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
&clippedMaskTransform, maskSurface,
&clipMaskTransform, maskSurface,
maskTransform, &result);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
maskTransform = clipMaskTransform;
} else {
// Either entire surface is clipped out, or gfx buffer allocation
// failure in nsSVGClipPathFrame::GetClipMask.

View File

@ -122,4 +122,5 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
GYP_DIRS['signalingtest'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
GYP_DIRS['signalingtest'].non_unified_sources += signaling_non_unified_sources
DIRS += ['signaling/fuzztest']
if CONFIG['ENABLE_TESTS']:
DIRS += ['signaling/fuzztest']

View File

@ -16,11 +16,6 @@
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
#include "FakeMediaStreams.h"
#include "FakeMediaStreamsImpl.h"
#include "FakeLogging.h"
#include "signaling/src/sdp/SdpMediaSection.h"
#include "signaling/src/sdp/SipccSdpParser.h"
@ -30,19 +25,17 @@
#include "signaling/src/jsep/JsepSessionImpl.h"
#include "signaling/src/jsep/JsepTrack.h"
#include "mtransport_test_utils.h"
#include "FakeIPC.h"
#include "FakeIPC.cpp"
#include "TestHarness.h"
namespace mozilla {
static std::string kAEqualsCandidate("a=candidate:");
const static size_t kNumCandidatesPerComponent = 3;
class JsepSessionTestBase : public ::testing::Test
{
public:
static void SetUpTestCase() {
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();
}
};
class FakeUuidGenerator : public mozilla::JsepUuidGenerator
@ -4220,16 +4213,3 @@ TEST_F(JsepSessionTest, TestNonDefaultProtocol)
}
} // namespace mozilla
int
main(int argc, char** argv)
{
// Prevents some log spew
ScopedXPCOM xpcom("jsep_session_unittest");
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -6,24 +6,11 @@
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
// Magic linker includes :(
#include "FakeMediaStreams.h"
#include "FakeMediaStreamsImpl.h"
#include "FakeLogging.h"
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/sdp/SipccSdp.h"
#include "signaling/src/sdp/SdpHelper.h"
#include "mtransport_test_utils.h"
#include "FakeIPC.h"
#include "FakeIPC.cpp"
#include "TestHarness.h"
namespace mozilla {
class JsepTrackTest : public ::testing::Test
@ -1256,14 +1243,3 @@ TEST_F(JsepTrackTest, NonDefaultOpusParameters)
}
} // namespace mozilla
int
main(int argc, char** argv)
{
// Prevents some log spew
ScopedXPCOM xpcom("jsep_track_unittest");
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -17,6 +17,8 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and
]
SOURCES += [
'jsep_session_unittest.cpp',
'jsep_track_unittest.cpp',
'sdp_unittests.cpp',
]

View File

@ -7,8 +7,6 @@
# TODO: bug 1172551 - get these tests working on iOS
if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'uikit':
GeckoCppUnitTests([
'jsep_session_unittest',
'jsep_track_unittest',
'mediaconduit_unittests',
'mediapipeline_unittest',
'signaling_unittests',

View File

@ -13,7 +13,7 @@ import org.mozilla.gecko.media.Sample;
interface ICodec {
void setCallbacks(in ICodecCallbacks callbacks);
boolean configure(in FormatParam format, inout Surface surface, int flags);
boolean configure(in FormatParam format, inout Surface surface, int flags, in String drmStubId);
oneway void start();
oneway void stop();
oneway void flush();

View File

@ -200,7 +200,7 @@ public class HomeAdapter extends FragmentStatePagerAdapter {
// Override top_sites with ActivityStream panel when enabled
// PanelType.toString() returns the panel id
if (type.toString() == "top_sites" &&
if ("top_sites".equals(type.toString()) &&
ActivityStream.isEnabled(context) &&
ActivityStream.isHomePanel()) {
return ActivityStreamHomeFragment.class.getName();

View File

@ -126,9 +126,10 @@ public class JavaAddonManagerV1 implements NativeEventListener {
if (dispatcher == null) {
Log.w(LOGTAG, "Attempting to unload addon with unknown associated dispatcher; ignoring.");
callback.sendSuccess(false);
} else {
dispatcher.unregisterAllEventListeners();
callback.sendSuccess(true);
}
dispatcher.unregisterAllEventListeners();
callback.sendSuccess(true);
}
break;
}

View File

@ -5,6 +5,8 @@
package org.mozilla.gecko.media;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodec.CryptoInfo;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.view.Surface;
@ -22,7 +24,7 @@ public interface AsyncCodec {
}
public abstract void setCallbacks(Callbacks callbacks, Handler handler);
public abstract void configure(MediaFormat format, Surface surface, int flags);
public abstract void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags);
public abstract void start();
public abstract void stop();
public abstract void flush();
@ -30,5 +32,6 @@ public interface AsyncCodec {
public abstract ByteBuffer getInputBuffer(int index);
public abstract ByteBuffer getOutputBuffer(int index);
public abstract void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
public abstract void queueSecureInputBuffer(int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
public abstract void releaseOutputBuffer(int index, boolean render);
}

View File

@ -7,6 +7,7 @@ package org.mozilla.gecko.media;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.IBinder;
import android.os.RemoteException;
@ -167,6 +168,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
Sample sample = mInputSamples.poll();
long pts = sample.info.presentationTimeUs;
int flags = sample.info.flags;
MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
if (!sample.isEOS() && sample.buffer != null) {
len = sample.info.size;
ByteBuffer buf = mCodec.getInputBuffer(index);
@ -180,7 +182,12 @@ import java.util.concurrent.ConcurrentLinkedQueue;
}
mSamplePool.recycleInput(sample);
}
mCodec.queueInputBuffer(index, 0, len, pts, flags);
if (cryptoInfo != null) {
mCodec.queueSecureInputBuffer(index, 0, cryptoInfo, pts, flags);
} else {
mCodec.queueInputBuffer(index, 0, len, pts, flags);
}
}
}
@ -214,7 +221,10 @@ import java.util.concurrent.ConcurrentLinkedQueue;
}
@Override
public synchronized boolean configure(FormatParam format, Surface surface, int flags) throws RemoteException {
public synchronized boolean configure(FormatParam format,
Surface surface,
int flags,
String drmStubId) throws RemoteException {
if (mCallbacks == null) {
Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()");
return false;
@ -236,8 +246,15 @@ import java.util.concurrent.ConcurrentLinkedQueue;
try {
AsyncCodec codec = AsyncCodecFactory.create(codecName);
MediaCrypto crypto = RemoteMediaDrmBridgeStub.getMediaCrypto(drmStubId);
if (DEBUG) {
boolean hasCrypto = crypto != null;
Log.d(LOGTAG, "configure mediacodec with crypto(" + hasCrypto + ") / Id :" + drmStubId);
}
codec.setCallbacks(new Callbacks(mCallbacks), null);
codec.configure(fmt, surface, flags);
codec.configure(fmt, surface, crypto, flags);
mCodec = codec;
mInputProcessor = new InputProcessor();
mSamplePool = new SamplePool(codecName);

View File

@ -28,6 +28,7 @@ public final class CodecProxy {
private FormatParam mFormat;
private Surface mOutputSurface;
private CallbacksForwarder mCallbacks;
private String mRemoteDrmStubId;
public interface Callbacks {
void onInputExhausted();
@ -82,24 +83,31 @@ public final class CodecProxy {
}
@WrapForJNI
public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
return RemoteManager.getInstance().createCodec(format, surface, callbacks);
public static CodecProxy create(MediaFormat format,
Surface surface,
Callbacks callbacks,
String drmStubId) {
return RemoteManager.getInstance().createCodec(format, surface, callbacks, drmStubId);
}
public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
return new CodecProxy(format, surface, callbacks);
public static CodecProxy createCodecProxy(MediaFormat format,
Surface surface,
Callbacks callbacks,
String drmStubId) {
return new CodecProxy(format, surface, callbacks, drmStubId);
}
private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks, String drmStubId) {
mFormat = new FormatParam(format);
mOutputSurface = surface;
mRemoteDrmStubId = drmStubId;
mCallbacks = new CallbacksForwarder(callbacks);
}
boolean init(ICodec remote) {
try {
remote.setCallbacks(mCallbacks);
remote.configure(mFormat, mOutputSurface, 0);
remote.configure(mFormat, mOutputSurface, 0, mRemoteDrmStubId);
remote.start();
} catch (RemoteException e) {
e.printStackTrace();
@ -128,7 +136,6 @@ public final class CodecProxy {
Log.e(LOGTAG, "cannot send input to an ended codec");
return false;
}
try {
Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ?
Sample.EOS : mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);

View File

@ -21,14 +21,16 @@ import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.media.DeniedByServerException;
import android.media.MediaCrypto;
import android.media.MediaCryptoException;
import android.media.MediaDrm;
import android.media.MediaDrmException;
import android.media.NotProvisionedException;
import android.util.Log;
public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
private static final String LOGTAG = "GeckoMediaDrmBridgeV21";
protected final String LOGTAG;
private static final String INVALID_SESSION_ID = "Invalid";
private static final String WIDEVINE_KEY_SYSTEM = "com.widevine.alpha";
private static final boolean DEBUG = false;
@ -98,7 +100,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
}
GeckoMediaDrmBridgeV21(String keySystem) throws Exception {
if (DEBUG) Log.d(LOGTAG, "GeckoMediaDrmBridgeV21()");
LOGTAG = getClass().getSimpleName();
if (DEBUG) Log.d(LOGTAG, "GeckoMediaDrmBridgeV21 ctor");
mProvisioningPromiseId = 0;
mSessionIds = new HashSet<ByteBuffer>();
@ -142,7 +145,6 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
}
ByteBuffer sessionId = null;
String strSessionId = null;
try {
boolean hasMediaCrypto = ensureMediaCryptoCreated();
if (!hasMediaCrypto) {
@ -170,9 +172,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
LICENSE_REQUEST_INITIAL,
request.getData());
mSessionMIMETypes.put(sessionId, initDataType);
strSessionId = new String(sessionId.array());
mSessionIds.add(sessionId);
if (DEBUG) Log.d(LOGTAG, " StringID : " + strSessionId + " is put into mSessionIds ");
if (DEBUG) Log.d(LOGTAG, " StringID : " + new String(sessionId.array()) + " is put into mSessionIds ");
} catch (android.media.NotProvisionedException e) {
if (DEBUG) Log.d(LOGTAG, "Device not provisioned:" + e.getMessage());
if (sessionId != null) {
@ -218,18 +219,10 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + sessionId);
onSessionUpdated(promiseId, session.array());
return;
} catch (android.media.NotProvisionedException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:" + e.getMessage());
onSessionError(session.array(), "Got NotProvisionedException.");
onRejectPromise(promiseId, "Not provisioned during updateSession.");
} catch (android.media.DeniedByServerException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:" + e.getMessage());
onSessionError(session.array(), "Got DeniedByServerException.");
onRejectPromise(promiseId, "Denied by server during updateSession.");
} catch (java.lang.IllegalStateException e) {
if (DEBUG) Log.d(LOGTAG, "Exception when calling provideKeyResponse():" + e.getMessage());
onSessionError(session.array(), "Got IllegalStateException.");
onRejectPromise(promiseId, "Rejected during updateSession.");
} catch (final NotProvisionedException | DeniedByServerException | IllegalStateException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:", e);
onSessionError(session.array(), "Got exception during updateSession.");
onRejectPromise(promiseId, "Got exception during updateSession.");
}
release();
return;
@ -371,7 +364,6 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
}
// On L, these events are treated as exceptions and handled correspondingly.
// Leaving this code block for logging message.
String sessionId = new String(session.array());
switch (event) {
case MediaDrm.EVENT_PROVISION_REQUIRED:
if (DEBUG) Log.d(LOGTAG, "MediaDrm.EVENT_PROVISION_REQUIRED");
@ -381,10 +373,10 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
// No need to handle here if we're not in privacy mode.
break;
case MediaDrm.EVENT_KEY_EXPIRED:
if (DEBUG) Log.d(LOGTAG, "MediaDrm.EVENT_KEY_EXPIRED, sessionId=" + sessionId);
if (DEBUG) Log.d(LOGTAG, "MediaDrm.EVENT_KEY_EXPIRED, sessionId=" + new String(session.array()));
break;
case MediaDrm.EVENT_VENDOR_DEFINED:
if (DEBUG) Log.d(LOGTAG, "MediaDrm.EVENT_VENDOR_DEFINED, sessionId=" + sessionId);
if (DEBUG) Log.d(LOGTAG, "MediaDrm.EVENT_VENDOR_DEFINED, sessionId=" + new String(session.array()));
break;
default:
if (DEBUG) Log.d(LOGTAG, "Invalid DRM event " + event);
@ -414,7 +406,7 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
}
}
private boolean sessionExists(ByteBuffer session) {
protected boolean sessionExists(ByteBuffer session) {
if (mCryptoSessionId == null) {
if (DEBUG) Log.d(LOGTAG, "Session doesn't exist because media crypto session is not created.");
return false;
@ -592,9 +584,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
final byte [] cryptoSessionId = mCryptoSessionId.array();
mCrypto = new MediaCrypto(mSchemeUUID, cryptoSessionId);
String strCryptoSessionId = new String(cryptoSessionId);
mSessionIds.add(mCryptoSessionId);
if (DEBUG) Log.d(LOGTAG, "MediaCrypto successfully created! - SId " + INVALID_SESSION_ID + ", " + strCryptoSessionId);
if (DEBUG) Log.d(LOGTAG, "MediaCrypto successfully created! - SId " + INVALID_SESSION_ID + ", " + new String(cryptoSessionId));
return true;
} else {
if (DEBUG) Log.d(LOGTAG, "Cannot create MediaCrypto for unsupported scheme.");

View File

@ -5,14 +5,18 @@
package org.mozilla.gecko.media;
import android.annotation.TargetApi;
import android.media.DeniedByServerException;
import android.media.NotProvisionedException;
import static android.os.Build.VERSION_CODES.M;
import android.media.MediaDrm;
import android.util.Log;
import java.lang.IllegalStateException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 {
private static final String LOGTAG = "GeckoMediaDrmBridgeV23";
private static final boolean DEBUG = false;
GeckoMediaDrmBridgeV23(String keySystem) throws Exception {
@ -39,6 +43,43 @@ public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 {
keyStatus.getStatusCode());
}
onSessionBatchedKeyChanged(sessionId, keyInfos);
if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + new String(sessionId));
}
}
@Override
public void updateSession(int promiseId,
String sessionId,
byte[] response) {
if (DEBUG) Log.d(LOGTAG, "updateSession(), sessionId = " + sessionId);
if (mDrm == null) {
onRejectPromise(promiseId, "MediaDrm instance doesn't exist !!");
return;
}
ByteBuffer session = ByteBuffer.wrap(sessionId.getBytes());
if (!sessionExists(session)) {
onRejectPromise(promiseId, "Invalid session during updateSession.");
return;
}
try {
final byte [] keySetId = mDrm.provideKeyResponse(session.array(), response);
if (DEBUG) {
HashMap<String, String> infoMap = mDrm.queryKeyStatus(session.array());
for (String strKey : infoMap.keySet()) {
String strValue = infoMap.get(strKey);
Log.d(LOGTAG, "InfoMap : key(" + strKey + ")/value(" + strValue + ")");
}
}
onSessionUpdated(promiseId, session.array());
return;
} catch (final NotProvisionedException | DeniedByServerException | IllegalStateException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:", e);
onSessionError(session.array(), "Got exception during updateSession.");
onRejectPromise(promiseId, "Got exception during updateSession.");
}
release();
return;
}
}

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko.media;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.os.HandlerThread;
@ -294,10 +295,10 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
}
@Override
public void configure(MediaFormat format, Surface surface, int flags) {
public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
assertCallbacks();
mCodec.configure(format, surface, null, flags);
mCodec.configure(format, surface, crypto, flags);
}
private void assertCallbacks() {
@ -336,6 +337,28 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
}
@Override
public final void queueSecureInputBuffer(int index,
int offset,
MediaCodec.CryptoInfo cryptoInfo,
long presentationTimeUs,
int flags) {
assertCallbacks();
mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
try {
mCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
} catch (IllegalStateException e) {
e.printStackTrace();
mCallbackSender.notifyError(ERROR_CODEC);
return;
}
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
}
@Override
public final void releaseOutputBuffer(int index, boolean render) {
assertCallbacks();

View File

@ -39,10 +39,12 @@ public final class MediaDrmProxy {
@WrapForJNI
private static final String OPUS = "audio/opus";
public static ArrayList<MediaDrmProxy> sProxyList = new ArrayList<MediaDrmProxy>();
// A flag to avoid using the native object that has been destroyed.
private boolean mDestroyed;
private GeckoMediaDrm mImpl;
public static ArrayList<MediaDrmProxy> mProxyList = new ArrayList<MediaDrmProxy>();
private String mDrmStubId;
private static boolean isSystemSupported() {
// Support versions >= LOLLIPOP
@ -250,19 +252,26 @@ public final class MediaDrmProxy {
public static MediaDrmProxy create(String keySystem,
Callbacks nativeCallbacks,
boolean isRemote) {
// TODO: Will implement {Local,Remote}MediaDrmBridge instantiation by
// '''isRemote''' flag in Bug 1307818.
MediaDrmProxy proxy = new MediaDrmProxy(keySystem, nativeCallbacks);
MediaDrmProxy proxy = new MediaDrmProxy(keySystem, nativeCallbacks, isRemote);
return proxy;
}
MediaDrmProxy(String keySystem, Callbacks nativeCallbacks) {
MediaDrmProxy(String keySystem, Callbacks nativeCallbacks, boolean isRemote) {
if (DEBUG) Log.d(LOGTAG, "Constructing MediaDrmProxy");
// TODO: Bug 1306185 will implement the LocalMediaDrmBridge as an impl
// of GeckoMediaDrm for in-process decoding mode.
//mImpl = new LocalMediaDrmBridge(keySystem);
mImpl.setCallbacks(new MediaDrmProxyCallbacks(this, nativeCallbacks));
mProxyList.add(this);
try {
mDrmStubId = UUID.randomUUID().toString();
if (isRemote) {
IMediaDrmBridge remoteBridge =
RemoteManager.getInstance().createRemoteMediaDrmBridge(keySystem, mDrmStubId);
mImpl = new RemoteMediaDrmBridge(remoteBridge);
} else {
mImpl = new LocalMediaDrmBridge(keySystem);
}
mImpl.setCallbacks(new MediaDrmProxyCallbacks(this, nativeCallbacks));
sProxyList.add(this);
} catch (Exception e) {
Log.e(LOGTAG, "Constructing MediaDrmProxy ... error", e);
}
}
@WrapForJNI
@ -289,6 +298,24 @@ public final class MediaDrmProxy {
mImpl.closeSession(promiseId, sessionId);
}
@WrapForJNI(calledFrom = "gecko")
private String getStubId() {
return mDrmStubId;
}
// Get corresponding MediaCrypto object by a generated UUID for MediaCodec.
// Will be called on MediaFormatReader's TaskQueue.
@WrapForJNI
public static MediaCrypto getMediaCrypto(String stubId) {
for (MediaDrmProxy proxy : sProxyList) {
if (proxy.getStubId().equals(stubId)) {
return proxy.getMediaCryptoFromBridge();
}
}
if (DEBUG) Log.d(LOGTAG, " NULL crytpo ");
return null;
}
@WrapForJNI // Called when natvie object is destroyed.
private void destroy() {
if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed.");
@ -301,7 +328,11 @@ public final class MediaDrmProxy {
private void release() {
if (DEBUG) Log.d(LOGTAG, "release");
mProxyList.remove(this);
sProxyList.remove(this);
mImpl.release();
}
private MediaCrypto getMediaCryptoFromBridge() {
return mImpl != null ? mImpl.getMediaCrypto() : null;
}
}

View File

@ -120,14 +120,15 @@ public final class RemoteManager implements IBinder.DeathRecipient {
public synchronized CodecProxy createCodec(MediaFormat format,
Surface surface,
CodecProxy.Callbacks callbacks) {
CodecProxy.Callbacks callbacks,
String drmStubId) {
if (mRemote == null) {
if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
return null;
}
try {
ICodec remote = mRemote.createCodec();
CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks);
CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks, drmStubId);
if (proxy.init(remote)) {
mProxies.add(proxy);
return proxy;

View File

@ -892,10 +892,15 @@ var BrowserApp = {
NativeWindow.contextmenus.add(
function(aTarget) {
if (aTarget instanceof HTMLVideoElement) {
// If a video element is zero width or height, its essentially
// an HTMLAudioElement.
if (aTarget.videoWidth == 0 || aTarget.videoHeight == 0 )
if (aTarget.readyState == aTarget.HAVE_NOTHING) {
// We don't know if the height/width of the video,
// show a generic string.
return Strings.browser.GetStringFromName("contextmenu.saveMedia");
} else if (aTarget.videoWidth == 0 || aTarget.videoHeight == 0) {
// If a video element is zero width or height, its essentially
// an HTMLAudioElement.
return Strings.browser.GetStringFromName("contextmenu.saveAudio");
}
return Strings.browser.GetStringFromName("contextmenu.saveVideo");
} else if (aTarget instanceof HTMLAudioElement) {
return Strings.browser.GetStringFromName("contextmenu.saveAudio");
@ -907,9 +912,19 @@ var BrowserApp = {
UITelemetry.addEvent("save.1", "contextmenu", null, "media");
let url = aTarget.currentSrc || aTarget.src;
let filePickerTitleKey = (aTarget instanceof HTMLVideoElement &&
(aTarget.videoWidth != 0 && aTarget.videoHeight != 0))
? "SaveVideoTitle" : "SaveAudioTitle";
let filePickerTitleKey;
if (aTarget instanceof HTMLVideoElement) {
if (aTarget.readyState == aTarget.HAVE_NOTHING) {
filePickerTitleKey = "SaveMediaTitle";
} else if (aTarget.videoWidth == 0 || aTarget.videoHeight == 0) {
filePickerTitleKey = "SaveAudioTitle";
}
filePickerTitleKey = "SaveVideoTitle";
} else {
filePickerTitleKey = "SaveAudioTitle";
}
// Skipped trying to pull MIME type out of cache for now
ContentAreaUtils.internalSave(url, null, null, null, null, false,
filePickerTitleKey, null, aTarget.ownerDocument.documentURIObject,

View File

@ -279,6 +279,10 @@ contextmenu.mute=Mute
contextmenu.unmute=Unmute
contextmenu.saveVideo=Save Video
contextmenu.saveAudio=Save Audio
# LOCALIZATION NOTE (contextmenu.saveMedia):
# The label that will be used in the contextmenu in place of "Save Video" or "Save Audio", for
# unloaded video elements.
contextmenu.saveMedia=Save Media
contextmenu.addToContacts=Add to Contacts
# LOCALIZATION NOTE (contextmenu.sendToDevice):
# The label that will be used in the contextmenu and the pageaction

View File

@ -61,7 +61,7 @@ body {
}
#filter-input::placeholder {
color: rgba(255,255,255,0.5);
color: #777777;
}
.toolbar input {

View File

@ -1111,10 +1111,7 @@ pref("print.print_edge_right", 0);
pref("print.print_edge_bottom", 0);
// Print via the parent process. This is only used when e10s is enabled.
// For Mac, limit to Nightly.
#if defined(XP_WIN)
pref("print.print_via_parent", true);
#elif defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
#if defined(XP_WIN) || defined(XP_MACOSX)
pref("print.print_via_parent", true);
#else
pref("print.print_via_parent", false);

View File

@ -1535,10 +1535,6 @@ VARIABLES = {
"""List of manifest files defining marionette-unit tests.
"""),
'MARIONETTE_UPDATE_MANIFESTS': (ManifestparserManifestList, list,
"""List of manifest files defining marionette-update tests.
"""),
'MARIONETTE_WEBAPI_MANIFESTS': (ManifestparserManifestList, list,
"""List of manifest files defining marionette-webapi tests.
"""),

View File

@ -135,8 +135,6 @@ ALLOWED_XPCOM_GLUE = {
('test_unlock_notify', 'storage/test'),
('test_IHistory', 'toolkit/components/places/tests/cpp'),
('testcrasher', 'toolkit/crashreporter/test'),
('jsep_session_unittest', 'media/webrtc/signaling/test'),
('jsep_track_unittest', 'media/webrtc/signaling/test'),
('mediaconduit_unittests', 'media/webrtc/signaling/test'),
('mediapipeline_unittest', 'media/webrtc/signaling/test'),
('sdp_file_parser', 'media/webrtc/signaling/fuzztest'),

View File

@ -287,7 +287,6 @@ TEST_MANIFESTS = dict(
# TODO(ato): make packaging work as for other test suites
MARIONETTE=('marionette', 'marionette', '.', False),
MARIONETTE_UNIT=('marionette', 'marionette', '.', False),
MARIONETTE_UPDATE=('marionette', 'marionette', '.', False),
MARIONETTE_WEBAPI=('marionette', 'marionette', '.', False),
METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True),

View File

@ -22,6 +22,7 @@ import struct
import os
import re
import subprocess
import buildconfig
from collections import OrderedDict
# Regular expressions for unifying install.rdf
@ -80,7 +81,8 @@ class UnifiedExecutableFile(BaseFile):
os.close(fd)
tmpfiles.append(f)
e.copy(f, skip_if_older=False)
subprocess.call(['lipo', '-create'] + tmpfiles + ['-output', dest])
lipo = buildconfig.substs.get('LIPO') or 'lipo'
subprocess.call([lipo, '-create'] + tmpfiles + ['-output', dest])
finally:
for f in tmpfiles:
os.unlink(f)

View File

@ -44,3 +44,27 @@ macosx64/opt:
script: "mozharness/scripts/fx_desktop_build.py"
secrets: true
tooltool-downloads: internal
macosx64-universal/opt:
description: "MacOS X Universal Cross-compile"
index:
product: firefox
job-name: macosx64-opt
treeherder:
platform: osx-10-7/opt
symbol: tc(Bu)
tier: 2
worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
worker:
implementation: docker-worker
max-run-time: 36000
run:
using: mozharness
actions: [get-secrets build generate-build-stats update]
config:
- builds/releng_base_mac_64_cross_builds.py
- balrog/production.py
script: "mozharness/scripts/fx_desktop_build.py"
secrets: true
custom-build-variant-cfg: cross-universal
tooltool-downloads: internal

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