зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
cc35095901
|
@ -145,7 +145,6 @@ devtools/client/webaudioeditor/**
|
|||
devtools/client/webconsole/net/**
|
||||
devtools/client/webconsole/new-console-output/test/mochitest/**
|
||||
devtools/client/webconsole/test/**
|
||||
devtools/client/webconsole/hudservice.js
|
||||
devtools/client/webconsole/webconsole-connection-proxy.js
|
||||
devtools/client/webconsole/webconsole.js
|
||||
devtools/client/webide/**
|
||||
|
|
15
.eslintrc.js
15
.eslintrc.js
|
@ -25,6 +25,21 @@ module.exports = {
|
|||
"eol-last": "off",
|
||||
}
|
||||
}, {
|
||||
// These xbl bindings are assumed to be in the browser-window environment,
|
||||
// we would mark it in the files, but ESLint made this more difficult with
|
||||
// our xml processor, so we list them here. Bug 1397874 & co are working
|
||||
// towards removing these files completely.
|
||||
"files": [
|
||||
"browser/base/content/tabbrowser.xml",
|
||||
"browser/base/content/urlbarBindings.xml",
|
||||
"browser/components/search/content/search.xml",
|
||||
"browser/components/translation/translation-infobar.xml",
|
||||
"toolkit/components/prompts/content/tabprompts.xml"
|
||||
],
|
||||
"env": {
|
||||
"mozilla/browser-window": true
|
||||
}
|
||||
},{
|
||||
// XXX Bug 1421969. These files/directories are still being fixed,
|
||||
// so turn off mozilla/use-services for them for now.
|
||||
"files": [
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module.exports = {
|
||||
"rules": {
|
||||
// Warn about cyclomatic complexity in functions.
|
||||
"complexity": ["error", 42],
|
||||
"complexity": ["error", 47],
|
||||
|
||||
// XXX These are rules that are enabled in the recommended configuration, but
|
||||
// disabled here due to failures when initially implemented. They should be
|
||||
|
|
|
@ -4,7 +4,7 @@ module.exports = {
|
|||
"rules": {
|
||||
// XXX Bug 1326071 - This should be reduced down - probably to 20 or to
|
||||
// be removed & synced with the mozilla/recommended value.
|
||||
"complexity": ["error", {"max": 40}],
|
||||
"complexity": ["error", {"max": 44}],
|
||||
|
||||
// Disallow empty statements. This will report an error for:
|
||||
// try { something(); } catch (e) {}
|
||||
|
|
|
@ -390,9 +390,7 @@ pref("browser.search.order.2", "chrome://browser-region/locale/re
|
|||
pref("browser.search.order.3", "chrome://browser-region/locale/region.properties");
|
||||
|
||||
// Market-specific search defaults
|
||||
// This is disabled globally, and then enabled for individual locales
|
||||
// in firefox-l10n.js (eg. it's enabled for en-US).
|
||||
pref("browser.search.geoSpecificDefaults", false);
|
||||
pref("browser.search.geoSpecificDefaults", true);
|
||||
pref("browser.search.geoSpecificDefaults.url", "https://search.services.mozilla.com/1/%APP%/%VERSION%/%CHANNEL%/%LOCALE%/%REGION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%");
|
||||
|
||||
// US specific default (used as a fallback if the geoSpecificDefaults request fails).
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- eslint-env mozilla/browser-window -->
|
||||
|
||||
<bindings id="tabBrowserBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
|
@ -644,6 +642,7 @@
|
|||
aMaxTotalProgress);
|
||||
},
|
||||
|
||||
/* eslint-disable complexity */
|
||||
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (!aRequest)
|
||||
return;
|
||||
|
@ -845,6 +844,7 @@
|
|||
this.mStateFlags = aStateFlags;
|
||||
this.mStatus = aStatus;
|
||||
},
|
||||
/* eslint-enable complexity */
|
||||
|
||||
onLocationChange(aWebProgress, aRequest, aLocation,
|
||||
aFlags) {
|
||||
|
|
|
@ -7,8 +7,6 @@ License, v. 2.0. If a copy of the MPL was not distributed with this
|
|||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<!-- eslint-env mozilla/browser-window -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
|
|
|
@ -4,7 +4,7 @@ module.exports = {
|
|||
"rules": {
|
||||
"block-scoped-var": "error",
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"complexity": ["error", {"max": 21}],
|
||||
"complexity": ["error", {"max": 22}],
|
||||
"indent-legacy": ["error", 2, {"SwitchCase": 1, "ArrayExpression": "first", "ObjectExpression": "first"}],
|
||||
"max-nested-callbacks": ["error", 3],
|
||||
"new-parens": "error",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
/* import-globals-from ../../base/content/utilityOverlay.js */
|
||||
/* import-globals-from ../../../toolkit/content/preferencesBindings.js */
|
||||
/* import-globals-from in-content/extensionControlled.js */
|
||||
|
||||
Preferences.addAll([
|
||||
{ id: "network.proxy.type", type: "int" },
|
||||
|
@ -36,6 +37,14 @@ window.addEventListener("DOMContentLoaded", () => {
|
|||
gConnectionsDialog.proxyTypeChanged.bind(gConnectionsDialog));
|
||||
Preferences.get("network.proxy.socks_version").on("change",
|
||||
gConnectionsDialog.updateDNSPref.bind(gConnectionsDialog));
|
||||
|
||||
document
|
||||
.getElementById("disableProxyExtension")
|
||||
.addEventListener(
|
||||
"command", makeDisableControllingExtension(
|
||||
PREF_SETTING_TYPE, PROXY_KEY).bind(gConnectionsDialog));
|
||||
gConnectionsDialog.updateProxySettingsUI();
|
||||
initializeProxyUI(gConnectionsDialog);
|
||||
}, { once: true, capture: true });
|
||||
|
||||
var gConnectionsDialog = {
|
||||
|
@ -227,5 +236,40 @@ var gConnectionsDialog = {
|
|||
if (shareProxiesPref.value)
|
||||
this.updateProtocolPrefs();
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getProxyControls() {
|
||||
let controlGroup = document.getElementById("networkProxyType");
|
||||
return [
|
||||
...controlGroup.querySelectorAll(":scope > radio"),
|
||||
...controlGroup.querySelectorAll("label"),
|
||||
...controlGroup.querySelectorAll("textbox"),
|
||||
...controlGroup.querySelectorAll("checkbox"),
|
||||
...document.querySelectorAll("#networkProxySOCKSVersion > radio"),
|
||||
...document.querySelectorAll("#ConnectionsDialogPane > checkbox"),
|
||||
];
|
||||
},
|
||||
|
||||
// Update the UI to show/hide the extension controlled message for
|
||||
// proxy settings.
|
||||
async updateProxySettingsUI() {
|
||||
let isLocked = API_PROXY_PREFS.some(
|
||||
pref => Services.prefs.prefIsLocked(pref));
|
||||
|
||||
function setInputsDisabledState(isControlled) {
|
||||
let disabled = isLocked || isControlled;
|
||||
for (let element of gConnectionsDialog.getProxyControls()) {
|
||||
element.disabled = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
if (isLocked) {
|
||||
// An extension can't control this setting if any pref is locked.
|
||||
hideControllingExtension(PROXY_KEY);
|
||||
setInputsDisabledState(false);
|
||||
} else {
|
||||
handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY)
|
||||
.then(setInputsDisabledState);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
%preferencesDTD;
|
||||
<!ENTITY % connectionDTD SYSTEM "chrome://browser/locale/preferences/connection.dtd">
|
||||
%connectionDTD;
|
||||
<!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
|
||||
%mainDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
||||
|
@ -35,6 +37,7 @@
|
|||
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
|
||||
|
||||
<keyset>
|
||||
<key key="&windowClose.key;" modifiers="accel" oncommand="Preferences.close(event)"/>
|
||||
|
@ -42,11 +45,19 @@
|
|||
|
||||
<vbox id="ConnectionsDialogPane" class="prefpane largeDialogContainer">
|
||||
|
||||
<stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
<stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/preferences/connection.js"/>
|
||||
|
||||
<hbox id="proxyExtensionContent" align="top" hidden="true">
|
||||
<description control="disableProxyExtension" flex="1" />
|
||||
<button id="disableProxyExtension"
|
||||
class="extension-controlled-button accessory-button"
|
||||
label="&disableExtension.label;" />
|
||||
</hbox>
|
||||
|
||||
<groupbox>
|
||||
<caption><label>&proxyTitle.label;</label></caption>
|
||||
<caption><label>&proxyTitle.label2;</label></caption>
|
||||
|
||||
<radiogroup id="networkProxyType" preference="network.proxy.type"
|
||||
onsyncfrompreference="return gConnectionsDialog.readProxyType();">
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
/* - 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/. */
|
||||
|
||||
/* import-globals-from preferences.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "DeferredTask",
|
||||
"resource://gre/modules/DeferredTask.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
|
||||
"privacy.trackingprotection.ui.enabled");
|
||||
|
||||
const PREF_SETTING_TYPE = "prefs";
|
||||
const PROXY_KEY = "proxyConfig";
|
||||
const API_PROXY_PREFS = [
|
||||
"network.proxy.type",
|
||||
"network.proxy.http",
|
||||
"network.proxy.http_port",
|
||||
"network.proxy.share_proxy_settings",
|
||||
"network.proxy.ftp",
|
||||
"network.proxy.ftp_port",
|
||||
"network.proxy.ssl",
|
||||
"network.proxy.ssl_port",
|
||||
"network.proxy.socks",
|
||||
"network.proxy.socks_port",
|
||||
"network.proxy.socks_version",
|
||||
"network.proxy.socks_remote_dns",
|
||||
"network.proxy.no_proxies_on",
|
||||
"network.proxy.autoconfig_url",
|
||||
"signon.autologin.proxy",
|
||||
];
|
||||
|
||||
let extensionControlledContentIds = {
|
||||
"privacy.containers": "browserContainersExtensionContent",
|
||||
"homepage_override": "browserHomePageExtensionContent",
|
||||
"newTabURL": "browserNewTabExtensionContent",
|
||||
"defaultSearch": "browserDefaultSearchExtensionContent",
|
||||
"proxyConfig": "proxyExtensionContent",
|
||||
get "websites.trackingProtectionMode"() {
|
||||
return {
|
||||
button: "trackingProtectionExtensionContentButton",
|
||||
section:
|
||||
trackingprotectionUiEnabled ?
|
||||
"trackingProtectionExtensionContentLabel" :
|
||||
"trackingProtectionPBMExtensionContentLabel",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function getExtensionControlledArgs(settingName) {
|
||||
switch (settingName) {
|
||||
case "proxyConfig":
|
||||
return [document.getElementById("bundleBrand").getString("brandShortName")];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
let extensionControlledIds = {};
|
||||
|
||||
/**
|
||||
* Check if a pref is being managed by an extension.
|
||||
*/
|
||||
async function getControllingExtensionInfo(type, settingName) {
|
||||
await ExtensionSettingsStore.initialize();
|
||||
return ExtensionSettingsStore.getSetting(type, settingName);
|
||||
}
|
||||
|
||||
function getControllingExtensionEls(settingName) {
|
||||
let idInfo = extensionControlledContentIds[settingName];
|
||||
let section = document.getElementById(idInfo.section || idInfo);
|
||||
let button = idInfo.button ?
|
||||
document.getElementById(idInfo.button) :
|
||||
section.querySelector("button");
|
||||
return {
|
||||
section,
|
||||
button,
|
||||
description: section.querySelector("description"),
|
||||
};
|
||||
}
|
||||
|
||||
async function getControllingExtension(type, settingName) {
|
||||
let info = await getControllingExtensionInfo(type, settingName);
|
||||
let addon = info && info.id
|
||||
&& await AddonManager.getAddonByID(info.id);
|
||||
return addon;
|
||||
}
|
||||
|
||||
async function handleControllingExtension(type, settingName) {
|
||||
let addon = await getControllingExtension(type, settingName);
|
||||
|
||||
// Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
|
||||
// an extension is controlling a setting but the extension has been uninstalled
|
||||
// outside of the regular lifecycle. If the extension isn't currently installed
|
||||
// then we should treat the setting as not being controlled.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
|
||||
if (addon) {
|
||||
extensionControlledIds[settingName] = addon.id;
|
||||
showControllingExtension(settingName, addon);
|
||||
} else {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
if (extensionControlledIds[settingName]
|
||||
&& !document.hidden
|
||||
&& elements.button) {
|
||||
showEnableExtensionMessage(settingName);
|
||||
} else {
|
||||
hideControllingExtension(settingName);
|
||||
}
|
||||
delete extensionControlledIds[settingName];
|
||||
}
|
||||
|
||||
return !!addon;
|
||||
}
|
||||
|
||||
function getControllingExtensionFragment(settingName, addon, ...extraArgs) {
|
||||
let msg = document.getElementById("bundlePreferences")
|
||||
.getString(`extensionControlled.${settingName}`);
|
||||
let image = document.createElement("image");
|
||||
const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
image.setAttribute("src", addon.iconURL || defaultIcon);
|
||||
image.classList.add("extension-controlled-icon");
|
||||
let addonBit = document.createDocumentFragment();
|
||||
addonBit.appendChild(image);
|
||||
addonBit.appendChild(document.createTextNode(" " + addon.name));
|
||||
return BrowserUtils.getLocalizedFragment(document, msg, addonBit, ...extraArgs);
|
||||
}
|
||||
|
||||
async function showControllingExtension(settingName, addon) {
|
||||
// Tell the user what extension is controlling the setting.
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
let extraArgs = getExtensionControlledArgs(settingName);
|
||||
|
||||
elements.section.classList.remove("extension-controlled-disabled");
|
||||
let description = elements.description;
|
||||
|
||||
// Remove the old content from the description.
|
||||
while (description.firstChild) {
|
||||
description.firstChild.remove();
|
||||
}
|
||||
|
||||
let fragment = getControllingExtensionFragment(
|
||||
settingName, addon, ...extraArgs);
|
||||
description.appendChild(fragment);
|
||||
|
||||
if (elements.button) {
|
||||
elements.button.hidden = false;
|
||||
}
|
||||
|
||||
// Show the controlling extension row and hide the old label.
|
||||
elements.section.hidden = false;
|
||||
}
|
||||
|
||||
function hideControllingExtension(settingName) {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
elements.section.hidden = true;
|
||||
if (elements.button) {
|
||||
elements.button.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function showEnableExtensionMessage(settingName) {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
|
||||
elements.button.hidden = true;
|
||||
elements.section.classList.add("extension-controlled-disabled");
|
||||
let icon = url => {
|
||||
let img = document.createElement("image");
|
||||
img.src = url;
|
||||
img.className = "extension-controlled-icon";
|
||||
return img;
|
||||
};
|
||||
let addonIcon = icon("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
|
||||
let toolbarIcon = icon("chrome://browser/skin/menu.svg");
|
||||
let message = document.getElementById("bundlePreferences")
|
||||
.getString("extensionControlled.enable");
|
||||
let frag = BrowserUtils.getLocalizedFragment(document, message, addonIcon, toolbarIcon);
|
||||
elements.description.innerHTML = "";
|
||||
elements.description.appendChild(frag);
|
||||
let dismissButton = document.createElement("image");
|
||||
dismissButton.setAttribute("class", "extension-controlled-icon close-icon");
|
||||
dismissButton.addEventListener("click", function dismissHandler() {
|
||||
hideControllingExtension(settingName);
|
||||
dismissButton.removeEventListener("click", dismissHandler);
|
||||
});
|
||||
elements.description.appendChild(dismissButton);
|
||||
}
|
||||
|
||||
function makeDisableControllingExtension(type, settingName) {
|
||||
return async function disableExtension() {
|
||||
let {id} = await getControllingExtensionInfo(type, settingName);
|
||||
let addon = await AddonManager.getAddonByID(id);
|
||||
addon.userDisabled = true;
|
||||
};
|
||||
}
|
||||
|
||||
function initializeProxyUI(container) {
|
||||
let deferredUpdate = new DeferredTask(() => {
|
||||
container.updateProxySettingsUI();
|
||||
}, 10);
|
||||
let proxyObserver = {
|
||||
observe: (subject, topic, data) => {
|
||||
if (API_PROXY_PREFS.includes(data)) {
|
||||
deferredUpdate.arm();
|
||||
}
|
||||
},
|
||||
};
|
||||
Services.prefs.addObserver("", proxyObserver);
|
||||
window.addEventListener("unload", () => {
|
||||
Services.prefs.removeObserver("", proxyObserver);
|
||||
});
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* import-globals-from extensionControlled.js */
|
||||
/* import-globals-from preferences.js */
|
||||
|
||||
var gSearchResultsPane = {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
browser.jar:
|
||||
content/browser/preferences/in-content/preferences.js
|
||||
content/browser/preferences/in-content/extensionControlled.js
|
||||
* content/browser/preferences/in-content/preferences.xul
|
||||
content/browser/preferences/in-content/subdialogs.js
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* import-globals-from extensionControlled.js */
|
||||
/* import-globals-from preferences.js */
|
||||
/* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
|
||||
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
|
||||
|
@ -367,6 +368,13 @@ var gMainPane = {
|
|||
Services.obs.removeObserver(newTabObserver, "newtab-url-changed");
|
||||
});
|
||||
|
||||
let connectionSettingsLink = document.getElementById("connectionSettingsLearnMore");
|
||||
let connectionSettingsUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
||||
"prefs-connection-settings";
|
||||
connectionSettingsLink.setAttribute("href", connectionSettingsUrl);
|
||||
this.updateProxySettingsUI();
|
||||
initializeProxyUI(gMainPane);
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
// Functionality for "Show tabs in taskbar" on Windows 7 and up.
|
||||
try {
|
||||
|
@ -1086,7 +1094,28 @@ var gMainPane = {
|
|||
* Displays a dialog in which proxy settings may be changed.
|
||||
*/
|
||||
showConnections() {
|
||||
gSubDialog.open("chrome://browser/content/preferences/connection.xul");
|
||||
gSubDialog.open("chrome://browser/content/preferences/connection.xul",
|
||||
null, null, this.updateProxySettingsUI.bind(this));
|
||||
},
|
||||
|
||||
// Update the UI to show the proper description depending on whether an
|
||||
// extension is in control or not.
|
||||
async updateProxySettingsUI() {
|
||||
let controllingExtension = await getControllingExtension(PREF_SETTING_TYPE, PROXY_KEY);
|
||||
let fragment = controllingExtension ?
|
||||
getControllingExtensionFragment(PROXY_KEY, controllingExtension, this._brandShortName) :
|
||||
BrowserUtils.getLocalizedFragment(
|
||||
document,
|
||||
this._prefsBundle.getString("connectionDesc.label"),
|
||||
this._brandShortName);
|
||||
let description = document.getElementById("connectionSettingsDescription");
|
||||
|
||||
// Remove the old content from the description.
|
||||
while (description.firstChild) {
|
||||
description.firstChild.remove();
|
||||
}
|
||||
|
||||
description.appendChild(fragment);
|
||||
},
|
||||
|
||||
checkBrowserContainers(event) {
|
||||
|
|
|
@ -749,7 +749,15 @@
|
|||
<caption class="search-header" hidden="true"><label>&networkProxy.label;</label></caption>
|
||||
|
||||
<hbox align="center">
|
||||
<description flex="1" control="connectionSettings">&connectionDesc.label;</description>
|
||||
<hbox align="center" flex="1">
|
||||
<description id="connectionSettingsDescription" control="connectionSettings"></description>
|
||||
<spacer width="5"/>
|
||||
<label id="connectionSettingsLearnMore" class="learnMore text-link">
|
||||
&connectionSettingsLearnMore.label;
|
||||
</label>
|
||||
<separator orient="vertical"/>
|
||||
</hbox>
|
||||
|
||||
<!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
|
||||
<hbox>
|
||||
<button id="connectionSettings"
|
||||
|
|
|
@ -15,22 +15,12 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "formAutofillParent",
|
||||
"resource://formautofill/FormAutofillParent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
|
||||
"privacy.trackingprotection.ui.enabled");
|
||||
|
||||
var gLastHash = "";
|
||||
|
||||
var gCategoryInits = new Map();
|
||||
|
@ -417,148 +407,3 @@ function appendSearchKeywords(aId, keywords) {
|
|||
}
|
||||
element.setAttribute("searchkeywords", keywords.join(" "));
|
||||
}
|
||||
|
||||
const PREF_SETTING_TYPE = "prefs";
|
||||
|
||||
let extensionControlledContentIds = {
|
||||
"privacy.containers": "browserContainersExtensionContent",
|
||||
"homepage_override": "browserHomePageExtensionContent",
|
||||
"newTabURL": "browserNewTabExtensionContent",
|
||||
"defaultSearch": "browserDefaultSearchExtensionContent",
|
||||
get "websites.trackingProtectionMode"() {
|
||||
return {
|
||||
button: "trackingProtectionExtensionContentButton",
|
||||
section:
|
||||
trackingprotectionUiEnabled ?
|
||||
"trackingProtectionExtensionContentLabel" :
|
||||
"trackingProtectionPBMExtensionContentLabel",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let extensionControlledIds = {};
|
||||
|
||||
/**
|
||||
* Check if a pref is being managed by an extension.
|
||||
*/
|
||||
async function getControllingExtensionInfo(type, settingName) {
|
||||
await ExtensionSettingsStore.initialize();
|
||||
return ExtensionSettingsStore.getSetting(type, settingName);
|
||||
}
|
||||
|
||||
function getControllingExtensionEls(settingName) {
|
||||
let idInfo = extensionControlledContentIds[settingName];
|
||||
let section = document.getElementById(idInfo.section || idInfo);
|
||||
let button = idInfo.button ?
|
||||
document.getElementById(idInfo.button) :
|
||||
section.querySelector("button");
|
||||
return {
|
||||
section,
|
||||
button,
|
||||
description: section.querySelector("description"),
|
||||
};
|
||||
}
|
||||
|
||||
async function handleControllingExtension(type, settingName) {
|
||||
let info = await getControllingExtensionInfo(type, settingName);
|
||||
let addon = info && info.id
|
||||
&& await AddonManager.getAddonByID(info.id);
|
||||
|
||||
// Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
|
||||
// an extension is controlling a setting but the extension has been uninstalled
|
||||
// outside of the regular lifecycle. If the extension isn't currently installed
|
||||
// then we should treat the setting as not being controlled.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
|
||||
if (addon) {
|
||||
extensionControlledIds[settingName] = info.id;
|
||||
showControllingExtension(settingName, addon);
|
||||
} else {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
if (extensionControlledIds[settingName]
|
||||
&& !document.hidden
|
||||
&& elements.button) {
|
||||
showEnableExtensionMessage(settingName);
|
||||
} else {
|
||||
hideControllingExtension(settingName);
|
||||
}
|
||||
delete extensionControlledIds[settingName];
|
||||
}
|
||||
|
||||
return !!addon;
|
||||
}
|
||||
|
||||
async function showControllingExtension(settingName, addon) {
|
||||
// Tell the user what extension is controlling the setting.
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
|
||||
elements.section.classList.remove("extension-controlled-disabled");
|
||||
let description = elements.description;
|
||||
|
||||
// Remove the old content from the description.
|
||||
while (description.firstChild) {
|
||||
description.firstChild.remove();
|
||||
}
|
||||
|
||||
// Populate the description.
|
||||
let msg = document.getElementById("bundlePreferences")
|
||||
.getString(`extensionControlled.${settingName}`);
|
||||
let image = document.createElement("image");
|
||||
const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||
image.setAttribute("src", addon.iconURL || defaultIcon);
|
||||
image.classList.add("extension-controlled-icon");
|
||||
let addonBit = document.createDocumentFragment();
|
||||
addonBit.appendChild(image);
|
||||
addonBit.appendChild(document.createTextNode(" " + addon.name));
|
||||
let fragment = BrowserUtils.getLocalizedFragment(document, msg, addonBit);
|
||||
description.appendChild(fragment);
|
||||
|
||||
if (elements.button) {
|
||||
elements.button.hidden = false;
|
||||
}
|
||||
|
||||
// Show the controlling extension row and hide the old label.
|
||||
elements.section.hidden = false;
|
||||
}
|
||||
|
||||
function hideControllingExtension(settingName) {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
elements.section.hidden = true;
|
||||
if (elements.button) {
|
||||
elements.button.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function showEnableExtensionMessage(settingName) {
|
||||
let elements = getControllingExtensionEls(settingName);
|
||||
|
||||
elements.button.hidden = true;
|
||||
elements.section.classList.add("extension-controlled-disabled");
|
||||
let icon = url => {
|
||||
let img = document.createElement("image");
|
||||
img.src = url;
|
||||
img.className = "extension-controlled-icon";
|
||||
return img;
|
||||
};
|
||||
let addonIcon = icon("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
|
||||
let toolbarIcon = icon("chrome://browser/skin/menu.svg");
|
||||
let message = document.getElementById("bundlePreferences")
|
||||
.getString("extensionControlled.enable");
|
||||
let frag = BrowserUtils.getLocalizedFragment(document, message, addonIcon, toolbarIcon);
|
||||
elements.description.innerHTML = "";
|
||||
elements.description.appendChild(frag);
|
||||
let dismissButton = document.createElement("image");
|
||||
dismissButton.setAttribute("class", "extension-controlled-icon close-icon");
|
||||
dismissButton.addEventListener("click", function dismissHandler() {
|
||||
hideControllingExtension(settingName);
|
||||
dismissButton.removeEventListener("click", dismissHandler);
|
||||
});
|
||||
elements.description.appendChild(dismissButton);
|
||||
}
|
||||
|
||||
function makeDisableControllingExtension(type, settingName) {
|
||||
return async function disableExtension() {
|
||||
let {id} = await getControllingExtensionInfo(type, settingName);
|
||||
let addon = await AddonManager.getAddonByID(id);
|
||||
addon.userDisabled = true;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -108,6 +108,8 @@
|
|||
src="chrome://global/content/preferencesBindings.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/preferences.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
|
||||
<script src="chrome://browser/content/preferences/in-content/findInPage.js"/>
|
||||
<script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* import-globals-from extensionControlled.js */
|
||||
/* import-globals-from preferences.js */
|
||||
|
||||
/* FIXME: ESlint globals workaround should be removed once bug 1395426 gets fixed */
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* import-globals-from extensionControlled.js */
|
||||
/* import-globals-from preferences.js */
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
|
|
@ -50,6 +50,7 @@ skip-if = os != "win" || (os == "win" && os_version == "6.1")
|
|||
skip-if = true || !healthreport # Bug 1185403 for the "true"
|
||||
[browser_homepages_filter_aboutpreferences.js]
|
||||
[browser_extension_controlled.js]
|
||||
[browser_languages_subdialog.js]
|
||||
[browser_layersacceleration.js]
|
||||
[browser_masterpassword.js]
|
||||
[browser_notifications_do_not_disturb.js]
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
/* eslint-env webextensions */
|
||||
|
||||
const PROXY_PREF = "network.proxy.type";
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
||||
"@mozilla.org/browser/aboutnewtab-service;1",
|
||||
"nsIAboutNewTabService");
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "proxyType", PROXY_PREF);
|
||||
|
||||
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
const CHROME_URL_ROOT = TEST_DIR + "/";
|
||||
|
@ -48,26 +51,37 @@ function waitForMutation(target, opts, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
function waitForMessageChange(id, cb, opts = { attributes: true, attributeFilter: ["hidden"] }) {
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
return waitForMutation(gBrowser.contentDocument.getElementById(id), opts, cb);
|
||||
function waitForMessageChange(element, cb, opts = { attributes: true, attributeFilter: ["hidden"] }) {
|
||||
return waitForMutation(element, opts, cb);
|
||||
}
|
||||
|
||||
function waitForMessageHidden(messageId) {
|
||||
return waitForMessageChange(messageId, target => target.hidden);
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
function getElement(id, doc = gBrowser.contentDocument) {
|
||||
return doc.getElementById(id);
|
||||
}
|
||||
|
||||
function waitForMessageShown(messageId) {
|
||||
return waitForMessageChange(messageId, target => !target.hidden);
|
||||
function waitForMessageHidden(messageId, doc) {
|
||||
return waitForMessageChange(getElement(messageId, doc), target => target.hidden);
|
||||
}
|
||||
|
||||
function waitForEnableMessage(messageId) {
|
||||
function waitForMessageShown(messageId, doc) {
|
||||
return waitForMessageChange(getElement(messageId, doc), target => !target.hidden);
|
||||
}
|
||||
|
||||
function waitForEnableMessage(messageId, doc) {
|
||||
return waitForMessageChange(
|
||||
messageId,
|
||||
getElement(messageId, doc),
|
||||
target => target.classList.contains("extension-controlled-disabled"),
|
||||
{ attributeFilter: ["class"], attributes: true });
|
||||
}
|
||||
|
||||
function waitForMessageContent(messageId, content, doc) {
|
||||
return waitForMessageChange(
|
||||
getElement(messageId, doc),
|
||||
target => target.textContent === content,
|
||||
{ childList: true });
|
||||
}
|
||||
|
||||
add_task(async function testExtensionControlledHomepage() {
|
||||
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
|
@ -604,3 +618,193 @@ add_task(async function testExtensionControlledTrackingProtection() {
|
|||
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
||||
add_task(async function testExtensionControlledProxyConfig() {
|
||||
const proxySvc = Ci.nsIProtocolProxyService;
|
||||
const PROXY_DEFAULT = proxySvc.PROXYCONFIG_SYSTEM;
|
||||
const EXTENSION_ID = "@set_proxy";
|
||||
const CONTROLLED_SECTION_ID = "proxyExtensionContent";
|
||||
const CONTROLLED_BUTTON_ID = "disableProxyExtension";
|
||||
const CONNECTION_SETTINGS_DESC_ID = "connectionSettingsDescription";
|
||||
const PANEL_URL = "chrome://browser/content/preferences/connection.xul";
|
||||
|
||||
await SpecialPowers.pushPrefEnv({"set": [[PROXY_PREF, PROXY_DEFAULT]]});
|
||||
|
||||
function background() {
|
||||
browser.browserSettings.proxyConfig.set({value: {proxyType: "none"}});
|
||||
}
|
||||
|
||||
function expectedConnectionSettingsMessage(doc, isControlled) {
|
||||
let brandShortName = doc.getElementById("bundleBrand").getString("brandShortName");
|
||||
return isControlled ?
|
||||
`An extension, set_proxy, is controlling how ${brandShortName} connects to the internet.` :
|
||||
`Configure how ${brandShortName} connects to the internet.`;
|
||||
}
|
||||
|
||||
function connectionSettingsMessagePromise(doc, isControlled) {
|
||||
return waitForMessageContent(
|
||||
CONNECTION_SETTINGS_DESC_ID,
|
||||
expectedConnectionSettingsMessage(doc, isControlled)
|
||||
);
|
||||
}
|
||||
|
||||
function verifyState(doc, isControlled) {
|
||||
let isPanel = doc.getElementById(CONTROLLED_BUTTON_ID);
|
||||
let brandShortName = doc.getElementById("bundleBrand").getString("brandShortName");
|
||||
is(proxyType === proxySvc.PROXYCONFIG_DIRECT, isControlled,
|
||||
"Proxy pref is set to the expected value.");
|
||||
|
||||
if (isPanel) {
|
||||
let controlledSection = doc.getElementById(CONTROLLED_SECTION_ID);
|
||||
|
||||
is(controlledSection.hidden, !isControlled, "The extension controlled row's visibility is as expected.");
|
||||
if (isPanel) {
|
||||
is(doc.getElementById(CONTROLLED_BUTTON_ID).hidden, !isControlled,
|
||||
"The disable extension button's visibility is as expected.");
|
||||
}
|
||||
if (isControlled) {
|
||||
let controlledDesc = controlledSection.querySelector("description");
|
||||
// There are two spaces before "set_proxy" because it's " <image /> set_proxy".
|
||||
is(controlledDesc.textContent, `An extension, set_proxy, is controlling how ${brandShortName} connects to the internet.`,
|
||||
"The user is notified that an extension is controlling proxy settings.");
|
||||
}
|
||||
function getProxyControls() {
|
||||
let controlGroup = doc.getElementById("networkProxyType");
|
||||
return [
|
||||
...controlGroup.querySelectorAll(":scope > radio"),
|
||||
...controlGroup.querySelectorAll("label"),
|
||||
...controlGroup.querySelectorAll("textbox"),
|
||||
...controlGroup.querySelectorAll("checkbox"),
|
||||
...doc.querySelectorAll("#networkProxySOCKSVersion > radio"),
|
||||
...doc.querySelectorAll("#ConnectionsDialogPane > checkbox"),
|
||||
];
|
||||
}
|
||||
let controlState = isControlled ? "disabled" : "enabled";
|
||||
for (let element of getProxyControls()) {
|
||||
is(element.disabled, isControlled, `Proxy controls are ${controlState}.`);
|
||||
}
|
||||
} else {
|
||||
is(doc.getElementById(CONNECTION_SETTINGS_DESC_ID).textContent,
|
||||
expectedConnectionSettingsMessage(doc, isControlled),
|
||||
"The connection settings description is as expected.");
|
||||
}
|
||||
}
|
||||
|
||||
async function disableViaClick() {
|
||||
let sectionId = CONTROLLED_SECTION_ID;
|
||||
let controlledSection = panelDoc.getElementById(sectionId);
|
||||
|
||||
let enableMessageShown = waitForEnableMessage(sectionId, panelDoc);
|
||||
panelDoc.getElementById(CONTROLLED_BUTTON_ID).click();
|
||||
await enableMessageShown;
|
||||
|
||||
// The user is notified how to enable the extension.
|
||||
let controlledDesc = controlledSection.querySelector("description");
|
||||
is(controlledDesc.textContent, "To enable the extension go to Add-ons in the menu.",
|
||||
"The user is notified of how to enable the extension again");
|
||||
|
||||
// The user can dismiss the enable instructions.
|
||||
let hidden = waitForMessageHidden(sectionId, panelDoc);
|
||||
controlledSection.querySelector("image:last-of-type").click();
|
||||
return hidden;
|
||||
}
|
||||
|
||||
async function reEnableExtension(addon) {
|
||||
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
|
||||
addon.userDisabled = false;
|
||||
await messageChanged;
|
||||
}
|
||||
|
||||
async function openProxyPanel() {
|
||||
let panel = await openAndLoadSubDialog(PANEL_URL);
|
||||
let closingPromise = waitForEvent(panel.document.documentElement, "dialogclosing");
|
||||
ok(panel, "Proxy panel opened.");
|
||||
return {panel, closingPromise};
|
||||
}
|
||||
|
||||
async function closeProxyPanel(panelObj) {
|
||||
panelObj.panel.document.documentElement.cancelDialog();
|
||||
let panelClosingEvent = await panelObj.closingPromise;
|
||||
ok(panelClosingEvent, "Proxy panel closed.");
|
||||
}
|
||||
|
||||
await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
let mainDoc = gBrowser.contentDocument;
|
||||
|
||||
is(gBrowser.currentURI.spec, "about:preferences#general",
|
||||
"#general should be in the URI for about:preferences");
|
||||
|
||||
verifyState(mainDoc, false);
|
||||
|
||||
// Open the connections panel.
|
||||
let panelObj = await openProxyPanel();
|
||||
let panelDoc = panelObj.panel.document;
|
||||
|
||||
verifyState(panelDoc, false);
|
||||
|
||||
await closeProxyPanel(panelObj);
|
||||
|
||||
verifyState(mainDoc, false);
|
||||
|
||||
// Install an extension that sets Tracking Protection.
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
useAddonManager: "permanent",
|
||||
manifest: {
|
||||
name: "set_proxy",
|
||||
applications: {gecko: {id: EXTENSION_ID}},
|
||||
permissions: ["browserSettings"],
|
||||
},
|
||||
background,
|
||||
});
|
||||
|
||||
let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
|
||||
await extension.startup();
|
||||
await messageChanged;
|
||||
let addon = await AddonManager.getAddonByID(EXTENSION_ID);
|
||||
|
||||
verifyState(mainDoc, true);
|
||||
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
|
||||
|
||||
panelObj = await openProxyPanel();
|
||||
panelDoc = panelObj.panel.document;
|
||||
|
||||
verifyState(panelDoc, true);
|
||||
|
||||
await disableViaClick();
|
||||
|
||||
verifyState(panelDoc, false);
|
||||
|
||||
await closeProxyPanel(panelObj);
|
||||
await messageChanged;
|
||||
|
||||
verifyState(mainDoc, false);
|
||||
|
||||
await reEnableExtension(addon);
|
||||
|
||||
verifyState(mainDoc, true);
|
||||
messageChanged = connectionSettingsMessagePromise(mainDoc, false);
|
||||
|
||||
panelObj = await openProxyPanel();
|
||||
panelDoc = panelObj.panel.document;
|
||||
|
||||
verifyState(panelDoc, true);
|
||||
|
||||
await disableViaClick();
|
||||
|
||||
verifyState(panelDoc, false);
|
||||
|
||||
await closeProxyPanel(panelObj);
|
||||
await messageChanged;
|
||||
|
||||
verifyState(mainDoc, false);
|
||||
|
||||
// Enable the extension so we get the UNINSTALL event, which is needed by
|
||||
// ExtensionPreferencesManager to clean up properly.
|
||||
// TODO: BUG 1408226
|
||||
await reEnableExtension(addon);
|
||||
|
||||
await extension.unload();
|
||||
|
||||
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
add_task(async function() {
|
||||
await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
const contentDocument = gBrowser.contentDocument;
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
const dialogOverlay = content.gSubDialog._preloadDialog._overlay;
|
||||
|
||||
async function languagesSubdialogOpened() {
|
||||
const promiseSubDialogLoaded = promiseLoadSubDialog("chrome://browser/content/preferences/languages.xul");
|
||||
contentDocument.getElementById("chooseLanguage").click();
|
||||
const win = await promiseSubDialogLoaded;
|
||||
win.Preferences.forceEnableInstantApply();
|
||||
is(dialogOverlay.style.visibility, "visible", "The dialog is visible.");
|
||||
return win;
|
||||
}
|
||||
|
||||
function closeLanguagesSubdialog() {
|
||||
const closeBtn = dialogOverlay.querySelector(".dialogClose");
|
||||
closeBtn.doCommand();
|
||||
}
|
||||
|
||||
is(dialogOverlay.style.visibility, "", "The dialog is invisible.");
|
||||
let win = await languagesSubdialogOpened();
|
||||
ok(win.document.getElementById("spoofEnglish").hidden, "The 'Request English' checkbox is hidden.");
|
||||
closeLanguagesSubdialog();
|
||||
is(dialogOverlay.style.visibility, "", "The dialog is invisible.");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
["privacy.resistFingerprinting", true],
|
||||
["privacy.spoof_english", 0],
|
||||
]});
|
||||
|
||||
win = await languagesSubdialogOpened();
|
||||
ok(!win.document.getElementById("spoofEnglish").hidden, "The 'Request English' checkbox isn't hidden.");
|
||||
ok(!win.document.getElementById("spoofEnglish").checked, "The 'Request English' checkbox isn't checked.");
|
||||
is(win.Preferences.get("privacy.spoof_english").value, 0, "The privacy.spoof_english pref is set to 0.");
|
||||
|
||||
win.document.getElementById("spoofEnglish").checked = true;
|
||||
win.document.getElementById("spoofEnglish").doCommand();
|
||||
ok(win.document.getElementById("spoofEnglish").checked, "The 'Request English' checkbox is checked.");
|
||||
is(win.Preferences.get("privacy.spoof_english").value, 2, "The privacy.spoof_english pref is set to 2.");
|
||||
closeLanguagesSubdialog();
|
||||
|
||||
win = await languagesSubdialogOpened();
|
||||
ok(!win.document.getElementById("spoofEnglish").hidden, "The 'Request English' checkbox isn't hidden.");
|
||||
ok(win.document.getElementById("spoofEnglish").checked, "The 'Request English' checkbox is checked.");
|
||||
is(win.Preferences.get("privacy.spoof_english").value, 2, "The privacy.spoof_english pref is set to 2.");
|
||||
|
||||
win.document.getElementById("spoofEnglish").checked = false;
|
||||
win.document.getElementById("spoofEnglish").doCommand();
|
||||
ok(!win.document.getElementById("spoofEnglish").checked, "The 'Request English' checkbox isn't checked.");
|
||||
is(win.Preferences.get("privacy.spoof_english").value, 1, "The privacy.spoof_english pref is set to 1.");
|
||||
closeLanguagesSubdialog();
|
||||
|
||||
win = await languagesSubdialogOpened();
|
||||
ok(!win.document.getElementById("spoofEnglish").hidden, "The 'Request English' checkbox isn't hidden.");
|
||||
ok(!win.document.getElementById("spoofEnglish").checked, "The 'Request English' checkbox isn't checked.");
|
||||
is(win.Preferences.get("privacy.spoof_english").value, 1, "The privacy.spoof_english pref is set to 1.");
|
||||
closeLanguagesSubdialog();
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
|
@ -319,24 +319,24 @@ var gLanguagesDialog = {
|
|||
return false;
|
||||
}
|
||||
|
||||
var spoofEnglish = document.getElementById("privacy.spoof_english").value;
|
||||
var spoofEnglish = Preferences.get("privacy.spoof_english").value;
|
||||
var activeLanguages = this._activeLanguages;
|
||||
var availableLanguages = this._availableLanguages;
|
||||
checkbox.hidden = false;
|
||||
switch (spoofEnglish) {
|
||||
case 1: // don't spoof intl.accept_lanauges
|
||||
case 1: // don't spoof intl.accept_languages
|
||||
activeLanguages.disabled = false;
|
||||
activeLanguages.selectItem(activeLanguages.firstChild);
|
||||
availableLanguages.disabled = false;
|
||||
this.onAvailableLanguageSelect();
|
||||
return false;
|
||||
case 2: // spoof intl.accept_lanauges
|
||||
case 2: // spoof intl.accept_languages
|
||||
activeLanguages.clearSelection();
|
||||
activeLanguages.disabled = true;
|
||||
availableLanguages.disabled = true;
|
||||
this.onAvailableLanguageSelect();
|
||||
return true;
|
||||
default: // will prompt for spoofing intl.accept_lanauges if resisting fingerprinting
|
||||
default: // will prompt for spoofing intl.accept_languages if resisting fingerprinting
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
@ -345,3 +345,8 @@ var gLanguagesDialog = {
|
|||
return document.getElementById("spoofEnglish").checked ? 2 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
// These focus and resize handlers hack around XUL bug 1194844
|
||||
// by triggering extra reflow (see bug 1194346).
|
||||
window.addEventListener("focus", () => gLanguagesDialog.forceReflow());
|
||||
window.addEventListener("resize", () => gLanguagesDialog.forceReflow());
|
||||
|
|
|
@ -26,12 +26,7 @@
|
|||
onload="gLanguagesDialog.init();"
|
||||
helpTopic="prefs-languages"
|
||||
ondialoghelp="openPrefsHelp()"
|
||||
style="width: &window.width;"
|
||||
onfocus="gLanguagesDialog.forceReflow()"
|
||||
onresize="gLanguagesDialog.forceReflow()">
|
||||
|
||||
<!-- The onfocus and onresize handlers above hack around XUL bug 1194844
|
||||
- by triggering extra reflow (see bug 1194346). -->
|
||||
style="width: &window.width;">
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- This file is imported into the browser window. -->
|
||||
<!-- eslint-env mozilla/browser-window -->
|
||||
|
||||
<!-- XULCommandEvent is a specialised global. -->
|
||||
<!-- global XULCommandEvent -->
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- eslint-env mozilla/browser-window -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
|
|
|
@ -58,7 +58,7 @@ module.exports = {
|
|||
"comma-dangle": ["error", "always-multiline"],
|
||||
|
||||
// Warn about cyclomatic complexity in functions.
|
||||
"complexity": ["error", {"max": 20}],
|
||||
"complexity": ["error", {"max": 26}],
|
||||
|
||||
// Enforce dots on the next line with property name.
|
||||
"dot-location": ["error", "property"],
|
||||
|
|
|
@ -123,6 +123,13 @@ class EditDialog {
|
|||
this.handleChange(event);
|
||||
break;
|
||||
}
|
||||
case "contextmenu": {
|
||||
if (!(event.target instanceof HTMLInputElement) &&
|
||||
!(event.target instanceof HTMLTextAreaElement)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +178,7 @@ class EditDialog {
|
|||
*/
|
||||
attachEventListeners() {
|
||||
window.addEventListener("keypress", this);
|
||||
window.addEventListener("contextmenu", this);
|
||||
this._elements.controlsContainer.addEventListener("click", this);
|
||||
document.addEventListener("input", this);
|
||||
}
|
||||
|
@ -180,6 +188,7 @@ class EditDialog {
|
|||
*/
|
||||
detachEventListeners() {
|
||||
window.removeEventListener("keypress", this);
|
||||
window.removeEventListener("contextmenu", this);
|
||||
this._elements.controlsContainer.removeEventListener("click", this);
|
||||
document.removeEventListener("input", this);
|
||||
}
|
||||
|
|
|
@ -206,6 +206,10 @@ class ManageRecords {
|
|||
this.handleKeyPress(event);
|
||||
break;
|
||||
}
|
||||
case "contextmenu": {
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,6 +254,7 @@ class ManageRecords {
|
|||
attachEventListeners() {
|
||||
window.addEventListener("unload", this, {once: true});
|
||||
window.addEventListener("keypress", this);
|
||||
window.addEventListener("contextmenu", this);
|
||||
this._elements.records.addEventListener("change", this);
|
||||
this._elements.records.addEventListener("click", this);
|
||||
this._elements.controlsContainer.addEventListener("click", this);
|
||||
|
@ -261,6 +266,7 @@ class ManageRecords {
|
|||
*/
|
||||
detachEventListeners() {
|
||||
window.removeEventListener("keypress", this);
|
||||
window.removeEventListener("contextmenu", this);
|
||||
this._elements.records.removeEventListener("change", this);
|
||||
this._elements.records.removeEventListener("click", this);
|
||||
this._elements.controlsContainer.removeEventListener("click", this);
|
||||
|
|
|
@ -14,10 +14,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
|
||||
"@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
|
||||
|
||||
// Flag to control if we want to send new/old telemetry
|
||||
// TODO: remove this flag and the legacy code in Bug 1419996
|
||||
const NEW_TABLE = true;
|
||||
|
||||
// Validate the content has non-empty string
|
||||
function hasString(str) {
|
||||
return typeof str == "string" && str.length > 0;
|
||||
|
@ -337,44 +333,6 @@ const EVENT_WHITELIST = {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* We send 2 kinds (firefox-onboarding-event, firefox-onboarding-session) of pings to ping centre
|
||||
* server (they call it `topic`). The `internal` state in `topic` field means this event is used internaly to
|
||||
* track states and will not send out any message.
|
||||
*
|
||||
* To save server space and make query easier, we track session begin and end but only send pings
|
||||
* when session end. Therefore the server will get single "onboarding/overlay/notification-session"
|
||||
* event which includes both `session_begin` and `session_end` timestamp.
|
||||
*
|
||||
* We send `session_begin` and `session_end` timestamps instead of `session_duration` diff because
|
||||
* of analytics engineer's request.
|
||||
*/
|
||||
const OLD_EVENT_WHITELIST = {
|
||||
// track when click the notification close button
|
||||
"notification-close-button-click": {topic: "firefox-onboarding-event", category: "notification-interactions"},
|
||||
// track when click the notification Call-To-Action button
|
||||
"notification-cta-click": {topic: "firefox-onboarding-event", category: "notification-interactions"},
|
||||
// track when notification is shown
|
||||
"notification-session-begin": {topic: "internal"},
|
||||
// track when the notification closed
|
||||
"notification-session-end": {topic: "firefox-onboarding-session", category: "notification-interactions"},
|
||||
// init onboarding session with session_key and page url
|
||||
"onboarding-register-session": {topic: "internal"},
|
||||
// track when the onboarding script inited
|
||||
"onboarding-session-begin": {topic: "internal"},
|
||||
// track when the onboarding script destoryed
|
||||
"onboarding-session-end": {topic: "firefox-onboarding-session", category: "overlay-interactions"},
|
||||
// track when click the overlay Call-To-Action button
|
||||
"overlay-cta-click": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
|
||||
// track when click or auto select the overlay navigate item
|
||||
"overlay-nav-click": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
|
||||
// track when the overlay is shown
|
||||
"overlay-session-begin": {topic: "internal"},
|
||||
// track when the overlay is closed
|
||||
"overlay-session-end": {topic: "firefox-onboarding-session", category: "overlay-interactions"},
|
||||
// track when click the overlay "skip tour" button
|
||||
"overlay-skip-tour": {topic: "firefox-onboarding-event", category: "overlay-interactions"},
|
||||
};
|
||||
const ONBOARDING_ID = "onboarding";
|
||||
|
||||
let OnboardingTelemetry = {
|
||||
|
@ -385,34 +343,11 @@ let OnboardingTelemetry = {
|
|||
},
|
||||
|
||||
init(startupData) {
|
||||
if (NEW_TABLE) {
|
||||
this.sessionProbe = new PingCentre({topic: "firefox-onboarding-session2"});
|
||||
this.eventProbe = new PingCentre({topic: "firefox-onboarding-event2"});
|
||||
} else {
|
||||
this.sessionProbe = new PingCentre({topic: "firefox-onboarding-session"});
|
||||
this.eventProbe = new PingCentre({topic: "firefox-onboarding-event"});
|
||||
}
|
||||
this.sessionProbe = new PingCentre({topic: "firefox-onboarding-session2"});
|
||||
this.eventProbe = new PingCentre({topic: "firefox-onboarding-event2"});
|
||||
this.state.addon_version = startupData.version;
|
||||
},
|
||||
|
||||
// register per tab session data
|
||||
registerNewTelemetrySession(data) {
|
||||
let { page, session_key, tour_type } = data;
|
||||
if (this.state.sessions[session_key]) {
|
||||
return;
|
||||
}
|
||||
// session_key and page url are must have
|
||||
if (!session_key || !page || !tour_type) {
|
||||
throw new Error("session_key, page url, and tour_type are required for onboarding-register-session");
|
||||
}
|
||||
let session_id = gUUIDGenerator.generateUUID().toString();
|
||||
this.state.sessions[session_key] = {
|
||||
page,
|
||||
session_id,
|
||||
tour_type,
|
||||
};
|
||||
},
|
||||
|
||||
// register per tab session data
|
||||
registerNewOnboardingSession(data) {
|
||||
let { page, session_key, tour_type } = data;
|
||||
|
@ -434,14 +369,6 @@ let OnboardingTelemetry = {
|
|||
},
|
||||
|
||||
process(data) {
|
||||
if (NEW_TABLE) {
|
||||
this.processPings(data);
|
||||
} else {
|
||||
this.processOldPings(data);
|
||||
}
|
||||
},
|
||||
|
||||
processPings(data) {
|
||||
let { type, session_key } = data;
|
||||
if (type === "onboarding-register-session") {
|
||||
this.registerNewOnboardingSession(data);
|
||||
|
@ -619,123 +546,6 @@ let OnboardingTelemetry = {
|
|||
}
|
||||
},
|
||||
|
||||
processOldPings(data) {
|
||||
let { event, session_key } = data;
|
||||
let topic = OLD_EVENT_WHITELIST[event] && OLD_EVENT_WHITELIST[event].topic;
|
||||
if (!topic) {
|
||||
throw new Error(`ping-centre doesn't know ${event}, only knows ${Object.keys(OLD_EVENT_WHITELIST)}`);
|
||||
}
|
||||
|
||||
if (event === "onboarding-register-session") {
|
||||
this.registerNewTelemetrySession(data);
|
||||
}
|
||||
|
||||
if (!this.state.sessions[session_key]) {
|
||||
throw new Error(`should pass valid session_key`);
|
||||
}
|
||||
|
||||
if (topic === "internal") {
|
||||
switch (event) {
|
||||
case "onboarding-session-begin":
|
||||
this.state.sessions[session_key].onboarding_session_begin = Date.now();
|
||||
break;
|
||||
case "overlay-session-begin":
|
||||
this.state.sessions[session_key].overlay_session_begin = Date.now();
|
||||
break;
|
||||
case "notification-session-begin":
|
||||
this.state.sessions[session_key].notification_session_begin = Date.now();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._sendOldPings(topic, data);
|
||||
}
|
||||
},
|
||||
|
||||
// send out pings by topic
|
||||
_sendOldPings(topic, data) {
|
||||
let {
|
||||
addon_version,
|
||||
} = this.state;
|
||||
let {
|
||||
event,
|
||||
tour_id = "",
|
||||
session_key,
|
||||
} = data;
|
||||
let {
|
||||
notification_session_begin,
|
||||
onboarding_session_begin,
|
||||
overlay_session_begin,
|
||||
page,
|
||||
session_id,
|
||||
tour_type,
|
||||
} = this.state.sessions[session_key];
|
||||
let category = OLD_EVENT_WHITELIST[event].category;
|
||||
// the field is used to identify how user open the overlay (through default logo or watermark),
|
||||
// the number of open from notification can be retrieved via `notification-cta-click` event
|
||||
let tour_source = Services.prefs.getStringPref("browser.onboarding.state", "default");
|
||||
let session_begin;
|
||||
switch (topic) {
|
||||
case "firefox-onboarding-session":
|
||||
switch (event) {
|
||||
case "onboarding-session-end":
|
||||
if (!onboarding_session_begin) {
|
||||
throw new Error(`should fire onboarding-session-begin event before ${event}`);
|
||||
}
|
||||
event = "onboarding-session";
|
||||
session_begin = onboarding_session_begin;
|
||||
delete this.state.sessions[session_key];
|
||||
break;
|
||||
case "overlay-session-end":
|
||||
if (!overlay_session_begin) {
|
||||
throw new Error(`should fire overlay-session-begin event before ${event}`);
|
||||
}
|
||||
event = "overlay-session";
|
||||
session_begin = overlay_session_begin;
|
||||
break;
|
||||
case "notification-session-end":
|
||||
if (!notification_session_begin) {
|
||||
throw new Error(`should fire notification-session-begin event before ${event}`);
|
||||
}
|
||||
event = "notification-session";
|
||||
session_begin = notification_session_begin;
|
||||
break;
|
||||
}
|
||||
|
||||
let session_end = Date.now();
|
||||
this.sessionProbe && this.sessionProbe.sendPing({
|
||||
addon_version,
|
||||
category,
|
||||
event,
|
||||
page,
|
||||
session_begin,
|
||||
session_end,
|
||||
session_id,
|
||||
tour_id,
|
||||
tour_source,
|
||||
tour_type,
|
||||
}, {filter: ONBOARDING_ID});
|
||||
break;
|
||||
case "firefox-onboarding-event":
|
||||
let impression = (event === "notification-close-button-click" ||
|
||||
event === "notification-cta-click") ?
|
||||
Services.prefs.getIntPref("browser.onboarding.notification.prompt-count", 0) : -1;
|
||||
let timestamp = Date.now();
|
||||
this.eventProbe && this.eventProbe.sendPing({
|
||||
addon_version,
|
||||
category,
|
||||
event,
|
||||
impression,
|
||||
page,
|
||||
session_id,
|
||||
timestamp,
|
||||
tour_id,
|
||||
tour_source,
|
||||
tour_type,
|
||||
}, {filter: ONBOARDING_ID});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// validate data sanitation and make sure correct ping params are sent
|
||||
_validatePayload(payload) {
|
||||
let type = payload.type;
|
||||
|
|
|
@ -155,6 +155,19 @@ Enrollment
|
|||
addonVersion
|
||||
The add-on's version (example: ``"1.2.3"``).
|
||||
|
||||
Enroll Failure
|
||||
method
|
||||
The string ``"enrollFailed"``
|
||||
object
|
||||
The string ``"addon_study"``
|
||||
value
|
||||
The name of the study (``recipe.arguments.slug``).
|
||||
reason
|
||||
A string containing the filename and line number of the code
|
||||
that failed, and the name of the error thrown. This information
|
||||
is purposely limited to avoid leaking personally identifiable
|
||||
information. This should be considered a bug.
|
||||
|
||||
Unenrollment
|
||||
method
|
||||
The string ``"unenroll"``.
|
||||
|
@ -186,5 +199,5 @@ Unenrollment
|
|||
uninstalled while Shield was inactive. This could be that
|
||||
the add-on is no longer compatible, or was manually removed
|
||||
from a profile.
|
||||
* ``"unknown"``: A reason was not specificied. This should be
|
||||
* ``"unknown"``: A reason was not specified. This should be
|
||||
considered a bug.
|
||||
|
|
|
@ -256,41 +256,48 @@ this.AddonStudies = {
|
|||
throw new Error(`A study for recipe ${recipeId} already exists.`);
|
||||
}
|
||||
|
||||
const addonFile = await this.downloadAddonToTemporaryFile(addonUrl);
|
||||
const install = await AddonManager.getInstallForFile(addonFile);
|
||||
const study = {
|
||||
recipeId,
|
||||
name,
|
||||
description,
|
||||
addonId: install.addon.id,
|
||||
addonVersion: install.addon.version,
|
||||
addonUrl,
|
||||
active: true,
|
||||
studyStartDate: new Date(),
|
||||
};
|
||||
|
||||
TelemetryEvents.sendEvent("enroll", "addon_study", name, {
|
||||
addonId: install.addon.id,
|
||||
addonVersion: install.addon.version,
|
||||
});
|
||||
|
||||
let addonFile;
|
||||
try {
|
||||
addonFile = await this.downloadAddonToTemporaryFile(addonUrl);
|
||||
const install = await AddonManager.getInstallForFile(addonFile);
|
||||
const study = {
|
||||
recipeId,
|
||||
name,
|
||||
description,
|
||||
addonId: install.addon.id,
|
||||
addonVersion: install.addon.version,
|
||||
addonUrl,
|
||||
active: true,
|
||||
studyStartDate: new Date(),
|
||||
};
|
||||
|
||||
await getStore(db).add(study);
|
||||
await Addons.applyInstall(install, false);
|
||||
return study;
|
||||
} catch (err) {
|
||||
await getStore(db).delete(recipeId);
|
||||
|
||||
TelemetryEvents.sendEvent("unenroll", "addon_study", name, {
|
||||
reason: "install-failure",
|
||||
TelemetryEvents.sendEvent("enroll", "addon_study", name, {
|
||||
addonId: install.addon.id,
|
||||
addonVersion: install.addon.version,
|
||||
});
|
||||
|
||||
return study;
|
||||
} catch (err) {
|
||||
await getStore(db).delete(recipeId);
|
||||
|
||||
// The actual stack trace and error message could possibly
|
||||
// contain PII, so we don't include them here. Instead include
|
||||
// some information that should still be helpful, and is less
|
||||
// likely to be unsafe.
|
||||
const safeErrorMessage = `${err.fileName}:${err.lineNumber}:${err.columnNumber} ${err.name}`;
|
||||
TelemetryEvents.sendEvent("enrollFailed", "addon_study", name, {
|
||||
reason: safeErrorMessage.slice(0, 80), // max length is 80 chars
|
||||
});
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
Services.obs.notifyObservers(addonFile, "flush-cache-entry");
|
||||
await OS.File.remove(addonFile.path);
|
||||
if (addonFile) {
|
||||
Services.obs.notifyObservers(addonFile, "flush-cache-entry");
|
||||
await OS.File.remove(addonFile.path);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -20,6 +20,13 @@ const TelemetryEvents = {
|
|||
record_on_release: true,
|
||||
},
|
||||
|
||||
enroll_failure: {
|
||||
methods: ["enrollFailed"],
|
||||
objects: ["addon_study"],
|
||||
extra_keys: ["reason"],
|
||||
record_on_release: true,
|
||||
},
|
||||
|
||||
unenroll: {
|
||||
methods: ["unenroll"],
|
||||
objects: ["preference_study", "addon_study"],
|
||||
|
|
|
@ -142,19 +142,31 @@ decorate_task(
|
|||
|
||||
decorate_task(
|
||||
withStub(Addons, "applyInstall"),
|
||||
withStub(TelemetryEvents, "sendEvent"),
|
||||
withWebExtension(),
|
||||
async function testStartAddonCleanup(applyInstallStub, [addonId, addonFile]) {
|
||||
applyInstallStub.rejects(new Error("Fake failure"));
|
||||
async function testStartAddonCleanup(applyInstallStub, sendEventStub, [addonId, addonFile]) {
|
||||
const fakeError = new Error("Fake failure");
|
||||
fakeError.fileName = "fake/filename.js";
|
||||
fakeError.lineNumber = 42;
|
||||
fakeError.columnNumber = 54;
|
||||
applyInstallStub.rejects(fakeError);
|
||||
|
||||
const addonUrl = Services.io.newFileURI(addonFile).spec;
|
||||
const args = startArgsFactory({addonUrl});
|
||||
await Assert.rejects(
|
||||
AddonStudies.start(startArgsFactory({addonUrl})),
|
||||
AddonStudies.start(args),
|
||||
/Fake failure/,
|
||||
"start rejects when the Addons.applyInstall function rejects"
|
||||
);
|
||||
|
||||
const addon = await Addons.get(addonId);
|
||||
ok(!addon, "If something fails during start after the add-on is installed, it is uninstalled.");
|
||||
|
||||
Assert.deepEqual(
|
||||
sendEventStub.getCall(0).args,
|
||||
["enrollFailed", "addon_study", args.name, {reason: "fake/filename.js:42:54 Error"}],
|
||||
"AddonStudies.start() should send an enroll-failed event when applyInstall rejects",
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ available. -->
|
|||
|
||||
<!ENTITY networkProxy.label "Network Proxy">
|
||||
|
||||
<!ENTITY connectionDesc.label "Configure how &brandShortName; connects to the Internet">
|
||||
<!ENTITY connectionSettingsLearnMore.label "Learn more">
|
||||
<!ENTITY connectionSettings.label "Settings…">
|
||||
<!ENTITY connectionSettings.accesskey "e">
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<!ENTITY window.width2 "49em">
|
||||
<!ENTITY window.macWidth2 "44em">
|
||||
|
||||
<!ENTITY proxyTitle.label "Configure Proxies to Access the Internet">
|
||||
<!ENTITY proxyTitle.label2 "Configure Proxy Access to the Internet">
|
||||
<!ENTITY noProxyTypeRadio.label "No proxy">
|
||||
<!ENTITY noProxyTypeRadio.accesskey "y">
|
||||
<!ENTITY systemTypeRadio.label "Use system proxy settings">
|
||||
|
|
|
@ -295,8 +295,18 @@ extensionControlled.privacy.containers = An extension, %S, requires Container Ta
|
|||
# This string is shown to notify the user that their tracking protection preferences are being controlled by an extension.
|
||||
extensionControlled.websites.trackingProtectionMode = An extension, %S, is controlling tracking protection.
|
||||
|
||||
# LOCALIZATION NOTE (extensionControlled.proxyConfig):
|
||||
# This string is shown to notify the user that their proxy configuration preferences are being controlled by an extension.
|
||||
# %1$S is the icon and name of the extension.
|
||||
# %2$S is the brandShortName from brand.properties (for example "Nightly")
|
||||
extensionControlled.proxyConfig = An extension, %1$S, is controlling how %2$S connects to the internet.
|
||||
|
||||
# LOCALIZATION NOTE (extensionControlled.enable):
|
||||
# %1$S is replaced with the icon for the add-ons menu.
|
||||
# %2$S is replaced with the icon for the toolbar menu.
|
||||
# This string is shown to notify the user how to enable an extension that they disabled.
|
||||
extensionControlled.enable = To enable the extension go to %1$S Add-ons in the %2$S menu.
|
||||
|
||||
# LOCALIZATION NOTE (connectionDesc.label):
|
||||
# %S is the brandShortName from brand.properties (for example "Nightly")
|
||||
connectionDesc.label = Configure how %S connects to the internet.
|
||||
|
|
|
@ -3,7 +3,3 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#filter substitution
|
||||
|
||||
# LOCALIZATION NOTE: this preference is set to true for en-US specifically,
|
||||
# locales without this line have the setting set to false by default.
|
||||
pref("browser.search.geoSpecificDefaults", true);
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
diff -Nru gdb-7.12/debian/changelog gdb-7.12/debian/changelog
|
||||
--- gdb-7.12/debian/changelog 2017-01-19 19:28:25.000000000 +0900
|
||||
+++ gdb-7.12/debian/changelog 2018-02-07 15:36:15.000000000 +0900
|
||||
@@ -1,3 +1,16 @@
|
||||
+gdb (7.12-6.deb7moz1) wheezy; urgency=medium
|
||||
+
|
||||
+ * Mozilla backport for wheezy.
|
||||
+ * debian/rules, debian/control: Don't use libbabeltrace, which is not
|
||||
+ available on wheezy.
|
||||
+ * debian/rules:
|
||||
+ - Use DEB_HOST_{ARCH,GNU_TYPE} instead of DEB_TARGET_{ARCH,GNU_TYPE}
|
||||
+ because the latter is not available on wheezy.
|
||||
+ - Don't pass --dbgsym-migration to dh_strip, it's not supported on
|
||||
+ wheezy's debhelper.
|
||||
+
|
||||
+ -- Mike Hommey <glandium@mozilla.com> Wed, 07 Feb 2018 15:36:15 +0900
|
||||
+
|
||||
gdb (7.12-6) unstable; urgency=medium
|
||||
|
||||
* debian/patches: import 7.12 branch fixes
|
||||
diff -Nru gdb-7.12/debian/control gdb-7.12/debian/control
|
||||
--- gdb-7.12/debian/control 2017-01-17 20:01:41.000000000 +0900
|
||||
+++ gdb-7.12/debian/control 2018-02-07 15:36:06.000000000 +0900
|
||||
@@ -33,8 +33,6 @@
|
||||
libreadline-dev,
|
||||
zlib1g-dev,
|
||||
liblzma-dev,
|
||||
- libbabeltrace-dev [amd64 armel armhf i386 kfreebsd-amd64 kfreebsd-i386 mips mipsel mips64el powerpc s390x],
|
||||
- libbabeltrace-ctf-dev [amd64 armel armhf i386 kfreebsd-amd64 kfreebsd-i386 mips mipsel mips64el powerpc s390x],
|
||||
python-dev,
|
||||
# Python3 build
|
||||
python3-dev,
|
||||
diff -Nru gdb-7.12/debian/rules gdb-7.12/debian/rules
|
||||
--- gdb-7.12/debian/rules 2016-12-15 09:31:54.000000000 +0900
|
||||
+++ gdb-7.12/debian/rules 2018-02-07 15:36:15.000000000 +0900
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
DEB_BUILDDIR := $(ALL_BUILDDIR)/objdir
|
||||
DEB_DH_INSTALL_SOURCEDIR := $(shell pwd)/debian/tmp
|
||||
-DEB_DH_STRIP_ARGS_gdb = --dbgsym-migration='gdb-dbg (<< 7.12-1~)'
|
||||
|
||||
# Override CDBS's default CFLAGS, which also includes -Wall; gdb
|
||||
# does not handle -Wunused well with -Werror, but defaults to
|
||||
@@ -35,8 +34,8 @@
|
||||
DEB_HOST_GNU_SYSTEM := $(shell dpkg-architecture -qDEB_HOST_GNU_SYSTEM)
|
||||
DEB_HOST_GNU_CPU := $(shell dpkg-architecture -qDEB_HOST_GNU_CPU)
|
||||
DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
|
||||
-DEB_TARGET_ARCH := $(shell dpkg-architecture -qDEB_TARGET_ARCH)
|
||||
-DEB_TARGET_GNU_TYPE := $(shell dpkg-architecture -qDEB_TARGET_GNU_TYPE)
|
||||
+DEB_TARGET_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
|
||||
+DEB_TARGET_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
|
||||
|
||||
# Cross configuration support. Check for an environment variable
|
||||
# $GDB_TARGET, or a file debian/target.
|
||||
@@ -89,10 +88,6 @@
|
||||
arch_config_args := --with-libunwind-ia64
|
||||
endif
|
||||
|
||||
-ifneq (,$(filter $(DEB_HOST_ARCH),amd64 armel armhf i386 kfreebsd-amd64 kfreebsd-i386 mips mipsel powerpc s390x))
|
||||
- arch_config_args += --with-babeltrace
|
||||
-endif
|
||||
-
|
||||
ifdef GDB_TARGET
|
||||
run_tests := no
|
||||
|
|
@ -81,7 +81,7 @@ module.exports = {
|
|||
// impair readability, but also not required either.
|
||||
"comma-dangle": "off",
|
||||
// Warn about cyclomatic complexity in functions.
|
||||
"complexity": ["error", 35],
|
||||
"complexity": ["error", 53],
|
||||
// Don't warn for inconsistent naming when capturing this (not so important
|
||||
// with auto-binding fat arrow functions).
|
||||
"consistent-this": "off",
|
||||
|
|
|
@ -18,7 +18,6 @@ class Font extends PureComponent {
|
|||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.getSectionClasses = this.getSectionClasses.bind(this);
|
||||
this.renderFontCSS = this.renderFontCSS.bind(this);
|
||||
this.renderFontCSSCode = this.renderFontCSSCode.bind(this);
|
||||
this.renderFontFormatURL = this.renderFontFormatURL.bind(this);
|
||||
|
@ -26,19 +25,6 @@ class Font extends PureComponent {
|
|||
this.renderFontPreview = this.renderFontPreview.bind(this);
|
||||
}
|
||||
|
||||
getSectionClasses() {
|
||||
let { font } = this.props;
|
||||
|
||||
let classes = ["font"];
|
||||
classes.push((font.URI) ? "is-remote" : "is-local");
|
||||
|
||||
if (font.rule) {
|
||||
classes.push("has-code");
|
||||
}
|
||||
|
||||
return classes.join(" ");
|
||||
}
|
||||
|
||||
renderFontCSS(cssFamilyName) {
|
||||
return dom.p(
|
||||
{
|
||||
|
@ -59,12 +45,12 @@ class Font extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
renderFontCSSCode(rule, ruleText) {
|
||||
renderFontCSSCode(ruleText) {
|
||||
return dom.pre(
|
||||
{
|
||||
className: "font-css-code"
|
||||
},
|
||||
rule ? ruleText : null
|
||||
ruleText
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,32 +120,16 @@ class Font extends PureComponent {
|
|||
URI,
|
||||
} = font;
|
||||
|
||||
return dom.section(
|
||||
return dom.li(
|
||||
{
|
||||
className: this.getSectionClasses(),
|
||||
className: "font",
|
||||
},
|
||||
this.renderFontPreview(previewUrl),
|
||||
dom.div(
|
||||
{
|
||||
className: "font-info",
|
||||
},
|
||||
this.renderFontName(name),
|
||||
dom.span(
|
||||
{
|
||||
className: "font-is-local",
|
||||
},
|
||||
" " + getStr("fontinspector.system")
|
||||
),
|
||||
dom.span(
|
||||
{
|
||||
className: "font-is-remote",
|
||||
},
|
||||
" " + getStr("fontinspector.remote")
|
||||
),
|
||||
this.renderFontFormatURL(URI, format),
|
||||
this.renderFontCSS(CSSFamilyName),
|
||||
this.renderFontCSSCode(rule, ruleText)
|
||||
)
|
||||
this.renderFontName(name),
|
||||
" " + (URI ? getStr("fontinspector.remote") : getStr("fontinspector.system")),
|
||||
URI ? this.renderFontFormatURL(URI, format) : null,
|
||||
this.renderFontCSS(CSSFamilyName),
|
||||
rule ? this.renderFontCSSCode(ruleText) : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,41 +43,46 @@ add_task(function* () {
|
|||
yield testShowAllFonts(inspector, viewDoc);
|
||||
});
|
||||
|
||||
function isRemote(fontLiEl) {
|
||||
return fontLiEl.querySelectorAll(".font-format-url").length === 1;
|
||||
}
|
||||
|
||||
function* testBodyFonts(inspector, viewDoc) {
|
||||
let s = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(s.length, 5, "Found 5 fonts");
|
||||
let lis = viewDoc.querySelectorAll("#all-fonts > li");
|
||||
is(lis.length, 5, "Found 5 fonts");
|
||||
|
||||
for (let i = 0; i < FONTS.length; i++) {
|
||||
let section = s[i];
|
||||
let li = lis[i];
|
||||
let font = FONTS[i];
|
||||
is(section.querySelector(".font-name").textContent, font.name,
|
||||
is(li.querySelector(".font-name").textContent, font.name,
|
||||
"font " + i + " right font name");
|
||||
is(section.classList.contains("is-remote"), font.remote,
|
||||
|
||||
is(isRemote(li), font.remote,
|
||||
"font " + i + " remote value correct");
|
||||
is(section.querySelector(".font-url").value, font.url,
|
||||
is(li.querySelector(".font-url").value, font.url,
|
||||
"font " + i + " url correct");
|
||||
is(section.querySelector(".font-format").hidden, !font.format,
|
||||
is(li.querySelector(".font-format").hidden, !font.format,
|
||||
"font " + i + " format hidden value correct");
|
||||
is(section.querySelector(".font-format").textContent,
|
||||
is(li.querySelector(".font-format").textContent,
|
||||
font.format, "font " + i + " format correct");
|
||||
is(section.querySelector(".font-css-name").textContent,
|
||||
is(li.querySelector(".font-css-name").textContent,
|
||||
font.cssName, "font " + i + " css name correct");
|
||||
}
|
||||
|
||||
// test that the bold and regular fonts have different previews
|
||||
let regSrc = s[0].querySelector(".font-preview").src;
|
||||
let boldSrc = s[1].querySelector(".font-preview").src;
|
||||
let regSrc = lis[0].querySelector(".font-preview").src;
|
||||
let boldSrc = lis[1].querySelector(".font-preview").src;
|
||||
isnot(regSrc, boldSrc, "preview for bold font is different from regular");
|
||||
|
||||
// test system font
|
||||
let localFontName = s[4].querySelector(".font-name").textContent;
|
||||
let localFontCSSName = s[4].querySelector(".font-css-name").textContent;
|
||||
let localFontName = lis[4].querySelector(".font-name").textContent;
|
||||
let localFontCSSName = lis[4].querySelector(".font-css-name").textContent;
|
||||
|
||||
// On Linux test machines, the Arial font doesn't exist.
|
||||
// The fallback is "Liberation Sans"
|
||||
ok((localFontName == "Arial") || (localFontName == "Liberation Sans"),
|
||||
"local font right font name");
|
||||
ok(s[4].classList.contains("is-local"), "local font is local");
|
||||
ok(!isRemote(lis[4]), "local font is local");
|
||||
ok((localFontCSSName == "Arial") || (localFontCSSName == "Liberation Sans"),
|
||||
"Arial", "local font has right css name");
|
||||
}
|
||||
|
@ -87,9 +92,9 @@ function* testDivFonts(inspector, viewDoc) {
|
|||
yield selectNode("div", inspector);
|
||||
yield updated;
|
||||
|
||||
let sections1 = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(sections1.length, 1, "Found 1 font on DIV");
|
||||
is(sections1[0].querySelector(".font-name").textContent,
|
||||
let lis = viewDoc.querySelectorAll("#all-fonts > li");
|
||||
is(lis.length, 1, "Found 1 font on DIV");
|
||||
is(lis[0].querySelector(".font-name").textContent,
|
||||
"Ostrich Sans Medium",
|
||||
"The DIV font has the right name");
|
||||
}
|
||||
|
@ -103,6 +108,6 @@ function* testShowAllFonts(inspector, viewDoc) {
|
|||
|
||||
// shouldn't change the node selection
|
||||
is(inspector.selection.nodeFront.nodeName, "DIV", "Show all fonts selected");
|
||||
let sections = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
is(sections.length, 6, "Font inspector shows 6 fonts (1 from iframe)");
|
||||
let lis = viewDoc.querySelectorAll("#all-fonts > li");
|
||||
is(lis.length, 6, "Font inspector shows 6 fonts (1 from iframe)");
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ add_task(function* () {
|
|||
let { nodes } = yield inspector.walker.children(bodyNode);
|
||||
yield selectNode(nodes[0], inspector);
|
||||
|
||||
let sections = viewDoc.querySelectorAll("#all-fonts > section");
|
||||
let sections = viewDoc.querySelectorAll("#all-fonts > li");
|
||||
is(sections.length, 1, "Font inspector shows 1 font");
|
||||
});
|
||||
|
|
|
@ -21,11 +21,6 @@ NetworkPanel.durationMS=%Sms
|
|||
|
||||
ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
|
||||
|
||||
# LOCALIZATION NOTE (webConsoleWindowTitleAndURL): the Web Console floating
|
||||
# panel title. For RTL languages you need to set the LRM in the string to give
|
||||
# the URL the correct direction. Parameters: %S is the web page URL.
|
||||
webConsoleWindowTitleAndURL=Web Console - %S
|
||||
|
||||
# LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
|
||||
# a URL in the Web Console that was requested using an XMLHttpRequest.
|
||||
# Should probably be the same as &btnConsoleXhr; in webConsole.dtd
|
||||
|
|
|
@ -199,11 +199,11 @@ class Tabbar extends Component {
|
|||
onTabChanged(index) {
|
||||
this.setState({
|
||||
activeTab: index
|
||||
}, () => {
|
||||
if (this.props.onSelect) {
|
||||
this.props.onSelect(this.state.tabs[index].id);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.props.onSelect) {
|
||||
this.props.onSelect(this.state.tabs[index].id);
|
||||
}
|
||||
}
|
||||
|
||||
onAllTabsMenuClick(event) {
|
||||
|
|
|
@ -322,6 +322,12 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
* label1: total => l10n.getFormatStr("...", total), // 5
|
||||
* label2: total => l10n.getFormatStr("...", total), // 9
|
||||
* }
|
||||
* - header: an object specifying strings to use for table column
|
||||
* headers
|
||||
* e.g. {
|
||||
* label1: l10n.getStr(...),
|
||||
* label2: l10n.getStr(...),
|
||||
* }
|
||||
* @return TableChart
|
||||
* A table chart proxy instance, which emits the following events:
|
||||
* - "mouseover", when the mouse enters a row
|
||||
|
@ -331,6 +337,7 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
function createTableChart(document, { title, data, strings, totals, header }) {
|
||||
strings = strings || {};
|
||||
totals = totals || {};
|
||||
header = header || {};
|
||||
let isPlaceholder = false;
|
||||
|
||||
// If there's no data available, display an empty placeholder.
|
||||
|
|
|
@ -33,20 +33,6 @@
|
|||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dim > #font-container,
|
||||
.font:not(.has-code) .font-css-code,
|
||||
.font-is-local,
|
||||
.font-is-remote,
|
||||
.font.is-local .font-format-url,
|
||||
#font-template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.font.is-remote .font-is-remote,
|
||||
.font.is-local .font-is-local {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.font-format::before {
|
||||
content: "(";
|
||||
}
|
||||
|
@ -98,10 +84,6 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.font-info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.font-name {
|
||||
display: inline;
|
||||
}
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
|
||||
const {extend} = require("devtools/shared/extend");
|
||||
var {TargetFactory} = require("devtools/client/framework/target");
|
||||
var {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
|
||||
var {Tools} = require("devtools/client/definitions");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
var promise = require("promise");
|
||||
const defer = require("devtools/shared/defer");
|
||||
var Services = require("Services");
|
||||
loader.lazyRequireGetter(this, "Utils", "devtools/client/webconsole/utils", true);
|
||||
loader.lazyRequireGetter(this, "extend", "devtools/shared/extend", true);
|
||||
loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
|
||||
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
|
||||
loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
|
||||
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
|
||||
loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
|
||||
loader.lazyRequireGetter(this, "NewWebConsoleFrame", "devtools/client/webconsole/new-webconsole", true);
|
||||
|
@ -21,18 +18,15 @@ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
|
|||
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
|
||||
loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
|
||||
loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
|
||||
const l10n = require("devtools/client/webconsole/webconsole-l10n");
|
||||
const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
|
||||
loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/webconsole-l10n");
|
||||
const BC_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
|
||||
|
||||
// The preference prefix for all of the Browser Console filters.
|
||||
const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
|
||||
const BC_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
|
||||
|
||||
var gHudId = 0;
|
||||
|
||||
// The HUD service
|
||||
|
||||
function HUD_SERVICE()
|
||||
{
|
||||
function HUD_SERVICE() {
|
||||
this.consoles = new Map();
|
||||
this.lastFinishedRequest = { callback: null };
|
||||
}
|
||||
|
@ -40,7 +34,7 @@ function HUD_SERVICE()
|
|||
HUD_SERVICE.prototype =
|
||||
{
|
||||
_browserConsoleID: null,
|
||||
_browserConsoleDefer: null,
|
||||
_browserConsoleInitializing: null,
|
||||
|
||||
/**
|
||||
* Keeps a reference for each Web Console / Browser Console that is created.
|
||||
|
@ -72,7 +66,7 @@ HUD_SERVICE.prototype =
|
|||
*
|
||||
* @returns nsIDOMWindow
|
||||
*/
|
||||
currentContext: function HS_currentContext() {
|
||||
currentContext() {
|
||||
return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
},
|
||||
|
||||
|
@ -81,19 +75,17 @@ HUD_SERVICE.prototype =
|
|||
*
|
||||
* @see devtools/framework/target.js for details about targets.
|
||||
*
|
||||
* @param object aTarget
|
||||
* @param object target
|
||||
* The target that the web console will connect to.
|
||||
* @param nsIDOMWindow aIframeWindow
|
||||
* @param nsIDOMWindow iframeWindow
|
||||
* The window where the web console UI is already loaded.
|
||||
* @param nsIDOMWindow aChromeWindow
|
||||
* @param nsIDOMWindow chromeWindow
|
||||
* The window of the web console owner.
|
||||
* @return object
|
||||
* A promise object for the opening of the new WebConsole instance.
|
||||
*/
|
||||
openWebConsole:
|
||||
function HS_openWebConsole(aTarget, aIframeWindow, aChromeWindow)
|
||||
{
|
||||
let hud = new WebConsole(aTarget, aIframeWindow, aChromeWindow);
|
||||
openWebConsole(target, iframeWindow, chromeWindow) {
|
||||
let hud = new WebConsole(target, iframeWindow, chromeWindow);
|
||||
this.consoles.set(hud.hudId, hud);
|
||||
return hud.init();
|
||||
},
|
||||
|
@ -103,19 +95,17 @@ HUD_SERVICE.prototype =
|
|||
*
|
||||
* @see devtools/framework/target.js for details about targets.
|
||||
*
|
||||
* @param object aTarget
|
||||
* @param object target
|
||||
* The target that the browser console will connect to.
|
||||
* @param nsIDOMWindow aIframeWindow
|
||||
* @param nsIDOMWindow iframeWindow
|
||||
* The window where the browser console UI is already loaded.
|
||||
* @param nsIDOMWindow aChromeWindow
|
||||
* @param nsIDOMWindow chromeWindow
|
||||
* The window of the browser console owner.
|
||||
* @return object
|
||||
* A promise object for the opening of the new BrowserConsole instance.
|
||||
*/
|
||||
openBrowserConsole:
|
||||
function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow)
|
||||
{
|
||||
let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow);
|
||||
openBrowserConsole(target, iframeWindow, chromeWindow) {
|
||||
let hud = new BrowserConsole(target, iframeWindow, chromeWindow);
|
||||
this._browserConsoleID = hud.hudId;
|
||||
this.consoles.set(hud.hudId, hud);
|
||||
return hud.init();
|
||||
|
@ -124,14 +114,13 @@ HUD_SERVICE.prototype =
|
|||
/**
|
||||
* Returns the Web Console object associated to a content window.
|
||||
*
|
||||
* @param nsIDOMWindow aContentWindow
|
||||
* @param nsIDOMWindow contentWindow
|
||||
* @returns object
|
||||
*/
|
||||
getHudByWindow: function HS_getHudByWindow(aContentWindow)
|
||||
{
|
||||
for (let [hudId, hud] of this.consoles) {
|
||||
getHudByWindow(contentWindow) {
|
||||
for (let [, hud] of this.consoles) {
|
||||
let target = hud.target;
|
||||
if (target && target.tab && target.window === aContentWindow) {
|
||||
if (target && target.tab && target.window === contentWindow) {
|
||||
return hud;
|
||||
}
|
||||
}
|
||||
|
@ -141,12 +130,11 @@ HUD_SERVICE.prototype =
|
|||
/**
|
||||
* Returns the console instance for a given id.
|
||||
*
|
||||
* @param string aId
|
||||
* @param string id
|
||||
* @returns Object
|
||||
*/
|
||||
getHudReferenceById: function HS_getHudReferenceById(aId)
|
||||
{
|
||||
return this.consoles.get(aId);
|
||||
getHudReferenceById(id) {
|
||||
return this.consoles.get(id);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -156,8 +144,7 @@ HUD_SERVICE.prototype =
|
|||
* The WebConsole object or null if the active tab has no open Web
|
||||
* Console.
|
||||
*/
|
||||
getOpenWebConsole: function HS_getOpenWebConsole()
|
||||
{
|
||||
getOpenWebConsole() {
|
||||
let tab = this.currentContext().gBrowser.selectedTab;
|
||||
if (!tab || !TargetFactory.isKnownTab(tab)) {
|
||||
return null;
|
||||
|
@ -171,25 +158,19 @@ HUD_SERVICE.prototype =
|
|||
/**
|
||||
* Toggle the Browser Console.
|
||||
*/
|
||||
toggleBrowserConsole: function HS_toggleBrowserConsole()
|
||||
{
|
||||
async toggleBrowserConsole() {
|
||||
if (this._browserConsoleID) {
|
||||
let hud = this.getHudReferenceById(this._browserConsoleID);
|
||||
return hud.destroy();
|
||||
}
|
||||
|
||||
if (this._browserConsoleDefer) {
|
||||
return this._browserConsoleDefer.promise;
|
||||
if (this._browserConsoleInitializing) {
|
||||
return this._browserConsoleInitializing;
|
||||
}
|
||||
|
||||
this._browserConsoleDefer = defer();
|
||||
|
||||
function connect()
|
||||
{
|
||||
let deferred = defer();
|
||||
|
||||
// Ensure that the root actor and the tab actors have been registered on the DebuggerServer,
|
||||
// so that the Browser Console can retrieve the console actors.
|
||||
async function connect() {
|
||||
// Ensure that the root actor and the tab actors have been registered on the
|
||||
// DebuggerServer, so that the Browser Console can retrieve the console actors.
|
||||
// (See Bug 1416105 for rationale).
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerActors({ root: true, tab: true });
|
||||
|
@ -197,62 +178,60 @@ HUD_SERVICE.prototype =
|
|||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
return client.connect()
|
||||
.then(() => client.getProcess())
|
||||
.then(aResponse => {
|
||||
// Use a TabActor in order to ensure calling `attach` to the ChromeActor
|
||||
return { form: aResponse.form, client, chrome: true, isTabActor: true };
|
||||
});
|
||||
await client.connect();
|
||||
let response = await client.getProcess();
|
||||
return { form: response.form, client, chrome: true, isTabActor: true };
|
||||
}
|
||||
|
||||
let target;
|
||||
function getTarget(aConnection)
|
||||
{
|
||||
return TargetFactory.forRemoteTab(aConnection);
|
||||
}
|
||||
function openWindow(aTarget)
|
||||
{
|
||||
target = aTarget;
|
||||
return new Promise(resolve => {
|
||||
let browserConsoleURL = Tools.webConsole.browserConsoleURL;
|
||||
let win = Services.ww.openWindow(null, browserConsoleURL, "_blank",
|
||||
BROWSER_CONSOLE_WINDOW_FEATURES, null);
|
||||
win.addEventListener("DOMContentLoaded", () => {
|
||||
win.document.title = l10n.getStr("browserConsole.title");
|
||||
if (browserConsoleURL === Tools.webConsole.oldWebConsoleURL) {
|
||||
resolve({iframeWindow: win, chromeWindow: win});
|
||||
} else {
|
||||
win.document.querySelector("iframe").addEventListener("DOMContentLoaded", (e) => {
|
||||
resolve({iframeWindow: e.target.defaultView, chromeWindow: win});
|
||||
}, { once: true });
|
||||
}
|
||||
}, {once: true});
|
||||
async function openWindow(t) {
|
||||
let browserConsoleURL = Tools.webConsole.browserConsoleURL;
|
||||
let win = Services.ww.openWindow(null, browserConsoleURL, "_blank",
|
||||
BC_WINDOW_FEATURES, null);
|
||||
await new Promise(resolve => {
|
||||
win.addEventListener("DOMContentLoaded", resolve, {once: true});
|
||||
});
|
||||
}
|
||||
connect().then(getTarget).then(openWindow).then(({iframeWindow, chromeWindow}) => {
|
||||
return this.openBrowserConsole(target, iframeWindow, chromeWindow)
|
||||
.then((aBrowserConsole) => {
|
||||
this._browserConsoleDefer.resolve(aBrowserConsole);
|
||||
this._browserConsoleDefer = null;
|
||||
});
|
||||
}, console.error.bind(console));
|
||||
|
||||
return this._browserConsoleDefer.promise;
|
||||
win.document.title = l10n.getStr("browserConsole.title");
|
||||
|
||||
if (browserConsoleURL === Tools.webConsole.oldWebConsoleURL) {
|
||||
return {iframeWindow: win, chromeWindow: win};
|
||||
}
|
||||
|
||||
let iframe = win.document.querySelector("iframe");
|
||||
await new Promise(resolve => {
|
||||
iframe.addEventListener("DOMContentLoaded", resolve, {once: true});
|
||||
});
|
||||
|
||||
return {iframeWindow: iframe.contentWindow, chromeWindow: win};
|
||||
}
|
||||
|
||||
// Temporarily cache the async startup sequence so that if toggleBrowserConsole
|
||||
// gets called again we can return this console instead of opening another one.
|
||||
this._browserConsoleInitializing = (async () => {
|
||||
let connection = await connect();
|
||||
let target = await TargetFactory.forRemoteTab(connection);
|
||||
let {iframeWindow, chromeWindow} = await openWindow(target);
|
||||
let browserConsole =
|
||||
await this.openBrowserConsole(target, iframeWindow, chromeWindow);
|
||||
return browserConsole;
|
||||
})();
|
||||
|
||||
let browserConsole = await this._browserConsoleInitializing;
|
||||
this._browserConsoleInitializing = null;
|
||||
return browserConsole;
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens or focuses the Browser Console.
|
||||
*/
|
||||
openBrowserConsoleOrFocus: function HS_openBrowserConsoleOrFocus()
|
||||
{
|
||||
openBrowserConsoleOrFocus() {
|
||||
let hud = this.getBrowserConsole();
|
||||
if (hud) {
|
||||
hud.iframeWindow.focus();
|
||||
return promise.resolve(hud);
|
||||
}
|
||||
else {
|
||||
return this.toggleBrowserConsole();
|
||||
return Promise.resolve(hud);
|
||||
}
|
||||
|
||||
return this.toggleBrowserConsole();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -262,13 +241,11 @@ HUD_SERVICE.prototype =
|
|||
* A BrowserConsole instance or null if the Browser Console is not
|
||||
* open.
|
||||
*/
|
||||
getBrowserConsole: function HS_getBrowserConsole()
|
||||
{
|
||||
getBrowserConsole() {
|
||||
return this.getHudReferenceById(this._browserConsoleID);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A WebConsole instance is an interactive console initialized *per target*
|
||||
* that displays console log data as well as provides an interactive terminal to
|
||||
|
@ -279,25 +256,24 @@ HUD_SERVICE.prototype =
|
|||
* UI and features.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aTarget
|
||||
* @param object target
|
||||
* The target that the web console will connect to.
|
||||
* @param nsIDOMWindow aIframeWindow
|
||||
* @param nsIDOMWindow iframeWindow
|
||||
* The window where the web console UI is already loaded.
|
||||
* @param nsIDOMWindow aChromeWindow
|
||||
* @param nsIDOMWindow chromeWindow
|
||||
* The window of the web console owner.
|
||||
*/
|
||||
function WebConsole(aTarget, aIframeWindow, aChromeWindow)
|
||||
{
|
||||
this.iframeWindow = aIframeWindow;
|
||||
this.chromeWindow = aChromeWindow;
|
||||
function WebConsole(target, iframeWindow, chromeWindow) {
|
||||
this.iframeWindow = iframeWindow;
|
||||
this.chromeWindow = chromeWindow;
|
||||
this.hudId = "hud_" + ++gHudId;
|
||||
this.target = aTarget;
|
||||
this.target = target;
|
||||
this.browserWindow = this.chromeWindow.top;
|
||||
let element = this.browserWindow.document.documentElement;
|
||||
if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
|
||||
this.browserWindow = HUDService.currentContext();
|
||||
}
|
||||
if (aIframeWindow.location.href === Tools.webConsole.newWebConsoleURL) {
|
||||
if (iframeWindow.location.href === Tools.webConsole.newWebConsoleURL) {
|
||||
this.ui = new NewWebConsoleFrame(this);
|
||||
} else {
|
||||
this.ui = new WebConsoleFrame(this);
|
||||
|
@ -320,8 +296,7 @@ WebConsole.prototype = {
|
|||
*
|
||||
* @type function
|
||||
*/
|
||||
get lastFinishedRequestCallback()
|
||||
{
|
||||
get lastFinishedRequestCallback() {
|
||||
return HUDService.lastFinishedRequest.callback;
|
||||
},
|
||||
|
||||
|
@ -333,8 +308,7 @@ WebConsole.prototype = {
|
|||
* hosts the utilities there.
|
||||
* @type nsIDOMWindow
|
||||
*/
|
||||
get chromeUtilsWindow()
|
||||
{
|
||||
get chromeUtilsWindow() {
|
||||
if (this.browserWindow) {
|
||||
return this.browserWindow;
|
||||
}
|
||||
|
@ -345,8 +319,7 @@ WebConsole.prototype = {
|
|||
* Getter for the xul:popupset that holds any popups we open.
|
||||
* @type nsIDOMElement
|
||||
*/
|
||||
get mainPopupSet()
|
||||
{
|
||||
get mainPopupSet() {
|
||||
return this.chromeUtilsWindow.document.getElementById("mainPopupSet");
|
||||
},
|
||||
|
||||
|
@ -354,13 +327,11 @@ WebConsole.prototype = {
|
|||
* Getter for the output element that holds messages we display.
|
||||
* @type nsIDOMElement
|
||||
*/
|
||||
get outputNode()
|
||||
{
|
||||
get outputNode() {
|
||||
return this.ui ? this.ui.outputNode : null;
|
||||
},
|
||||
|
||||
get gViewSourceUtils()
|
||||
{
|
||||
get gViewSourceUtils() {
|
||||
return this.chromeUtilsWindow.gViewSourceUtils;
|
||||
},
|
||||
|
||||
|
@ -370,30 +341,16 @@ WebConsole.prototype = {
|
|||
* @return object
|
||||
* A promise for the initialization.
|
||||
*/
|
||||
init: function WC_init()
|
||||
{
|
||||
init() {
|
||||
return this.ui.init().then(() => this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the Web Console panel title.
|
||||
*
|
||||
* @return string
|
||||
* The Web Console panel title.
|
||||
*/
|
||||
getPanelTitle: function WC_getPanelTitle()
|
||||
{
|
||||
let url = this.ui ? this.ui.contentLocation : "";
|
||||
return l10n.getFormatStr("webConsoleWindowTitleAndURL", [url]);
|
||||
},
|
||||
|
||||
/**
|
||||
* The JSTerm object that manages the console's input.
|
||||
* @see webconsole.js::JSTerm
|
||||
* @type object
|
||||
*/
|
||||
get jsterm()
|
||||
{
|
||||
get jsterm() {
|
||||
return this.ui ? this.ui.jsterm : null;
|
||||
},
|
||||
|
||||
|
@ -401,8 +358,7 @@ WebConsole.prototype = {
|
|||
* The clear output button handler.
|
||||
* @private
|
||||
*/
|
||||
_onClearButton: function WC__onClearButton()
|
||||
{
|
||||
_onClearButton() {
|
||||
if (this.target.isLocalTab) {
|
||||
gDevToolsBrowser.getDeveloperToolbar(this.browserWindow)
|
||||
.resetErrorsCount(this.target.tab);
|
||||
|
@ -413,46 +369,45 @@ WebConsole.prototype = {
|
|||
* Alias for the WebConsoleFrame.setFilterState() method.
|
||||
* @see webconsole.js::WebConsoleFrame.setFilterState()
|
||||
*/
|
||||
setFilterState: function WC_setFilterState()
|
||||
{
|
||||
setFilterState() {
|
||||
this.ui && this.ui.setFilterState.apply(this.ui, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a link in a new tab.
|
||||
*
|
||||
* @param string aLink
|
||||
* @param string link
|
||||
* The URL you want to open in a new tab.
|
||||
*/
|
||||
openLink: function WC_openLink(aLink, e)
|
||||
{
|
||||
openLink(link, e) {
|
||||
let isOSX = Services.appinfo.OS == "Darwin";
|
||||
if (e != null && (e.button === 1 || (e.button === 0 && (isOSX ? e.metaKey : e.ctrlKey)))) {
|
||||
this.chromeUtilsWindow.openUILinkIn(aLink, "tabshifted");
|
||||
if (e && (e.button === 1 || (e.button === 0 && (isOSX ? e.metaKey : e.ctrlKey)))) {
|
||||
this.chromeUtilsWindow.openUILinkIn(link, "tabshifted");
|
||||
} else {
|
||||
this.chromeUtilsWindow.openUILinkIn(aLink, "tab");
|
||||
this.chromeUtilsWindow.openUILinkIn(link, "tab");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a link in Firefox's view source.
|
||||
*
|
||||
* @param string aSourceURL
|
||||
* @param string sourceURL
|
||||
* The URL of the file.
|
||||
* @param integer aSourceLine
|
||||
* @param integer sourceLine
|
||||
* The line number which should be highlighted.
|
||||
*/
|
||||
viewSource: function WC_viewSource(aSourceURL, aSourceLine) {
|
||||
viewSource(sourceURL, sourceLine) {
|
||||
// Attempt to access view source via a browser first, which may display it in
|
||||
// a tab, if enabled.
|
||||
let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
if (browserWin && browserWin.BrowserViewSourceOfDocument) {
|
||||
return browserWin.BrowserViewSourceOfDocument({
|
||||
URL: aSourceURL,
|
||||
lineNumber: aSourceLine
|
||||
URL: sourceURL,
|
||||
lineNumber: sourceLine
|
||||
});
|
||||
}
|
||||
this.gViewSourceUtils.viewSource(aSourceURL, null, this.iframeWindow.document, aSourceLine || 0);
|
||||
return this.gViewSourceUtils.viewSource(
|
||||
sourceURL, null, this.iframeWindow.document, sourceLine || 0);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -462,18 +417,18 @@ WebConsole.prototype = {
|
|||
*
|
||||
* Manually handle the case where toolbox does not exist (Browser Console).
|
||||
*
|
||||
* @param string aSourceURL
|
||||
* @param string sourceURL
|
||||
* The URL of the file.
|
||||
* @param integer aSourceLine
|
||||
* @param integer sourceLine
|
||||
* The line number which you want to place the caret.
|
||||
*/
|
||||
viewSourceInStyleEditor: function WC_viewSourceInStyleEditor(aSourceURL, aSourceLine) {
|
||||
viewSourceInStyleEditor(sourceURL, sourceLine) {
|
||||
let toolbox = gDevTools.getToolbox(this.target);
|
||||
if (!toolbox) {
|
||||
this.viewSource(aSourceURL, aSourceLine);
|
||||
this.viewSource(sourceURL, sourceLine);
|
||||
return;
|
||||
}
|
||||
toolbox.viewSourceInStyleEditor(aSourceURL, aSourceLine);
|
||||
toolbox.viewSourceInStyleEditor(sourceURL, sourceLine);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -483,18 +438,18 @@ WebConsole.prototype = {
|
|||
*
|
||||
* Manually handle the case where toolbox does not exist (Browser Console).
|
||||
*
|
||||
* @param string aSourceURL
|
||||
* @param string sourceURL
|
||||
* The URL of the file.
|
||||
* @param integer aSourceLine
|
||||
* @param integer sourceLine
|
||||
* The line number which you want to place the caret.
|
||||
*/
|
||||
viewSourceInDebugger: function WC_viewSourceInDebugger(aSourceURL, aSourceLine) {
|
||||
viewSourceInDebugger(sourceURL, sourceLine) {
|
||||
let toolbox = gDevTools.getToolbox(this.target);
|
||||
if (!toolbox) {
|
||||
this.viewSource(aSourceURL, aSourceLine);
|
||||
this.viewSource(sourceURL, sourceLine);
|
||||
return;
|
||||
}
|
||||
toolbox.viewSourceInDebugger(aSourceURL, aSourceLine).then(() => {
|
||||
toolbox.viewSourceInDebugger(sourceURL, sourceLine).then(() => {
|
||||
this.ui.emit("source-in-debugger-opened");
|
||||
});
|
||||
},
|
||||
|
@ -503,11 +458,11 @@ WebConsole.prototype = {
|
|||
* Tries to open a JavaScript file related to the web page for the web console
|
||||
* instance in the corresponding Scratchpad.
|
||||
*
|
||||
* @param string aSourceURL
|
||||
* @param string sourceURL
|
||||
* The URL of the file which corresponds to a Scratchpad id.
|
||||
*/
|
||||
viewSourceInScratchpad: function WC_viewSourceInScratchpad(aSourceURL, aSourceLine) {
|
||||
viewSource.viewSourceInScratchpad(aSourceURL, aSourceLine);
|
||||
viewSourceInScratchpad(sourceURL, sourceLine) {
|
||||
viewSource.viewSourceInScratchpad(sourceURL, sourceLine);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -523,8 +478,7 @@ WebConsole.prototype = {
|
|||
* If the debugger is not open or if it's not paused, then |null| is
|
||||
* returned.
|
||||
*/
|
||||
getDebuggerFrames: function WC_getDebuggerFrames()
|
||||
{
|
||||
getDebuggerFrames() {
|
||||
let toolbox = gDevTools.getToolbox(this.target);
|
||||
if (!toolbox) {
|
||||
return null;
|
||||
|
@ -549,8 +503,7 @@ WebConsole.prototype = {
|
|||
* If the inspector was never opened, or no node was ever selected,
|
||||
* then |null| is returned.
|
||||
*/
|
||||
getInspectorSelection: function WC_getInspectorSelection()
|
||||
{
|
||||
getInspectorSelection() {
|
||||
let toolbox = gDevTools.getToolbox(this.target);
|
||||
if (!toolbox) {
|
||||
return null;
|
||||
|
@ -569,48 +522,40 @@ WebConsole.prototype = {
|
|||
* @return object
|
||||
* A promise object that is resolved once the Web Console is closed.
|
||||
*/
|
||||
destroy: function WC_destroy()
|
||||
{
|
||||
async destroy() {
|
||||
if (this._destroyer) {
|
||||
return this._destroyer.promise;
|
||||
return this._destroyer;
|
||||
}
|
||||
|
||||
HUDService.consoles.delete(this.hudId);
|
||||
this._destroyer = (async () => {
|
||||
HUDService.consoles.delete(this.hudId);
|
||||
|
||||
this._destroyer = defer();
|
||||
|
||||
// The document may already be removed
|
||||
if (this.chromeUtilsWindow && this.mainPopupSet) {
|
||||
let popupset = this.mainPopupSet;
|
||||
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
|
||||
for (let panel of panels) {
|
||||
panel.hidePopup();
|
||||
// The document may already be removed
|
||||
if (this.chromeUtilsWindow && this.mainPopupSet) {
|
||||
let popupset = this.mainPopupSet;
|
||||
let panels = popupset.querySelectorAll("panel[hudId=" + this.hudId + "]");
|
||||
for (let panel of panels) {
|
||||
panel.hidePopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.ui) {
|
||||
await this.ui.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
let onDestroy = Task.async(function* () {
|
||||
if (!this._browserConsole) {
|
||||
try {
|
||||
yield this.target.activeTab.focus();
|
||||
}
|
||||
catch (ex) {
|
||||
await this.target.activeTab.focus();
|
||||
} catch (ex) {
|
||||
// Tab focus can fail if the tab or target is closed.
|
||||
}
|
||||
}
|
||||
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
let id = Utils.supportsString(this.hudId);
|
||||
Services.obs.notifyObservers(id, "web-console-destroyed");
|
||||
this._destroyer.resolve(null);
|
||||
}.bind(this));
|
||||
})();
|
||||
|
||||
if (this.ui) {
|
||||
this.ui.destroy().then(onDestroy);
|
||||
}
|
||||
else {
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
return this._destroyer.promise;
|
||||
return this._destroyer;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -624,23 +569,22 @@ WebConsole.prototype = {
|
|||
* UI and features.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aTarget
|
||||
* @param object target
|
||||
* The target that the browser console will connect to.
|
||||
* @param nsIDOMWindow aIframeWindow
|
||||
* @param nsIDOMWindow iframeWindow
|
||||
* The window where the browser console UI is already loaded.
|
||||
* @param nsIDOMWindow aChromeWindow
|
||||
* @param nsIDOMWindow chromeWindow
|
||||
* The window of the browser console owner.
|
||||
*/
|
||||
function BrowserConsole()
|
||||
{
|
||||
function BrowserConsole() {
|
||||
WebConsole.apply(this, arguments);
|
||||
this._telemetry = new Telemetry();
|
||||
}
|
||||
|
||||
BrowserConsole.prototype = extend(WebConsole.prototype, {
|
||||
_browserConsole: true,
|
||||
_bc_init: null,
|
||||
_bc_destroyer: null,
|
||||
_bcInit: null,
|
||||
_bcDestroyer: null,
|
||||
|
||||
$init: WebConsole.prototype.init,
|
||||
|
||||
|
@ -650,27 +594,24 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
|
|||
* @return object
|
||||
* A promise for the initialization.
|
||||
*/
|
||||
init: function BC_init()
|
||||
{
|
||||
if (this._bc_init) {
|
||||
return this._bc_init;
|
||||
init() {
|
||||
if (this._bcInit) {
|
||||
return this._bcInit;
|
||||
}
|
||||
|
||||
// Only add the shutdown observer if we've opened a Browser Console window.
|
||||
ShutdownObserver.init();
|
||||
|
||||
this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
|
||||
this.ui._filterPrefsPrefix = BC_FILTER_PREFS_PREFIX;
|
||||
|
||||
let window = this.iframeWindow;
|
||||
|
||||
// Make sure that the closing of the Browser Console window destroys this
|
||||
// instance.
|
||||
let onClose = () => {
|
||||
window.removeEventListener("unload", onClose);
|
||||
window.addEventListener("unload", () => {
|
||||
window.removeEventListener("focus", onFocus);
|
||||
this.destroy();
|
||||
};
|
||||
window.addEventListener("unload", onClose);
|
||||
}, {once: true});
|
||||
|
||||
this._telemetry.toolOpened("browserconsole");
|
||||
|
||||
|
@ -680,8 +621,8 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
|
|||
let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
|
||||
window.addEventListener("focus", onFocus);
|
||||
|
||||
this._bc_init = this.$init();
|
||||
return this._bc_init;
|
||||
this._bcInit = this.$init();
|
||||
return this._bcInit;
|
||||
},
|
||||
|
||||
$destroy: WebConsole.prototype.destroy,
|
||||
|
@ -692,25 +633,20 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
|
|||
* @return object
|
||||
* A promise object that is resolved once the Browser Console is closed.
|
||||
*/
|
||||
destroy: function BC_destroy()
|
||||
{
|
||||
if (this._bc_destroyer) {
|
||||
return this._bc_destroyer.promise;
|
||||
destroy() {
|
||||
if (this._bcDestroyer) {
|
||||
return this._bcDestroyer;
|
||||
}
|
||||
|
||||
this._telemetry.toolClosed("browserconsole");
|
||||
this._bcDestroyer = (async () => {
|
||||
this._telemetry.toolClosed("browserconsole");
|
||||
await this.$destroy();
|
||||
await this.target.client.close();
|
||||
HUDService._browserConsoleID = null;
|
||||
this.chromeWindow.close();
|
||||
})();
|
||||
|
||||
this._bc_destroyer = defer();
|
||||
|
||||
let chromeWindow = this.chromeWindow;
|
||||
this.$destroy().then(() =>
|
||||
this.target.client.close().then(() => {
|
||||
HUDService._browserConsoleID = null;
|
||||
chromeWindow.close();
|
||||
this._bc_destroyer.resolve(null);
|
||||
}));
|
||||
|
||||
return this._bc_destroyer.promise;
|
||||
return this._bcDestroyer;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1040,6 +1040,7 @@ AudioCallbackDriver::StateCallback(cubeb_state aState)
|
|||
LOG(LogLevel::Debug, ("AudioCallbackDriver State: %d", aState));
|
||||
|
||||
if (aState == CUBEB_STATE_ERROR && mShouldFallbackIfError) {
|
||||
mShouldFallbackIfError = false;
|
||||
MonitorAutoLock lock(GraphImpl()->GetMonitor());
|
||||
RemoveCallback();
|
||||
FallbackToSystemClockDriver();
|
||||
|
|
|
@ -35,6 +35,12 @@ static mozilla::LazyLogModule gU2FLog("u2fmanager");
|
|||
NS_NAMED_LITERAL_STRING(kFinishEnrollment, "navigator.id.finishEnrollment");
|
||||
NS_NAMED_LITERAL_STRING(kGetAssertion, "navigator.id.getAssertion");
|
||||
|
||||
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
|
||||
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
|
||||
"https://www.gstatic.com/securitykey/origins.json");
|
||||
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
|
||||
"https://www.gstatic.com/securitykey/a/google.com/origins.json");
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
|
@ -122,9 +128,15 @@ RegisteredKeysToScopedCredentialList(const nsAString& aAppId,
|
|||
}
|
||||
}
|
||||
|
||||
enum class U2FOperation
|
||||
{
|
||||
Register,
|
||||
Sign
|
||||
};
|
||||
|
||||
static ErrorCode
|
||||
EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
||||
/* in/out */ nsString& aAppId)
|
||||
const U2FOperation& aOp, /* in/out */ nsString& aAppId)
|
||||
{
|
||||
// Facet is the specification's way of referring to the web origin.
|
||||
nsAutoCString facetString = NS_ConvertUTF16toUTF8(aOrigin);
|
||||
|
@ -208,6 +220,15 @@ EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
|
|||
return ErrorCode::OK;
|
||||
}
|
||||
|
||||
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
|
||||
if (aOp == U2FOperation::Sign && lowestFacetHost.EqualsLiteral("google.com") &&
|
||||
(aAppId.Equals(kGoogleAccountsAppId1) ||
|
||||
aAppId.Equals(kGoogleAccountsAppId2))) {
|
||||
MOZ_LOG(gU2FLog, LogLevel::Debug,
|
||||
("U2F permitted for Google Accounts via Bug #1436085"));
|
||||
return ErrorCode::OK;
|
||||
}
|
||||
|
||||
return ErrorCode::BAD_REQUEST;
|
||||
}
|
||||
|
||||
|
@ -356,7 +377,8 @@ U2F::Register(const nsAString& aAppId,
|
|||
// Evaluate the AppID
|
||||
nsString adjustedAppId;
|
||||
adjustedAppId.Assign(aAppId);
|
||||
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, adjustedAppId);
|
||||
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, U2FOperation::Register,
|
||||
adjustedAppId);
|
||||
if (appIdResult != ErrorCode::OK) {
|
||||
RegisterResponse response;
|
||||
response.mErrorCode.Construct(static_cast<uint32_t>(appIdResult));
|
||||
|
@ -518,7 +540,8 @@ U2F::Sign(const nsAString& aAppId,
|
|||
// Evaluate the AppID
|
||||
nsString adjustedAppId;
|
||||
adjustedAppId.Assign(aAppId);
|
||||
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, adjustedAppId);
|
||||
ErrorCode appIdResult = EvaluateAppID(mParent, mOrigin, U2FOperation::Sign,
|
||||
adjustedAppId);
|
||||
if (appIdResult != ErrorCode::OK) {
|
||||
SignResponse response;
|
||||
response.mErrorCode.Construct(static_cast<uint32_t>(appIdResult));
|
||||
|
|
|
@ -195,7 +195,7 @@ WebAuthnManager::~WebAuthnManager()
|
|||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WebAuthnManager::MakeCredential(const MakePublicKeyCredentialOptions& aOptions,
|
||||
WebAuthnManager::MakeCredential(const PublicKeyCredentialCreationOptions& aOptions,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
|
|
@ -103,7 +103,7 @@ public:
|
|||
{ }
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MakeCredential(const MakePublicKeyCredentialOptions& aOptions,
|
||||
MakeCredential(const PublicKeyCredentialCreationOptions& aOptions,
|
||||
const Optional<OwningNonNull<AbortSignal>>& aSignal);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
|
|
|
@ -29,6 +29,6 @@ dictionary CredentialRequestOptions {
|
|||
};
|
||||
|
||||
dictionary CredentialCreationOptions {
|
||||
MakePublicKeyCredentialOptions publicKey;
|
||||
PublicKeyCredentialCreationOptions publicKey;
|
||||
AbortSignal signal;
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ dictionary PublicKeyCredentialParameters {
|
|||
required COSEAlgorithmIdentifier alg;
|
||||
};
|
||||
|
||||
dictionary MakePublicKeyCredentialOptions {
|
||||
dictionary PublicKeyCredentialCreationOptions {
|
||||
required PublicKeyCredentialRpEntity rp;
|
||||
required PublicKeyCredentialUserEntity user;
|
||||
|
||||
|
|
|
@ -20,6 +20,80 @@ class JSTracer;
|
|||
|
||||
class PseudoStack;
|
||||
|
||||
// This file defines the classes PseudoStack and ProfileEntry.
|
||||
// The PseudoStack manages an array of ProfileEntries.
|
||||
// Usage:
|
||||
//
|
||||
// PseudoStack* pseudoStack = ...;
|
||||
//
|
||||
// // For CPP stack frames:
|
||||
// pseudoStack->pushCppFrame(...);
|
||||
// // Execute some code. When finished, pop the entry:
|
||||
// pseudoStack->pop();
|
||||
//
|
||||
// // For JS stack frames:
|
||||
// pseudoStack->pushJSFrame(...);
|
||||
// // Execute some code. When finished, pop the entry:
|
||||
// pseudoStack->pop();
|
||||
//
|
||||
//
|
||||
// Concurrency considerations
|
||||
//
|
||||
// A thread's pseudo stack (and the entries inside it) is only modified by
|
||||
// that thread. However, the pseudo stack can be *read* by a different thread,
|
||||
// the sampler thread: Whenever the profiler wants to sample a given thread A,
|
||||
// the following happens:
|
||||
// (1) Thread A is suspended.
|
||||
// (2) The sampler thread (thread S) reads the PseudoStack of thread A,
|
||||
// including all ProfileEntries that are currently in that stack
|
||||
// (pseudoStack->entries[0..pseudoStack->stackSize()]).
|
||||
// (3) Thread A is resumed.
|
||||
//
|
||||
// Thread suspension is achieved using platform-specific APIs; refer to each
|
||||
// platform's Sampler::SuspendAndSampleAndResumeThread implementation in
|
||||
// platform-*.cpp for details.
|
||||
//
|
||||
// When the thread is suspended, the values in pseudoStack->stackPointer and in
|
||||
// the entry range pseudoStack->entries[0..pseudoStack->stackPointer] need to
|
||||
// be in a consistent state, so that thread A does not read partially-
|
||||
// constructed profile entries. More specifically, we have two requirements:
|
||||
// (1) When adding a new entry at the top of the stack, its ProfileEntry data
|
||||
// needs to be put in place *before* the stackPointer is incremented, and
|
||||
// the compiler + CPU need to know that this order matters.
|
||||
// (2) When popping an entry from the stack and then preparing the
|
||||
// ProfileEntry data for the next frame that is about to be pushed, the
|
||||
// decrement of the stackPointer in pop() needs to happen *before* the
|
||||
// ProfileEntry for the new frame is being popuplated, and the compiler +
|
||||
// CPU need to know that this order matters.
|
||||
//
|
||||
// We can express the relevance of these orderings in multiple ways.
|
||||
// Option A is to make stackPointer an atomic with SequentiallyConsistent
|
||||
// memory ordering. This would ensure that no writes in thread A would be
|
||||
// reordered across any writes to stackPointer, which satisfies requirements
|
||||
// (1) and (2) at the same time. Option A is the simplest.
|
||||
// Option B is to use ReleaseAcquire memory ordering both for writes to
|
||||
// stackPointer *and* for writes to ProfileEntry fields. Release-stores ensure
|
||||
// that all writes that happened *before this write in program order* are not
|
||||
// reordered to happen after this write. ReleaseAcquire ordering places no
|
||||
// requirements on the ordering of writes that happen *after* this write in
|
||||
// program order.
|
||||
// Using release-stores for writes to stackPointer expresses requirement (1),
|
||||
// and using release-stores for writes to the ProfileEntry fields expresses
|
||||
// requirement (2).
|
||||
//
|
||||
// Option B is more complicated than option A, but has much better performance
|
||||
// on x86/64: In a microbenchmark run on a Macbook Pro from 2017, switching
|
||||
// from option A to option B reduced the overhead of pushing+popping a
|
||||
// ProfileEntry by 10 nanoseconds.
|
||||
// On x86/64, release-stores require no explicit hardware barriers or lock
|
||||
// instructions.
|
||||
// On ARM/64, option B may be slower than option A, because the compiler will
|
||||
// generate hardware barriers for every single release-store instead of just
|
||||
// for the writes to stackPointer. However, the actual performance impact of
|
||||
// this has not yet been measured on ARM, so we're currently using option B
|
||||
// everywhere. This is something that we may want to change in the future once
|
||||
// we've done measurements.
|
||||
|
||||
namespace js {
|
||||
|
||||
// A call stack can be specified to the JS engine such that all JS entry/exits
|
||||
|
@ -31,24 +105,38 @@ class ProfileEntry
|
|||
{
|
||||
// A ProfileEntry represents either a C++ profile entry or a JS one.
|
||||
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// All the fields below are Atomic<...,ReleaseAcquire>. This is needed so
|
||||
// that writes to these fields are release-writes, which ensures that
|
||||
// earlier writes in this thread don't get reordered after the writes to
|
||||
// these fields. In particular, the decrement of the stack pointer in
|
||||
// PseudoStack::pop() is a write that *must* happen before the values in
|
||||
// this ProfileEntry are changed. Otherwise, the sampler thread might see
|
||||
// an inconsistent state where the stack pointer still points to a
|
||||
// ProfileEntry which has already been popped off the stack and whose
|
||||
// fields have now been partially repopulated with new values.
|
||||
// See the "Concurrency considerations" paragraph at the top of this file
|
||||
// for more details.
|
||||
|
||||
// Descriptive label for this entry. Must be a static string! Can be an
|
||||
// empty string, but not a null pointer.
|
||||
const char* label_;
|
||||
mozilla::Atomic<const char*, mozilla::ReleaseAcquire> label_;
|
||||
|
||||
// An additional descriptive string of this entry which is combined with
|
||||
// |label_| in profiler output. Need not be (and usually isn't) static. Can
|
||||
// be null.
|
||||
const char* dynamicString_;
|
||||
mozilla::Atomic<const char*, mozilla::ReleaseAcquire> dynamicString_;
|
||||
|
||||
// Stack pointer for non-JS entries, the script pointer otherwise.
|
||||
void* spOrScript;
|
||||
mozilla::Atomic<void*, mozilla::ReleaseAcquire> spOrScript;
|
||||
|
||||
// Line number for non-JS entries, the bytecode offset otherwise.
|
||||
int32_t lineOrPcOffset;
|
||||
mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> lineOrPcOffset;
|
||||
|
||||
// Bits 0..1 hold the Kind. Bits 2..3 are unused. Bits 4..12 hold the
|
||||
// Bits 0...1 hold the Kind. Bits 2...3 are unused. Bits 4...12 hold the
|
||||
// Category.
|
||||
uint32_t kindAndCategory_;
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> kindAndCategory_;
|
||||
|
||||
static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
|
||||
|
||||
|
@ -160,7 +248,8 @@ class ProfileEntry
|
|||
// Note that the pointer returned might be invalid.
|
||||
JSScript* rawScript() const {
|
||||
MOZ_ASSERT(isJs());
|
||||
return (JSScript*)spOrScript;
|
||||
void* script = spOrScript;
|
||||
return static_cast<JSScript*>(script);
|
||||
}
|
||||
|
||||
// We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
|
||||
|
@ -198,8 +287,9 @@ RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
|
|||
//
|
||||
// - When pushing a new entry, we increment the stack pointer -- making the new
|
||||
// entry visible to the sampler thread -- only after the new entry has been
|
||||
// fully written. The stack pointer is Atomic<> (with SequentiallyConsistent
|
||||
// semantics) to ensure the incrementing is not reordered before the writes.
|
||||
// fully written. The stack pointer is Atomic<uint32_t,ReleaseAcquire>, so
|
||||
// the increment is a release-store, which ensures that this store is not
|
||||
// reordered before the writes of the entry.
|
||||
//
|
||||
// - When popping an old entry, the only operation is the decrementing of the
|
||||
// stack pointer, which is obviously atomic.
|
||||
|
@ -225,8 +315,15 @@ class PseudoStack
|
|||
}
|
||||
|
||||
// This must happen at the end! The compiler will not reorder this
|
||||
// update because stackPointer is Atomic.
|
||||
stackPointer++;
|
||||
// update because stackPointer is Atomic<..., ReleaseAcquire>, so any
|
||||
// the writes above will not be reordered below the stackPointer store.
|
||||
// Do the read and the write as two separate statements, in order to
|
||||
// make it clear that we don't need an atomic increment, which would be
|
||||
// more expensive on x86 than the separate operations done here.
|
||||
// This thread is the only one that ever changes the value of
|
||||
// stackPointer.
|
||||
uint32_t oldStackPointer = stackPointer;
|
||||
stackPointer = oldStackPointer + 1;
|
||||
}
|
||||
|
||||
void pushJsFrame(const char* label, const char* dynamicString, JSScript* script,
|
||||
|
@ -236,13 +333,27 @@ class PseudoStack
|
|||
}
|
||||
|
||||
// This must happen at the end! The compiler will not reorder this
|
||||
// update because stackPointer is Atomic.
|
||||
stackPointer++;
|
||||
// update because stackPointer is Atomic<..., ReleaseAcquire>, which
|
||||
// makes this assignment a release-store, so the writes above will not
|
||||
// be reordered to occur after the stackPointer store.
|
||||
// Do the read and the write as two separate statements, in order to
|
||||
// make it clear that we don't need an atomic increment, which would be
|
||||
// more expensive on x86 than the separate operations done here.
|
||||
// This thread is the only one that ever changes the value of
|
||||
// stackPointer.
|
||||
uint32_t oldStackPointer = stackPointer;
|
||||
stackPointer = oldStackPointer + 1;
|
||||
}
|
||||
|
||||
void pop() {
|
||||
MOZ_ASSERT(stackPointer > 0);
|
||||
stackPointer--;
|
||||
// Do the read and the write as two separate statements, in order to
|
||||
// make it clear that we don't need an atomic decrement, which would be
|
||||
// more expensive on x86 than the separate operations done here.
|
||||
// This thread is the only one that ever changes the value of
|
||||
// stackPointer.
|
||||
uint32_t oldStackPointer = stackPointer;
|
||||
stackPointer = oldStackPointer - 1;
|
||||
}
|
||||
|
||||
uint32_t stackSize() const { return std::min(uint32_t(stackPointer), uint32_t(MaxEntries)); }
|
||||
|
@ -262,7 +373,13 @@ class PseudoStack
|
|||
// determine the number of valid samples in entries. When this is less
|
||||
// than MaxEntries, it refers to the first free entry past the top of the
|
||||
// in-use stack (i.e. entries[stackPointer - 1] is the top stack entry).
|
||||
mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> stackPointer;
|
||||
//
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// This is an atomic variable that uses ReleaseAcquire memory ordering.
|
||||
// See the "Concurrency considerations" paragraph at the top of this file
|
||||
// for more details.
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> stackPointer;
|
||||
};
|
||||
|
||||
#endif /* js_ProfilingStack_h */
|
||||
|
|
|
@ -427,7 +427,7 @@ JS_PUBLIC_API(JSScript*)
|
|||
ProfileEntry::script() const
|
||||
{
|
||||
MOZ_ASSERT(isJs());
|
||||
auto script = reinterpret_cast<JSScript*>(spOrScript);
|
||||
auto script = reinterpret_cast<JSScript*>(spOrScript.operator void*());
|
||||
if (!script)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ struct DisplayItemClipChain {
|
|||
DisplayItemClip mClip;
|
||||
const ActiveScrolledRoot* mASR;
|
||||
RefPtr<const DisplayItemClipChain> mParent;
|
||||
mutable uint32_t mRefCount = 0;
|
||||
uint32_t mRefCount = 0;
|
||||
#ifdef DEBUG
|
||||
bool mOnStack;
|
||||
#endif
|
||||
|
|
|
@ -52,13 +52,10 @@ public:
|
|||
}
|
||||
|
||||
class AutoSaveRestore;
|
||||
friend class AutoSaveRestore;
|
||||
|
||||
class AutoClipContainingBlockDescendantsToContentBox;
|
||||
friend class AutoClipContainingBlockDescendantsToContentBox;
|
||||
|
||||
class AutoClipMultiple;
|
||||
friend class AutoClipMultiple;
|
||||
|
||||
enum {
|
||||
ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT = 0x01
|
||||
|
|
|
@ -1024,8 +1024,6 @@ public:
|
|||
* set mCurrentFrame and related state. Also temporarily sets mDirtyRect.
|
||||
* aDirtyRect is relative to aForChild.
|
||||
*/
|
||||
class AutoBuildingDisplayList;
|
||||
friend class AutoBuildingDisplayList;
|
||||
class AutoBuildingDisplayList {
|
||||
public:
|
||||
AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
|
@ -1117,8 +1115,6 @@ public:
|
|||
/**
|
||||
* A helper class to temporarily set the value of mInTransform.
|
||||
*/
|
||||
class AutoInTransformSetter;
|
||||
friend class AutoInTransformSetter;
|
||||
class AutoInTransformSetter {
|
||||
public:
|
||||
AutoInTransformSetter(nsDisplayListBuilder* aBuilder, bool aInTransform)
|
||||
|
@ -1136,8 +1132,6 @@ public:
|
|||
/**
|
||||
* A helper class to temporarily set the value of mFilterASR.
|
||||
*/
|
||||
class AutoFilterASRSetter;
|
||||
friend class AutoFilterASRSetter;
|
||||
class AutoFilterASRSetter {
|
||||
public:
|
||||
AutoFilterASRSetter(nsDisplayListBuilder* aBuilder, bool aUsingFilter)
|
||||
|
@ -1155,8 +1149,6 @@ public:
|
|||
const ActiveScrolledRoot* mOldValue;
|
||||
};
|
||||
|
||||
class AutoSaveRestorePerspectiveIndex;
|
||||
friend class AutoSaveRestorePerspectiveIndex;
|
||||
class AutoSaveRestorePerspectiveIndex {
|
||||
public:
|
||||
AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
|
||||
|
@ -1184,8 +1176,6 @@ public:
|
|||
/**
|
||||
* A helper class to temporarily set the value of mCurrentScrollParentId.
|
||||
*/
|
||||
class AutoCurrentScrollParentIdSetter;
|
||||
friend class AutoCurrentScrollParentIdSetter;
|
||||
class AutoCurrentScrollParentIdSetter {
|
||||
public:
|
||||
AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollId)
|
||||
|
@ -1230,8 +1220,6 @@ public:
|
|||
* Used to update the current active scrolled root on the display list
|
||||
* builder, and to create new active scrolled roots.
|
||||
*/
|
||||
class AutoCurrentActiveScrolledRootSetter;
|
||||
friend class AutoCurrentActiveScrolledRootSetter;
|
||||
class AutoCurrentActiveScrolledRootSetter {
|
||||
public:
|
||||
explicit AutoCurrentActiveScrolledRootSetter(nsDisplayListBuilder* aBuilder)
|
||||
|
@ -1296,8 +1284,6 @@ public:
|
|||
* The rule is: all child items of the container item need to have
|
||||
* clipped bounds with respect to the container ASR.
|
||||
*/
|
||||
class AutoContainerASRTracker;
|
||||
friend class AutoContainerASRTracker;
|
||||
class AutoContainerASRTracker {
|
||||
public:
|
||||
explicit AutoContainerASRTracker(nsDisplayListBuilder* aBuilder)
|
||||
|
@ -1329,8 +1315,6 @@ public:
|
|||
* A helper class to temporarily set the value of mCurrentScrollbarTarget
|
||||
* and mCurrentScrollbarFlags.
|
||||
*/
|
||||
class AutoCurrentScrollbarInfoSetter;
|
||||
friend class AutoCurrentScrollbarInfoSetter;
|
||||
class AutoCurrentScrollbarInfoSetter {
|
||||
public:
|
||||
AutoCurrentScrollbarInfoSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollTargetID,
|
||||
|
@ -1361,8 +1345,6 @@ public:
|
|||
* context root. The 3D context root computes it's bounds from
|
||||
* these transformed bounds.
|
||||
*/
|
||||
class AutoAccumulateTransform;
|
||||
friend class AutoAccumulateTransform;
|
||||
class AutoAccumulateTransform {
|
||||
public:
|
||||
typedef mozilla::gfx::Matrix4x4 Matrix4x4;
|
||||
|
@ -1402,8 +1384,6 @@ public:
|
|||
* transform on the path, but it is not empty for the accumulated
|
||||
* transform.
|
||||
*/
|
||||
class AutoAccumulateRect;
|
||||
friend class AutoAccumulateRect;
|
||||
class AutoAccumulateRect {
|
||||
public:
|
||||
explicit AutoAccumulateRect(nsDisplayListBuilder* aBuilder)
|
||||
|
@ -1686,8 +1666,6 @@ public:
|
|||
* the value of mPreserves3DCtx before returning back to the parent.
|
||||
* This class do it for the users.
|
||||
*/
|
||||
class AutoPreserves3DContext;
|
||||
friend class AutoPreserves3DContext;
|
||||
class AutoPreserves3DContext {
|
||||
public:
|
||||
explicit AutoPreserves3DContext(nsDisplayListBuilder* aBuilder)
|
||||
|
|
|
@ -142,6 +142,7 @@ for (let [key, val] of Object.entries({
|
|||
|
||||
startAfter: undefined,
|
||||
suiteStarted: false,
|
||||
manageSuite: false,
|
||||
|
||||
// The enabled-state of the test-plugins, stored so they can be reset later
|
||||
testPluginEnabledStates: null,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ReadTopManifest"];
|
||||
this.EXPORTED_SYMBOLS = ["ReadTopManifest", "CreateUrls"];
|
||||
|
||||
var CC = Components.classes;
|
||||
const CI = Components.interfaces;
|
||||
|
@ -296,25 +296,15 @@ function ReadManifest(aURL, aFilter)
|
|||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
ReadManifest(incURI, aFilter);
|
||||
}
|
||||
} else if (items[0] == TYPE_LOAD) {
|
||||
} else if (items[0] == TYPE_LOAD || items[0] == TYPE_SCRIPT) {
|
||||
if (items.length != 2)
|
||||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to load";
|
||||
if (expected_status != EXPECTED_PASS &&
|
||||
expected_status != EXPECTED_DEATH)
|
||||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to " + items[0];
|
||||
if (items[0] == TYPE_LOAD && expected_status != EXPECTED_PASS && expected_status != EXPECTED_DEATH)
|
||||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect known failure type for load test";
|
||||
var [testURI] = runHttp
|
||||
? ServeFiles(principal, httpDepth,
|
||||
listURL, [items[1]])
|
||||
: [g.ioService.newURI(items[1], null, listURL)];
|
||||
var prettyPath = runHttp
|
||||
? g.ioService.newURI(items[1], null, listURL).spec
|
||||
: testURI.spec;
|
||||
secMan.checkLoadURIWithPrincipal(principal, testURI,
|
||||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
AddTestItem({ type: TYPE_LOAD,
|
||||
expected: expected_status,
|
||||
manifest: aURL.spec,
|
||||
allowSilentFail: allow_silent_fail,
|
||||
prettyPath: prettyPath,
|
||||
minAsserts: minAsserts,
|
||||
maxAsserts: maxAsserts,
|
||||
needsFocus: needs_focus,
|
||||
|
@ -325,36 +315,9 @@ function ReadManifest(aURL, aFilter)
|
|||
fuzzyMaxDelta: fuzzy_delta.max,
|
||||
fuzzyMinPixels: fuzzy_pixels.min,
|
||||
fuzzyMaxPixels: fuzzy_pixels.max,
|
||||
url1: testURI,
|
||||
url2: null,
|
||||
chaosMode: chaosMode }, aFilter);
|
||||
} else if (items[0] == TYPE_SCRIPT) {
|
||||
if (items.length != 2)
|
||||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": incorrect number of arguments to script";
|
||||
var [testURI] = runHttp
|
||||
? ServeFiles(principal, httpDepth,
|
||||
listURL, [items[1]])
|
||||
: [g.ioService.newURI(items[1], null, listURL)];
|
||||
var prettyPath = runHttp
|
||||
? g.ioService.newURI(items[1], null, listURL).spec
|
||||
: testURI.spec;
|
||||
secMan.checkLoadURIWithPrincipal(principal, testURI,
|
||||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
AddTestItem({ type: TYPE_SCRIPT,
|
||||
expected: expected_status,
|
||||
allowSilentFail: allow_silent_fail,
|
||||
prettyPath: prettyPath,
|
||||
minAsserts: minAsserts,
|
||||
maxAsserts: maxAsserts,
|
||||
needsFocus: needs_focus,
|
||||
slow: slow,
|
||||
prefSettings1: testPrefSettings,
|
||||
prefSettings2: refPrefSettings,
|
||||
fuzzyMinDelta: fuzzy_delta.min,
|
||||
fuzzyMaxDelta: fuzzy_delta.max,
|
||||
fuzzyMinPixels: fuzzy_pixels.min,
|
||||
fuzzyMaxPixels: fuzzy_pixels.max,
|
||||
url1: testURI,
|
||||
runHttp: runHttp,
|
||||
httpDepth: httpDepth,
|
||||
url1: items[1],
|
||||
url2: null,
|
||||
chaosMode: chaosMode }, aFilter);
|
||||
} else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL || items[0] == TYPE_PRINT) {
|
||||
|
@ -367,22 +330,9 @@ function ReadManifest(aURL, aFilter)
|
|||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": minimum fuzz must be zero for tests of type " + items[0];
|
||||
}
|
||||
|
||||
var [testURI, refURI] = runHttp
|
||||
? ServeFiles(principal, httpDepth,
|
||||
listURL, [items[1], items[2]])
|
||||
: [g.ioService.newURI(items[1], null, listURL),
|
||||
g.ioService.newURI(items[2], null, listURL)];
|
||||
var prettyPath = runHttp
|
||||
? g.ioService.newURI(items[1], null, listURL).spec
|
||||
: testURI.spec;
|
||||
secMan.checkLoadURIWithPrincipal(principal, testURI,
|
||||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
secMan.checkLoadURIWithPrincipal(principal, refURI,
|
||||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
var type = items[0];
|
||||
if (g.compareStyloToGecko || g.compareRetainedDisplayLists) {
|
||||
type = TYPE_REFTEST_EQUAL;
|
||||
refURI = testURI;
|
||||
|
||||
// We expect twice as many assertion failures when running in
|
||||
// styloVsGecko mode because we run each test twice: once in
|
||||
|
@ -402,8 +352,8 @@ function ReadManifest(aURL, aFilter)
|
|||
|
||||
AddTestItem({ type: type,
|
||||
expected: expected_status,
|
||||
manifest: aURL.spec,
|
||||
allowSilentFail: allow_silent_fail,
|
||||
prettyPath: prettyPath,
|
||||
minAsserts: minAsserts,
|
||||
maxAsserts: maxAsserts,
|
||||
needsFocus: needs_focus,
|
||||
|
@ -414,8 +364,10 @@ function ReadManifest(aURL, aFilter)
|
|||
fuzzyMaxDelta: fuzzy_delta.max,
|
||||
fuzzyMinPixels: fuzzy_pixels.min,
|
||||
fuzzyMaxPixels: fuzzy_pixels.max,
|
||||
url1: testURI,
|
||||
url2: refURI,
|
||||
runHttp: runHttp,
|
||||
httpDepth: httpDepth,
|
||||
url1: items[1],
|
||||
url2: items[2],
|
||||
chaosMode: chaosMode }, aFilter);
|
||||
} else {
|
||||
throw "Error in manifest file " + aURL.spec + " line " + lineNo + ": unknown test type " + items[0];
|
||||
|
@ -560,12 +512,7 @@ sandbox.compareRetainedDisplayLists = g.compareRetainedDisplayLists;
|
|||
sandbox.styloVsGecko = false;
|
||||
#endif
|
||||
|
||||
// Printing via Skia PDF is only supported on Mac for now.
|
||||
#ifdef XP_MACOSX && MOZ_ENABLE_SKIA_PDF
|
||||
sandbox.skiaPdf = true;
|
||||
#else
|
||||
sandbox.skiaPdf = false;
|
||||
#endif
|
||||
|
||||
#ifdef RELEASE_OR_BETA
|
||||
sandbox.release_or_beta = true;
|
||||
|
@ -692,7 +639,7 @@ function ExtractRange(matches, startIndex, defaultMin = 0) {
|
|||
};
|
||||
}
|
||||
|
||||
function ServeFiles(manifestPrincipal, depth, aURL, files) {
|
||||
function ServeTestBase(aURL, depth) {
|
||||
var listURL = aURL.QueryInterface(CI.nsIFileURL);
|
||||
var directory = listURL.file.parent;
|
||||
|
||||
|
@ -717,33 +664,51 @@ function ServeFiles(manifestPrincipal, depth, aURL, files) {
|
|||
|
||||
// Give the testbase URI access to XUL and XBL
|
||||
Services.perms.add(testbase, "allowXULXBL", Services.perms.ALLOW_ACTION);
|
||||
return testbase;
|
||||
}
|
||||
|
||||
function CreateUrls(test) {
|
||||
let secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
|
||||
.getService(CI.nsIScriptSecurityManager);
|
||||
|
||||
let manifestURL = g.ioService.newURI(test.manifest);
|
||||
let principal = secMan.createCodebasePrincipal(manifestURL, {});
|
||||
|
||||
let testbase = manifestURL;
|
||||
if (test.runHttp)
|
||||
testbase = ServeTestBase(manifestURL, test.httpDepth)
|
||||
|
||||
function FileToURI(file)
|
||||
{
|
||||
// Only serve relative URIs via the HTTP server, not absolute
|
||||
// ones like about:blank.
|
||||
if (file === null)
|
||||
return file;
|
||||
|
||||
var testURI = g.ioService.newURI(file, null, testbase);
|
||||
|
||||
// XXX necessary? manifestURL guaranteed to be file, others always HTTP
|
||||
secMan.checkLoadURIWithPrincipal(manifestPrincipal, testURI,
|
||||
secMan.checkLoadURIWithPrincipal(principal, testURI,
|
||||
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
|
||||
|
||||
return testURI;
|
||||
}
|
||||
|
||||
return files.map(FileToURI);
|
||||
let files = [test.url1, test.url2];
|
||||
[test.url1, test.url2] = files.map(FileToURI);
|
||||
if (test.url2 && g.compareStyloToGecko)
|
||||
test.url2 = test.url1;
|
||||
|
||||
return test;
|
||||
}
|
||||
|
||||
function AddTestItem(aTest, aFilter) {
|
||||
if (!aFilter)
|
||||
aFilter = [null, [], false];
|
||||
|
||||
var {url1, url2} = CreateUrls(Object.assign({}, aTest));
|
||||
|
||||
var globalFilter = aFilter[0];
|
||||
var manifestFilter = aFilter[1];
|
||||
var invertManifest = aFilter[2];
|
||||
if ((globalFilter && !globalFilter.test(aTest.url1.spec)) ||
|
||||
if ((globalFilter && !globalFilter.test(url1.spec)) ||
|
||||
(manifestFilter &&
|
||||
!(invertManifest ^ manifestFilter.test(aTest.url1.spec))))
|
||||
!(invertManifest ^ manifestFilter.test(url1.spec))))
|
||||
return;
|
||||
if (g.focusFilterMode == FOCUS_FILTER_NEEDS_FOCUS_TESTS &&
|
||||
!aTest.needsFocus)
|
||||
|
@ -752,10 +717,9 @@ function AddTestItem(aTest, aFilter) {
|
|||
aTest.needsFocus)
|
||||
return;
|
||||
|
||||
if (aTest.url2 !== null)
|
||||
aTest.identifier = [aTest.prettyPath, aTest.type, aTest.url2.spec];
|
||||
if (url2 !== null)
|
||||
aTest.identifier = [url1.spec, aTest.type, url2.spec];
|
||||
else
|
||||
aTest.identifier = aTest.prettyPath;
|
||||
|
||||
aTest.identifier = url1.spec;
|
||||
g.urls.push(aTest);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import json
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
|
||||
from mozlog.formatters import TbplFormatter
|
||||
from mozrunner.utils import get_stack_fixer_function
|
||||
|
@ -129,6 +130,7 @@ class OutputHandler(object):
|
|||
self.stack_fixer_function = get_stack_fixer_function(utilityPath, symbolsPath)
|
||||
self.log = log
|
||||
self.proc_name = None
|
||||
self.results = defaultdict(int)
|
||||
|
||||
def __call__(self, line):
|
||||
# need to return processed messages to appease remoteautomation.py
|
||||
|
@ -143,7 +145,11 @@ class OutputHandler(object):
|
|||
return [line]
|
||||
|
||||
if isinstance(data, dict) and 'action' in data:
|
||||
self.log.log_raw(data)
|
||||
if data['action'] == 'results':
|
||||
for k, v in data['results'].items():
|
||||
self.results[k] += v
|
||||
else:
|
||||
self.log.log_raw(data)
|
||||
else:
|
||||
self.verbatim(json.dumps(data))
|
||||
|
||||
|
|
|
@ -321,10 +321,10 @@ function InitAndStartRefTests()
|
|||
|
||||
// Focus the content browser.
|
||||
if (g.focusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
|
||||
g.browser.addEventListener("focus", StartTests, true);
|
||||
g.browser.addEventListener("focus", ReadTests, true);
|
||||
g.browser.focus();
|
||||
} else {
|
||||
StartTests();
|
||||
ReadTests();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,13 +346,94 @@ function Shuffle(array)
|
|||
}
|
||||
}
|
||||
|
||||
function ReadTests() {
|
||||
try {
|
||||
if (g.focusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
|
||||
g.browser.removeEventListener("focus", ReadTests, true);
|
||||
}
|
||||
|
||||
g.urls = [];
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
/* There are three modes implemented here:
|
||||
* 1) reftest.manifests
|
||||
* 2) reftest.manifests and reftest.manifests.dumpTests
|
||||
* 3) reftest.tests
|
||||
*
|
||||
* The first will parse the specified manifests, then immediately
|
||||
* run the tests. The second will parse the manifests, save the test
|
||||
* objects to a file and exit. The third will load a file of test
|
||||
* objects and run them.
|
||||
*
|
||||
* The latter two modes are used to pass test data back and forth
|
||||
* with python harness.
|
||||
*/
|
||||
let manifests = prefs.getCharPref("reftest.manifests", null);
|
||||
let dumpTests = prefs.getCharPref("reftest.manifests.dumpTests", null);
|
||||
let testList = prefs.getCharPref("reftest.tests", null);
|
||||
|
||||
if ((testList && manifests) || !(testList || manifests)) {
|
||||
logger.error("Exactly one of reftest.manifests or reftest.tests must be specified.");
|
||||
DoneTests();
|
||||
}
|
||||
|
||||
if (testList) {
|
||||
logger.debug("Reading test objects from: " + testList);
|
||||
let promise = OS.File.read(testList).then(function onSuccess(array) {
|
||||
let decoder = new TextDecoder();
|
||||
g.urls = JSON.parse(decoder.decode(array)).map(CreateUrls);
|
||||
StartTests();
|
||||
});
|
||||
} else if (manifests) {
|
||||
// Parse reftest manifests
|
||||
// XXX There is a race condition in the manifest parsing code which
|
||||
// sometimes shows up on Android jsreftests (bug 1416125). It seems
|
||||
// adding/removing log statements can change its frequency.
|
||||
logger.debug("Reading " + manifests.length + " manifests");
|
||||
manifests = JSON.parse(manifests);
|
||||
g.urlsFilterRegex = manifests[null];
|
||||
|
||||
var globalFilter = manifests.hasOwnProperty("") ? new RegExp(manifests[""]) : null;
|
||||
var manifestURLs = Object.keys(manifests);
|
||||
|
||||
// Ensure we read manifests from higher up the directory tree first so that we
|
||||
// process includes before reading the included manifest again
|
||||
manifestURLs.sort(function(a,b) {return a.length - b.length})
|
||||
manifestURLs.forEach(function(manifestURL) {
|
||||
logger.info("Reading manifest " + manifestURL);
|
||||
var filter = manifests[manifestURL] ? new RegExp(manifests[manifestURL]) : null;
|
||||
ReadTopManifest(manifestURL, [globalFilter, filter, false]);
|
||||
});
|
||||
|
||||
if (dumpTests) {
|
||||
logger.debug("Dumping test objects to file: " + dumpTests);
|
||||
let encoder = new TextEncoder();
|
||||
let tests = encoder.encode(JSON.stringify(g.urls));
|
||||
OS.File.writeAtomic(dumpTests, tests, {flush: true}).then(
|
||||
function onSuccess() {
|
||||
DoneTests();
|
||||
},
|
||||
function onFailure(reason) {
|
||||
logger.error("failed to write test data: " + reason);
|
||||
DoneTests();
|
||||
}
|
||||
)
|
||||
} else {
|
||||
logger.debug("Running " + g.urls.length + " test objects");
|
||||
g.manageSuite = true;
|
||||
g.urls = g.urls.map(CreateUrls);
|
||||
StartTests();
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
++g.testResults.Exception;
|
||||
logger.error("EXCEPTION: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
function StartTests()
|
||||
{
|
||||
if (g.focusFilterMode != FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS) {
|
||||
g.browser.removeEventListener("focus", StartTests, true);
|
||||
}
|
||||
|
||||
var manifests;
|
||||
/* These prefs are optional, so we don't need to spit an error to the log */
|
||||
try {
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
|
@ -388,28 +469,7 @@ function StartTests()
|
|||
g.noCanvasCache = true;
|
||||
}
|
||||
|
||||
g.urls = [];
|
||||
|
||||
try {
|
||||
var manifests = JSON.parse(prefs.getCharPref("reftest.manifests"));
|
||||
g.urlsFilterRegex = manifests[null];
|
||||
} catch(e) {
|
||||
logger.error("Unable to find reftest.manifests pref. Please ensure your profile is setup properly");
|
||||
DoneTests();
|
||||
}
|
||||
|
||||
try {
|
||||
var globalFilter = manifests.hasOwnProperty("") ? new RegExp(manifests[""]) : null;
|
||||
var manifestURLs = Object.keys(manifests);
|
||||
|
||||
// Ensure we read manifests from higher up the directory tree first so that we
|
||||
// process includes before reading the included manifest again
|
||||
manifestURLs.sort(function(a,b) {return a.length - b.length})
|
||||
manifestURLs.forEach(function(manifestURL) {
|
||||
logger.info("Reading manifest " + manifestURL);
|
||||
var filter = manifests[manifestURL] ? new RegExp(manifests[manifestURL]) : null;
|
||||
ReadTopManifest(manifestURL, [globalFilter, filter, false]);
|
||||
});
|
||||
BuildUseCounts();
|
||||
|
||||
// Filter tests which will be skipped to get a more even distribution when chunking
|
||||
|
@ -449,7 +509,7 @@ function StartTests()
|
|||
g.urls = g.urls.slice(start, end);
|
||||
}
|
||||
|
||||
if (g.startAfter === undefined && !g.suiteStarted) {
|
||||
if (g.manageSuite && g.startAfter === undefined && !g.suiteStarted) {
|
||||
var ids = g.urls.map(function(obj) {
|
||||
return obj.identifier;
|
||||
});
|
||||
|
@ -725,8 +785,12 @@ function StartCurrentURI(aURLTargetType)
|
|||
|
||||
function DoneTests()
|
||||
{
|
||||
logger.suiteEnd({'results': g.testResults});
|
||||
g.suiteStarted = false
|
||||
if (g.manageSuite) {
|
||||
g.suiteStarted = false
|
||||
logger.suiteEnd({'results': g.testResults});
|
||||
} else {
|
||||
logger._logData('results', {results: g.testResults});
|
||||
}
|
||||
logger.info("Slowest test took " + g.slowestTestTime + "ms (" + g.slowestTestURL + ")");
|
||||
logger.info("Total canvas count = " + g.recycledCanvases.length);
|
||||
if (g.failedUseWidgetLayers) {
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
# 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/.
|
||||
|
||||
from contextlib import closing
|
||||
import sys
|
||||
import logging
|
||||
import os
|
||||
import psutil
|
||||
import signal
|
||||
import time
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import traceback
|
||||
import urllib2
|
||||
from contextlib import closing
|
||||
|
||||
import mozdevice
|
||||
import mozinfo
|
||||
|
@ -143,6 +143,7 @@ class ReftestServer:
|
|||
|
||||
class RemoteReftest(RefTest):
|
||||
use_marionette = False
|
||||
parse_manifest = False
|
||||
remoteApp = ''
|
||||
resolver_cls = RemoteReftestResolver
|
||||
|
||||
|
@ -167,11 +168,11 @@ class RemoteReftest(RefTest):
|
|||
self._devicemanager.removeDir(self.remoteCache)
|
||||
|
||||
self._populate_logger(options)
|
||||
outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath)
|
||||
self.outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath)
|
||||
# RemoteAutomation.py's 'messageLogger' is also used by mochitest. Mimic a mochitest
|
||||
# MessageLogger object to re-use this code path.
|
||||
outputHandler.write = outputHandler.__call__
|
||||
self.automation._processArgs['messageLogger'] = outputHandler
|
||||
self.outputHandler.write = self.outputHandler.__call__
|
||||
self.automation._processArgs['messageLogger'] = self.outputHandler
|
||||
|
||||
def findPath(self, paths, filename=None):
|
||||
for path in paths:
|
||||
|
@ -259,12 +260,12 @@ class RemoteReftest(RefTest):
|
|||
# may not be able to access process info for all processes
|
||||
continue
|
||||
|
||||
def createReftestProfile(self, options, manifest, startAfter=None):
|
||||
def createReftestProfile(self, options, startAfter=None, **kwargs):
|
||||
profile = RefTest.createReftestProfile(self,
|
||||
options,
|
||||
manifest,
|
||||
server=options.remoteWebServer,
|
||||
port=options.httpPort)
|
||||
port=options.httpPort,
|
||||
**kwargs)
|
||||
if startAfter is not None:
|
||||
print ("WARNING: Continuing after a crash is not supported for remote "
|
||||
"reftest yet.")
|
||||
|
@ -283,6 +284,11 @@ class RemoteReftest(RefTest):
|
|||
# reftest pages at 1.0 zoom, rather than zooming to fit the CSS viewport.
|
||||
prefs["apz.allow_zooming"] = False
|
||||
|
||||
if options.totalChunks:
|
||||
prefs['reftest.totalChunks'] = options.totalChunks
|
||||
if options.thisChunk:
|
||||
prefs['reftest.thisChunk'] = options.thisChunk
|
||||
|
||||
# Set the extra prefs.
|
||||
profile.set_preferences(prefs)
|
||||
|
||||
|
@ -333,10 +339,21 @@ class RemoteReftest(RefTest):
|
|||
del browserEnv["XPCOM_MEM_BLOAT_LOG"]
|
||||
return browserEnv
|
||||
|
||||
def runApp(self, profile, binary, cmdargs, env,
|
||||
timeout=None, debuggerInfo=None,
|
||||
symbolsPath=None, options=None,
|
||||
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
|
||||
def runApp(self, options, cmdargs=None, timeout=None, debuggerInfo=None, symbolsPath=None,
|
||||
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, **profileArgs):
|
||||
if cmdargs is None:
|
||||
cmdargs = []
|
||||
|
||||
if self.use_marionette:
|
||||
cmdargs.append('-marionette')
|
||||
|
||||
binary = options.app
|
||||
profile = self.createReftestProfile(options, **profileArgs)
|
||||
|
||||
# browser environment
|
||||
env = self.buildBrowserEnv(options, profile.profile)
|
||||
|
||||
self.log.info("Running with e10s: {}".format(options.e10s))
|
||||
status, lastTestSeen = self.automation.runApp(None, env,
|
||||
binary,
|
||||
profile.profile,
|
||||
|
@ -349,7 +366,9 @@ class RemoteReftest(RefTest):
|
|||
if status == 1:
|
||||
# when max run time exceeded, avoid restart
|
||||
lastTestSeen = RefTest.TEST_SEEN_FINAL
|
||||
return status, lastTestSeen
|
||||
|
||||
self.cleanup(profile.profile)
|
||||
return status, lastTestSeen, self.outputHandler.results
|
||||
|
||||
def cleanup(self, profileDir):
|
||||
# Pull results back from device
|
||||
|
|
|
@ -18,6 +18,7 @@ import shutil
|
|||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
@ -28,12 +29,14 @@ if SCRIPT_DIRECTORY not in sys.path:
|
|||
|
||||
import mozcrash
|
||||
import mozdebug
|
||||
import mozfile
|
||||
import mozinfo
|
||||
import mozleak
|
||||
import mozlog
|
||||
import mozprocess
|
||||
import mozprofile
|
||||
import mozrunner
|
||||
from manifestparser import TestManifest, filters as mpf
|
||||
from mozrunner.utils import get_stack_fixer_function, test_environment
|
||||
from mozscreenshot import printstatus, dump_screen
|
||||
|
||||
|
@ -226,9 +229,10 @@ class ReftestResolver(object):
|
|||
class RefTest(object):
|
||||
TEST_SEEN_INITIAL = 'reftest'
|
||||
TEST_SEEN_FINAL = 'Main app process exited normally'
|
||||
use_marionette = True
|
||||
oldcwd = os.getcwd()
|
||||
parse_manifest = True
|
||||
resolver_cls = ReftestResolver
|
||||
use_marionette = True
|
||||
|
||||
def __init__(self):
|
||||
update_mozinfo()
|
||||
|
@ -236,6 +240,7 @@ class RefTest(object):
|
|||
self.haveDumpedScreen = False
|
||||
self.resolver = self.resolver_cls()
|
||||
self.log = None
|
||||
self.testDumpFile = os.path.join(tempfile.gettempdir(), 'reftests.json')
|
||||
|
||||
def _populate_logger(self, options):
|
||||
if self.log:
|
||||
|
@ -259,17 +264,21 @@ class RefTest(object):
|
|||
"Get an absolute path relative to self.oldcwd."
|
||||
return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path)))
|
||||
|
||||
def createReftestProfile(self, options, manifests, server='localhost', port=0,
|
||||
profile_to_clone=None, startAfter=None):
|
||||
def createReftestProfile(self, options, tests=None, manifests=None,
|
||||
server='localhost', port=0, profile_to_clone=None,
|
||||
startAfter=None, prefs=None):
|
||||
"""Sets up a profile for reftest.
|
||||
|
||||
:param options: Object containing command line options
|
||||
:param manifests: Dictionary of the form {manifest_path: [filters]}
|
||||
:param tests: List of test objects to run
|
||||
:param manifests: List of manifest files to parse (only takes effect
|
||||
if tests were not passed in)
|
||||
:param server: Server name to use for http tests
|
||||
:param profile_to_clone: Path to a profile to use as the basis for the
|
||||
test profile
|
||||
:param startAfter: Start running tests after the specified test id
|
||||
:param prefs: Extra preferences to set in the profile
|
||||
"""
|
||||
|
||||
locations = mozprofile.permissions.ServerLocations()
|
||||
locations.add_host(server, scheme='http', port=port)
|
||||
locations.add_host(server, scheme='https', port=port)
|
||||
|
@ -277,12 +286,8 @@ class RefTest(object):
|
|||
# Set preferences for communication between our command line arguments
|
||||
# and the reftest harness. Preferences that are required for reftest
|
||||
# to work should instead be set in reftest-preferences.js .
|
||||
prefs = {}
|
||||
prefs = prefs or {}
|
||||
prefs['reftest.timeout'] = options.timeout * 1000
|
||||
if options.totalChunks:
|
||||
prefs['reftest.totalChunks'] = options.totalChunks
|
||||
if options.thisChunk:
|
||||
prefs['reftest.thisChunk'] = options.thisChunk
|
||||
if options.logFile:
|
||||
prefs['reftest.logFile'] = options.logFile
|
||||
if options.ignoreWindowSize:
|
||||
|
@ -299,7 +304,6 @@ class RefTest(object):
|
|||
prefs['reftest.cleanupPendingCrashes'] = True
|
||||
prefs['reftest.focusFilterMode'] = options.focusFilterMode
|
||||
prefs['reftest.logLevel'] = options.log_tbpl_level or 'info'
|
||||
prefs['reftest.manifests'] = json.dumps(manifests)
|
||||
prefs['reftest.suite'] = options.suite
|
||||
|
||||
if startAfter not in (None, self.TEST_SEEN_INITIAL, self.TEST_SEEN_FINAL):
|
||||
|
@ -380,6 +384,14 @@ class RefTest(object):
|
|||
else:
|
||||
profile = mozprofile.Profile(**kwargs)
|
||||
|
||||
if tests:
|
||||
testlist = os.path.join(profile.profile, 'reftests.json')
|
||||
with open(testlist, 'w') as fh:
|
||||
json.dump(tests, fh)
|
||||
profile.set_preferences({'reftest.tests': testlist})
|
||||
elif manifests:
|
||||
profile.set_preferences({'reftest.manifests': json.dumps(manifests)})
|
||||
|
||||
if os.path.join(here, 'chrome') not in options.extraProfileFiles:
|
||||
options.extraProfileFiles.append(os.path.join(here, 'chrome'))
|
||||
|
||||
|
@ -659,10 +671,23 @@ class RefTest(object):
|
|||
self.log.info("Can't trigger Breakpad, just killing process")
|
||||
process.kill()
|
||||
|
||||
def runApp(self, profile, binary, cmdargs, env,
|
||||
timeout=None, debuggerInfo=None,
|
||||
symbolsPath=None, options=None,
|
||||
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
|
||||
def runApp(self, options, cmdargs=None, timeout=None, debuggerInfo=None,
|
||||
symbolsPath=None, valgrindPath=None, valgrindArgs=None,
|
||||
valgrindSuppFiles=None, **profileArgs):
|
||||
|
||||
if cmdargs is None:
|
||||
cmdargs = []
|
||||
|
||||
if self.use_marionette:
|
||||
cmdargs.append('-marionette')
|
||||
|
||||
binary = options.app
|
||||
profile = self.createReftestProfile(options, **profileArgs)
|
||||
|
||||
# browser environment
|
||||
env = self.buildBrowserEnv(options, profile.profile)
|
||||
|
||||
self.log.info("Running with e10s: {}".format(options.e10s))
|
||||
|
||||
def timeoutHandler():
|
||||
self.handleTimeout(
|
||||
|
@ -769,12 +794,44 @@ class RefTest(object):
|
|||
status = 1
|
||||
|
||||
runner.cleanup()
|
||||
self.cleanup(profile.profile)
|
||||
|
||||
if marionette_exception is not None:
|
||||
exc, value, tb = marionette_exception
|
||||
raise exc, value, tb
|
||||
|
||||
return status, self.lastTestSeen
|
||||
self.log.info("Process mode: {}".format('e10s' if options.e10s else 'non-e10s'))
|
||||
return status, self.lastTestSeen, outputHandler.results
|
||||
|
||||
def getActiveTests(self, manifests, options, testDumpFile=None):
|
||||
# These prefs will cause reftest.jsm to parse the manifests,
|
||||
# dump the resulting tests to a file, and exit.
|
||||
prefs = {
|
||||
'reftest.manifests': json.dumps(manifests),
|
||||
'reftest.manifests.dumpTests': testDumpFile or self.testDumpFile,
|
||||
}
|
||||
cmdargs = [] # ['-headless']
|
||||
status, _, _ = self.runApp(options, cmdargs=cmdargs, prefs=prefs)
|
||||
|
||||
with open(self.testDumpFile, 'r') as fh:
|
||||
tests = json.load(fh)
|
||||
|
||||
if os.path.isfile(self.testDumpFile):
|
||||
mozfile.remove(self.testDumpFile)
|
||||
|
||||
for test in tests:
|
||||
# Name and path are expected by manifestparser, but not used in reftest.
|
||||
test['name'] = test['path'] = test['url1']
|
||||
|
||||
mp = TestManifest(strict=False)
|
||||
mp.tests = tests
|
||||
|
||||
filters = []
|
||||
if options.totalChunks:
|
||||
filters.append(mpf.chunk_by_slice(options.thisChunk, options.totalChunks))
|
||||
|
||||
tests = mp.active_tests(exists=False, filters=filters)
|
||||
return tests
|
||||
|
||||
def runSerialTests(self, manifests, options, cmdargs=None):
|
||||
debuggerInfo = None
|
||||
|
@ -782,75 +839,67 @@ class RefTest(object):
|
|||
debuggerInfo = mozdebug.get_debugger_info(options.debugger, options.debuggerArgs,
|
||||
options.debuggerInteractive)
|
||||
|
||||
profileDir = None
|
||||
tests = None
|
||||
if self.parse_manifest:
|
||||
tests = self.getActiveTests(manifests, options)
|
||||
|
||||
ids = [t['identifier'] for t in tests]
|
||||
self.log.suite_start(ids, name=options.suite)
|
||||
|
||||
startAfter = None # When the previous run crashed, we skip the tests we ran before
|
||||
prevStartAfter = None
|
||||
for i in itertools.count():
|
||||
try:
|
||||
if cmdargs is None:
|
||||
cmdargs = []
|
||||
status, startAfter, results = self.runApp(
|
||||
options,
|
||||
tests=tests,
|
||||
manifests=manifests,
|
||||
cmdargs=cmdargs,
|
||||
# We generally want the JS harness or marionette
|
||||
# to handle timeouts if they can.
|
||||
# The default JS harness timeout is currently
|
||||
# 300 seconds (default options.timeout).
|
||||
# The default Marionette socket timeout is
|
||||
# currently 360 seconds.
|
||||
# Give the JS harness extra time to deal with
|
||||
# its own timeouts and try to usually exceed
|
||||
# the 360 second marionette socket timeout.
|
||||
# See bug 479518 and bug 1414063.
|
||||
timeout=options.timeout + 70.0,
|
||||
symbolsPath=options.symbolsPath,
|
||||
debuggerInfo=debuggerInfo
|
||||
)
|
||||
mozleak.process_leak_log(self.leakLogFile,
|
||||
leak_thresholds=options.leakThresholds,
|
||||
stack_fixer=get_stack_fixer_function(options.utilityPath,
|
||||
options.symbolsPath))
|
||||
|
||||
if self.use_marionette:
|
||||
cmdargs.append('-marionette')
|
||||
if status == 0:
|
||||
break
|
||||
|
||||
profile = self.createReftestProfile(options,
|
||||
manifests,
|
||||
startAfter=startAfter)
|
||||
profileDir = profile.profile # name makes more sense
|
||||
if startAfter == self.TEST_SEEN_FINAL:
|
||||
self.log.info("Finished running all tests, skipping resume "
|
||||
"despite non-zero status code: %s" % status)
|
||||
break
|
||||
|
||||
# browser environment
|
||||
browserEnv = self.buildBrowserEnv(options, profileDir)
|
||||
if startAfter is not None and options.shuffle:
|
||||
self.log.error("Can not resume from a crash with --shuffle "
|
||||
"enabled. Please consider disabling --shuffle")
|
||||
break
|
||||
if startAfter is not None and options.maxRetries <= i:
|
||||
self.log.error("Hit maximum number of allowed retries ({}) "
|
||||
"in the test run".format(options.maxRetries))
|
||||
break
|
||||
if startAfter == prevStartAfter:
|
||||
# If the test stuck on the same test, or there the crashed
|
||||
# test appeared more then once, stop
|
||||
self.log.error("Force stop because we keep running into "
|
||||
"test \"{}\"".format(startAfter))
|
||||
break
|
||||
prevStartAfter = startAfter
|
||||
# TODO: we need to emit an SUITE-END log if it crashed
|
||||
|
||||
self.log.info("Running with e10s: {}".format(options.e10s))
|
||||
status, startAfter = self.runApp(profile,
|
||||
binary=options.app,
|
||||
cmdargs=cmdargs,
|
||||
env=browserEnv,
|
||||
# We generally want the JS harness or marionette
|
||||
# to handle timeouts if they can.
|
||||
# The default JS harness timeout is currently
|
||||
# 300 seconds (default options.timeout).
|
||||
# The default Marionette socket timeout is
|
||||
# currently 360 seconds.
|
||||
# Give the JS harness extra time to deal with
|
||||
# its own timeouts and try to usually exceed
|
||||
# the 360 second marionette socket timeout.
|
||||
# See bug 479518 and bug 1414063.
|
||||
timeout=options.timeout + 70.0,
|
||||
symbolsPath=options.symbolsPath,
|
||||
options=options,
|
||||
debuggerInfo=debuggerInfo)
|
||||
self.log.info("Process mode: {}".format('e10s' if options.e10s else 'non-e10s'))
|
||||
mozleak.process_leak_log(self.leakLogFile,
|
||||
leak_thresholds=options.leakThresholds,
|
||||
stack_fixer=get_stack_fixer_function(options.utilityPath,
|
||||
options.symbolsPath))
|
||||
if status == 0:
|
||||
break
|
||||
|
||||
if startAfter == self.TEST_SEEN_FINAL:
|
||||
self.log.info("Finished running all tests, skipping resume "
|
||||
"despite non-zero status code: %s" % status)
|
||||
break
|
||||
|
||||
if startAfter is not None and options.shuffle:
|
||||
self.log.error("Can not resume from a crash with --shuffle "
|
||||
"enabled. Please consider disabling --shuffle")
|
||||
break
|
||||
if startAfter is not None and options.maxRetries <= i:
|
||||
self.log.error("Hit maximum number of allowed retries ({}) "
|
||||
"in the test run".format(options.maxRetries))
|
||||
break
|
||||
if startAfter == prevStartAfter:
|
||||
# If the test stuck on the same test, or there the crashed
|
||||
# test appeared more then once, stop
|
||||
self.log.error("Force stop because we keep running into "
|
||||
"test \"{}\"".format(startAfter))
|
||||
break
|
||||
prevStartAfter = startAfter
|
||||
# TODO: we need to emit an SUITE-END log if it crashed
|
||||
finally:
|
||||
self.cleanup(profileDir)
|
||||
if self.parse_manifest:
|
||||
self.log.suite_end(extra={'results': results})
|
||||
return status
|
||||
|
||||
def copyExtraFilesToProfile(self, options, profile):
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
|
||||
"integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w=="
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz",
|
||||
"integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ=="
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "3.0.1",
|
||||
|
@ -24,9 +24,9 @@
|
|||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
|
||||
"integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"fast-deep-equal": "1.0.0",
|
||||
|
@ -167,6 +167,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"chardet": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
|
||||
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
|
||||
},
|
||||
"circular-json": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
|
||||
|
@ -243,7 +248,7 @@
|
|||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
||||
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
||||
"requires": {
|
||||
"es5-ext": "0.10.35"
|
||||
"es5-ext": "0.10.38"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
|
@ -274,12 +279,11 @@
|
|||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
|
||||
"integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
|
||||
"requires": {
|
||||
"esutils": "2.0.2",
|
||||
"isarray": "1.0.0"
|
||||
"esutils": "2.0.2"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
|
@ -326,9 +330,9 @@
|
|||
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.35",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.35.tgz",
|
||||
"integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=",
|
||||
"version": "0.10.38",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.38.tgz",
|
||||
"integrity": "sha512-jCMyePo7AXbUESwbl8Qi01VSH2piY9s/a3rSU/5w/MlTIx8HPL1xn2InGN8ejt/xulcJgnTO7vqNtOAxzYd2Kg==",
|
||||
"requires": {
|
||||
"es6-iterator": "2.0.3",
|
||||
"es6-symbol": "3.1.1"
|
||||
|
@ -340,7 +344,7 @@
|
|||
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35",
|
||||
"es5-ext": "0.10.38",
|
||||
"es6-symbol": "3.1.1"
|
||||
}
|
||||
},
|
||||
|
@ -350,7 +354,7 @@
|
|||
"integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35",
|
||||
"es5-ext": "0.10.38",
|
||||
"es6-iterator": "2.0.3",
|
||||
"es6-set": "0.1.5",
|
||||
"es6-symbol": "3.1.1",
|
||||
|
@ -363,7 +367,7 @@
|
|||
"integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35",
|
||||
"es5-ext": "0.10.38",
|
||||
"es6-iterator": "2.0.3",
|
||||
"es6-symbol": "3.1.1",
|
||||
"event-emitter": "0.3.5"
|
||||
|
@ -375,7 +379,7 @@
|
|||
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35"
|
||||
"es5-ext": "0.10.38"
|
||||
}
|
||||
},
|
||||
"es6-weak-map": {
|
||||
|
@ -384,7 +388,7 @@
|
|||
"integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35",
|
||||
"es5-ext": "0.10.38",
|
||||
"es6-iterator": "2.0.3",
|
||||
"es6-symbol": "3.1.1"
|
||||
}
|
||||
|
@ -406,34 +410,34 @@
|
|||
}
|
||||
},
|
||||
"eslint": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.8.0.tgz",
|
||||
"integrity": "sha1-Ip7w41Tg5h2DfHqA/fuoJeGZgV4=",
|
||||
"version": "4.17.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz",
|
||||
"integrity": "sha512-AyxBUCANU/o/xC0ijGMKavo5Ls3oK6xykiOITlMdjFjrKOsqLrA7Nf5cnrDgcKrHzBirclAZt63XO7YZlVUPwA==",
|
||||
"requires": {
|
||||
"ajv": "5.3.0",
|
||||
"ajv": "5.5.2",
|
||||
"babel-code-frame": "6.26.0",
|
||||
"chalk": "2.3.0",
|
||||
"concat-stream": "1.6.0",
|
||||
"cross-spawn": "5.1.0",
|
||||
"debug": "3.1.0",
|
||||
"doctrine": "2.0.0",
|
||||
"doctrine": "2.1.0",
|
||||
"eslint-scope": "3.7.1",
|
||||
"espree": "3.5.2",
|
||||
"eslint-visitor-keys": "1.0.0",
|
||||
"espree": "3.5.3",
|
||||
"esquery": "1.0.0",
|
||||
"estraverse": "4.2.0",
|
||||
"esutils": "2.0.2",
|
||||
"file-entry-cache": "2.0.0",
|
||||
"functional-red-black-tree": "1.0.1",
|
||||
"glob": "7.1.2",
|
||||
"globals": "9.18.0",
|
||||
"globals": "11.3.0",
|
||||
"ignore": "3.3.7",
|
||||
"imurmurhash": "0.1.4",
|
||||
"inquirer": "3.3.0",
|
||||
"is-resolvable": "1.0.0",
|
||||
"is-resolvable": "1.1.0",
|
||||
"js-yaml": "3.10.0",
|
||||
"json-stable-stringify": "1.0.1",
|
||||
"json-stable-stringify-without-jsonify": "1.0.1",
|
||||
"levn": "0.3.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"minimatch": "3.0.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"natural-compare": "1.4.0",
|
||||
|
@ -442,7 +446,7 @@
|
|||
"pluralize": "7.0.0",
|
||||
"progress": "2.0.0",
|
||||
"require-uncached": "1.0.3",
|
||||
"semver": "5.4.1",
|
||||
"semver": "5.5.0",
|
||||
"strip-ansi": "4.0.0",
|
||||
"strip-json-comments": "2.0.1",
|
||||
"table": "4.0.2",
|
||||
|
@ -450,9 +454,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-html": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.0.tgz",
|
||||
"integrity": "sha512-xK/909qOTq5JVzuO2jo4a24nQcWhkOBz9dOIkORvB7RxC75a4b6B9wFpBXAl8WDhwJGFDj5gBDRN+/L3kK/ghw==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-4.0.2.tgz",
|
||||
"integrity": "sha512-CrQd0F8GWdNWnu4PFrYZl+LjUCXNVy2h0uhDMtnf/7VKc9HRcnkXSrlg0BSGfptZPSzmwnnwCaREAa9+fnQhYw==",
|
||||
"requires": {
|
||||
"htmlparser2": "3.9.2"
|
||||
}
|
||||
|
@ -465,9 +469,9 @@
|
|||
}
|
||||
},
|
||||
"eslint-plugin-no-unsanitized": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-2.0.1.tgz",
|
||||
"integrity": "sha1-yt7dDQrRfI3FIm23/4hATlmo1n8=",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-2.0.2.tgz",
|
||||
"integrity": "sha1-pCqDybPZOGEB1v1pFcQXWfp6N/c=",
|
||||
"requires": {
|
||||
"eslint": "3.19.0"
|
||||
},
|
||||
|
@ -528,9 +532,9 @@
|
|||
"chalk": "1.1.3",
|
||||
"concat-stream": "1.6.0",
|
||||
"debug": "2.6.9",
|
||||
"doctrine": "2.0.0",
|
||||
"doctrine": "2.1.0",
|
||||
"escope": "3.6.0",
|
||||
"espree": "3.5.2",
|
||||
"espree": "3.5.3",
|
||||
"esquery": "1.0.0",
|
||||
"estraverse": "4.2.0",
|
||||
"esutils": "2.0.2",
|
||||
|
@ -540,12 +544,12 @@
|
|||
"ignore": "3.3.7",
|
||||
"imurmurhash": "0.1.4",
|
||||
"inquirer": "0.12.0",
|
||||
"is-my-json-valid": "2.16.1",
|
||||
"is-resolvable": "1.0.0",
|
||||
"is-my-json-valid": "2.17.1",
|
||||
"is-resolvable": "1.1.0",
|
||||
"js-yaml": "3.10.0",
|
||||
"json-stable-stringify": "1.0.1",
|
||||
"levn": "0.3.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"mkdirp": "0.5.1",
|
||||
"natural-compare": "1.4.0",
|
||||
"optionator": "0.8.2",
|
||||
|
@ -570,6 +574,11 @@
|
|||
"object-assign": "4.1.1"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
|
||||
|
@ -581,7 +590,7 @@
|
|||
"cli-cursor": "1.0.2",
|
||||
"cli-width": "2.2.0",
|
||||
"figures": "1.7.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"readline2": "1.0.1",
|
||||
"run-async": "0.1.0",
|
||||
"rx-lite": "3.1.2",
|
||||
|
@ -666,7 +675,7 @@
|
|||
"ajv": "4.11.8",
|
||||
"ajv-keywords": "1.5.1",
|
||||
"chalk": "1.1.3",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"slice-ansi": "0.0.4",
|
||||
"string-width": "2.1.1"
|
||||
},
|
||||
|
@ -707,7 +716,7 @@
|
|||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.1.0.tgz",
|
||||
"integrity": "sha1-J3cKzzn1/UnNCvQIPOWBBOs5DUw=",
|
||||
"requires": {
|
||||
"doctrine": "2.0.0",
|
||||
"doctrine": "2.1.0",
|
||||
"has": "1.0.1",
|
||||
"jsx-ast-utils": "1.4.1"
|
||||
}
|
||||
|
@ -724,12 +733,17 @@
|
|||
"estraverse": "4.2.0"
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
"integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ=="
|
||||
},
|
||||
"espree": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz",
|
||||
"integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz",
|
||||
"integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==",
|
||||
"requires": {
|
||||
"acorn": "5.2.1",
|
||||
"acorn": "5.4.1",
|
||||
"acorn-jsx": "3.0.1"
|
||||
}
|
||||
},
|
||||
|
@ -771,7 +785,7 @@
|
|||
"integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
|
||||
"requires": {
|
||||
"d": "1.0.0",
|
||||
"es5-ext": "0.10.35"
|
||||
"es5-ext": "0.10.38"
|
||||
}
|
||||
},
|
||||
"exit-hook": {
|
||||
|
@ -780,12 +794,12 @@
|
|||
"integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g="
|
||||
},
|
||||
"external-editor": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz",
|
||||
"integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz",
|
||||
"integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==",
|
||||
"requires": {
|
||||
"chardet": "0.4.2",
|
||||
"iconv-lite": "0.4.19",
|
||||
"jschardet": "1.6.0",
|
||||
"tmp": "0.0.33"
|
||||
}
|
||||
},
|
||||
|
@ -874,9 +888,9 @@
|
|||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz",
|
||||
"integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw=="
|
||||
},
|
||||
"globby": {
|
||||
"version": "5.0.0",
|
||||
|
@ -973,9 +987,9 @@
|
|||
"chalk": "2.3.0",
|
||||
"cli-cursor": "2.1.0",
|
||||
"cli-width": "2.2.0",
|
||||
"external-editor": "2.0.5",
|
||||
"external-editor": "2.1.0",
|
||||
"figures": "2.0.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"mute-stream": "0.0.7",
|
||||
"run-async": "2.3.0",
|
||||
"rx-lite": "4.0.8",
|
||||
|
@ -986,9 +1000,9 @@
|
|||
}
|
||||
},
|
||||
"interpret": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz",
|
||||
"integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA="
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
|
||||
"integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
|
@ -996,9 +1010,9 @@
|
|||
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz",
|
||||
"integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==",
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz",
|
||||
"integrity": "sha512-Q2khNw+oBlWuaYvEEHtKSw/pCxD2L5Rc1C+UQme9X6JdRDh7m5D7HkozA0qa3DUkQ6VzCnEm8mVIQPyIRkI5sQ==",
|
||||
"requires": {
|
||||
"generate-function": "2.0.0",
|
||||
"generate-object-property": "1.2.0",
|
||||
|
@ -1016,13 +1030,13 @@
|
|||
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
|
||||
"integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
|
||||
"requires": {
|
||||
"is-path-inside": "1.0.0"
|
||||
"is-path-inside": "1.0.1"
|
||||
}
|
||||
},
|
||||
"is-path-inside": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
|
||||
"integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
|
||||
"integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
|
||||
"requires": {
|
||||
"path-is-inside": "1.0.2"
|
||||
}
|
||||
|
@ -1038,12 +1052,9 @@
|
|||
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
|
||||
},
|
||||
"is-resolvable": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
|
||||
"integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
|
||||
"requires": {
|
||||
"tryit": "1.0.3"
|
||||
}
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
|
||||
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
|
@ -1069,11 +1080,6 @@
|
|||
"esprima": "4.0.0"
|
||||
}
|
||||
},
|
||||
"jschardet": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.6.0.tgz",
|
||||
"integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ=="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
|
||||
|
@ -1087,6 +1093,11 @@
|
|||
"jsonify": "0.0.0"
|
||||
}
|
||||
},
|
||||
"json-stable-stringify-without-jsonify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
||||
|
@ -1112,9 +1123,9 @@
|
|||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
|
||||
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
|
||||
"version": "4.17.5",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
|
||||
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "4.1.1",
|
||||
|
@ -1126,9 +1137,9 @@
|
|||
}
|
||||
},
|
||||
"mimic-fn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz",
|
||||
"integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
|
@ -1189,7 +1200,7 @@
|
|||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
|
||||
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
|
||||
"requires": {
|
||||
"mimic-fn": "1.1.0"
|
||||
"mimic-fn": "1.2.0"
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
|
@ -1391,9 +1402,9 @@
|
|||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
|
@ -1414,7 +1425,7 @@
|
|||
"integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=",
|
||||
"requires": {
|
||||
"glob": "7.1.2",
|
||||
"interpret": "1.0.4",
|
||||
"interpret": "1.1.0",
|
||||
"rechoir": "0.6.2"
|
||||
}
|
||||
},
|
||||
|
@ -1488,10 +1499,10 @@
|
|||
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
|
||||
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
|
||||
"requires": {
|
||||
"ajv": "5.3.0",
|
||||
"ajv": "5.5.2",
|
||||
"ajv-keywords": "2.1.1",
|
||||
"chalk": "2.3.0",
|
||||
"lodash": "4.17.4",
|
||||
"lodash": "4.17.5",
|
||||
"slice-ansi": "1.0.0",
|
||||
"string-width": "2.1.1"
|
||||
}
|
||||
|
@ -1514,11 +1525,6 @@
|
|||
"os-tmpdir": "1.0.2"
|
||||
}
|
||||
},
|
||||
"tryit": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
|
||||
"integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics="
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
"repository": {},
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"eslint": "4.8.0",
|
||||
"eslint-plugin-html": "4.0.0",
|
||||
"eslint": "4.17.0",
|
||||
"eslint-plugin-html": "4.0.2",
|
||||
"eslint-plugin-mozilla": "file:tools/lint/eslint/eslint-plugin-mozilla",
|
||||
"eslint-plugin-no-unsanitized": "2.0.1",
|
||||
"eslint-plugin-no-unsanitized": "2.0.2",
|
||||
"eslint-plugin-react": "7.1.0",
|
||||
"eslint-plugin-spidermonkey-js": "file:tools/lint/eslint/eslint-plugin-spidermonkey-js"
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ from __future__ import absolute_import
|
|||
import errno
|
||||
import random
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import types
|
||||
import xml.etree.ElementTree as ET
|
||||
|
@ -171,6 +172,16 @@ class CppEclipseBackend(CommonBackend):
|
|||
["eclipse", "-application", "-nosplash",
|
||||
"org.eclipse.cdt.managedbuilder.core.headlessbuild",
|
||||
"-data", self._workspace_dir, "-importAll", self._project_dir])
|
||||
except OSError as e:
|
||||
# Remove the workspace directory so we re-generate it and
|
||||
# try to import again when the backend is invoked again.
|
||||
shutil.rmtree(self._workspace_dir)
|
||||
|
||||
if e.errno == errno.ENOENT:
|
||||
raise Exception("Failed to launch eclipse to import project. "
|
||||
"Ensure 'eclipse' is in your PATH and try again")
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
self._remove_noindex()
|
||||
|
||||
|
@ -181,7 +192,12 @@ class CppEclipseBackend(CommonBackend):
|
|||
|
||||
def _remove_noindex(self):
|
||||
noindex_path = os.path.join(self._project_dir, '.settings/org.eclipse.cdt.core.prefs')
|
||||
os.remove(noindex_path)
|
||||
# This may fail if the entire tree has been removed; that's fine.
|
||||
try:
|
||||
os.remove(noindex_path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
def _define_entry(self, name, value):
|
||||
define = ET.Element('entry')
|
||||
|
|
|
@ -339,9 +339,8 @@ class Preprocessor:
|
|||
"""
|
||||
self.marker = aMarker
|
||||
if aMarker:
|
||||
self.instruction = re.compile('{0}(?P<cmd>[a-z]+)(?:\s(?P<args>.*))?$'
|
||||
.format(aMarker),
|
||||
re.U)
|
||||
self.instruction = re.compile('{0}(?P<cmd>[a-z]+)(?:\s+(?P<args>.*?))?\s*$'
|
||||
.format(aMarker))
|
||||
self.comment = re.compile(aMarker, re.U)
|
||||
else:
|
||||
class NoMatch(object):
|
||||
|
@ -606,7 +605,7 @@ class Preprocessor:
|
|||
if self.disableLevel and not replace:
|
||||
self.disableLevel += 1
|
||||
return
|
||||
if re.match('\W', args, re.U):
|
||||
if re.search('\W', args, re.U):
|
||||
raise Preprocessor.Error(self, 'INVALID_VAR', args)
|
||||
if args not in self.context:
|
||||
self.disableLevel = 1
|
||||
|
@ -621,7 +620,7 @@ class Preprocessor:
|
|||
if self.disableLevel and not replace:
|
||||
self.disableLevel += 1
|
||||
return
|
||||
if re.match('\W', args, re.U):
|
||||
if re.search('\W', args, re.U):
|
||||
raise Preprocessor.Error(self, 'INVALID_VAR', args)
|
||||
if args in self.context:
|
||||
self.disableLevel = 1
|
||||
|
|
|
@ -642,5 +642,24 @@ class TestPreprocessor(unittest.TestCase):
|
|||
self.pp.handleCommandLine(['-Fsubstitution', '-Dfoo=foobarbaz', '@foo@.in'])
|
||||
self.assertEqual(self.pp.out.getvalue(), 'foobarbaz\n')
|
||||
|
||||
def test_invalid_ifdef(self):
|
||||
with MockedOpen({'dummy': '#ifdef FOO == BAR\nPASS\n#endif'}):
|
||||
with self.assertRaises(Preprocessor.Error) as e:
|
||||
self.pp.do_include('dummy')
|
||||
self.assertEqual(e.exception.key, 'INVALID_VAR')
|
||||
|
||||
with MockedOpen({'dummy': '#ifndef FOO == BAR\nPASS\n#endif'}):
|
||||
with self.assertRaises(Preprocessor.Error) as e:
|
||||
self.pp.do_include('dummy')
|
||||
self.assertEqual(e.exception.key, 'INVALID_VAR')
|
||||
|
||||
# Trailing whitespaces, while not nice, shouldn't be an error.
|
||||
self.do_include_pass([
|
||||
'#ifndef FOO ',
|
||||
'PASS',
|
||||
'#endif',
|
||||
])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -836,7 +836,7 @@ SyncEngine.prototype = {
|
|||
if (Array.isArray(json)) {
|
||||
// Pre-`JSONFile` storage stored an array, but `JSONFile` defaults to
|
||||
// an object, so we wrap the array for consistency.
|
||||
return { ids: json };
|
||||
json = { ids: json };
|
||||
}
|
||||
if (!json.ids) {
|
||||
json.ids = [];
|
||||
|
|
|
@ -2918,6 +2918,7 @@ dependencies = [
|
|||
"servo_url 0.0.1",
|
||||
"smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"string_cache 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"style_derive 0.0.1",
|
||||
"style_traits 0.0.1",
|
||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -21,7 +21,7 @@ gecko = ["nsstring", "num_cpus",
|
|||
use_bindgen = ["bindgen", "regex", "toml"]
|
||||
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
|
||||
"servo_url"]
|
||||
"servo_url", "string_cache"]
|
||||
gecko_debug = ["nsstring/gecko_debug"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -61,6 +61,7 @@ servo_atoms = {path = "../atoms", optional = true}
|
|||
servo_config = {path = "../config", optional = true}
|
||||
smallbitvec = "1.0.6"
|
||||
smallvec = "0.6"
|
||||
string_cache = { version = "0.7", optional = true }
|
||||
style_derive = {path = "../style_derive"}
|
||||
style_traits = {path = "../style_traits"}
|
||||
servo_url = {path = "../url", optional = true}
|
||||
|
|
|
@ -1728,10 +1728,10 @@ impl ToCss for PseudoElement {
|
|||
dest.write_char('(')?;
|
||||
let mut iter = args.iter();
|
||||
if let Some(first) = iter.next() {
|
||||
serialize_identifier(&first.to_string(), dest)?;
|
||||
serialize_atom_identifier(first, dest)?;
|
||||
for item in iter {
|
||||
dest.write_str(", ")?;
|
||||
serialize_identifier(&item.to_string(), dest)?;
|
||||
serialize_atom_identifier(item, dest)?;
|
||||
}
|
||||
}
|
||||
dest.write_char(')')?;
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
|
||||
//! need to update the checked-in files for Servo.
|
||||
|
||||
use cssparser::{ToCss, serialize_identifier};
|
||||
use cssparser::ToCss;
|
||||
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
||||
use properties::{CascadeFlags, ComputedValues, PropertyFlags};
|
||||
use properties::longhands::display::computed_value::T as Display;
|
||||
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
|
||||
use std::fmt;
|
||||
use string_cache::Atom;
|
||||
use values::serialize_atom_identifier;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
|
||||
|
||||
|
|
|
@ -277,10 +277,10 @@ impl ToCss for PseudoElement {
|
|||
dest.write_char('(')?;
|
||||
let mut iter = args.iter();
|
||||
if let Some(first) = iter.next() {
|
||||
serialize_identifier(&first.to_string(), dest)?;
|
||||
serialize_atom_identifier(&first, dest)?;
|
||||
for item in iter {
|
||||
dest.write_str(", ")?;
|
||||
serialize_identifier(&item.to_string(), dest)?;
|
||||
serialize_atom_identifier(item, dest)?;
|
||||
}
|
||||
}
|
||||
dest.write_char(')')?;
|
||||
|
|
|
@ -13,15 +13,14 @@ use gecko_bindings::bindings::Gecko_ReleaseAtom;
|
|||
use gecko_bindings::structs::{nsAtom, nsAtom_AtomKind, nsStaticAtom};
|
||||
use nsstring::{nsAString, nsStr};
|
||||
use precomputed_hash::PrecomputedHash;
|
||||
use std::{mem, slice, str};
|
||||
#[allow(unused_imports)] use std::ascii::AsciiExt;
|
||||
use std::borrow::{Cow, Borrow};
|
||||
use std::char::{self, DecodeUtf16};
|
||||
use std::fmt::{self, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::Cloned;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::slice;
|
||||
|
||||
#[macro_use]
|
||||
#[allow(improper_ctypes, non_camel_case_types, missing_docs)]
|
||||
|
@ -128,21 +127,38 @@ impl WeakAtom {
|
|||
/// Find alternatives to this function when possible, please, since it's
|
||||
/// pretty slow.
|
||||
pub fn with_str<F, Output>(&self, cb: F) -> Output
|
||||
where F: FnOnce(&str) -> Output
|
||||
where
|
||||
F: FnOnce(&str) -> Output
|
||||
{
|
||||
// FIXME(bholley): We should measure whether it makes more sense to
|
||||
// cache the UTF-8 version in the Gecko atom table somehow.
|
||||
let owned = self.to_string();
|
||||
cb(&owned)
|
||||
}
|
||||
let mut buffer: [u8; 64] = unsafe { mem::uninitialized() };
|
||||
|
||||
/// Convert this Atom into a string, decoding the UTF-16 bytes.
|
||||
///
|
||||
/// Find alternatives to this function when possible, please, since it's
|
||||
/// pretty slow.
|
||||
#[inline]
|
||||
pub fn to_string(&self) -> String {
|
||||
String::from_utf16(self.as_slice()).unwrap()
|
||||
// The total string length in utf16 is going to be less than or equal
|
||||
// the slice length (each utf16 character is going to take at least one
|
||||
// and at most 2 items in the utf16 slice).
|
||||
//
|
||||
// Each of those characters will take at most four bytes in the utf8
|
||||
// one. Thus if the slice is less than 64 / 4 (16) we can guarantee that
|
||||
// we'll decode it in place.
|
||||
let owned_string;
|
||||
let len = self.len();
|
||||
let utf8_slice = if len <= 16 {
|
||||
let mut total_len = 0;
|
||||
|
||||
for c in self.chars() {
|
||||
let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
|
||||
let utf8_len = c.encode_utf8(&mut buffer[total_len..]).len();
|
||||
total_len += utf8_len;
|
||||
}
|
||||
|
||||
let slice = unsafe { str::from_utf8_unchecked(&buffer[..total_len]) };
|
||||
debug_assert_eq!(slice, String::from_utf16_lossy(self.as_slice()));
|
||||
slice
|
||||
} else {
|
||||
owned_string = String::from_utf16_lossy(self.as_slice());
|
||||
&*owned_string
|
||||
};
|
||||
|
||||
cb(utf8_slice)
|
||||
}
|
||||
|
||||
/// Returns whether this atom is static.
|
||||
|
|
|
@ -68,6 +68,7 @@ pub extern crate servo_arc;
|
|||
#[cfg(feature = "servo")] extern crate servo_url;
|
||||
extern crate smallbitvec;
|
||||
extern crate smallvec;
|
||||
#[cfg(feature = "servo")] extern crate string_cache;
|
||||
#[macro_use]
|
||||
extern crate style_derive;
|
||||
extern crate style_traits;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
use Atom;
|
||||
use cssparser::{AtRuleParser, AtRuleType, BasicParseErrorKind, DeclarationListParser, DeclarationParser, Parser};
|
||||
use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier};
|
||||
use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token};
|
||||
use error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||
#[cfg(feature = "gecko")]
|
||||
use gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
|
||||
|
@ -21,6 +21,7 @@ use str::CssStringWriter;
|
|||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
use stylesheets::CssRuleType;
|
||||
use values::computed::font::FamilyName;
|
||||
use values::serialize_atom_identifier;
|
||||
|
||||
/// A @font-feature-values block declaration.
|
||||
/// It is `<ident>: <integer>+`.
|
||||
|
@ -41,7 +42,7 @@ impl<T: ToCss> ToCss for FFVDeclaration<T> {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
serialize_identifier(&self.name.to_string(), dest)?;
|
||||
serialize_atom_identifier(&self.name, dest)?;
|
||||
dest.write_str(": ")?;
|
||||
self.value.to_css(dest)?;
|
||||
dest.write_str(";")
|
||||
|
|
|
@ -461,6 +461,7 @@ impl SingleFontFamily {
|
|||
/// Get the corresponding font-family with family name
|
||||
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
|
||||
use gecko_bindings::structs::FontFamilyType;
|
||||
use values::serialize_atom_identifier;
|
||||
|
||||
match family.mType {
|
||||
FontFamilyType::eFamily_sans_serif => SingleFontFamily::Generic(atom!("sans-serif")),
|
||||
|
@ -472,7 +473,7 @@ impl SingleFontFamily {
|
|||
FontFamilyType::eFamily_named => {
|
||||
let name = Atom::from(&*family.mName);
|
||||
let mut serialization = String::new();
|
||||
serialize_identifier(&name.to_string(), &mut serialization).unwrap();
|
||||
serialize_atom_identifier(&name, &mut serialization).unwrap();
|
||||
SingleFontFamily::FamilyName(FamilyName {
|
||||
name: name.clone(),
|
||||
syntax: FamilyNameSyntax::Identifiers(serialization),
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
//! Computed values.
|
||||
|
||||
use {Atom, Namespace};
|
||||
use Atom;
|
||||
#[cfg(feature = "servo")]
|
||||
use Prefix;
|
||||
use context::QuirksMode;
|
||||
use euclid::Size2D;
|
||||
use font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
|
||||
|
@ -414,7 +416,8 @@ trivial_to_computed_value!(u32);
|
|||
trivial_to_computed_value!(Atom);
|
||||
trivial_to_computed_value!(BorderStyle);
|
||||
trivial_to_computed_value!(CursorKind);
|
||||
trivial_to_computed_value!(Namespace);
|
||||
#[cfg(feature = "servo")]
|
||||
trivial_to_computed_value!(Prefix);
|
||||
trivial_to_computed_value!(String);
|
||||
trivial_to_computed_value!(Box<str>);
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
//! [images]: https://drafts.csswg.org/css-images/#image-values
|
||||
|
||||
use Atom;
|
||||
use cssparser::serialize_identifier;
|
||||
use custom_properties;
|
||||
use servo_arc::Arc;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
use values::serialize_atom_identifier;
|
||||
|
||||
/// An [image].
|
||||
///
|
||||
|
@ -151,7 +151,7 @@ impl ToCss for PaintWorklet {
|
|||
W: Write,
|
||||
{
|
||||
dest.write_str("paint(")?;
|
||||
serialize_identifier(&*self.name.to_string(), dest)?;
|
||||
serialize_atom_identifier(&self.name, dest)?;
|
||||
for argument in &self.arguments {
|
||||
dest.write_str(", ")?;
|
||||
argument.to_css(dest)?;
|
||||
|
@ -200,7 +200,7 @@ impl<G, R, U> ToCss for Image<G, R, U>
|
|||
Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
|
||||
Image::Element(ref selector) => {
|
||||
dest.write_str("-moz-element(#")?;
|
||||
serialize_identifier(&selector.to_string(), dest)?;
|
||||
serialize_atom_identifier(selector, dest)?;
|
||||
dest.write_str(")")
|
||||
},
|
||||
}
|
||||
|
|
|
@ -33,6 +33,25 @@ define_keyword_type!(None_, "none");
|
|||
define_keyword_type!(Auto, "auto");
|
||||
define_keyword_type!(Normal, "normal");
|
||||
|
||||
/// Serialize an identifier which is represented as an atom.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn serialize_atom_identifier<W>(ident: &Atom, dest: &mut W) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
ident.with_str(|s| serialize_identifier(s, dest))
|
||||
}
|
||||
|
||||
/// Serialize an identifier which is represented as an atom.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn serialize_atom_identifier<Static, W>(ident: &::string_cache::Atom<Static>, dest: &mut W) -> fmt::Result
|
||||
where
|
||||
Static: ::string_cache::StaticAtomSet,
|
||||
W: Write,
|
||||
{
|
||||
serialize_identifier(&ident, dest)
|
||||
}
|
||||
|
||||
/// Serialize a normalized value into percentage.
|
||||
pub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
|
@ -114,7 +133,7 @@ impl ToCss for CustomIdent {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
serialize_identifier(&self.0.to_string(), dest)
|
||||
serialize_atom_identifier(&self.0, dest)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,9 +165,10 @@ impl From<LengthOrPercentage> for FontSize {
|
|||
}
|
||||
|
||||
/// Specifies a prioritized list of font family names or generic family names.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, ToCss)]
|
||||
pub enum FontFamily {
|
||||
/// List of `font-family`
|
||||
#[css(iterable, comma)]
|
||||
Values(FontFamilyList),
|
||||
/// System font
|
||||
System(SystemFont),
|
||||
|
@ -245,26 +246,6 @@ impl MallocSizeOf for FontFamily {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToCss for FontFamily {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
match *self {
|
||||
FontFamily::Values(ref v) => {
|
||||
let mut iter = v.iter();
|
||||
iter.next().unwrap().to_css(dest)?;
|
||||
for family in iter {
|
||||
dest.write_str(", ")?;
|
||||
family.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FontFamily::System(sys) => sys.to_css(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for FontFamily {
|
||||
/// <family-name>#
|
||||
/// <family-name> = <string> | [ <ident>+ ]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//!
|
||||
//! TODO(emilio): Enhance docs.
|
||||
|
||||
use Namespace;
|
||||
use Prefix;
|
||||
use context::QuirksMode;
|
||||
use cssparser::{Parser, Token, serialize_identifier};
|
||||
use num_traits::One;
|
||||
|
@ -22,6 +22,7 @@ use super::computed::{Context, ToComputedValue};
|
|||
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
|
||||
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
|
||||
use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
|
||||
use values::serialize_atom_identifier;
|
||||
use values::specified::calc::CalcNode;
|
||||
|
||||
pub use properties::animated_properties::TransitionProperty;
|
||||
|
@ -748,8 +749,8 @@ pub type NamespaceId = ();
|
|||
/// `[namespace? `|`]? ident`
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
|
||||
pub struct Attr {
|
||||
/// Optional namespace
|
||||
pub namespace: Option<(Namespace, NamespaceId)>,
|
||||
/// Optional namespace prefix, with the actual namespace id.
|
||||
pub namespace: Option<(Prefix, NamespaceId)>,
|
||||
/// Attribute name
|
||||
pub attribute: String,
|
||||
}
|
||||
|
@ -761,35 +762,18 @@ impl Parse for Attr {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
/// Get the namespace id from the namespace map
|
||||
fn get_id_for_namespace(namespace: &Namespace, context: &ParserContext) -> Result<NamespaceId, ()> {
|
||||
let namespaces_map = match context.namespaces {
|
||||
Some(map) => map,
|
||||
None => {
|
||||
// If we don't have a namespace map (e.g. in inline styles)
|
||||
// we can't parse namespaces
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
match namespaces_map.prefixes.get(&namespace.0) {
|
||||
Some(entry) => Ok(entry.1),
|
||||
None => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
/// Get the namespace id from the namespace map
|
||||
fn get_id_for_namespace(_: &Namespace, _: &ParserContext) -> Result<NamespaceId, ()> {
|
||||
Ok(())
|
||||
/// Get the Namespace id from the namespace map.
|
||||
fn get_id_for_namespace(prefix: &Prefix, context: &ParserContext) -> Option<NamespaceId> {
|
||||
Some(context.namespaces.as_ref()?.prefixes.get(prefix)?.1)
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
/// Parse contents of attr() assuming we have already parsed `attr` and are
|
||||
/// within a parse_nested_block()
|
||||
pub fn parse_function<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||
-> Result<Attr, ParseError<'i>> {
|
||||
pub fn parse_function<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Attr, ParseError<'i>> {
|
||||
// Syntax is `[namespace? `|`]? ident`
|
||||
// no spaces allowed
|
||||
let first = input.try(|i| i.expect_ident_cloned()).ok();
|
||||
|
@ -804,11 +788,14 @@ impl Attr {
|
|||
};
|
||||
|
||||
let ns_with_id = if let Some(ns) = first {
|
||||
let ns = Namespace::from(ns.as_ref());
|
||||
let id: Result<_, ParseError> =
|
||||
get_id_for_namespace(&ns, context)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
Some((ns, id?))
|
||||
let ns = Prefix::from(ns.as_ref());
|
||||
let id = match get_id_for_namespace(&ns, context) {
|
||||
Some(id) => id,
|
||||
None => return Err(location.new_custom_error(
|
||||
StyleParseErrorKind::UnspecifiedError
|
||||
)),
|
||||
};
|
||||
Some((ns, id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -819,7 +806,7 @@ impl Attr {
|
|||
}
|
||||
// In the case of attr(foobar ) we don't want to error out
|
||||
// because of the trailing whitespace
|
||||
Token::WhiteSpace(_) => (),
|
||||
Token::WhiteSpace(..) => {},
|
||||
ref t => return Err(input.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
}
|
||||
|
@ -841,8 +828,8 @@ impl ToCss for Attr {
|
|||
W: Write,
|
||||
{
|
||||
dest.write_str("attr(")?;
|
||||
if let Some(ref ns) = self.namespace {
|
||||
serialize_identifier(&ns.0.to_string(), dest)?;
|
||||
if let Some((ref prefix, _id)) = self.namespace {
|
||||
serialize_atom_identifier(prefix, dest)?;
|
||||
dest.write_str("|")?;
|
||||
}
|
||||
serialize_identifier(&self.attribute, dest)?;
|
||||
|
|
|
@ -44,7 +44,7 @@ pub fn derive(input: DeriveInput) -> Tokens {
|
|||
#expr
|
||||
|
||||
for item in #binding.iter() {
|
||||
writer.item(item)?;
|
||||
writer.item(&item)?;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
|
|
@ -29,6 +29,7 @@ jobs:
|
|||
BASE_TAG: '20171210'
|
||||
SNAPSHOT: '20171210T214726Z'
|
||||
packages:
|
||||
- deb7-gdb
|
||||
- deb7-git
|
||||
- deb7-make
|
||||
- deb7-mercurial
|
||||
|
|
|
@ -175,3 +175,14 @@ jobs:
|
|||
# of devscripts.
|
||||
- deb7-devscripts-2.14
|
||||
- deb7-dpkg-1.17
|
||||
|
||||
deb7-gdb:
|
||||
description: "gdb for Debian wheezy"
|
||||
treeherder:
|
||||
symbol: Deb7(gdb)
|
||||
run:
|
||||
using: debian-package
|
||||
dsc:
|
||||
url: http://snapshot.debian.org/archive/debian/20170119T152956Z/pool/main/g/gdb/gdb_7.12-6.dsc
|
||||
sha256: 9727dcb3d6b655e4f2a92110f5db076a490aa50b739804be239905ecff3aacc8
|
||||
patch: gdb-wheezy.diff
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
for task in "$@"; do
|
||||
echo "deb [trusted=yes] https://queue.taskcluster.net/v1/task/$task/runs/0/artifacts/public/build/ debian/"
|
||||
done > /etc/apt/sources.list.d/99packages.list
|
||||
echo "deb [trusted=yes] https://queue.taskcluster.net/v1/task/$task/runs/0/artifacts/public/build/ debian/" > "/etc/apt/sources.list.d/99$task.list"
|
||||
done
|
||||
|
|
|
@ -16,6 +16,38 @@ git clone -n https://github.com/llvm-mirror/llvm
|
|||
cd llvm
|
||||
git checkout 4727bc748a48e46824eae55a81ae890cd25c3a34
|
||||
|
||||
patch -p1 <<'EOF'
|
||||
diff --git a/lib/DebugInfo/DWARF/DWARFDie.cpp b/lib/DebugInfo/DWARF/DWARFDie.cpp
|
||||
index 17559d2..b08a8d9 100644
|
||||
--- a/lib/DebugInfo/DWARF/DWARFDie.cpp
|
||||
+++ b/lib/DebugInfo/DWARF/DWARFDie.cpp
|
||||
@@ -304,20 +304,24 @@ DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const {
|
||||
|
||||
Optional<DWARFFormValue>
|
||||
DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const {
|
||||
if (!isValid())
|
||||
return None;
|
||||
if (auto Value = find(Attrs))
|
||||
return Value;
|
||||
if (auto Die = getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) {
|
||||
+ if (Die.getOffset() == getOffset())
|
||||
+ return None;
|
||||
if (auto Value = Die.findRecursively(Attrs))
|
||||
return Value;
|
||||
}
|
||||
if (auto Die = getAttributeValueAsReferencedDie(DW_AT_specification)) {
|
||||
+ if (Die.getOffset() == getOffset())
|
||||
+ return None;
|
||||
if (auto Value = Die.findRecursively(Attrs))
|
||||
return Value;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
DWARFDie
|
||||
DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const {
|
||||
EOF
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ class StructuredLogger(object):
|
|||
if not self._ensure_suite_state('suite_end', data):
|
||||
return
|
||||
|
||||
self._log_data("suite_end")
|
||||
self._log_data("suite_end", data)
|
||||
|
||||
@log_action(TestId("test"),
|
||||
Unicode("path", default=None, optional=True))
|
||||
|
|
|
@ -169,6 +169,6 @@ window.talosDebug = {
|
|||
};
|
||||
|
||||
// Enable testing outside of talos by providing an alternative report function.
|
||||
if (typeof (tpRecordTime) === "undefined") {
|
||||
if (typeof(tpRecordTime) === "undefined") {
|
||||
tpRecordTime = window.talosDebug.tpRecordTime;
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ Damp.prototype = {
|
|||
reloadPage(onReload) {
|
||||
return new Promise(resolve => {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
if (typeof (onReload) == "function") {
|
||||
if (typeof(onReload) == "function") {
|
||||
onReload().then(resolve);
|
||||
} else {
|
||||
browser.addEventListener("load", resolve, {capture: true, once: true});
|
||||
|
|
|
@ -4,7 +4,7 @@ module.exports = {
|
|||
rules: {
|
||||
// XXX Bug 1326071 - This should be reduced down - probably to 20 or to
|
||||
// be removed & synced with the mozilla/recommended value.
|
||||
"complexity": ["error", 41],
|
||||
"complexity": ["error", 44],
|
||||
|
||||
"mozilla/no-task": "error",
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ module.exports = {
|
|||
"rules": {
|
||||
// XXX Bug 1358949 - This should be reduced down - probably to 20 or to
|
||||
// be removed & synced with the mozilla/recommended value.
|
||||
"complexity": ["error", 43],
|
||||
"complexity": ["error", 56],
|
||||
|
||||
"no-unused-vars": ["error", {"args": "none", "vars": "local", "varsIgnorePattern": "^(ids|ignored|unused)$"}],
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
<!-- This file is imported into the browser window, and expects various variables,
|
||||
e.g. Ci, Services, to be available. -->
|
||||
<!-- eslint-env mozilla/browser-window -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
|
||||
|
|
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
"block-scoped-var": "error",
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
complexity: ["error", {
|
||||
max: 20,
|
||||
max: 23,
|
||||
}],
|
||||
curly: ["error", "all"],
|
||||
"dot-location": ["error", "property"],
|
||||
|
|
|
@ -5,7 +5,7 @@ module.exports = {
|
|||
// Warn about cyclomatic complexity in functions.
|
||||
// XXX Bug 1326071 - This should be reduced down - probably to 20 or to
|
||||
// be removed & synced with the mozilla/recommended value.
|
||||
"complexity": ["error", {"max": 60}],
|
||||
"complexity": ["error", {"max": 68}],
|
||||
|
||||
"no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}],
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ module.exports = {
|
|||
|
||||
// Warn about cyclomatic complexity in functions.
|
||||
// XXX Get this down to 20?
|
||||
"complexity": ["error", 32],
|
||||
"complexity": ["error", 34],
|
||||
|
||||
// Don't require spaces around computed properties
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"visibility": "public",
|
||||
"filename": "eslint-plugin-mozilla.tar.gz",
|
||||
"unpack": true,
|
||||
"digest": "eadcdb0395a62b361817c88889546f7e21dbcf8b34afed1abd9e24ea769e32e5a6b6b46330e5bb3151e9337e2b0193d63bae71b40d3eac21a5b2164af4a909aa",
|
||||
"size": 3516485
|
||||
"digest": "0de957b34d0d49cb0d682b39bc9e956f5a77d9836fc70ed6cca588b2acf83cdd9f2d3f17cb4187591bad30e01173c6e286fb6e28df4453d8611951769d2674ac",
|
||||
"size": 3171850
|
||||
}
|
||||
]
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче