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

View File

@ -4,7 +4,11 @@ ac_add_options --disable-install-strip
ac_add_options --enable-verify-mar ac_add_options --enable-verify-mar
ac_add_options --enable-profiling ac_add_options --enable-profiling
ac_add_options --enable-instruments 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 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
ac_add_options --with-macbundlename-prefix=Firefox ac_add_options --with-macbundlename-prefix=Firefox

View File

@ -23,8 +23,9 @@
<!ENTITY aboutPrivateBrowsing.note.before "Private Browsing "> <!ENTITY aboutPrivateBrowsing.note.before "Private Browsing ">
<!ENTITY aboutPrivateBrowsing.note.emphasize "doesnt make you anonymous"> <!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.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.learnMore3.before "Learn more about ">
<!ENTITY aboutPrivateBrowsing.learnMore2.title "Private Browsing"> <!ENTITY aboutPrivateBrowsing.learnMore3.title "Private Browsing">
<!ENTITY aboutPrivateBrowsing.learnMore3.after ".">
<!ENTITY trackingProtection.title "Tracking Protection"> <!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."> <!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) { switch (aTopic) {
case "decoder-doctor-notification": case "decoder-doctor-notification":
let data = JSON.parse(aData); 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 && aSubject.top.document == this.content.document &&
data.formats.toLowerCase().includes("application/x-mpegurl", 0)) { 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.content.pluginRequiresReload = true;
this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", this.updateNotificationUI(this.content.document);
{ plugins: [...this.pluginData.values()],
showNow: true,
location: location,
}, null, principal);
} }
} }
}, },
@ -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) { _getPluginInfoForTag: function(pluginTag, tagMimetype) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
@ -269,7 +271,11 @@ PluginContent.prototype = {
pluginName: pluginName, pluginName: pluginName,
pluginTag: pluginTag, pluginTag: pluginTag,
permissionString: permissionString, 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, 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(AR, "${TOOLCHAIN_PREFIX}ar", :)
AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :) AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :)
AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :) AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :)
AC_CHECK_PROGS(LIPO, "${TOOLCHAIN_PREFIX}lipo", :)
AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :) AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :)
AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :) AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :)
AC_CHECK_PROGS(OTOOL, "${TOOLCHAIN_PREFIX}otool", :) AC_CHECK_PROGS(OTOOL, "${TOOLCHAIN_PREFIX}otool", :)

View File

@ -4,13 +4,16 @@
mk_add_options MOZ_UNIFY_BDATE=1 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 i386 --target=i386-apple-darwin$DARWIN_VERSION
ac_add_app_options x86_64 --target=x86_64-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 i386 --with-unify-dist=../x86_64/dist
ac_add_app_options x86_64 --with-unify-dist=../i386/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 . $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" CXX="$CXX -arch $TARGET_CPU"
# These must be set for cross builds, and don't hurt straight builds. # These must be set for cross builds, and don't hurt straight builds.
RANLIB=ranlib RANLIB="${TOOLCHAIN_PREFIX}ranlib"
AR=ar AR="${TOOLCHAIN_PREFIX}ar"
AS=$CC AS=$CC
LD=ld LD=ld
STRIP="strip" STRIP="strip"
OTOOL="otool" OTOOL="${TOOLCHAIN_PREFIX}otool"
# Each per-CPU build should be entirely oblivious to the fact that a # Each per-CPU build should be entirely oblivious to the fact that a
# universal binary will be produced. The exception is packager.mk, which # universal binary will be produced. The exception is packager.mk, which

View File

@ -726,13 +726,14 @@ gDevTools.getToolDefinitionArray()
// and the new ones. // and the new ones.
gDevTools.on("tool-registered", function (ev, toolId) { gDevTools.on("tool-registered", function (ev, toolId) {
let toolDefinition = gDevTools._tools.get(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) { gDevTools.on("tool-unregistered", function (ev, toolId) {
if (typeof toolId != "string") {
toolId = toolId.id;
}
gDevToolsBrowser._removeToolFromWindows(toolId); gDevToolsBrowser._removeToolFromWindows(toolId);
}); });

View File

@ -140,12 +140,16 @@ DevTools.prototype = {
tool = this._tools.get(tool); tool = this._tools.get(tool);
} }
else { 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; toolId = tool.id;
} }
this._tools.delete(toolId); this._tools.delete(toolId);
if (!isQuitApplication) { 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_toggle.js]
[browser_toolbox_tool_ready.js] [browser_toolbox_tool_ready.js]
[browser_toolbox_tool_remote_reopen.js] [browser_toolbox_tool_remote_reopen.js]
[browser_toolbox_tools_per_toolbox_registration.js]
[browser_toolbox_transport_events.js] [browser_toolbox_transport_events.js]
[browser_toolbox_view_source_01.js] [browser_toolbox_view_source_01.js]
[browser_toolbox_view_source_02.js] [browser_toolbox_view_source_02.js]

View File

@ -71,9 +71,8 @@ function testUnregister()
gDevTools.unregisterTool("test-tool"); 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"); is(toolId, "test-tool", "tool-unregistered event handler sent tool id");
ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map"); ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map");

View File

