Merge fx-team to central, a=merge

This commit is contained in:
Wes Kocher 2016-05-27 14:41:36 -07:00
commit 4e72b32062
78 changed files with 2004 additions and 298 deletions

View File

@ -369,6 +369,8 @@ pref("browser.search.redirectWindowsSearch", true);
pref("browser.search.redirectWindowsSearch", false);
#endif
pref("browser.search.reset.enabled", true);
pref("browser.usedOnWindows10", false);
pref("browser.usedOnWindows10.introURL", "https://www.mozilla.org/%LOCALE%/firefox/windows-10/welcome/?utm_source=firefox-browser&utm_medium=firefox-browser");

View File

@ -7052,7 +7052,7 @@ var gIdentityHandler = {
this._uriHasHost = false;
}
let whitelist = /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)(?:[?#]|$)/i;
let whitelist = /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i;
this._isSecureInternalUI = uri.schemeIs("about") && whitelist.test(uri.path);
// Create a channel for the sole purpose of getting the resolved URI

View File

@ -740,41 +740,41 @@
<box id="notification-popup-box" hidden="true" align="center">
<image id="default-notification-icon" class="notification-anchor-icon" role="button"
aria-label="&urlbar.defaultNotificationAnchor.label;"/>
<image id="geo-notification-icon" class="notification-anchor-icon" role="button"
<image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"
<image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
aria-label="&urlbar.addonsNotificationAnchor.label;"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"
<image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
<image id="login-fill-notification-icon" class="notification-anchor-icon" role="button"
<image id="login-fill-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
<image id="password-notification-icon" class="notification-anchor-icon" role="button"
<image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
aria-label="&urlbar.passwordNotificationAnchor.label;"/>
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"
<image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"
<image id="web-notifications-notification-icon" class="notification-anchor-icon web-notifications-icon" role="button"
aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon" role="button"
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
<image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"
<image id="pointerLock-notification-icon" class="notification-anchor-icon pointer-icon" role="button"
aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
<image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"
<image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
aria-label="&urlbar.servicesNotificationAnchor.label;"/>
<image id="translate-notification-icon" class="notification-anchor-icon" role="button"
<image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
aria-label="&urlbar.translateNotificationAnchor.label;"/>
<image id="translated-notification-icon" class="notification-anchor-icon" role="button"
<image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
aria-label="&urlbar.translatedNotificationAnchor.label;"/>
<image id="eme-notification-icon" class="notification-anchor-icon" role="button"
<image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
aria-label="&urlbar.emeNotificationAnchor.label;"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup

View File

@ -13,10 +13,10 @@
<xul:label class="chat-title" flex="1" xbl:inherits="crop=titlecrop,value=label" crop="end"/>
</xul:hbox>
<xul:toolbarbutton anonid="webRTC-shareScreen-icon"
class="notification-anchor-icon chat-toolbarbutton"
class="notification-anchor-icon chat-toolbarbutton screen-icon"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>
<xul:toolbarbutton anonid="webRTC-sharingScreen-icon"
class="notification-anchor-icon chat-toolbarbutton"
class="notification-anchor-icon chat-toolbarbutton screen-icon in-use"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>
<xul:toolbarbutton anonid="notification-icon" class="notification-anchor-icon chat-toolbarbutton"
oncommand="document.getBindingParent(this).showNotifications(this); event.stopPropagation();"/>

View File

@ -196,7 +196,7 @@ function checkPopup(popup, notifyObj) {
"popup-notification-icon");
if (notifyObj.id == "geolocation") {
isnot(icon.boxObject.width, 0, "icon for geo displayed");
is(popup.anchorNode.className, "notification-anchor-icon",
ok(popup.anchorNode.classList.contains("notification-anchor-icon"),
"notification anchored to icon");
}
is(notification.getAttribute("label"), notifyObj.message, "message matches");

View File

@ -73,6 +73,9 @@ static RedirEntry kRedirMap[] = {
{ "robots", "chrome://browser/content/aboutRobots.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::ALLOW_SCRIPT },
{ "searchreset", "chrome://browser/content/search/searchReset.xhtml",
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
{ "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
{ "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",

View File

@ -96,6 +96,7 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "searchreset", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },

View File

@ -113,7 +113,7 @@
<vbox id="identity-popup-securityView-body" flex="1">
<!-- (EV) Certificate Information -->
<description id="identity-popup-content-verified-by"
when-connection="secure-ev">&identity.connectionVerified1;</description>
when-connection="secure-ev">&identity.connectionVerified2;</description>
<description id="identity-popup-content-owner"
when-connection="secure-ev"
class="header"/>

View File

@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
const TELEMETRY_RESULT_ENUM = {
RESTORED_DEFAULT: 0,
KEPT_CURRENT: 1,
CHANGED_ENGINE: 2,
CLOSED_PAGE: 3
};
window.onload = function() {
let list = document.getElementById("defaultEngine");
let originalDefault = Services.search.originalDefaultEngine.name;
Services.search.getDefaultEngines().forEach(e => {
let opt = document.createElement("option");
opt.setAttribute("value", e.name);
opt.engine = e;
opt.textContent = e.name;
if (e.iconURI)
opt.style.backgroundImage = 'url("' + e.iconURI.spec + '")';
if (e.name == originalDefault)
opt.setAttribute("selected", "true");
list.appendChild(opt);
});
let updateIcon = () => {
list.style.setProperty("--engine-icon-url",
list.selectedOptions[0].style.backgroundImage);
};
list.addEventListener("change", updateIcon);
// When selecting using the keyboard, the 'change' event is only fired after
// the user presses <enter> or moves the focus elsewhere.
// keypress/keyup fire too late and cause flicker when updating the icon.
// keydown fires too early and the selected option isn't changed yet.
list.addEventListener("keydown", () => {
Services.tm.mainThread.dispatch(updateIcon, Ci.nsIThread.DISPATCH_NORMAL);
});
updateIcon();
document.getElementById("searchResetChangeEngine").focus();
window.addEventListener("unload", recordPageClosed);
};
function doSearch() {
let queryString = "";
let purpose = "";
let params = window.location.href.match(/^about:searchreset\?([^#]*)/);
if (params) {
params = params[1].split("&");
for (let param of params) {
if (param.startsWith("data="))
queryString = decodeURIComponent(param.slice(5));
else if (param.startsWith("purpose="))
purpose = param.slice(8);
}
}
let engine = Services.search.currentEngine;
let submission = engine.getSubmission(queryString, null, purpose);
window.removeEventListener("unload", recordPageClosed);
let win = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
win.openUILinkIn(submission.uri.spec, "current", false, submission.postData);
}
function record(result) {
Services.telemetry.getHistogramById("SEARCH_RESET_RESULT").add(result);
}
function keepCurrentEngine() {
// Calling the currentEngine setter will force a correct loadPathHash to be
// written for this engine, so that we don't prompt the user again.
Services.search.currentEngine = Services.search.currentEngine;
record(TELEMETRY_RESULT_ENUM.KEPT_CURRENT);
doSearch();
}
function changeSearchEngine() {
let list = document.getElementById("defaultEngine");
let engine = list.selectedOptions[0].engine;
if (engine.hidden)
engine.hidden = false;
Services.search.currentEngine = engine;
// Record if we restored the original default or changed to another engine.
let originalDefault = Services.search.originalDefaultEngine.name;
let code = TELEMETRY_RESULT_ENUM.CHANGED_ENGINE;
if (Services.search.originalDefaultEngine.name == engine.name)
code = TELEMETRY_RESULT_ENUM.RESTORED_DEFAULT;
record(code);
doSearch();
}
function recordPageClosed() {
record(TELEMETRY_RESULT_ENUM.CLOSED_PAGE);
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % searchresetDTD SYSTEM "chrome://browser/locale/aboutSearchReset.dtd">
%searchresetDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<head>
<title>&searchreset.tabtitle;</title>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://global/skin/in-content/info-pages.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/searchReset.css"/>
<link rel="icon" type="image/png"
href="chrome://browser/skin/favicon-search-16.svg"/>
<script type="application/javascript;version=1.8"
src="chrome://browser/content/search/searchReset.js"/>
</head>
<body dir="&locale.dir;">
<div class="container">
<div class="title">
<h1 class="title-text">&searchreset.pageTitle;</h1>
</div>
<div class="description">
<p>&searchreset.pageInfo1;</p>
<p>&searchreset.selector.label;
<select id="defaultEngine"></select>
</p>
<p>&searchreset.beforelink.pageInfo2;<a id="linkSettingsPage" href="about:preferences#search">&searchreset.link.pageInfo2;</a>&searchreset.afterlink.pageInfo2;</p>
</div>
<div class="button-container">
<xul:button id="searchResetKeepCurrent"
label="&searchreset.noChangeButton;"
accesskey="&searchreset.noChangeButton.access;"
oncommand="keepCurrentEngine();"/>
<xul:button class="primary"
id="searchResetChangeEngine"
label="&searchreset.changeEngineButton;"
accesskey="&searchreset.changeEngineButton.access;"
oncommand="changeSearchEngine();"/>
</div>
</div>
</body>
</html>

View File

@ -5,3 +5,5 @@
browser.jar:
content/browser/search/search.xml (content/search.xml)
content/browser/search/searchbarBindings.css (content/searchbarBindings.css)
content/browser/search/searchReset.xhtml (content/searchReset.xhtml)
content/browser/search/searchReset.js (content/searchReset.js)

View File

@ -37,6 +37,7 @@ skip-if = os == "mac" # bug 967013
[browser_yahoo_behavior.js]
[browser_abouthome_behavior.js]
skip-if = true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
[browser_aboutSearchReset.js]
[browser_searchbar_openpopup.js]
skip-if = os == "linux" # Linux has different focus behaviours.
[browser_searchbar_keyboard_navigation.js]

View File

@ -0,0 +1,181 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const TELEMETRY_RESULT_ENUM = {
RESTORED_DEFAULT: 0,
KEPT_CURRENT: 1,
CHANGED_ENGINE: 2,
CLOSED_PAGE: 3
};
const kSearchStr = "a search";
const kSearchPurpose = "searchbar";
const kTestEngine = "testEngine.xml";
function checkTelemetryRecords(expectedValue) {
let histogram = Services.telemetry.getHistogramById("SEARCH_RESET_RESULT");
let snapshot = histogram.snapshot();
// The probe is declared with 5 values, but we get 6 back from .counts
let expectedCounts = [0, 0, 0, 0, 0, 0];
if (expectedValue != null) {
expectedCounts[expectedValue] = 1;
}
Assert.deepEqual(snapshot.counts, expectedCounts,
"histogram has expected content");
histogram.clear();
}
function promiseStoppedLoad(expectedURL) {
return new Promise(resolve => {
let browser = gBrowser.selectedBrowser;
let original = browser.loadURIWithFlags;
browser.loadURIWithFlags = function(URI) {
if (URI == expectedURL) {
browser.loadURIWithFlags = original;
ok(true, "loaded expected url: " + URI);
resolve();
return;
}
original.apply(browser, arguments);
};
});
}
var gTests = [
{
desc: "Test the 'Keep Current Settings' button.",
run: function* () {
let engine = yield promiseNewEngine(kTestEngine, {setAsCurrent: true});
let expectedURL = engine.
getSubmission(kSearchStr, null, kSearchPurpose).
uri.spec;
let rawEngine = engine.wrappedJSObject;
let initialHash = rawEngine.getAttr("loadPathHash");
rawEngine.setAttr("loadPathHash", "broken");
let loadPromise = promiseStoppedLoad(expectedURL);
gBrowser.contentDocument.getElementById("searchResetKeepCurrent").click();
yield loadPromise;
is(engine, Services.search.currentEngine,
"the custom engine is still default");
is(rawEngine.getAttr("loadPathHash"), initialHash,
"the loadPathHash has been fixed");
checkTelemetryRecords(TELEMETRY_RESULT_ENUM.KEPT_CURRENT);
}
},
{
desc: "Test the 'Restore Search Defaults' button.",
run: function* () {
let currentEngine = Services.search.currentEngine;
let originalEngine = Services.search.originalDefaultEngine;
let expectedURL = originalEngine.
getSubmission(kSearchStr, null, kSearchPurpose).
uri.spec;
let loadPromise = promiseStoppedLoad(expectedURL);
let doc = gBrowser.contentDocument;
let button = doc.getElementById("searchResetChangeEngine");
is(doc.activeElement, button,
"the 'Change Search Engine' button is focused");
button.click();
yield loadPromise;
is(originalEngine, Services.search.currentEngine,
"the default engine is back to the original one");
checkTelemetryRecords(TELEMETRY_RESULT_ENUM.RESTORED_DEFAULT);
Services.search.currentEngine = currentEngine;
}
},
{
desc: "Test the engine selector drop down.",
run: function* () {
let originalEngineName = Services.search.originalDefaultEngine.name;
let doc = gBrowser.contentDocument;
let list = doc.getElementById("defaultEngine");
is(list.value, originalEngineName,
"the default selection of the dropdown is the original default engine");
let defaultEngines = Services.search.getDefaultEngines();
is(list.childNodes.length, defaultEngines.length,
"the dropdown has the correct count of engines");
// Select an engine that isn't the original default one.
let engine;
for (let i = 0; i < defaultEngines.length; ++i) {
if (defaultEngines[i].name != originalEngineName) {
engine = defaultEngines[i];
engine.hidden = true;
break;
}
}
list.value = engine.name;
let expectedURL = engine.getSubmission(kSearchStr, null, kSearchPurpose)
.uri.spec;
let loadPromise = promiseStoppedLoad(expectedURL);
doc.getElementById("searchResetChangeEngine").click();
yield loadPromise;
ok(!engine.hidden, "the selected engine has been unhidden");
is(engine, Services.search.currentEngine,
"the current engine is what was selected in the drop down");
checkTelemetryRecords(TELEMETRY_RESULT_ENUM.CHANGED_ENGINE);
}
},
{
desc: "Load another page without clicking any of the buttons.",
run: function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, "about:mozilla");
checkTelemetryRecords(TELEMETRY_RESULT_ENUM.CLOSED_PAGE);
}
},
];
function test()
{
waitForExplicitFinish();
Task.spawn(function* () {
let oldCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
checkTelemetryRecords();
for (let test of gTests) {
info(test.desc);
// Create a tab to run the test.
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
// Start loading about:searchreset and wait for it to complete.
let url = "about:searchreset?data=" + encodeURIComponent(kSearchStr) +
"&purpose=" + kSearchPurpose;
yield promiseTabLoadEvent(tab, url);
info("Running test");
yield test.run();
info("Cleanup");
gBrowser.removeCurrentTab();
}
Services.telemetry.canRecordExtended = oldCanRecord;
}).then(finish, ex => {
ok(false, "Unexpected Exception: " + ex);
finish();
});
}

View File

@ -1,6 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://gre/modules/Promise.jsm");
/**
* Recursively compare two objects and check that every property of expectedObj has the same value
* on actualObj.
@ -85,3 +87,52 @@ function promiseNewEngine(basename, options = {}) {
});
});
}
/**
* Waits for a load (or custom) event to finish in a given tab. If provided
* load an uri into the tab.
*
* @param tab
* The tab to load into.
* @param [optional] url
* The url to load, or the current url.
* @return {Promise} resolved when the event is handled.
* @resolves to the received event
* @rejects if a valid load event is not received within a meaningful interval
*/
function promiseTabLoadEvent(tab, url)
{
let deferred = Promise.defer();
info("Wait tab event: load");
function handle(loadedUrl) {
if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
info(`Skipping spurious load event for ${loadedUrl}`);
return false;
}
info("Tab event received: load");
return true;
}
// Create two promises: one resolved from the content process when the page
// loads and one that is rejected if we take too long to load the url.
let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle);
let timeout = setTimeout(() => {
deferred.reject(new Error("Timed out while waiting for a 'load' event"));
}, 30000);
loaded.then(() => {
clearTimeout(timeout);
deferred.resolve()
});
if (url)
BrowserTestUtils.loadURI(tab.linkedBrowser, url);
// Promise.all rejects if either promise rejects (i.e. if we time out) and
// if our loaded promise resolves before the timeout, then we resolve the
// timeout promise as well, causing the all promise to resolve.
return Promise.all([deferred.promise, loaded]);
}

View File

@ -1,3 +1,3 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.5.256
Current extension version is: 1.5.276

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdf = {}));
// Use strict in our context only - users might not want it
'use strict';
var pdfjsVersion = '1.5.256';
var pdfjsBuild = '1c04335';
var pdfjsVersion = '1.5.276';
var pdfjsBuild = '41f978c';
var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ?
@ -2812,7 +2812,6 @@ var Util = sharedUtil.Util;
var createPromiseCapability = sharedUtil.createPromiseCapability;
var CustomStyle = displayDOMUtils.CustomStyle;
var getDefaultSetting = displayDOMUtils.getDefaultSetting;
var PageViewport = sharedUtil.PageViewport;
/**
* Text layer render parameters.

View File

@ -28,8 +28,8 @@ factory((root.pdfjsDistBuildPdfWorker = {}));
// Use strict in our context only - users might not want it
'use strict';
var pdfjsVersion = '1.5.256';
var pdfjsBuild = '1c04335';
var pdfjsVersion = '1.5.276';
var pdfjsBuild = '41f978c';
var pdfjsFilePath =
typeof document !== 'undefined' && document.currentScript ?
@ -4457,6 +4457,11 @@ var CFFDict = (function CFFDictClosure() {
// remove the array wrapping these types of values
if (type === 'num' || type === 'sid' || type === 'offset') {
value = value[0];
// Ignore invalid values (fixes bug 1068432).
if (isNaN(value)) {
warn('Invalid CFFDict value: ' + value + ', for key: ' + key + '.');
return true;
}
}
this.values[key] = value;
return true;
@ -37199,7 +37204,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
textState = stateManager.state;
var fn = operation.fn;
args = operation.args;
var advance;
var advance, diff;
switch (fn | 0) {
case OPS.setFont:
@ -37232,8 +37237,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
(args[0] - textContentItem.lastAdvanceWidth);
textContentItem.height +=
(args[1] - textContentItem.lastAdvanceHeight);
var diff = (args[0] - textContentItem.lastAdvanceWidth) -
(args[1] - textContentItem.lastAdvanceHeight);
diff = (args[0] - textContentItem.lastAdvanceWidth) -
(args[1] - textContentItem.lastAdvanceHeight);
addFakeSpaces(diff, textContentItem.str);
break;
}
@ -37253,6 +37258,24 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
textState.carriageReturn();
break;
case OPS.setTextMatrix:
// Optimization to treat same line movement as advance.
advance = textState.calcTextLineMatrixAdvance(
args[0], args[1], args[2], args[3], args[4], args[5]);
if (advance !== null && textContentItem.initialized &&
advance.value > 0 &&
advance.value <= textContentItem.fakeMultiSpaceMax) {
textState.translateTextLineMatrix(advance.width,
advance.height);
textContentItem.width +=
(advance.width - textContentItem.lastAdvanceWidth);
textContentItem.height +=
(advance.height - textContentItem.lastAdvanceHeight);
diff = (advance.width - textContentItem.lastAdvanceWidth) -
(advance.height - textContentItem.lastAdvanceHeight);
addFakeSpaces(diff, textContentItem.str);
break;
}
flushTextContentItem();
textState.setTextMatrix(args[0], args[1], args[2], args[3],
args[4], args[5]);
@ -38350,6 +38373,30 @@ var TextState = (function TextStateClosure() {
m[4] = m[0] * x + m[2] * y + m[4];
m[5] = m[1] * x + m[3] * y + m[5];
},
calcTextLineMatrixAdvance:
function TextState_calcTextLineMatrixAdvance(a, b, c, d, e, f) {
var font = this.font;
if (!font) {
return null;
}
var m = this.textLineMatrix;
if (!(a === m[0] && b === m[1] && c === m[2] && d === m[3])) {
return null;
}
var txDiff = e - m[4], tyDiff = f - m[5];
if ((font.vertical && txDiff !== 0) || (!font.vertical && tyDiff !== 0)) {
return null;
}
var tx, ty, denominator = a * d - b * c;
if (font.vertical) {
tx = -tyDiff * c / denominator;
ty = tyDiff * a / denominator;
} else {
tx = txDiff * d / denominator;
ty = -txDiff * b / denominator;
}
return { width: tx, height: ty, value: (font.vertical ? ty : tx), };
},
calcRenderMatrix: function TextState_calcRendeMatrix(ctm) {
// 9.4.4 Text Space Details
var tsm = [this.fontSize * this.textHScale, 0,
@ -39202,28 +39249,49 @@ var Annotation = (function AnnotationClosure() {
}
Annotation.prototype = {
/**
* @private
*/
_hasFlag: function Annotation_hasFlag(flags, flag) {
return !!(flags & flag);
},
/**
* @private
*/
_isViewable: function Annotation_isViewable(flags) {
return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&
!this._hasFlag(flags, AnnotationFlag.HIDDEN) &&
!this._hasFlag(flags, AnnotationFlag.NOVIEW);
},
/**
* @private
*/
_isPrintable: function AnnotationFlag_isPrintable(flags) {
return this._hasFlag(flags, AnnotationFlag.PRINT) &&
!this._hasFlag(flags, AnnotationFlag.INVISIBLE) &&
!this._hasFlag(flags, AnnotationFlag.HIDDEN);
},
/**
* @return {boolean}
*/
get viewable() {
if (this.flags) {
return !this.hasFlag(AnnotationFlag.INVISIBLE) &&
!this.hasFlag(AnnotationFlag.HIDDEN) &&
!this.hasFlag(AnnotationFlag.NOVIEW);
if (this.flags === 0) {
return true;
}
return true;
return this._isViewable(this.flags);
},
/**
* @return {boolean}
*/
get printable() {
if (this.flags) {
return this.hasFlag(AnnotationFlag.PRINT) &&
!this.hasFlag(AnnotationFlag.INVISIBLE) &&
!this.hasFlag(AnnotationFlag.HIDDEN);
if (this.flags === 0) {
return false;
}
return false;
return this._isPrintable(this.flags);
},
/**
@ -39236,11 +39304,7 @@ var Annotation = (function AnnotationClosure() {
* @see {@link shared/util.js}
*/
setFlags: function Annotation_setFlags(flags) {
if (isInt(flags)) {
this.flags = flags;
} else {
this.flags = 0;
}
this.flags = (isInt(flags) && flags > 0) ? flags : 0;
},
/**
@ -39254,10 +39318,7 @@ var Annotation = (function AnnotationClosure() {
* @see {@link shared/util.js}
*/
hasFlag: function Annotation_hasFlag(flag) {
if (this.flags) {
return (this.flags & flag) > 0;
}
return false;
return this._hasFlag(this.flags, flag);
},
/**
@ -39835,6 +39896,16 @@ var PopupAnnotation = (function PopupAnnotationClosure() {
this.setColor(parentItem.getArray('C'));
this.data.color = this.color;
}
// If the Popup annotation is not viewable, but the parent annotation is,
// that is most likely a bug. Fallback to inherit the flags from the parent
// annotation (this is consistent with the behaviour in Adobe Reader).
if (!this.viewable) {
var parentFlags = parentItem.get('F');
if (this._isViewable(parentFlags)) {
this.setFlags(parentFlags);
}
}
}
Util.inherit(PopupAnnotation, Annotation, {});

View File

@ -2956,7 +2956,7 @@ exports.PDFDocumentProperties = PDFDocumentProperties;
{
factory((root.pdfjsWebPDFFindController = {}), root.pdfjsWebUIUtils);
}
}(this, function (exports, uiUtils, firefoxCom) {
}(this, function (exports, uiUtils) {
var scrollIntoView = uiUtils.scrollIntoView;
@ -3254,10 +3254,9 @@ var PDFFindController = (function PDFFindControllerClosure() {
* @param {number} index - match index.
* @param {Array} elements - text layer div elements array.
* @param {number} beginIdx - start index of the div array for the match.
* @param {number} endIdx - end index of the div array for the match.
*/
updateMatchPosition: function PDFFindController_updateMatchPosition(
pageIndex, index, elements, beginIdx, endIdx) {
pageIndex, index, elements, beginIdx) {
if (this.selected.matchIdx === index &&
this.selected.pageIdx === pageIndex) {
var spot = {
@ -4995,7 +4994,6 @@ var PDFPageView = (function PDFPageViewClosure() {
var isScalingRestricted = false;
if (this.canvas && pdfjsLib.PDFJS.maxCanvasPixels > 0) {
var outputScale = this.outputScale;
var pixelsInViewport = this.viewport.width * this.viewport.height;
if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) *
((Math.floor(this.viewport.height) * outputScale.sy) | 0) >
pdfjsLib.PDFJS.maxCanvasPixels) {
@ -5801,7 +5799,7 @@ var TextLayerBuilder = (function TextLayerBuilderClosure() {
if (this.findController) {
this.findController.updateMatchPosition(pageIdx, i, textDivs,
begin.divIdx, end.divIdx);
begin.divIdx);
}
// Match inside new div.
@ -7570,7 +7568,6 @@ var PDFViewerApplication = {
pdfViewer.setDocument(pdfDocument);
var firstPagePromise = pdfViewer.firstPagePromise;
var pagesPromise = pdfViewer.pagesPromise;
var onePageRendered = pdfViewer.onePageRendered;
this.pageRotation = 0;

View File

@ -0,0 +1,28 @@
<!-- 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/. -->
<!ENTITY searchreset.tabtitle "Restore Search Settings">
<!ENTITY searchreset.pageTitle "Restore your search settings?">
<!ENTITY searchreset.pageInfo1 "Your search settings might be out-of-date. Firefox can help you restore the default search settings.">
<!ENTITY searchreset.selector.label "This will set your default search engine to">
<!-- LOCALIZATION NOTE (searchreset.beforelink.pageInfo,
searchreset.afterlink.pageInfo): these two string are used respectively
before and after the the "Settings page" link (searchreset.link.pageInfo).
Localizers can use one of them, or both, to better adapt this sentence to
their language.
-->
<!ENTITY searchreset.beforelink.pageInfo2 "You can change these settings at any time from the ">
<!ENTITY searchreset.afterlink.pageInfo2 ".">
<!ENTITY searchreset.link.pageInfo2 "Settings page">
<!ENTITY searchreset.noChangeButton "Dont Change">
<!ENTITY searchreset.noChangeButton.access "D">
<!ENTITY searchreset.changeEngineButton "Change Search Engine">
<!ENTITY searchreset.changeEngineButton.access "C">

View File

@ -682,7 +682,7 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY identity.connectionSecure "Secure Connection">
<!ENTITY identity.connectionNotSecure "Connection is Not Secure">
<!ENTITY identity.connectionFile "This page is stored on your computer.">
<!ENTITY identity.connectionVerified1 "You are securely connected to this site, run by:">
<!ENTITY identity.connectionVerified2 "You are securely connected to this site, owned by:">
<!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">
<!ENTITY identity.insecureLoginForms2 "Logins entered on this page could be compromised.">

View File

@ -17,6 +17,7 @@
#ifdef MOZ_SERVICES_HEALTHREPORT
locale/browser/aboutHealthReport.dtd (%chrome/browser/aboutHealthReport.dtd)
#endif
locale/browser/aboutSearchReset.dtd (%chrome/browser/aboutSearchReset.dtd)
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
locale/browser/aboutTabCrashed.dtd (%chrome/browser/aboutTabCrashed.dtd)
locale/browser/syncCustomize.dtd (%chrome/browser/syncCustomize.dtd)

View File

@ -1018,8 +1018,7 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
outline: 1px dotted -moz-DialogText;
}
.indexedDB-notification-icon,
#indexedDB-notification-icon {
.indexedDB-icon {
list-style-image: url(moz-icon://stock/gtk-dialog-question?size=16);
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<circle cx="8" cy="8" r="8" fill="#58bf43"/>
<circle cx="8" cy="8" r="7.5" stroke="#41a833" stroke-width="1" fill="none"/>
<path d="M12.879,12L12,12.879,9.015,9.9A4.276,4.276,0,1,1,9.9,9.015ZM6.5,3.536A2.964,2.964,0,1,0,9.464,6.5,2.964,2.964,0,0,0,6.5,3.536Z" stroke="#41a833" stroke-width="2" fill="none"/>
<path d="M12.879,12L12,12.879,9.015,9.9A4.276,4.276,0,1,1,9.9,9.015ZM6.5,3.536A2.964,2.964,0,1,0,9.464,6.5,2.964,2.964,0,0,0,6.5,3.536Z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 809 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<ellipse cx="32" cy="34" rx="29.5" ry="30" fill="#000" fill-opacity=".1"/>
<circle cx="32" cy="32" r="30" fill="#58bf43"/>
<circle cx="32" cy="32" r="29.5" stroke="#41a833" stroke-width="1" fill="none"/>
<path d="M50,47.131L47.131,50,36.776,39.647a16.038,16.038,0,1,1,2.871-2.871ZM27,15A12,12,0,1,0,39,27,12,12,0,0,0,27,15Z" stroke="#41a833" stroke-width="2" fill="none"/>
<path d="M50,47.131L47.131,50,36.776,39.647a16.038,16.038,0,1,1,2.871-2.871ZM27,15A12,12,0,1,0,39,27,12,12,0,0,0,27,15Z" fill="#fff"/>
<circle cx="27" cy="27" r="13" fill="#fff" fill-opacity=".2"/>
</svg>

After

Width:  |  Height:  |  Size: 925 B

View File

@ -88,6 +88,7 @@
skin/classic/browser/search-indicator@2x.png (../shared/search/search-indicator@2x.png)
skin/classic/browser/search-engine-placeholder.png (../shared/search/search-engine-placeholder.png)
skin/classic/browser/search-engine-placeholder@2x.png (../shared/search/search-engine-placeholder@2x.png)
skin/classic/browser/searchReset.css (../shared/searchReset.css)
skin/classic/browser/badge-add-engine.png (../shared/search/badge-add-engine.png)
skin/classic/browser/badge-add-engine@2x.png (../shared/search/badge-add-engine@2x.png)
skin/classic/browser/search-indicator-badge-add.png (../shared/search/search-indicator-badge-add.png)
@ -119,6 +120,8 @@
skin/classic/browser/cert-error.svg (../shared/incontent-icons/cert-error.svg)
skin/classic/browser/session-restore.svg (../shared/incontent-icons/session-restore.svg)
skin/classic/browser/tab-crashed.svg (../shared/incontent-icons/tab-crashed.svg)
skin/classic/browser/favicon-search-16.svg (../shared/favicon-search-16.svg)
skin/classic/browser/icon-search-64.svg (../shared/incontent-icons/icon-search-64.svg)
skin/classic/browser/welcome-back.svg (../shared/incontent-icons/welcome-back.svg)
skin/classic/browser/reader-tour.png (../shared/reader/reader-tour.png)
skin/classic/browser/reader-tour@2x.png (../shared/reader/reader-tour@2x.png)

View File

@ -81,6 +81,10 @@
list-style-image: url(chrome://browser/skin/pointerLock-64.png);
}
.popup-notification-icon[popupid="servicesInstall"] {
list-style-image: url(chrome://browser/skin/social/services-64.png);
}
/* Notification icon box */
#notification-popup-box {
position: relative;
@ -111,6 +115,9 @@
transform: none;
}
/* This class can be used alone or in combination with the class defining the
type of icon displayed. This rule must be defined before the others in order
for its list-style-image to be overridden. */
.notification-anchor-icon {
%ifdef MOZ_WIDGET_GTK
list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16);
@ -122,59 +129,66 @@
margin: 0 2px;
}
.geo-notification-icon,
#geo-notification-icon {
@media (min-resolution: 1.1dppx) {
.notification-anchor-icon {
%ifdef MOZ_WIDGET_GTK
list-style-image: url(moz-icon://stock/gtk-dialog-info?size=dialog);
%else
list-style-image: url(chrome://global/skin/icons/information-32.png);
%endif
}
}
.geo-icon {
list-style-image: url(chrome://browser/skin/Geolocation-16.png);
}
#addons-notification-icon {
.install-icon {
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#default);
}
#addons-notification-icon:hover {
.install-icon:hover {
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#hover);
}
#addons-notification-icon:hover:active {
.install-icon:hover:active {
list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#active);
}
.indexedDB-notification-icon,
#indexedDB-notification-icon {
.indexedDB-icon {
list-style-image: url(chrome://global/skin/icons/question-16.png);
}
#password-notification-icon {
.login-icon {
list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png);
}
#login-fill-notification-icon {
/* Temporary icon until the capture and fill doorhangers are unified. */
list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png);
/* Temporary solution until the capture and fill doorhangers are unified. */
transform: scaleX(-1);
}
#plugins-notification-icon {
.plugin-icon {
list-style-image: url(chrome://browser/skin/notification-pluginNormal.png);
}
#plugins-notification-icon.plugin-hidden {
.plugin-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert.png);
}
#plugins-notification-icon.plugin-blocked {
.plugin-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
}
#plugins-notification-icon {
.plugin-icon {
-moz-image-region: rect(0, 16px, 16px, 0);
}
#plugins-notification-icon:hover {
.plugin-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#plugins-notification-icon:active {
.plugin-icon:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
@ -198,84 +212,72 @@
}
}
.webRTC-shareDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
.camera-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
}
/* The first selector is used by socialchat.xml (bug 1275558). */
.webRTC-sharingDevices-notification-icon,
#webRTC-sharingDevices-notification-icon {
.camera-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingDevice-16.png);
}
.webRTC-shareMicrophone-notification-icon,
#webRTC-shareMicrophone-notification-icon {
.microphone-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16.png);
}
/* The first selector is used by socialchat.xml (bug 1275558). */
.webRTC-sharingMicrophone-notification-icon,
#webRTC-sharingMicrophone-notification-icon {
.microphone-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16.png);
}
.webRTC-shareScreen-notification-icon,
#webRTC-shareScreen-notification-icon {
.screen-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16.png);
}
.webRTC-sharingScreen-notification-icon,
#webRTC-sharingScreen-notification-icon {
.screen-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16.png);
}
.web-notifications-notification-icon,
#web-notifications-notification-icon {
.web-notifications-icon {
list-style-image: url(chrome://browser/skin/web-notifications-tray.svg);
-moz-image-region: rect(0, 16px, 16px, 0);
}
.web-notifications-notification-icon:hover,
#web-notifications-notification-icon:hover {
.web-notifications-icon:hover {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
.web-notifications-notification-icon:hover:active,
#web-notifications-notification-icon:hover:active {
.web-notifications-icon:hover:active {
-moz-image-region: rect(0, 48px, 16px, 32px);
}
.pointerLock-notification-icon,
#pointerLock-notification-icon {
.pointer-icon {
list-style-image: url(chrome://browser/skin/pointerLock-16.png);
}
.translate-notification-icon,
#translate-notification-icon {
.service-icon {
list-style-image: url(chrome://browser/skin/social/services-16.png);
}
.translation-icon {
list-style-image: url(chrome://browser/skin/translation-16.png);
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.translated-notification-icon,
#translated-notification-icon {
list-style-image: url(chrome://browser/skin/translation-16.png);
.translation-icon.in-use {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.popup-notification-icon[popupid="servicesInstall"] {
list-style-image: url(chrome://browser/skin/social/services-64.png);
}
#servicesInstall-notification-icon {
list-style-image: url(chrome://browser/skin/social/services-16.png);
}
/* EME notifications */
.popup-notification-icon[popupid="drmContentPlaying"],
#eme-notification-icon {
.drm-icon {
list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
}
#eme-notification-icon:hover:active {
.drm-icon:hover:active {
list-style-image: url("chrome://browser/skin/drm-icon.svg#chains-pressed");
}
@ -296,41 +298,27 @@
border-image: url("chrome://browser/skin/urlbar-arrow@2x.png") 0 16 0 0 fill;
}
.notification-anchor-icon {
%ifdef MOZ_WIDGET_GTK
list-style-image: url(moz-icon://stock/gtk-dialog-info?size=dialog);
%else
list-style-image: url(chrome://global/skin/icons/information-32.png);
%endif
}
.webRTC-shareDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
.camera-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16@2x.png);
}
.webRTC-sharingDevices-notification-icon,
#webRTC-sharingDevices-notification-icon {
.camera-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingDevice-16@2x.png);
}
.webRTC-shareMicrophone-notification-icon,
#webRTC-shareMicrophone-notification-icon {
.microphone-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareMicrophone-16@2x.png);
}
.webRTC-sharingMicrophone-notification-icon,
#webRTC-sharingMicrophone-notification-icon {
.microphone-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingMicrophone-16@2x.png);
}
.webRTC-shareScreen-notification-icon,
#webRTC-shareScreen-notification-icon {
.screen-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareScreen-16@2x.png);
}
.webRTC-sharingScreen-notification-icon,
#webRTC-sharingScreen-notification-icon {
.screen-icon.in-use {
list-style-image: url(chrome://browser/skin/webRTC-sharingScreen-16@2x.png);
}
@ -351,59 +339,52 @@
%ifdef XP_MACOSX
/* OSX only until we have icons for Windows and Linux */
.geo-notification-icon,
#geo-notification-icon {
.geo-icon {
list-style-image: url(chrome://browser/skin/Geolocation-16@2x.png);
}
.indexedDB-notification-icon,
#indexedDB-notification-icon {
.indexedDB-icon {
list-style-image: url(chrome://global/skin/icons/question-32.png);
}
#login-fill-notification-icon,
#password-notification-icon {
.login-icon {
list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16@2x.png);
}
#plugins-notification-icon {
.plugin-icon {
list-style-image: url(chrome://browser/skin/notification-pluginNormal@2x.png);
}
#plugins-notification-icon.plugin-hidden {
.plugin-icon.plugin-hidden {
list-style-image: url(chrome://browser/skin/notification-pluginAlert@2x.png);
}
#plugins-notification-icon.plugin-blocked {
.plugin-icon.plugin-blocked {
list-style-image: url(chrome://browser/skin/notification-pluginBlocked@2x.png);
}
#plugins-notification-icon {
.plugin-icon {
-moz-image-region: rect(0, 32px, 32px, 0);
}
#plugins-notification-icon:hover {
.plugin-icon:hover {
-moz-image-region: rect(0, 64px, 32px, 32px);
}
#plugins-notification-icon:active {
.plugin-icon:active {
-moz-image-region: rect(0, 96px, 32px, 64px);
}
.pointerLock-notification-icon,
#pointerLock-notification-icon {
.pointer-icon {
list-style-image: url(chrome://browser/skin/pointerLock-16@2x.png);
}
.translate-notification-icon,
#translate-notification-icon {
.translation-icon {
list-style-image: url(chrome://browser/skin/translation-16@2x.png);
-moz-image-region: rect(0px, 32px, 32px, 0px);
}
.translated-notification-icon,
#translated-notification-icon {
list-style-image: url(chrome://browser/skin/translation-16@2x.png);
.translation-icon.in-use {
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
@ -419,7 +400,7 @@
list-style-image: url(chrome://browser/skin/social/services-64@2x.png);
}
#servicesInstall-notification-icon {
.service-icon {
list-style-image: url(chrome://browser/skin/social/services-16@2x.png);
}
%endif

View File

@ -0,0 +1,36 @@
/* 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/. */
body {
align-items: center;
}
.title {
background-image: url("chrome://browser/skin/icon-search-64.svg");
}
select {
font: inherit;
padding-inline-end: 24px;
padding-inline-start: 26px;
background-image: var(--engine-icon-url),
url("chrome://global/skin/in-content/dropdown.svg#dropdown");
background-repeat: no-repeat;
background-position: 8px center, calc(100% - 4px) center;
background-size: 16px, 16px;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 var(--in-content-text-color);
}
option {
padding: 4px;
padding-inline-start: 30px;
background-repeat: no-repeat;
background-position: 8px center;
background-size: 16px;
background-color: var(--in-content-page-background);
}

View File

@ -91,8 +91,8 @@
/* We want a 4px gap between the TabsToolbar and the toolbar-menubar when the
toolbar-menu is displayed, and a 16px gap when it is not. 1px is taken care
of by the (light) outer shadow of the tab, the remaining 3/15 are these margins. */
#toolbar-menubar:not([moz-collapsed=true]):not([autohide=true]) ~ #TabsToolbar,
#toolbar-menubar:not([moz-collapsed=true])[autohide=true]:not([inactive]) ~ #TabsToolbar {
#toolbar-menubar:not([autohide=true]) ~ #TabsToolbar:not([inFullscreen]),
#toolbar-menubar[autohide=true]:not([inactive]) ~ #TabsToolbar:not([inFullscreen]) {
margin-top: 3px;
}

View File

@ -2,5 +2,7 @@
subsuite = screenshots
support-files =
head.js
mozscreenshots/extension/lib/permissionPrompts.html
mozscreenshots/extension/lib/borderify.xpi
[browser_screenshots.js]

View File

@ -10,5 +10,5 @@ add_task(function* capture() {
}
let sets = ["DevTools"];
yield TestRunner.start(sets);
yield TestRunner.start(sets, "devtools");
});

View File

@ -9,6 +9,7 @@ BROWSER_CHROME_MANIFESTS += [
# run-by-dir.
'browser.ini',
'devtools/browser.ini',
'permissionPrompts/browser.ini',
'preferences/browser.ini',
'primaryUI/browser.ini',
]

View File

@ -7,7 +7,6 @@
this.EXPORTED_SYMBOLS = ["TestRunner"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const defaultSetNames = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
const HOME_PAGE = "chrome://mozscreenshots/content/lib/mozscreenshots.html";
@ -51,9 +50,7 @@ this.TestRunner = {
/**
* Load specified sets, execute all combinations of them, and capture screenshots.
*/
start: Task.async(function*(setNames = null) {
setNames = setNames || defaultSetNames;
start: Task.async(function*(setNames, jobName = null) {
let subDirs = ["mozscreenshots",
(new Date()).toISOString().replace(/:/g, "-") + "_" + Services.appinfo.OS];
let screenshotPath = FileUtils.getFile("TmpD", subDirs).path;
@ -65,7 +62,11 @@ this.TestRunner = {
log.info("Saving screenshots to:", screenshotPath);
let screenshotPrefix = Services.appinfo.appBuildID + "_";
let screenshotPrefix = Services.appinfo.appBuildID;
if (jobName) {
screenshotPrefix += "-" + jobName;
}
screenshotPrefix += "_";
Screenshot.init(screenshotPath, this._extensionPath, screenshotPrefix);
this._libDir = this._extensionPath.QueryInterface(Ci.nsIFileURL).file.clone();
this._libDir.append("chrome");

View File

@ -31,6 +31,7 @@ this.DevTools = {
this.configurations[panel] = {};
this.configurations[panel].applyConfig = Task.async(function* () {
yield gDevTools.showToolbox(getTargetForSelectedTab(), panel, "bottom");
yield new Promise(resolve => setTimeout(resolve, 500));
});
});
},
@ -39,7 +40,7 @@ this.DevTools = {
bottomToolbox: {
applyConfig: Task.async(function* () {
yield gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "bottom");
yield new Promise(resolve => setTimeout(resolve, 500));
yield new Promise(resolve => setTimeout(resolve, 1000));
}),
},
sideToolbox: {

View File

@ -0,0 +1,130 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["PermissionPrompts"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/E10SUtils.jsm");
Cu.import("resource://testing-common/ContentTask.jsm");
Cu.import("resource://testing-common/BrowserTestUtils.jsm");
const URL = "https://test1.example.com/extensions/mozscreenshots/browser/chrome/mozscreenshots/lib/permissionPrompts.html";
let lastTab = null;
this.PermissionPrompts = {
init(libDir) {
Services.prefs.setBoolPref("media.navigator.permission.fake", true);
Services.prefs.setCharPref("media.getusermedia.screensharing.allowed_domains",
"test1.example.com");
Services.prefs.setBoolPref("extensions.install.requireBuiltInCerts", false);
},
configurations: {
shareDevices: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#webRTC-shareDevices");
}),
},
shareMicrophone: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#webRTC-shareMicrophone");
}),
},
shareVideoAndMicrophone: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#webRTC-shareDevices2");
}),
},
shareScreen: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#webRTC-shareScreen");
}),
},
geo: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#geo");
}),
},
loginCapture: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#login-capture", URL);
}),
},
notifications: {
applyConfig: Task.async(function*() {
yield closeLastTab();
yield clickOn("#web-notifications", URL);
}),
},
addons: {
applyConfig: Task.async(function*() {
Services.prefs.setBoolPref("xpinstall.whitelist.required", true);
yield closeLastTab();
yield clickOn("#addons", URL);
}),
},
addonsNoWhitelist: {
applyConfig: Task.async(function*() {
Services.prefs.setBoolPref("xpinstall.whitelist.required", false);
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let notification = browserWindow.document.getElementById("addon-install-confirmation-notification");
yield closeLastTab();
yield clickOn("#addons", URL);
// We want to skip the progress-notification, so we wait for
// the install-confirmation screen to be "not hidden" = shown.
yield BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
"addon install confirmation did not show", 200);
}),
},
},
};
function* closeLastTab(selector) {
if (!lastTab) {
return;
}
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
yield BrowserTestUtils.removeTab(lastTab);
lastTab = null;
}
function* clickOn(selector) {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
// Save the tab so we can close it later.
lastTab = yield BrowserTestUtils.openNewForegroundTab(browserWindow.gBrowser, URL);
yield ContentTask.spawn(lastTab.linkedBrowser, selector, function* (arg) {
E10SUtils.wrapHandlingUserInput(content, true, function() {
let element = content.document.querySelector(arg);
element.click();
});
});
// Wait for the popup to actually be shown before making the screenshot
yield BrowserTestUtils.waitForEvent(browserWindow.PopupNotifications.panel, "popupshown");
}

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Permission Prompts</title>
</head>
<body>
<button id="geo" onclick="navigator.geolocation.getCurrentPosition(() => {})">Geolocation</button>
<button id="webRTC-shareDevices" onclick="shareDevice({video: true, fake: true});">Video</button>
<button id="webRTC-shareMicrophone" onclick="shareDevice({audio: true, fake: true});">Audio</button>
<button id="webRTC-shareDevices2" onclick="shareDevice({audio: true, video: true, fake: true});">Audio and Video</button>
<button id="webRTC-shareScreen" onclick="shareDevice({video: {mediaSource: 'screen'}});">Screen</button>
<button id="web-notifications" onclick="Notification.requestPermission()">web-notifications</button>
<a id="addons" href="borderify.xpi">Install Add-On</a>
<form>
<input type="email" id="email" value="email@example.com" />
<input type="password" id="password" value="123456" />
<button type="submit" id="login-capture">Login</button>
</form>
<script type="application/javascript">
function shareDevice(config) {
navigator.mediaDevices.getUserMedia(config);
}
</script>
</body>
</html>

View File

@ -0,0 +1,6 @@
[DEFAULT]
subsuite = screenshots
support-files =
../head.js
[browser_permissionPrompts.js]

View File

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
add_task(function* capture() {
if (!shouldCapture()) {
return;
}
let sets = ["LightweightThemes", "PermissionPrompts"];
yield TestRunner.start(sets, "permissionPrompts");
});

View File

@ -10,5 +10,5 @@ add_task(function* capture() {
}
let sets = ["Preferences"];
yield TestRunner.start(sets);
yield TestRunner.start(sets, "preferences");
});

View File

@ -10,5 +10,5 @@ add_task(function* capture() {
}
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
yield TestRunner.start(sets);
yield TestRunner.start(sets, "primaryUI");
});

View File

@ -36,3 +36,4 @@ rsa.pth:python/rsa
futures.pth:python/futures
ecc.pth:python/PyECC
xpcshell.pth:testing/xpcshell
pyyaml.pth:python/pyyaml/lib

View File

@ -17,6 +17,7 @@ var clipboard = require("sdk/clipboard");
const {executeSoon} = require("devtools/shared/DevToolsUtils");
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
var {Task} = require("devtools/shared/task");
const {initCssProperties} = require("devtools/shared/fronts/css-properties");
loader.lazyRequireGetter(this, "CSS", "CSS");
@ -108,15 +109,14 @@ InspectorPanel.prototype = {
/**
* open is effectively an asynchronous constructor
*/
open: function () {
return this.target.makeRemote().then(() => {
return this._getPageStyle();
}).then(() => {
return this._getDefaultNodeForSelection();
}).then(defaultSelection => {
return this._deferredOpen(defaultSelection);
}).then(null, console.error);
},
open: Task.async(function* () {
this._cssPropertiesLoaded = initCssProperties(this.toolbox);
yield this._cssPropertiesLoaded;
yield this.target.makeRemote();
yield this._getPageStyle();
let defaultSelection = yield this._getDefaultNodeForSelection();
return yield this._deferredOpen(defaultSelection);
}),
get toolbox() {
return this._toolbox;
@ -649,6 +649,12 @@ InspectorPanel.prototype = {
this.layoutview.destroy();
}
let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => {
if (front) {
front.destroy();
}
});
this.sidebar.off("select", this._setDefaultSidebar);
let sidebarDestroyer = this.sidebar.destroy();
this.sidebar = null;
@ -678,7 +684,8 @@ InspectorPanel.prototype = {
this._panelDestroyer = promise.all([
sidebarDestroyer,
markupDestroyer
markupDestroyer,
cssPropertiesDestroyer
]);
return this._panelDestroyer;

View File

@ -12,6 +12,7 @@ const {InplaceEditor, editableItem} =
require("devtools/client/shared/inplace-editor");
const {ReflowFront} = require("devtools/server/actors/layout");
const {LocalizationHelper} = require("devtools/client/shared/l10n");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const STRINGS_URI = "chrome://devtools/locale/shared.properties";
const SHARED_L10N = new LocalizationHelper(STRINGS_URI);
@ -21,16 +22,19 @@ const LONG_TEXT_ROTATE_LIMIT = 3;
/**
* An instance of EditingSession tracks changes that have been made during the
* modification of box model values. All of these changes can be reverted by
* calling revert.
* calling revert. The main parameter is the LayoutView that created it.
*
* @param doc A DOM document that can be used to test style rules.
* @param rules An array of the style rules defined for the node being edited.
* These should be in order of priority, least important first.
* @param inspector The inspector panel.
* @param doc A DOM document that can be used to test style rules.
* @param rules An array of the style rules defined for the node being
* edited. These should be in order of priority, least
* important first.
*/
function EditingSession(doc, rules) {
function EditingSession({inspector, doc, elementRules}) {
this._doc = doc;
this._rules = rules;
this._rules = elementRules;
this._modifications = new Map();
this._cssProperties = getCssProperties(inspector.toolbox);
}
EditingSession.prototype = {
@ -110,7 +114,8 @@ EditingSession.prototype = {
// StyleRuleActor to make changes to CSS properties.
// Note that RuleRewriter doesn't support modifying several properties at
// once, so we do this in a sequence here.
let modifications = this._rules[0].startModifyingProperties();
let modifications = this._rules[0].startModifyingProperties(
this._cssProperties);
// Remember the property so it can be reverted.
if (!this._modifications.has(property.name)) {
@ -143,7 +148,8 @@ EditingSession.prototype = {
// Revert each property that we modified previously, one by one. See
// setProperties for information about why.
for (let [property, value] of this._modifications) {
let modifications = this._rules[0].startModifyingProperties();
let modifications = this._rules[0].startModifyingProperties(
this._cssProperties);
// Find the index of the property to be reverted.
let index = this.getPropertyIndex(property);
@ -358,7 +364,7 @@ LayoutView.prototype = {
*/
initEditor: function (element, event, dimension) {
let { property } = dimension;
let session = new EditingSession(this.doc, this.elementRules);
let session = new EditingSession(this);
let initialValue = session.getProperty(property);
let editor = new InplaceEditor({

View File

@ -14,6 +14,7 @@ const {TextProperty} =
require("devtools/client/inspector/rules/models/text-property");
const {promiseWarn} = require("devtools/client/inspector/shared/utils");
const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -59,6 +60,9 @@ function Rule(elementStyle, options) {
this.mediaText = this.domRule.mediaText;
}
const toolbox = this.elementStyle.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
// Populate the text properties with the style's current authoredText
// value, and add in any disabled properties from the store.
this.textProps = this._getTextProperties();
@ -248,7 +252,8 @@ Rule.prototype = {
// Note that even though StyleRuleActors normally provide parsed
// declarations already, _applyPropertiesNoAuthored is only used when
// connected to older backend that do not provide them. So parse here.
for (let cssProp of parseDeclarations(this.style.authoredText)) {
for (let cssProp of parseDeclarations(this.cssProperties.isKnown,
this.style.authoredText)) {
cssProps[cssProp.name] = cssProp;
}
@ -312,7 +317,8 @@ Rule.prototype = {
// until it settles before applying the next modification.
let resultPromise =
promise.resolve(this._applyingModifications).then(() => {
let modifications = this.style.startModifyingProperties();
let modifications = this.style.startModifyingProperties(
this.cssProperties);
modifier(modifications);
if (this.style.canSetRuleText) {
return this._applyPropertiesAuthored(modifications);
@ -388,7 +394,7 @@ Rule.prototype = {
* The property's priority (either "important" or an empty string).
*/
previewPropertyValue: function (property, value, priority) {
let modifications = this.style.startModifyingProperties();
let modifications = this.style.startModifyingProperties(this.cssProperties);
modifications.setProperty(this.textProps.indexOf(property),
property.name, value, priority);
modifications.apply().then(() => {
@ -444,7 +450,8 @@ Rule.prototype = {
// Starting with FF49, StyleRuleActors provide parsed declarations.
let props = this.style.declarations;
if (!props) {
props = parseDeclarations(this.style.authoredText, true);
props = parseDeclarations(this.cssProperties.isKnown,
this.style.authoredText, true);
}
for (let prop of props) {

View File

@ -8,6 +8,7 @@
const {Cc, Ci, Cu} = require("chrome");
const {escapeCSSComment} = require("devtools/shared/css-parsing-utils");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -49,6 +50,9 @@ function TextProperty(rule, name, value, priority, enabled = true,
this.enabled = !!enabled;
this.invisible = invisible;
this.updateComputed();
const toolbox = this.rule.elementStyle.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
}
TextProperty.prototype = {
@ -186,15 +190,7 @@ TextProperty.prototype = {
* @return {Boolean} true if the property name is known, false otherwise.
*/
isKnownProperty: function () {
try {
// If the property name is invalid, the cssPropertyIsShorthand
// will throw an exception. But if it is valid, no exception will
// be thrown; so we just ignore the return value.
domUtils.cssPropertyIsShorthand(this.name);
return true;
} catch (e) {
return false;
}
return this.cssProperties.isKnown(this.name);
},
/**

View File

@ -442,7 +442,7 @@ RuleEditor.prototype = {
// Auto-close the input if multiple rules get pasted into new property.
this.editor.input.addEventListener("paste",
blurOnMultipleProperties, false);
blurOnMultipleProperties(this.rule.cssProperties), false);
},
/**
@ -462,7 +462,8 @@ RuleEditor.prototype = {
// case, we're creating a new declaration, it doesn't make sense to accept
// these entries
this.multipleAddedProperties =
parseDeclarations(value, true).filter(d => d.name);
parseDeclarations(this.rule.cssProperties.isKnown, value, true)
.filter(d => d.name);
// Blur the editor field now and deal with adding declarations later when
// the field gets destroyed (see _newPropertyDestroy)

View File

@ -6,6 +6,7 @@
const {Ci} = require("chrome");
const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
const {InplaceEditor, editableField} =
require("devtools/client/shared/inplace-editor");
const {
@ -44,6 +45,9 @@ function TextPropertyEditor(ruleEditor, property) {
this.browserWindow = this.doc.defaultView.top;
this._populatedComputed = false;
const toolbox = this.ruleView.inspector.toolbox;
this.cssProperties = getCssProperties(toolbox);
this._onEnableClicked = this._onEnableClicked.bind(this);
this._onExpandClicked = this._onExpandClicked.bind(this);
this._onStartEditing = this._onStartEditing.bind(this);
@ -199,7 +203,7 @@ TextPropertyEditor.prototype = {
// Auto blur name field on multiple CSS rules get pasted in.
this.nameContainer.addEventListener("paste",
blurOnMultipleProperties, false);
blurOnMultipleProperties(this.cssProperties), false);
this.valueContainer.addEventListener("click", (event) => {
// Clicks within the value shouldn't propagate any further.
@ -559,7 +563,7 @@ TextPropertyEditor.prototype = {
// Adding multiple rules inside of name field overwrites the current
// property with the first, then adds any more onto the property list.
let properties = parseDeclarations(value);
let properties = parseDeclarations(this.cssProperties.isKnown, value);
if (properties.length) {
this.prop.setName(properties[0].name);
@ -618,7 +622,8 @@ TextPropertyEditor.prototype = {
*/
_onValueDone: function (value = "", commit, direction) {
let parsedProperties = this._getValueAndExtraProperties(value);
let val = parseSingleValue(parsedProperties.firstValue);
let val = parseSingleValue(this.cssProperties.isKnown,
parsedProperties.firstValue);
let isValueUnchanged = (!commit && !this.ruleEditor.isEditing) ||
!parsedProperties.propertiesToAdd.length &&
this.committed.value === val.value &&
@ -706,7 +711,7 @@ TextPropertyEditor.prototype = {
let firstValue = value;
let propertiesToAdd = [];
let properties = parseDeclarations(value);
let properties = parseDeclarations(this.cssProperties.isKnown, value);
// Check to see if the input string can be parsed as multiple properties
if (properties.length) {
@ -745,7 +750,7 @@ TextPropertyEditor.prototype = {
return;
}
let val = parseSingleValue(value);
let val = parseSingleValue(this.cssProperties.isKnown, value);
this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
val.priority);
},

View File

@ -132,13 +132,15 @@ exports.throttle = throttle;
* Event handler that causes a blur on the target if the input has
* multiple CSS properties as the value.
*/
function blurOnMultipleProperties(e) {
setTimeout(() => {
let props = parseDeclarations(e.target.value);
if (props.length > 1) {
e.target.blur();
}
}, 0);
function blurOnMultipleProperties(cssProperties) {
return (e) => {
setTimeout(() => {
let props = parseDeclarations(cssProperties.isKnown, e.target.value);
if (props.length > 1) {
e.target.blur();
}
}, 0);
};
}
exports.blurOnMultipleProperties = blurOnMultipleProperties;

View File

@ -0,0 +1,422 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* This list is generated from the output of the CssPropertiesActor. If a server
* does not support the actor, this is loaded as a backup. This list does not
* guarantee that the server actually supports these CSS properties.
*/
exports.propertiesList = [
"align-content",
"align-items",
"align-self",
"animation-delay",
"animation-direction",
"animation-duration",
"animation-fill-mode",
"animation-iteration-count",
"animation-name",
"animation-play-state",
"animation-timing-function",
"-moz-appearance",
"backface-visibility",
"background-attachment",
"background-blend-mode",
"background-clip",
"background-color",
"background-image",
"background-origin",
"background-position-x",
"background-position-y",
"background-repeat",
"background-size",
"-moz-binding",
"block-size",
"border-block-end-color",
"border-block-end-style",
"border-block-end-width",
"border-block-start-color",
"border-block-start-style",
"border-block-start-width",
"border-bottom-color",
"-moz-border-bottom-colors",
"border-bottom-left-radius",
"border-bottom-right-radius",
"border-bottom-style",
"border-bottom-width",
"border-collapse",
"border-image-outset",
"border-image-repeat",
"border-image-slice",
"border-image-source",
"border-image-width",
"border-inline-end-color",
"border-inline-end-style",
"border-inline-end-width",
"border-inline-start-color",
"border-inline-start-style",
"border-inline-start-width",
"border-left-color",
"-moz-border-left-colors",
"border-left-style",
"border-left-width",
"border-right-color",
"-moz-border-right-colors",
"border-right-style",
"border-right-width",
"border-spacing",
"border-top-color",
"-moz-border-top-colors",
"border-top-left-radius",
"border-top-right-radius",
"border-top-style",
"border-top-width",
"bottom",
"-moz-box-align",
"box-decoration-break",
"-moz-box-direction",
"-moz-box-flex",
"-moz-box-ordinal-group",
"-moz-box-orient",
"-moz-box-pack",
"box-shadow",
"box-sizing",
"caption-side",
"clear",
"clip",
"clip-path",
"clip-rule",
"color",
"color-adjust",
"color-interpolation",
"color-interpolation-filters",
"-moz-column-count",
"-moz-column-fill",
"-moz-column-gap",
"-moz-column-rule-color",
"-moz-column-rule-style",
"-moz-column-rule-width",
"-moz-column-width",
"content",
"-moz-control-character-visibility",
"counter-increment",
"counter-reset",
"cursor",
"direction",
"display",
"dominant-baseline",
"empty-cells",
"fill",
"fill-opacity",
"fill-rule",
"filter",
"flex-basis",
"flex-direction",
"flex-grow",
"flex-shrink",
"flex-wrap",
"float",
"-moz-float-edge",
"flood-color",
"flood-opacity",
"font-family",
"font-feature-settings",
"font-kerning",
"font-language-override",
"font-size",
"font-size-adjust",
"font-stretch",
"font-style",
"font-synthesis",
"font-variant-alternates",
"font-variant-caps",
"font-variant-east-asian",
"font-variant-ligatures",
"font-variant-numeric",
"font-variant-position",
"font-weight",
"-moz-force-broken-image-icon",
"grid-auto-columns",
"grid-auto-flow",
"grid-auto-rows",
"grid-column-end",
"grid-column-gap",
"grid-column-start",
"grid-row-end",
"grid-row-gap",
"grid-row-start",
"grid-template-areas",
"grid-template-columns",
"grid-template-rows",
"height",
"hyphens",
"image-orientation",
"-moz-image-region",
"image-rendering",
"ime-mode",
"inline-size",
"isolation",
"justify-content",
"justify-items",
"justify-self",
"left",
"letter-spacing",
"lighting-color",
"line-height",
"list-style-image",
"list-style-position",
"list-style-type",
"margin-block-end",
"margin-block-start",
"margin-bottom",
"margin-inline-end",
"margin-inline-start",
"margin-left",
"margin-right",
"margin-top",
"marker-end",
"marker-mid",
"marker-offset",
"marker-start",
"mask",
"mask-type",
"max-block-size",
"max-height",
"max-inline-size",
"max-width",
"min-block-size",
"min-height",
"min-inline-size",
"min-width",
"mix-blend-mode",
"object-fit",
"object-position",
"offset-block-end",
"offset-block-start",
"offset-inline-end",
"offset-inline-start",
"opacity",
"order",
"-moz-orient",
"-moz-osx-font-smoothing",
"outline-color",
"outline-offset",
"-moz-outline-radius-bottomleft",
"-moz-outline-radius-bottomright",
"-moz-outline-radius-topleft",
"-moz-outline-radius-topright",
"outline-style",
"outline-width",
"overflow-x",
"overflow-y",
"padding-block-end",
"padding-block-start",
"padding-bottom",
"padding-inline-end",
"padding-inline-start",
"padding-left",
"padding-right",
"padding-top",
"page-break-after",
"page-break-before",
"page-break-inside",
"paint-order",
"perspective",
"perspective-origin",
"pointer-events",
"position",
"quotes",
"resize",
"right",
"ruby-align",
"ruby-position",
"scroll-behavior",
"scroll-snap-coordinate",
"scroll-snap-destination",
"scroll-snap-points-x",
"scroll-snap-points-y",
"scroll-snap-type-x",
"scroll-snap-type-y",
"shape-rendering",
"-moz-stack-sizing",
"stop-color",
"stop-opacity",
"stroke",
"stroke-dasharray",
"stroke-dashoffset",
"stroke-linecap",
"stroke-linejoin",
"stroke-miterlimit",
"stroke-opacity",
"stroke-width",
"-moz-tab-size",
"table-layout",
"text-align",
"-moz-text-align-last",
"text-anchor",
"text-combine-upright",
"text-decoration-color",
"text-decoration-line",
"text-decoration-style",
"text-emphasis-color",
"text-emphasis-position",
"text-emphasis-style",
"-webkit-text-fill-color",
"text-indent",
"text-orientation",
"text-overflow",
"text-rendering",
"text-shadow",
"-moz-text-size-adjust",
"-webkit-text-stroke-color",
"-webkit-text-stroke-width",
"text-transform",
"top",
"transform",
"transform-box",
"transform-origin",
"transform-style",
"transition-delay",
"transition-duration",
"transition-property",
"transition-timing-function",
"unicode-bidi",
"-moz-user-focus",
"-moz-user-input",
"-moz-user-modify",
"-moz-user-select",
"vector-effect",
"vertical-align",
"visibility",
"white-space",
"width",
"will-change",
"-moz-window-dragging",
"word-break",
"word-spacing",
"word-wrap",
"writing-mode",
"z-index",
"all",
"animation",
"background",
"background-position",
"border",
"border-block-end",
"border-block-start",
"border-bottom",
"border-color",
"border-image",
"border-inline-end",
"border-inline-start",
"border-left",
"border-radius",
"border-right",
"border-style",
"border-top",
"border-width",
"-moz-column-rule",
"-moz-columns",
"flex",
"flex-flow",
"font",
"font-variant",
"grid",
"grid-area",
"grid-column",
"grid-gap",
"grid-row",
"grid-template",
"list-style",
"margin",
"marker",
"outline",
"-moz-outline-radius",
"overflow",
"padding",
"scroll-snap-type",
"text-decoration",
"text-emphasis",
"-webkit-text-stroke",
"-moz-transform",
"transition",
"-moz-transform-origin",
"-moz-perspective-origin",
"-moz-perspective",
"-moz-transform-style",
"-moz-backface-visibility",
"-moz-border-image",
"-moz-transition",
"-moz-transition-delay",
"-moz-transition-duration",
"-moz-transition-property",
"-moz-transition-timing-function",
"-moz-animation",
"-moz-animation-delay",
"-moz-animation-direction",
"-moz-animation-duration",
"-moz-animation-fill-mode",
"-moz-animation-iteration-count",
"-moz-animation-name",
"-moz-animation-play-state",
"-moz-animation-timing-function",
"-moz-box-sizing",
"-moz-font-feature-settings",
"-moz-font-language-override",
"-moz-padding-end",
"-moz-padding-start",
"-moz-margin-end",
"-moz-margin-start",
"-moz-border-end",
"-moz-border-end-color",
"-moz-border-end-style",
"-moz-border-end-width",
"-moz-border-start",
"-moz-border-start-color",
"-moz-border-start-style",
"-moz-border-start-width",
"-moz-hyphens",
"-webkit-animation",
"-webkit-animation-delay",
"-webkit-animation-direction",
"-webkit-animation-duration",
"-webkit-animation-fill-mode",
"-webkit-animation-iteration-count",
"-webkit-animation-name",
"-webkit-animation-play-state",
"-webkit-animation-timing-function",
"-webkit-filter",
"-webkit-text-size-adjust",
"-webkit-transform",
"-webkit-transform-origin",
"-webkit-transform-style",
"-webkit-backface-visibility",
"-webkit-perspective",
"-webkit-perspective-origin",
"-webkit-transition",
"-webkit-transition-delay",
"-webkit-transition-duration",
"-webkit-transition-property",
"-webkit-transition-timing-function",
"-webkit-border-radius",
"-webkit-border-top-left-radius",
"-webkit-border-top-right-radius",
"-webkit-border-bottom-left-radius",
"-webkit-border-bottom-right-radius",
"-webkit-background-clip",
"-webkit-background-origin",
"-webkit-background-size",
"-webkit-border-image",
"-webkit-box-shadow",
"-webkit-box-sizing",
"-webkit-box-flex",
"-webkit-box-ordinal-group",
"-webkit-box-orient",
"-webkit-box-direction",
"-webkit-box-align",
"-webkit-box-pack",
"-webkit-user-select"
];

View File

@ -21,6 +21,7 @@ DevToolsModules(
'css-angle.js',
'css-color-db.js',
'css-color.js',
'css-properties-db.js',
'css-reload.js',
'Curl.jsm',
'demangle.js',

View File

@ -8,6 +8,7 @@
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {parseDeclarations, _parseCommentDeclarations} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
// Simple test
@ -359,7 +360,8 @@ function run_basic_tests() {
do_print("Test input string " + test.input);
let output;
try {
output = parseDeclarations(test.input, test.parseComments);
output = parseDeclarations(isCssPropertyKnown, test.input,
test.parseComments);
} catch (e) {
do_print("parseDeclarations threw an exception with the given input " +
"string");
@ -394,7 +396,7 @@ const COMMENT_DATA = [
function run_comment_tests() {
for (let test of COMMENT_DATA) {
do_print("Test input string " + test.input);
let output = _parseCommentDeclarations(test.input, 0,
let output = _parseCommentDeclarations(isCssPropertyKnown, test.input, 0,
test.input.length + 4);
deepEqual(output, test.expected);
}

View File

@ -8,6 +8,7 @@
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {parseSingleValue} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
{input: null, throws: true},
@ -69,7 +70,7 @@ function run_test() {
for (let test of TEST_DATA) {
do_print("Test input value " + test.input);
try {
let output = parseSingleValue(test.input);
let output = parseSingleValue(isCssPropertyKnown, test.input);
assertOutput(output, test.expected);
} catch (e) {
do_print("parseSingleValue threw an exception with the given input " +

View File

@ -8,6 +8,7 @@
var Cu = Components.utils;
Cu.import("resource://devtools/shared/Loader.jsm");
const {RuleRewriter} = devtools.require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const TEST_DATA = [
{
@ -443,7 +444,7 @@ const TEST_DATA = [
];
function rewriteDeclarations(inputString, instruction, defaultIndentation) {
let rewriter = new RuleRewriter(null, inputString);
let rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
rewriter.defaultIndentation = defaultIndentation;
switch (instruction.type) {

View File

@ -0,0 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cc, Ci, Cu } = require("chrome");
loader.lazyGetter(this, "DOMUtils", () => {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
const protocol = require("devtools/shared/protocol");
const { ActorClassWithSpec, Actor } = protocol;
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
var CssPropertiesActor = exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
typeName: "cssProperties",
initialize: function(conn, parent) {
Actor.prototype.initialize.call(this, conn);
this.parent = parent;
},
destroy: function() {
Actor.prototype.destroy.call(this);
},
getCSSDatabase: function() {
const propertiesList = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
return { propertiesList };
}
});
/**
* Test if a CSS is property is known using server-code.
*
* @param {string} name
* @return {Boolean}
*/
function isCssPropertyKnown(name) {
try {
// If the property name is unknown, the cssPropertyIsShorthand
// will throw an exception. But if it is known, no exception will
// be thrown; so we just ignore the return value.
DOMUtils.cssPropertyIsShorthand(name);
return true;
} catch (e) {
return false;
}
}
exports.isCssPropertyKnown = isCssPropertyKnown

View File

@ -21,6 +21,7 @@ DevToolsModules(
'childtab.js',
'chrome.js',
'common.js',
'css-properties.js',
'csscoverage.js',
'device.js',
'director-manager.js',

View File

@ -10,6 +10,7 @@ const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const {getDefinedGeometryProperties} = require("devtools/server/actors/highlighters/geometry-editor");
const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
const {Task} = require("devtools/shared/task");
const events = require("sdk/event/core");
@ -1093,8 +1094,9 @@ var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
// and so that we can safely determine if a declaration is valid rather than
// have the client guess it.
if (form.authoredText || form.cssText) {
let declarations = parseDeclarations(form.authoredText ||
form.cssText, true);
let declarations = parseDeclarations(isCssPropertyKnown,
form.authoredText || form.cssText,
true);
form.declarations = declarations.map(decl => {
decl.isValid = DOMUtils.cssPropertyIsValid(decl.name, decl.value);
return decl;

View File

@ -525,6 +525,11 @@ var DebuggerServer = {
constructor: "ReflowActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/css-properties", {
prefix: "cssProperties",
constructor: "CssPropertiesActor",
type: { tab: true }
});
this.registerModule("devtools/server/actors/csscoverage", {
prefix: "cssUsage",
constructor: "CSSUsageActor",

View File

@ -8,6 +8,7 @@ support-files =
Debugger.Source.prototype.element.html
director-helpers.js
hello-actor.js
inspector_css-properties.html
inspector_getImageData.html
inspector-delay-image-response.sjs
inspector-helpers.js
@ -32,6 +33,7 @@ skip-if = buildapp == 'mulet'
[test_css-logic-inheritance.html]
[test_css-logic-media-queries.html]
[test_css-logic-specificity.html]
[test_css-properties.html]
[test_Debugger.Source.prototype.introductionScript.html]
[test_Debugger.Source.prototype.introductionType.html]
[test_Debugger.Source.prototype.element.html]

View File

@ -0,0 +1,10 @@
<html>
<head>
<body>
<script type="text/javascript">
window.onload = function() {
window.opener.postMessage('ready', '*');
};
</script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1265798 - Replace inIDOMUtils.cssPropertyIsShorthand
-->
<head>
<meta charset="utf-8">
<title>Test CSS Properties Actor</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
window.onload = function() {
const { initCssProperties, getCssProperties } =
require("devtools/shared/fronts/css-properties");
function promiseAttachUrl (url) {
return new Promise((resolve, reject) => {
attachURL(url, function(err, client, tab, doc) {
if (err) {
return reject(err);
}
resolve({client, tab, doc});
});
})
}
const runCssPropertiesTests = Task.async(function* (url, useActor) {
info("Opening two tabs.");
let attachmentA = yield promiseAttachUrl(url);
let attachmentB = yield promiseAttachUrl(url);
const toolboxMockA = {
target: {
hasActor: () => useActor,
client: attachmentA.client,
form: attachmentA.tab
}
};
const toolboxMockB = {
target: {
hasActor: () => useActor,
client: attachmentB.client,
form: attachmentB.tab
}
};
yield initCssProperties(toolboxMockA);
yield initCssProperties(toolboxMockB);
const cssProperties = getCssProperties(toolboxMockA);
const cssPropertiesA = getCssProperties(toolboxMockA);
const cssPropertiesB = getCssProperties(toolboxMockB);
is(cssProperties, cssPropertiesA,
"Multiple calls with the same toolbox returns the same object.");
isnot(cssProperties, cssPropertiesB,
"Multiple calls with the different toolboxes return different "+
" objects.");
ok(cssProperties.isKnown("border"),
"The `border` shorthand property is known.");
ok(cssProperties.isKnown("display"),
"The `display` property is known.");
ok(!cssProperties.isKnown("foobar"),
"A fake property is not known.");
ok(cssProperties.isKnown("--foobar"),
"A CSS variable properly evaluates.");
ok(cssProperties.isKnown("--foob\\{ar"),
"A CSS variable with escaped character properly evaluates.");
ok(cssProperties.isKnown("--fübar"),
"A CSS variable unicode properly evaluates.");
ok(!cssProperties.isKnown("--foo bar"),
"A CSS variable with spaces fails");
});
addAsyncTest(function* setup() {
let url = document.getElementById("cssProperties").href;
yield runCssPropertiesTests(url, true);
yield runCssPropertiesTests(url, false);
runNextTest();
});
SimpleTest.waitForExplicitFinish();
runNextTest();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265798">Mozilla Bug 1265798</a>
<a id="cssProperties" target="_blank" href="inspector_css-properties.html">Test Document</a>
</body>
</html>

View File

@ -12,6 +12,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
<script type="application/javascript;version=1.8">
const inspector = require("devtools/server/actors/inspector");
const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
window.onload = function() {
SimpleTest.waitForExplicitFinish();
@ -82,13 +83,13 @@ addAsyncTest(function* modifyProperties() {
});
function* setProperty(rule, index, name, value) {
let changes = rule.startModifyingProperties();
let changes = rule.startModifyingProperties(isCssPropertyKnown);
changes.setProperty(index, name, value);
yield changes.apply();
}
function* removeProperty(rule, index, name) {
let changes = rule.startModifyingProperties();
let changes = rule.startModifyingProperties(isCssPropertyKnown);
changes.removeProperty(index, name);
yield changes.apply();
}

View File

@ -14,14 +14,10 @@
"use strict";
const {Cc, Ci} = require("chrome");
loader.lazyRequireGetter(this, "CSS", "CSS");
const promise = require("promise");
const {getCSSLexer} = require("devtools/shared/css-lexer");
const {Task} = require("devtools/shared/task");
loader.lazyGetter(this, "DOMUtils", () => {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
const SELECTOR_ATTRIBUTE = exports.SELECTOR_ATTRIBUTE = 1;
const SELECTOR_ELEMENT = exports.SELECTOR_ELEMENT = 2;
@ -150,33 +146,15 @@ function unescapeCSSComment(inputString) {
return result.replace(/\*\\(\\*)\//g, "*$1/");
}
/**
* A helper function for parseDeclarations that implements a heuristic
* to decide whether a given bit of comment text should be parsed as a
* declaration.
*
* @param {String} name the property name that has been parsed
* @return {Boolean} true if the property should be parsed, false if
* the remainder of the comment should be skipped
*/
function shouldParsePropertyInComment(name) {
try {
// If the property name is invalid, the cssPropertyIsShorthand
// will throw an exception. But if it is valid, no exception will
// be thrown; so we just ignore the return value.
DOMUtils.cssPropertyIsShorthand(name);
return true;
} catch (e) {
return false;
}
}
/**
* A helper function for @see parseDeclarations that handles parsing
* of comment text. This wraps a recursive call to parseDeclarations
* with the processing needed to ensure that offsets in the result
* refer back to the original, unescaped, input string.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* @param {String} commentText The text of the comment, without the
* delimiters.
* @param {Number} startOffset The offset of the comment opener
@ -186,7 +164,8 @@ function shouldParsePropertyInComment(name) {
* @return {array} Array of declarations of the same form as returned
* by parseDeclarations.
*/
function parseCommentDeclarations(commentText, startOffset, endOffset) {
function parseCommentDeclarations(isCssPropertyKnown, commentText, startOffset,
endOffset) {
let commentOverride = false;
if (commentText === "") {
return [];
@ -242,8 +221,8 @@ function parseCommentDeclarations(commentText, startOffset, endOffset) {
// seem worthwhile to support declarations in comments-in-comments
// here, as there's no way to generate those using the tools, and
// users would be crazy to write such things.
let newDecls = parseDeclarationsInternal(rewrittenText, false,
true, commentOverride);
let newDecls = parseDeclarationsInternal(isCssPropertyKnown, rewrittenText,
false, true, commentOverride);
for (let decl of newDecls) {
decl.offsets[0] = rewrites[decl.offsets[0]];
decl.offsets[1] = rewrites[decl.offsets[1]];
@ -276,6 +255,8 @@ function getEmptyDeclaration() {
* The return value and arguments are like parseDeclarations, with
* these additional arguments.
*
* @param {Function} isCssPropertyKnown
* Function to check if the CSS property is known.
* @param {Boolean} inComment
* If true, assume that this call is parsing some text
* which came from a comment in another declaration.
@ -287,8 +268,8 @@ function getEmptyDeclaration() {
* rewriteDeclarations, and skip the usual name-checking
* heuristic.
*/
function parseDeclarationsInternal(inputString, parseComments,
inComment, commentOverride) {
function parseDeclarationsInternal(isCssPropertyKnown, inputString,
parseComments, inComment, commentOverride) {
if (inputString === null || inputString === undefined) {
throw new Error("empty input string");
}
@ -335,7 +316,7 @@ function parseDeclarationsInternal(inputString, parseComments,
// When parsing a comment body, if the left-hand-side is not a
// valid property name, then drop it and stop parsing.
if (inComment && !commentOverride &&
!shouldParsePropertyInComment(lastProp.name)) {
!isCssPropertyKnown(lastProp.name)) {
lastProp.name = null;
break;
}
@ -378,7 +359,8 @@ function parseDeclarationsInternal(inputString, parseComments,
if (parseComments && !lastProp.name && !lastProp.value) {
let commentText = inputString.substring(token.startOffset + 2,
token.endOffset - 2);
let newDecls = parseCommentDeclarations(commentText, token.startOffset,
let newDecls = parseCommentDeclarations(isCssPropertyKnown, commentText,
token.startOffset,
token.endOffset);
// Insert the new declarations just before the final element.
@ -420,13 +402,18 @@ function parseDeclarationsInternal(inputString, parseComments,
/**
* Returns an array of CSS declarations given a string.
* For example, parseDeclarations("width: 1px; height: 1px") would return
* For example, parseDeclarations(isCssPropertyKnown, "width: 1px; height: 1px")
* would return:
* [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
*
* The input string is assumed to only contain declarations so { and }
* characters will be treated as part of either the property or value,
* depending where it's found.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server.
* @param {String} inputString
* An input string of CSS
* @param {Boolean} parseComments
@ -450,8 +437,10 @@ function parseDeclarationsInternal(inputString, parseComments,
* on the object, which will hold the offsets of the start
* and end of the enclosing comment.
*/
function parseDeclarations(inputString, parseComments = false) {
return parseDeclarationsInternal(inputString, parseComments, false, false);
function parseDeclarations(isCssPropertyKnown, inputString,
parseComments = false) {
return parseDeclarationsInternal(isCssPropertyKnown, inputString,
parseComments, false, false);
}
/**
@ -464,7 +453,8 @@ function parseDeclarations(inputString, parseComments = false) {
*
* An example showing how to disable the 3rd property in a rule:
*
* let rewriter = new RuleRewriter(ruleActor, ruleActor.authoredText);
* let rewriter = new RuleRewriter(isCssPropertyKnown, ruleActor,
* ruleActor.authoredText);
* rewriter.setPropertyEnabled(3, "color", false);
* rewriter.apply().then(() => { ... the change is made ... });
*
@ -478,13 +468,20 @@ function parseDeclarations(inputString, parseComments = false) {
* on this object. This property has the same form as the |changed|
* property of the object returned by |getResult|.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server. Note that if Bug 1222047
* is completed then isCssPropertyKnown will not need to be passed in.
* The CssProperty front will be able to obtained directly from the
* RuleRewriter.
* @param {StyleRuleFront} rule The style rule to use. Note that this
* is only needed by the |apply| and |getDefaultIndentation| methods;
* and in particular for testing it can be |null|.
* @param {String} inputString The CSS source text to parse and modify.
* @return {Object} an object that can be used to rewrite the input text.
*/
function RuleRewriter(rule, inputString) {
function RuleRewriter(isCssPropertyKnown, rule, inputString) {
this.rule = rule;
this.inputString = inputString;
// Whether there are any newlines in the input text.
@ -493,7 +490,8 @@ function RuleRewriter(rule, inputString) {
// performing the requested action.
this.changedDeclarations = {};
// The declarations.
this.declarations = parseDeclarations(this.inputString, true);
this.declarations = parseDeclarations(isCssPropertyKnown, this.inputString,
true);
this.decl = null;
this.result = null;
@ -1082,12 +1080,17 @@ function parsePseudoClassesAndAttributes(value) {
* Expects a single CSS value to be passed as the input and parses the value
* and priority.
*
* @param {Function} isCssPropertyKnown
* A function to check if the CSS property is known. This is either an
* internal server function or from the CssPropertiesFront.
* that are supported by the server.
* @param {String} value
* The value from the text editor.
* @return {Object} an object with 'value' and 'priority' properties.
*/
function parseSingleValue(value) {
let declaration = parseDeclarations("a: " + value + ";")[0];
function parseSingleValue(isCssPropertyKnown, value) {
let declaration = parseDeclarations(isCssPropertyKnown,
"a: " + value + ";")[0];
return {
value: declaration ? declaration.value : "",
priority: declaration ? declaration.priority : ""

View File

@ -0,0 +1,128 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
const { Task } = require("devtools/shared/task");
/**
* Build up a regular expression that matches a CSS variable token. This is an
* ident token that starts with two dashes "--".
*
* https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
*/
var NON_ASCII = "[^\\x00-\\x7F]";
var ESCAPE = "\\\\[^\n\r]";
var FIRST_CHAR = ["[_a-z]", NON_ASCII, ESCAPE].join("|");
var TRAILING_CHAR = ["[_a-z0-9-]", NON_ASCII, ESCAPE].join("|");
var IS_VARIABLE_TOKEN = new RegExp(`^--(${FIRST_CHAR})(${TRAILING_CHAR})*$`,
"i");
/**
* Check that this is a CSS variable.
*
* @param {String} input
* @return {Boolean}
*/
function isCssVariable(input) {
return !!input.match(IS_VARIABLE_TOKEN);
}
var cachedCssProperties = new WeakMap();
/**
* The CssProperties front provides a mechanism to have a one-time asynchronous
* load of a CSS properties database. This is then fed into the CssProperties
* interface that provides synchronous methods for finding out what CSS
* properties the current server supports.
*/
const CssPropertiesFront = FrontClassWithSpec(cssPropertiesSpec, {
initialize: function (client, { cssPropertiesActor }) {
Front.prototype.initialize.call(this, client, {actor: cssPropertiesActor});
this.manage(this);
}
});
exports.CssPropertiesFront = CssPropertiesFront;
/**
* Ask questions to a CSS database. This class does not care how the database
* gets loaded in, only the questions that you can ask to it.
*
* @param {array} properties
* A list of all supported CSS properties.
*/
function CssProperties(properties) {
this.properties = properties;
// Bind isKnown so it can be passed around to helper functions.
this.isKnown = this.isKnown.bind(this);
}
CssProperties.prototype = {
/**
* Checks to see if the property is known by the browser. This function has
* `this` already bound so that it can be passed around by reference.
*
* @param {String} property
* The property name to be checked.
* @return {Boolean}
*/
isKnown(property) {
return this.properties.includes(property) || isCssVariable(property);
}
};
exports.CssProperties = CssProperties;
/**
* Create a CssProperties object with a fully loaded CSS database. The
* CssProperties interface can be queried synchronously, but the initialization
* is potentially async and should be handled up-front when the tool is created.
*
* The front is returned only with this function so that it can be destroyed
* once the toolbox is destroyed.
*
* @param {Toolbox} The current toolbox.
* @returns {Promise} Resolves to {cssProperties, cssPropertiesFront}.
*/
exports.initCssProperties = Task.async(function* (toolbox) {
let client = toolbox.target.client;
if (cachedCssProperties.has(client)) {
return cachedCssProperties.get(client);
}
let propertiesList, front;
// Get the list dynamically if the cssProperties exists.
if (toolbox.target.hasActor("cssProperties")) {
front = CssPropertiesFront(client, toolbox.target.form);
const db = yield front.getCSSDatabase();
propertiesList = db.propertiesList;
} else {
// The target does not support this actor, so require a static list of
// supported properties.
const db = require("devtools/client/shared/css-properties-db");
propertiesList = db.propertiesList;
}
const cssProperties = new CssProperties(propertiesList);
cachedCssProperties.set(client, {cssProperties, front});
return {cssProperties, front};
});
/**
* Synchronously get a cached and initialized CssProperties.
*
* @param {Toolbox} The current toolbox.
* @returns {CssProperties}
*/
exports.getCssProperties = function (toolbox) {
if (!cachedCssProperties.has(toolbox.target.client)) {
throw new Error("The CSS database has not been initialized, please make " +
"sure initCssDatabase was called once before for this " +
"toolbox.");
}
return cachedCssProperties.get(toolbox.target.client).cssProperties;
};
exports.CssPropertiesFront = CssPropertiesFront;

View File

@ -7,6 +7,7 @@
DevToolsModules(
'addons.js',
'animation.js',
'css-properties.js',
'highlighters.js',
'inspector.js',
'storage.js',

View File

@ -128,10 +128,14 @@ const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
* A RuleRewriter will be returned when the rule's canSetRuleText
* trait is true; otherwise a RuleModificationList will be
* returned.
*
* @param {CssPropertiesFront} cssProperties
* This is needed by the RuleRewriter.
* @return {RuleModificationList}
*/
startModifyingProperties: function () {
startModifyingProperties: function (cssProperties) {
if (this.canSetRuleText) {
return new RuleRewriter(this, this.authoredText);
return new RuleRewriter(cssProperties.isKnown, this, this.authoredText);
}
return new RuleModificationList(this);
},
@ -416,4 +420,3 @@ var RuleModificationList = Class({
// Nothing.
},
});

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { RetVal, generateActorSpec } = require("devtools/shared/protocol");
const cssPropertiesSpec = generateActorSpec({
typeName: "cssProperties",
methods: {
getCSSDatabase: {
request: {},
response: RetVal("json"),
}
}
});
exports.cssPropertiesSpec = cssPropertiesSpec;

View File

@ -7,6 +7,7 @@
DevToolsModules(
'addons.js',
'animation.js',
'css-properties.js',
'highlighters.js',
'inspector.js',
'storage.js',

View File

@ -5226,6 +5226,8 @@ pref("browser.search.update", true);
pref("browser.search.update.log", false);
pref("browser.search.update.interval", 21600);
pref("browser.search.suggest.enabled", true);
pref("browser.search.reset.enabled", false);
pref("browser.search.reset.whitelist", "");
pref("browser.search.geoSpecificDefaults", false);
pref("browser.search.geoip.url", "https://location.services.mozilla.com/v1/country?key=%MOZILLA_API_KEY%");
// NOTE: this timeout figure is also the "high" value for the telemetry probe

View File

@ -436,6 +436,12 @@ interface nsIBrowserSearchService : nsISupports
*/
void removeEngine(in nsISearchEngine engine);
/**
* The original Engine object that is the default for this region,
* ignoring changes the user may have subsequently made.
*/
readonly attribute nsISearchEngine originalDefaultEngine;
/**
* Alias for the currentEngine attribute, kept for add-on compatibility.
*/

View File

@ -169,21 +169,6 @@ var LoginManagerParent = {
return;
}
let allLoginsCount = Services.logins.countLogins(formOrigin, "", null);
// If there are no logins for this site, bail out now.
if (!allLoginsCount) {
try {
target.sendAsyncMessage("RemoteLogins:loginsFound", {
requestId: requestId,
logins: [],
recipes,
});
} catch (e) {
log("error sending message to target", e);
}
return;
}
// If we're currently displaying a master password prompt, defer
// processing this form until the user handles the prompt.
if (Services.logins.uiBusy) {
@ -231,16 +216,6 @@ var LoginManagerParent = {
logins: jsLogins,
recipes,
});
const PWMGR_FORM_ACTION_EFFECT = Services.telemetry.getHistogramById("PWMGR_FORM_ACTION_EFFECT");
if (logins.length == 0) {
PWMGR_FORM_ACTION_EFFECT.add(2);
} else if (logins.length == allLoginsCount) {
PWMGR_FORM_ACTION_EFFECT.add(0);
} else {
// logins.length < allLoginsCount
PWMGR_FORM_ACTION_EFFECT.add(1);
}
}),
doAutocompleteSearch: function({ formOrigin, actionOrigin,

View File

@ -2408,6 +2408,21 @@ Engine.prototype = {
},
#endif
get _isWhiteListed() {
let url = this._getURLOfType(URLTYPE_SEARCH_HTML).template;
let hostname = makeURI(url).host;
let whitelist = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
.getCharPref("reset.whitelist")
.split(",");
if (whitelist.includes(hostname)) {
LOG("The hostname " + hostname + " is white listed, " +
"we won't show the search reset prompt");
return true;
}
return false;
},
// from nsISearchEngine
getSubmission: function SRCH_ENG_getSubmission(aData, aResponseType, aPurpose) {
#ifdef ANDROID
@ -2419,6 +2434,24 @@ Engine.prototype = {
aResponseType = URLTYPE_SEARCH_HTML;
}
if (aResponseType == URLTYPE_SEARCH_HTML &&
Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF).getBoolPref("reset.enabled") &&
this.name == Services.search.currentEngine.name &&
!this._isDefault &&
(!this.getAttr("loadPathHash") ||
this.getAttr("loadPathHash") != getVerificationHash(this._loadPath)) &&
!this._isWhiteListed) {
let url = "about:searchreset";
let data = [];
if (aData)
data.push("data=" + encodeURIComponent(aData));
if (aPurpose)
data.push("purpose=" + aPurpose);
if (data.length)
url += "?" + data.join("&");
return new Submission(makeURI(url));
}
var url = this._getURLOfType(aResponseType);
if (!url)
@ -2426,7 +2459,7 @@ Engine.prototype = {
if (!aData) {
// Return a dummy submission object with our searchForm attribute
return new Submission(makeURI(this._getSearchFormWithPurpose(aPurpose)), null);
return new Submission(makeURI(this._getSearchFormWithPurpose(aPurpose)));
}
LOG("getSubmission: In data: \"" + aData + "\"; Purpose: \"" + aPurpose + "\"");
@ -2822,7 +2855,7 @@ SearchService.prototype = {
// Get the original Engine object that is the default for this region,
// ignoring changes the user may have subsequently made.
get _originalDefaultEngine() {
get originalDefaultEngine() {
let defaultEngine = this.getVerifiedGlobalAttr("searchDefault");
if (!defaultEngine) {
let defaultPrefB = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF);
@ -2841,7 +2874,7 @@ SearchService.prototype = {
},
resetToOriginalDefaultEngine: function SRCH_SVC__resetToOriginalDefaultEngine() {
this.currentEngine = this._originalDefaultEngine;
this.currentEngine = this.originalDefaultEngine;
},
_buildCache: function SRCH_SVC__buildCache() {
@ -3948,6 +3981,7 @@ SearchService.prototype = {
var engine = new Engine(sanitizeName(aName), false);
engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
aMethod, aTemplate, aExtensionID);
engine._loadPath = "[other]addEngineWithDetails";
this._addEngineToStore(engine);
},
@ -4111,13 +4145,13 @@ SearchService.prototype = {
this._currentEngine = engine;
}
if (!name)
this._currentEngine = this._originalDefaultEngine;
this._currentEngine = this.originalDefaultEngine;
}
// If the current engine is not set or hidden, we fallback...
if (!this._currentEngine || this._currentEngine.hidden) {
// first to the original default engine
let originalDefault = this._originalDefaultEngine;
let originalDefault = this.originalDefaultEngine;
if (!originalDefault || originalDefault.hidden) {
// then to the first visible engine
let firstVisible = this._getSortedEngines(false)[0];
@ -4154,9 +4188,11 @@ SearchService.prototype = {
if (!newCurrentEngine)
FAIL("Can't find engine in store!", Cr.NS_ERROR_UNEXPECTED);
if (!newCurrentEngine._isDefault && newCurrentEngine._loadPath) {
if (!newCurrentEngine._isDefault) {
// If a non default engine is being set as the current engine, ensure
// its loadPath has a verification hash.
if (!newCurrentEngine._loadPath)
newCurrentEngine._loadPath = "[other]unknown";
let loadPathHash = getVerificationHash(newCurrentEngine._loadPath);
let currentHash = newCurrentEngine.getAttr("loadPathHash");
if (!currentHash || currentHash != loadPathHash) {
@ -4176,7 +4212,7 @@ SearchService.prototype = {
// build's default engine, so that the currentEngine getter falls back to
// whatever the default is.
let newName = this._currentEngine.name;
if (this._currentEngine == this._originalDefaultEngine) {
if (this._currentEngine == this.originalDefaultEngine) {
newName = "";
}

View File

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const kSearchEngineID = "addEngineWithDetails_test_engine";
const kSearchEngineURL = "http://example.com/?search={searchTerms}";
const kSearchTerm = "foo";
add_task(function* test_addEngineWithDetails() {
do_check_false(Services.search.isInitialized);
Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
.setBoolPref("reset.enabled", true);
yield asyncInit();
Services.search.addEngineWithDetails(kSearchEngineID, "", "", "", "get",
kSearchEngineURL);
// An engine added with addEngineWithDetails should have a load path, even
// though we can't point to a specific file.
let engine = Services.search.getEngineByName(kSearchEngineID);
do_check_eq(engine.wrappedJSObject._loadPath, "[other]addEngineWithDetails");
// Set the engine as default; this should set a loadPath verification hash,
// which should ensure we don't show the search reset prompt.
Services.search.currentEngine = engine;
let expectedURL = kSearchEngineURL.replace("{searchTerms}", kSearchTerm);
let submission =
Services.search.currentEngine.getSubmission(kSearchTerm, null, "searchbar");
do_check_eq(submission.uri.spec, expectedURL);
});

View File

@ -0,0 +1,137 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const NS_APP_USER_SEARCH_DIR = "UsrSrchPlugns";
const kTestEngineShortName = "engine";
const kWhiteListPrefName = "reset.whitelist";
function run_test() {
// Copy an engine to [profile]/searchplugin/
let dir = Services.dirsvc.get(NS_APP_USER_SEARCH_DIR, Ci.nsIFile);
if (!dir.exists())
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
do_get_file("data/engine.xml").copyTo(dir, kTestEngineShortName + ".xml");
let file = dir.clone();
file.append(kTestEngineShortName + ".xml");
do_check_true(file.exists());
do_check_false(Services.search.isInitialized);
Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
.setBoolPref("reset.enabled", true);
run_next_test();
}
function* removeLoadPathHash() {
// Remove the loadPathHash and re-initialize the search service.
let cache = yield promiseCacheData();
for (let engine of cache.engines) {
if (engine._shortName == kTestEngineShortName) {
delete engine._metaData["loadPathHash"];
break;
}
}
yield promiseSaveCacheData(cache);
yield asyncReInit();
}
add_task(function* test_no_prompt_when_valid_loadPathHash() {
yield asyncInit();
// test the engine is loaded ok.
let engine = Services.search.getEngineByName(kTestEngineName);
do_check_neq(engine, null);
yield promiseAfterCache();
// The test engine has been found in the profile directory and imported,
// so it shouldn't have a loadPathHash.
let metadata = yield promiseEngineMetadata();
do_check_true(kTestEngineShortName in metadata);
do_check_false("loadPathHash" in metadata[kTestEngineShortName]);
// After making it the currentEngine with the search service API,
// the test engine should have a valid loadPathHash.
Services.search.currentEngine = engine;
yield promiseAfterCache();
metadata = yield promiseEngineMetadata();
do_check_true("loadPathHash" in metadata[kTestEngineShortName]);
let loadPathHash = metadata[kTestEngineShortName].loadPathHash;
do_check_eq(typeof loadPathHash, "string");
do_check_eq(loadPathHash.length, 44);
// A search should not cause the search reset prompt.
let submission =
Services.search.currentEngine.getSubmission("foo", null, "searchbar");
do_check_eq(submission.uri.spec,
"http://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&aq=t");
});
add_task(function* test_promptURLs() {
yield removeLoadPathHash();
// The default should still be the test engine.
let currentEngine = Services.search.currentEngine;
do_check_eq(currentEngine.name, kTestEngineName);
// but the submission url should be about:searchreset
let url = (data, purpose) =>
currentEngine.getSubmission(data, null, purpose).uri.spec;
do_check_eq(url("foo", "searchbar"),
"about:searchreset?data=foo&purpose=searchbar");
do_check_eq(url("foo"), "about:searchreset?data=foo");
do_check_eq(url("", "searchbar"), "about:searchreset?purpose=searchbar");
do_check_eq(url(""), "about:searchreset");
do_check_eq(url("", ""), "about:searchreset");
// Calling the currentEngine setter for the same engine should
// prevent further prompts.
Services.search.currentEngine = Services.search.currentEngine;
do_check_eq(url("foo", "searchbar"),
"http://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&aq=t");
// And the loadPathHash should be back.
yield promiseAfterCache();
let metadata = yield promiseEngineMetadata();
do_check_true("loadPathHash" in metadata[kTestEngineShortName]);
let loadPathHash = metadata[kTestEngineShortName].loadPathHash;
do_check_eq(typeof loadPathHash, "string");
do_check_eq(loadPathHash.length, 44);
});
add_task(function* test_whitelist() {
yield removeLoadPathHash();
// The default should still be the test engine.
let currentEngine = Services.search.currentEngine;
do_check_eq(currentEngine.name, kTestEngineName);
let expectPrompt = shouldPrompt => {
let expectedURL =
shouldPrompt ? "about:searchreset?data=foo&purpose=searchbar"
: "http://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&aq=t";
let url = currentEngine.getSubmission("foo", null, "searchbar").uri.spec;
do_check_eq(url, expectedURL);
};
expectPrompt(true);
// Unless we whitelist our test engine.
let branch = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF);
let initialWhiteList = branch.getCharPref(kWhiteListPrefName);
branch.setCharPref(kWhiteListPrefName, "example.com,test.tld");
expectPrompt(true);
branch.setCharPref(kWhiteListPrefName, "www.google.com");
expectPrompt(false);
branch.setCharPref(kWhiteListPrefName, "example.com,www.google.com,test.tld");
expectPrompt(false);
// The loadPathHash should not be back after the prompt was skipped due to the
// whitelist.
yield asyncReInit();
let metadata = yield promiseEngineMetadata();
do_check_false("loadPathHash" in metadata[kTestEngineShortName]);
branch.setCharPref(kWhiteListPrefName, initialWhiteList);
expectPrompt(true);
});

View File

@ -94,3 +94,5 @@ tags = addons
[test_require_engines_in_cache.js]
[test_update_telemetry.js]
[test_svg_icon.js]
[test_searchReset.js]
[test_addEngineWithDetails.js]

View File

@ -5518,6 +5518,15 @@
"releaseChannelCollection": "opt-out",
"description": "Record the search counts for search engines"
},
"SEARCH_RESET_RESULT": {
"alert_emails": ["fqueze@mozilla.com"],
"bug_numbers": [1203168],
"expires_in_version": "53",
"kind": "enumerated",
"n_values": 5,
"releaseChannelCollection": "opt-out",
"description": "Result of showing the search reset prompt to the user. 0=restored original default, 1=kept current engine, 2=changed engine, 3=closed the page"
},
"SEARCH_SERVICE_INIT_MS": {
"expires_in_version": "never",
"kind": "exponential",
@ -9270,12 +9279,6 @@
"n_buckets" : 10,
"description": "The number of sites for which the user has explicitly rejected saving logins"
},
"PWMGR_FORM_ACTION_EFFECT": {
"expires_in_version": "never",
"kind": "enumerated",
"n_values" : 5,
"description": "The effect of the form action on signon autofill. (0=No effect, 1=Fewer logins after considering the form action, 2=No logins match form origin and action."
},
"PWMGR_FORM_AUTOFILL_RESULT": {
"expires_in_version": "never",
"kind": "enumerated",

View File

@ -826,7 +826,6 @@
"PROCESS_CRASH_SUBMIT_ATTEMPT",
"PROCESS_CRASH_SUBMIT_SUCCESS",
"PWMGR_BLOCKLIST_NUM_SITES",
"PWMGR_FORM_ACTION_EFFECT",
"PWMGR_FORM_AUTOFILL_RESULT",
"PWMGR_LOGIN_LAST_USED_DAYS",
"PWMGR_LOGIN_PAGE_SAFETY",
@ -2041,7 +2040,6 @@
"PUSH_API_UNSUBSCRIBE_SUCCEEDED",
"PUSH_API_USED",
"PWMGR_BLOCKLIST_NUM_SITES",
"PWMGR_FORM_ACTION_EFFECT",
"PWMGR_FORM_AUTOFILL_RESULT",
"PWMGR_LOGIN_LAST_USED_DAYS",
"PWMGR_LOGIN_PAGE_SAFETY",

View File

@ -1389,8 +1389,8 @@ add_task(function* test_defaultSearchEngine() {
const EXPECTED_SEARCH_ENGINE_DATA = {
name: "telemetry_default",
loadPath: null,
origin: "unverified"
loadPath: "[other]addEngineWithDetails",
origin: "verified"
};
Assert.deepEqual(data.settings.defaultSearchEngineData, EXPECTED_SEARCH_ENGINE_DATA);
TelemetryEnvironment.unregisterChangeListener("testWatch_SearchDefault");

View File

@ -175,6 +175,7 @@ html|button {
/* xul buttons and menulists */
*|button,
html|select,
xul|colorpicker[type="button"],
xul|menulist {
-moz-appearance: none;
@ -190,6 +191,7 @@ xul|menulist {
}
html|button:enabled:hover,
html|select:enabled:hover,
xul|button:not([disabled="true"]):hover,
xul|colorpicker[type="button"]:not([disabled="true"]):hover,
xul|menulist:not([disabled="true"]):hover {
@ -197,6 +199,7 @@ xul|menulist:not([disabled="true"]):hover {
}
html|button:enabled:hover:active,
html|select:enabled:hover:active,
xul|button:not([disabled="true"]):hover:active,
xul|colorpicker[type="button"]:not([disabled="true"]):hover:active,
xul|menulist[open="true"]:not([disabled="true"]) {
@ -204,6 +207,7 @@ xul|menulist[open="true"]:not([disabled="true"]) {
}
html|button:disabled,
html|select:disabled,
xul|button[disabled="true"],
xul|colorpicker[type="button"][disabled="true"],
xul|menulist[disabled="true"] {