зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1177162 - Show an info panel on the tracking protection doorhanger anchor the first time tracking elements are blocked. r=bgrins,ttaubert
--HG-- extra : commitid : FsPzDGrKpXv extra : rebase_source : b6e829a815fdf48657071dd79e1bf7690f5f5bfb
This commit is contained in:
Родитель
dbf261d22c
Коммит
ffc30df2d5
|
@ -1918,6 +1918,8 @@ pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");
|
|||
pref("browser.polaris.enabled", false);
|
||||
pref("privacy.trackingprotection.ui.enabled", false);
|
||||
#endif
|
||||
pref("privacy.trackingprotection.introCount", 0);
|
||||
pref("privacy.trackingprotection.introURL", "https://support.mozilla.org/kb/tracking-protection-firefox");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// At the moment, autostart.2 is used, while autostart.1 is unused.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
let TrackingProtection = {
|
||||
MAX_INTROS: 0,
|
||||
PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
|
||||
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
|
||||
enabledGlobally: false,
|
||||
|
@ -64,6 +65,15 @@ let TrackingProtection = {
|
|||
for (let element of [this.icon, this.content]) {
|
||||
if (state & STATE_BLOCKED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "blocked-tracking-content");
|
||||
|
||||
// Open the tracking protection introduction panel, if applicable.
|
||||
let introCount = gPrefService.getIntPref("privacy.trackingprotection.introCount");
|
||||
if (introCount < TrackingProtection.MAX_INTROS) {
|
||||
gPrefService.setIntPref("privacy.trackingprotection.introCount", ++introCount);
|
||||
gPrefService.savePrefFile(null);
|
||||
this.showIntroPanel();
|
||||
}
|
||||
|
||||
} else if (state & STATE_LOADED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "loaded-tracking-content");
|
||||
} else {
|
||||
|
@ -107,4 +117,47 @@ let TrackingProtection = {
|
|||
|
||||
BrowserReload();
|
||||
},
|
||||
|
||||
showIntroPanel: Task.async(function*() {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
|
||||
let openStep2 = () => {
|
||||
// When the user proceeds in the tour, adjust the counter to indicate that
|
||||
// the user doesn't need to see the intro anymore.
|
||||
gPrefService.setIntPref("privacy.trackingprotection.introCount",
|
||||
this.MAX_INTROS);
|
||||
gPrefService.savePrefFile(null);
|
||||
|
||||
let nextURL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
|
||||
"#step2";
|
||||
switchToTabHavingURI(nextURL, true, {
|
||||
// Ignore the fragment in case the intro is shown on the tour page
|
||||
// (e.g. if the user manually visited the tour or clicked the link from
|
||||
// about:privatebrowsing) so we can avoid a reload.
|
||||
ignoreFragment: true,
|
||||
});
|
||||
};
|
||||
|
||||
let buttons = [
|
||||
{
|
||||
label: gNavigatorBundle.getString("trackingProtection.intro.step1of3"),
|
||||
style: "text",
|
||||
},
|
||||
{
|
||||
callback: openStep2,
|
||||
label: gNavigatorBundle.getString("trackingProtection.intro.nextButton.label"),
|
||||
style: "primary",
|
||||
},
|
||||
];
|
||||
|
||||
let panelTarget = yield UITour.getTarget(window, "siteIdentity");
|
||||
UITour.initForBrowser(gBrowser.selectedBrowser);
|
||||
UITour.showInfo(window, mm, panelTarget,
|
||||
gNavigatorBundle.getString("trackingProtection.intro.title"),
|
||||
gNavigatorBundle.getFormattedString("trackingProtection.intro.description",
|
||||
[brandShortName]),
|
||||
undefined, buttons);
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -263,6 +263,9 @@ this.UITour = {
|
|||
return element;
|
||||
},
|
||||
}],
|
||||
["siteIdentity", {
|
||||
query: "#page-proxy-favicon",
|
||||
}],
|
||||
["urlbar", {
|
||||
query: "#urlbar",
|
||||
widgetName: "urlbar-container",
|
||||
|
@ -410,9 +413,6 @@ this.UITour = {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Do this before bailing if there's no tab, so later we can pick up the pieces:
|
||||
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
|
||||
switch (action) {
|
||||
case "registerPageID": {
|
||||
if (typeof data.pageID != "string") {
|
||||
|
@ -503,9 +503,12 @@ this.UITour = {
|
|||
if (typeof buttonData == "object" &&
|
||||
typeof buttonData.label == "string" &&
|
||||
typeof buttonData.callbackID == "string") {
|
||||
let callback = buttonData.callbackID;
|
||||
let button = {
|
||||
label: buttonData.label,
|
||||
callbackID: buttonData.callbackID,
|
||||
callback: event => {
|
||||
this.sendPageCallback(messageManager, callback);
|
||||
},
|
||||
};
|
||||
|
||||
if (typeof buttonData.icon == "string")
|
||||
|
@ -730,16 +733,24 @@ this.UITour = {
|
|||
}
|
||||
}
|
||||
|
||||
this.initForBrowser(browser);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
initForBrowser(aBrowser) {
|
||||
let window = aBrowser.ownerDocument.defaultView;
|
||||
|
||||
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
|
||||
if (!this.tourBrowsersByWindow.has(window)) {
|
||||
this.tourBrowsersByWindow.set(window, new Set());
|
||||
}
|
||||
this.tourBrowsersByWindow.get(window).add(browser);
|
||||
this.tourBrowsersByWindow.get(window).add(aBrowser);
|
||||
|
||||
Services.obs.addObserver(this, "message-manager-close", false);
|
||||
|
||||
window.addEventListener("SSWindowClosing", this);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
|
@ -1400,22 +1411,28 @@ this.UITour = {
|
|||
tooltipButtons.firstChild.remove();
|
||||
|
||||
for (let button of aButtons) {
|
||||
let el = document.createElement("button");
|
||||
el.setAttribute("label", button.label);
|
||||
if (button.iconURL)
|
||||
el.setAttribute("image", button.iconURL);
|
||||
let isButton = button.style != "text";
|
||||
let el = document.createElement(isButton ? "button" : "label");
|
||||
el.setAttribute(isButton ? "label" : "value", button.label);
|
||||
|
||||
if (button.style == "link")
|
||||
el.setAttribute("class", "button-link");
|
||||
if (isButton) {
|
||||
if (button.iconURL)
|
||||
el.setAttribute("image", button.iconURL);
|
||||
|
||||
if (button.style == "primary")
|
||||
el.setAttribute("class", "button-primary");
|
||||
if (button.style == "link")
|
||||
el.setAttribute("class", "button-link");
|
||||
|
||||
let callbackID = button.callbackID;
|
||||
el.addEventListener("command", event => {
|
||||
tooltip.hidePopup();
|
||||
this.sendPageCallback(aMessageManager, callbackID);
|
||||
});
|
||||
if (button.style == "primary")
|
||||
el.setAttribute("class", "button-primary");
|
||||
|
||||
// Don't close the popup or call the callback for style=text as they
|
||||
// aren't links/buttons.
|
||||
let callback = button.callback;
|
||||
el.addEventListener("command", event => {
|
||||
tooltip.hidePopup();
|
||||
callback(event);
|
||||
});
|
||||
}
|
||||
|
||||
tooltipButtons.appendChild(el);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
|||
skip-if = e10s # Bug 1073247 - UITour tests not e10s friendly
|
||||
[browser_openSearchPanel.js]
|
||||
skip-if = true # Bug 1113038 - Intermittent "Popup was opened"
|
||||
[browser_trackingProtection.js]
|
||||
tag = trackingprotection
|
||||
[browser_UITour.js]
|
||||
skip-if = os == "linux" || e10s # Intermittent failures, bug 951965
|
||||
[browser_UITour2.js]
|
||||
|
|
|
@ -55,16 +55,30 @@ let tests = [
|
|||
is(icon.src, imageURL, "Popup should have correct icon shown");
|
||||
|
||||
buttons = document.getElementById("UITourTooltipButtons");
|
||||
is(buttons.childElementCount, 2, "Popup should have two buttons");
|
||||
is(buttons.childElementCount, 4, "Popup should have four buttons");
|
||||
|
||||
is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
|
||||
is(buttons.childNodes[0].nodeName, "label", "Text label should be a <label>");
|
||||
is(buttons.childNodes[0].getAttribute("value"), "Regular text", "Text label should have correct value");
|
||||
is(buttons.childNodes[0].getAttribute("image"), "", "Text should have no image");
|
||||
is(buttons.childNodes[0].className, "", "Text should have no class");
|
||||
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
is(buttons.childNodes[1].nodeName, "button", "Link should be a <button>");
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
|
||||
is(buttons.childNodes[1].className, "button-link", "Check link class");
|
||||
|
||||
is(buttons.childNodes[2].nodeName, "button", "Button 1 should be a <button>");
|
||||
is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
|
||||
is(buttons.childNodes[2].className, "", "Button 1 should have no class");
|
||||
|
||||
is(buttons.childNodes[3].nodeName, "button", "Button 2 should be a <button>");
|
||||
is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
is(buttons.childNodes[3].className, "button-primary", "Check button 2 class");
|
||||
|
||||
let promiseHidden = promisePanelElementHidden(window, popup);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[0], {}, window);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[2], {}, window);
|
||||
yield promiseHidden;
|
||||
|
||||
ok(true, "Popup should close automatically");
|
||||
|
@ -81,7 +95,7 @@ let tests = [
|
|||
|
||||
let buttons = gContentWindow.makeButtons();
|
||||
|
||||
yield showInfoPromise("urlbar", "another title", "moar text", "./image.png", buttons);
|
||||
yield showInfoPromise("siteIdentity", "another title", "moar text", "./image.png", buttons);
|
||||
|
||||
is(title.textContent, "another title", "Popup should have correct title");
|
||||
is(desc.textContent, "moar text", "Popup should have correct description text");
|
||||
|
@ -91,16 +105,20 @@ let tests = [
|
|||
is(icon.src, imageURL, "Popup should have correct icon shown");
|
||||
|
||||
buttons = document.getElementById("UITourTooltipButtons");
|
||||
is(buttons.childElementCount, 2, "Popup should have two buttons");
|
||||
is(buttons.childElementCount, 4, "Popup should have four buttons");
|
||||
|
||||
is(buttons.childNodes[0].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[0].getAttribute("image"), "", "First button should have no image");
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Link", "Link should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), "", "Link should have no image");
|
||||
ok(buttons.childNodes[1].classList.contains("button-link"), "Link should have button-link class");
|
||||
|
||||
is(buttons.childNodes[1].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[1].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
is(buttons.childNodes[2].getAttribute("label"), "Button 1", "First button should have correct label");
|
||||
is(buttons.childNodes[2].getAttribute("image"), "", "First button should have no image");
|
||||
|
||||
is(buttons.childNodes[3].getAttribute("label"), "Button 2", "Second button should have correct label");
|
||||
is(buttons.childNodes[3].getAttribute("image"), imageURL, "Second button should have correct image");
|
||||
|
||||
let promiseHidden = promisePanelElementHidden(window, popup);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[1], {}, window);
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.childNodes[3], {}, window);
|
||||
yield promiseHidden;
|
||||
|
||||
ok(true, "Popup should close automatically");
|
||||
|
|
|
@ -61,6 +61,7 @@ let tests = [
|
|||
"readerMode-urlBar",
|
||||
"search",
|
||||
"searchIcon",
|
||||
"siteIdentity",
|
||||
"urlbar",
|
||||
...searchEngineTargets(),
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
|
@ -93,6 +94,7 @@ let tests = [
|
|||
"readerMode-urlBar",
|
||||
"search",
|
||||
"searchIcon",
|
||||
"siteIdentity",
|
||||
"urlbar",
|
||||
...searchEngineTargets(),
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
|
@ -128,6 +130,7 @@ let tests = [
|
|||
"privateWindow",
|
||||
"quit",
|
||||
"readerMode-urlBar",
|
||||
"siteIdentity",
|
||||
"urlbar",
|
||||
...(hasWebIDE ? ["webide"] : [])
|
||||
]);
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
const PREF_INTRO_COUNT = "privacy.trackingprotection.introCount";
|
||||
const PREF_TP_ENABLED = "privacy.trackingprotection.enabled";
|
||||
const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/benignPage.html";
|
||||
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html";
|
||||
const TOOLTIP_PANEL = document.getElementById("UITourTooltip");
|
||||
const TOOLTIP_ANCHOR = document.getElementById("page-proxy-favicon");
|
||||
|
||||
let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
Services.prefs.clearUserPref(PREF_TP_ENABLED);
|
||||
Services.prefs.clearUserPref(PREF_INTRO_COUNT);
|
||||
});
|
||||
|
||||
function allowOneIntro() {
|
||||
Services.prefs.setIntPref(PREF_INTRO_COUNT, TrackingProtection.MAX_INTROS - 1);
|
||||
}
|
||||
|
||||
add_task(function* setup_test() {
|
||||
yield UrlClassifierTestUtils.addTestTrackers();
|
||||
Services.prefs.setBoolPref(PREF_TP_ENABLED, true);
|
||||
});
|
||||
|
||||
add_task(function* test_benignPage() {
|
||||
info("Load a test page not containing tracking elements");
|
||||
allowOneIntro();
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: BENIGN_PAGE}, function*() {
|
||||
yield waitForConditionPromise(() => {
|
||||
return is_visible(TOOLTIP_PANEL);
|
||||
}, "Info panel shouldn't appear on a benign page").
|
||||
then(() => ok(false, "Info panel shouldn't appear"),
|
||||
() => {
|
||||
ok(true, "Info panel didn't appear on a benign page");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_trackingPages() {
|
||||
info("Load a test page containing tracking elements");
|
||||
allowOneIntro();
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: TRACKING_PAGE}, function*() {
|
||||
yield new Promise((resolve, reject) => {
|
||||
waitForPopupAtAnchor(TOOLTIP_PANEL, TOOLTIP_ANCHOR, resolve,
|
||||
"Intro panel should appear");
|
||||
});
|
||||
|
||||
is(Services.prefs.getIntPref(PREF_INTRO_COUNT), TrackingProtection.MAX_INTROS, "Check intro count increased");
|
||||
|
||||
let step2URL = Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL") +
|
||||
"#step2";
|
||||
let buttons = document.getElementById("UITourTooltipButtons");
|
||||
|
||||
info("Click the step text and nothing should happen");
|
||||
let tabCount = gBrowser.tabs.length;
|
||||
yield EventUtils.synthesizeMouseAtCenter(buttons.children[0], {});
|
||||
is(gBrowser.tabs.length, tabCount, "Same number of tabs should be open");
|
||||
|
||||
info("Resetting count to test that viewing the tour prevents future panels");
|
||||
allowOneIntro();
|
||||
|
||||
let panelHiddenPromise = promisePanelElementHidden(window, TOOLTIP_PANEL);
|
||||
let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, step2URL);
|
||||
info("Clicking the main button");
|
||||
EventUtils.synthesizeMouseAtCenter(buttons.children[1], {});
|
||||
let tab = yield tabPromise;
|
||||
is(Services.prefs.getIntPref(PREF_INTRO_COUNT), TrackingProtection.MAX_INTROS,
|
||||
"Check intro count is at the max after opening step 2");
|
||||
is(gBrowser.tabs.length, tabCount + 1, "Tour step 2 tab opened");
|
||||
yield panelHiddenPromise;
|
||||
ok(true, "Panel hid when the button was clicked");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
info("Open another tracking page and make sure we don't show the panel again");
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: TRACKING_PAGE}, function*() {
|
||||
yield waitForConditionPromise(() => {
|
||||
return is_visible(TOOLTIP_PANEL);
|
||||
}, "Info panel shouldn't appear more than MAX_INTROS").
|
||||
then(() => ok(false, "Info panel shouldn't appear again"),
|
||||
() => {
|
||||
ok(true, "Info panel didn't appear more than MAX_INTROS on tracking pages");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -17,8 +17,11 @@
|
|||
// Defined in content to avoid weird issues when crossing between chrome/content.
|
||||
function makeButtons() {
|
||||
return [
|
||||
{label: "Regular text", style: "text"},
|
||||
{label: "Link", callback: makeCallback("link"), style: "link"},
|
||||
{label: "Button 1", callback: makeCallback("button1")},
|
||||
{label: "Button 2", callback: makeCallback("button2"), icon: "image.png"}
|
||||
{label: "Button 2", callback: makeCallback("button2"), icon: "image.png",
|
||||
style: "primary"}
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -336,6 +336,13 @@ identity.mixed_active_loaded2=This website contains interactive content that isn
|
|||
|
||||
identity.unknown.tooltip=This website does not supply identity information.
|
||||
|
||||
trackingProtection.intro.title=How Tracking Protection works
|
||||
# LOCALIZATION NOTE (trackingProtection.intro.description): %S is brandShortName
|
||||
trackingProtection.intro.description=When the shield is visible, that means Firefox is actively blocking content that tracks you.
|
||||
# LOCALIZATION NOTE (trackingProtection.intro.step1of3): Indicates that the intro panel is step one of three in a tour.
|
||||
trackingProtection.intro.step1of3=1 of 3
|
||||
trackingProtection.intro.nextButton.label=Next
|
||||
|
||||
# Edit Bookmark UI
|
||||
editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
|
||||
editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
|
||||
|
|
|
@ -86,10 +86,12 @@
|
|||
padding: 2em 15px;
|
||||
}
|
||||
|
||||
#UITourTooltipButtons > label,
|
||||
#UITourTooltipButtons > button {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
#UITourTooltipButtons > label:first-child,
|
||||
#UITourTooltipButtons > button:first-child {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
@ -100,6 +102,7 @@
|
|||
-moz-margin-end: 5px;
|
||||
}
|
||||
|
||||
#UITourTooltipButtons > label,
|
||||
#UITourTooltipButtons > button .button-text {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
@ -122,6 +125,7 @@
|
|||
box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
#UITourTooltipButtons > label,
|
||||
#UITourTooltipButtons > button.button-link {
|
||||
-moz-appearance: none;
|
||||
background: transparent;
|
||||
|
|
|
@ -90,6 +90,7 @@ user_pref("browser.safebrowsing.updateURL", "http://%(server)s/safebrowsing-dumm
|
|||
user_pref("browser.safebrowsing.appRepURL", "http://%(server)s/safebrowsing-dummy/update");
|
||||
user_pref("browser.trackingprotection.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
|
||||
user_pref("browser.trackingprotection.updateURL", "http://%(server)s/safebrowsing-dummy/update");
|
||||
user_pref("privacy.trackingprotection.introURL", "http://%(server)s/trackingprotection/tour");
|
||||
// Point update checks to the local testing server for fast failures
|
||||
user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL");
|
||||
user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
|
||||
|
|
Загрузка…
Ссылка в новой задаче