@ -21,6 +21,7 @@ add_task(function* () {
let target = TargetFactory.forTab(tab); let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target); toolbox = yield gDevTools.showToolbox(target);
doc = toolbox.doc; doc = toolbox.doc;
yield registerNewPerToolboxTool();
yield testSelectTool(); yield testSelectTool();
yield testOptionsShortcut(); yield testOptionsShortcut();
yield testOptions(); yield testOptions();
@ -46,6 +47,31 @@ function registerNewTool() {
"The tool is registered"); "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() { function* testSelectTool() {
info("Checking to make sure that the options panel can be selected."); 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])"); "#additional-tools-box input[type=checkbox]:not([data-unsupported])");
let enabledTools = [...toolNodes].filter(node => node.checked); let enabledTools = [...toolNodes].filter(node => node.checked);
let toggleableTools = gDevTools.getDefaultTools().filter(tool => { let toggleableTools = gDevTools.getDefaultTools()
return tool.visibilityswitch; .filter(tool => {
}).concat(gDevTools.getAdditionalTools()); return tool.visibilityswitch;
})
.concat(gDevTools.getAdditionalTools())
.concat(toolbox.getAdditionalTools());
for (let node of toolNodes) { for (let node of toolNodes) {
let id = node.getAttribute("id"); let id = node.getAttribute("id");
@ -235,7 +265,7 @@ function* toggleTool(node) {
} }
function checkUnregistered(toolId, deferred, event, data) { function checkUnregistered(toolId, deferred, event, data) {
if (data.id == toolId) { if (data == toolId) {
ok(true, "Correct tool removed"); ok(true, "Correct tool removed");
// checking tab on the toolbox // checking tab on the toolbox
ok(!doc.getElementById("toolbox-tab-" + toolId), 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"); "tools-not-supported-label");
let atleastOneToolNotSupported = false; 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 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 // Set the kill switch pref boolean to true
Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked); Services.prefs.setBoolPref(toolDefinition.visibilityswitch, this.checked);
if (this.checked) { gDevTools.emit(this.checked ? "tool-registered" : "tool-unregistered", id);
gDevTools.emit("tool-registered", id);
} else {
gDevTools.emit("tool-unregistered", toolDefinition);
}
}; };
let createToolCheckbox = tool => { let createToolCheckbox = tool => {
@ -243,6 +243,13 @@ OptionsPanel.prototype = {
additionalToolsBox.appendChild(createToolCheckbox(tool)); 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) { if (!atleastOneAddon) {
additionalToolsBox.style.display = "none"; additionalToolsBox.style.display = "none";
} }

View File

@ -895,6 +895,7 @@ Toolbox.prototype = {
* Add tabs to the toolbox UI for registered tools * Add tabs to the toolbox UI for registered tools
*/ */
_buildTabs: function () { _buildTabs: function () {
// Build tabs for global registered tools.
for (let definition of gDevTools.getToolDefinitionArray()) { for (let definition of gDevTools.getToolDefinitionArray()) {
this._buildTabForTool(definition); this._buildTabForTool(definition);
} }
@ -1252,6 +1253,81 @@ Toolbox.prototype = {
this._addKeysToWindow(); 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. * Ensure the tool with the given id is loaded.
* *
@ -1280,7 +1356,9 @@ Toolbox.prototype = {
return deferred.promise; 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) { if (!definition) {
deferred.reject(new Error("no such tool id " + id)); deferred.reject(new Error("no such tool id " + id));
return deferred.promise; return deferred.promise;
@ -1907,41 +1985,47 @@ Toolbox.prototype = {
}, },
/** /**
* Return if the tool is available as a tab (i.e. if it's checked * Test the availability of a tool (both globally registered tools and
* in the options panel). This is different from Toolbox.getPanel - * additional tools registered to this toolbox) by tool id.
* a tool could be registered but not yet opened in which case *
* isToolRegistered would return true but getPanel would return false. * @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) { isToolRegistered: function (toolId) {
return gDevTools.getToolDefinitionMap().has(toolId); return !!this.getToolDefinition(toolId);
}, },
/** /**
* Handler for the tool-registered event. * Return the tool definition registered globally or additional tools registered
* @param {string} event * to this toolbox.
* Name of the event ("tool-registered") *
* @param {string} toolId * @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) { getToolDefinition: function (toolId) {
let tool = gDevTools.getToolDefinition(toolId); return gDevTools.getToolDefinition(toolId) ||
this._buildTabForTool(tool); this.additionalToolDefinitions.get(toolId);
// 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. * Internal helper that removes a loaded tool from the toolbox,
* @param {string} event * it removes a loaded tool panel and tab from the toolbox without removing
* Name of the event ("tool-unregistered") * its definition, so that it can still be listed in options and re-added later.
* @param {string|object} toolId *
* Definition or id of the tool that was unregistered. Passing the * @param {string} toolId
* tool id should be avoided as it is a temporary measure. * Id of the tool to be removed.
*/ */
_toolUnregistered: function (event, toolId) { unloadTool: function (toolId) {
if (typeof toolId != "string") { if (typeof toolId != "string") {
toolId = toolId.id; throw new Error("Unexpected non-string toolId received.");
} }
if (this._toolPanels.has(toolId)) { if (this._toolPanels.has(toolId)) {
@ -1980,6 +2064,38 @@ Toolbox.prototype = {
key.parentNode.removeChild(key); 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 // Emit the event so tools can listen to it from the toolbox level
// instead of gDevTools // instead of gDevTools
this.emit("tool-unregistered", toolId); this.emit("tool-unregistered", toolId);

View File

@ -4,45 +4,47 @@
"use strict"; "use strict";
// Test for as-authored styles. // Test for as-authored color styles.
function* createTestContent(style) { /**
let html = `<style type="text/css"> * Array of test color objects:
${style} * {String} name: name of the used & expected color format.
</style> * {String} id: id of the element that will be created to test this color.
<div id="testid" class="testclass">Styled Node</div>`; * {String} color: initial value of the color property applied to the test element.
let tab = yield addTab("data:text/html;charset=utf-8," + * {String} result: expected value of the color property after edition.
encodeURIComponent(html)); */
const colors = [
let {inspector, view} = yield openRuleView(); {name: "hex", id: "test1", color: "#f0c", result: "#0f0"},
yield selectNode("#testid", inspector); {name: "rgb", id: "test2", color: "rgb(0,128,250)", result: "rgb(0, 255, 0)"},
return {view, tab}; // Test case preservation.
} {name: "hex", id: "test3", color: "#F0C", result: "#0F0"},
];
add_task(function* () { 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"); Services.prefs.setCharPref("devtools.defaultColorUnit", "authored");
for (let color of colors) { let html = "";
let {view, tab} = yield createTestContent("#testid {" + for (let {color, id} of colors) {
" color: " + color.text + ";" + 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 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"); .querySelector(".ruleview-colorswatch");
let onColorPickerReady = cPicker.once("ready"); let onColorPickerReady = cPicker.once("ready");
swatch.click(); swatch.click();
yield onColorPickerReady; yield onColorPickerReady;
yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], { yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], {
selector: "#testid", selector,
name: "color", name: "color",
value: "rgb(0, 255, 0)" value: "rgb(0, 255, 0)"
}); });
@ -55,11 +57,11 @@ add_task(function* () {
yield onHidden; yield onHidden;
yield onRuleViewChanged; 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); "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 */ /* globals window, dumpn, gNetwork, $, EVENTS, NetMonitorView */
"use strict"; "use strict";
const {Task} = require("devtools/shared/task"); const { Task } = require("devtools/shared/task");
const {writeHeaderText, getKeyWithEvent} = require("./request-utils"); const {
writeHeaderText,
loader.lazyRequireGetter(this, "NetworkHelper", getKeyWithEvent,
"devtools/shared/webconsole/network-helper"); getUrlQuery,
parseQueryString,
} = require("./request-utils");
/** /**
* Functions handling the custom request view. * Functions handling the custom request view.
@ -112,8 +114,7 @@ CustomRequestView.prototype = {
* The URL to extract query string from. * The URL to extract query string from.
*/ */
updateCustomQuery: function (url) { updateCustomQuery: function (url) {
let paramsArray = NetworkHelper.parseQueryString( const paramsArray = parseQueryString(getUrlQuery(url));
NetworkHelper.nsIURL(url).query);
if (!paramsArray) { if (!paramsArray) {
$("#custom-query").hidden = true; $("#custom-query").hidden = true;
@ -135,7 +136,7 @@ CustomRequestView.prototype = {
let queryString = writeQueryString(params); let queryString = writeQueryString(params);
let url = $("#custom-url-value").value; let url = $("#custom-url-value").value;
let oldQuery = NetworkHelper.nsIURL(url).query; let oldQuery = getUrlQuery(url);
let path = url.replace(oldQuery, queryString); let path = url.replace(oldQuery, queryString);
$("#custom-url-value").value = path; $("#custom-url-value").value = path;

View File

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

View File

@ -56,14 +56,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Chart",
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper", XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); "@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. * Object defining the network monitor controller components.
*/ */

View File

@ -8,10 +8,6 @@
/* exported $, $all */ /* exported $, $all */
"use strict"; "use strict";
XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function () {
return require("devtools/shared/webconsole/network-helper");
});
/* eslint-disable mozilla/reject-some-requires */ /* eslint-disable mozilla/reject-some-requires */
const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm"); const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
/* eslint-disable mozilla/reject-some-requires */ /* 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 {testing: isTesting} = require("devtools/shared/flags");
const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers"); const {ViewHelpers, Heritage} = require("devtools/client/shared/widgets/view-helpers");
const {Filters} = require("./filter-predicates"); const {Filters} = require("./filter-predicates");
const {getFormDataSections, const {
formDataURI, formDataURI,
getUriHostPort} = require("./request-utils"); decodeUnicodeUrl,
getFormDataSections,
getUrlBaseName,
getUrlQuery,
getUrlHost,
parseQueryString,
} = require("./request-utils");
const {L10N} = require("./l10n"); const {L10N} = require("./l10n");
const {RequestsMenuView} = require("./requests-menu-view"); const {RequestsMenuView} = require("./requests-menu-view");
const {CustomRequestView} = require("./custom-request-view"); const {CustomRequestView} = require("./custom-request-view");
@ -552,7 +554,7 @@ NetworkDetailsView.prototype = {
*/ */
_setSummary: function (data) { _setSummary: function (data) {
if (data.url) { 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("value", unicodeUrl);
$("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl); $("#headers-summary-url-value").setAttribute("tooltiptext", unicodeUrl);
$("#headers-summary-url").removeAttribute("hidden"); $("#headers-summary-url").removeAttribute("hidden");
@ -743,7 +745,7 @@ NetworkDetailsView.prototype = {
* The request's url. * The request's url.
*/ */
_setRequestGetParams: function (url) { _setRequestGetParams: function (url) {
let query = NetworkHelper.nsIURL(url).query; let query = getUrlQuery(url);
if (query) { if (query) {
this._addParams(this._paramsQueryString, query); this._addParams(this._paramsQueryString, query);
} }
@ -825,7 +827,7 @@ NetworkDetailsView.prototype = {
* A query string of params (e.g. "?foo=bar&baz=42"). * A query string of params (e.g. "?foo=bar&baz=42").
*/ */
_addParams: function (name, queryString) { _addParams: function (name, queryString) {
let paramsArray = NetworkHelper.parseQueryString(queryString); let paramsArray = parseQueryString(queryString);
if (!paramsArray) { if (!paramsArray) {
return; return;
} }
@ -918,7 +920,7 @@ NetworkDetailsView.prototype = {
// Immediately display additional information about the image: // Immediately display additional information about the image:
// file name, mime type and encoding. // file name, mime type and encoding.
$("#response-content-image-name-value").setAttribute("value", $("#response-content-image-name-value").setAttribute("value",
NetworkHelper.nsIURL(url).fileName); getUrlBaseName(url));
$("#response-content-image-mime-value").setAttribute("value", mimeType); $("#response-content-image-mime-value").setAttribute("value", mimeType);
// Wait for the image to load in order to display the width and height. // 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); setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
// Host header // Host header
let domain = getUriHostPort(url); let domain = getUrlHost(url);
let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader", let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader",
domain); domain);
setValue("#security-info-host-header", hostHeader); 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 Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item"); const MenuItem = require("devtools/client/framework/menu-item");
const { L10N } = require("./l10n"); const { L10N } = require("./l10n");
const { formDataURI, getFormDataSections } = require("./request-utils"); const {
formDataURI,
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("./request-utils");
loader.lazyRequireGetter(this, "HarExporter", loader.lazyRequireGetter(this, "HarExporter",
"devtools/client/netmonitor/har/har-exporter", true); "devtools/client/netmonitor/har/har-exporter", true);
@ -21,9 +26,6 @@ loader.lazyRequireGetter(this, "HarExporter",
loader.lazyServiceGetter(this, "clipboardHelper", loader.lazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
loader.lazyRequireGetter(this, "NetworkHelper",
"devtools/shared/webconsole/network-helper");
function RequestListContextMenu() {} function RequestListContextMenu() {}
RequestListContextMenu.prototype = { RequestListContextMenu.prototype = {
@ -56,8 +58,7 @@ RequestListContextMenu.prototype = {
id: "request-menu-context-copy-url-params", id: "request-menu-context-copy-url-params",
label: L10N.getStr("netmonitor.context.copyUrlParams"), label: L10N.getStr("netmonitor.context.copyUrlParams"),
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"), accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
visible: !!(selectedItem && visible: !!(selectedItem && getUrlQuery(selectedItem.attachment.url)),
NetworkHelper.nsIURL(selectedItem.attachment.url).query),
click: () => this.copyUrlParams(), click: () => this.copyUrlParams(),
})); }));
@ -203,7 +204,7 @@ RequestListContextMenu.prototype = {
*/ */
copyUrlParams() { copyUrlParams() {
let { url } = this.selectedItem.attachment; 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"); let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
clipboardHelper.copyString(string); clipboardHelper.copyString(string);
}, },
@ -224,7 +225,7 @@ RequestListContextMenu.prototype = {
let params = []; let params = [];
formDataSections.forEach(section => { formDataSections.forEach(section => {
let paramsArray = NetworkHelper.parseQueryString(section); let paramsArray = parseQueryString(section);
if (paramsArray) { if (paramsArray) {
params = [...params, ...paramsArray]; params = [...params, ...paramsArray];
} }

View File

@ -1,26 +1,24 @@
"use strict"; "use strict";
/* eslint-disable mozilla/reject-some-requires */ /* eslint-disable mozilla/reject-some-requires */
const { Ci } = require("chrome"); const { Ci } = require("chrome");
const { KeyCodes } = require("devtools/client/shared/keycodes"); const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task"); 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 * 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 * an event listener directly and is executed only when data-key is
* present in event.target. * present in event.target.
* *
* @param function callback * @param {function} callback - function to execute execute when data-key
* Function to execute execute when data-key * is present in event.target.
* is present in event.target. * @param {bool} onlySpaceOrReturn - flag to indicate if callback should only
* @param bool onlySpaceOrReturn * be called when the space or return button
* Flag to indicate if callback should only be called * is pressed
when the space or return button is pressed * @return {function} wrapped function with the target data-key as the first argument
* @return function * and the event as the second argument.
* 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) { return function (event) {
let key = event.target.getAttribute("data-key"); let key = event.target.getAttribute("data-key");
let filterKeyboardEvent = !onlySpaceOrReturn || let filterKeyboardEvent = !onlySpaceOrReturn ||
@ -31,24 +29,19 @@ exports.getKeyWithEvent = function (callback, onlySpaceOrReturn) {
callback.call(null, key); callback.call(null, key);
} }
}; };
}; }
/** /**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request. * POST request.
* *
* @param object headers * @param {object} headers - the "requestHeaders".
* The "requestHeaders". * @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
* @param object uploadHeaders * @param {object} postData - the "requestPostData".
* The "requestHeadersFromUploadStream". * @param {function} getString - callback to retrieve a string from a LongStringGrip.
* @param object postData * @return {array} a promise list that is resolved with the extracted form data.
* 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.
*/ */
exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, postData, const getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
getString) { getString) {
let formDataSections = []; 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. * Form a data: URI given a mime type, encoding, and some text.
* *
* @param {String} mimeType the mime type * @param {string} mimeType - mime type
* @param {String} encoding the encoding to use; if not set, the * @param {string} encoding - encoding to use; if not set, the
* text will be base64-encoded. * text will be base64-encoded.
* @param {String} text the text of the URI. * @param {string} text - text of the URI.
* @return {String} a data: URI * @return {string} a data URI
*/ */
exports.formDataURI = function (mimeType, encoding, text) { function formDataURI(mimeType, encoding, text) {
if (!encoding) { if (!encoding) {
encoding = "base64"; encoding = "base64";
text = btoa(text); text = btoa(text);
} }
return "data:" + mimeType + ";" + encoding + "," + text; return "data:" + mimeType + ";" + encoding + "," + text;
}; }
/** /**
* Write out a list of headers into a chunk of text * Write out a list of headers into a chunk of text
* *
* @param array headers * @param {array} headers - array of headers info { name, value }
* Array of headers info {name, value} * @return {string} list of headers in text format
* @return string text
* List of headers in text format
*/ */
exports.writeHeaderText = function (headers) { function writeHeaderText(headers) {
return headers.map(({name, value}) => name + ": " + value).join("\n"); 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. * Helper for getting an abbreviated string for a mime type.
* *
* @param string mimeType * @param {string} mimeType - mime type
* @return string * @return {string} abbreviated mime type
*/ */
exports.getAbbreviatedMimeType = function (mimeType) { function getAbbreviatedMimeType(mimeType) {
if (!mimeType) { if (!mimeType) {
return ""; return "";
} }
return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0]; 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 * @param {string} url - url string
* @return string * @return {string} unicode basename of a url
*/ */
exports.getUriNameWithQuery = function (url) { function getUrlBaseName(url) {
if (!(url instanceof Ci.nsIURL)) { const pathname = (new URL(url)).pathname;
url = NetworkHelper.nsIURL(url); 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( return query.replace(/^[?&]/, "").split("&").map(e => {
unescape(url.fileName || url.filePath || "/")); let param = e.split("=");
let query = NetworkHelper.convertToUnicode(unescape(url.query)); return {
name: param[0] ? decodeUnicodeUrl(param[0]) : "",
return name + (query ? "?" + query : ""); value: param[1] ? decodeUnicodeUrl(param[1]) : "",
}; };
});
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+$/, "");
};
/** /**
* Convert a nsIContentPolicy constant to a display string * Convert a nsIContentPolicy constant to a display string
@ -180,6 +232,22 @@ const LOAD_CAUSE_STRINGS = {
[Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest" [Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest"
}; };
exports.loadCauseString = function (causeType) { function loadCauseString(causeType) {
return LOAD_CAUSE_STRINGS[causeType] || "unknown"; 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 {L10N, WEBCONSOLE_L10N} = require("./l10n");
const {formDataURI, const {formDataURI,
writeHeaderText, writeHeaderText,
decodeUnicodeUrl,
getKeyWithEvent, getKeyWithEvent,
getAbbreviatedMimeType, getAbbreviatedMimeType,
getUriNameWithQuery, getUrlBaseNameWithQuery,
getUriHostPort, getUrlHost,
getUriHost, getUrlHostName,
loadCauseString} = require("./request-utils"); loadCauseString} = require("./request-utils");
const Actions = require("./actions/index"); const Actions = require("./actions/index");
const RequestListContextMenu = require("./request-list-context-menu"); 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 HTML_NS = "http://www.w3.org/1999/xhtml";
const EPSILON = 0.001; const EPSILON = 0.001;
// ms // ms
@ -925,17 +923,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
break; break;
} }
case "url": { case "url": {
let uri; let nameWithQuery = getUrlBaseNameWithQuery(value);
try { let hostPort = getUrlHost(value);
uri = NetworkHelper.nsIURL(value); let host = getUrlHostName(value);
} catch (e) { let unicodeUrl = decodeUnicodeUrl(value);
// 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 file = $(".requests-menu-file", target); let file = $(".requests-menu-file", target);
file.setAttribute("value", nameWithQuery); file.setAttribute("value", nameWithQuery);

View File

@ -1,8 +1,8 @@
"use strict"; "use strict";
const { getAbbreviatedMimeType, const { getAbbreviatedMimeType,
getUriNameWithQuery, getUrlBaseNameWithQuery,
getUriHostPort, getUrlHost,
loadCauseString } = require("./request-utils"); loadCauseString } = require("./request-utils");
/** /**
@ -36,8 +36,8 @@ function method(first, second) {
} }
function file(first, second) { function file(first, second) {
let firstUrl = getUriNameWithQuery(first.url).toLowerCase(); let firstUrl = getUrlBaseNameWithQuery(first.url).toLowerCase();
let secondUrl = getUriNameWithQuery(second.url).toLowerCase(); let secondUrl = getUrlBaseNameWithQuery(second.url).toLowerCase();
if (firstUrl == secondUrl) { if (firstUrl == secondUrl) {
return first.startedMillis - second.startedMillis; return first.startedMillis - second.startedMillis;
} }
@ -45,8 +45,8 @@ function file(first, second) {
} }
function domain(first, second) { function domain(first, second) {
let firstDomain = getUriHostPort(first.url).toLowerCase(); let firstDomain = getUrlHost(first.url).toLowerCase();
let secondDomain = getUriHostPort(second.url).toLowerCase(); let secondDomain = getUrlHost(second.url).toLowerCase();
if (firstDomain == secondDomain) { if (firstDomain == secondDomain) {
return first.startedMillis - second.startedMillis; 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", "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this); this);
var NetworkHelper = require("devtools/shared/webconsole/network-helper");
var { Toolbox } = require("devtools/client/framework/toolbox"); 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 EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
const HTTPS_EXAMPLE_URL = "https://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; transferred, size, time, displayedStatus } = aData;
let { attachment, target } = aRequestItem; let { attachment, target } = aRequestItem;
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL); let unicodeUrl = decodeUnicodeUrl(aUrl);
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl)); let name = getUrlBaseName(aUrl);
let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/")); let query = getUrlQuery(aUrl);
let query = NetworkHelper.convertToUnicode(unescape(uri.query)); let hostPort = getUrlHost(aUrl);
let hostPort = uri.hostPort;
let remoteAddress = attachment.remoteAddress; let remoteAddress = attachment.remoteAddress;
if (fuzzyUrl) { if (fuzzyUrl) {

View File

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

View File

@ -127,6 +127,12 @@ ConsoleAPIStorageService.prototype = {
} }
let storage = _consoleStorage.get(aId); 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); storage.push(aEvent);
// truncate // truncate

View File

@ -407,7 +407,8 @@ EventTargetChainItem::HandleEventTargetChain(
} }
aVisitor.mEvent->mFlags.mInBubblingPhase = false; 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 // Dispatch to the system event group. Make sure to clear the
// STOP_DISPATCH flag since this resets for each event group. // STOP_DISPATCH flag since this resets for each event group.
aVisitor.mEvent->mFlags.mPropagationStopped = false; aVisitor.mEvent->mFlags.mPropagationStopped = false;

View File

@ -118,10 +118,8 @@ support-files =
support-files = pointerevent_setpointercapture_to_same_element_twice-manual.html support-files = pointerevent_setpointercapture_to_same_element_twice-manual.html
[test_pointerevent_suppress_compat_events_on_click.html] [test_pointerevent_suppress_compat_events_on_click.html]
support-files = 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] [test_pointerevent_suppress_compat_events_on_drag_mouse.html]
support-files = 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] [test_touch_action.html]
support-files = support-files =
../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js ../../../../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 support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
[test_bug1293174_implicit_pointer_capture_for_touch_2.html] [test_bug1293174_implicit_pointer_capture_for_touch_2.html]
support-files = 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] [test_empty_file.html]
disabled = disabled # Bug 1150091 - Issue with support-files 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)); 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) { addTrack: function(track, stream) {
if (stream.currentTime === undefined) { if (stream.currentTime === undefined) {
throw new this._win.DOMException("invalid stream.", "InvalidParameterError"); throw new this._win.DOMException("invalid stream.", "InvalidParameterError");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -469,6 +469,14 @@ PDMFactory::GetDecoder(const TrackInfo& aTrackInfo,
void void
PDMFactory::SetCDMProxy(CDMProxy* aProxy) 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(); RefPtr<PDMFactory> m = new PDMFactory();
mEMEPDM = new EMEDecoderModule(aProxy, m); mEMEPDM = new EMEDecoderModule(aProxy, m);
} }

View File

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

View File

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

View File

@ -56,9 +56,10 @@ public:
VideoDataDecoder(const VideoInfo& aConfig, VideoDataDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat, MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback, MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer) layers::ImageContainer* aImageContainer,
const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType, : MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
aFormat, aCallback) aFormat, aCallback, aDrmStubId)
, mImageContainer(aImageContainer) , mImageContainer(aImageContainer)
, mConfig(aConfig) , mConfig(aConfig)
{ {
@ -81,7 +82,6 @@ public:
if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) { if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
} }
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__); return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
} }
@ -127,15 +127,59 @@ protected:
layers::ImageContainer* mImageContainer; layers::ImageContainer* mImageContainer;
const VideoInfo& mConfig; const VideoInfo& mConfig;
RefPtr<AndroidSurfaceTexture> mSurfaceTexture; 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 class AudioDataDecoder : public MediaCodecDataDecoder
{ {
public: public:
AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat, AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback) MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType, : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
aFormat, aCallback) aFormat, aCallback, aDrmStubId)
{ {
JNIEnv* const env = jni::GetEnvForThread(); 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* MediaDataDecoder*
MediaCodecDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig, MediaCodecDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
MediaFormat::Param aFormat, java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback) 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* MediaDataDecoder*
MediaCodecDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig, MediaCodecDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
MediaFormat::Param aFormat, java::sdk::MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback, 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, MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
const nsACString& aMimeType, const nsACString& aMimeType,
MediaFormat::Param aFormat, MediaFormat::Param aFormat,
MediaDataDecoderCallback* aCallback) MediaDataDecoderCallback* aCallback,
const nsString& aDrmStubId)
: mType(aType) : mType(aType)
, mMimeType(aMimeType) , mMimeType(aMimeType)
, mFormat(aFormat) , mFormat(aFormat)
@ -239,8 +345,8 @@ MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
, mOutputBuffers(nullptr) , mOutputBuffers(nullptr)
, mMonitor("MediaCodecDataDecoder::mMonitor") , mMonitor("MediaCodecDataDecoder::mMonitor")
, mState(ModuleState::kDecoding) , mState(ModuleState::kDecoding)
, mDrmStubId(aDrmStubId)
{ {
} }
MediaCodecDataDecoder::~MediaCodecDataDecoder() MediaCodecDataDecoder::~MediaCodecDataDecoder()
@ -273,8 +379,11 @@ MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)
return NS_ERROR_FAILURE; 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; 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 = mDecoder->Start(), rv);
NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv); NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1567,32 +1567,18 @@ struct MaskLayerUserData : public LayerUserData
struct CSSMaskLayerUserData : public LayerUserData struct CSSMaskLayerUserData : public LayerUserData
{ {
CSSMaskLayerUserData() CSSMaskLayerUserData()
: mImageLayers(nsStyleImageLayers::LayerType::Mask) : mFrame(nullptr)
{ } { }
CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntRect& aBounds) CSSMaskLayerUserData(nsIFrame* aFrame, const nsIntSize& aMaskSize)
: mImageLayers(aFrame->StyleSVGReset()->mMask), : mFrame(aFrame),
mContentRect(aFrame->GetContentRectRelativeToSelf()), mMaskSize(aMaskSize)
mPaddingRect(aFrame->GetPaddingRectRelativeToSelf()), { }
mBorderRect(aFrame->GetRectRelativeToSelf()),
mMarginRect(aFrame->GetMarginRectRelativeToSelf()),
mBounds(aBounds)
{
Hash(aFrame);
}
CSSMaskLayerUserData& operator=(const CSSMaskLayerUserData& aOther) CSSMaskLayerUserData& operator=(const CSSMaskLayerUserData& aOther)
{ {
mImageLayers = aOther.mImageLayers; mFrame = aOther.mFrame;
mMaskSize = aOther.mMaskSize;
mContentRect = aOther.mContentRect;
mPaddingRect = aOther.mPaddingRect;
mBorderRect = aOther.mBorderRect;
mMarginRect = aOther.mMarginRect;
mBounds = aOther.mBounds;
mHash = aOther.mHash;
return *this; return *this;
} }
@ -1600,22 +1586,16 @@ struct CSSMaskLayerUserData : public LayerUserData
bool bool
operator==(const CSSMaskLayerUserData& aOther) const operator==(const CSSMaskLayerUserData& aOther) const
{ {
if (mHash != aOther.mHash) { if (mFrame != aOther.mFrame) {
return false; return false;
} }
if (mImageLayers.mLayers != aOther.mImageLayers.mLayers) { // Even if the frame is valid, check the size of the display item's
return false; // 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
if (!mContentRect.IsEqualEdges(aOther.mContentRect) || // space.
!mPaddingRect.IsEqualEdges(aOther.mPaddingRect) || if (mMaskSize != aOther.mMaskSize) {
!mBorderRect.IsEqualEdges(aOther.mBorderRect) ||
!mMarginRect.IsEqualEdges(aOther.mMarginRect)) {
return false;
}
if (!mBounds.IsEqualEdges(aOther.mBounds)) {
return false; return false;
} }
@ -1623,36 +1603,8 @@ struct CSSMaskLayerUserData : public LayerUserData
} }
private: private:
void Hash(nsIFrame* aFrame) nsIFrame* mFrame;
{ nsIntSize mMaskSize;
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;
}; };
/* /*
@ -3915,8 +3867,9 @@ ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
bool snap; bool snap;
nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap); nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
nsIntRect itemRect = ScaleToOutsidePixels(bounds, snap); nsIntRect itemRect = ScaleToOutsidePixels(bounds, snap);
CSSMaskLayerUserData newUserData(aMaskItem->Frame(), itemRect); CSSMaskLayerUserData newUserData(aMaskItem->Frame(), itemRect.Size());
if (*oldUserData == newUserData) { nsRect dirtyRect;
if (!aMaskItem->IsInvalid(dirtyRect) && *oldUserData == newUserData) {
aLayer->SetMaskLayer(maskLayer); aLayer->SetMaskLayer(maskLayer);
return; return;
} }

View File

@ -7140,10 +7140,13 @@ bool nsDisplayMask::ShouldPaintOnMaskLayer(LayerManager* aManager)
nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::MaskUsage maskUsage;
nsSVGUtils::DetermineMaskUsage(mFrame, mHandleOpacity, maskUsage); nsSVGUtils::DetermineMaskUsage(mFrame, mHandleOpacity, maskUsage);
if (!maskUsage.shouldGenerateMaskLayer || if (!maskUsage.shouldGenerateMaskLayer &&
maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath || !maskUsage.shouldGenerateClipMaskLayer) {
maskUsage.shouldApplyBasicShape || return false;
maskUsage.shouldGenerateClipMaskLayer) { }
if (maskUsage.opacity != 1.0 || maskUsage.shouldApplyClipPath ||
maskUsage.shouldApplyBasicShape) {
return false; return false;
} }
@ -7151,20 +7154,6 @@ bool nsDisplayMask::ShouldPaintOnMaskLayer(LayerManager* aManager)
return false; 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()) { if (gfxPrefs::DrawMaskLayer()) {
return false; return false;
} }

View File

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

View File

@ -6834,6 +6834,82 @@ FlushThrottledStyles(nsIDocument *aDocument, void *aData)
return true; 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 static nsresult
DispatchPointerFromMouseOrTouch(PresShell* aShell, DispatchPointerFromMouseOrTouch(PresShell* aShell,
nsIFrame* aFrame, nsIFrame* aFrame,
@ -6880,8 +6956,10 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
mouseEvent->pressure ? mouseEvent->pressure : 0.5f : mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
0.0f; 0.0f;
event.convertToPointer = mouseEvent->convertToPointer = false; event.convertToPointer = mouseEvent->convertToPointer = false;
PreHandlePointerEventsPreventDefault(&event, aEvent);
aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus, aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
aTargetContent); aTargetContent);
PostHandlePointerEventsPreventDefault(&event, aEvent);
} else if (aEvent->mClass == eTouchEventClass) { } else if (aEvent->mClass == eTouchEventClass) {
WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
// loop over all touches and dispatch pointer events on each touch // loop over all touches and dispatch pointer events on each touch
@ -6926,8 +7004,10 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell,
event.buttons = WidgetMouseEvent::eLeftButtonFlag; event.buttons = WidgetMouseEvent::eLeftButtonFlag;
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
event.convertToPointer = touch->convertToPointer = false; event.convertToPointer = touch->convertToPointer = false;
PreHandlePointerEventsPreventDefault(&event, aEvent);
aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus, aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
aTargetContent); aTargetContent);
PostHandlePointerEventsPreventDefault(&event, aEvent);
} }
} }
return NS_OK; 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-animation.html about:blank
== zero-opacity-text.html about:blank == zero-opacity-text.html about:blank
== negative-w-component.html negative-w-component-ref.html == 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_type;
* typedef ... input_array_type; * typedef ... input_array_type;
* *
* typedef ... coeff_type;
*
* typedef ... result_type; * typedef ... result_type;
* *
* // GetUnit(avalue) must return the correct nsCSSUnit for any * // GetUnit(avalue) must return the correct nsCSSUnit for any
@ -40,17 +42,17 @@ namespace css {
* *
* result_type * result_type
* MergeMultiplicativeL(nsCSSUnit aCalcFunction, * MergeMultiplicativeL(nsCSSUnit aCalcFunction,
* float aValue1, result_type aValue2); * coeff_type aValue1, result_type aValue2);
* *
* result_type * result_type
* MergeMultiplicativeR(nsCSSUnit aCalcFunction, * MergeMultiplicativeR(nsCSSUnit aCalcFunction,
* result_type aValue1, float aValue2); * result_type aValue1, coeff_type aValue2);
* *
* result_type * result_type
* ComputeLeafValue(const input_type& aValue); * ComputeLeafValue(const input_type& aValue);
* *
* float * coeff_type
* ComputeNumber(const input_type& aValue); * ComputeCoefficient(const coeff_type& aValue);
* *
* The CalcOps methods might compute the calc() expression down to a * The CalcOps methods might compute the calc() expression down to a
* number, reduce some parts of it to a number but replicate other * number, reduce some parts of it to a number but replicate other
@ -59,18 +61,21 @@ namespace css {
* values). * values).
* *
* For each leaf in the calc() expression, ComputeCalc will call either * 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). * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
* (The CalcOps in the CSS parser that reduces purely numeric * (The CalcOps in the CSS parser that reduces purely numeric
* expressions in turn calls ComputeCalc on numbers; other ops can * expressions in turn calls ComputeCalc on numbers; other ops can
* presume that expressions in the number positions have already been * presume that expressions in the coefficient positions have already been
* normalized to a single numeric value and derive from * normalized to a single numeric value and derive from, if their coefficient
* NumbersAlreadyNormalizedCalcOps.) * 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: * For non-leaves, one of the Merge functions will be called:
* MergeAdditive for Plus and Minus * MergeAdditive for Plus and Minus
* MergeMultiplicativeL for Times_L (number * value) * MergeMultiplicativeL for Times_L (coeff * value)
* MergeMultiplicativeR for Times_R (value * number) and Divided * MergeMultiplicativeR for Times_R (value * coeff) and Divided
*/ */
template <class CalcOps> template <class CalcOps>
static typename CalcOps::result_type static typename CalcOps::result_type
@ -93,7 +98,7 @@ ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
case eCSSUnit_Calc_Times_L: { case eCSSUnit_Calc_Times_L: {
typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length"); 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); typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs); 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(); typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 2, "unexpected length"); MOZ_ASSERT(arr->Count() == 2, "unexpected length");
typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps); 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); return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
} }
default: { default: {
@ -135,6 +140,7 @@ struct CSSValueInputCalcOps
struct BasicCoordCalcOps struct BasicCoordCalcOps
{ {
typedef nscoord result_type; typedef nscoord result_type;
typedef float coeff_type;
result_type result_type
MergeAdditive(nsCSSUnit aCalcFunction, MergeAdditive(nsCSSUnit aCalcFunction,
@ -150,7 +156,7 @@ struct BasicCoordCalcOps
result_type result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction, MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2) coeff_type aValue1, result_type aValue2)
{ {
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit"); "unexpected unit");
@ -159,7 +165,7 @@ struct BasicCoordCalcOps
result_type result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction, MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2) result_type aValue1, coeff_type aValue2)
{ {
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R || MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R ||
aCalcFunction == eCSSUnit_Calc_Divided, aCalcFunction == eCSSUnit_Calc_Divided,
@ -174,6 +180,7 @@ struct BasicCoordCalcOps
struct BasicFloatCalcOps struct BasicFloatCalcOps
{ {
typedef float result_type; typedef float result_type;
typedef float coeff_type;
result_type result_type
MergeAdditive(nsCSSUnit aCalcFunction, MergeAdditive(nsCSSUnit aCalcFunction,
@ -189,7 +196,7 @@ struct BasicFloatCalcOps
result_type result_type
MergeMultiplicativeL(nsCSSUnit aCalcFunction, MergeMultiplicativeL(nsCSSUnit aCalcFunction,
float aValue1, result_type aValue2) coeff_type aValue1, result_type aValue2)
{ {
MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
"unexpected unit"); "unexpected unit");
@ -198,7 +205,7 @@ struct BasicFloatCalcOps
result_type result_type
MergeMultiplicativeR(nsCSSUnit aCalcFunction, MergeMultiplicativeR(nsCSSUnit aCalcFunction,
result_type aValue1, float aValue2) result_type aValue1, coeff_type aValue2)
{ {
if (aCalcFunction == eCSSUnit_Calc_Times_R) { if (aCalcFunction == eCSSUnit_Calc_Times_R) {
return aValue1 * aValue2; return aValue1 * aValue2;
@ -209,13 +216,55 @@ struct BasicFloatCalcOps
} }
}; };
/** struct BasicIntegerCalcOps
* A ComputeNumber implementation for callers that can assume numbers
* are already normalized (i.e., anything past the parser).
*/
struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
{ {
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"); MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
return aValue.GetFloatValue(); return aValue.GetFloatValue();
@ -240,7 +289,11 @@ struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
* *
* void Append(const char* aString); * void Append(const char* aString);
* void AppendLeafValue(const input_type& aValue); * 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 * Data structures given may or may not have a toplevel eCSSUnit_Calc
* node representing a calc whose toplevel is not min() or max(). * 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("("); aOps.Append("(");
} }
if (unit == eCSSUnit_Calc_Times_L) { if (unit == eCSSUnit_Calc_Times_L) {
aOps.AppendNumber(array->Item(0)); aOps.AppendCoefficient(array->Item(0));
} else { } else {
SerializeCalcInternal(array->Item(0), aOps); SerializeCalcInternal(array->Item(0), aOps);
} }
@ -344,7 +397,7 @@ SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
if (unit == eCSSUnit_Calc_Times_L) { if (unit == eCSSUnit_Calc_Times_L) {
SerializeCalcInternal(array->Item(1), aOps); SerializeCalcInternal(array->Item(1), aOps);
} else { } else {
aOps.AppendNumber(array->Item(1)); aOps.AppendCoefficient(array->Item(1));
} }
if (needParens) { if (needParens) {
aOps.Append(")"); 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 css
} // namespace mozilla } // namespace mozilla

View File

@ -8148,10 +8148,14 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
} }
if ((aVariantMask & VARIANT_CALC) && if ((aVariantMask & VARIANT_CALC) &&
IsCSSTokenCalcFunction(*tk)) { IsCSSTokenCalcFunction(*tk)) {
// calc() currently allows only lengths and percents and number inside it. // calc() currently allows only lengths, percents, numbers, and integers.
// And note that in current implementation, number cannot be mixed with //
// length and percent. // Note that VARIANT_NUMBER can be mixed with VARIANT_LENGTH and
if (!ParseCalc(aValue, aVariantMask & VARIANT_LPN)) { // 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::Error;
} }
return CSSParseResult::Ok; 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 // This can be done without lookahead when we assume that the property
// values cannot themselves be numbers. // values cannot themselves be numbers.
MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); 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; bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk;
mUnitlessLengthQuirk = false; 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 // * If aVariantMask is VARIANT_NUMBER, this function parses the
// <number-multiplicative-expression> production. // <number-multiplicative-expression> production.
// * If aVariantMask does not contain VARIANT_NUMBER, this function // * If aVariantMask does not contain VARIANT_NUMBER, this function
@ -13748,9 +13740,18 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
nsCSSValue *storage = &aValue; nsCSSValue *storage = &aValue;
for (;;) { for (;;) {
uint32_t variantMask; 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; variantMask = VARIANT_NUMBER;
} else { } 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; variantMask = aVariantMask | VARIANT_NUMBER;
} }
if (!ParseCalcTerm(*storage, variantMask)) if (!ParseCalcTerm(*storage, variantMask))
@ -13764,7 +13765,7 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
if (variantMask & VARIANT_NUMBER) { if (variantMask & VARIANT_NUMBER) {
// Simplify the value immediately so we can check for division by // Simplify the value immediately so we can check for division by
// zero. // zero.
ReduceNumberCalcOps ops; mozilla::css::ReduceNumberCalcOps ops;
float number = mozilla::css::ComputeCalc(*storage, ops); float number = mozilla::css::ComputeCalc(*storage, ops);
if (number == 0.0 && afterDivision) if (number == 0.0 && afterDivision)
return false; return false;
@ -13778,9 +13779,15 @@ CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue,
MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1), MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
"unexpected relationship to current storage"); "unexpected relationship to current storage");
nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0); nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
ReduceNumberCalcOps ops; if (variantMask & VARIANT_INTEGER) {
float number = mozilla::css::ComputeCalc(leftValue, ops); mozilla::css::ReduceIntegerCalcOps ops;
leftValue.SetFloatValue(number, eCSSUnit_Number); 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; unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L;
afterDivision = false; afterDivision = false;
} else if (mToken.IsSymbol('/')) { } 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; unit = eCSSUnit_Calc_Divided;
afterDivision = true; afterDivision = true;
} else { } else {
@ -13853,15 +13873,24 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask)
} }
// ... or just a value // ... or just a value
UngetToken(); UngetToken();
// Always pass VARIANT_NUMBER to ParseVariant so that unitless zero if (aVariantMask & VARIANT_INTEGER) {
// always gets picked up // Integers aren't mixed with anything else (see the assert at the
if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) != // top of CSSParserImpl::ParseCalc).
CSSParseResult::Ok) { if (ParseVariant(aValue, aVariantMask, nullptr) != CSSParseResult::Ok) {
return false; return false;
} }
// ...and do the VARIANT_NUMBER check ourselves. } else {
if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) { // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero
return false; // 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 // If we did the value parsing, we need to adjust aVariantMask to
// reflect which option we took (see above). // reflect which option we took (see above).

View File

@ -953,7 +953,7 @@ struct CSSValueSerializeCalcOps {
aValue.AppendToString(mProperty, mResult, mValueSerialization); 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"); MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
aValue.AppendToString(mProperty, mResult, mValueSerialization); aValue.AppendToString(mProperty, mResult, mValueSerialization);

View File

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

View File

@ -478,10 +478,9 @@ PaintMaskSurface(const PaintFramesParams& aParams,
gfxContextMatrixAutoSaveRestore matRestore(maskContext); gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(ThebesMatrix(svgMaskMatrix)); 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, aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
drawRect.TopLeft(), Point(0, 0),
DrawOptions(1.0, compositionOp)); DrawOptions(1.0, compositionOp));
} }
} else { } else {
gfxContextMatrixAutoSaveRestore matRestore(maskContext); gfxContextMatrixAutoSaveRestore matRestore(maskContext);
@ -704,7 +703,8 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::MaskUsage maskUsage;
nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity, nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
maskUsage); maskUsage);
MOZ_ASSERT(maskUsage.shouldGenerateMaskLayer); MOZ_ASSERT(maskUsage.shouldGenerateMaskLayer ||
maskUsage.shouldGenerateClipMaskLayer);
nsIFrame* frame = aParams.frame; nsIFrame* frame = aParams.frame;
if (!ValidateSVGFrame(frame)) { if (!ValidateSVGFrame(frame)) {
@ -716,25 +716,77 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
} }
gfxContext& ctx = aParams.ctx; gfxContext& ctx = aParams.ctx;
gfxContextMatrixAutoSaveRestore matSR(&ctx);
nsIFrame* firstFrame = nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame); nsSVGEffects::GetEffectProperties(firstFrame);
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
bool opacityApplied = !HasNonSVGMask(maskFrames);
DrawResult result = DrawResult::SUCCESS;
nsPoint offsetToBoundingBox; nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace; nsPoint offsetToUserSpace;
SetupContextMatrix(frame, aParams, offsetToBoundingBox, gfxContextMatrixAutoSaveRestore matSR;
offsetToUserSpace, false); DrawTarget* target = ctx.GetDrawTarget();
return PaintMaskSurface(aParams, ctx.GetDrawTarget(), // Paint mask onto ctx.
opacityApplied ? maskUsage.opacity : 1.0, if (maskUsage.shouldGenerateMaskLayer) {
firstFrame->StyleContext(), maskFrames, matSR.SetContext(&ctx);
ctx.CurrentMatrix(), offsetToUserSpace);
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 DrawResult
@ -828,15 +880,15 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox, SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, false); offsetToUserSpace, false);
Matrix clippedMaskTransform; Matrix clipMaskTransform;
RefPtr<SourceSurface> clipMaskSurface = RefPtr<SourceSurface> clipMaskSurface =
clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
&clippedMaskTransform, maskSurface, &clipMaskTransform, maskSurface,
maskTransform, &result); maskTransform, &result);
if (clipMaskSurface) { if (clipMaskSurface) {
maskSurface = clipMaskSurface; maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform; maskTransform = clipMaskTransform;
} else { } else {
// Either entire surface is clipped out, or gfx buffer allocation // Either entire surface is clipped out, or gfx buffer allocation
// failure in nsSVGClipPathFrame::GetClipMask. // 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'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
GYP_DIRS['signalingtest'].non_unified_sources += signaling_non_unified_sources 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 #define GTEST_HAS_RTTI 0
#include "gtest/gtest.h" #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/SdpMediaSection.h"
#include "signaling/src/sdp/SipccSdpParser.h" #include "signaling/src/sdp/SipccSdpParser.h"
@ -30,19 +25,17 @@
#include "signaling/src/jsep/JsepSessionImpl.h" #include "signaling/src/jsep/JsepSessionImpl.h"
#include "signaling/src/jsep/JsepTrack.h" #include "signaling/src/jsep/JsepTrack.h"
#include "mtransport_test_utils.h"
#include "FakeIPC.h"
#include "FakeIPC.cpp"
#include "TestHarness.h"
namespace mozilla { namespace mozilla {
static std::string kAEqualsCandidate("a=candidate:"); static std::string kAEqualsCandidate("a=candidate:");
const static size_t kNumCandidatesPerComponent = 3; const static size_t kNumCandidatesPerComponent = 3;
class JsepSessionTestBase : public ::testing::Test class JsepSessionTestBase : public ::testing::Test
{ {
public:
static void SetUpTestCase() {
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();
}
}; };
class FakeUuidGenerator : public mozilla::JsepUuidGenerator class FakeUuidGenerator : public mozilla::JsepUuidGenerator
@ -4220,16 +4213,3 @@ TEST_F(JsepSessionTest, TestNonDefaultProtocol)
} }
} // namespace mozilla } // 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 #define GTEST_HAS_RTTI 0
#include "gtest/gtest.h" #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/jsep/JsepTrack.h"
#include "signaling/src/sdp/SipccSdp.h" #include "signaling/src/sdp/SipccSdp.h"
#include "signaling/src/sdp/SdpHelper.h" #include "signaling/src/sdp/SdpHelper.h"
#include "mtransport_test_utils.h"
#include "FakeIPC.h"
#include "FakeIPC.cpp"
#include "TestHarness.h"
namespace mozilla { namespace mozilla {
class JsepTrackTest : public ::testing::Test class JsepTrackTest : public ::testing::Test
@ -1256,14 +1243,3 @@ TEST_F(JsepTrackTest, NonDefaultOpusParameters)
} }
} // namespace mozilla } // 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 += [ SOURCES += [
'jsep_session_unittest.cpp',
'jsep_track_unittest.cpp',
'sdp_unittests.cpp', 'sdp_unittests.cpp',
] ]

View File

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

View File

@ -13,7 +13,7 @@ import org.mozilla.gecko.media.Sample;
interface ICodec { interface ICodec {
void setCallbacks(in ICodecCallbacks callbacks); 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 start();
oneway void stop(); oneway void stop();
oneway void flush(); oneway void flush();

View File

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

View File

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

View File

@ -5,6 +5,8 @@
package org.mozilla.gecko.media; package org.mozilla.gecko.media;
import android.media.MediaCodec.BufferInfo; import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodec.CryptoInfo;
import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.Handler; import android.os.Handler;
import android.view.Surface; import android.view.Surface;
@ -22,7 +24,7 @@ public interface AsyncCodec {
} }
public abstract void setCallbacks(Callbacks callbacks, Handler handler); 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 start();
public abstract void stop(); public abstract void stop();
public abstract void flush(); public abstract void flush();
@ -30,5 +32,6 @@ public interface AsyncCodec {
public abstract ByteBuffer getInputBuffer(int index); public abstract ByteBuffer getInputBuffer(int index);
public abstract ByteBuffer getOutputBuffer(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 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); 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.MediaCodec;
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
import android.media.MediaCodecList; import android.media.MediaCodecList;
import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
@ -167,6 +168,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
Sample sample = mInputSamples.poll(); Sample sample = mInputSamples.poll();
long pts = sample.info.presentationTimeUs; long pts = sample.info.presentationTimeUs;
int flags = sample.info.flags; int flags = sample.info.flags;
MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
if (!sample.isEOS() && sample.buffer != null) { if (!sample.isEOS() && sample.buffer != null) {
len = sample.info.size; len = sample.info.size;
ByteBuffer buf = mCodec.getInputBuffer(index); ByteBuffer buf = mCodec.getInputBuffer(index);
@ -180,7 +182,12 @@ import java.util.concurrent.ConcurrentLinkedQueue;
} }
mSamplePool.recycleInput(sample); 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 @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) { if (mCallbacks == null) {
Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()"); Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()");
return false; return false;
@ -236,8 +246,15 @@ import java.util.concurrent.ConcurrentLinkedQueue;
try { try {
AsyncCodec codec = AsyncCodecFactory.create(codecName); 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.setCallbacks(new Callbacks(mCallbacks), null);
codec.configure(fmt, surface, flags); codec.configure(fmt, surface, crypto, flags);
mCodec = codec; mCodec = codec;
mInputProcessor = new InputProcessor(); mInputProcessor = new InputProcessor();
mSamplePool = new SamplePool(codecName); mSamplePool = new SamplePool(codecName);

View File

@ -28,6 +28,7 @@ public final class CodecProxy {
private FormatParam mFormat; private FormatParam mFormat;
private Surface mOutputSurface; private Surface mOutputSurface;
private CallbacksForwarder mCallbacks; private CallbacksForwarder mCallbacks;
private String mRemoteDrmStubId;
public interface Callbacks { public interface Callbacks {
void onInputExhausted(); void onInputExhausted();
@ -82,24 +83,31 @@ public final class CodecProxy {
} }
@WrapForJNI @WrapForJNI
public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) { public static CodecProxy create(MediaFormat format,
return RemoteManager.getInstance().createCodec(format, surface, callbacks); Surface surface,
Callbacks callbacks,
String drmStubId) {
return RemoteManager.getInstance().createCodec(format, surface, callbacks, drmStubId);
} }
public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) { public static CodecProxy createCodecProxy(MediaFormat format,
return new CodecProxy(format, surface, callbacks); 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); mFormat = new FormatParam(format);
mOutputSurface = surface; mOutputSurface = surface;
mRemoteDrmStubId = drmStubId;
mCallbacks = new CallbacksForwarder(callbacks); mCallbacks = new CallbacksForwarder(callbacks);
} }
boolean init(ICodec remote) { boolean init(ICodec remote) {
try { try {
remote.setCallbacks(mCallbacks); remote.setCallbacks(mCallbacks);
remote.configure(mFormat, mOutputSurface, 0); remote.configure(mFormat, mOutputSurface, 0, mRemoteDrmStubId);
remote.start(); remote.start();
} catch (RemoteException e) { } catch (RemoteException e) {
e.printStackTrace(); e.printStackTrace();
@ -128,7 +136,6 @@ public final class CodecProxy {
Log.e(LOGTAG, "cannot send input to an ended codec"); Log.e(LOGTAG, "cannot send input to an ended codec");
return false; return false;
} }
try { try {
Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ? Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ?
Sample.EOS : mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo); 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.AsyncTask;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.media.DeniedByServerException;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaCryptoException; import android.media.MediaCryptoException;
import android.media.MediaDrm; import android.media.MediaDrm;
import android.media.MediaDrmException; import android.media.MediaDrmException;
import android.media.NotProvisionedException;
import android.util.Log; import android.util.Log;
public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm { 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 INVALID_SESSION_ID = "Invalid";
private static final String WIDEVINE_KEY_SYSTEM = "com.widevine.alpha"; private static final String WIDEVINE_KEY_SYSTEM = "com.widevine.alpha";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@ -98,7 +100,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
} }
GeckoMediaDrmBridgeV21(String keySystem) throws Exception { GeckoMediaDrmBridgeV21(String keySystem) throws Exception {
if (DEBUG) Log.d(LOGTAG, "GeckoMediaDrmBridgeV21()"); LOGTAG = getClass().getSimpleName();
if (DEBUG) Log.d(LOGTAG, "GeckoMediaDrmBridgeV21 ctor");
mProvisioningPromiseId = 0; mProvisioningPromiseId = 0;
mSessionIds = new HashSet<ByteBuffer>(); mSessionIds = new HashSet<ByteBuffer>();
@ -142,7 +145,6 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
} }
ByteBuffer sessionId = null; ByteBuffer sessionId = null;
String strSessionId = null;
try { try {
boolean hasMediaCrypto = ensureMediaCryptoCreated(); boolean hasMediaCrypto = ensureMediaCryptoCreated();
if (!hasMediaCrypto) { if (!hasMediaCrypto) {
@ -170,9 +172,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
LICENSE_REQUEST_INITIAL, LICENSE_REQUEST_INITIAL,
request.getData()); request.getData());
mSessionMIMETypes.put(sessionId, initDataType); mSessionMIMETypes.put(sessionId, initDataType);
strSessionId = new String(sessionId.array());
mSessionIds.add(sessionId); 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) { } catch (android.media.NotProvisionedException e) {
if (DEBUG) Log.d(LOGTAG, "Device not provisioned:" + e.getMessage()); if (DEBUG) Log.d(LOGTAG, "Device not provisioned:" + e.getMessage());
if (sessionId != null) { if (sessionId != null) {
@ -218,18 +219,10 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + sessionId); if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + sessionId);
onSessionUpdated(promiseId, session.array()); onSessionUpdated(promiseId, session.array());
return; return;
} catch (android.media.NotProvisionedException e) { } catch (final NotProvisionedException | DeniedByServerException | IllegalStateException e) {
if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:" + e.getMessage()); if (DEBUG) Log.d(LOGTAG, "Failed to provide key response:", e);
onSessionError(session.array(), "Got NotProvisionedException."); onSessionError(session.array(), "Got exception during updateSession.");
onRejectPromise(promiseId, "Not provisioned during updateSession."); onRejectPromise(promiseId, "Got exception 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.");
} }
release(); release();
return; return;
@ -371,7 +364,6 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
} }
// On L, these events are treated as exceptions and handled correspondingly. // On L, these events are treated as exceptions and handled correspondingly.
// Leaving this code block for logging message. // Leaving this code block for logging message.
String sessionId = new String(session.array());
switch (event) { switch (event) {
case MediaDrm.EVENT_PROVISION_REQUIRED: case MediaDrm.EVENT_PROVISION_REQUIRED:
if (DEBUG) Log.d(LOGTAG, "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. // No need to handle here if we're not in privacy mode.
break; break;
case MediaDrm.EVENT_KEY_EXPIRED: 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; break;
case MediaDrm.EVENT_VENDOR_DEFINED: 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; break;
default: default:
if (DEBUG) Log.d(LOGTAG, "Invalid DRM event " + event); 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 (mCryptoSessionId == null) {
if (DEBUG) Log.d(LOGTAG, "Session doesn't exist because media crypto session is not created."); if (DEBUG) Log.d(LOGTAG, "Session doesn't exist because media crypto session is not created.");
return false; return false;
@ -592,9 +584,8 @@ public class GeckoMediaDrmBridgeV21 implements GeckoMediaDrm {
if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) { if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
final byte [] cryptoSessionId = mCryptoSessionId.array(); final byte [] cryptoSessionId = mCryptoSessionId.array();
mCrypto = new MediaCrypto(mSchemeUUID, cryptoSessionId); mCrypto = new MediaCrypto(mSchemeUUID, cryptoSessionId);
String strCryptoSessionId = new String(cryptoSessionId);
mSessionIds.add(mCryptoSessionId); 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; return true;
} else { } else {
if (DEBUG) Log.d(LOGTAG, "Cannot create MediaCrypto for unsupported scheme."); if (DEBUG) Log.d(LOGTAG, "Cannot create MediaCrypto for unsupported scheme.");

View File

@ -5,14 +5,18 @@
package org.mozilla.gecko.media; package org.mozilla.gecko.media;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.DeniedByServerException;
import android.media.NotProvisionedException;
import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.M;
import android.media.MediaDrm; import android.media.MediaDrm;
import android.util.Log; import android.util.Log;
import java.lang.IllegalStateException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List; import java.util.List;
public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 { public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 {
private static final String LOGTAG = "GeckoMediaDrmBridgeV23";
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
GeckoMediaDrmBridgeV23(String keySystem) throws Exception { GeckoMediaDrmBridgeV23(String keySystem) throws Exception {
@ -39,6 +43,43 @@ public class GeckoMediaDrmBridgeV23 extends GeckoMediaDrmBridgeV21 {
keyStatus.getStatusCode()); keyStatus.getStatusCode());
} }
onSessionBatchedKeyChanged(sessionId, keyInfos); 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; package org.mozilla.gecko.media;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
@ -294,10 +295,10 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
} }
@Override @Override
public void configure(MediaFormat format, Surface surface, int flags) { public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
assertCallbacks(); assertCallbacks();
mCodec.configure(format, surface, null, flags); mCodec.configure(format, surface, crypto, flags);
} }
private void assertCallbacks() { private void assertCallbacks() {
@ -336,6 +337,28 @@ final class JellyBeanAsyncCodec implements AsyncCodec {
mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS); 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 @Override
public final void releaseOutputBuffer(int index, boolean render) { public final void releaseOutputBuffer(int index, boolean render) {
assertCallbacks(); assertCallbacks();

View File

@ -39,10 +39,12 @@ public final class MediaDrmProxy {
@WrapForJNI @WrapForJNI
private static final String OPUS = "audio/opus"; 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. // A flag to avoid using the native object that has been destroyed.
private boolean mDestroyed; private boolean mDestroyed;
private GeckoMediaDrm mImpl; private GeckoMediaDrm mImpl;
public static ArrayList<MediaDrmProxy> mProxyList = new ArrayList<MediaDrmProxy>(); private String mDrmStubId;
private static boolean isSystemSupported() { private static boolean isSystemSupported() {
// Support versions >= LOLLIPOP // Support versions >= LOLLIPOP
@ -250,19 +252,26 @@ public final class MediaDrmProxy {
public static MediaDrmProxy create(String keySystem, public static MediaDrmProxy create(String keySystem,
Callbacks nativeCallbacks, Callbacks nativeCallbacks,
boolean isRemote) { boolean isRemote) {
// TODO: Will implement {Local,Remote}MediaDrmBridge instantiation by MediaDrmProxy proxy = new MediaDrmProxy(keySystem, nativeCallbacks, isRemote);
// '''isRemote''' flag in Bug 1307818.
MediaDrmProxy proxy = new MediaDrmProxy(keySystem, nativeCallbacks);
return proxy; return proxy;
} }
MediaDrmProxy(String keySystem, Callbacks nativeCallbacks) { MediaDrmProxy(String keySystem, Callbacks nativeCallbacks, boolean isRemote) {
if (DEBUG) Log.d(LOGTAG, "Constructing MediaDrmProxy"); if (DEBUG) Log.d(LOGTAG, "Constructing MediaDrmProxy");
// TODO: Bug 1306185 will implement the LocalMediaDrmBridge as an impl try {
// of GeckoMediaDrm for in-process decoding mode. mDrmStubId = UUID.randomUUID().toString();
//mImpl = new LocalMediaDrmBridge(keySystem); if (isRemote) {
mImpl.setCallbacks(new MediaDrmProxyCallbacks(this, nativeCallbacks)); IMediaDrmBridge remoteBridge =
mProxyList.add(this); 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 @WrapForJNI
@ -289,6 +298,24 @@ public final class MediaDrmProxy {
mImpl.closeSession(promiseId, sessionId); 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. @WrapForJNI // Called when natvie object is destroyed.
private void destroy() { private void destroy() {
if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed."); if (DEBUG) Log.d(LOGTAG, "destroy!! Native object is destroyed.");
@ -301,7 +328,11 @@ public final class MediaDrmProxy {
private void release() { private void release() {
if (DEBUG) Log.d(LOGTAG, "release"); if (DEBUG) Log.d(LOGTAG, "release");
mProxyList.remove(this); sProxyList.remove(this);
mImpl.release(); 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, public synchronized CodecProxy createCodec(MediaFormat format,
Surface surface, Surface surface,
CodecProxy.Callbacks callbacks) { CodecProxy.Callbacks callbacks,
String drmStubId) {
if (mRemote == null) { if (mRemote == null) {
if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize"); if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
return null; return null;
} }
try { try {
ICodec remote = mRemote.createCodec(); ICodec remote = mRemote.createCodec();
CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks); CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks, drmStubId);
if (proxy.init(remote)) { if (proxy.init(remote)) {
mProxies.add(proxy); mProxies.add(proxy);
return proxy; return proxy;

View File

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

View File

@ -279,6 +279,10 @@ contextmenu.mute=Mute
contextmenu.unmute=Unmute contextmenu.unmute=Unmute
contextmenu.saveVideo=Save Video contextmenu.saveVideo=Save Video
contextmenu.saveAudio=Save Audio 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 contextmenu.addToContacts=Add to Contacts
# LOCALIZATION NOTE (contextmenu.sendToDevice): # LOCALIZATION NOTE (contextmenu.sendToDevice):
# The label that will be used in the contextmenu and the pageaction # The label that will be used in the contextmenu and the pageaction

View File

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

View File

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

View File

@ -1535,10 +1535,6 @@ VARIABLES = {
"""List of manifest files defining marionette-unit tests. """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, 'MARIONETTE_WEBAPI_MANIFESTS': (ManifestparserManifestList, list,
"""List of manifest files defining marionette-webapi tests. """List of manifest files defining marionette-webapi tests.
"""), """),

View File

@ -135,8 +135,6 @@ ALLOWED_XPCOM_GLUE = {
('test_unlock_notify', 'storage/test'), ('test_unlock_notify', 'storage/test'),
('test_IHistory', 'toolkit/components/places/tests/cpp'), ('test_IHistory', 'toolkit/components/places/tests/cpp'),
('testcrasher', 'toolkit/crashreporter/test'), ('testcrasher', 'toolkit/crashreporter/test'),
('jsep_session_unittest', 'media/webrtc/signaling/test'),
('jsep_track_unittest', 'media/webrtc/signaling/test'),
('mediaconduit_unittests', 'media/webrtc/signaling/test'), ('mediaconduit_unittests', 'media/webrtc/signaling/test'),
('mediapipeline_unittest', 'media/webrtc/signaling/test'), ('mediapipeline_unittest', 'media/webrtc/signaling/test'),
('sdp_file_parser', 'media/webrtc/signaling/fuzztest'), ('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 # TODO(ato): make packaging work as for other test suites
MARIONETTE=('marionette', 'marionette', '.', False), MARIONETTE=('marionette', 'marionette', '.', False),
MARIONETTE_UNIT=('marionette', 'marionette', '.', False), MARIONETTE_UNIT=('marionette', 'marionette', '.', False),
MARIONETTE_UPDATE=('marionette', 'marionette', '.', False),
MARIONETTE_WEBAPI=('marionette', 'marionette', '.', False), MARIONETTE_WEBAPI=('marionette', 'marionette', '.', False),
METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True), METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True),

View File

@ -22,6 +22,7 @@ import struct
import os import os
import re import re
import subprocess import subprocess
import buildconfig
from collections import OrderedDict from collections import OrderedDict
# Regular expressions for unifying install.rdf # Regular expressions for unifying install.rdf
@ -80,7 +81,8 @@ class UnifiedExecutableFile(BaseFile):
os.close(fd) os.close(fd)
tmpfiles.append(f) tmpfiles.append(f)
e.copy(f, skip_if_older=False) 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: finally:
for f in tmpfiles: for f in tmpfiles:
os.unlink(f) os.unlink(f)

View File

@ -44,3 +44,27 @@ macosx64/opt:
script: "mozharness/scripts/fx_desktop_build.py" script: "mozharness/scripts/fx_desktop_build.py"
secrets: true secrets: true
tooltool-downloads: internal 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