mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge fx-team to central, a=merge
This commit is contained in:
commit
4e72b32062
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();"/>
|
||||
|
@ -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");
|
||||
|
@ -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",
|
||||
|
@ -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 },
|
||||
|
@ -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"/>
|
||||
|
111
browser/components/search/content/searchReset.js
Normal file
111
browser/components/search/content/searchReset.js
Normal 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);
|
||||
}
|
61
browser/components/search/content/searchReset.xhtml
Normal file
61
browser/components/search/content/searchReset.xhtml
Normal 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>
|
@ -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)
|
||||
|
@ -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]
|
||||
|
181
browser/components/search/test/browser_aboutSearchReset.js
Normal file
181
browser/components/search/test/browser_aboutSearchReset.js
Normal 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();
|
||||
});
|
||||
}
|
@ -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]);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
119
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
119
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
@ -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, {});
|
||||
|
@ -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;
|
||||
|
||||
|
28
browser/locales/en-US/chrome/browser/aboutSearchReset.dtd
Normal file
28
browser/locales/en-US/chrome/browser/aboutSearchReset.dtd
Normal 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 "Don’t Change">
|
||||
<!ENTITY searchreset.noChangeButton.access "D">
|
||||
|
||||
<!ENTITY searchreset.changeEngineButton "Change Search Engine">
|
||||
<!ENTITY searchreset.changeEngineButton.access "C">
|
@ -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.">
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
10
browser/themes/shared/favicon-search-16.svg
Normal file
10
browser/themes/shared/favicon-search-16.svg
Normal 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 |
12
browser/themes/shared/incontent-icons/icon-search-64.svg
Normal file
12
browser/themes/shared/incontent-icons/icon-search-64.svg
Normal 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 |
@ -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)
|
||||
|
@ -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
|
||||
|
36
browser/themes/shared/searchReset.css
Normal file
36
browser/themes/shared/searchReset.css
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -2,5 +2,7 @@
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
head.js
|
||||
mozscreenshots/extension/lib/permissionPrompts.html
|
||||
mozscreenshots/extension/lib/borderify.xpi
|
||||
|
||||
[browser_screenshots.js]
|
||||
|
@ -10,5 +10,5 @@ add_task(function* capture() {
|
||||
}
|
||||
let sets = ["DevTools"];
|
||||
|
||||
yield TestRunner.start(sets);
|
||||
yield TestRunner.start(sets, "devtools");
|
||||
});
|
||||
|
@ -9,6 +9,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
# run-by-dir.
|
||||
'browser.ini',
|
||||
'devtools/browser.ini',
|
||||
'permissionPrompts/browser.ini',
|
||||
'preferences/browser.ini',
|
||||
'primaryUI/browser.ini',
|
||||
]
|
||||
|
@ -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");
|
||||
|
@ -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: {
|
||||
|
@ -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");
|
||||
}
|
Binary file not shown.
@ -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>
|
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
../head.js
|
||||
|
||||
[browser_permissionPrompts.js]
|
@ -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");
|
||||
});
|
@ -10,5 +10,5 @@ add_task(function* capture() {
|
||||
}
|
||||
let sets = ["Preferences"];
|
||||
|
||||
yield TestRunner.start(sets);
|
||||
yield TestRunner.start(sets, "preferences");
|
||||
});
|
||||
|
@ -10,5 +10,5 @@ add_task(function* capture() {
|
||||
}
|
||||
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
|
||||
|
||||
yield TestRunner.start(sets);
|
||||
yield TestRunner.start(sets, "primaryUI");
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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({
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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;
|
||||
|
422
devtools/client/shared/css-properties-db.js
Normal file
422
devtools/client/shared/css-properties-db.js
Normal 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"
|
||||
];
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 " +
|
||||
|
@ -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) {
|
||||
|
53
devtools/server/actors/css-properties.js
Normal file
53
devtools/server/actors/css-properties.js
Normal 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
|
@ -21,6 +21,7 @@ DevToolsModules(
|
||||
'childtab.js',
|
||||
'chrome.js',
|
||||
'common.js',
|
||||
'css-properties.js',
|
||||
'csscoverage.js',
|
||||
'device.js',
|
||||
'director-manager.js',
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
window.opener.postMessage('ready', '*');
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
95
devtools/server/tests/mochitest/test_css-properties.html
Normal file
95
devtools/server/tests/mochitest/test_css-properties.html
Normal 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>
|
@ -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();
|
||||
}
|
||||
|
@ -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 : ""
|
||||
|
128
devtools/shared/fronts/css-properties.js
Normal file
128
devtools/shared/fronts/css-properties.js
Normal 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;
|
@ -7,6 +7,7 @@
|
||||
DevToolsModules(
|
||||
'addons.js',
|
||||
'animation.js',
|
||||
'css-properties.js',
|
||||
'highlighters.js',
|
||||
'inspector.js',
|
||||
'storage.js',
|
||||
|
@ -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.
|
||||
},
|
||||
});
|
||||
|
||||
|
19
devtools/shared/specs/css-properties.js
Normal file
19
devtools/shared/specs/css-properties.js
Normal 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;
|
@ -7,6 +7,7 @@
|
||||
DevToolsModules(
|
||||
'addons.js',
|
||||
'animation.js',
|
||||
'css-properties.js',
|
||||
'highlighters.js',
|
||||
'inspector.js',
|
||||
'storage.js',
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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 = "";
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
});
|
137
toolkit/components/search/tests/xpcshell/test_searchReset.js
Normal file
137
toolkit/components/search/tests/xpcshell/test_searchReset.js
Normal 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);
|
||||
});
|
@ -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]
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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");
|
||||
|
@ -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"] {
|
||||
|
Loading…
Reference in New Issue
Block a user