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:
Matthew Noorenberghe 2015-07-15 11:50:40 -07:00
parent dbf261d22c
commit ffc30df2d5
11 changed files with 234 additions and 34 deletions

View File

@ -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.

View File

@ -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);
}),
};

View File

@ -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);
}

View File

@ -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]

View File

@ -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");

View File

@ -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"] : [])
]);

View File

@ -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");
});
});
});

View File

@ -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"}
];
}

View File

@ -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.

View File

@ -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;

View File

@ -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");