Merge mozilla-central to mozilla-inbound

This commit is contained in:
Iris Hsiao 2016-09-26 19:08:19 +08:00
Родитель 2c4a3e9768 767e1e9b11
Коммит a09b58856d
130 изменённых файлов: 13108 добавлений и 12028 удалений

Просмотреть файл

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1472845813000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1474644720000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -12,12 +12,6 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1229" id="/^(.*@(unblocker\.yt|sparpilot\.com))|axtara@axtara\.com$/">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i640" id="jid0-l9BxpNUhx1UUgRfKigWzSfrZqAc@jetpack">
<versionRange minVersion="0" maxVersion="*" severity="3">
@ -158,6 +152,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i1229" id="/^(.*@(unblocker\.yt|sparpilot\.com))|(axtara@axtara\.com)$/">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i5" id="support@daemon-tools.cc">
<versionRange minVersion=" " maxVersion="1.0.0.5">
@ -3612,6 +3612,18 @@
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="21.0.0.242" maxVersion="22.0.0.192" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem os="Linux" blockID="p1272">
<match name="filename" exp="libflashplayer\.so" /> <versionRange minVersion="11.2.202.626" maxVersion="11.2.202.632" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem blockID="p1273">
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="18.0.0.360" maxVersion="18.0.0.366" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem blockID="p1274">
<match name="filename" exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="22.0.0.192" maxVersion="22.0.0.211" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
</pluginItems>
<gfxItems>

Просмотреть файл

@ -1361,7 +1361,11 @@ pref("media.gmp.trial-create.enabled", true);
#ifdef MOZ_ADOBE_EME
pref("media.gmp-eme-adobe.visible", true);
pref("media.gmp-eme-adobe.enabled", true);
// When Adobe EME is enabled in the build system, we don't actually enable
// the plugin by default, so that it doesn't download and install by default.
// When Adobe EME is first used, Firefox will prompt the user to enable it,
// and then download the CDM.
pref("media.gmp-eme-adobe.enabled", false);
#endif
#ifdef MOZ_WIDEVINE_EME

Просмотреть файл

@ -345,13 +345,6 @@ var gFxAccounts = {
},
openAccountsPage: function (action, urlParams={}) {
// An entrypoint param is used for server-side metrics. If the current tab
// is UITour, assume that it initiated the call to this method and override
// the entrypoint accordingly.
if (UITour.tourBrowsersByWindow.get(window) &&
UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)) {
urlParams.entrypoint = "uitour";
}
let params = new URLSearchParams();
if (action) {
params.set("action", action);

Просмотреть файл

@ -290,11 +290,6 @@ var gSyncUI = {
openSetup: function SUI_openSetup(wizardType, entryPoint = "syncbutton") {
if (this.weaveService.fxAccountsEnabled) {
// If the user is also in an uitour, set the entrypoint to `uitour`
if (UITour.tourBrowsersByWindow.get(window) &&
UITour.tourBrowsersByWindow.get(window).has(gBrowser.selectedBrowser)) {
entryPoint = "uitour";
}
this.openPrefs(entryPoint);
} else {
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");

Просмотреть файл

@ -119,9 +119,6 @@ function* asyncCleanup() {
// When Sync is not setup.
add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs"));
add_task(asyncCleanup);
// Test that uitour is in progress, the entrypoint is `uitour` and not `menupanel`
add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "uitour"));
add_task(asyncCleanup);
// When Sync is configured in a "needs reauthentication" state.
add_task(function* () {

Просмотреть файл

@ -13,4 +13,5 @@ EXTRA_COMPONENTS += [
DIRS += ['schemas']
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']

Просмотреть файл

@ -82,6 +82,7 @@
{
"namespace": "commands",
"description": "Use the commands API to add keyboard shortcuts that trigger actions in your extension, for example, an action to open the browser action or send a command to the xtension.",
"permissions": ["manifest:commands"],
"types": [
{
"id": "Command",

Просмотреть файл

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
tags = webextensions
[test_ext_all_apis.html]

Просмотреть файл

@ -0,0 +1,75 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
</head>
<body>
<script>
"use strict";
/* exported expectedContentApisTargetSpecific, expectedBackgroundApisTargetSpecific */
let expectedContentApisTargetSpecific = [
];
let expectedBackgroundApisTargetSpecific = [
"tabs.MutedInfoReason",
"tabs.TAB_ID_NONE",
"tabs.TabStatus",
"tabs.WindowType",
"tabs.ZoomSettingsMode",
"tabs.ZoomSettingsScope",
"tabs.connect",
"tabs.create",
"tabs.detectLanguage",
"tabs.duplicate",
"tabs.executeScript",
"tabs.get",
"tabs.getCurrent",
"tabs.getZoom",
"tabs.getZoomSettings",
"tabs.highlight",
"tabs.insertCSS",
"tabs.move",
"tabs.onActivated",
"tabs.onAttached",
"tabs.onCreated",
"tabs.onDetached",
"tabs.onHighlighted",
"tabs.onMoved",
"tabs.onRemoved",
"tabs.onReplaced",
"tabs.onUpdated",
"tabs.onZoomChange",
"tabs.query",
"tabs.reload",
"tabs.remove",
"tabs.removeCSS",
"tabs.sendMessage",
"tabs.setZoom",
"tabs.setZoomSettings",
"tabs.update",
"windows.CreateType",
"windows.WINDOW_ID_CURRENT",
"windows.WINDOW_ID_NONE",
"windows.WindowState",
"windows.WindowType",
"windows.create",
"windows.get",
"windows.getAll",
"windows.getCurrent",
"windows.getLastFocused",
"windows.onCreated",
"windows.onFocusChanged",
"windows.onRemoved",
"windows.remove",
"windows.update",
];
</script>
<script src="test_ext_all_apis.js"></script>
</body>
</html>

Просмотреть файл

@ -132,8 +132,7 @@
</columns>
<rows>
<row align="center">
<label accesskey="&syncDeviceName.accesskey;"
control="syncComputerName">
<label control="syncComputerName">
&syncDeviceName.label;
</label>
<textbox id="syncComputerName"/>
@ -185,9 +184,9 @@
</vbox>
<vbox flex="1">
<label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
<hbox class="fxaAccountBoxButtons" align="center">
<button id="noFxaSignUp" label="&signedOut.accountBox.create;"></button>
<button id="noFxaSignIn" label="&signedOut.accountBox.signin;"></button>
<hbox class="fxaAccountBoxButtons">
<button id="noFxaSignUp" label="&signedOut.accountBox.create;" accesskey="&signedOut.accountBox.create.accesskey;"></button>
<button id="noFxaSignIn" label="&signedOut.accountBox.signin;" accesskey="&signedOut.accountBox.signin.accesskey;"></button>
</hbox>
</vbox>
</hbox>
@ -230,11 +229,12 @@
<vbox flex="1" pack="center">
<label id="fxaDisplayName" hidden="true"/>
<label id="fxaEmailAddress1"/>
<hbox class="fxaAccountBoxButtons" align="center">
<button id="fxaUnlinkButton" label="&disconnect.label;"/>
<label id="verifiedManage" class="text-link"
<hbox class="fxaAccountBoxButtons">
<button id="fxaUnlinkButton" label="&disconnect.label;" accesskey="&disconnect.label.accesskey;"/>
<html:a id="verifiedManage" target="_blank"
accesskey="&verifiedManage.label.accesskey;"
onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
-->&verifiedManage.label;</label>
-->&verifiedManage.label;</html:a>
</hbox>
</vbox>
</hbox>
@ -253,9 +253,9 @@
&signedInUnverified.aftername.label;
</description>
</hbox>
<hbox class="fxaAccountBoxButtons" align="center">
<button id="verifyFxaAccount">&verify.label;</button>
<button id="unverifiedUnlinkFxaAccount">&forget.label;</button>
<hbox class="fxaAccountBoxButtons">
<button id="verifyFxaAccount" accesskey="&verify.label.accesskey;">&verify.label;</button>
<button id="unverifiedUnlinkFxaAccount" accesskey="&forget.label.accesskey;">&forget.label;</button>
</hbox>
</vbox>
</hbox>
@ -274,9 +274,9 @@
&signedInLoginFailure.aftername.label;
</description>
</hbox>
<hbox class="fxaAccountBoxButtons" align="center">
<button id="rejectReSignIn">&signIn.label;</button>
<button id="rejectUnlinkFxaAccount">&forget.label;</button>
<hbox class="fxaAccountBoxButtons">
<button id="rejectReSignIn" accessky="&signIn.label.accesskey;">&signIn.label;</button>
<button id="rejectUnlinkFxaAccount" accesskey="&rejectUnlinkFxaAccount.forget.label.accesskey;">&forget.label;</button>
</hbox>
</vbox>
</hbox>
@ -317,8 +317,7 @@
</hbox>
<groupbox>
<caption>
<label accesskey="&syncDeviceName.accesskey;"
control="fxaSyncComputerName">
<label control="fxaSyncComputerName">
&fxaSyncDeviceName.label;
</label>
</caption>
@ -326,12 +325,15 @@
<textbox id="fxaSyncComputerName" disabled="true"/>
<hbox>
<button id="fxaChangeDeviceName"
label="&changeSyncDeviceName.label;"/>
label="&changeSyncDeviceName.label;"
accesskey="&changeSyncDeviceName.label.accesskey;"/>
<button id="fxaCancelChangeDeviceName"
label="&cancelChangeSyncDeviceName.label;"
accesskey="&cancelChangeSyncDeviceName.label.accesskey;"
hidden="true"/>
<button id="fxaSaveChangeDeviceName"
label="&saveChangeSyncDeviceName.label;"
accesskey="&saveChangeSyncDeviceName.label.accesskey;"
hidden="true"/>
</hbox>
</hbox>

Просмотреть файл

@ -38,11 +38,13 @@
<!-- Device Settings -->
<!ENTITY syncDeviceName.label "Device Name:">
<!ENTITY syncDeviceName.accesskey "c">
<!ENTITY fxaSyncDeviceName.label "Device Name">
<!ENTITY changeSyncDeviceName.label "Change Device Name…">
<!ENTITY changeSyncDeviceName.label.accesskey "h">
<!ENTITY cancelChangeSyncDeviceName.label "Cancel">
<!ENTITY cancelChangeSyncDeviceName.label.accesskey "n">
<!ENTITY saveChangeSyncDeviceName.label "Save">
<!ENTITY saveChangeSyncDeviceName.label.accesskey "v">
<!ENTITY unlinkDevice.label "Unlink This Device">
<!-- Footer stuff -->
@ -71,11 +73,17 @@ both, to better adapt this sentence to their language.
<!ENTITY notSignedIn.label "You are not signed in.">
<!ENTITY signIn.label "Sign in">
<!ENTITY signIn.label.accesskey "g">
<!ENTITY profilePicture.tooltip "Change profile picture">
<!ENTITY verifiedManage.label "Manage Account">
<!ENTITY verifiedManage.label.accesskey "o">
<!ENTITY disconnect.label "Disconnect…">
<!ENTITY disconnect.label.accesskey "D">
<!ENTITY verify.label "Verify Email">
<!ENTITY verify.label.accesskey "V">
<!ENTITY forget.label "Forget this Email">
<!ENTITY forget.label.accesskey "F">
<!ENTITY rejectUnlinkFxaAccount.forget.label.accesskey "e">
<!ENTITY welcome.description "Access your tabs, bookmarks, passwords and more wherever you use &brandShortName;.">
<!ENTITY welcome.signIn.label "Sign In">
@ -87,7 +95,9 @@ both, to better adapt this sentence to their language.
<!ENTITY signedOut.description "Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.">
<!ENTITY signedOut.accountBox.title "Connect with a &syncBrand.fxAccount.label;">
<!ENTITY signedOut.accountBox.create "Create Account">
<!ENTITY signedOut.accountBox.create.accesskey "C">
<!ENTITY signedOut.accountBox.signin "Sign In">
<!ENTITY signedOut.accountBox.signin.accesskey "I">
<!ENTITY signedIn.engines.label "Sync across all devices">

Просмотреть файл

@ -353,6 +353,10 @@ this.UnsubmittedCrashHandler = {
Services.prefs.getBranch("browser.crashReports.unsubmittedCheck.");
},
get enabled() {
return this.prefs.getBoolPref("enabled");
},
// showingNotification is set to true once a notification
// is successfully shown, and then set back to false if
// the notification is dismissed by an action by the user.
@ -371,9 +375,12 @@ this.UnsubmittedCrashHandler = {
this.initialized = true;
let shouldCheck = this.prefs.getBoolPref("enabled");
if (shouldCheck) {
// UnsubmittedCrashHandler can be initialized but still be disabled.
// This is intentional, as this makes simulating UnsubmittedCrashHandler's
// reactions to browser startup and shutdown easier in test automation.
//
// UnsubmittedCrashHandler, when initialized but not enabled, is inert.
if (this.enabled) {
if (this.prefs.prefHasUserValue("suppressUntilDate")) {
if (this.prefs.getCharPref("suppressUntilDate") > this.dateString()) {
// We'll be suppressing any notifications until after suppressedDate,
@ -400,6 +407,10 @@ this.UnsubmittedCrashHandler = {
this.initialized = false;
if (!this.enabled) {
return;
}
if (this.suppressed) {
this.suppressed = false;
// No need to do any more clean-up, since we were suppressed.

Просмотреть файл

@ -198,11 +198,20 @@ add_task(function* setup() {
let oldServerURL = env.get("MOZ_CRASHREPORTER_URL");
env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
// nsBrowserGlue starts up UnsubmittedCrashHandler automatically
// so at this point, it is initialized. It's possible that it
// was initialized, but is preffed off, so it's inert, so we
// shut it down, make sure it's preffed on, and then restart it.
// Note that making the component initialize even when it's
// disabled is an intentional choice, as this allows for easier
// simulation of startup and shutdown.
UnsubmittedCrashHandler.uninit();
yield SpecialPowers.pushPrefEnv({
set: [
["browser.crashReports.unsubmittedCheck.enabled", true],
],
});
UnsubmittedCrashHandler.init();
registerCleanupFunction(function() {
gNotificationBox = null;

Просмотреть файл

@ -409,8 +409,12 @@ function prompt(aBrowser, aRequest) {
if ((!audioDevices.length || micPerm) && (!videoDevices.length || camPerm)) {
// All permissions we were about to request are already persistently set.
let allowedDevices = [];
if (videoDevices.length && camPerm == perms.ALLOW_ACTION)
if (videoDevices.length && camPerm == perms.ALLOW_ACTION) {
allowedDevices.push(videoDevices[0].deviceIndex);
let perms = Services.perms;
perms.add(uri, "MediaManagerVideo", perms.ALLOW_ACTION,
perms.EXPIRE_SESSION);
}
if (audioDevices.length && micPerm == perms.ALLOW_ACTION)
allowedDevices.push(audioDevices[0].deviceIndex);

Просмотреть файл

@ -497,6 +497,8 @@ description > html|a {
.fxaAccountBoxButtons {
margin-bottom: 0 !important;
margin-top: 11px;
display: flex;
align-items: center;
}
.fxaAccountBoxButtons > * {

Просмотреть файл

@ -33,7 +33,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle:2.1.3'
classpath('com.stanfy.spoon:spoon-gradle-plugin:1.0.4') {
// Without these, we get errors linting.
exclude module: 'guava'

Просмотреть файл

@ -423,6 +423,16 @@ InspectorPanel.prototype = {
return this._InspectorTabPanel;
},
/**
* Check if the inspector should use the landscape mode.
*
* @return {Boolean} true if the inspector should be in landscape mode.
*/
useLandscapeMode: function () {
let { clientWidth } = this.panelDoc.getElementById("inspector-splitter-box");
return clientWidth > PORTRAIT_MODE_WIDTH;
},
/**
* Build Splitter located between the main and side area of
* the Inspector panel.
@ -445,7 +455,8 @@ InspectorPanel.prototype = {
}),
endPanel: this.InspectorTabPanel({
id: "inspector-sidebar-container"
})
}),
vert: this.useLandscapeMode(),
});
this._splitter = this.ReactDOM.render(splitter,
@ -473,9 +484,8 @@ InspectorPanel.prototype = {
* to `horizontal` to support portrait view.
*/
onPanelWindowResize: function () {
let box = this.panelDoc.getElementById("inspector-splitter-box");
this._splitter.setState({
vert: (box.clientWidth > PORTRAIT_MODE_WIDTH)
vert: this.useLandscapeMode(),
});
},

Просмотреть файл

@ -18,216 +18,214 @@
<?xml-stylesheet href="resource://devtools/client/inspector/components/inspector-tab-panel.css" type="text/css"?>
<?xml-stylesheet href="resource://devtools/client/shared/components/splitter/split-box.css" type="text/css"?>
<!DOCTYPE window>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:html="http://www.w3.org/1999/xhtml">
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"></script>
</head>
<body class="theme-body devtools-monospace" role="application">
<html:div class="inspector-responsive-container theme-body inspector">
<div class="inspector-responsive-container theme-body inspector">
<!-- Main Panel Content -->
<html:div id="inspector-main-content" class="devtools-main-content">
<html:div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true"
<div id="inspector-main-content" class="devtools-main-content">
<div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true"
data-localization-bundle="devtools/locale/inspector.properties">
<html:button id="inspector-element-add-button" class="devtools-button"
<button id="inspector-element-add-button" class="devtools-button"
data-localization="title=inspectorAddNode.label"/>
<html:div class="devtools-toolbar-spacer" />
<html:span id="inspector-searchlabel" />
<html:div id="inspector-search" class="devtools-searchbox has-clear-btn">
<html:input id="inspector-searchbox" class="devtools-searchinput"
<div class="devtools-toolbar-spacer" />
<span id="inspector-searchlabel" />
<div id="inspector-search" class="devtools-searchbox has-clear-btn">
<input id="inspector-searchbox" class="devtools-searchinput"
type="search"
data-localization="placeholder=inspectorSearchHTML.label3"/>
<html:button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></html:button>
</html:div>
<html:button id="inspector-eyedropper-toggle"
<button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></button>
</div>
<button id="inspector-eyedropper-toggle"
data-localization="title=inspector.eyedropper.label"
class="devtools-button command-button-invertable" />
<html:div id="inspector-sidebar-toggle-box" />
</html:div>
<html:div id="markup-box" />
<html:div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
<html:div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
<div id="inspector-sidebar-toggle-box" />
</div>
<div id="markup-box" />
<div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
<div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
role="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0" />
</html:div>
</html:div>
</div>
</div>
<!-- Splitter -->
<html:div
<div
xmlns="http://www.w3.org/1999/xhtml"
id="inspector-splitter-box">
</html:div>
</div>
<!-- Sidebar Container -->
<html:div id="inspector-sidebar-container">
<html:div
<div id="inspector-sidebar-container">
<div
xmlns="http://www.w3.org/1999/xhtml"
id="inspector-sidebar"
hidden="true" />
</html:div>
</div>
<!-- Sidebar panel definitions -->
<html:div id="tabpanels" style="visibility:collapse">
<html:div id="sidebar-panel-ruleview" class="devtools-monospace theme-sidebar inspector-tabpanel"
<div id="tabpanels" style="visibility:collapse">
<div id="sidebar-panel-ruleview" class="devtools-monospace theme-sidebar inspector-tabpanel"
data-localization-bundle="devtools/locale/inspector.properties">
<html:div id="ruleview-toolbar-container" class="devtools-toolbar">
<html:div id="ruleview-toolbar">
<html:div class="devtools-searchbox has-clear-btn">
<html:input id="ruleview-searchbox"
<div id="ruleview-toolbar-container" class="devtools-toolbar">
<div id="ruleview-toolbar">
<div class="devtools-searchbox has-clear-btn">
<input id="ruleview-searchbox"
class="devtools-filterinput devtools-rule-searchbox"
type="search"
data-localization="placeholder=inspector.filterStyles.placeholder"/>
<html:button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
</html:div>
<html:div id="ruleview-command-toolbar">
<html:button id="ruleview-add-rule-button" data-localization="title=inspector.addRule.tooltip" class="devtools-button"></html:button>
<html:button id="pseudo-class-panel-toggle" data-localization="title=inspector.togglePseudo.tooltip" class="devtools-button"></html:button>
</html:div>
</html:div>
<html:div id="pseudo-class-panel" hidden="true">
<html:label><html:input id="pseudo-hover-toggle" type="checkbox" value=":hover" tabindex="-1" />:hover</html:label>
<html:label><html:input id="pseudo-active-toggle" type="checkbox" value=":active" tabindex="-1" />:active</html:label>
<html:label><html:input id="pseudo-focus-toggle" type="checkbox" value=":focus" tabindex="-1" />:focus</html:label>
</html:div>
</html:div>
<button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></button>
</div>
<div id="ruleview-command-toolbar">
<button id="ruleview-add-rule-button" data-localization="title=inspector.addRule.tooltip" class="devtools-button"></button>
<button id="pseudo-class-panel-toggle" data-localization="title=inspector.togglePseudo.tooltip" class="devtools-button"></button>
</div>
</div>
<div id="pseudo-class-panel" hidden="true">
<label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" tabindex="-1" />:hover</label>
<label><input id="pseudo-active-toggle" type="checkbox" value=":active" tabindex="-1" />:active</label>
<label><input id="pseudo-focus-toggle" type="checkbox" value=":focus" tabindex="-1" />:focus</label>
</div>
</div>
<html:div id="ruleview-container" class="ruleview">
<html:div id="ruleview-container-focusable" tabindex="-1">
</html:div>
</html:div>
</html:div>
<div id="ruleview-container" class="ruleview">
<div id="ruleview-container-focusable" tabindex="-1">
</div>
</div>
</div>
<html:div id="sidebar-panel-computedview" class="devtools-monospace theme-sidebar inspector-tabpanel"
<div id="sidebar-panel-computedview" class="devtools-monospace theme-sidebar inspector-tabpanel"
data-localization-bundle="devtools/locale/inspector.properties">
<html:div id="computedview-toolbar" class="devtools-toolbar">
<html:div class="devtools-searchbox has-clear-btn">
<html:input id="computedview-searchbox"
<div id="computedview-toolbar" class="devtools-toolbar">
<div class="devtools-searchbox has-clear-btn">
<input id="computedview-searchbox"
class="devtools-filterinput devtools-rule-searchbox"
type="search"
data-localization="placeholder=inspector.filterStyles.placeholder"/>
<html:button id="computedview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
</html:div>
<html:input id="browser-style-checkbox"
<button id="computedview-searchinput-clear" class="devtools-searchinput-clear"></button>
</div>
<input id="browser-style-checkbox"
type="checkbox"
class="includebrowserstyles"/>
<html:label id="browser-style-checkbox-label" for="browser-style-checkbox"
<label id="browser-style-checkbox-label" for="browser-style-checkbox"
data-localization="content=inspector.browserStyles.label"/>
</html:div>
</div>
<html:div id="computedview-container">
<html:div id="computedview-container-focusable" tabindex="-1">
<html:div id="boxmodel-wrapper" tabindex="0"
<div id="computedview-container">
<div id="computedview-container-focusable" tabindex="-1">
<div id="boxmodel-wrapper" tabindex="0"
data-localization-bundle="devtools/locale/boxmodel.properties">
<html:div id="boxmodel-header">
<html:div id="boxmodel-expander" class="expander theme-twisty expandable" open=""></html:div>
<html:span data-localization="content=boxmodel.title"/>
</html:div>
<div id="boxmodel-header">
<div id="boxmodel-expander" class="expander theme-twisty expandable" open=""></div>
<span data-localization="content=boxmodel.title"/>
</div>
<html:div id="boxmodel-container">
<html:div id="boxmodel-main">
<html:span class="boxmodel-legend" data-box="margin" data-localization="content=boxmodel.margin;title=boxmodel.margin"/>
<html:div id="boxmodel-margins" data-box="margin" data-localization="title=boxmodel.margin">
<html:span class="boxmodel-legend" data-box="border" data-localization="content=boxmodel.border;title=boxmodel.border"/>
<html:div id="boxmodel-borders" data-box="border" data-localization="title=boxmodel.border">
<html:span class="boxmodel-legend" data-box="padding" data-localization="content=boxmodel.padding;title=boxmodel.padding"/>
<html:div id="boxmodel-padding" data-box="padding" data-localization="title=boxmodel.padding">
<html:div id="boxmodel-content" data-box="content" data-localization="title=boxmodel.content">
</html:div>
</html:div>
</html:div>
</html:div>
<div id="boxmodel-container">
<div id="boxmodel-main">
<span class="boxmodel-legend" data-box="margin" data-localization="content=boxmodel.margin;title=boxmodel.margin"/>
<div id="boxmodel-margins" data-box="margin" data-localization="title=boxmodel.margin">
<span class="boxmodel-legend" data-box="border" data-localization="content=boxmodel.border;title=boxmodel.border"/>
<div id="boxmodel-borders" data-box="border" data-localization="title=boxmodel.border">
<span class="boxmodel-legend" data-box="padding" data-localization="content=boxmodel.padding;title=boxmodel.padding"/>
<div id="boxmodel-padding" data-box="padding" data-localization="title=boxmodel.padding">
<div id="boxmodel-content" data-box="content" data-localization="title=boxmodel.content">
</div>
</div>
</div>
</div>
<html:p class="boxmodel-margin boxmodel-top"><html:span data-box="margin" class="boxmodel-editable" title="margin-top"></html:span></html:p>
<html:p class="boxmodel-margin boxmodel-right"><html:span data-box="margin" class="boxmodel-editable" title="margin-right"></html:span></html:p>
<html:p class="boxmodel-margin boxmodel-bottom"><html:span data-box="margin" class="boxmodel-editable" title="margin-bottom"></html:span></html:p>
<html:p class="boxmodel-margin boxmodel-left"><html:span data-box="margin" class="boxmodel-editable" title="margin-left"></html:span></html:p>
<p class="boxmodel-margin boxmodel-top"><span data-box="margin" class="boxmodel-editable" title="margin-top"></span></p>
<p class="boxmodel-margin boxmodel-right"><span data-box="margin" class="boxmodel-editable" title="margin-right"></span></p>
<p class="boxmodel-margin boxmodel-bottom"><span data-box="margin" class="boxmodel-editable" title="margin-bottom"></span></p>
<p class="boxmodel-margin boxmodel-left"><span data-box="margin" class="boxmodel-editable" title="margin-left"></span></p>
<html:p class="boxmodel-border boxmodel-top"><html:span data-box="border" class="boxmodel-editable" title="border-top"></html:span></html:p>
<html:p class="boxmodel-border boxmodel-right"><html:span data-box="border" class="boxmodel-editable" title="border-right"></html:span></html:p>
<html:p class="boxmodel-border boxmodel-bottom"><html:span data-box="border" class="boxmodel-editable" title="border-bottom"></html:span></html:p>
<html:p class="boxmodel-border boxmodel-left"><html:span data-box="border" class="boxmodel-editable" title="border-left"></html:span></html:p>
<p class="boxmodel-border boxmodel-top"><span data-box="border" class="boxmodel-editable" title="border-top"></span></p>
<p class="boxmodel-border boxmodel-right"><span data-box="border" class="boxmodel-editable" title="border-right"></span></p>
<p class="boxmodel-border boxmodel-bottom"><span data-box="border" class="boxmodel-editable" title="border-bottom"></span></p>
<p class="boxmodel-border boxmodel-left"><span data-box="border" class="boxmodel-editable" title="border-left"></span></p>
<html:p class="boxmodel-padding boxmodel-top"><html:span data-box="padding" class="boxmodel-editable" title="padding-top"></html:span></html:p>
<html:p class="boxmodel-padding boxmodel-right"><html:span data-box="padding" class="boxmodel-editable" title="padding-right"></html:span></html:p>
<html:p class="boxmodel-padding boxmodel-bottom"><html:span data-box="padding" class="boxmodel-editable" title="padding-bottom"></html:span></html:p>
<html:p class="boxmodel-padding boxmodel-left"><html:span data-box="padding" class="boxmodel-editable" title="padding-left"></html:span></html:p>
<p class="boxmodel-padding boxmodel-top"><span data-box="padding" class="boxmodel-editable" title="padding-top"></span></p>
<p class="boxmodel-padding boxmodel-right"><span data-box="padding" class="boxmodel-editable" title="padding-right"></span></p>
<p class="boxmodel-padding boxmodel-bottom"><span data-box="padding" class="boxmodel-editable" title="padding-bottom"></span></p>
<p class="boxmodel-padding boxmodel-left"><span data-box="padding" class="boxmodel-editable" title="padding-left"></span></p>
<html:p class="boxmodel-size">
<html:span data-box="content" data-localization="title=boxmodel.content"></html:span>
</html:p>
</html:div>
<p class="boxmodel-size">
<span data-box="content" data-localization="title=boxmodel.content"></span>
</p>
</div>
<html:div id="boxmodel-info">
<html:span id="boxmodel-element-size"></html:span>
<html:section id="boxmodel-position-group">
<html:button class="devtools-button" id="layout-geometry-editor"
data-localization="title=boxmodel.geometryButton.tooltip"></html:button>
<html:span id="boxmodel-element-position"></html:span>
</html:section>
</html:div>
<div id="boxmodel-info">
<span id="boxmodel-element-size"></span>
<section id="boxmodel-position-group">
<button class="devtools-button" id="layout-geometry-editor"
data-localization="title=boxmodel.geometryButton.tooltip"></button>
<span id="boxmodel-element-position"></span>
</section>
</div>
<html:div style="display: none">
<html:p id="boxmodel-dummy"></html:p>
</html:div>
</html:div>
</html:div>
<div style="display: none">
<p id="boxmodel-dummy"></p>
</div>
</div>
</div>
<html:div id="propertyContainer" class="theme-separator" tabindex="0">
</html:div>
<div id="propertyContainer" class="theme-separator" tabindex="0">
</div>
<html:div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"/>
</html:div>
</html:div>
</html:div>
<div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"/>
</div>
</div>
</div>
<html:div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel"
<div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel"
data-localization-bundle="devtools/locale/font-inspector.properties">
<html:div class="devtools-toolbar">
<html:div class="devtools-searchbox">
<html:input id="font-preview-text-input" class="devtools-textinput" type="search"
<div class="devtools-toolbar">
<div class="devtools-searchbox">
<input id="font-preview-text-input" class="devtools-textinput" type="search"
data-localization="placeholder=fontinspector.previewText"/>
</html:div>
<html:label id="font-showall" class="theme-link"
</div>
<label id="font-showall" class="theme-link"
data-localization="content=fontinspector.seeAll;
title=fontinspector.seeAll.tooltip"/>
</html:div>
</div>
<html:div id="font-container">
<html:ul id="all-fonts"></html:ul>
</html:div>
<div id="font-container">
<ul id="all-fonts"></ul>
</div>
<html:div id="font-template">
<html:section class="font">
<html:div class="font-preview-container">
<html:img class="font-preview"></html:img>
</html:div>
<html:div class="font-info">
<html:h1 class="font-name"></html:h1>
<html:span class="font-is-local" data-localization="content=fontinspector.system"/>
<html:span class="font-is-remote" data-localization="content=fontinspector.remote"/>
<html:p class="font-format-url">
<html:input readonly="readonly" class="font-url"></html:input>
<html:span class="font-format"></html:span>
</html:p>
<html:p class="font-css">
<html:span data-localization="content=fontinspector.usedAs"/> "<html:span class="font-css-name"></html:span>"
</html:p>
<html:pre class="font-css-code"></html:pre>
</html:div>
</html:section>
</html:div>
</html:div>
<div id="font-template">
<section class="font">
<div class="font-preview-container">
<img class="font-preview"></img>
</div>
<div class="font-info">
<h1 class="font-name"></h1>
<span class="font-is-local" data-localization="content=fontinspector.system"/>
<span class="font-is-remote" data-localization="content=fontinspector.remote"/>
<p class="font-format-url">
<input readonly="readonly" class="font-url"></input>
<span class="font-format"></span>
</p>
<p class="font-css">
<span data-localization="content=fontinspector.usedAs"/> "<span class="font-css-name"></span>"
</p>
<pre class="font-css-code"></pre>
</div>
</section>
</div>
</div>
<html:div id="sidebar-panel-animationinspector" class="devtools-monospace theme-sidebar inspector-tabpanel">
<html:iframe class="devtools-inspector-tab-frame" />
</html:div>
</html:div>
<div id="sidebar-panel-animationinspector" class="devtools-monospace theme-sidebar inspector-tabpanel">
<iframe class="devtools-inspector-tab-frame" />
</div>
</div>
</html:div>
</div>
</body>
</html>

Просмотреть файл

@ -226,11 +226,7 @@ MarkupView.prototype = {
let container = target.container;
if (this._hoveredNode !== container.node) {
if (container.node.nodeType !== nodeConstants.TEXT_NODE) {
this._showBoxModel(container.node);
} else {
this._hideBoxModel();
}
this._showBoxModel(container.node);
}
this._showContainerAsHovered(container.node);

Просмотреть файл

@ -134,6 +134,7 @@ subsuite = clipboard
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard
[browser_inspector_picker-stop-on-destroy.js]
[browser_inspector_picker-stop-on-tool-change.js]
[browser_inspector_portrait_mode.js]
[browser_inspector_pseudoclass-lock.js]
[browser_inspector_pseudoclass-menu.js]
[browser_inspector_reload-01.js]

Просмотреть файл

@ -51,8 +51,13 @@ add_task(function* () {
yield hoverElement("#id4");
yield assertHighlighterHidden();
info("Hovering over a text node and waiting for highlighter to appear.");
yield hoverTextNode("Visible text node");
yield assertHighlighterShownOnTextNode("body", 14);
function hoverContainer(container) {
let promise = inspector.toolbox.once("node-highlight");
EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
markupView.doc.defaultView);
@ -60,7 +65,7 @@ add_task(function* () {
}
function* hoverElement(selector) {
info("Hovering node " + selector + " in the markup view");
info(`Hovering node ${selector} in the markup view`);
let container = yield getContainerForSelector(selector, inspector);
return hoverContainer(container);
}
@ -75,11 +80,25 @@ add_task(function* () {
return null;
}
function hoverTextNode(text) {
info(`Hovering the text node "${text}" in the markup view`);
let container = [...markupView._containers].filter(([nodeFront]) => {
return nodeFront.nodeType === Ci.nsIDOMNode.TEXT_NODE &&
nodeFront._form.nodeValue.trim() === text.trim();
})[0][1];
return hoverContainer(container);
}
function* assertHighlighterShownOn(selector) {
ok((yield testActor.assertHighlightedNode(selector)),
"Highlighter is shown on the right node: " + selector);
}
function* assertHighlighterShownOnTextNode(parentSelector, childNodeIndex) {
ok((yield testActor.assertHighlightedTextNode(parentSelector, childNodeIndex)),
"Highlighter is shown on the right text node");
}
function* assertHighlighterHidden() {
let isVisible = yield testActor.isHighlighting();
ok(!isVisible, "Highlighter is hidden");

Просмотреть файл

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Test that the inspector splitter is properly initialized in horizontal mode if the
// inspector starts in portrait mode.
add_task(function* () {
let { inspector, toolbox } = yield openInspectorForURL(
"data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>", "window");
let hostWindow = toolbox._host._window;
let originalWidth = hostWindow.outerWidth;
let originalHeight = hostWindow.outerHeight;
let splitter = inspector.panelDoc.querySelector(".inspector-sidebar-splitter");
// If the inspector is not already in landscape mode.
if (!splitter.classList.contains("vert")) {
info("Resize toolbox window to force inspector to landscape mode");
let onClassnameMutation = waitForClassMutation(splitter);
hostWindow.resizeTo(800, 500);
yield onClassnameMutation;
ok(splitter.classList.contains("vert"), "Splitter is in vertical mode");
}
info("Resize toolbox window to force inspector to portrait mode");
let onClassnameMutation = waitForClassMutation(splitter);
hostWindow.resizeTo(500, 500);
yield onClassnameMutation;
ok(splitter.classList.contains("horz"), "Splitter is in horizontal mode");
info("Close the inspector");
let target = TargetFactory.forTab(gBrowser.selectedTab);
yield gDevTools.closeToolbox(target);
info("Reopen inspector");
({ inspector, toolbox } = yield openInspector("window"));
// Devtools window should still be 500px * 500px, inspector should still be in portrait.
splitter = inspector.panelDoc.querySelector(".inspector-sidebar-splitter");
ok(splitter.classList.contains("horz"), "Splitter is in horizontal mode");
info("Restore original window size");
toolbox._host._window.resizeTo(originalWidth, originalHeight);
});
/**
* Helper waiting for a class attribute mutation on the provided target. Returns a
* promise.
*
* @param {Node} target
*   Node to observe
* @return {Promise} promise that will resolve upon receiving a mutation for the class
* attribute on the target.
*/
function waitForClassMutation(target) {
return new Promise(resolve => {
let observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.attributeName === "class") {
observer.disconnect();
resolve();
return;
}
}
});
observer.observe(target, { attributes: true });
});
}
registerCleanupFunction(function () {
// Restore the host type for other tests.
Services.prefs.clearUserPref("devtools.toolbox.host");
});

Просмотреть файл

@ -14,5 +14,6 @@
</script>
<div id="id3">Visible div 3</div>
<div id="id4" style="display:none;">Invisible div node</div>
Visible text node
</body>
</html>

Просмотреть файл

@ -37,7 +37,7 @@
.theme-firebug .toolbar {
border-bottom: 1px solid rgb(170, 188, 207);
background-color: rgb(219, 234, 249) !important;
background-color: var(--theme-tab-toolbar-background) !important;
background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2));
}

Просмотреть файл

@ -132,7 +132,6 @@
/* Firebug Theme */
.theme-firebug .tabs .tabs-navigation {
background-color: rgb(219, 234, 249);
background-image: linear-gradient(rgba(253, 253, 253, 0.2), rgba(253, 253, 253, 0));
padding-top: 3px;
padding-left: 3px;

Просмотреть файл

@ -260,6 +260,15 @@ var testSpec = protocol.generateActorSpec({
value: RetVal("json")
}
},
getTextNodeRect: {
request: {
parentSelector: Arg(0, "string"),
childNodeIndex: Arg(1, "number")
},
response: {
value: RetVal("json")
}
},
getNodeInfo: {
request: {
selector: Arg(0, "string")
@ -717,6 +726,12 @@ var TestActor = exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
return getRect(this.content, node, this.content);
}),
getTextNodeRect: Task.async(function* (parentSelector, childNodeIndex) {
let parentNode = this._querySelector(parentSelector);
let node = parentNode.childNodes[childNodeIndex];
return getAdjustedQuads(this.content, node)[0].bounds;
}),
/**
* Get information about a DOM element, identified by a selector.
* @param {String} selector The CSS selector to get the node (can be an array
@ -891,75 +906,59 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp
return ret;
}),
/**
* Check that the box-model highlighter is currently highlighting the node matching the
* given selector.
* @param {String} selector
* @return {Boolean}
*/
assertHighlightedNode: Task.async(function* (selector) {
// Taken and tweaked from:
// https://github.com/iominh/point-in-polygon-extended/blob/master/src/index.js#L30-L85
function isLeft(p0, p1, p2) {
let l = ((p1[0] - p0[0]) * (p2[1] - p0[1])) -
((p2[0] - p0[0]) * (p1[1] - p0[1]));
return l;
}
function isInside(point, polygon) {
if (polygon.length === 0) {
return false;
}
let rect = yield this.getNodeRect(selector);
return yield this.isNodeRectHighlighted(rect);
}),
var n = polygon.length;
var newPoints = polygon.slice(0);
newPoints.push(polygon[0]);
var wn = 0; // wn counter
// loop through all edges of the polygon
for (var i = 0; i < n; i++) {
// Accept points on the edges
let r = isLeft(newPoints[i], newPoints[i + 1], point);
if (r === 0) {
return true;
}
if (newPoints[i][1] <= point[1]) {
if (newPoints[i + 1][1] > point[1] && r > 0) {
wn++;
}
} else {
if (newPoints[i + 1][1] <= point[1] && r < 0) {
wn--;
}
}
}
if (wn === 0) {
dumpn(JSON.stringify(point) + " is outside of " + JSON.stringify(polygon));
}
// the point is outside only when this winding number wn===0, otherwise it's inside
return wn !== 0;
}
/**
* Check that the box-model highlighter is currently highlighting the text node that can
* be found at a given index within the list of childNodes of a parent element matching
* the given selector.
* @param {String} parentSelector
* @param {Number} childNodeIndex
* @return {Boolean}
*/
assertHighlightedTextNode: Task.async(function* (parentSelector, childNodeIndex) {
let rect = yield this.getTextNodeRect(parentSelector, childNodeIndex);
return yield this.isNodeRectHighlighted(rect);
}),
/**
* Check that the box-model highlighter is currently highlighting the given rect.
* @param {Object} rect
* @return {Boolean}
*/
isNodeRectHighlighted: Task.async(function* ({ left, top, width, height }) {
let {visible, border} = yield this._getBoxModelStatus();
let points = border.points;
if (visible) {
// Check that the node is within the box model
let { left, top, width, height } = yield this.getNodeRect(selector);
let right = left + width;
let bottom = top + height;
// Converts points dictionnary into an array
let list = [];
for (var i = 1; i <= 4; i++) {
let p = points["p" + i];
list.push([p.x, p.y]);
}
points = list;
// Check that each point of the node is within the box model
if (!isInside([left, top], points) ||
!isInside([right, top], points) ||
!isInside([right, bottom], points) ||
!isInside([left, bottom], points)) {
return false;
}
return true;
} else {
if (!visible) {
return false;
}
// Check that the node is within the box model
let right = left + width;
let bottom = top + height;
// Converts points dictionnary into an array
let list = [];
for (let i = 1; i <= 4; i++) {
let p = points["p" + i];
list.push([p.x, p.y]);
}
points = list;
// Check that each point of the node is within the box model
return isInside([left, top], points) &&
isInside([right, top], points) &&
isInside([right, bottom], points) &&
isInside([left, bottom], points);
}),
/**
@ -1086,3 +1085,49 @@ var TestActorFront = exports.TestActorFront = protocol.FrontClassWithSpec(testSp
return {d, points};
})
});
/**
* Check whether a point is included in a polygon.
* Taken and tweaked from:
* https://github.com/iominh/point-in-polygon-extended/blob/master/src/index.js#L30-L85
* @param {Array} point [x,y] coordinates
* @param {Array} polygon An array of [x,y] points
* @return {Boolean}
*/
function isInside(point, polygon) {
if (polygon.length === 0) {
return false;
}
const n = polygon.length;
const newPoints = polygon.slice(0);
newPoints.push(polygon[0]);
let wn = 0;
// loop through all edges of the polygon
for (let i = 0; i < n; i++) {
// Accept points on the edges
let r = isLeft(newPoints[i], newPoints[i + 1], point);
if (r === 0) {
return true;
}
if (newPoints[i][1] <= point[1]) {
if (newPoints[i + 1][1] > point[1] && r > 0) {
wn++;
}
} else if (newPoints[i + 1][1] <= point[1] && r < 0) {
wn--;
}
}
if (wn === 0) {
dumpn(JSON.stringify(point) + " is outside of " + JSON.stringify(polygon));
}
// the point is outside only when this winding number wn===0, otherwise it's inside
return wn !== 0;
}
function isLeft(p0, p1, p2) {
let l = ((p1[0] - p0[0]) * (p2[1] - p0[1])) -
((p2[0] - p0[0]) * (p1[1] - p0[1]));
return l;
}

Просмотреть файл

@ -454,6 +454,25 @@ const TEST_DATA = [
index: 0, enabled: false},
expected: "/*! content: '*\\/'; */"
},
{
desc: "delete disabled property",
input: "\n a:b;\n /* color:#f0c; */\n e:f;",
instruction: {type: "remove", name: "color", index: 1},
expected: "\n a:b;\n e:f;",
},
{
desc: "delete heuristic-disabled property",
input: "\n a:b;\n /*! c:d; */\n e:f;",
instruction: {type: "remove", name: "c", index: 1},
expected: "\n a:b;\n e:f;",
},
{
desc: "delete disabled property leaving other disabled property",
input: "\n a:b;\n /* color:#f0c; background-color: seagreen; */\n e:f;",
instruction: {type: "remove", name: "color", index: 1},
expected: "\n a:b;\n /* background-color: seagreen; */\n e:f;",
},
];
function rewriteDeclarations(inputString, instruction, defaultIndentation) {

Просмотреть файл

@ -72,7 +72,7 @@
}
.theme-firebug .devtools-sidebar-tabs tabs {
background-color: rgb(219, 234, 249) !important;
background-color: var(--theme-tab-toolbar-background) !important;
background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2));
}
@ -168,7 +168,7 @@
.theme-firebug toolbar,
.theme-firebug .devtools-toolbar {
border-bottom: 1px solid rgb(170, 188, 207) !important;
background-color: rgb(219, 234, 249) !important;
background-color: var(--theme-tab-toolbar-background) !important;
background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2));
padding-inline-end: 4px;
}

Просмотреть файл

@ -38,6 +38,7 @@ window {
#inspector-splitter-box {
height: 100vh;
width: 100vw;
position: fixed;
}
/* Minimum width for the Inspector main (uncontrolled) area. */
@ -197,7 +198,8 @@ iframe {
#markup-box {
width: 100%;
flex: 1 1 auto;
flex: 1;
min-height: 0;
}
#markup-box > iframe {

Просмотреть файл

@ -139,7 +139,7 @@
--theme-sidebar-background: #fcfcfc;
--theme-contrast-background: #e6b064;
--theme-tab-toolbar-background: #ebeced;
--theme-tab-toolbar-background: #d8eaf9;
--theme-toolbar-background: #f0f1f2;
--theme-selection-background: #3399ff;
--theme-selection-background-semitransparent: rgba(128,128,128,0.2);

Просмотреть файл

@ -103,6 +103,21 @@ const FilterBar = createClass({
label: "Debug",
filterKey: MESSAGE_LEVEL.DEBUG,
dispatch
}),
dom.span({
className: "devtools-separator",
}),
FilterButton({
active: filter.netxhr,
label: "XHR",
filterKey: "netxhr",
dispatch
}),
FilterButton({
active: filter.network,
label: "Requests",
filterKey: "network",
dispatch
})
)
);

Просмотреть файл

@ -13,6 +13,8 @@ const FilterState = Immutable.Record({
error: true,
info: true,
log: true,
network: true,
netxhr: true,
text: "",
warn: true,
});

Просмотреть файл

@ -9,7 +9,8 @@ const { l10n } = require("devtools/client/webconsole/new-console-output/utils/me
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getLogLimit } = require("devtools/client/webconsole/new-console-output/selectors/prefs");
const {
MESSAGE_TYPE
MESSAGE_TYPE,
MESSAGE_SOURCE
} = require("devtools/client/webconsole/new-console-output/constants");
function getAllMessages(state) {
@ -19,7 +20,10 @@ function getAllMessages(state) {
return prune(
search(
filterLevel(messages, filters),
filterNetwork(
filterLevel(messages, filters),
filters
),
filters.text
),
logLimit
@ -37,6 +41,17 @@ function filterLevel(messages, filters) {
});
}
function filterNetwork(messages, filters) {
return messages.filter((message) => {
return (
message.source !== MESSAGE_SOURCE.NETWORK
|| (filters.get("network") === true && message.isXHR === false)
|| (filters.get("netxhr") === true && message.isXHR === true)
|| [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type)
);
});
}
function search(messages, text = "") {
if (text === "") {
return messages;

Просмотреть файл

@ -20,6 +20,8 @@ function configureStore() {
warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"),
info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"),
log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"),
network: Services.prefs.getBoolPref("devtools.webconsole.filter.network"),
netxhr: Services.prefs.getBoolPref("devtools.webconsole.filter.netxhr"),
})
};

Просмотреть файл

@ -13,6 +13,7 @@ const { getAllFilters } = require("devtools/client/webconsole/new-console-output
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
describe("Filtering", () => {
let store;
@ -58,6 +59,30 @@ describe("Filtering", () => {
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
it("filters xhr messages", () => {
let message = stubPreparedMessages.get("XHR GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
store.dispatch(actions.filterToggle("netxhr"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
});
it("filters network messages", () => {
let message = stubPreparedMessages.get("GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
store.dispatch(actions.filterToggle("network"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
});
});
describe("Text filter", () => {
@ -137,13 +162,17 @@ describe("Clear filters", () => {
// Setup test case
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
store.dispatch(actions.filterToggle("netxhr"));
store.dispatch(actions.filterTextSet("foobar"));
let filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"debug": true,
"error": false,
"info": true,
"log": true,
"network": true,
"netxhr": false,
"warn": true,
"text": "foobar"
});
@ -156,6 +185,8 @@ describe("Clear filters", () => {
"error": true,
"info": true,
"log": true,
"network": true,
"netxhr": true,
"warn": true,
"text": ""
});

Просмотреть файл

@ -3298,6 +3298,9 @@ WebConsoleConnectionProxy.prototype = {
messages.sort((a, b) => a.timeStamp - b.timeStamp);
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
// Filter out CSS page errors.
messages = messages.filter(message => !(message._type == "PageError"
&& Utils.categoryForScriptError(message) === CATEGORY_CSS));
for (let packet of messages) {
this.dispatchMessageAdd(packet);
}

Просмотреть файл

@ -13,7 +13,7 @@ const protocol = require("devtools/shared/protocol");
const Services = require("Services");
const { isWindowIncluded } = require("devtools/shared/layout/utils");
const { highlighterSpec, customHighlighterSpec } = require("devtools/shared/specs/highlighters");
const { isXUL, isNodeValid } = require("./highlighters/utils/markup");
const { isXUL } = require("./highlighters/utils/markup");
const { SimpleOutlineHighlighter } = require("./highlighters/simple-outline");
const HIGHLIGHTER_PICKED_TIMER = 1000;
@ -182,9 +182,7 @@ var HighlighterActor = exports.HighlighterActor = protocol.ActorClassWithSpec(hi
* all options may be supported by all types of highlighters.
*/
showBoxModel: function (node, options = {}) {
if (node && isNodeValid(node.rawNode)) {
this._highlighter.show(node.rawNode, options);
} else {
if (!node || !this._highlighter.show(node.rawNode, options)) {
this._highlighter.hide();
}
},
@ -468,7 +466,7 @@ var CustomHighlighterActor = exports.CustomHighlighterActor = protocol.ActorClas
* (FF41+)
*/
show: function (node, options) {
if (!node || !isNodeValid(node.rawNode) || !this._highlighter) {
if (!node || !this._highlighter) {
return false;
}

Просмотреть файл

@ -65,7 +65,7 @@ AutoRefreshHighlighter.prototype = {
let isSameNode = node === this.currentNode;
let isSameOptions = this._isSameOptions(options);
if (!isNodeValid(node) || (isSameNode && isSameOptions)) {
if (!this._isNodeValid(node) || (isSameNode && isSameOptions)) {
return false;
}
@ -87,7 +87,7 @@ AutoRefreshHighlighter.prototype = {
* Hide the highlighter
*/
hide: function () {
if (!isNodeValid(this.currentNode)) {
if (!this._isNodeValid(this.currentNode)) {
return;
}
@ -100,6 +100,17 @@ AutoRefreshHighlighter.prototype = {
this.emit("hidden");
},
/**
* Whether the current node is valid for this highlighter type.
* This is implemented by default to check if the node is an element node. Highlighter
* sub-classes should override this method if they want to highlight other node types.
* @param {DOMNode} node
* @return {Boolean}
*/
_isNodeValid: function (node) {
return isNodeValid(node);
},
/**
* Are the provided options the same as the currently stored options?
* Returns false if there are no options stored currently.
@ -151,7 +162,7 @@ AutoRefreshHighlighter.prototype = {
* Update the highlighter if the node has moved since the last update.
*/
update: function () {
if (!isNodeValid(this.currentNode) || !this._hasMoved()) {
if (!this._isNodeValid(this.currentNode) || !this._hasMoved()) {
return;
}

Просмотреть файл

@ -7,11 +7,17 @@
const { extend } = require("sdk/core/heritage");
const { AutoRefreshHighlighter } = require("./auto-refresh");
const {
CanvasFrameAnonymousContentHelper, moveInfobar,
getBindingElementAndPseudo, hasPseudoClassLock, getComputedStyle,
createSVGNode, createNode, isNodeValid } = require("./utils/markup");
CanvasFrameAnonymousContentHelper,
createNode,
createSVGNode,
getBindingElementAndPseudo,
hasPseudoClassLock,
isNodeValid,
moveInfobar,
} = require("./utils/markup");
const { setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const inspector = require("devtools/server/actors/inspector");
const nodeConstants = require("devtools/shared/dom-node-constants");
// Note that the order of items in this array is important because it is used
// for drawing the BoxModelHighlighter's path elements correctly.
@ -94,8 +100,6 @@ function BoxModelHighlighter(highlighterEnv) {
* regionFill property: `highlighter.regionFill.margin = "red";
*/
this.regionFill = {};
this._currentNode = null;
}
BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
@ -103,15 +107,6 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
ID_CLASS_PREFIX: "box-model-",
get currentNode() {
return this._currentNode;
},
set currentNode(node) {
this._currentNode = node;
this._computedStyle = null;
},
_buildMarkup: function () {
let doc = this.win.document;
@ -258,16 +253,23 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
*/
destroy: function () {
AutoRefreshHighlighter.prototype.destroy.call(this);
this.markup.destroy();
this._currentNode = null;
},
getElement: function (id) {
return this.markup.getElement(this.ID_CLASS_PREFIX + id);
},
/**
* Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
* text nodes since these can also be highlighted.
* @param {DOMNode} node
* @return {Boolean}
*/
_isNodeValid: function (node) {
return node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE));
},
/**
* Show the highlighter on a given node
*/
@ -311,7 +313,9 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
setIgnoreLayoutChanges(true);
if (this._updateBoxModel()) {
if (!this.options.hideInfoBar) {
// Show the infobar only if configured to do so and the node is an element.
if (!this.options.hideInfoBar &&
this.currentNode.nodeType === this.currentNode.ELEMENT_NODE) {
this._showInfobar();
} else {
this._hideInfobar();
@ -519,20 +523,15 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
return path;
},
/**
* Can the current node be highlighted? Does it have quads.
* @return {Boolean}
*/
_nodeNeedsHighlighting: function () {
let hasNoQuads = !this.currentQuads.margin.length &&
!this.currentQuads.border.length &&
!this.currentQuads.padding.length &&
!this.currentQuads.content.length;
if (!isNodeValid(this.currentNode) || hasNoQuads) {
return false;
}
if (!this._computedStyle) {
this._computedStyle = getComputedStyle(this.currentNode);
}
return this._computedStyle.getPropertyValue("display") !== "none";
return this.currentQuads.margin.length ||
this.currentQuads.border.length ||
this.currentQuads.padding.length ||
this.currentQuads.content.length;
},
_getOuterBounds: function () {

Просмотреть файл

@ -6,12 +6,9 @@
const { extend } = require("sdk/core/heritage");
const { AutoRefreshHighlighter } = require("./auto-refresh");
const {
CanvasFrameAnonymousContentHelper, getCSSStyleRules, getComputedStyle,
createSVGNode, createNode } = require("./utils/markup");
const { setIgnoreLayoutChanges,
getAdjustedQuads } = require("devtools/shared/layout/utils");
const { CanvasFrameAnonymousContentHelper, getCSSStyleRules, getComputedStyle,
createSVGNode, createNode } = require("./utils/markup");
const { setIgnoreLayoutChanges, getAdjustedQuads } = require("devtools/shared/layout/utils");
const GEOMETRY_LABEL_SIZE = 6;

Просмотреть файл

@ -121,18 +121,25 @@ function installHelperSheet(win, source, type = "agent") {
}
exports.installHelperSheet = installHelperSheet;
function isNodeValid(node) {
// Is it null or dead?
/**
* Returns true if a DOM node is "valid", where "valid" means that the node isn't a dead
* object wrapper, is still attached to a document, and is of a given type.
* @param {DOMNode} node
* @param {Number} nodeType Optional, defaults to ELEMENT_NODE
* @return {Boolean}
*/
function isNodeValid(node, nodeType = Ci.nsIDOMNode.ELEMENT_NODE) {
// Is it still alive?
if (!node || Cu.isDeadWrapper(node)) {
return false;
}
// Is it an element node
if (node.nodeType !== node.ELEMENT_NODE) {
// Is it of the right type?
if (node.nodeType !== nodeType) {
return false;
}
// Is the document inaccessible?
// Is its document accessible?
let doc = node.ownerDocument;
if (!doc || !doc.defaultView) {
return false;

Просмотреть файл

@ -486,18 +486,12 @@ function parseDeclarations(isCssPropertyKnown, inputString,
*/
function RuleRewriter(isCssPropertyKnown, rule, inputString) {
this.rule = rule;
this.inputString = inputString;
// Whether there are any newlines in the input text.
this.hasNewLine = /[\r\n]/.test(this.inputString);
this.isCssPropertyKnown = isCssPropertyKnown;
// Keep track of which any declarations we had to rewrite while
// performing the requested action.
this.changedDeclarations = {};
// The declarations.
this.declarations = parseDeclarations(isCssPropertyKnown, this.inputString,
true);
this.decl = null;
this.result = null;
// If not null, a promise that must be wait upon before |apply| can
// do its work.
this.editPromise = null;
@ -507,9 +501,28 @@ function RuleRewriter(isCssPropertyKnown, rule, inputString) {
// indentation based on the style sheet's text. This override
// facility is for testing.
this.defaultIndentation = null;
this.startInitialization(inputString);
}
RuleRewriter.prototype = {
/**
* An internal function to initialize the rewriter with a given
* input string.
*
* @param {String} inputString the input to use
*/
startInitialization: function (inputString) {
this.inputString = inputString;
// Whether there are any newlines in the input text.
this.hasNewLine = /[\r\n]/.test(this.inputString);
// The declarations.
this.declarations = parseDeclarations(this.isCssPropertyKnown, this.inputString,
true);
this.decl = null;
this.result = null;
},
/**
* An internal function to complete initialization and set some
* properties for further processing.
@ -924,6 +937,17 @@ RuleRewriter.prototype = {
return;
}
// If the property is disabled, then first enable it, and then
// delete it. We take this approach because we want to remove the
// entire comment if possible; but the logic for dealing with
// comments is hairy and already implemented in
// setPropertyEnabled.
if (this.decl.commentOffsets) {
this.setPropertyEnabled(index, name, true);
this.startInitialization(this.result);
this.completeInitialization(index);
}
let copyOffset = this.decl.offsets[1];
// Maybe removing this rule left us with a completely blank
// line. In this case, we'll delete the whole thing. We only

Просмотреть файл

@ -6,6 +6,7 @@
#include "AnimationUtils.h"
#include "nsContentUtils.h" // For nsContentUtils::IsCallerChrome
#include "nsDebug.h"
#include "nsIAtom.h"
#include "nsIContent.h"
@ -63,7 +64,7 @@ AnimationUtils::IsOffscreenThrottlingEnabled()
}
/* static */ bool
AnimationUtils::IsCoreAPIEnabled()
AnimationUtils::IsCoreAPIEnabledForCaller()
{
static bool sCoreAPIEnabled;
static bool sPrefCached = false;
@ -74,7 +75,7 @@ AnimationUtils::IsCoreAPIEnabled()
"dom.animations-api.core.enabled");
}
return sCoreAPIEnabled;
return sCoreAPIEnabled || nsContentUtils::IsCallerChrome();
}
} // namespace mozilla

Просмотреть файл

@ -63,10 +63,10 @@ public:
/**
* Returns true if the preference to enable the core Web Animations API is
* true.
* true or the caller is chrome.
*/
static bool
IsCoreAPIEnabled();
IsCoreAPIEnabledForCaller();
};
} // namespace mozilla

Просмотреть файл

@ -132,7 +132,7 @@ KeyframeEffect::SetIterationComposite(
{
// Ignore iterationComposite if the Web Animations API is not enabled,
// then the default value 'Replace' will be used.
if (!AnimationUtils::IsCoreAPIEnabled()) {
if (!AnimationUtils::IsCoreAPIEnabledForCaller()) {
return;
}

Просмотреть файл

@ -115,7 +115,7 @@ KeyframeEffectParams::ParseSpacing(const nsAString& aSpacing,
// Ignore spacing if the core API is not enabled since it is not yet ready to
// ship.
if (!AnimationUtils::IsCoreAPIEnabled()) {
if (!AnimationUtils::IsCoreAPIEnabledForCaller()) {
aSpacingMode = SpacingMode::distribute;
return;
}

Просмотреть файл

@ -512,7 +512,7 @@ KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
aRv);
// Ignore iterationComposite if the Web Animations API is not enabled,
// then the default value 'Replace' will be used.
if (AnimationUtils::IsCoreAPIEnabled()) {
if (AnimationUtils::IsCoreAPIEnabledForCaller()) {
result.mIterationComposite = options.mIterationComposite;
}
}

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<script>
window.onload=function(){
var e=document.createElement("q");
document.documentElement.appendChild(e);
e.style="mask-image:url(data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=),url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)";
setTimeout(function(){
e.style="mask-image:url(data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=)";
},0);
};
</script>
</html>

Просмотреть файл

@ -207,3 +207,4 @@ load xhr_empty_datauri.html
load xhr_html_nullresponse.html
load 1230422.html
load 1251361.html
load 1304437.html

Просмотреть файл

@ -525,7 +525,6 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
, INIT_MIRROR(mPlaybackPosition, 0)
, INIT_MIRROR(mIsAudioDataAudible, false)
, INIT_CANONICAL(mVolume, 0.0)
, INIT_CANONICAL(mPlaybackRate, 1.0)
, INIT_CANONICAL(mPreservesPitch, true)
, INIT_CANONICAL(mEstimatedDuration, NullableTimeUnit())
, INIT_CANONICAL(mExplicitDuration, Maybe<double>())
@ -744,6 +743,9 @@ MediaDecoder::SetStateMachineParameters()
if (mMinimizePreroll) {
mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
}
if (mPlaybackRate != 1 && mPlaybackRate != 0) {
mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
}
mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate);
mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
@ -1510,7 +1512,10 @@ MediaDecoder::SetPlaybackRate(double aPlaybackRate)
if (mPlaybackRate == 0.0) {
mPausedForPlaybackRateNull = true;
Pause();
} else if (mPausedForPlaybackRateNull) {
return;
}
if (mPausedForPlaybackRateNull) {
// Play() uses mPausedForPlaybackRateNull value, so must reset it first
mPausedForPlaybackRateNull = false;
// If the playbackRate is no longer null, restart the playback, iff the
@ -1519,6 +1524,10 @@ MediaDecoder::SetPlaybackRate(double aPlaybackRate)
Play();
}
}
if (mDecoderStateMachine) {
mDecoderStateMachine->DispatchSetPlaybackRate(aPlaybackRate);
}
}
void

Просмотреть файл

@ -764,7 +764,7 @@ protected:
Canonical<double> mVolume;
// PlaybackRate and pitch preservation status we should start at.
Canonical<double> mPlaybackRate;
double mPlaybackRate = 1;
Canonical<bool> mPreservesPitch;
@ -827,9 +827,6 @@ public:
AbstractCanonical<double>* CanonicalVolume() {
return &mVolume;
}
AbstractCanonical<double>* CanonicalPlaybackRate() {
return &mPlaybackRate;
}
AbstractCanonical<bool>* CanonicalPreservesPitch() {
return &mPreservesPitch;
}

Просмотреть файл

@ -745,7 +745,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
INIT_MIRROR(mNextPlayState, MediaDecoder::PLAY_STATE_PAUSED),
INIT_MIRROR(mVolume, 1.0),
INIT_MIRROR(mLogicalPlaybackRate, 1.0),
INIT_MIRROR(mPreservesPitch, true),
INIT_MIRROR(mSameOriginMedia, false),
INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
@ -807,7 +806,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
mPlayState.Connect(aDecoder->CanonicalPlayState());
mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
mVolume.Connect(aDecoder->CanonicalVolume());
mLogicalPlaybackRate.Connect(aDecoder->CanonicalPlaybackRate());
mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle());
@ -824,7 +822,6 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
mWatchManager.Watch(mVideoCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
@ -1494,8 +1491,7 @@ MediaDecoderStateMachine::MaybeStartBuffering()
bool shouldBuffer;
if (mReader->UseBufferingHeuristics()) {
shouldBuffer = HasLowDecodedData(EXHAUSTED_DATA_MARGIN_USECS) &&
HasLowBufferedData();
shouldBuffer = HasLowDecodedData() && HasLowBufferedData();
} else {
MOZ_ASSERT(mReader->IsWaitForDataSupported());
shouldBuffer = (OutOfDecodedAudio() && mReader->IsWaitingAudioData()) ||
@ -1710,7 +1706,6 @@ MediaDecoderStateMachine::Shutdown()
mPlayState.DisconnectIfConnected();
mNextPlayState.DisconnectIfConnected();
mVolume.DisconnectIfConnected();
mLogicalPlaybackRate.DisconnectIfConnected();
mPreservesPitch.DisconnectIfConnected();
mSameOriginMedia.DisconnectIfConnected();
mMediaPrincipalHandle.DisconnectIfConnected();
@ -2318,17 +2313,28 @@ MediaDecoderStateMachine::StartMediaSink()
}
}
bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs)
bool
MediaDecoderStateMachine::HasLowDecodedAudio()
{
MOZ_ASSERT(OnTaskQueue());
return IsAudioDecoding() &&
GetDecodedAudioDuration() < EXHAUSTED_DATA_MARGIN_USECS * mPlaybackRate;
}
bool
MediaDecoderStateMachine::HasLowDecodedVideo()
{
MOZ_ASSERT(OnTaskQueue());
return IsVideoDecoding() &&
VideoQueue().GetSize() < LOW_VIDEO_FRAMES * mPlaybackRate;
}
bool
MediaDecoderStateMachine::HasLowDecodedData()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mReader->UseBufferingHeuristics());
// We consider ourselves low on decoded data if we're low on audio,
// provided we've not decoded to the end of the audio stream, or
// if we're low on video frames, provided
// we've not decoded to the end of the video stream.
return ((IsAudioDecoding() && GetDecodedAudioDuration() < aAudioUsecs) ||
(IsVideoDecoding() &&
static_cast<uint32_t>(VideoQueue().GetSize()) < LOW_VIDEO_FRAMES));
return HasLowDecodedAudio() || HasLowDecodedVideo();
}
bool MediaDecoderStateMachine::OutOfDecodedAudio()
@ -2782,16 +2788,12 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const
}
void
MediaDecoderStateMachine::LogicalPlaybackRateChanged()
MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(aPlaybackRate != 0, "Should be handled by MediaDecoder::Pause()");
if (mLogicalPlaybackRate == 0) {
// This case is handled in MediaDecoder by pausing playback.
return;
}
mPlaybackRate = mLogicalPlaybackRate;
mPlaybackRate = aPlaybackRate;
mMediaSink->SetPlaybackRate(mPlaybackRate);
if (mIsAudioPrerolling && DonePrerollingAudio()) {

Просмотреть файл

@ -174,6 +174,12 @@ public:
// Seeks to the decoder to aTarget asynchronously.
RefPtr<MediaDecoder::SeekPromise> InvokeSeek(SeekTarget aTarget);
void DispatchSetPlaybackRate(double aPlaybackRate)
{
OwnerThread()->DispatchStateChange(NewRunnableMethod<double>(
this, &MediaDecoderStateMachine::SetPlaybackRate, aPlaybackRate));
}
// Set/Unset dormant state.
void DispatchSetDormant(bool aDormant);
@ -367,7 +373,7 @@ protected:
void AudioAudibleChanged(bool aAudible);
void VolumeChanged();
void LogicalPlaybackRateChanged();
void SetPlaybackRate(double aPlaybackRate);
void PreservesPitchChanged();
MediaQueue<MediaData>& AudioQueue() { return mAudioQueue; }
@ -381,11 +387,13 @@ protected:
// decode more.
bool NeedToDecodeVideo();
// Returns true if we've got less than aAudioUsecs microseconds of decoded
// and playable data. The decoder monitor must be held.
//
// True if we are low in decoded audio/video data.
// May not be invoked when mReader->UseBufferingHeuristics() is false.
bool HasLowDecodedData(int64_t aAudioUsecs);
bool HasLowDecodedData();
bool HasLowDecodedAudio();
bool HasLowDecodedVideo();
bool OutOfDecodedAudio();
@ -886,11 +894,6 @@ private:
// Volume of playback. 0.0 = muted. 1.0 = full volume.
Mirror<double> mVolume;
// TODO: The separation between mPlaybackRate and mLogicalPlaybackRate is a
// kludge to preserve existing fragile logic while converting this setup to
// state-mirroring. Some hero should clean this up.
Mirror<double> mLogicalPlaybackRate;
// Pitch preservation for the playback rate.
Mirror<bool> mPreservesPitch;

Просмотреть файл

@ -30,8 +30,9 @@ TextTrackList::TextTrackList(nsPIDOMWindowInner* aOwnerWindow)
TextTrackList::TextTrackList(nsPIDOMWindowInner* aOwnerWindow,
TextTrackManager* aTextTrackManager)
: DOMEventTargetHelper(aOwnerWindow)
, mTextTrackManager(aTextTrackManager)
: DOMEventTargetHelper(aOwnerWindow)
, mPendingTextTrackChange(false)
, mTextTrackManager(aTextTrackManager)
{
}
@ -131,7 +132,7 @@ TextTrackList::DidSeek()
}
}
class TrackEventRunner final: public Runnable
class TrackEventRunner : public Runnable
{
public:
TrackEventRunner(TextTrackList* aList, nsIDOMEvent* aEvent)
@ -144,11 +145,25 @@ public:
return mList->DispatchTrackEvent(mEvent);
}
private:
RefPtr<TextTrackList> mList;
private:
RefPtr<nsIDOMEvent> mEvent;
};
class ChangeEventRunner final : public TrackEventRunner
{
public:
ChangeEventRunner(TextTrackList* aList, nsIDOMEvent* aEvent)
: TrackEventRunner(aList, aEvent)
{}
NS_IMETHOD Run() override
{
mList->mPendingTextTrackChange = false;
return TrackEventRunner::Run();
}
};
nsresult
TextTrackList::DispatchTrackEvent(nsIDOMEvent* aEvent)
{
@ -158,13 +173,17 @@ TextTrackList::DispatchTrackEvent(nsIDOMEvent* aEvent)
void
TextTrackList::CreateAndDispatchChangeEvent()
{
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
MOZ_ASSERT(NS_IsMainThread());
if (!mPendingTextTrackChange) {
mPendingTextTrackChange = true;
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
event->InitEvent(NS_LITERAL_STRING("change"), false, false);
event->SetTrusted(true);
event->InitEvent(NS_LITERAL_STRING("change"), false, false);
event->SetTrusted(true);
nsCOMPtr<nsIRunnable> eventRunner = new TrackEventRunner(this, event);
NS_DispatchToMainThread(eventRunner);
nsCOMPtr<nsIRunnable> eventRunner = new ChangeEventRunner(this, event);
NS_DispatchToMainThread(eventRunner);
}
}
void

Просмотреть файл

@ -67,6 +67,8 @@ public:
IMPL_EVENT_HANDLER(addtrack)
IMPL_EVENT_HANDLER(removetrack)
bool mPendingTextTrackChange;
private:
~TextTrackList();

Просмотреть файл

@ -61,10 +61,15 @@ class VideoPuppeteer(object):
'var video = arguments[0];'
'var currentTime = video.wrappedJSObject.currentTime;'
'var duration = video.wrappedJSObject.duration;'
'var buffered = video.wrappedJSObject.buffered;'
'var bufferedRanges = [];'
'for (var i = 0; i < buffered.length; i++) {'
'bufferedRanges.push([buffered.start(i), buffered.end(i)]);'
'}'
'var played = video.wrappedJSObject.played;'
'var timeRanges = [];'
'var playedRanges = [];'
'for (var i = 0; i < played.length; i++) {'
'timeRanges.push([played.start(i), played.end(i)]);'
'playedRanges.push([played.start(i), played.end(i)]);'
'}'
'var totalFrames = '
'video.getVideoPlaybackQuality()["totalVideoFrames"];'
@ -248,6 +253,11 @@ class VideoPuppeteer(object):
current_time: The current time of the wrapped element.
duration: the duration of the wrapped element.
buffered: the buffered ranges of the wrapped element. In its raw form
this is as a list where the first element is the length and the second
element is a list of 2 item lists, where each two items are a buffered
range. Once assigned to the tuple this data should be wrapped in the
TimeRanges class.
played: the played ranges of the wrapped element. In its raw form this
is as a list where the first element is the length and the second
element is a list of 2 item lists, where each two items are a played
@ -267,6 +277,7 @@ class VideoPuppeteer(object):
['current_time',
'duration',
'remaining_time',
'buffered',
'played',
'lag',
'total_frames',
@ -279,23 +290,28 @@ class VideoPuppeteer(object):
"""
Create an instance of the video_state_info named tuple. This function
expects a dictionary populated with the following keys: current_time,
duration, raw_time_ranges, total_frames, dropped_frames, and
duration, raw_played_ranges, total_frames, dropped_frames, and
corrupted_frames.
Aside from raw_time_ranges, see `_video_state_named_tuple` for more
information on the above keys and values. For raw_time_ranges a
Aside from raw_played_ranges, see `_video_state_named_tuple` for more
information on the above keys and values. For raw_played_ranges a
list is expected that can be consumed to make a TimeRanges object.
:return: A named tuple 'video_state_info' derived from arguments and
state information from the puppeteer.
"""
raw_time_ranges = video_state_info_kwargs['raw_time_ranges']
# Remove raw ranges from dict as it is not used in the final named
raw_buffered_ranges = video_state_info_kwargs['raw_buffered_ranges']
raw_played_ranges = video_state_info_kwargs['raw_played_ranges']
# Remove raw ranges from dict as they are not used in the final named
# tuple and will provide an unexpected kwarg if kept.
del video_state_info_kwargs['raw_time_ranges']
del video_state_info_kwargs['raw_buffered_ranges']
del video_state_info_kwargs['raw_played_ranges']
# Create buffered ranges
video_state_info_kwargs['buffered'] = (
TimeRanges(raw_buffered_ranges[0], raw_buffered_ranges[1]))
# Create played ranges
video_state_info_kwargs['played'] = (
TimeRanges(raw_time_ranges[0], raw_time_ranges[1]))
TimeRanges(raw_played_ranges[0], raw_played_ranges[1]))
# Calculate elapsed times
elapsed_current_time = (video_state_info_kwargs['current_time'] -
self._first_seen_time)
@ -327,7 +343,8 @@ class VideoPuppeteer(object):
'return ['
'currentTime,'
'duration,'
'[played.length, timeRanges],'
'[buffered.length, bufferedRanges],'
'[played.length, playedRanges],'
'totalFrames,'
'droppedFrames,'
'corruptedFrames];')
@ -342,8 +359,9 @@ class VideoPuppeteer(object):
information, such as lag. This is stored in the last seen state to
stress that it's based on the snapshot.
"""
keys = ['current_time', 'duration', 'raw_time_ranges', 'total_frames',
'dropped_frames', 'corrupted_frames']
keys = ['current_time', 'duration', 'raw_buffered_ranges',
'raw_played_ranges', 'total_frames', 'dropped_frames',
'corrupted_frames']
values = self._execute_video_script(self._fetch_state_script)
self._last_seen_video_state = (
self._create_video_state_info(**dict(zip(keys, values))))

Просмотреть файл

@ -346,7 +346,8 @@ class YouTubePuppeteer(VideoPuppeteer):
'return ['
'currentTime,'
'duration,'
'[played.length, timeRanges],'
'[buffered.length, bufferedRanges],'
'[played.length, playedRanges],'
'totalFrames,'
'droppedFrames,'
'corruptedFrames,'
@ -371,8 +372,9 @@ class YouTubePuppeteer(VideoPuppeteer):
stress that it's based on the snapshot.
"""
values = self._execute_yt_script(self._fetch_state_script)
video_keys = ['current_time', 'duration', 'raw_time_ranges',
'total_frames', 'dropped_frames', 'corrupted_frames']
video_keys = ['current_time', 'duration', 'raw_buffered_ranges',
'raw_played_ranges', 'total_frames', 'dropped_frames',
'corrupted_frames']
player_keys = ['player_duration', 'player_current_time',
'player_playback_quality', 'player_movie_id',
'player_movie_title', 'player_url', 'player_state',

Просмотреть файл

@ -28,15 +28,22 @@ video.textTracks.addEventListener("change", changed);
is(track.mode, "hidden", "New TextTrack's mode should be hidden.");
track.mode = "showing";
// Bug882674: change the mode again to see if we receive only one
// change event.
track.mode = "hidden";
var eventCount = 0;
function changed(event) {
eventCount++;
is(eventCount, 1, "change event dispatched multiple times.");
is(event.target, video.textTracks, "change event's target should be video.textTracks.");
ok(event instanceof window.Event, "change event should be a simple event.");
ok(!event.bubbles, "change event should not bubble.");
ok(event.isTrusted, "change event should be trusted.");
ok(!event.cancelable, "change event should not be cancelable.");
SimpleTest.finish();
// Delay the finish function call for testing the change event count.
setTimeout(SimpleTest.finish, 0);
}
</script>
</pre>

Просмотреть файл

@ -186,7 +186,6 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
NS_ENSURE_ARG_POINTER(aLoader);
NS_ENSURE_ARG_POINTER(aReporter);
NS_ConvertUTF16toUTF8 utf8Hash(aString);
nsCOMPtr<nsIChannel> channel;
aLoader->GetChannel(getter_AddRefs(channel));
@ -203,7 +202,10 @@ SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
nsresult rv;
rv = verifier.Update(utf8Hash.Length(), (uint8_t*)utf8Hash.get());
nsDependentCString rawBuffer;
rv = aLoader->GetRawBuffer(rawBuffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = verifier.Update(rawBuffer.Length(), (const uint8_t*)rawBuffer.get());
NS_ENSURE_SUCCESS(rv, rv);
return verifier.Verify(aMetadata, channel, aSourceFileURI, aReporter);

Просмотреть файл

@ -0,0 +1,2 @@
/*! Simple test for bug 1271796 */
p::before { content: "\2014"; }

Просмотреть файл

@ -72,13 +72,13 @@
ok(true, "A UTF8 stylesheet (with BOM) was correctly loaded when integrity matched");
}
function bad_correctUTF8BOMHashBlocked() {
todo(false, "We should load UTF8 (with BOM) stylesheets with hashes that match!");
ok(false, "We should load UTF8 (with BOM) stylesheets with hashes that match!");
}
function good_correctUTF8ishHashLoaded() {
ok(true, "A UTF8ish stylesheet was correctly loaded when integrity matched");
}
function bad_correctUTF8ishHashBlocked() {
todo(false, "We should load UTF8ish stylesheets with hashes that match!");
ok(false, "We should load UTF8ish stylesheets with hashes that match!");
}
</script>

Просмотреть файл

@ -1,5 +1,6 @@
[DEFAULT]
support-files =
file_bug_1271796.css
iframe_require-sri-for_main.html
iframe_require-sri-for_main.html^headers^
iframe_script_crossdomain.html
@ -43,3 +44,4 @@ support-files =
[test_style_sameorigin.html]
[test_require-sri-for_csp_directive.html]
[test_require-sri-for_csp_directive_disabled.html]
[test_bug_1271796.html]

Просмотреть файл

@ -0,0 +1,30 @@
<!DOCTYPE HTML>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
function good_shouldLoadEncodingProblem() {
ok(true, "Problematically encoded file correctly loaded.")
};
function bad_shouldntEncounterBug1271796() {
ok(false, "Problematically encoded should load!")
}
window.onload = function() {
SimpleTest.finish();
}
</script>
<link rel="stylesheet" href="file_bug_1271796.css" crossorigin="anonymous"
integrity="sha384-8Xl0mTN4S2QZ5xeliG1sd4Ar9o1xMw6JoJy9RNjyHGQDha7GiLxo8l1llwLVgTNG"
onload="good_shouldLoadEncodingProblem();"
onerror="bad_shouldntEncounterBug1271796();">
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1271796">Bug 1271796</a><br>
<p>This text is prepended by emdash if css has loaded</p>
</body>
</html>

Просмотреть файл

@ -1660,7 +1660,9 @@ CompositorBridgeParent::FlushApzRepaints(const LayerTransactionParent* aLayerTre
// use the compositor's root layer tree id.
layersId = mRootLayerTreeID;
}
mApzcTreeManager->FlushApzRepaints(layersId);
APZThreadUtils::RunOnControllerThread(NS_NewRunnableFunction([=] () {
mApzcTreeManager->FlushApzRepaints(layersId);
}));
}
void
@ -1671,29 +1673,6 @@ CompositorBridgeParent::GetAPZTestData(const LayerTransactionParent* aLayerTree,
*aOutData = sIndirectLayerTrees[mRootLayerTreeID].mApzTestData;
}
class NotifyAPZConfirmedTargetTask : public Runnable
{
public:
explicit NotifyAPZConfirmedTargetTask(const RefPtr<APZCTreeManager>& aAPZCTM,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets)
: mAPZCTM(aAPZCTM),
mInputBlockId(aInputBlockId),
mTargets(aTargets)
{
}
NS_IMETHOD Run() override {
mAPZCTM->SetTargetAPZC(mInputBlockId, mTargets);
return NS_OK;
}
private:
RefPtr<APZCTreeManager> mAPZCTM;
uint64_t mInputBlockId;
nsTArray<ScrollableLayerGuid> mTargets;
};
void
CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
const uint64_t& aInputBlockId,
@ -1702,8 +1681,13 @@ CompositorBridgeParent::SetConfirmedTargetAPZC(const LayerTransactionParent* aLa
if (!mApzcTreeManager) {
return;
}
RefPtr<Runnable> task =
new NotifyAPZConfirmedTargetTask(mApzcTreeManager, aInputBlockId, aTargets);
// Need to specifically bind this since it's overloaded.
void (APZCTreeManager::*setTargetApzcFunc)
(uint64_t, const nsTArray<ScrollableLayerGuid>&) =
&APZCTreeManager::SetTargetAPZC;
RefPtr<Runnable> task = NewRunnableMethod
<uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>>
(mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
APZThreadUtils::RunOnControllerThread(task.forget());
}

Просмотреть файл

@ -592,40 +592,42 @@ class gfxContextMatrixAutoSaveRestore
{
public:
gfxContextMatrixAutoSaveRestore() :
mContext(nullptr)
mContext(nullptr)
{
}
explicit gfxContextMatrixAutoSaveRestore(gfxContext *aContext) :
mContext(aContext), mMatrix(aContext->CurrentMatrix())
mContext(aContext), mMatrix(aContext->CurrentMatrix())
{
}
~gfxContextMatrixAutoSaveRestore()
{
if (mContext) {
mContext->SetMatrix(mMatrix);
}
if (mContext) {
mContext->SetMatrix(mMatrix);
}
}
void SetContext(gfxContext *aContext)
{
NS_ASSERTION(!mContext, "Not going to restore the matrix on some context!");
mContext = aContext;
mMatrix = aContext->CurrentMatrix();
NS_ASSERTION(!mContext,
"Not going to restore the matrix on some context!");
mContext = aContext;
mMatrix = aContext->CurrentMatrix();
}
void Restore()
{
if (mContext) {
mContext->SetMatrix(mMatrix);
}
if (mContext) {
mContext->SetMatrix(mMatrix);
mContext = nullptr;
}
}
const gfxMatrix& Matrix()
{
MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
return mMatrix;
MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix");
return mMatrix;
}
bool HasMatrix() const { return !!mContext; }

6
gradle/wrapper/gradle-wrapper.properties поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
#Tue Apr 12 09:52:06 CEST 2016
#Fri Sep 16 15:41:50 PDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
distributionSha256Sum=496d60c331f8666f99b66d08ff67a880697a7e85a9d9b76ff08814cf97f61a4c
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionSha256Sum=88a910cdf2e03ebbb5fe90f7ecf534fc9ac22e12112dc9a2fee810c598a76091

Просмотреть файл

@ -328,7 +328,7 @@ RestyleTracker::AddPendingRestyle(Element* aElement,
// We can only treat this element as a restyle root if we would
// actually restyle its descendants (so either call
// ReResolveStyleContext on it or just reframe it).
// ElementRestyler::Restyle on it or just reframe it).
if ((aRestyleHint & ~eRestyle_LaterSiblings) ||
(aMinChangeHint & nsChangeHint_ReconstructFrame)) {
Element* cur =

Просмотреть файл

@ -3238,7 +3238,8 @@ nsCSSRendering::PaintBackgroundWithSC(const PaintBGParams& aParams,
clipSet = true;
if (!clipBorderArea.IsEqualEdges(aParams.borderArea)) {
// We're drawing the background for the joined continuation boxes
// so we need to clip that to the slice that we want for this frame.
// so we need to clip that to the slice that we want for this
// frame.
gfxRect clip =
nsLayoutUtils::RectToGfxRect(aParams.borderArea, appUnitsPerPixel);
autoSR.EnsureSaved(ctx);
@ -3259,7 +3260,8 @@ nsCSSRendering::PaintBackgroundWithSC(const PaintBGParams& aParams,
if (!state.mFillArea.IsEmpty()) {
if (co != CompositionOp::OP_OVER) {
NS_ASSERTION(ctx->CurrentOp() == CompositionOp::OP_OVER,
"It is assumed the initial op is OP_OVER, when it is restored later");
"It is assumed the initial op is OP_OVER, when it is "
"restored later");
ctx->SetOp(co);
}

Просмотреть файл

@ -6832,16 +6832,28 @@ bool nsDisplayMask::TryMerge(nsDisplayItem* aItem)
// items for the same content element should be merged into a single
// compositing group
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
if (aItem->Frame()->GetContent() != mFrame->GetContent())
if (aItem->Frame()->GetContent() != mFrame->GetContent()) {
return false;
if (aItem->GetClip() != GetClip())
}
if (aItem->GetClip() != GetClip()) {
return false;
if (aItem->ScrollClip() != ScrollClip())
}
if (aItem->ScrollClip() != ScrollClip()) {
return false;
}
// Do not merge if mFrame has mask. Continuation frames should apply mask
// independently(just like nsDisplayBackgroundImage).
const nsStyleSVGReset *style = mFrame->StyleSVGReset();
if (style->mMask.HasLayerWithImage()) {
return false;
}
nsDisplayMask* other = static_cast<nsDisplayMask*>(aItem);
MergeFromTrackingMergedFrames(other);
mEffectsBounds.UnionRect(mEffectsBounds,
other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
return true;
}
@ -6886,7 +6898,7 @@ nsDisplayMask::GetLayerState(nsDisplayListBuilder* aBuilder,
}
bool nsDisplayMask::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion)
nsRegion* aVisibleRegion)
{
// Our children may be made translucent or arbitrarily deformed so we should
// not allow them to subtract area from aVisibleRegion.

Просмотреть файл

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Masking: mask on inline element</title>
<link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style type="text/css">
div {
width: 100px;
height: 100px;
font-size: 100px;
line-height: 100px;
}
div.mask-by-png {
mask-image: url(support/transparent-100x50-blue-100x50.png);
}
</style>
</head>
<body>
<div class="mask-by-png">A</div>
<div class="mask-by-png">B</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Masking: mask on inline element</title>
<link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://www.w3.org/TR/css-masking-1/#the-mask-image">
<link rel="match" href="mask-image-6-ref.html">
<meta name="assert" content="Test checks whether mask on inline elemnt works correctly or not.">
<style type="text/css">
div {
width: 100px;
height: 100px;
}
span {
font-size: 100px;
line-height: 100px;
mask-image: url(support/transparent-100x50-blue-100x50.png);
mask-repeat: repeat;
}
</style>
</head>
<body>
<div>
<span>A B</span>
</div>
</body>
</html>

Просмотреть файл

@ -29,6 +29,7 @@ fuzzy-if(skiaContent,50,50) == mask-image-3f.html mask-image-3-ref.html
== mask-image-4a.html blank.html
== mask-image-4b.html blank.html
== mask-image-5.html mask-image-5-ref.html
== mask-image-6.html mask-image-6-ref.html
# mask-clip test cases
== mask-clip-1.html mask-clip-1-ref.html

Просмотреть файл

@ -32,7 +32,7 @@ enum class CSSPseudoElementType : uint8_t;
* (with a few exceptions, like system color changes), the data in an
* nsStyleContext are also immutable (with the additional exception of
* GetUniqueStyleData). When style data change,
* nsFrameManager::ReResolveStyleContext creates a new style context.
* ElementRestyler::Restyle creates a new style context.
*
* Style contexts are reference counted. References are generally held
* by:

Просмотреть файл

@ -1318,7 +1318,8 @@ nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aNewData) const
hint |= nsChangeHint_RepaintFrame;
}
hint |= mMask.CalcDifference(aNewData.mMask, nsChangeHint_RepaintFrame);
hint |= mMask.CalcDifference(aNewData.mMask,
nsStyleImageLayers::LayerType::Mask);
return hint;
}
@ -2427,8 +2428,13 @@ nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers &aSource)
nsChangeHint
nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aNewLayers,
nsChangeHint aPositionChangeHint) const
nsStyleImageLayers::LayerType aType) const
{
nsChangeHint positionChangeHint =
(aType == nsStyleImageLayers::LayerType::Background)
? nsChangeHint_UpdateBackgroundPosition
: nsChangeHint_RepaintFrame;
nsChangeHint hint = nsChangeHint(0);
const nsStyleImageLayers& moreLayers =
@ -2442,7 +2448,7 @@ nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aNewLayers,
if (i < lessLayers.mImageCount) {
nsChangeHint layerDifference =
moreLayers.mLayers[i].CalcDifference(lessLayers.mLayers[i],
aPositionChangeHint);
positionChangeHint);
hint |= layerDifference;
if (layerDifference &&
((moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) ||
@ -2457,6 +2463,11 @@ nsStyleImageLayers::CalcDifference(const nsStyleImageLayers& aNewLayers,
}
}
if (aType == nsStyleImageLayers::LayerType::Mask &&
mImageCount != aNewLayers.mImageCount) {
hint |= nsChangeHint_UpdateEffects;
}
if (hint) {
return hint;
}
@ -2729,7 +2740,7 @@ nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aNewL
{
nsChangeHint hint = nsChangeHint(0);
if (mSourceURI != aNewLayer.mSourceURI) {
hint |= nsChangeHint_RepaintFrame;
hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects;
// If Layer::mSourceURI links to a SVG mask, it has a fragment. Not vice
// versa. Here are examples of URI contains a fragment, two of them link
@ -2757,10 +2768,9 @@ nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aNewL
}
}
// Return nsChangeHint_UpdateEffects and nsChangeHint_UpdateOverflow if
// either URI might link to an SVG mask.
// Return nsChangeHint_UpdateOverflow if either URI might link to an SVG
// mask.
if (maybeSVGMask) {
hint |= nsChangeHint_UpdateEffects;
// Mask changes require that we update the PreEffectsBBoxProperty,
// which is done during overflow computation.
hint |= nsChangeHint_UpdateOverflow;
@ -2827,7 +2837,7 @@ nsStyleBackground::CalcDifference(const nsStyleBackground& aNewData) const
}
hint |= mImage.CalcDifference(aNewData.mImage,
nsChangeHint_UpdateBackgroundPosition);
nsStyleImageLayers::LayerType::Background);
return hint;
}
@ -3555,17 +3565,17 @@ nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
nsChangeHint
nsStyleContent::CalcDifference(const nsStyleContent& aNewData) const
{
// In ReResolveStyleContext we assume that if there's no existing
// In ElementRestyler::Restyle we assume that if there's no existing
// ::before or ::after and we don't have to restyle children of the
// node then we can't end up with a ::before or ::after due to the
// restyle of the node itself. That's not quite true, but the only
// exception to the above is when the 'content' property of the node
// changes and the pseudo-element inherits the changed value. Since
// the code here triggers a frame change on the node in that case,
// the optimization in ReResolveStyleContext is ok. But if we ever
// the optimization in ElementRestyler::Restyle is ok. But if we ever
// change this code to not reconstruct frames on changes to the
// 'content' property, then we will need to revisit the optimization
// in ReResolveStyleContext.
// in ElementRestyler::Restyle.
// Unfortunately we need to reframe even if the content lengths are the same;
// a simple reflow will not pick up different text or different image URLs,

Просмотреть файл

@ -823,7 +823,7 @@ struct nsStyleImageLayers {
}
nsChangeHint CalcDifference(const nsStyleImageLayers& aNewLayers,
nsChangeHint aPositionChangeHint) const;
nsStyleImageLayers::LayerType aType) const;
bool HasLayerWithImage() const;

Просмотреть файл

@ -337,7 +337,7 @@ public:
/**
* StyleContextChanged
*
* To be called from nsFrameManager::ReResolveStyleContext when the
* To be called from RestyleManager::TryStartingTransition when the
* style of an element has changed, to initiate transitions from
* that style change. For style contexts with :before and :after
* pseudos, aElement is expected to be the generated before/after

Просмотреть файл

@ -180,11 +180,6 @@ GetOffsetToBoundingBox(nsIFrame* aFrame)
// no offset adjustment to make.
return nsPoint();
}
// We could allow aFrame to be any continuation, but since that would require
// a GetPrevContinuation() virtual call and conditional returns, and since
// all our current consumers always pass in the first continuation, we don't
// currently bother.
NS_ASSERTION(!aFrame->GetPrevContinuation(), "Not first continuation");
// The GetAllInFlowRectsUnion() call gets the union of the frame border-box
// rects over all continuations, relative to the origin (top-left of the
@ -434,8 +429,10 @@ ComputeClipExtsInDeviceSpace(gfxContext& aCtx)
: IntRect();
}
typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
static IntRect
ComputeMaskGeometry(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
ComputeMaskGeometry(const PaintFramesParams& aParams,
const nsStyleSVGReset *svgReset,
const nsPoint& aOffsetToUserSpace,
const nsTArray<nsSVGMaskFrame *>& aMaskFrames)
@ -494,7 +491,7 @@ ComputeMaskGeometry(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
}
static DrawResult
GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
GenerateMaskSurface(const PaintFramesParams& aParams,
float aOpacity, nsStyleContext* aSC,
const nsTArray<nsSVGMaskFrame *>& aMaskFrames,
const nsPoint& aOffsetToUserSpace,
@ -612,7 +609,7 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
}
static float
ComputeOpacity(const nsSVGIntegrationUtils::PaintFramesParams& aParams)
ComputeOpacity(const PaintFramesParams& aParams)
{
nsIFrame* frame = aParams.frame;
float opacity = frame->StyleEffects()->mOpacity;
@ -626,8 +623,8 @@ ComputeOpacity(const nsSVGIntegrationUtils::PaintFramesParams& aParams)
}
static bool
ValidateSVGFrame(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
bool aHasSVGLayout, DrawResult* aResult)
ValidateSVGFrame(const PaintFramesParams& aParams, bool aHasSVGLayout,
DrawResult* aResult)
{
#ifdef DEBUG
NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
@ -655,28 +652,37 @@ ValidateSVGFrame(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
return true;
}
/**
* Setup transform matrix of a gfx context by a specific frame. Depend on
* aClipCtx, this function may clip that context by the visual overflow area
* of aFrame.
*
* @param aFrame is the target frame.
* @param aOffsetToBoundingBox returns the offset between the reference frame
* and the bounding box of aFrame.
* @oaram aOffsetToUserSpace returns the offset between the reference frame and
* the user space coordinate of aFrame.
* @param aClipCtx indicate whether clip aParams.ctx by visual overflow rect of
* aFrame or not.
*/
static void
SetupContextMatrix(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
nsPoint& aOffsetToBoundingBox,
nsPoint& aToUserSpace,
nsPoint& aOffsetToUserSpace)
SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace,
bool aClipCtx)
{
nsIFrame* frame = aParams.frame;
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset;
if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
GetOffsetToBoundingBox(aFrame);
if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
/* Snap the offset if the reference frame is not a SVG frame,
* since other frames will be snapped to pixel when rendering. */
aOffsetToBoundingBox = nsPoint(
frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
}
// After applying only "aOffsetToBoundingBox", aCtx would have its origin at
// the top left corner of frame's bounding box (over all continuations).
// After applying only "aOffsetToBoundingBox", aParams.ctx would have its
// origin at the top left corner of frame's bounding box (over all
// continuations).
// However, SVG painting needs the origin to be located at the origin of the
// SVG frame's "user space", i.e. the space in which, for example, the
// frame's BBox lives.
@ -686,28 +692,36 @@ SetupContextMatrix(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
// frame's position so that SVG painting can later add it again and the
// frame is painted in the right place.
gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(frame);
aToUserSpace =
gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
nsPoint toUserSpace =
nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
aOffsetToUserSpace = aOffsetToBoundingBox - aToUserSpace;
aOffsetToUserSpace = aOffsetToBoundingBox - toUserSpace;
#ifdef DEBUG
bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
NS_ASSERTION(hasSVGLayout || aOffsetToBoundingBox == aOffsetToUserSpace,
"For non-SVG frames there shouldn't be any additional offset");
#endif
gfxPoint devPixelOffsetToUserSpace =
nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
frame->PresContext()->AppUnitsPerDevPixel());
aParams.ctx.SetMatrix(aParams.ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
aFrame->PresContext()->AppUnitsPerDevPixel());
gfxContext& context = aParams.ctx;
context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
if (aClipCtx) {
nsRect clipRect =
aParams.frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
context.Clip(NSRectToSnappedRect(clipRect,
aFrame->PresContext()->AppUnitsPerDevPixel(),
*context.GetDrawTarget()));
}
}
static already_AddRefed<gfxContext>
CreateBlendTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
IntPoint& aTargetOffset)
CreateBlendTarget(const PaintFramesParams& aParams, IntPoint& aTargetOffset)
{
MOZ_ASSERT(aParams.frame->StyleEffects()->mMixBlendMode !=
NS_STYLE_BLEND_NORMAL);
@ -731,8 +745,8 @@ CreateBlendTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
}
static void
BlendToTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
gfxContext* aTarget, const IntPoint& aTargetOffset)
BlendToTarget(const PaintFramesParams& aParams, gfxContext* aTarget,
const IntPoint& aTargetOffset)
{
MOZ_ASSERT(aParams.frame->StyleEffects()->mMixBlendMode !=
NS_STYLE_BLEND_NORMAL);
@ -782,11 +796,6 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
gfxContext& context = aParams.ctx;
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
nsPoint offsetToBoundingBox;
nsPoint toUserSpace;
nsPoint offsetToUserSpace;
SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace,
offsetToUserSpace);
/* Properties are added lazily and may have been removed by a restyle,
so make sure all applicable ones are set again. */
@ -830,12 +839,23 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
MOZ_ASSERT_IF(shouldGenerateClipMaskLayer,
!shouldApplyClipPath && !shouldApplyBasicShape);
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
// These are used if we require a temporary surface for a custom blend mode.
// Clip the source context first, so that we can generate a smaller temporary
// surface. (Since we will clip this context in SetupContextMatrix, a pair
// of save/restore is needed.)
context.Save();
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, true);
IntPoint targetOffset;
RefPtr<gfxContext> target =
(aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
? RefPtr<gfxContext>(&aParams.ctx).forget()
: CreateBlendTarget(aParams, targetOffset);
context.Restore();
if (!target) {
return DrawResult::TEMPORARY_ERROR;
}
@ -846,52 +866,79 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (shouldGenerateMask) {
context.Save();
nsRect clipRect =
frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
context.Clip(NSRectToSnappedRect(clipRect,
frame->PresContext()->AppUnitsPerDevPixel(),
*context.GetDrawTarget()));
gfxContextMatrixAutoSaveRestore matSR;
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface;
if (shouldGenerateMaskLayer) {
matSR.SetContext(&context);
// For css-mask, we want to generate a mask for each continuation frame,
// so we setup context matrix by the position of the current frame,
// instead of the first continuation frame.
SetupContextMatrix(frame, aParams, offsetToBoundingBox,
offsetToUserSpace, true);
result = GenerateMaskSurface(aParams, opacity,
firstFrame->StyleContext(),
maskFrames, offsetToUserSpace,
maskTransform, maskSurface);
}
if (shouldGenerateMaskLayer && !maskSurface) {
// Entire surface is clipped out.
context.Restore();
return result;
context.PopClip();
if (!maskSurface) {
// Entire surface is clipped out.
return result;
}
}
if (shouldGenerateClipMaskLayer) {
matSR.Restore();
matSR.SetContext(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, true);
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface =
clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
&clippedMaskTransform, maskSurface,
maskTransform, &result);
context.PopClip();
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
} else {
// Either entire surface is clipped out, or gfx buffer allocation
// failure in nsSVGClipPathFrame::GetClipMask.
return result;
}
}
// opacity != 1.0f.
if (!shouldGenerateClipMaskLayer && !shouldGenerateMaskLayer) {
MOZ_ASSERT(opacity != 1.0f);
matSR.SetContext(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, true);
}
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
* we can just do normal painting and get it clipped appropriately.
*/
if (shouldApplyClipPath) {
if (shouldApplyClipPath || shouldApplyBasicShape) {
context.Save();
clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
} else if (shouldApplyBasicShape) {
context.Save();
nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, false);
MOZ_ASSERT(!shouldApplyClipPath || !shouldApplyBasicShape);
if (shouldApplyClipPath) {
clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
} else {
nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
}
}
/* Paint the child */
@ -900,7 +947,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
RefPtr<gfxContext> oldCtx = basic->GetTarget();
basic->SetTarget(target);
aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
aParams.builder);
aParams.builder);
basic->SetTarget(oldCtx);
if (shouldApplyClipPath || shouldApplyBasicShape) {
@ -909,7 +956,12 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
if (shouldGenerateMask) {
target->PopGroupAndBlend();
context.Restore();
if (!shouldGenerateClipMaskLayer && !shouldGenerateMaskLayer) {
MOZ_ASSERT(opacity != 1.0f);
// Pop the clip push by SetupContextMatrix
context.PopClip();
}
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
@ -940,14 +992,6 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
return DrawResult::SUCCESS;
}
gfxContext& context = aParams.ctx;
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
nsPoint offsetToBoundingBox;
nsPoint toUserSpace;
nsPoint offsetToUserSpace;
SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace,
offsetToUserSpace);
/* Properties are added lazily and may have been removed by a restyle,
so make sure all applicable ones are set again. */
nsIFrame* firstFrame =
@ -959,24 +1003,28 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
return DrawResult::NOT_READY;
}
gfxContext& context = aParams.ctx;
nsPoint offsetToBoundingBox;
nsPoint offsetToUserSpace;
// These are used if we require a temporary surface for a custom blend mode.
// Clip the source context first, so that we can generate a smaller temporary
// surface. (Since we will clip this context in SetupContextMatrix, a pair
// of save/restore is needed.)
gfxContextAutoSaveRestore autoSR(&context);
SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
offsetToUserSpace, true);
IntPoint targetOffset;
RefPtr<gfxContext> target =
(aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
? RefPtr<gfxContext>(&aParams.ctx).forget()
: CreateBlendTarget(aParams, targetOffset);
if (!target) {
context.Restore();
return DrawResult::TEMPORARY_ERROR;
}
if (opacity != 1.0f) {
context.Save();
nsRect clipRect =
frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
context.Clip(NSRectToSnappedRect(clipRect,
frame->PresContext()->AppUnitsPerDevPixel(),
*context.GetDrawTarget()));
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
nullptr, Matrix());
}
@ -991,7 +1039,6 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
if (opacity != 1.0f) {
target->PopGroupAndBlend();
context.Restore();
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {

Просмотреть файл

@ -340,9 +340,10 @@ bool
MP4Metadata::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
{
#ifdef MOZ_RUST_MP4PARSE
if (mRust && mPreferRust) {
return mRust->ReadTrackIndex(aDest, aTrackID);
if (mRust && mPreferRust && mRust->ReadTrackIndex(aDest, aTrackID)) {
return true;
}
aDest.Clear();
#endif
return mStagefright->ReadTrackIndex(aDest, aTrackID);
}
@ -839,7 +840,7 @@ MP4MetadataRust::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::T
}
// For non-fragmented mp4.
MOZ_ASSERT(false, "Not yet implemented");
NS_WARNING("Not yet implemented");
return false;
}

Просмотреть файл

@ -227,7 +227,7 @@ dependencies {
compile project(':thirdparty')
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.robolectric:robolectric:3.1.2'
testCompile 'org.simpleframework:simple-http:6.0.1'
testCompile 'org.mockito:mockito-core:1.10.19'
@ -412,3 +412,47 @@ afterEvaluate {
dependsOn tasks["spoonLocalOldDebugAndroidTest"]
}
}
// Bug 1299015: Complain to treeherder if checkstyle, lint, or unittest fails. It's not obvious
// how to listen to individual errors in most cases, so we just link to the reports for now.
def makeTaskExecutionListener(artifactRootUrl) {
return new TaskExecutionListener() {
void beforeExecute(Task task) {
// Do nothing.
}
void afterExecute(Task task, TaskState state) {
if (!state.failure) {
return
}
// Link to the failing report. The task path and the report path
// depend on the android-lint task in
// taskcluster/ci/android-stuff/kind.yml. It's not possible to link
// directly, so for now consumers will need to copy-paste the URL.
switch (task.path) {
case ':app:checkstyle':
def url = "${artifactRootUrl}/public/android/checkstyle/checkstyle.xml"
println "TEST-UNEXPECTED-FAIL | android-checkstyle | Checkstyle rule violations were found. See the report at: $url"
break
case ':app:lintAutomationDebug':
def url = "${artifactRootUrl}/public/android/lint/lint-results-automationDebug.html"
println "TEST-UNEXPECTED-FAIL | android-lint | Lint found errors in the project; aborting build. See the report at: $url"
break
case ':app:testAutomationDebugUnitTest':
def url = "${artifactRootUrl}/public/android/unittest/automationDebug/index.html"
println "TEST-UNEXPECTED-FAIL | android-test | There were failing tests. See the report at: $url"
break
}
}
}
}
// TASK_ID and RUN_ID are provided by docker-worker; see
// https://docs.taskcluster.net/manual/execution/workers/docker-worker.
if (System.env.TASK_ID && System.env.RUN_ID) {
def artifactRootUrl = "https://queue.taskcluster.net/v1/task/${System.env.TASK_ID}/runs/${System.env.RUN_ID}/artifacts"
gradle.addListener(makeTaskExecutionListener(artifactRootUrl))
}

Просмотреть файл

@ -12,4 +12,5 @@ EXTRA_COMPONENTS += [
DIRS += ['schemas']
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']

Просмотреть файл

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
../../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
tags = webextensions
[test_ext_all_apis.html]

Просмотреть файл

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<meta charset="utf-8">
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
</head>
<body>
<script>
"use strict";
/* exported expectedContentApisTargetSpecific, expectedBackgroundApisTargetSpecific */
let expectedContentApisTargetSpecific = [
];
let expectedBackgroundApisTargetSpecific = [
];
</script>
<script src="test_ext_all_apis.js"></script>
</body>
</html>

Просмотреть файл

@ -35,16 +35,16 @@
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
"digest": "43754910576cf6173f0dcb215ec7988f2a30dbfe32050f53ac7a9088b8b878c3ead80e02afc1dc31e229386116c82e88a403a309e5b5695f0b74f5defb13dec7",
"size": 42832932
"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
"size": 47315996
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
"digest": "990edc0e4039dbe5f77790ef59dc0d58faebbb8c82ee497615c7991eec99fe4668d0ab05508c48664b635ff6c0cfd4272db464ae1efaa548e471ab451fe0944f",
"size": 51955340
"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
"size": 51512016
},
{
"algorithm": "sha512",

Просмотреть файл

@ -50,16 +50,16 @@
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
"digest": "43754910576cf6173f0dcb215ec7988f2a30dbfe32050f53ac7a9088b8b878c3ead80e02afc1dc31e229386116c82e88a403a309e5b5695f0b74f5defb13dec7",
"size": 42832932
"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
"size": 47315996
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
"digest": "990edc0e4039dbe5f77790ef59dc0d58faebbb8c82ee497615c7991eec99fe4668d0ab05508c48664b635ff6c0cfd4272db464ae1efaa548e471ab451fe0944f",
"size": 51955340
"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
"size": 51512016
},
{
"size": 30899096,

Просмотреть файл

@ -60,16 +60,16 @@
"visibility": "public",
"filename": "jcentral.tar.xz",
"unpack": true,
"digest": "43754910576cf6173f0dcb215ec7988f2a30dbfe32050f53ac7a9088b8b878c3ead80e02afc1dc31e229386116c82e88a403a309e5b5695f0b74f5defb13dec7",
"size": 42832932
"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
"size": 47315996
},
{
"algorithm": "sha512",
"visibility": "public",
"filename": "gradle-dist.tar.xz",
"unpack": true,
"digest": "990edc0e4039dbe5f77790ef59dc0d58faebbb8c82ee497615c7991eec99fe4668d0ab05508c48664b635ff6c0cfd4272db464ae1efaa548e471ab451fe0944f",
"size": 51955340
"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
"size": 51512016
},
{
"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",

Просмотреть файл

@ -25,7 +25,7 @@
package org.mozilla.gecko.background.testhelpers;
import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.FileFsFile;
@ -46,7 +46,7 @@ import org.robolectric.util.ReflectionHelpers;
* that uses a Gradle `buildConfigField` to find build outputs.
* See https://github.com/robolectric/robolectric/issues/1648#issuecomment-113731011.
*/
public class TestRunner extends RobolectricGradleTestRunner {
public class TestRunner extends RobolectricTestRunner {
private FsFile buildFolder;
public TestRunner(Class<?> klass) throws InitializationError {

Просмотреть файл

@ -10,6 +10,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -51,6 +52,13 @@ public class BrowserProviderHistoryTest extends BrowserProviderHistoryVisitsTest
).build();
}
@After
@Override
public void tearDown() {
thumbnailClient.release();
super.tearDown();
}
/**
* Test aggressive expiration on new (recent) history items
*/

Просмотреть файл

@ -54,7 +54,7 @@ public class BrowserProviderHistoryVisitsTestBase {
}
/* package-private */ Uri insertHistoryItem(String url, String guid, Long lastVisited, Integer visitCount) throws RemoteException {
return insertHistoryItem(url, guid, System.currentTimeMillis(), null, null);
return insertHistoryItem(url, guid, lastVisited, visitCount, null);
}
/* package-private */ Uri insertHistoryItem(String url, String guid, Long lastVisited, Integer visitCount, String title) throws RemoteException {

Просмотреть файл

@ -79,4 +79,10 @@ interface nsIUnicharStreamLoader : nsIStreamListener
* called.
*/
readonly attribute ACString charset;
/**
* Get the raw bytes as seen on the wire prior to character converstion.
* Used by Subresource Integrity checker to generate the correct hash.
*/
readonly attribute ACString rawBuffer;
};

Просмотреть файл

@ -102,10 +102,19 @@ nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
mContext = nullptr;
mChannel = nullptr;
mCharset.Truncate();
mRawData.Truncate();
mRawBuffer.Truncate();
mBuffer.Truncate();
return rv;
}
NS_IMETHODIMP
nsUnicharStreamLoader::GetRawBuffer(nsACString& aRawBuffer)
{
aRawBuffer = mRawBuffer;
return NS_OK;
}
/* nsIStreamListener implementation */
NS_IMETHODIMP
nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
@ -220,6 +229,10 @@ nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
return NS_ERROR_OUT_OF_MEMORY;
}
if (!self->mRawBuffer.Append(aSegment, aCount, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = self->mDecoder->Convert(aSegment,
&srcLen,
self->mBuffer.BeginWriting() + haveRead,

Просмотреть файл

@ -47,6 +47,10 @@ protected:
// It will be passed to the OnDetermineCharset callback.
nsCString mRawData;
// Holds complete raw bytes as received so that SRI checks can be
// calculated on the raw data prior to character conversion.
nsCString mRawBuffer;
// This holds the complete contents of the stream so far, after
// decoding to UTF-16. It will be passed to the OnStreamComplete
// callback.

Просмотреть файл

@ -15,6 +15,9 @@ from mozbuild.shellutil import quote as shell_quote
from .common import CommonBackend
from ..frontend.data import (
ContextDerived,
Defines,
GeneratedFile,
HostDefines,
)
from ..util import (
FileAvoidWrite,
@ -33,6 +36,9 @@ class BackendTupfile(object):
self.environment = environment
self.name = mozpath.join(objdir, 'Tupfile')
self.rules_included = False
self.shell_exported = False
self.defines = []
self.host_defines = []
self.fh = FileAvoidWrite(self.name, capture_diff=True)
self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
@ -46,18 +52,39 @@ class BackendTupfile(object):
self.write('include_rules\n')
self.rules_included = True
def rule(self, cmd, inputs=None, outputs=None, display=None, extra_outputs=None):
def rule(self, cmd, inputs=None, outputs=None, display=None, extra_outputs=None, check_unchanged=False):
inputs = inputs or []
outputs = outputs or []
display = display or ""
self.include_rules()
flags = ""
if check_unchanged:
# This flag causes tup to compare the outputs with the previous run
# of the command, and skip the rest of the DAG for any that are the
# same.
flags += "o"
if display:
caret_text = flags + ' ' + display
else:
caret_text = flags
self.write(': %(inputs)s |> %(display)s%(cmd)s |> %(outputs)s%(extra_outputs)s\n' % {
'inputs': ' '.join(inputs),
'display': '^ %s^ ' % display if display else '',
'display': '^%s^ ' % caret_text if caret_text else '',
'cmd': ' '.join(cmd),
'outputs': ' '.join(outputs),
'extra_outputs': ' | ' + ' '.join(extra_outputs) if extra_outputs else '',
})
def export_shell(self):
if not self.shell_exported:
# These are used by mach/mixin/process.py to determine the current
# shell.
for var in ('SHELL', 'MOZILLABUILD', 'COMSPEC'):
self.write('export %s\n' % var)
self.shell_exported = True
def close(self):
return self.fh.close()
@ -85,6 +112,17 @@ class TupOnly(CommonBackend, PartialBackend):
self.environment.topsrcdir, self.environment.topobjdir)
return self._backend_files[objdir]
def _get_backend_file_for(self, obj):
return self._get_backend_file(obj.relativedir)
def _py_action(self, action):
cmd = [
'$(PYTHON)',
'-m',
'mozbuild.action.%s' % action,
]
return cmd
def consume_object(self, obj):
"""Write out build files necessary to build with tup."""
@ -98,6 +136,43 @@ class TupOnly(CommonBackend, PartialBackend):
if consumed:
return False
backend_file = self._get_backend_file_for(obj)
if isinstance(obj, GeneratedFile):
# TODO: These are directories that don't work in the tup backend
# yet, because things they depend on aren't built yet.
skip_directories = (
'build', # FinalTargetPreprocessedFiles
'layout/style/test', # HostSimplePrograms
'toolkit/library', # libxul.so
)
if obj.script and obj.method and obj.relobjdir not in skip_directories:
backend_file.export_shell()
cmd = self._py_action('file_generate')
cmd.extend([
obj.script,
obj.method,
obj.outputs[0],
'%s.pp' % obj.outputs[0], # deps file required
])
full_inputs = [f.full_path for f in obj.inputs]
cmd.extend(full_inputs)
outputs = []
outputs.extend(obj.outputs)
outputs.append('%s.pp' % obj.outputs[0])
backend_file.rule(
display='python {script}:{method} -> [%o]'.format(script=obj.script, method=obj.method),
cmd=cmd,
inputs=full_inputs,
outputs=outputs,
)
elif isinstance(obj, Defines):
self._process_defines(backend_file, obj)
elif isinstance(obj, HostDefines):
self._process_defines(backend_file, obj, host=True)
return True
def consume_finished(self):
@ -123,34 +198,24 @@ class TupOnly(CommonBackend, PartialBackend):
fh.write('PYTHON_PATH = $(PYTHON) $(topsrcdir)/config/pythonpath.py\n')
fh.write('PLY_INCLUDE = -I$(topsrcdir)/other-licenses/ply\n')
fh.write('IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser\n')
fh.write('IDL_PARSER_CACHE_DIR = $(MOZ_OBJ_ROOT)/xpcom/idl-parser\n')
fh.write('IDL_PARSER_CACHE_DIR = $(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl\n')
# Run 'tup init' if necessary.
if not os.path.exists(mozpath.join(self.environment.topsrcdir, ".tup")):
tup = self.environment.substs.get('TUP', 'tup')
self._cmd.run_process(cwd=self.environment.topsrcdir, log_name='tup', args=[tup, 'init'])
def _process_defines(self, backend_file, obj, host=False):
defines = list(obj.get_defines())
if defines:
if host:
backend_file.host_defines = defines
else:
backend_file.defines = defines
def _handle_idl_manager(self, manager):
# TODO: This should come from GENERATED_FILES, and can be removed once
# those are implemented.
backend_file = self._get_backend_file('xpcom/idl-parser')
backend_file.rule(
display='python header.py -> [%o]',
cmd=[
'$(PYTHON_PATH)',
'$(PLY_INCLUDE)',
'$(topsrcdir)/xpcom/idl-parser/xpidl/header.py',
],
outputs=['xpidlyacc.py', 'xpidllex.py'],
)
backend_file = self._get_backend_file('xpcom/xpidl')
# These are used by mach/mixin/process.py to determine the current
# shell.
for var in ('SHELL', 'MOZILLABUILD', 'COMSPEC'):
backend_file.write('export %s\n' % var)
backend_file.export_shell()
for module, data in sorted(manager.modules.iteritems()):
dest, idls = data
@ -160,7 +225,7 @@ class TupOnly(CommonBackend, PartialBackend):
'-I$(IDL_PARSER_DIR)',
'-I$(IDL_PARSER_CACHE_DIR)',
'$(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py',
'--cache-dir', '$(MOZ_OBJ_ROOT)/xpcom/idl-parser',
'--cache-dir', '$(IDL_PARSER_CACHE_DIR)',
'$(DIST)/idl',
'$(DIST)/include',
'$(MOZ_OBJ_ROOT)/%s/components' % dest,
@ -172,14 +237,26 @@ class TupOnly(CommonBackend, PartialBackend):
outputs.extend(['$(MOZ_OBJ_ROOT)/dist/include/%s.h' % f for f in sorted(idls)])
backend_file.rule(
inputs=[
'$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidllex.py',
'$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidlyacc.py',
'$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidllex.py',
'$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidlyacc.py',
],
display='XPIDL %s' % module,
cmd=cmd,
outputs=outputs,
)
def _preprocess(self, backend_file, input_file):
cmd = self._py_action('preprocessor')
cmd.extend(backend_file.defines)
cmd.extend(['$(ACDEFINES)', '%f', '-o', '%o'])
backend_file.rule(
inputs=[input_file],
display='Preprocess %o',
cmd=cmd,
outputs=[mozpath.basename(input_file)],
)
def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
unified_ipdl_cppsrcs_mapping):
# TODO: This isn't implemented yet in the tup backend, but it is called
@ -189,9 +266,33 @@ class TupOnly(CommonBackend, PartialBackend):
def _handle_webidl_build(self, bindings_dir, unified_source_mapping,
webidls, expected_build_output_files,
global_define_files):
# TODO: This isn't implemented yet in the tup backend, but it is called
# by the CommonBackend.
pass
backend_file = self._get_backend_file('dom/bindings')
backend_file.export_shell()
for source in sorted(webidls.all_preprocessed_sources()):
self._preprocess(backend_file, source)
cmd = self._py_action('webidl')
cmd.append(mozpath.join(self.environment.topsrcdir, 'dom', 'bindings'))
# The WebIDLCodegenManager knows all of the .cpp and .h files that will
# be created (expected_build_output_files), but there are a few
# additional files that are also created by the webidl py_action.
outputs = [
'_cache/webidlyacc.py',
'codegen.json',
'codegen.pp',
'parser.out',
]
outputs.extend(expected_build_output_files)
backend_file.rule(
display='WebIDL code generation',
cmd=cmd,
inputs=webidls.all_non_static_basenames(),
outputs=outputs,
check_unchanged=True,
)
class TupBackend(HybridBackend(TupOnly, RecursiveMakeBackend)):

Просмотреть файл

@ -1162,4 +1162,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1482583977213000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1483189296336000);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -65,7 +65,7 @@ class CollectionValidator {
}
getServerItems(engine) {
let collection = engine._itemSource();
let collection = engine.itemSource();
let collectionKey = engine.service.collectionKeys.keyForCollection(engine.name);
collection.full = true;
let items = [];

Просмотреть файл

@ -1140,13 +1140,14 @@ add_task(function* test_onItemDeleted_removeFolderTransaction() {
_("Undo the remove folder transaction");
txn.undoTransaction();
yield verifyTrackedItems(["menu"]);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
yield resetTracker();
// At this point, the restored folder has the same ID, but a different GUID.
let new_folder_guid = yield PlacesUtils.promiseItemGuid(folder_id);
yield verifyTrackedItems(["menu", new_folder_guid]);
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
yield resetTracker();
_("Redo the transaction");
txn.redoTransaction();
yield verifyTrackedItems(["menu", new_folder_guid]);

Просмотреть файл

@ -612,7 +612,7 @@ var TPS = {
let getServerBookmarkState = () => {
let bookmarkEngine = Weave.Service.engineManager.get('bookmarks');
let collection = bookmarkEngine._itemSource();
let collection = bookmarkEngine.itemSource();
let collectionKey = bookmarkEngine.service.collectionKeys.keyForCollection(bookmarkEngine.name);
collection.full = true;
let items = [];

Просмотреть файл

@ -29,6 +29,8 @@ job-defaults:
# the functionality that l10n needs
JOB_SCRIPT: "taskcluster/scripts/builder/build-l10n.sh"
# don't run anywhere by default, but still available via try
run-on-projects: []
when:
files-changed:
- browser/locales/all-locales

Просмотреть файл

@ -23,9 +23,7 @@ logger = logging.getLogger(__name__)
CONCURRENCY = 50
def create_tasks(taskgraph, label_to_taskid):
# TODO: use the taskGroupId of the decision task
task_group_id = slugid()
def create_tasks(taskgraph, label_to_taskid, params):
taskid_to_label = {t: l for l, t in label_to_taskid.iteritems()}
session = requests.Session()
@ -40,6 +38,13 @@ def create_tasks(taskgraph, label_to_taskid):
decision_task_id = os.environ.get('TASK_ID')
# when running as an actual decision task, we use the decision task's
# taskId as the taskGroupId. The process that created the decision task
# helpfully placed it in this same taskGroup. If there is no $TASK_ID,
# fall back to a slugid
task_group_id = decision_task_id or slugid()
scheduler_id = 'gecko-level-{}'.format(params['level'])
with futures.ThreadPoolExecutor(CONCURRENCY) as e:
fs = {}
@ -62,7 +67,7 @@ def create_tasks(taskgraph, label_to_taskid):
task_def['dependencies'] = [decision_task_id]
task_def['taskGroupId'] = task_group_id
task_def['schedulerId'] = '-'
task_def['schedulerId'] = scheduler_id
# Wait for dependencies before submitting this.
deps_fs = [fs[dep] for dep in task_def.get('dependencies', [])

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше