Merge fx-team to m-c a=merge CLOSED TREE
@ -219,10 +219,6 @@ const gXPInstallObserver = {
|
||||
acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
|
||||
|
||||
let showNotification = () => {
|
||||
// The download may have been cancelled during the security delay
|
||||
if (!PopupNotifications.getNotification("addon-progress", browser))
|
||||
return;
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab)
|
||||
gBrowser.selectedTab = tab;
|
||||
@ -243,15 +239,20 @@ const gXPInstallObserver = {
|
||||
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
};
|
||||
|
||||
let downloadDuration = 0;
|
||||
let progressNotification = PopupNotifications.getNotification("addon-progress", browser);
|
||||
if (progressNotification)
|
||||
downloadDuration = Date.now() - progressNotification._startTime;
|
||||
let securityDelay = Services.prefs.getIntPref("security.dialog_enable_delay") - downloadDuration;
|
||||
if (securityDelay > 0)
|
||||
setTimeout(showNotification, securityDelay);
|
||||
else
|
||||
showNotification();
|
||||
if (progressNotification) {
|
||||
let downloadDuration = Date.now() - progressNotification._startTime;
|
||||
let securityDelay = Services.prefs.getIntPref("security.dialog_enable_delay") - downloadDuration;
|
||||
if (securityDelay > 0) {
|
||||
setTimeout(() => {
|
||||
// The download may have been cancelled during the security delay
|
||||
if (PopupNotifications.getNotification("addon-progress", browser))
|
||||
showNotification();
|
||||
}, securityDelay);
|
||||
break;
|
||||
}
|
||||
}
|
||||
showNotification();
|
||||
break; }
|
||||
case "addon-install-complete": {
|
||||
let needsRestart = installInfo.installs.some(function(i) {
|
||||
|
@ -262,16 +262,19 @@ input[type=button] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.newtab-site[type=related] .newtab-suggested {
|
||||
.newtab-site[suggested] .newtab-suggested {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.sponsored-explain,
|
||||
.sponsored-explain a {
|
||||
.sponsored-explain a,
|
||||
.suggested-explain,
|
||||
.suggested-explain a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sponsored-explain {
|
||||
.sponsored-explain,
|
||||
.suggested-explain {
|
||||
background-color: rgba(51, 51, 51, 0.95);
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
@ -283,7 +286,8 @@ input[type=button] {
|
||||
}
|
||||
|
||||
#newtab-intro-panel input,
|
||||
.sponsored-explain input {
|
||||
.sponsored-explain input,
|
||||
.suggested-explain input {
|
||||
background-size: 18px;
|
||||
height: 18px;
|
||||
opacity: 1;
|
||||
|
@ -132,6 +132,7 @@ Site.prototype = {
|
||||
this.node.setAttribute("type", this.link.type);
|
||||
|
||||
if (this.link.targetedSite) {
|
||||
this.node.setAttribute("suggested", true);
|
||||
let targetedSite = `<strong> ${this.link.targetedSite} </strong>`;
|
||||
this._querySelector(".newtab-suggested").innerHTML =
|
||||
`<div class='newtab-suggested-bounds'> ${newTabString("suggested.button", [targetedSite])} </div>`;
|
||||
@ -233,24 +234,24 @@ Site.prototype = {
|
||||
.add(aIndex);
|
||||
},
|
||||
|
||||
_toggleSponsored: function() {
|
||||
let button = this._querySelector(".newtab-sponsored");
|
||||
_toggleLegalText: function(buttonClass, explanationTextClass) {
|
||||
let button = this._querySelector(buttonClass);
|
||||
if (button.hasAttribute("active")) {
|
||||
let explain = this._querySelector(".sponsored-explain");
|
||||
let explain = this._querySelector(explanationTextClass);
|
||||
explain.parentNode.removeChild(explain);
|
||||
|
||||
button.removeAttribute("active");
|
||||
}
|
||||
else {
|
||||
let explain = document.createElementNS(HTML_NAMESPACE, "div");
|
||||
explain.className = "sponsored-explain";
|
||||
explain.className = explanationTextClass.slice(1); // Slice off the first character, '.'
|
||||
this.node.appendChild(explain);
|
||||
|
||||
let link = '<a href="' + TILES_EXPLAIN_LINK + '">' +
|
||||
newTabString("learn.link") + "</a>";
|
||||
let type = this.node.getAttribute("type");
|
||||
let type = this.node.getAttribute("suggested") ? "suggested" : this.node.getAttribute("type");
|
||||
let icon = '<input type="button" class="newtab-control newtab-' +
|
||||
(type == "sponsored" ? "control-block" : "customize") + '"/>';
|
||||
(type == "enhanced" ? "customize" : "control-block") + '"/>';
|
||||
explain.innerHTML = newTabString(type + ".explain", [icon, link]);
|
||||
|
||||
button.setAttribute("active", "true");
|
||||
@ -279,14 +280,11 @@ Site.prototype = {
|
||||
else if (target.parentElement.classList.contains("sponsored-explain")) {
|
||||
action = "sponsored_link";
|
||||
}
|
||||
else if (target.parentElement.classList.contains("suggested-explain")) {
|
||||
action = "suggested_link";
|
||||
}
|
||||
// Only handle primary clicks for the remaining targets
|
||||
else if (button == 0) {
|
||||
if (target.parentElement.classList.contains("newtab-suggested") ||
|
||||
target.classList.contains("newtab-suggested")) {
|
||||
// Suggested explanation text should do nothing when clicked and
|
||||
// the link in the suggested explanation should act as default.
|
||||
return;
|
||||
}
|
||||
aEvent.preventDefault();
|
||||
if (target.classList.contains("newtab-control-block")) {
|
||||
this.block();
|
||||
@ -294,9 +292,16 @@ Site.prototype = {
|
||||
}
|
||||
else if (target.classList.contains("sponsored-explain") ||
|
||||
target.classList.contains("newtab-sponsored")) {
|
||||
this._toggleSponsored();
|
||||
this._toggleLegalText(".newtab-sponsored", ".sponsored-explain");
|
||||
action = "sponsored";
|
||||
}
|
||||
else if (target.classList.contains("suggested-explain") ||
|
||||
target.classList.contains("newtab-suggested-bounds") ||
|
||||
target.parentElement.classList.contains("newtab-suggested-bounds") ||
|
||||
target.classList.contains("newtab-suggested")) {
|
||||
this._toggleLegalText(".newtab-suggested", ".suggested-explain");
|
||||
action = "suggested";
|
||||
}
|
||||
else if (pinned) {
|
||||
this.unpin();
|
||||
action = "unpin";
|
||||
|
@ -5523,8 +5523,6 @@
|
||||
if (this._selectedPanel != panel) {
|
||||
var event = document.createEvent("Events");
|
||||
event.initEvent("select", true, true);
|
||||
event.fromTab = fromTab;
|
||||
event.toTab = toTab;
|
||||
this.dispatchEvent(event);
|
||||
|
||||
this._selectedIndex = val;
|
||||
|
@ -25,45 +25,7 @@ function typeAndSubmitAndStop(url) {
|
||||
URLBarSetURI();
|
||||
is(gURLBar.textValue, gURLBar.trimValue(url), "location bar reflects loading page");
|
||||
|
||||
let promise = waitForDocLoadAndStopIt();
|
||||
let promise = waitForDocLoadAndStopIt(url);
|
||||
gURLBar.handleCommand();
|
||||
return promise;
|
||||
}
|
||||
|
||||
function waitForDocLoadAndStopIt() {
|
||||
function content_script() {
|
||||
const {interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let progressListener = {
|
||||
onStateChange(webProgress, req, flags, status) {
|
||||
if (flags & Ci.nsIWebProgressListener.STATE_START) {
|
||||
wp.removeProgressListener(progressListener);
|
||||
|
||||
/* Hammer time. */
|
||||
content.stop();
|
||||
|
||||
/* Let the parent know we're done. */
|
||||
sendAsyncMessage("{MSG}");
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"])
|
||||
};
|
||||
|
||||
let wp = docShell.QueryInterface(Ci.nsIWebProgress);
|
||||
wp.addProgressListener(progressListener, wp.NOTIFY_ALL);
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
const MSG = "test:waitForDocLoadAndStopIt";
|
||||
const SCRIPT = content_script.toString().replace("{MSG}", MSG);
|
||||
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + SCRIPT + ")();", true);
|
||||
mm.addMessageListener(MSG, function onComplete() {
|
||||
mm.removeMessageListener(MSG, onComplete);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -379,19 +379,35 @@ function waitForDocLoadAndStopIt(aExpectedURL, aBrowser=gBrowser.selectedBrowser
|
||||
let progressListener = {
|
||||
onStateChange: function (webProgress, req, flags, status) {
|
||||
dump("waitForDocLoadAndStopIt: onStateChange " + flags.toString(16) + ": " + req.name + "\n");
|
||||
let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
|
||||
Ci.nsIWebProgressListener.STATE_START;
|
||||
if (((flags & docStart) == docStart) && webProgress.isTopLevel) {
|
||||
dump("waitForDocLoadAndStopIt: Document start: " +
|
||||
req.QueryInterface(Ci.nsIChannel).URI.spec + "\n");
|
||||
req.cancel(Components.results.NS_ERROR_FAILURE);
|
||||
|
||||
if (webProgress.isTopLevel &&
|
||||
flags & Ci.nsIWebProgressListener.STATE_START) {
|
||||
wp.removeProgressListener(progressListener);
|
||||
sendAsyncMessage("Test:WaitForDocLoadAndStopIt", { uri: req.originalURI.spec });
|
||||
|
||||
let chan = req.QueryInterface(Ci.nsIChannel);
|
||||
dump(`waitForDocLoadAndStopIt: Document start: ${chan.URI.spec}\n`);
|
||||
|
||||
/* Hammer time. */
|
||||
content.stop();
|
||||
|
||||
/* Let the parent know we're done. */
|
||||
sendAsyncMessage("Test:WaitForDocLoadAndStopIt", { uri: chan.originalURI.spec });
|
||||
}
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI(["nsISupportsWeakReference"])
|
||||
};
|
||||
wp.addProgressListener(progressListener, wp.NOTIFY_ALL);
|
||||
wp.addProgressListener(progressListener, wp.NOTIFY_STATE_WINDOW);
|
||||
|
||||
/**
|
||||
* As |this| is undefined and we can't extend |docShell|, adding an unload
|
||||
* event handler is the easiest way to ensure the weakly referenced
|
||||
* progress listener is kept alive as long as necessary.
|
||||
*/
|
||||
addEventListener("unload", function () {
|
||||
try {
|
||||
wp.removeProgressListener(progressListener);
|
||||
} catch (e) { /* Will most likely fail. */ }
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -4,7 +4,7 @@
|
||||
const PRELOAD_PREF = "browser.newtab.preload";
|
||||
|
||||
gDirectorySource = "data:application/json," + JSON.stringify({
|
||||
"en-US": [{
|
||||
"directory": [{
|
||||
url: "http://example.com/",
|
||||
enhancedImageURI: "data:image/png;base64,helloWORLD",
|
||||
title: "title",
|
||||
|
@ -4,7 +4,7 @@
|
||||
const PRELOAD_PREF = "browser.newtab.preload";
|
||||
|
||||
gDirectorySource = "data:application/json," + JSON.stringify({
|
||||
"en-US": [{
|
||||
"directory": [{
|
||||
url: "http://example.com/organic",
|
||||
type: "organic"
|
||||
}, {
|
||||
|
@ -1117,7 +1117,8 @@ this.MozLoopService = {
|
||||
if (window) {
|
||||
window.LoopUI.showNotification({
|
||||
sound: "room-joined",
|
||||
title: room.roomName,
|
||||
// Fallback to the brand short name if the roomName isn't available.
|
||||
title: room.roomName || MozLoopServiceInternal.localizedStrings.get("clientShortname2"),
|
||||
message: MozLoopServiceInternal.localizedStrings.get("rooms_room_joined_label"),
|
||||
selectTab: "rooms"
|
||||
});
|
||||
|
@ -373,7 +373,7 @@ loop.shared.actions = (function() {
|
||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
||||
*/
|
||||
SetupRoomInfo: Action.define("setupRoomInfo", {
|
||||
roomName: String,
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomToken: String,
|
||||
roomUrl: String
|
||||
@ -386,7 +386,7 @@ loop.shared.actions = (function() {
|
||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
||||
*/
|
||||
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
||||
roomName: String,
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
}),
|
||||
|
@ -37,7 +37,7 @@ loop.store = loop.store || {};
|
||||
var roomSchema = {
|
||||
roomToken: String,
|
||||
roomUrl: String,
|
||||
roomName: String,
|
||||
// roomName: String - Optional.
|
||||
maxSize: Number,
|
||||
participants: Array,
|
||||
ctime: Number
|
||||
|
@ -93,7 +93,6 @@ loop.StandaloneMozLoop = (function(mozL10n) {
|
||||
try {
|
||||
// We currently only require things we need rather than everything possible.
|
||||
callback(null, validate(responseData, {
|
||||
roomName: String,
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
}));
|
||||
|
@ -45,14 +45,11 @@
|
||||
<body><![CDATA[
|
||||
let target = aEvent.target;
|
||||
|
||||
// Don't draw the drop indicator outside of markers.
|
||||
// The markers are hidden, since otherwise sometimes popups acquire
|
||||
// scrollboxes on OS X, so we can't use them directly.
|
||||
let firstChildTop = this._startMarker.nextSibling.boxObject.y;
|
||||
let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
|
||||
this._endMarker.previousSibling.boxObject.height;
|
||||
let betweenMarkers = target.boxObject.y >= firstChildTop ||
|
||||
target.boxObject.y <= lastChildBottom;
|
||||
// Don't draw the drop indicator outside of markers or if current
|
||||
// node is not a Places node.
|
||||
let betweenMarkers =
|
||||
(this._startMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_FOLLOWING) &&
|
||||
(this._endMarker.compareDocumentPosition(target) & Node.DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
// Hide the dropmarker if current node is not a Places node.
|
||||
return !(target && target._placesNode && betweenMarkers);
|
||||
|
@ -265,7 +265,10 @@ MockClient.prototype = {
|
||||
return new MockResponse(200, item);
|
||||
},
|
||||
|
||||
delete(body, routeMatch) {
|
||||
// There's a bug in pre-39's ES strict mode around forbidding the
|
||||
// redefinition of reserved keywords that flags defining `delete` on an
|
||||
// object as a syntax error. This weird syntax works around that.
|
||||
["delete"](body, routeMatch) {
|
||||
let id = routeMatch[1];
|
||||
let item = this.itemByID(id);
|
||||
if (!item) {
|
||||
|
@ -43,7 +43,7 @@ function test() {
|
||||
}
|
||||
|
||||
function testChromeActor() {
|
||||
gClient.attachProcess().then(aResponse => {
|
||||
gClient.getProcess().then(aResponse => {
|
||||
gClient.addListener("newGlobal", onNewGlobal);
|
||||
gClient.addListener("newSource", onNewSource);
|
||||
|
||||
|
@ -129,13 +129,13 @@ let onConnectionReady = Task.async(function*(aType, aTraits) {
|
||||
let gParent = document.getElementById("globalActors");
|
||||
|
||||
// Build the Remote Process button
|
||||
// If Fx<37, tab actors were used to be exposed on RootActor
|
||||
// but in Fx>=37, chrome is debuggable via attachProcess() and ChromeActor
|
||||
// If Fx<39, tab actors were used to be exposed on RootActor
|
||||
// but in Fx>=39, chrome is debuggable via getProcess() and ChromeActor
|
||||
if (globals.consoleActor || gClient.mainRoot.traits.allowChromeProcess) {
|
||||
let a = document.createElement("a");
|
||||
a.onclick = function() {
|
||||
if (gClient.mainRoot.traits.allowChromeProcess) {
|
||||
gClient.attachProcess()
|
||||
gClient.getProcess()
|
||||
.then(aResponse => {
|
||||
openToolbox(aResponse.form, true);
|
||||
});
|
||||
|
@ -749,7 +749,7 @@ let gDevToolsBrowser = {
|
||||
return;
|
||||
}
|
||||
// Otherwise, arbitrary connect to the unique content process.
|
||||
client.attachProcess(contentProcesses[0].id)
|
||||
client.getProcess(contentProcesses[0].id)
|
||||
.then(response => {
|
||||
let options = {
|
||||
form: response.form,
|
||||
|
@ -426,26 +426,9 @@ TabTarget.prototype = {
|
||||
|
||||
if (this.isLocalTab) {
|
||||
this._client.connect((aType, aTraits) => {
|
||||
this._client.listTabs(aResponse => {
|
||||
this._root = aResponse;
|
||||
|
||||
if (this.window) {
|
||||
let windowUtils = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let outerWindow = windowUtils.outerWindowID;
|
||||
aResponse.tabs.some((tab) => {
|
||||
if (tab.outerWindowID === outerWindow) {
|
||||
this._form = tab;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!this._form) {
|
||||
this._form = aResponse.tabs[aResponse.selected];
|
||||
}
|
||||
this._client.getTab({ tab: this.tab })
|
||||
.then(aResponse => {
|
||||
this._form = aResponse.tab;
|
||||
attachTab();
|
||||
});
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ support-files =
|
||||
[browser_toolbox_getpanelwhenready.js]
|
||||
[browser_toolbox_highlight.js]
|
||||
[browser_toolbox_hosts.js]
|
||||
[browser_toolbox_hosts_size.js]
|
||||
[browser_toolbox_options.js]
|
||||
[browser_toolbox_options_disable_buttons.js]
|
||||
[browser_toolbox_options_disable_cache-01.js]
|
||||
|
@ -0,0 +1,69 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that getPanelWhenReady returns the correct panel in promise
|
||||
// resolutions regardless of whether it has opened first.
|
||||
|
||||
const URL = "data:text/html;charset=utf8,test for host sizes";
|
||||
|
||||
add_task(function*() {
|
||||
// Set size prefs to make the hosts way too big, so that the size has
|
||||
// to be clamped to fit into the browser window.
|
||||
Services.prefs.setIntPref("devtools.toolbox.footer.height", 10000);
|
||||
Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 10000);
|
||||
|
||||
let tab = yield addTab(URL);
|
||||
let nbox = gBrowser.getNotificationBox();
|
||||
let {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
|
||||
let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
|
||||
|
||||
is (nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
|
||||
is (nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
|
||||
|
||||
let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
|
||||
is (iframe.clientHeight, nboxHeight - 10, "The iframe fits within the available space ");
|
||||
|
||||
yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
|
||||
iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
|
||||
iframe.style.minWidth = "1px"; // Disable the min width set in css
|
||||
is (iframe.clientWidth, nboxWidth - 10, "The iframe fits within the available space");
|
||||
|
||||
yield cleanup(toolbox);
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
// Set size prefs to something reasonable, so we can check to make sure
|
||||
// they are being set properly.
|
||||
Services.prefs.setIntPref("devtools.toolbox.footer.height", 100);
|
||||
Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 100);
|
||||
|
||||
let tab = yield addTab(URL);
|
||||
let nbox = gBrowser.getNotificationBox();
|
||||
let {clientHeight: nboxHeight, clientWidth: nboxWidth} = nbox;
|
||||
let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
|
||||
|
||||
is (nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
|
||||
is (nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
|
||||
|
||||
let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
|
||||
is (iframe.clientHeight, 100, "The iframe is resized properly");
|
||||
|
||||
yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
|
||||
iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
|
||||
iframe.style.minWidth = "1px"; // Disable the min width set in css
|
||||
is (iframe.clientWidth, 100, "The iframe is resized properly");
|
||||
|
||||
yield cleanup(toolbox);
|
||||
});
|
||||
|
||||
function* cleanup(toolbox) {
|
||||
Services.prefs.clearUserPref("devtools.toolbox.host");
|
||||
Services.prefs.clearUserPref("devtools.toolbox.footer.height");
|
||||
Services.prefs.clearUserPref("devtools.toolbox.sidebar.width");
|
||||
yield toolbox.destroy();
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
@ -54,11 +54,64 @@ function connect() {
|
||||
gTabActor1 = response.tabs.filter(a => a.url === TAB_URL_1)[0];
|
||||
gTabActor2 = response.tabs.filter(a => a.url === TAB_URL_2)[0];
|
||||
|
||||
checkSelectedTabActor();
|
||||
checkGetTab();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkGetTab() {
|
||||
gClient.getTab({tab: gTab1})
|
||||
.then(response => {
|
||||
is(JSON.stringify(gTabActor1), JSON.stringify(response.tab),
|
||||
"getTab returns the same tab grip for first tab");
|
||||
})
|
||||
.then(() => {
|
||||
let filter = {};
|
||||
// Filter either by tabId or outerWindowID,
|
||||
// if we are running tests OOP or not.
|
||||
if (gTab1.linkedBrowser.frameLoader.tabParent) {
|
||||
filter.tabId = gTab1.linkedBrowser.frameLoader.tabParent.tabId;
|
||||
} else {
|
||||
let windowUtils = gTab1.linkedBrowser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
filter.outerWindowID = windowUtils.outerWindowID;
|
||||
}
|
||||
return gClient.getTab(filter);
|
||||
})
|
||||
.then(response => {
|
||||
is(JSON.stringify(gTabActor1), JSON.stringify(response.tab),
|
||||
"getTab returns the same tab grip when filtering by tabId/outerWindowID");
|
||||
})
|
||||
.then(() => gClient.getTab({tab: gTab2}))
|
||||
.then(response => {
|
||||
is(JSON.stringify(gTabActor2), JSON.stringify(response.tab),
|
||||
"getTab returns the same tab grip for second tab");
|
||||
})
|
||||
.then(checkGetTabFailures);
|
||||
}
|
||||
|
||||
function checkGetTabFailures() {
|
||||
gClient.getTab({ tabId: -999 })
|
||||
.then(
|
||||
response => ok(false, "getTab unexpectedly succeed with a wrong tabId"),
|
||||
response => {
|
||||
is(response.error, "noTab");
|
||||
is(response.message, "Unable to find tab with tabId '-999'");
|
||||
}
|
||||
)
|
||||
.then(() => gClient.getTab({ outerWindowID: -999 }))
|
||||
.then(
|
||||
response => ok(false, "getTab unexpectedly succeed with a wrong outerWindowID"),
|
||||
response => {
|
||||
is(response.error, "noTab");
|
||||
is(response.message, "Unable to find tab with outerWindowID '-999'");
|
||||
}
|
||||
)
|
||||
.then(checkSelectedTabActor);
|
||||
|
||||
}
|
||||
|
||||
function checkSelectedTabActor() {
|
||||
// Send a naive request to the second tab actor
|
||||
// to check if it works
|
||||
|
@ -166,7 +166,7 @@ function getChromeActors(callback)
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(() => {
|
||||
client.attachProcess().then(response => {
|
||||
client.getProcess().then(response => {
|
||||
callback(client, response.form);
|
||||
});
|
||||
});
|
||||
|
@ -48,15 +48,18 @@ BottomHost.prototype = {
|
||||
|
||||
let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
|
||||
let ownerDocument = gBrowser.ownerDocument;
|
||||
this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);
|
||||
|
||||
this._splitter = ownerDocument.createElement("splitter");
|
||||
this._splitter.setAttribute("class", "devtools-horizontal-splitter");
|
||||
|
||||
this.frame = ownerDocument.createElement("iframe");
|
||||
this.frame.className = "devtools-toolbox-bottom-iframe";
|
||||
this.frame.height = Services.prefs.getIntPref(this.heightPref);
|
||||
this.frame.height = Math.min(
|
||||
Services.prefs.getIntPref(this.heightPref),
|
||||
this._nbox.clientHeight - 10 // Always show at least some page content
|
||||
);
|
||||
|
||||
this._nbox = gBrowser.getNotificationBox(this.hostTab.linkedBrowser);
|
||||
this._nbox.appendChild(this._splitter);
|
||||
this._nbox.appendChild(this.frame);
|
||||
|
||||
@ -131,15 +134,19 @@ SidebarHost.prototype = {
|
||||
|
||||
let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
|
||||
let ownerDocument = gBrowser.ownerDocument;
|
||||
this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);
|
||||
|
||||
this._splitter = ownerDocument.createElement("splitter");
|
||||
this._splitter.setAttribute("class", "devtools-side-splitter");
|
||||
|
||||
this.frame = ownerDocument.createElement("iframe");
|
||||
this.frame.className = "devtools-toolbox-side-iframe";
|
||||
this.frame.width = Services.prefs.getIntPref(this.widthPref);
|
||||
|
||||
this._sidebar = gBrowser.getSidebarContainer(this.hostTab.linkedBrowser);
|
||||
this.frame.width = Math.min(
|
||||
Services.prefs.getIntPref(this.widthPref),
|
||||
this._sidebar.clientWidth - 10 // Always show at least some page content
|
||||
);
|
||||
|
||||
this._sidebar.appendChild(this._splitter);
|
||||
this._sidebar.appendChild(this.frame);
|
||||
|
||||
|
@ -41,7 +41,7 @@ let connect = Task.async(function*() {
|
||||
openToolbox({ form: addonActor, chrome: true, isTabActor: false });
|
||||
});
|
||||
} else {
|
||||
gClient.attachProcess().then(aResponse => {
|
||||
gClient.getProcess().then(aResponse => {
|
||||
openToolbox({ form: aResponse.form, chrome: true });
|
||||
});
|
||||
}
|
||||
|
@ -317,14 +317,16 @@ MarkupView.prototype = {
|
||||
},
|
||||
|
||||
_briefBoxModelTimer: null,
|
||||
_brieflyShowBoxModel: function(nodeFront) {
|
||||
let win = this._frame.contentWindow;
|
||||
|
||||
_clearBriefBoxModelTimer: function() {
|
||||
if (this._briefBoxModelTimer) {
|
||||
clearTimeout(this._briefBoxModelTimer);
|
||||
this._briefBoxModelTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
_brieflyShowBoxModel: function(nodeFront) {
|
||||
this._clearBriefBoxModelTimer();
|
||||
this._showBoxModel(nodeFront);
|
||||
|
||||
this._briefBoxModelTimer = setTimeout(() => {
|
||||
@ -1379,6 +1381,7 @@ MarkupView.prototype = {
|
||||
// We ignore the promise that |_hideBoxModel| returns, since we should still
|
||||
// proceed with the rest of destruction if it fails.
|
||||
this._hideBoxModel();
|
||||
this._clearBriefBoxModelTimer();
|
||||
|
||||
this._elt.removeEventListener("click", this._onMouseClick, false);
|
||||
|
||||
|
@ -37,8 +37,11 @@ function test() {
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls)[0], "file://F",
|
||||
"The '.A.E' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls["file://F"].calls).length, 0,
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls["file://F"].calls).length, 1,
|
||||
"The correct number of child calls were calculated for the '.A.E.F' node.");
|
||||
is(Object.keys(root.calls["http://A"].calls["https://E"].calls["file://F"].calls)[0], "app://H",
|
||||
"The '.A.E.F' node's only child call is correct.");
|
||||
|
||||
is(Object.keys(root.calls["http://D"].calls).length, 0,
|
||||
"The correct number of child calls were calculated for the '.D' node.");
|
||||
|
||||
@ -59,6 +62,7 @@ let gSamples = [{
|
||||
{ location: "(root)" },
|
||||
{ location: "chrome://A" },
|
||||
{ location: "resource://B" },
|
||||
{ location: "jar:file://G" },
|
||||
{ location: "http://D" }
|
||||
]
|
||||
}, {
|
||||
@ -67,7 +71,8 @@ let gSamples = [{
|
||||
{ location: "(root)" },
|
||||
{ location: "http://A" },
|
||||
{ location: "https://E" },
|
||||
{ location: "file://F" }
|
||||
{ location: "file://F" },
|
||||
{ location: "app://H" },
|
||||
]
|
||||
}, {
|
||||
time: 5 + 6 + 7 + 8,
|
||||
|
@ -99,14 +99,13 @@ let OverviewView = {
|
||||
* The { startTime, endTime }, in milliseconds.
|
||||
*/
|
||||
setTimeInterval: function(interval, options = {}) {
|
||||
if (this.isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (recording == null) {
|
||||
throw new Error("A recording should be available in order to set the selection.");
|
||||
}
|
||||
if (this.isDisabled()) {
|
||||
return;
|
||||
}
|
||||
let mapStart = () => 0;
|
||||
let mapEnd = () => recording.getDuration();
|
||||
let selection = { start: interval.startTime, end: interval.endTime };
|
||||
@ -123,14 +122,12 @@ let OverviewView = {
|
||||
*/
|
||||
getTimeInterval: function() {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
|
||||
if (this.isDisabled()) {
|
||||
return { startTime: 0, endTime: recording.getDuration() };
|
||||
}
|
||||
|
||||
if (recording == null) {
|
||||
throw new Error("A recording should be available in order to get the selection.");
|
||||
}
|
||||
if (this.isDisabled()) {
|
||||
return { startTime: 0, endTime: recording.getDuration() };
|
||||
}
|
||||
let mapStart = () => 0;
|
||||
let mapEnd = () => recording.getDuration();
|
||||
let selection = this.markersOverview.getMappedSelection({ mapStart, mapEnd });
|
||||
|
@ -64,8 +64,8 @@ function test() {
|
||||
|
||||
is(numberOfRules(), 2, "Should have two rules initially.");
|
||||
|
||||
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
|
||||
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
|
||||
ruleView.on("ruleview-refreshed", function refresh() {
|
||||
ruleView.off("ruleview-refreshed", refresh, false);
|
||||
is(numberOfRules(), 3, "Should have three rules after shrinking.");
|
||||
testGrow();
|
||||
}, false);
|
||||
@ -74,8 +74,8 @@ function test() {
|
||||
}
|
||||
|
||||
function testGrow() {
|
||||
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
|
||||
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
|
||||
ruleView.on("ruleview-refreshed", function refresh() {
|
||||
ruleView.off("ruleview-refreshed", refresh, false);
|
||||
is(numberOfRules(), 2, "Should have two rules after growing.");
|
||||
testEscapeOpensSplitConsole();
|
||||
}, false);
|
||||
|
@ -2156,7 +2156,7 @@ ScratchpadWindow.prototype = Heritage.extend(ScratchpadTab.prototype, {
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(() => {
|
||||
client.attachProcess().then(aResponse => {
|
||||
client.getProcess().then(aResponse => {
|
||||
if (aResponse.error) {
|
||||
reportError("listTabs", aResponse);
|
||||
deferred.reject(aResponse);
|
||||
|
@ -13,8 +13,8 @@ loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
|
||||
loader.lazyRequireGetter(this, "CATEGORY_JIT",
|
||||
"devtools/shared/profiler/global", true);
|
||||
|
||||
const CHROME_SCHEMES = ["chrome://", "resource://"];
|
||||
const CONTENT_SCHEMES = ["http://", "https://", "file://"];
|
||||
const CHROME_SCHEMES = ["chrome://", "resource://", "jar:file://"];
|
||||
const CONTENT_SCHEMES = ["http://", "https://", "file://", "app://"];
|
||||
|
||||
exports.ThreadNode = ThreadNode;
|
||||
exports.FrameNode = FrameNode;
|
||||
|
@ -16,6 +16,7 @@ const {OutputParser} = require("devtools/output-parser");
|
||||
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils");
|
||||
const {parseSingleValue, parseDeclarations} = require("devtools/styleinspector/css-parsing-utils");
|
||||
const overlays = require("devtools/styleinspector/style-inspector-overlays");
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
@ -1152,6 +1153,8 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
|
||||
this.tooltips.addToView();
|
||||
this.highlighters = new overlays.HighlightersOverlay(this);
|
||||
this.highlighters.addToView();
|
||||
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
exports.CssRuleView = CssRuleView;
|
||||
@ -1206,6 +1209,90 @@ CssRuleView.prototype = {
|
||||
popupset.appendChild(this._contextmenu);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an instance of SelectorHighlighter (used to highlight nodes that match
|
||||
* selectors in the rule-view). A new instance is only created the first time
|
||||
* this function is called. The same instance will then be returned.
|
||||
* @return {Promise} Resolves to the instance of the highlighter.
|
||||
*/
|
||||
getSelectorHighlighter: Task.async(function*() {
|
||||
let utils = this.inspector.toolbox.highlighterUtils;
|
||||
if (!utils.supportsCustomHighlighters()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.selectorHighlighter) {
|
||||
return this.selectorHighlighter;
|
||||
}
|
||||
|
||||
try {
|
||||
let h = yield utils.getHighlighterByType("SelectorHighlighter");
|
||||
return this.selectorHighlighter = h;
|
||||
} catch (e) {
|
||||
// The SelectorHighlighter type could not be created in the current target.
|
||||
// It could be an older server, or a XUL page.
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Highlight/unhighlight all the nodes that match a given set of selectors
|
||||
* inside the document of the current selected node.
|
||||
* Only one selector can be highlighted at a time, so calling the method a
|
||||
* second time with a different selector will first unhighlight the previously
|
||||
* highlighted nodes.
|
||||
* Calling the method a second time with the same selector will just
|
||||
* unhighlight the highlighted nodes.
|
||||
*
|
||||
* @param {DOMNode} The icon that was clicked to toggle the selector. The
|
||||
* class 'highlighted' will be added when the selector is highlighted.
|
||||
* @param {String} The selector used to find nodes in the page.
|
||||
*/
|
||||
toggleSelectorHighlighter: function(selectorIcon, selector) {
|
||||
if (this.lastSelectorIcon) {
|
||||
this.lastSelectorIcon.classList.remove("highlighted");
|
||||
}
|
||||
selectorIcon.classList.remove("highlighted");
|
||||
|
||||
this.unhighlightSelector().then(() => {
|
||||
if (selector !== this.highlightedSelector) {
|
||||
this.highlightedSelector = selector;
|
||||
selectorIcon.classList.add("highlighted");
|
||||
this.lastSelectorIcon = selectorIcon;
|
||||
this.highlightSelector(selector).then(() => {
|
||||
this.emit("ruleview-selectorhighlighter-toggled", true);
|
||||
}, Cu.reportError);
|
||||
} else {
|
||||
this.highlightedSelector = null;
|
||||
this.emit("ruleview-selectorhighlighter-toggled", false);
|
||||
}
|
||||
}, Cu.reportError);
|
||||
},
|
||||
|
||||
highlightSelector: Task.async(function*(selector) {
|
||||
let node = this.inspector.selection.nodeFront;
|
||||
|
||||
let highlighter = yield this.getSelectorHighlighter();
|
||||
if (!highlighter) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield highlighter.show(node, {
|
||||
hideInfoBar: true,
|
||||
hideGuides: true,
|
||||
selector
|
||||
});
|
||||
}),
|
||||
|
||||
unhighlightSelector: Task.async(function*() {
|
||||
let highlighter = yield this.getSelectorHighlighter();
|
||||
if (!highlighter) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield highlighter.hide();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update the context menu. This means enabling or disabling menuitems as
|
||||
* appropriate.
|
||||
@ -1610,9 +1697,7 @@ CssRuleView.prototype = {
|
||||
this._createEditors();
|
||||
|
||||
// Notify anyone that cares that we refreshed.
|
||||
var evt = this.doc.createEvent("Events");
|
||||
evt.initEvent("CssRuleViewRefreshed", true, false);
|
||||
this.element.dispatchEvent(evt);
|
||||
this.emit("ruleview-refreshed");
|
||||
return undefined;
|
||||
}).then(null, promiseWarn);
|
||||
},
|
||||
@ -1644,6 +1729,8 @@ CssRuleView.prototype = {
|
||||
* Clear the rule view.
|
||||
*/
|
||||
clear: function() {
|
||||
this.lastSelectorIcon = null;
|
||||
|
||||
this._clearRules();
|
||||
this._viewedElement = null;
|
||||
|
||||
@ -1658,9 +1745,7 @@ CssRuleView.prototype = {
|
||||
* Emits an event that clients can listen to.
|
||||
*/
|
||||
_changed: function() {
|
||||
var evt = this.doc.createEvent("Events");
|
||||
evt.initEvent("CssRuleViewChanged", true, false);
|
||||
this.element.dispatchEvent(evt);
|
||||
this.emit("ruleview-changed");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1836,6 +1921,7 @@ function RuleEditor(aRuleView, aRule) {
|
||||
this.ruleView = aRuleView;
|
||||
this.doc = this.ruleView.doc;
|
||||
this.rule = aRule;
|
||||
|
||||
this.isEditable = !aRule.isSystem;
|
||||
// Flag that blocks updates of the selector and properties when it is
|
||||
// being edited
|
||||
@ -1880,11 +1966,7 @@ RuleEditor.prototype = {
|
||||
return;
|
||||
}
|
||||
let rule = this.rule.domRule;
|
||||
let evt = this.doc.createEvent("CustomEvent");
|
||||
evt.initCustomEvent("CssRuleViewCSSLinkClicked", true, false, {
|
||||
rule: rule,
|
||||
});
|
||||
this.element.dispatchEvent(evt);
|
||||
this.ruleView.emit("ruleview-linked-clicked", rule);
|
||||
}.bind(this));
|
||||
let sourceLabel = this.doc.createElementNS(XUL_NS, "label");
|
||||
sourceLabel.setAttribute("crop", "center");
|
||||
@ -1903,6 +1985,20 @@ RuleEditor.prototype = {
|
||||
class: "ruleview-selectorcontainer"
|
||||
});
|
||||
|
||||
if (this.rule.domRule.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE &&
|
||||
this.rule.domRule.selectors) {
|
||||
let selector = this.rule.domRule.selectors.join(", ");
|
||||
|
||||
let selectorHighlighter = createChild(header, "span", {
|
||||
class: "ruleview-selectorhighlighter" +
|
||||
(this.ruleView.highlightedSelector === selector ? " highlighted": ""),
|
||||
title: CssLogic.l10n("rule.selectorHighlighter.tooltip")
|
||||
});
|
||||
selectorHighlighter.addEventListener("click", () => {
|
||||
this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
|
||||
});
|
||||
}
|
||||
|
||||
this.selectorText = createChild(this.selectorContainer, "span", {
|
||||
class: "ruleview-selector theme-fg-color3"
|
||||
});
|
||||
|
@ -29,14 +29,6 @@ const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
|
||||
const TOOLTIP_IMAGE_TYPE = "image";
|
||||
const TOOLTIP_FONTFAMILY_TYPE = "font-family";
|
||||
|
||||
// Types of existing highlighters
|
||||
const HIGHLIGHTER_TRANSFORM_TYPE = "CssTransformHighlighter";
|
||||
const HIGHLIGHTER_SELECTOR_TYPE = "SelectorHighlighter";
|
||||
const HIGHLIGHTER_TYPES = [
|
||||
HIGHLIGHTER_TRANSFORM_TYPE,
|
||||
HIGHLIGHTER_SELECTOR_TYPE
|
||||
];
|
||||
|
||||
// Types of nodes in the rule/computed-view
|
||||
const VIEW_NODE_SELECTOR_TYPE = exports.VIEW_NODE_SELECTOR_TYPE = 1;
|
||||
const VIEW_NODE_PROPERTY_TYPE = exports.VIEW_NODE_PROPERTY_TYPE = 2;
|
||||
@ -121,25 +113,17 @@ HighlightersOverlay.prototype = {
|
||||
}
|
||||
|
||||
// Choose the type of highlighter required for the hovered node
|
||||
let type, options;
|
||||
let type;
|
||||
if (this._isRuleViewTransform(nodeInfo) ||
|
||||
this._isComputedViewTransform(nodeInfo)) {
|
||||
type = HIGHLIGHTER_TRANSFORM_TYPE;
|
||||
} else if (nodeInfo.type === VIEW_NODE_SELECTOR_TYPE) {
|
||||
type = HIGHLIGHTER_SELECTOR_TYPE;
|
||||
options = {
|
||||
selector: nodeInfo.value,
|
||||
hideInfoBar: true,
|
||||
showOnly: "border",
|
||||
region: "border"
|
||||
};
|
||||
type = "CssTransformHighlighter";
|
||||
}
|
||||
|
||||
if (type) {
|
||||
this.highlighterShown = type;
|
||||
let node = this.view.inspector.selection.nodeFront;
|
||||
this._getHighlighter(type).then(highlighter => {
|
||||
highlighter.show(node, options);
|
||||
highlighter.show(node);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -34,9 +34,9 @@ function RuleViewTool(inspector, window, iframe) {
|
||||
this.onViewRefreshed = this.onViewRefreshed.bind(this);
|
||||
this.onPanelSelected = this.onPanelSelected.bind(this);
|
||||
|
||||
this.view.element.addEventListener("CssRuleViewChanged", this.onPropertyChanged);
|
||||
this.view.element.addEventListener("CssRuleViewRefreshed", this.onViewRefreshed);
|
||||
this.view.element.addEventListener("CssRuleViewCSSLinkClicked", this.onLinkClicked);
|
||||
this.view.on("ruleview-changed", this.onPropertyChanged);
|
||||
this.view.on("ruleview-refreshed", this.onViewRefreshed);
|
||||
this.view.on("ruleview-linked-clicked", this.onLinkClicked);
|
||||
|
||||
this.inspector.selection.on("detached", this.onSelected);
|
||||
this.inspector.selection.on("new-node-front", this.onSelected);
|
||||
@ -104,8 +104,7 @@ RuleViewTool.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
onLinkClicked: function(event) {
|
||||
let rule = event.detail.rule;
|
||||
onLinkClicked: function(e, rule) {
|
||||
let sheet = rule.parentStyleSheet;
|
||||
|
||||
// Chrome stylesheets are not listed in the style editor, so show
|
||||
@ -149,9 +148,9 @@ RuleViewTool.prototype = {
|
||||
this.inspector.target.off("navigate", this.clearUserProperties);
|
||||
this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
|
||||
|
||||
this.view.element.removeEventListener("CssRuleViewCSSLinkClicked", this.onLinkClicked);
|
||||
this.view.element.removeEventListener("CssRuleViewChanged", this.onPropertyChanged);
|
||||
this.view.element.removeEventListener("CssRuleViewRefreshed", this.onViewRefreshed);
|
||||
this.view.off("ruleview-linked-clicked", this.onLinkClicked);
|
||||
this.view.off("ruleview-changed", this.onPropertyChanged);
|
||||
this.view.off("ruleview-refreshed", this.onViewRefreshed);
|
||||
|
||||
this.doc.documentElement.removeChild(this.view.element);
|
||||
|
||||
|
@ -102,6 +102,7 @@ skip-if = e10s # Bug 1090340
|
||||
[browser_ruleview_select-and-copy-styles.js]
|
||||
[browser_ruleview_selector-highlighter_01.js]
|
||||
[browser_ruleview_selector-highlighter_02.js]
|
||||
[browser_ruleview_selector-highlighter_03.js]
|
||||
[browser_ruleview_style-editor-link.js]
|
||||
skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
|
||||
[browser_ruleview_urls-clickable.js]
|
||||
|
@ -57,7 +57,7 @@ function* runTestData(inspector, view, data) {
|
||||
ok(!view.menuitemAddRule.hidden, "Add rule is visible");
|
||||
|
||||
info("Waiting for rule view to change");
|
||||
let onRuleViewChanged = once(view.element, "CssRuleViewChanged");
|
||||
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||
|
||||
info("Adding the new rule");
|
||||
view.menuitemAddRule.click();
|
||||
|
@ -40,7 +40,7 @@ add_task(function*() {
|
||||
ok(!view.menuitemAddRule.hidden, "Add rule is visible");
|
||||
|
||||
info("Waiting for rule view to change");
|
||||
let onRuleViewChanged = once(view.element, "CssRuleViewChanged");
|
||||
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||
|
||||
info("Adding the new rule");
|
||||
view.menuitemAddRule.click();
|
||||
@ -63,7 +63,7 @@ function* testEditSelector(view, name) {
|
||||
editor.value = name;
|
||||
|
||||
info("Waiting for rule view to refresh");
|
||||
let onRuleViewRefresh = once(view.element, "CssRuleViewRefreshed");
|
||||
let onRuleViewRefresh = once(view, "ruleview-refreshed");
|
||||
|
||||
info("Entering the commit key");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
@ -40,7 +40,7 @@ add_task(function*() {
|
||||
ok(!view.menuitemAddRule.hidden, "Add rule is visible");
|
||||
|
||||
info("Waiting for rule view to change");
|
||||
let onRuleViewChanged = once(view.element, "CssRuleViewChanged");
|
||||
let onRuleViewChanged = once(view, "ruleview-changed");
|
||||
|
||||
info("Adding the new rule");
|
||||
view.menuitemAddRule.click();
|
||||
@ -93,7 +93,7 @@ function* testEditSelector(view, name) {
|
||||
editor.input.value = name;
|
||||
|
||||
info("Waiting for rule view to refresh");
|
||||
let onRuleViewRefresh = once(view.element, "CssRuleViewRefreshed");
|
||||
let onRuleViewRefresh = once(view, "ruleview-refreshed");
|
||||
|
||||
info("Entering the commit key");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
@ -78,7 +78,7 @@ function* runTestData(view, {value, commitKey, modifiers, expected}) {
|
||||
if (commitKey === "VK_ESCAPE") {
|
||||
is(propEditor.valueSpan.textContent, expected, "Value is as expected: " + expected);
|
||||
} else {
|
||||
yield once(view.element, "CssRuleViewChanged");
|
||||
yield once(view, "ruleview-changed");
|
||||
is(propEditor.valueSpan.textContent, expected, "Value is as expected: " + expected);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ function* runTestData(inspector, view, data) {
|
||||
"Value is as expected: " + expected);
|
||||
is(idRuleEditor.isEditing, false, "Selector is not being edited.")
|
||||
} else {
|
||||
yield once(view.element, "CssRuleViewRefreshed");
|
||||
yield once(view, "ruleview-refreshed");
|
||||
ok(getRuleViewRule(view, expected),
|
||||
"Rule with " + name + " selector exists.");
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ function* testEditSelector(view, name) {
|
||||
editor.input.value = name;
|
||||
|
||||
info("Waiting for rule view to refresh");
|
||||
let onRuleViewRefresh = once(view.element, "CssRuleViewRefreshed");
|
||||
let onRuleViewRefresh = once(view, "ruleview-refreshed");
|
||||
|
||||
info("Entering the commit key");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
@ -64,7 +64,7 @@ function* testEditSelector(view, name) {
|
||||
editor.input.value = name;
|
||||
|
||||
info("Waiting for rule view to refresh");
|
||||
let onRuleViewRefresh = once(view.element, "CssRuleViewRefreshed");
|
||||
let onRuleViewRefresh = once(view, "ruleview-refreshed");
|
||||
|
||||
info("Entering the commit key");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the selector highlighter is created when hovering over a selector
|
||||
// in the rule view
|
||||
// Test that the selector highlighter is created when clicking on a selector
|
||||
// icon in the rule view.
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
@ -16,29 +16,19 @@ const PAGE_CONTENT = [
|
||||
'Test the selector highlighter'
|
||||
].join("\n");
|
||||
|
||||
let TYPE = "SelectorHighlighter";
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + PAGE_CONTENT);
|
||||
|
||||
let {view: rView} = yield openRuleView();
|
||||
let hs = rView.highlighters;
|
||||
let {view} = yield openRuleView();
|
||||
ok(!view.selectorHighlighter, "No selectorhighlighter exist in the rule-view");
|
||||
|
||||
ok(!hs.highlighters[TYPE], "No highlighter exists in the rule-view (1)");
|
||||
ok(!hs.promises[TYPE], "No highlighter is being created in the rule-view (1)");
|
||||
info("Clicking on a selector icon");
|
||||
let icon = getRuleViewSelectorHighlighterIcon(view, "body, p, td");
|
||||
|
||||
info("Faking a mousemove NOT on a selector");
|
||||
let {valueSpan} = getRuleViewProperty(rView, "body, p, td", "background");
|
||||
hs._onMouseMove({target: valueSpan});
|
||||
ok(!hs.highlighters[TYPE], "No highlighter exists in the rule-view (2)");
|
||||
ok(!hs.promises[TYPE], "No highlighter is being created in the rule-view (2)");
|
||||
let onToggled = view.once("ruleview-selectorhighlighter-toggled");
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {}, view.doc.defaultView);
|
||||
let isVisible = yield onToggled;
|
||||
|
||||
info("Faking a mousemove on the body selector");
|
||||
let selectorContainer = getRuleViewSelector(rView, "body, p, td");
|
||||
// The highlighter appears for individual selectors only
|
||||
let bodySelector = selectorContainer.firstElementChild;
|
||||
hs._onMouseMove({target: bodySelector});
|
||||
ok(hs.promises[TYPE], "The highlighter is being initialized");
|
||||
let h = yield hs.promises[TYPE];
|
||||
is(h, hs.highlighters[TYPE], "The initialized highlighter is the right one");
|
||||
ok(view.selectorHighlighter, "The selectorhighlighter instance was created");
|
||||
ok(isVisible, "The toggle event says the highlighter is visible");
|
||||
});
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the selector highlighter is shown when hovering over a selector
|
||||
// Test that the selector highlighter is shown when clicking on a selector icon
|
||||
// in the rule-view
|
||||
|
||||
// Note that in this test, we mock the highlighter front, merely testing the
|
||||
@ -22,12 +22,10 @@ const PAGE_CONTENT = [
|
||||
'<p>Testing the selector highlighter</p>'
|
||||
].join("\n");
|
||||
|
||||
const TYPE = "SelectorHighlighter";
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + PAGE_CONTENT);
|
||||
|
||||
let {inspector, view: rView} = yield openRuleView();
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
// Mock the highlighter front to get the reference of the NodeFront
|
||||
let HighlighterFront = {
|
||||
@ -47,39 +45,41 @@ add_task(function*() {
|
||||
};
|
||||
|
||||
// Inject the mock highlighter in the rule-view
|
||||
rView.highlighters.promises[TYPE] = {
|
||||
then: function(cb) {
|
||||
cb(HighlighterFront);
|
||||
}
|
||||
};
|
||||
view.selectorHighlighter = HighlighterFront;
|
||||
|
||||
let selectorSpan = getRuleViewSelector(rView, "body").firstElementChild;
|
||||
let icon = getRuleViewSelectorHighlighterIcon(view, "body");
|
||||
|
||||
info("Checking that the HighlighterFront's show/hide methods are called");
|
||||
rView.highlighters._onMouseMove({target: selectorSpan});
|
||||
|
||||
info("Clicking once on the body selector highlighter icon");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(HighlighterFront.isShown, "The highlighter is shown");
|
||||
rView.highlighters._onMouseLeave();
|
||||
|
||||
info("Clicking once again on the body selector highlighter icon");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(!HighlighterFront.isShown, "The highlighter is hidden");
|
||||
|
||||
info("Checking that the right NodeFront reference and options are passed");
|
||||
yield selectNode("p", inspector);
|
||||
selectorSpan = getRuleViewSelector(rView, "p").firstElementChild;
|
||||
rView.highlighters._onMouseMove({target: selectorSpan});
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, "p");
|
||||
|
||||
yield clickSelectorIcon(icon, view);
|
||||
is(HighlighterFront.nodeFront.tagName, "P",
|
||||
"The right NodeFront is passed to the highlighter (1)");
|
||||
is(HighlighterFront.options.selector, "p",
|
||||
"The right selector option is passed to the highlighter (1)");
|
||||
|
||||
yield selectNode("body", inspector);
|
||||
selectorSpan = getRuleViewSelector(rView, "body").firstElementChild;
|
||||
rView.highlighters._onMouseMove({target: selectorSpan});
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, "body");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
is(HighlighterFront.nodeFront.tagName, "BODY",
|
||||
"The right NodeFront is passed to the highlighter (2)");
|
||||
is(HighlighterFront.options.selector, "body",
|
||||
"The right selector option is passed to the highlighter (2)");
|
||||
|
||||
info("Checking that the highlighter gets hidden when hovering somewhere else");
|
||||
let {valueSpan} = getRuleViewProperty(rView, "body", "background");
|
||||
rView.highlighters._onMouseMove({target: valueSpan});
|
||||
ok(!HighlighterFront.isShown, "The highlighter is hidden");
|
||||
});
|
||||
|
||||
function* clickSelectorIcon(icon, view) {
|
||||
let onToggled = view.once("ruleview-selectorhighlighter-toggled");
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {}, view.doc.defaultView);
|
||||
yield onToggled;
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the selector highlighter toggling mechanism works correctly.
|
||||
|
||||
// Note that in this test, we mock the highlighter front, merely testing the
|
||||
// behavior of the style-inspector UI for now
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' div {text-decoration: underline;}',
|
||||
' .node-1 {color: red;}',
|
||||
' .node-2 {color: green;}',
|
||||
'</style>',
|
||||
'<div class="node-1">Node 1</div>',
|
||||
'<div class="node-2">Node 2</div>'
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + PAGE_CONTENT);
|
||||
|
||||
let {inspector, view} = yield openRuleView();
|
||||
|
||||
// Mock the highlighter front.
|
||||
let HighlighterFront = {
|
||||
isShown: false,
|
||||
show: function() {
|
||||
this.isShown = true;
|
||||
},
|
||||
hide: function() {
|
||||
this.isShown = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Inject the mock highlighter in the rule-view
|
||||
view.selectorHighlighter = HighlighterFront;
|
||||
|
||||
info("Select .node-1 and click on the .node-1 selector icon");
|
||||
yield selectNode(".node-1", inspector);
|
||||
let icon = getRuleViewSelectorHighlighterIcon(view, ".node-1");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(HighlighterFront.isShown, "The highlighter is shown");
|
||||
|
||||
info("With .node-1 still selected, click again on the .node-1 selector icon");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(!HighlighterFront.isShown, "The highlighter is now hidden");
|
||||
|
||||
info("With .node-1 still selected, click on the div selector icon");
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, "div");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(HighlighterFront.isShown, "The highlighter is shown again");
|
||||
|
||||
info("With .node-1 still selected, click again on the .node-1 selector icon");
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, ".node-1");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(HighlighterFront.isShown,
|
||||
"The highlighter is shown again since the clicked selector was different");
|
||||
|
||||
info("Selecting .node-2");
|
||||
yield selectNode(".node-2", inspector);
|
||||
ok(HighlighterFront.isShown, "The highlighter is still shown after selection");
|
||||
|
||||
info("With .node-2 selected, click on the div selector icon");
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, "div");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(HighlighterFront.isShown,
|
||||
"The highlighter is shown still since the selected was different");
|
||||
|
||||
info("Switching back to .node-1 and clicking on the div selector");
|
||||
yield selectNode(".node-1", inspector);
|
||||
icon = getRuleViewSelectorHighlighterIcon(view, "div");
|
||||
yield clickSelectorIcon(icon, view);
|
||||
ok(!HighlighterFront.isShown,
|
||||
"The highlighter is hidden now that the same selector was clicked");
|
||||
});
|
||||
|
||||
function* clickSelectorIcon(icon, view) {
|
||||
let onToggled = view.once("ruleview-selectorhighlighter-toggled");
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {}, view.doc.defaultView);
|
||||
yield onToggled;
|
||||
}
|
@ -652,6 +652,18 @@ function getRuleViewSelector(view, selectorText) {
|
||||
return rule.querySelector(".ruleview-selector, .ruleview-selector-matched");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the selectorhighlighter icon DOM element corresponding to
|
||||
* a given selector in the rule-view
|
||||
* @param {CssRuleView} view The instance of the rule-view panel
|
||||
* @param {String} selectorText The selector in the rule-view to look for
|
||||
* @return {DOMNode} The selectorhighlighter icon DOM element
|
||||
*/
|
||||
function getRuleViewSelectorHighlighterIcon(view, selectorText) {
|
||||
let rule = getRuleViewRule(view, selectorText);
|
||||
return rule.querySelector(".ruleview-selectorhighlighter");
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a color change in a given color picker tooltip, and optionally wait
|
||||
* for a given element in the page to have its style changed as a result
|
||||
|
@ -191,7 +191,7 @@ HUD_SERVICE.prototype =
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(() => {
|
||||
client.attachProcess().then(aResponse => {
|
||||
client.getProcess().then(aResponse => {
|
||||
// Set chrome:false in order to attach to the target
|
||||
// (i.e. send an `attach` request to the chrome actor)
|
||||
deferred.resolve({ form: aResponse.form, client: client, chrome: false });
|
||||
|
@ -229,9 +229,9 @@ let AppManager = exports.AppManager = {
|
||||
|
||||
getTarget: function() {
|
||||
if (this.selectedProject.type == "mainProcess") {
|
||||
// Fx >=37 exposes a ChromeActor to debug the main process
|
||||
// Fx >=39 exposes a ChromeActor to debug the main process
|
||||
if (this.connection.client.mainRoot.traits.allowChromeProcess) {
|
||||
return this.connection.client.attachProcess()
|
||||
return this.connection.client.getProcess()
|
||||
.then(aResponse => {
|
||||
return devtools.TargetFactory.forRemoteTab({
|
||||
form: aResponse.form,
|
||||
@ -240,7 +240,7 @@ let AppManager = exports.AppManager = {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Fx <37 exposes tab actors on the root actor
|
||||
// Fx <39 exposes tab actors on the root actor
|
||||
return devtools.TargetFactory.forRemoteTab({
|
||||
form: this._listTabsResponse,
|
||||
client: this.connection.client,
|
||||
@ -442,8 +442,8 @@ let AppManager = exports.AppManager = {
|
||||
},
|
||||
|
||||
isMainProcessDebuggable: function() {
|
||||
// Fx <37 exposes chrome tab actors on RootActor
|
||||
// Fx >=37 exposes a dedicated actor via attachProcess request
|
||||
// Fx <39 exposes chrome tab actors on RootActor
|
||||
// Fx >=39 exposes a dedicated actor via getProcess request
|
||||
return this.connection.client &&
|
||||
this.connection.client.mainRoot &&
|
||||
this.connection.client.mainRoot.traits.allowChromeProcess ||
|
||||
|
@ -419,6 +419,14 @@ Experiments.Experiments = function (policy=new Experiments.Policy()) {
|
||||
Experiments.Experiments.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
|
||||
|
||||
/**
|
||||
* `true` if the experiments manager is currently setup (has been fully initialized
|
||||
* and not uninitialized yet).
|
||||
*/
|
||||
get isReady() {
|
||||
return !this._shutdown;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._shutdown = false;
|
||||
configureLogging();
|
||||
|
@ -56,7 +56,10 @@ ExperimentsService.prototype = {
|
||||
if (OS.Constants.Path.profileDir === undefined) {
|
||||
throw Error("Update timer fired before profile was initialized?");
|
||||
}
|
||||
Experiments.instance().updateManifest();
|
||||
let instance = Experiments.instance();
|
||||
if (instance.isReady) {
|
||||
instance.updateManifest();
|
||||
}
|
||||
},
|
||||
|
||||
_delayedInit: function () {
|
||||
|
@ -18,6 +18,10 @@ newtab.suggested.button=Suggested for %1$S visitors
|
||||
# the (X) block icon. %2$S will be replaced by an active link using string
|
||||
# newtab.learn.link as text.
|
||||
newtab.sponsored.explain=This tile is being shown to you on behalf of a Mozilla partner. You can remove it at any time by clicking the %1$S button. %2$S
|
||||
# LOCALIZATION NOTE(newtab.suggested.explain): %1$S will be replaced inline by
|
||||
# the (X) block icon. %2$S will be replaced by an active link using string
|
||||
# newtab.learn.link as text.
|
||||
newtab.suggested.explain=This site is suggested to you by Mozilla. You can remove it at any time by clicking the %1$S button. %2$S
|
||||
# LOCALIZATION NOTE(newtab.enhanced.explain): %1$S will be replaced inline by
|
||||
# the gear icon used to customize the new tab window. %2$S will be replaced by
|
||||
# an active link using string newtab.learn.link as text.
|
||||
|
@ -202,7 +202,7 @@ let DirectoryLinksProvider = {
|
||||
},
|
||||
|
||||
_cacheRelatedLinks: function(link) {
|
||||
for (let relatedSite of link.related) {
|
||||
for (let relatedSite of link.frecent_sites) {
|
||||
let relatedMap = this._relatedLinks.get(relatedSite) || new Map();
|
||||
relatedMap.set(link.url, link);
|
||||
this._relatedLinks.set(relatedSite, relatedMap);
|
||||
@ -293,25 +293,27 @@ let DirectoryLinksProvider = {
|
||||
|
||||
/**
|
||||
* Reads directory links file and parses its content
|
||||
* @return a promise resolved to valid list of links or [] if read or parse fails
|
||||
* @return a promise resolved to an object with keys 'directory' and 'suggested',
|
||||
* each containing a valid list of links,
|
||||
* or {'directory': [], 'suggested': []} if read or parse fails.
|
||||
*/
|
||||
_readDirectoryLinksFile: function DirectoryLinksProvider_readDirectoryLinksFile() {
|
||||
let emptyOutput = {directory: [], suggested: []};
|
||||
return OS.File.read(this._directoryFilePath).then(binaryData => {
|
||||
let output;
|
||||
try {
|
||||
let locale = this.locale;
|
||||
let json = gTextDecoder.decode(binaryData);
|
||||
let list = JSON.parse(json);
|
||||
output = list[locale];
|
||||
let linksObj = JSON.parse(json);
|
||||
output = {directory: linksObj.directory || [], suggested: linksObj.suggested || []};
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
return output || [];
|
||||
return output || emptyOutput;
|
||||
},
|
||||
error => {
|
||||
Cu.reportError(error);
|
||||
return [];
|
||||
return emptyOutput;
|
||||
});
|
||||
},
|
||||
|
||||
@ -415,29 +417,34 @@ let DirectoryLinksProvider = {
|
||||
this._enhancedLinks.clear();
|
||||
this._relatedLinks.clear();
|
||||
|
||||
let links = [];
|
||||
rawLinks.filter(link => {
|
||||
let validityFilter = function(link) {
|
||||
// Make sure the link url is allowed and images too if they exist
|
||||
return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES) &&
|
||||
this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES) &&
|
||||
this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES);
|
||||
}).forEach((link, position) => {
|
||||
}.bind(this);
|
||||
|
||||
let setCommonProperties = function(link, length, position) {
|
||||
// Stash the enhanced image for the site
|
||||
if (link.enhancedImageURI) {
|
||||
this._enhancedLinks.set(NewTabUtils.extractSite(link.url), link);
|
||||
}
|
||||
link.lastVisitDate = rawLinks.length - position;
|
||||
link.lastVisitDate = length - position;
|
||||
}.bind(this);
|
||||
|
||||
rawLinks.suggested.filter(validityFilter).forEach((link, position) => {
|
||||
setCommonProperties(link, rawLinks.suggested.length, position);
|
||||
|
||||
// We cache related tiles here but do not push any of them in the links list yet.
|
||||
// The decision for which related tile to include will be made separately.
|
||||
if ("related" == link.type) {
|
||||
this._cacheRelatedLinks(link);
|
||||
return;
|
||||
}
|
||||
link.frecency = DIRECTORY_FRECENCY;
|
||||
links.push(link);
|
||||
this._cacheRelatedLinks(link);
|
||||
});
|
||||
|
||||
return rawLinks.directory.filter(validityFilter).map((link, position) => {
|
||||
setCommonProperties(link, rawLinks.directory.length, position);
|
||||
link.frecency = DIRECTORY_FRECENCY;
|
||||
return link;
|
||||
});
|
||||
return links;
|
||||
}).catch(ex => {
|
||||
Cu.reportError(ex);
|
||||
return [];
|
||||
@ -542,12 +549,12 @@ let DirectoryLinksProvider = {
|
||||
this.maxNumLinks = initialLength;
|
||||
if (initialLength) {
|
||||
let mostFrecentLink = sortedLinks[0];
|
||||
if ("related" == mostFrecentLink.type) {
|
||||
if (mostFrecentLink.targetedSite) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: mostFrecentLink.url,
|
||||
frecency: 0,
|
||||
lastVisitDate: mostFrecentLink.lastVisitDate,
|
||||
type: "related",
|
||||
type: mostFrecentLink.type,
|
||||
}, 0, true);
|
||||
}
|
||||
}
|
||||
@ -589,7 +596,7 @@ let DirectoryLinksProvider = {
|
||||
title: chosenRelatedLink.title,
|
||||
frecency: RELATED_FRECENCY,
|
||||
lastVisitDate: chosenRelatedLink.lastVisitDate,
|
||||
type: "related",
|
||||
type: chosenRelatedLink.type,
|
||||
|
||||
// Choose the first site a user has visited as the target. In the future,
|
||||
// this should be the site with the highest frecency. However, we currently
|
||||
|
@ -26,7 +26,7 @@ do_get_profile();
|
||||
|
||||
const DIRECTORY_LINKS_FILE = "directoryLinks.json";
|
||||
const DIRECTORY_FRECENCY = 1000;
|
||||
const kURLData = {"en-US": [{"url":"http://example.com","title":"LocalSource"}]};
|
||||
const kURLData = {"directory": [{"url":"http://example.com","title":"LocalSource"}]};
|
||||
const kTestURL = 'data:application/json,' + JSON.stringify(kURLData);
|
||||
|
||||
// DirectoryLinksProvider preferences
|
||||
@ -53,7 +53,7 @@ Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
|
||||
Services.prefs.setBoolPref(kNewtabEnhancedPref, true);
|
||||
|
||||
const kHttpHandlerData = {};
|
||||
kHttpHandlerData[kExamplePath] = {"en-US": [{"url":"http://example.com","title":"RemoteSource"}]};
|
||||
kHttpHandlerData[kExamplePath] = {"directory": [{"url":"http://example.com","title":"RemoteSource"}]};
|
||||
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
@ -63,9 +63,9 @@ let gLastRequestPath;
|
||||
|
||||
let relatedTile1 = {
|
||||
url: "http://turbotax.com",
|
||||
type: "related",
|
||||
lastVisitDate: 4,
|
||||
related: [
|
||||
type: "affiliate",
|
||||
lastVisitDate: 3,
|
||||
frecent_sites: [
|
||||
"taxact.com",
|
||||
"hrblock.com",
|
||||
"1040.com",
|
||||
@ -74,9 +74,9 @@ let relatedTile1 = {
|
||||
};
|
||||
let relatedTile2 = {
|
||||
url: "http://irs.gov",
|
||||
type: "related",
|
||||
lastVisitDate: 3,
|
||||
related: [
|
||||
type: "affiliate",
|
||||
lastVisitDate: 2,
|
||||
frecent_sites: [
|
||||
"taxact.com",
|
||||
"hrblock.com",
|
||||
"freetaxusa.com",
|
||||
@ -85,9 +85,9 @@ let relatedTile2 = {
|
||||
};
|
||||
let relatedTile3 = {
|
||||
url: "http://hrblock.com",
|
||||
type: "related",
|
||||
lastVisitDate: 2,
|
||||
related: [
|
||||
type: "affiliate",
|
||||
lastVisitDate: 1,
|
||||
frecent_sites: [
|
||||
"taxact.com",
|
||||
"freetaxusa.com",
|
||||
"1040.com",
|
||||
@ -218,7 +218,7 @@ add_task(function test_updateRelatedTile() {
|
||||
let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
|
||||
|
||||
// Initial setup
|
||||
let data = {"en-US": [relatedTile1, relatedTile2, relatedTile3, someOtherSite]};
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
let testObserver = new TestFirstRun();
|
||||
@ -248,7 +248,7 @@ add_task(function test_updateRelatedTile() {
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], ["hrblock.com", "1040.com", "freetaxusa.com"]);
|
||||
do_check_true(possibleLinks.indexOf(link.url) > -1);
|
||||
do_check_eq(link.frecency, Infinity);
|
||||
do_check_eq(link.type, "related");
|
||||
do_check_eq(link.type, "affiliate");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
@ -262,7 +262,7 @@ add_task(function test_updateRelatedTile() {
|
||||
let possibleLinks = [relatedTile1.url, relatedTile2.url, relatedTile3.url];
|
||||
|
||||
do_check_true(possibleLinks.indexOf(link.url) > -1);
|
||||
do_check_eq(link.type, "related");
|
||||
do_check_eq(link.type, "affiliate");
|
||||
do_check_true(this.count <= 2);
|
||||
|
||||
if (this.count == 1) {
|
||||
@ -285,7 +285,7 @@ add_task(function test_updateRelatedTile() {
|
||||
this.onLinkChanged = (directoryLinksProvider, link) => {
|
||||
this.count++;
|
||||
|
||||
do_check_eq(link.type, "related");
|
||||
do_check_eq(link.type, "affiliate");
|
||||
do_check_eq(this.count, 1);
|
||||
do_check_eq(link.frecency, 0);
|
||||
do_check_eq(link.url, links.shift().url);
|
||||
@ -340,7 +340,7 @@ add_task(function test_updateRelatedTile() {
|
||||
});
|
||||
|
||||
add_task(function test_relatedLinksMap() {
|
||||
let data = {"en-US": [relatedTile1, relatedTile2, relatedTile3, someOtherSite]};
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
@ -386,7 +386,7 @@ add_task(function test_topSitesWithRelatedLinks() {
|
||||
// We start off with no top sites with related links.
|
||||
do_check_eq(DirectoryLinksProvider._topSitesWithRelatedLinks.size, 0);
|
||||
|
||||
let data = {"en-US": [relatedTile1, relatedTile2, relatedTile3, someOtherSite]};
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
@ -605,39 +605,6 @@ add_task(function test_DirectoryLinksProvider__linkObservers() {
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
||||
add_task(function test_linksURL_locale() {
|
||||
let data = {
|
||||
"en-US": [{url: "http://example.com", title: "US"}],
|
||||
"zh-CN": [
|
||||
{url: "http://example.net", title: "CN"},
|
||||
{url:"http://example.net/2", title: "CN2"}
|
||||
],
|
||||
};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
let links;
|
||||
let expected_data;
|
||||
|
||||
links = yield fetchData();
|
||||
do_check_eq(links.length, 1);
|
||||
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
|
||||
isIdentical(links, expected_data);
|
||||
|
||||
yield promiseDirectoryDownloadOnPrefChange("general.useragent.locale", "zh-CN");
|
||||
|
||||
links = yield fetchData();
|
||||
do_check_eq(links.length, 2)
|
||||
expected_data = [
|
||||
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2},
|
||||
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}
|
||||
];
|
||||
isIdentical(links, expected_data);
|
||||
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider__prefObserver_url() {
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: kTestURL});
|
||||
|
||||
@ -667,8 +634,13 @@ add_task(function test_DirectoryLinksProvider__prefObserver_url() {
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getLinks_noLocaleData() {
|
||||
yield promiseSetupDirectoryLinksProvider({locale: 'zh-CN'});
|
||||
add_task(function test_DirectoryLinksProvider_getLinks_noDirectoryData() {
|
||||
let data = {
|
||||
"directory": [],
|
||||
};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
let links = yield fetchData();
|
||||
do_check_eq(links.length, 0);
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
@ -804,7 +776,7 @@ add_task(function test_DirectoryLinksProvider_getLinksFromCorruptedFile() {
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getAllowedLinks() {
|
||||
let data = {"en-US": [
|
||||
let data = {"directory": [
|
||||
{url: "ftp://example.com"},
|
||||
{url: "http://example.net"},
|
||||
{url: "javascript:5"},
|
||||
@ -820,12 +792,12 @@ add_task(function test_DirectoryLinksProvider_getAllowedLinks() {
|
||||
do_check_eq(links.length, 2);
|
||||
|
||||
// The only remaining url should be http and https
|
||||
do_check_eq(links[0].url, data["en-US"][1].url);
|
||||
do_check_eq(links[1].url, data["en-US"][3].url);
|
||||
do_check_eq(links[0].url, data["directory"][1].url);
|
||||
do_check_eq(links[1].url, data["directory"][3].url);
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getAllowedImages() {
|
||||
let data = {"en-US": [
|
||||
let data = {"directory": [
|
||||
{url: "http://example.com", imageURI: "ftp://example.com"},
|
||||
{url: "http://example.com", imageURI: "http://example.net"},
|
||||
{url: "http://example.com", imageURI: "javascript:5"},
|
||||
@ -841,12 +813,12 @@ add_task(function test_DirectoryLinksProvider_getAllowedImages() {
|
||||
do_check_eq(links.length, 2);
|
||||
|
||||
// The only remaining images should be https and data
|
||||
do_check_eq(links[0].imageURI, data["en-US"][3].imageURI);
|
||||
do_check_eq(links[1].imageURI, data["en-US"][5].imageURI);
|
||||
do_check_eq(links[0].imageURI, data["directory"][3].imageURI);
|
||||
do_check_eq(links[1].imageURI, data["directory"][5].imageURI);
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getAllowedEnhancedImages() {
|
||||
let data = {"en-US": [
|
||||
let data = {"directory": [
|
||||
{url: "http://example.com", enhancedImageURI: "ftp://example.com"},
|
||||
{url: "http://example.com", enhancedImageURI: "http://example.net"},
|
||||
{url: "http://example.com", enhancedImageURI: "javascript:5"},
|
||||
@ -862,12 +834,12 @@ add_task(function test_DirectoryLinksProvider_getAllowedEnhancedImages() {
|
||||
do_check_eq(links.length, 2);
|
||||
|
||||
// The only remaining enhancedImages should be http and https and data
|
||||
do_check_eq(links[0].enhancedImageURI, data["en-US"][3].enhancedImageURI);
|
||||
do_check_eq(links[1].enhancedImageURI, data["en-US"][5].enhancedImageURI);
|
||||
do_check_eq(links[0].enhancedImageURI, data["directory"][3].enhancedImageURI);
|
||||
do_check_eq(links[1].enhancedImageURI, data["directory"][5].enhancedImageURI);
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getEnhancedLink() {
|
||||
let data = {"en-US": [
|
||||
let data = {"directory": [
|
||||
{url: "http://example.net", enhancedImageURI: "data:,net1"},
|
||||
{url: "http://example.com", enhancedImageURI: "data:,com1"},
|
||||
{url: "http://example.com", enhancedImageURI: "data:,com2"},
|
||||
@ -907,7 +879,7 @@ add_task(function test_DirectoryLinksProvider_getEnhancedLink() {
|
||||
checkEnhanced("http://127.0.0.1", undefined);
|
||||
|
||||
// Make sure old data is not cached
|
||||
data = {"en-US": [
|
||||
data = {"directory": [
|
||||
{url: "http://example.com", enhancedImageURI: "data:,fresh"},
|
||||
]};
|
||||
dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
@ -232,3 +232,19 @@
|
||||
.ruleview-selector-separator, .ruleview-selector-unmatched {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.ruleview-selectorhighlighter {
|
||||
background: url("chrome://browser/skin/devtools/vview-open-inspector.png") no-repeat 0 0;
|
||||
padding-left: 16px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ruleview-selectorhighlighter:hover {
|
||||
background-position: -32px 0;
|
||||
}
|
||||
|
||||
.ruleview-selectorhighlighter:active,
|
||||
.ruleview-selectorhighlighter.highlighted {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
|
@ -142,6 +142,17 @@
|
||||
color: #5c5c5c;
|
||||
}
|
||||
|
||||
.newtab-suggested:hover {
|
||||
color: #588FE4;
|
||||
border: 1px solid #588FE4;
|
||||
}
|
||||
|
||||
.newtab-suggested[active] {
|
||||
background-color: rgba(51, 51, 51, 0.95);
|
||||
border: 0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.newtab-suggested {
|
||||
background-color: white;
|
||||
}
|
||||
|
@ -199,19 +199,22 @@ MediaEngineTabVideoSource::Draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
float pixelRatio;
|
||||
win->GetDevicePixelRatio(&pixelRatio);
|
||||
const int deviceInnerWidth = (int)(pixelRatio * innerWidth);
|
||||
const int deviceInnerHeight = (int)(pixelRatio * innerHeight);
|
||||
|
||||
IntSize size;
|
||||
// maintain source aspect ratio
|
||||
if (mBufWidthMax/innerWidth < mBufHeightMax/innerHeight) {
|
||||
// mBufWidthMax is quite large by default, so use innerWidth if less.
|
||||
int32_t width = std::min(innerWidth, mBufWidthMax);
|
||||
// adjust width to be divisible by 4 to work around bug 1125393
|
||||
width = width - (width % 4);
|
||||
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
||||
|
||||
if ((deviceInnerWidth <= mBufWidthMax) && (deviceInnerHeight <= mBufHeightMax)) {
|
||||
size = IntSize(deviceInnerWidth, deviceInnerHeight);
|
||||
} else {
|
||||
int32_t width = std::min(innerHeight, mBufHeightMax) *
|
||||
((float) innerWidth/innerHeight);
|
||||
width = width - (width % 4);
|
||||
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
||||
|
||||
const float scaleWidth = (float)mBufWidthMax / (float)deviceInnerWidth;
|
||||
const float scaleHeight = (float)mBufHeightMax / (float)deviceInnerHeight;
|
||||
const float scale = scaleWidth < scaleHeight ? scaleWidth : scaleHeight;
|
||||
|
||||
size = IntSize((int)(scale * deviceInnerWidth), (int)(scale * deviceInnerHeight));
|
||||
}
|
||||
|
||||
gfxImageFormat format = gfxImageFormat::RGB24;
|
||||
|
@ -553,6 +553,13 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY bookmarkhistory_import_wait "Please wait...">
|
||||
|
||||
<!ENTITY suggestions_prompt3 "Would you like to turn on search suggestions?">
|
||||
<!-- Localization note (search_bar_item_desc): When the user clicks the url bar
|
||||
and starts typing, a list of icons of search engines appears at the bottom
|
||||
of the screen. When a user clicks an icon, the entered text will be searched
|
||||
via the search engine that uses the icon they clicked. This text is used
|
||||
for screen reader users when they hover each icon - &formatS; will be
|
||||
replaced with the name of the currently highlighted icon. -->
|
||||
<!ENTITY search_bar_item_desc "Search with &formatS;">
|
||||
|
||||
<!-- Localization note (suggestion_for_engine): The placeholder &formatS1; will be
|
||||
replaced with the name of the search engine. The placeholder &formatS2; will be
|
||||
|
@ -148,10 +148,10 @@ public class SendTabDeviceListArrayAdapter extends ArrayAdapter<ParcelableClient
|
||||
|
||||
private static int getImage(ParcelableClientRecord record) {
|
||||
if ("mobile".equals(record.type)) {
|
||||
return R.drawable.sync_mobile_inactive;
|
||||
return R.drawable.device_mobile;
|
||||
}
|
||||
|
||||
return R.drawable.sync_desktop_inactive;
|
||||
return R.drawable.device_pc;
|
||||
}
|
||||
|
||||
public void switchState(State newState) {
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/device_mobile.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
mobile/android/base/resources/drawable-hdpi/device_pc.png
Normal file
After Width: | Height: | Size: 342 B |
BIN
mobile/android/base/resources/drawable-mdpi/device_mobile.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
mobile/android/base/resources/drawable-mdpi/device_pc.png
Normal file
After Width: | Height: | Size: 254 B |
BIN
mobile/android/base/resources/drawable-xhdpi/device_mobile.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
mobile/android/base/resources/drawable-xhdpi/device_pc.png
Normal file
After Width: | Height: | Size: 392 B |
BIN
mobile/android/base/resources/drawable-xxhdpi/device_mobile.png
Normal file
After Width: | Height: | Size: 296 B |
BIN
mobile/android/base/resources/drawable-xxhdpi/device_pc.png
Normal file
After Width: | Height: | Size: 554 B |
@ -7,7 +7,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/dropshadow"
|
||||
android:padding="3dp">
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<ScrollView android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -12,7 +12,7 @@
|
||||
android:scaleType="center"/>
|
||||
|
||||
<TextView
|
||||
android:textAppearance="@style/ShareOverlayTextAppearance"
|
||||
android:textAppearance="@style/TextAppearance.ShareOverlay"
|
||||
android:id="@+id/overlaybtn_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -25,7 +25,7 @@
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/ShareOverlayTitle"
|
||||
android:textAppearance="@style/ShareOverlayTextAppearance.Header"
|
||||
android:textAppearance="@style/TextAppearance.ShareOverlay.Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
@ -37,7 +37,7 @@
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
style="@style/ShareOverlayTitle"
|
||||
android:textAppearance="@style/ShareOverlayTextAppearance.Header"
|
||||
android:textAppearance="@style/TextAppearance.ShareOverlay.Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
|
@ -784,11 +784,11 @@
|
||||
<item name="android:paddingRight">15dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ShareOverlayTextAppearance">
|
||||
<style name="TextAppearance.ShareOverlay">
|
||||
<item name="android:fontFamily">sans-serif</item>
|
||||
</style>
|
||||
|
||||
<style name="ShareOverlayTextAppearance.Header">
|
||||
<style name="TextAppearance.ShareOverlay.Header">
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
|
@ -474,8 +474,9 @@
|
||||
<string name="updater_apply_title">&updater_apply_title2;</string>
|
||||
<string name="updater_apply_select">&updater_apply_select2;</string>
|
||||
|
||||
<!-- Search suggestions opt-in -->
|
||||
<!-- Awesomescreen screen -->
|
||||
<string name="suggestions_prompt">&suggestions_prompt3;</string>
|
||||
<string name="search_bar_item_desc">&search_bar_item_desc;</string>
|
||||
|
||||
<string name="suggestion_for_engine">&suggestion_for_engine;</string>
|
||||
|
||||
|
@ -1029,8 +1029,10 @@ pref("privacy.popups.disable_from_plugins", 2);
|
||||
|
||||
// send "do not track" HTTP header, disabled by default
|
||||
pref("privacy.donottrackheader.enabled", false);
|
||||
// Enforce tracking protection
|
||||
// Enforce tracking protection in all modes
|
||||
pref("privacy.trackingprotection.enabled", false);
|
||||
// Enforce tracking protection in Private Browsing mode
|
||||
pref("privacy.trackingprotection.pbmode.enabled", false);
|
||||
|
||||
pref("dom.event.contextmenu.enabled", true);
|
||||
pref("dom.event.clipboardevents.enabled", true);
|
||||
|
@ -68,7 +68,9 @@ nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel,
|
||||
NS_ENSURE_ARG(result);
|
||||
*result = false;
|
||||
|
||||
if (!Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
|
||||
if (!Preferences::GetBool("privacy.trackingprotection.enabled", false) &&
|
||||
(!Preferences::GetBool("privacy.trackingprotection.pbmode.enabled",
|
||||
false) || !NS_UsePrivateBrowsing(aChannel))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import errno
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
# These are the platform and build-tools versions for building
|
||||
# mobile/android, respectively. Try to keep these in synch with the
|
||||
# build system and Mozilla's automation.
|
||||
@ -82,6 +83,7 @@ def check_output(*args, **kwargs):
|
||||
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
|
||||
def list_missing_android_packages(android_tool, packages):
|
||||
'''
|
||||
Use the given |android| tool to return the sub-list of Android
|
||||
@ -93,7 +95,7 @@ def list_missing_android_packages(android_tool, packages):
|
||||
# but packages that are installed don't appear in the list of
|
||||
# available packages.
|
||||
lines = check_output([android_tool,
|
||||
'list', 'sdk', '--no-ui', '--extended']).splitlines()
|
||||
'list', 'sdk', '--no-ui', '--extended']).splitlines()
|
||||
|
||||
# Lines look like: 'id: 59 or "extra-google-simulators"'
|
||||
for line in lines:
|
||||
@ -113,6 +115,7 @@ def list_missing_android_packages(android_tool, packages):
|
||||
|
||||
return missing
|
||||
|
||||
|
||||
def install_mobile_android_sdk_or_ndk(url, path):
|
||||
'''
|
||||
Fetch an Android SDK or NDK from |url| and unpack it into
|
||||
@ -158,6 +161,7 @@ def install_mobile_android_sdk_or_ndk(url, path):
|
||||
finally:
|
||||
os.chdir(old_path)
|
||||
|
||||
|
||||
def ensure_android_sdk_and_ndk(path, sdk_path, sdk_url, ndk_path, ndk_url):
|
||||
'''
|
||||
Ensure the Android SDK and NDK are found at the given paths. If not, fetch
|
||||
@ -180,6 +184,7 @@ def ensure_android_sdk_and_ndk(path, sdk_path, sdk_url, ndk_path, ndk_url):
|
||||
else:
|
||||
install_mobile_android_sdk_or_ndk(sdk_url, path)
|
||||
|
||||
|
||||
def ensure_android_packages(android_tool, packages=None):
|
||||
'''
|
||||
Use the given android tool (like 'android') to install required Android
|
||||
@ -198,13 +203,14 @@ def ensure_android_packages(android_tool, packages=None):
|
||||
# may be prompted to agree to the Android license.
|
||||
print(INSTALLING_ANDROID_PACKAGES % ', '.join(missing))
|
||||
subprocess.check_call([android_tool,
|
||||
'update', 'sdk', '--no-ui',
|
||||
'--filter', ','.join(missing)])
|
||||
'update', 'sdk', '--no-ui',
|
||||
'--filter', ','.join(missing)])
|
||||
|
||||
# Let's verify.
|
||||
failing = list_missing_android_packages(android_tool, packages=packages)
|
||||
if failing:
|
||||
raise Exception(MISSING_ANDROID_PACKAGES % (', '.join(missing), ', '.join(failing)))
|
||||
|
||||
|
||||
def suggest_mozconfig(sdk_path=None, ndk_path=None):
|
||||
print(MOBILE_ANDROID_MOZCONFIG_TEMPLATE % (sdk_path, ndk_path))
|
||||
|
@ -10,6 +10,7 @@ import glob
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class ArchlinuxBootstrapper(BaseBootstrapper):
|
||||
'''Archlinux experimental bootstrapper.'''
|
||||
|
||||
@ -74,7 +75,7 @@ class ArchlinuxBootstrapper(BaseBootstrapper):
|
||||
|
||||
def install_mobile_android_packages(self):
|
||||
raise NotImplementedError('Bootstrap support for mobile-android is '
|
||||
'not yet available for Archlinux')
|
||||
'not yet available for Archlinux')
|
||||
|
||||
def _update_package_manager(self):
|
||||
self.pacman_update
|
||||
@ -125,9 +126,9 @@ class ArchlinuxBootstrapper(BaseBootstrapper):
|
||||
def aur_install(self, *packages):
|
||||
path = tempfile.mkdtemp()
|
||||
print('WARNING! This script requires to install packages from the AUR '
|
||||
'This is potentially unsecure so I recommend that you carefully '
|
||||
'read each package description and check the sources.'
|
||||
'These packages will be built in ' + path + '.')
|
||||
'This is potentially unsecure so I recommend that you carefully '
|
||||
'read each package description and check the sources.'
|
||||
'These packages will be built in ' + path + '.')
|
||||
choice = raw_input('Do you want to continue? (yes/no) [no]')
|
||||
if choice != 'yes':
|
||||
sys.exit(1)
|
||||
|
@ -93,7 +93,7 @@ class BaseBootstrapper(object):
|
||||
build system (like autoconf).
|
||||
'''
|
||||
raise NotImplementedError('%s must implement install_system_packages()' %
|
||||
__name__)
|
||||
__name__)
|
||||
|
||||
def install_browser_packages(self):
|
||||
'''
|
||||
@ -101,7 +101,8 @@ class BaseBootstrapper(object):
|
||||
'browser').
|
||||
'''
|
||||
raise NotImplementedError('Cannot bootstrap Firefox for Desktop: '
|
||||
'%s does not yet implement install_browser_packages()' % __name__)
|
||||
'%s does not yet implement install_browser_packages()' %
|
||||
__name__)
|
||||
|
||||
def suggest_browser_mozconfig(self):
|
||||
'''
|
||||
@ -119,7 +120,8 @@ class BaseBootstrapper(object):
|
||||
'mobile/android', also known as Fennec).
|
||||
'''
|
||||
raise NotImplementedError('Cannot bootstrap Firefox for Android: '
|
||||
'%s does not yet implement install_mobile_android_packages()' % __name__)
|
||||
'%s does not yet implement install_mobile_android_packages()'
|
||||
% __name__)
|
||||
|
||||
def suggest_mobile_android_mozconfig(self):
|
||||
'''
|
||||
@ -130,7 +132,7 @@ class BaseBootstrapper(object):
|
||||
paths to the Android SDK and NDK.
|
||||
'''
|
||||
raise NotImplementedError('%s does not yet implement suggest_mobile_android_mozconfig()' %
|
||||
__name__)
|
||||
__name__)
|
||||
|
||||
def which(self, name):
|
||||
"""Python implementation of which.
|
||||
@ -276,7 +278,7 @@ class BaseBootstrapper(object):
|
||||
|
||||
if not installed or modern:
|
||||
print('Your version of Mercurial (%s) is sufficiently modern.' %
|
||||
version)
|
||||
version)
|
||||
return
|
||||
|
||||
self._ensure_package_manager_updated()
|
||||
@ -305,7 +307,7 @@ class BaseBootstrapper(object):
|
||||
assert python
|
||||
|
||||
info = self.check_output([python, '--version'],
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT)
|
||||
match = re.search('Python ([a-z0-9\.]+)', info)
|
||||
if not match:
|
||||
print('ERROR Unable to identify Python version.')
|
||||
@ -323,7 +325,7 @@ class BaseBootstrapper(object):
|
||||
return
|
||||
|
||||
print('Your version of Python (%s) is too old. Will try to upgrade.' %
|
||||
version)
|
||||
version)
|
||||
|
||||
self._ensure_package_manager_updated()
|
||||
self.upgrade_python(version)
|
||||
|
@ -97,10 +97,10 @@ class Bootstrapper(object):
|
||||
args['version'] = platform.uname()[2]
|
||||
|
||||
elif sys.platform.startswith('dragonfly') or \
|
||||
sys.platform.startswith('freebsd'):
|
||||
sys.platform.startswith('freebsd'):
|
||||
cls = FreeBSDBootstrapper
|
||||
args['version'] = platform.release()
|
||||
args['flavor'] = platform.system()
|
||||
args['flavor'] = platform.system()
|
||||
|
||||
if cls is None:
|
||||
raise NotImplementedError('Bootstrap support is not yet available '
|
||||
@ -108,10 +108,9 @@ class Bootstrapper(object):
|
||||
|
||||
self.instance = cls(**args)
|
||||
|
||||
|
||||
def bootstrap(self):
|
||||
# Like ['1. Firefox for Desktop', '2. Firefox for Android'].
|
||||
labels = [ '%s. %s' % (i + 1, name) for (i, (name, _)) in enumerate(APPLICATIONS) ]
|
||||
labels = ['%s. %s' % (i + 1, name) for (i, (name, _)) in enumerate(APPLICATIONS)]
|
||||
prompt = APPLICATION_CHOICE % '\n'.join(labels)
|
||||
choice = self.instance.prompt_int(prompt=prompt, low=1, high=len(APPLICATIONS))
|
||||
name, application = APPLICATIONS[choice-1]
|
||||
|
@ -2,11 +2,12 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import platform
|
||||
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class CentOSBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version, dist_id):
|
||||
BaseBootstrapper.__init__(self)
|
||||
@ -62,4 +63,3 @@ class CentOSBootstrapper(BaseBootstrapper):
|
||||
|
||||
def upgrade_mercurial(self, current):
|
||||
self.yum_update('mercurial')
|
||||
|
||||
|
@ -7,6 +7,7 @@ import sys
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class DebianBootstrapper(BaseBootstrapper):
|
||||
# These are common packages for all Debian-derived distros (such as
|
||||
# Ubuntu).
|
||||
@ -52,11 +53,11 @@ class DebianBootstrapper(BaseBootstrapper):
|
||||
# These are common packages for building Firefox for Android
|
||||
# (mobile/android) for all Debian-derived distros (such as Ubuntu).
|
||||
MOBILE_ANDROID_COMMON_PACKAGES = [
|
||||
'zlib1g-dev', # mobile/android requires system zlib.
|
||||
'zlib1g-dev', # mobile/android requires system zlib.
|
||||
'openjdk-7-jdk',
|
||||
'ant',
|
||||
'wget', # For downloading the Android SDK and NDK.
|
||||
'libncurses5:i386', # See comments about i386 below.
|
||||
'wget', # For downloading the Android SDK and NDK.
|
||||
'libncurses5:i386', # See comments about i386 below.
|
||||
'libstdc++6:i386',
|
||||
'zlib1g:i386',
|
||||
]
|
||||
@ -111,8 +112,8 @@ class DebianBootstrapper(BaseBootstrapper):
|
||||
else:
|
||||
self.ndk_url = 'https://dl.google.com/android/ndk/android-ndk-r8e-linux-x86.tar.bz2'
|
||||
android.ensure_android_sdk_and_ndk(path=mozbuild_path,
|
||||
sdk_path=self.sdk_path, sdk_url=self.sdk_url,
|
||||
ndk_path=self.ndk_path, ndk_url=self.ndk_url)
|
||||
sdk_path=self.sdk_path, sdk_url=self.sdk_url,
|
||||
ndk_path=self.ndk_path, ndk_url=self.ndk_url)
|
||||
|
||||
# 3. We expect the |android| tool to at
|
||||
# ~/.mozbuild/android-sdk-linux/tools/android.
|
||||
@ -125,8 +126,7 @@ class DebianBootstrapper(BaseBootstrapper):
|
||||
# The SDK path that mozconfig wants includes platforms/android-21.
|
||||
sdk_path = os.path.join(self.sdk_path, 'platforms', android.ANDROID_PLATFORM)
|
||||
android.suggest_mozconfig(sdk_path=sdk_path,
|
||||
ndk_path=self.ndk_path)
|
||||
ndk_path=self.ndk_path)
|
||||
|
||||
def _update_package_manager(self):
|
||||
self.run_as_root(['apt-get', 'update'])
|
||||
|
||||
|
@ -2,10 +2,9 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class FedoraBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version, dist_id):
|
||||
BaseBootstrapper.__init__(self)
|
||||
|
@ -4,11 +4,12 @@
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class FreeBSDBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version, flavor):
|
||||
BaseBootstrapper.__init__(self)
|
||||
self.version = int(version.split('.')[0])
|
||||
self.flavor = flavor.lower()
|
||||
self.flavor = flavor.lower()
|
||||
|
||||
self.packages = [
|
||||
'autoconf213',
|
||||
|
@ -2,10 +2,9 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class GentooBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version, dist_id):
|
||||
BaseBootstrapper.__init__(self)
|
||||
|
@ -5,7 +5,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from mach.decorators import (
|
||||
CommandArgument,
|
||||
CommandProvider,
|
||||
Command,
|
||||
)
|
||||
@ -16,7 +15,7 @@ class Bootstrap(object):
|
||||
"""Bootstrap system and mach for optimal development experience."""
|
||||
|
||||
@Command('bootstrap', category='devenv',
|
||||
description='Install required system packages for building.')
|
||||
description='Install required system packages for building.')
|
||||
def bootstrap(self):
|
||||
from mozboot.bootstrap import Bootstrapper
|
||||
|
||||
|
@ -2,10 +2,9 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
|
||||
from mozboot.base import BaseBootstrapper
|
||||
|
||||
|
||||
class OpenBSDBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version):
|
||||
BaseBootstrapper.__init__(self)
|
||||
|
@ -26,7 +26,7 @@ HOMEBREW_AUTOCONF213 = 'https://raw.github.com/Homebrew/homebrew-versions/master
|
||||
MACPORTS_URL = {'9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.2.1-10.9-Mavericks.pkg',
|
||||
'8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.8-MountainLion.pkg',
|
||||
'7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.7-Lion.pkg',
|
||||
'6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.6-SnowLeopard.pkg',}
|
||||
'6': 'https://distfiles.macports.org/MacPorts/MacPorts-2.1.3-10.6-SnowLeopard.pkg', }
|
||||
|
||||
MACPORTS_CLANG_PACKAGE = 'clang-3.3'
|
||||
|
||||
@ -166,6 +166,7 @@ license for you by downloading the JDK. If this is unacceptable you should
|
||||
uninstall.
|
||||
'''
|
||||
|
||||
|
||||
class OSXBootstrapper(BaseBootstrapper):
|
||||
def __init__(self, version):
|
||||
BaseBootstrapper.__init__(self)
|
||||
@ -210,14 +211,14 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
select = self.which('xcode-select')
|
||||
try:
|
||||
output = self.check_output([select, '--print-path'],
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
# This seems to appear on fresh OS X machines before any Xcode
|
||||
# has been installed. It may only occur on OS X 10.9 and later.
|
||||
if 'unable to get active developer directory' in e.output:
|
||||
print(XCODE_NO_DEVELOPER_DIRECTORY)
|
||||
self._install_xcode_app_store()
|
||||
assert False # Above should exit.
|
||||
assert False # Above should exit.
|
||||
|
||||
output = e.output
|
||||
|
||||
@ -227,19 +228,19 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
if '.app/' not in output:
|
||||
print(XCODE_REQUIRED)
|
||||
self._install_xcode_app_store()
|
||||
assert False # Above should exit.
|
||||
assert False # Above should exit.
|
||||
|
||||
# Once Xcode is installed, you need to agree to the license before you can
|
||||
# use it.
|
||||
try:
|
||||
output = self.check_output(['/usr/bin/xcrun', 'clang'],
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if 'license' in e.output:
|
||||
xcodebuild = self.which('xcodebuild')
|
||||
try:
|
||||
subprocess.check_call([xcodebuild, '-license'],
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if 'requires admin privileges' in e.output:
|
||||
self.run_as_root([xcodebuild, '-license'])
|
||||
@ -319,7 +320,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
print(PACKAGE_MANAGER_OLD_CLANG % ('Homebrew',))
|
||||
|
||||
subprocess.check_call([self.brew, '-v', 'install', 'llvm',
|
||||
'--with-clang', '--all-targets'])
|
||||
'--with-clang', '--all-targets'])
|
||||
|
||||
def ensure_homebrew_mobile_android_packages(self):
|
||||
import android
|
||||
@ -333,9 +334,9 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
# packages. If we used the android.py module, we'd need wget.
|
||||
packages = [
|
||||
('android-sdk', 'android-sdk'),
|
||||
('android-ndk', os.path.join(path_to_android, 'android-ndk.rb')), # This is a locally provided brew formula!
|
||||
('android-ndk', os.path.join(path_to_android, 'android-ndk.rb')), # This is a locally provided brew formula!
|
||||
('ant', 'ant'),
|
||||
('brew-cask', 'caskroom/cask/brew-cask'), # For installing Java later.
|
||||
('brew-cask', 'caskroom/cask/brew-cask'), # For installing Java later.
|
||||
]
|
||||
self._ensure_homebrew_packages(packages)
|
||||
|
||||
@ -344,7 +345,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
]
|
||||
installed = self._ensure_homebrew_casks(casks)
|
||||
if installed:
|
||||
print(JAVA_LICENSE_NOTICE) # We accepted a license agreement for the user.
|
||||
print(JAVA_LICENSE_NOTICE) # We accepted a license agreement for the user.
|
||||
|
||||
# We could probably fish this path from |brew info android-sdk|.
|
||||
android_tool = '/usr/local/opt/android-sdk/tools/android'
|
||||
@ -461,7 +462,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
url = MACPORTS_URL.get(self.minor_version, None)
|
||||
if not url:
|
||||
raise Exception('We do not have a MacPorts install URL for your '
|
||||
'OS X version. You will need to install MacPorts manually.')
|
||||
'OS X version. You will need to install MacPorts manually.')
|
||||
|
||||
print(PACKAGE_MANAGER_INSTALL % ('MacPorts', 'MacPorts', 'MacPorts', 'port'))
|
||||
pkg = urlopen(url=url, timeout=300).read()
|
||||
@ -484,7 +485,7 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
if self.package_manager == 'homebrew':
|
||||
try:
|
||||
subprocess.check_output([self.brew, '-v', 'upgrade', package],
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if 'already installed' not in e.output:
|
||||
raise
|
||||
@ -501,4 +502,3 @@ class OSXBootstrapper(BaseBootstrapper):
|
||||
self._upgrade_package('python')
|
||||
else:
|
||||
self._upgrade_package('python27')
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import os
|
||||
|
||||
|
||||
from mozboot.debian import DebianBootstrapper
|
||||
|
||||
|
||||
@ -12,6 +13,7 @@ Ubuntu does not provide a modern Mercurial in its package repository. So,
|
||||
we will install a PPA that does.
|
||||
'''.strip()
|
||||
|
||||
|
||||
# Ubuntu shares much logic with Debian, so it inherits from it.
|
||||
class UbuntuBootstrapper(DebianBootstrapper):
|
||||
DISTRO_PACKAGES = [
|
||||
|
@ -230,6 +230,9 @@ function testInit() {
|
||||
if (config.closeWhenDone) {
|
||||
require("sdk/system").exit(failed == 0 ? 0 : 1);
|
||||
}
|
||||
else {
|
||||
loaderModule.unload(loader, "shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
function testNextModule() {
|
||||
|
@ -401,7 +401,7 @@ function _setupDebuggerServer(breakpointFiles, callback) {
|
||||
let threadActor = subject.wrappedJSObject;
|
||||
for (let file of breakpointFiles) {
|
||||
let sourceActor = threadActor.sources.source({originalUrl: file});
|
||||
sourceActor._setBreakpoint(new OriginalLocation(sourceActor, 1));
|
||||
sourceActor._getOrCreateBreakpointActor(new OriginalLocation(sourceActor, 1));
|
||||
}
|
||||
} catch (ex) {
|
||||
do_print("Failed to initialize breakpoints: " + ex + "\n" + ex.stack);
|
||||
|
@ -108,7 +108,7 @@ this.SafeBrowsing = {
|
||||
debug = Services.prefs.getBoolPref("browser.safebrowsing.debug");
|
||||
this.phishingEnabled = Services.prefs.getBoolPref("browser.safebrowsing.enabled");
|
||||
this.malwareEnabled = Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
|
||||
this.trackingEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled");
|
||||
this.trackingEnabled = Services.prefs.getBoolPref("privacy.trackingprotection.enabled") || Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled");
|
||||
this.updateProviderURLs();
|
||||
|
||||
// XXX The listManager backend gets confused if this is called before the
|
||||
|
@ -74,6 +74,9 @@ PRLogModuleInfo *gUrlClassifierDbServiceLog = nullptr;
|
||||
#define CHECK_TRACKING_PREF "privacy.trackingprotection.enabled"
|
||||
#define CHECK_TRACKING_DEFAULT false
|
||||
|
||||
#define CHECK_TRACKING_PB_PREF "privacy.trackingprotection.pbmode.enabled"
|
||||
#define CHECK_TRACKING_PB_DEFAULT false
|
||||
|
||||
#define GETHASH_NOISE_PREF "urlclassifier.gethashnoise"
|
||||
#define GETHASH_NOISE_DEFAULT 4
|
||||
|
||||
@ -1116,8 +1119,9 @@ nsUrlClassifierDBService::Init()
|
||||
CHECK_MALWARE_DEFAULT);
|
||||
mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
|
||||
CHECK_PHISHING_DEFAULT);
|
||||
mCheckTracking = Preferences::GetBool(CHECK_TRACKING_PREF,
|
||||
CHECK_TRACKING_DEFAULT);
|
||||
mCheckTracking =
|
||||
Preferences::GetBool(CHECK_TRACKING_PREF, CHECK_TRACKING_DEFAULT) ||
|
||||
Preferences::GetBool(CHECK_TRACKING_PB_PREF, CHECK_TRACKING_PB_DEFAULT);
|
||||
uint32_t gethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
|
||||
GETHASH_NOISE_DEFAULT);
|
||||
gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
|
||||
@ -1128,6 +1132,7 @@ nsUrlClassifierDBService::Init()
|
||||
Preferences::AddStrongObserver(this, CHECK_MALWARE_PREF);
|
||||
Preferences::AddStrongObserver(this, CHECK_PHISHING_PREF);
|
||||
Preferences::AddStrongObserver(this, CHECK_TRACKING_PREF);
|
||||
Preferences::AddStrongObserver(this, CHECK_TRACKING_PB_PREF);
|
||||
Preferences::AddStrongObserver(this, GETHASH_NOISE_PREF);
|
||||
Preferences::AddStrongObserver(this, CONFIRM_AGE_PREF);
|
||||
Preferences::AddStrongObserver(this, PHISH_TABLE_PREF);
|
||||
@ -1527,9 +1532,11 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
} else if (NS_LITERAL_STRING(CHECK_PHISHING_PREF).Equals(aData)) {
|
||||
mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
|
||||
CHECK_PHISHING_DEFAULT);
|
||||
} else if (NS_LITERAL_STRING(CHECK_TRACKING_PREF).Equals(aData)) {
|
||||
mCheckTracking = Preferences::GetBool(CHECK_TRACKING_PREF,
|
||||
CHECK_TRACKING_DEFAULT);
|
||||
} else if (NS_LITERAL_STRING(CHECK_TRACKING_PREF).Equals(aData) ||
|
||||
NS_LITERAL_STRING(CHECK_TRACKING_PB_PREF).Equals(aData)) {
|
||||
mCheckTracking =
|
||||
Preferences::GetBool(CHECK_TRACKING_PREF, CHECK_TRACKING_DEFAULT) ||
|
||||
Preferences::GetBool(CHECK_TRACKING_PB_PREF, CHECK_TRACKING_PB_DEFAULT);
|
||||
} else if (
|
||||
NS_LITERAL_STRING(PHISH_TABLE_PREF).Equals(aData) ||
|
||||
NS_LITERAL_STRING(MALWARE_TABLE_PREF).Equals(aData) ||
|
||||
@ -1569,6 +1576,7 @@ nsUrlClassifierDBService::Shutdown()
|
||||
prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
|
||||
prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
|
||||
prefs->RemoveObserver(CHECK_TRACKING_PREF, this);
|
||||
prefs->RemoveObserver(CHECK_TRACKING_PB_PREF, this);
|
||||
prefs->RemoveObserver(PHISH_TABLE_PREF, this);
|
||||
prefs->RemoveObserver(MALWARE_TABLE_PREF, this);
|
||||
prefs->RemoveObserver(TRACKING_TABLE_PREF, this);
|
||||
|
@ -3,7 +3,9 @@ skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
allowlistAnnotatedFrame.html
|
||||
classifiedAnnotatedFrame.html
|
||||
classifiedAnnotatedPBFrame.html
|
||||
|
||||
[test_lookup_system_principal.html]
|
||||
[test_classified_annotations.html]
|
||||
[test_allowlisted_annotations.html]
|
||||
[test_privatebrowsing_trackingprotection.html]
|
||||
|
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
<link id="badcss" rel="stylesheet" type="text/css" href="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script id="badscript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
|
||||
|
||||
<!-- The image cache can cache JS handlers, so make sure we use a different URL for raptor.jpg each time -->
|
||||
<img id="badimage" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/>
|
||||
|
||||
The following should not be hidden:
|
||||
<div id="styleCheck">STYLE TEST</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,184 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Tracking Protection in Private Browsing mode</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var Cc = SpecialPowers.Cc;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
|
||||
var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
var contentPage = "chrome://mochitests/content/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html"
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
||||
function testOnWindow(aPrivate, aCallback) {
|
||||
var win = mainWindow.OpenBrowserWindow({private: aPrivate});
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
whenDelayedStartupFinished(win, function() {
|
||||
win.addEventListener("DOMContentLoaded", function onInnerLoad() {
|
||||
if (win.content.location.href != contentPage) {
|
||||
win.gBrowser.loadURI(contentPage);
|
||||
return;
|
||||
}
|
||||
win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
|
||||
|
||||
win.content.addEventListener('load', function innerLoad2() {
|
||||
win.content.removeEventListener('load', innerLoad2, false);
|
||||
SimpleTest.executeSoon(function() { aCallback(win); });
|
||||
}, false, true);
|
||||
}, true);
|
||||
SimpleTest.executeSoon(function() { win.gBrowser.loadURI(contentPage); });
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Add some URLs to the tracking database
|
||||
var testData = "tracking.example.com/";
|
||||
var testUpdate =
|
||||
"n:1000\ni:test-track-simple\nad:1\n" +
|
||||
"a:524:32:" + testData.length + "\n" +
|
||||
testData;
|
||||
|
||||
var badids = [
|
||||
"badscript",
|
||||
"badimage",
|
||||
"badcss"
|
||||
];
|
||||
|
||||
function checkLoads(aWindow, aBlocked) {
|
||||
var win = aWindow.content;
|
||||
is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
|
||||
is(win.document.getElementById("badimage").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking images");
|
||||
|
||||
var elt = win.document.getElementById("styleCheck");
|
||||
var style = win.document.defaultView.getComputedStyle(elt, "");
|
||||
isnot(style.visibility, aBlocked ? "hidden" : "", "Should not load tracking css");
|
||||
|
||||
is(win.document.blockedTrackingNodeCount, aBlocked ? badids.length : 0, "Should identify all tracking elements");
|
||||
|
||||
var blockedTrackingNodes = win.document.blockedTrackingNodes;
|
||||
|
||||
// Make sure that every node in blockedTrackingNodes exists in the tree
|
||||
// (that may not always be the case but do not expect any nodes to disappear
|
||||
// from the tree here)
|
||||
var allNodeMatch = true;
|
||||
for (var i = 0; i < blockedTrackingNodes.length; i++) {
|
||||
var nodeMatch = false;
|
||||
for (var j = 0; j < badids.length && !nodeMatch; j++) {
|
||||
nodeMatch |=
|
||||
(blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
|
||||
}
|
||||
|
||||
allNodeMatch &= nodeMatch;
|
||||
}
|
||||
is(allNodeMatch, true, "All annotated nodes are expected in the tree");
|
||||
|
||||
// Make sure that every node with a badid (see badids) is found in the
|
||||
// blockedTrackingNodes. This tells us if we are neglecting to annotate
|
||||
// some nodes
|
||||
allNodeMatch = true;
|
||||
for (var j = 0; j < badids.length; j++) {
|
||||
var nodeMatch = false;
|
||||
for (var i = 0; i < blockedTrackingNodes.length && !nodeMatch; i++) {
|
||||
nodeMatch |=
|
||||
(blockedTrackingNodes[i] == win.document.getElementById(badids[j]));
|
||||
}
|
||||
|
||||
allNodeMatch &= nodeMatch;
|
||||
}
|
||||
is(allNodeMatch, aBlocked, "All tracking nodes are expected to be annotated as such");
|
||||
}
|
||||
|
||||
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIUrlClassifierDBService);
|
||||
|
||||
function doUpdate(update) {
|
||||
var listener = {
|
||||
QueryInterface: function(iid)
|
||||
{
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
updateUrlRequested: function(url) { },
|
||||
streamFinished: function(status) { },
|
||||
updateError: function(errorCode) {
|
||||
ok(false, "Couldn't update classifier.");
|
||||
// Abort test.
|
||||
SimpleTest.finish();
|
||||
},
|
||||
updateSuccess: function(requestedTimeout) {
|
||||
// Normal mode, with the pref (trackers should be loaded)
|
||||
testOnWindow(false, function(aWindow) {
|
||||
checkLoads(aWindow, false);
|
||||
aWindow.close();
|
||||
|
||||
// Private Browsing, with the pref (trackers should be blocked)
|
||||
testOnWindow(true, function(aWindow) {
|
||||
checkLoads(aWindow, true);
|
||||
aWindow.close();
|
||||
|
||||
// Private Browsing, without the pref (trackers should be loaded)
|
||||
SpecialPowers.setBoolPref("privacy.trackingprotection.pbmode.enabled", false);
|
||||
testOnWindow(true, function(aWindow) {
|
||||
checkLoads(aWindow, false);
|
||||
aWindow.close();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
dbService.beginUpdate(listener, "test-track-simple", "");
|
||||
dbService.beginStream("", "");
|
||||
dbService.updateStream(update);
|
||||
dbService.finishStream();
|
||||
dbService.finishUpdate();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{"set" : [["urlclassifier.trackingTable", "test-track-simple"],
|
||||
["privacy.trackingprotection.enabled", false],
|
||||
["privacy.trackingprotection.pbmode.enabled", true],
|
||||
["channelclassifier.allowlist_example", true]]},
|
||||
function() { doUpdate(testUpdate); });
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
||||
</pre>
|
||||
<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
|
||||
</body>
|
||||
</html>
|
@ -667,8 +667,6 @@
|
||||
if (this._selectedPanel != panel) {
|
||||
var event = document.createEvent("Events");
|
||||
event.initEvent("select", true, true);
|
||||
event.fromTab = this.getRelatedElement(panel);
|
||||
event.toTab = this.getRelatedElement(this._selectedPanel);
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
return val;
|
||||
|
@ -468,6 +468,8 @@ DebuggerClient.prototype = {
|
||||
*/
|
||||
listAddons: function (aOnResponse) { return this.mainRoot.listAddons(aOnResponse); },
|
||||
|
||||
getTab: function (aFilter) { return this.mainRoot.getTab(aFilter); },
|
||||
|
||||
/**
|
||||
* Attach to a tab actor.
|
||||
*
|
||||
@ -622,16 +624,17 @@ DebuggerClient.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to a process in order to get the form of a ChildProcessActor.
|
||||
* Fetch the ChromeActor for the main process or ChildProcessActor for a
|
||||
* a given child process ID.
|
||||
*
|
||||
* @param number aId
|
||||
* The ID for the process to attach (returned by `listProcesses`).
|
||||
* Connected to the main process if omitted, or is 0.
|
||||
*/
|
||||
attachProcess: function (aId) {
|
||||
getProcess: function (aId) {
|
||||
let packet = {
|
||||
to: "root",
|
||||
type: "attachProcess"
|
||||
type: "getProcess"
|
||||
}
|
||||
if (typeof(aId) == "number") {
|
||||
packet.id = aId;
|
||||
@ -1406,6 +1409,51 @@ RootClient.prototype = {
|
||||
listProcesses: DebuggerClient.requester({ type: "listProcesses" },
|
||||
{ telemetry: "LISTPROCESSES" }),
|
||||
|
||||
/**
|
||||
* Fetch the TabActor for the currently selected tab, or for a specific
|
||||
* tab given as first parameter.
|
||||
*
|
||||
* @param [optional] object aFilter
|
||||
* A dictionary object with following optional attributes:
|
||||
* - outerWindowID: used to match tabs in parent process
|
||||
* - tabId: used to match tabs in child processes
|
||||
* - tab: a reference to xul:tab element
|
||||
* If nothing is specified, returns the actor for the currently
|
||||
* selected tab.
|
||||
*/
|
||||
getTab: function (aFilter) {
|
||||
let packet = {
|
||||
to: this.actor,
|
||||
type: "getTab"
|
||||
};
|
||||
|
||||
if (aFilter) {
|
||||
if (typeof(aFilter.outerWindowID) == "number") {
|
||||
packet.outerWindowID = aFilter.outerWindowID;
|
||||
} else if (typeof(aFilter.tabId) == "number") {
|
||||
packet.tabId = aFilter.tabId;
|
||||
} else if ("tab" in aFilter) {
|
||||
let browser = aFilter.tab.linkedBrowser;
|
||||
if (browser.frameLoader.tabParent) {
|
||||
// Tabs in child process
|
||||
packet.tabId = browser.frameLoader.tabParent.tabId;
|
||||
} else {
|
||||
// Tabs in parent process
|
||||
let windowUtils = browser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
packet.outerWindowID = windowUtils.outerWindowID;
|
||||
}
|
||||
} else {
|
||||
// Throw if a filter object have been passed but without
|
||||
// any clearly idenfified filter.
|
||||
throw new Error("Unsupported argument given to getTab request");
|
||||
}
|
||||
}
|
||||
|
||||
return this.request(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Description of protocol's actors and methods.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@ const makeDebugger = require("./utils/make-debugger");
|
||||
* Creates a TabActor for debugging all the chrome content in the
|
||||
* current process. Most of the implementation is inherited from TabActor.
|
||||
* ChromeActor is a child of RootActor, it can be instanciated via
|
||||
* RootActor.attachProcess request.
|
||||
* RootActor.getProcess request.
|
||||
* ChromeActor exposes all tab actors via its form() request, like TabActor.
|
||||
*
|
||||
* History lecture:
|
||||
|
@ -156,9 +156,9 @@ RootActor.prototype = {
|
||||
memoryActorAllocations: true,
|
||||
// Whether root actor exposes tab actors
|
||||
// if allowChromeProcess is true, you can fetch a ChromeActor instance
|
||||
// to debug chrome and any non-content ressource via attachProcess request
|
||||
// to debug chrome and any non-content ressource via getProcess request
|
||||
// if allocChromeProcess is defined, but not true, it means that root actor
|
||||
// no longer expose tab actors, but also that attachProcess forbids
|
||||
// no longer expose tab actors, but also that getProcess forbids
|
||||
// exposing actors for security reasons
|
||||
get allowChromeProcess() {
|
||||
return DebuggerServer.allowChromeProcess;
|
||||
@ -268,6 +268,33 @@ RootActor.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
onGetTab: function (options) {
|
||||
let tabList = this._parameters.tabList;
|
||||
if (!tabList) {
|
||||
return { error: "noTabs",
|
||||
message: "This root actor has no browser tabs." };
|
||||
}
|
||||
if (!this._tabActorPool) {
|
||||
this._tabActorPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
}
|
||||
return tabList.getTab(options)
|
||||
.then(tabActor => {
|
||||
tabActor.parentID = this.actorID;
|
||||
this._tabActorPool.addActor(tabActor);
|
||||
|
||||
return { tab: tabActor.form() };
|
||||
}, error => {
|
||||
if (error.error) {
|
||||
// Pipe expected errors as-is to the client
|
||||
return error;
|
||||
} else {
|
||||
return { error: "noTab",
|
||||
message: "Unexpected error while calling getTab(): " + error };
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onTabListChanged: function () {
|
||||
this.conn.send({ from: this.actorID, type:"tabListChanged" });
|
||||
/* It's a one-shot notification; no need to watch any more. */
|
||||
@ -319,14 +346,14 @@ RootActor.prototype = {
|
||||
return { processes: processes };
|
||||
},
|
||||
|
||||
onAttachProcess: function (aRequest) {
|
||||
onGetProcess: function (aRequest) {
|
||||
if (!DebuggerServer.allowChromeProcess) {
|
||||
return { error: "forbidden",
|
||||
message: "You are not allowed to debug chrome." };
|
||||
}
|
||||
if (("id" in aRequest) && typeof(aRequest.id) != "number") {
|
||||
return { error: "wrongParameter",
|
||||
message: "attachProcess requires a valid `id` attribute." };
|
||||
message: "getProcess requires a valid `id` attribute." };
|
||||
}
|
||||
// If the request doesn't contains id parameter or id is 0
|
||||
// (id == 0, based on onListProcesses implementation)
|
||||
@ -391,9 +418,10 @@ RootActor.prototype = {
|
||||
|
||||
RootActor.prototype.requestTypes = {
|
||||
"listTabs": RootActor.prototype.onListTabs,
|
||||
"getTab": RootActor.prototype.onGetTab,
|
||||
"listAddons": RootActor.prototype.onListAddons,
|
||||
"listProcesses": RootActor.prototype.onListProcesses,
|
||||
"attachProcess": RootActor.prototype.onAttachProcess,
|
||||
"getProcess": RootActor.prototype.onGetProcess,
|
||||
"echo": RootActor.prototype.onEcho,
|
||||
"protocolDescription": RootActor.prototype.onProtocolDescription
|
||||
};
|
||||
|
@ -1221,7 +1221,7 @@ ThreadActor.prototype = {
|
||||
this.threadLifetimePool.addActor(actor);
|
||||
let scripts = this.scripts.getScriptsBySourceAndLine(script.source, line);
|
||||
let entryPoints = findEntryPointsForLine(scripts, line);
|
||||
setBreakpointForActorAtEntryPoints(actor, entryPoints);
|
||||
setBreakpointAtEntryPoints(actor, entryPoints);
|
||||
this._hiddenBreakpoints.set(actor.actorID, actor);
|
||||
break;
|
||||
}
|
||||
@ -2046,7 +2046,7 @@ ThreadActor.prototype = {
|
||||
let actor = _actor;
|
||||
|
||||
if (actor.isPending) {
|
||||
promises.push(sourceActor._setBreakpointForActor(actor));
|
||||
promises.push(sourceActor._setBreakpoint(actor));
|
||||
} else {
|
||||
promises.push(this.sources.getGeneratedLocation(actor.originalLocation)
|
||||
.then((generatedLocation) => {
|
||||
@ -2054,7 +2054,10 @@ ThreadActor.prototype = {
|
||||
if (generatedLocation.generatedSourceActor.actorID === sourceActor.actorID &&
|
||||
generatedLocation.generatedLine >= aScript.startLine &&
|
||||
generatedLocation.generatedLine <= endLine) {
|
||||
sourceActor._setBreakpointForActorAtLocation(actor, generatedLocation);
|
||||
sourceActor._setBreakpointAtGeneratedLocation(
|
||||
actor,
|
||||
generatedLocation
|
||||
);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -2300,6 +2303,7 @@ SourceActor.prototype = {
|
||||
},
|
||||
|
||||
get threadActor() { return this._threadActor; },
|
||||
get sources() { return this._threadActor.sources; },
|
||||
get dbg() { return this.threadActor.dbg; },
|
||||
get scripts() { return this.threadActor.scripts; },
|
||||
get source() { return this._source; },
|
||||
@ -2743,7 +2747,10 @@ SourceActor.prototype = {
|
||||
|
||||
let { location: { line, column }, condition } = request;
|
||||
let location = new OriginalLocation(this, line, column);
|
||||
return this._setBreakpoint(location, condition).then((actor) => {
|
||||
return this._getOrCreateBreakpointActor(
|
||||
location,
|
||||
condition
|
||||
).then((actor) => {
|
||||
if (actor.isPending) {
|
||||
return {
|
||||
error: "noCodeAtLocation",
|
||||
@ -2779,7 +2786,7 @@ SourceActor.prototype = {
|
||||
* @returns BreakpointActor
|
||||
* A BreakpointActor representing the breakpoint.
|
||||
*/
|
||||
_setBreakpoint: function (originalLocation, condition) {
|
||||
_getOrCreateBreakpointActor: function (originalLocation, condition) {
|
||||
let actor = this.breakpointActorMap.getActor(originalLocation);
|
||||
if (!actor) {
|
||||
actor = new BreakpointActor(this.threadActor, originalLocation);
|
||||
@ -2789,7 +2796,7 @@ SourceActor.prototype = {
|
||||
|
||||
actor.condition = condition;
|
||||
|
||||
return this._setBreakpointForActor(actor);
|
||||
return this._setBreakpoint(actor);
|
||||
},
|
||||
|
||||
/*
|
||||
@ -2812,127 +2819,163 @@ SourceActor.prototype = {
|
||||
*
|
||||
* @returns A Promise that resolves to the given BreakpointActor.
|
||||
*/
|
||||
_setBreakpointForActor: function (actor) {
|
||||
_setBreakpoint: function (actor) {
|
||||
let { originalLocation } = actor;
|
||||
let { originalColumn } = originalLocation;
|
||||
|
||||
if (this.isSourceMapped) {
|
||||
// TODO: Refactor breakpoint sliding for source mapped sources.
|
||||
return this.threadActor.sources.getGeneratedLocation(originalLocation)
|
||||
.then((generatedLocation) => {
|
||||
return generatedLocation.generatedSourceActor
|
||||
._setBreakpointForActorAtLocationWithSliding(
|
||||
actor,
|
||||
generatedLocation
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// If this is a non-source mapped source, the original location and
|
||||
// generated location are the same, so we can safely convert between them.
|
||||
let generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
|
||||
let { generatedColumn } = generatedLocation;
|
||||
|
||||
// Try to set the breakpoint on the generated location directly. If this
|
||||
// succeeds, we can avoid the more expensive breakpoint sliding algorithm
|
||||
// below.
|
||||
if (this._setBreakpointForActorAtLocation(actor, generatedLocation)) {
|
||||
return Promise.resolve(actor);
|
||||
return this._setBreakpointAtOriginalLocation(actor, originalLocation)
|
||||
.then((actualLocation) => {
|
||||
if (actualLocation) {
|
||||
return actualLocation;
|
||||
}
|
||||
|
||||
// There were no scripts that matched the given location, so we need to
|
||||
// perform breakpoint sliding.
|
||||
if (generatedColumn === undefined) {
|
||||
// To perform breakpoint sliding for line breakpoints, we need to build
|
||||
// a map from line numbers to a list of entry points for each line,
|
||||
// implemented as a sparse array. An entry point is a (script, offsets)
|
||||
// pair, and represents all offsets in that script that are entry points
|
||||
// for the corresponding line.
|
||||
let lineToEntryPointsMap = [];
|
||||
if (!this.isSourceMapped) {
|
||||
// There were no scripts that matched the given location, so we need to
|
||||
// perform breakpoint sliding.
|
||||
if (originalColumn === undefined) {
|
||||
// To perform breakpoint sliding for line breakpoints, we need to build
|
||||
// a map from line numbers to a list of entry points for each line,
|
||||
// implemented as a sparse array. An entry point is a (script, offsets)
|
||||
// pair, and represents all offsets in that script that are entry points
|
||||
// for the corresponding line.
|
||||
let lineToEntryPointsMap = [];
|
||||
|
||||
// Iterate over all scripts that correspond to this source actor.
|
||||
let scripts = this.scripts.getScriptsBySourceActor(this);
|
||||
for (let script of scripts) {
|
||||
// Get all offsets for each line in the current script. This returns
|
||||
// a map from line numbers fo a list of offsets for each line,
|
||||
// implemented as a sparse array.
|
||||
let lineToOffsetsMap = script.getAllOffsets();
|
||||
// Iterate over all scripts that correspond to this source actor.
|
||||
let scripts = this.scripts.getScriptsBySourceActor(this);
|
||||
for (let script of scripts) {
|
||||
// Get all offsets for each line in the current script. This returns
|
||||
// a map from line numbers fo a list of offsets for each line,
|
||||
// implemented as a sparse array.
|
||||
let lineToOffsetsMap = script.getAllOffsets();
|
||||
|
||||
// Iterate over each line, and add their list of offsets to the map
|
||||
// from line numbers to entry points by forming a (script, offsets)
|
||||
// pair, where script is the current script, and offsets is the list
|
||||
// of offsets for the current line.
|
||||
for (let line = 0; line < lineToOffsetsMap.length; ++line) {
|
||||
let offsets = lineToOffsetsMap[line];
|
||||
if (offsets) {
|
||||
let entryPoints = lineToEntryPointsMap[line];
|
||||
if (!entryPoints) {
|
||||
// We dont have a list of entry points for the current line
|
||||
// number yet, so create it and add it to the map.
|
||||
entryPoints = [];
|
||||
lineToEntryPointsMap[line] = entryPoints;
|
||||
// Iterate over each line, and add their list of offsets to the map
|
||||
// from line numbers to entry points by forming a (script, offsets)
|
||||
// pair, where script is the current script, and offsets is the list
|
||||
// of offsets for the current line.
|
||||
for (let line = 0; line < lineToOffsetsMap.length; ++line) {
|
||||
let offsets = lineToOffsetsMap[line];
|
||||
if (offsets) {
|
||||
let entryPoints = lineToEntryPointsMap[line];
|
||||
if (!entryPoints) {
|
||||
// We dont have a list of entry points for the current line
|
||||
// number yet, so create it and add it to the map.
|
||||
entryPoints = [];
|
||||
lineToEntryPointsMap[line] = entryPoints;
|
||||
}
|
||||
entryPoints.push({ script, offsets });
|
||||
}
|
||||
entryPoints.push({ script, offsets });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let {
|
||||
originalSourceActor,
|
||||
originalLine,
|
||||
originalColumn
|
||||
} = originalLocation;
|
||||
let {
|
||||
originalSourceActor,
|
||||
originalLine,
|
||||
originalColumn
|
||||
} = originalLocation;
|
||||
|
||||
// Now that we have a map from line numbers to a list of entry points
|
||||
// for each line, we can use it to perform breakpoint sliding. Start
|
||||
// at the original line of the breakpoint actor, and keep incrementing
|
||||
// it by one, until either we find a line that has at least one entry
|
||||
// point, or we go past the last line in the map.
|
||||
//
|
||||
// Note that by computing the entire map up front, and implementing it
|
||||
// as a sparse array, we can easily tell when we went past the last line
|
||||
// in the map.
|
||||
let actualLine = originalLine;
|
||||
while (actualLine < lineToEntryPointsMap.length) {
|
||||
let entryPoints = lineToEntryPointsMap[actualLine];
|
||||
if (entryPoints) {
|
||||
setBreakpointForActorAtEntryPoints(actor, entryPoints);
|
||||
break;
|
||||
// Now that we have a map from line numbers to a list of entry points
|
||||
// for each line, we can use it to perform breakpoint sliding. Start
|
||||
// at the original line of the breakpoint actor, and keep incrementing
|
||||
// it by one, until either we find a line that has at least one entry
|
||||
// point, or we go past the last line in the map.
|
||||
//
|
||||
// Note that by computing the entire map up front, and implementing it
|
||||
// as a sparse array, we can easily tell when we went past the last line
|
||||
// in the map.
|
||||
let actualLine = originalLine;
|
||||
while (actualLine < lineToEntryPointsMap.length) {
|
||||
let entryPoints = lineToEntryPointsMap[actualLine];
|
||||
if (entryPoints) {
|
||||
setBreakpointAtEntryPoints(actor, entryPoints);
|
||||
break;
|
||||
}
|
||||
++actualLine;
|
||||
}
|
||||
if (actualLine === lineToEntryPointsMap.length) {
|
||||
// We went past the last line in the map, so breakpoint sliding
|
||||
// failed. Keep the BreakpointActor in the BreakpointActorMap as a
|
||||
// pending breakpoint, so we can try again whenever a new script is
|
||||
// introduced.
|
||||
return originalLocation;
|
||||
}
|
||||
++actualLine;
|
||||
}
|
||||
if (actualLine === lineToEntryPointsMap.length) {
|
||||
// We went past the last line in the map, so breakpoint sliding
|
||||
// failed. Keep the BreakpointActor in the BreakpointActorMap as a
|
||||
// pending breakpoint, so we can try again whenever a new script is
|
||||
// introduced.
|
||||
return Promise.resolve(actor);
|
||||
}
|
||||
|
||||
// If the actual line on which the BreakpointActor was set differs from
|
||||
// the original line that was requested, the BreakpointActor and the
|
||||
// BreakpointActorMap need to be updated accordingly.
|
||||
if (actualLine !== originalLine) {
|
||||
let actualLocation = new OriginalLocation(
|
||||
return new OriginalLocation(
|
||||
originalSourceActor,
|
||||
actualLine
|
||||
);
|
||||
let existingActor = this.breakpointActorMap.getActor(actualLocation);
|
||||
if (existingActor) {
|
||||
actor.onDelete();
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
actor = existingActor;
|
||||
} else {
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
actor.originalLocation = actualLocation;
|
||||
this.breakpointActorMap.setActor(actualLocation, actor);
|
||||
}
|
||||
} else {
|
||||
// TODO: Implement breakpoint sliding for column breakpoints
|
||||
return originalLocation;
|
||||
}
|
||||
} else {
|
||||
// TODO: Refactor breakpoint sliding for source mapped sources.
|
||||
return this.threadActor.sources.getGeneratedLocation(originalLocation)
|
||||
.then((generatedLocation) => {
|
||||
return generatedLocation.generatedSourceActor
|
||||
._setBreakpointAtLocationWithSliding(
|
||||
actor,
|
||||
generatedLocation
|
||||
);
|
||||
});
|
||||
}
|
||||
}).then((actualLocation) => {
|
||||
// If the actual location on which the BreakpointActor ended up being
|
||||
// set differs from the original line that was requested, both the
|
||||
// BreakpointActor and the BreakpointActorMap need to be updated
|
||||
// accordingly.
|
||||
if (!actualLocation.equals(originalLocation)) {
|
||||
let existingActor = this.breakpointActorMap.getActor(actualLocation);
|
||||
if (existingActor) {
|
||||
actor.onDelete();
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
actor = existingActor;
|
||||
} else {
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
actor.originalLocation = actualLocation;
|
||||
this.breakpointActorMap.setActor(actualLocation, actor);
|
||||
}
|
||||
}
|
||||
|
||||
return actor;
|
||||
});
|
||||
},
|
||||
|
||||
_setBreakpointAtOriginalLocation: function (actor, originalLocation) {
|
||||
if (!this.isSourceMapped) {
|
||||
if (!this._setBreakpointAtGeneratedLocation(
|
||||
actor,
|
||||
GeneratedLocation.fromOriginalLocation(originalLocation)
|
||||
)) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return Promise.resolve(originalLocation);
|
||||
} else {
|
||||
return this.sources.getAllGeneratedLocations(originalLocation)
|
||||
.then((generatedLocations) => {
|
||||
if (!this._setBreakpointAtAllGeneratedLocations(
|
||||
actor,
|
||||
generatedLocations
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Promise.resolve(actor);
|
||||
} else {
|
||||
// TODO: Implement breakpoint sliding for column breakpoints
|
||||
return Promise.resolve(actor);
|
||||
return this.threadActor.sources.getOriginalLocation(generatedLocations[0]);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_setBreakpointAtAllGeneratedLocations: function (actor, generatedLocations) {
|
||||
let success = false;
|
||||
for (let generatedLocation of generatedLocations) {
|
||||
if (this._setBreakpointAtGeneratedLocation(
|
||||
actor,
|
||||
generatedLocation
|
||||
)) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
},
|
||||
|
||||
/*
|
||||
@ -2949,7 +2992,7 @@ SourceActor.prototype = {
|
||||
* @returns A Boolean that is true if the BreakpointActor was set as a
|
||||
* breakpoint handler on at least one script, and false otherwise.
|
||||
*/
|
||||
_setBreakpointForActorAtLocation: function (actor, generatedLocation) {
|
||||
_setBreakpointAtGeneratedLocation: function (actor, generatedLocation) {
|
||||
let { generatedLine, generatedColumn } = generatedLocation;
|
||||
|
||||
// Find all scripts that match the given source actor and line number.
|
||||
@ -2990,7 +3033,7 @@ SourceActor.prototype = {
|
||||
if (entryPoints.length === 0) {
|
||||
return false;
|
||||
}
|
||||
setBreakpointForActorAtEntryPoints(actor, entryPoints);
|
||||
setBreakpointAtEntryPoints(actor, entryPoints);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -3003,7 +3046,7 @@ SourceActor.prototype = {
|
||||
* locations, because there is no guarantee that the next line in the
|
||||
* generated source corresponds to the next line in an original source.
|
||||
*
|
||||
* The only place this method is still used is from setBreakpointForActor
|
||||
* The only place this method is still used is from setBreakpoint
|
||||
* when called for a source mapped source. Once that code has been refactored,
|
||||
* this method can be removed.
|
||||
*
|
||||
@ -3017,7 +3060,7 @@ SourceActor.prototype = {
|
||||
* @returns A Boolean that is true if the BreakpointActor was set as a
|
||||
* breakpoint handler on at least one script, and false otherwise.
|
||||
*/
|
||||
_setBreakpointForActorAtLocationWithSliding: function (actor, generatedLocation) {
|
||||
_setBreakpointAtLocationWithSliding: function (actor, generatedLocation) {
|
||||
let originalLocation = actor.originalLocation;
|
||||
let { generatedLine, generatedColumn } = generatedLocation;
|
||||
|
||||
@ -3082,7 +3125,7 @@ SourceActor.prototype = {
|
||||
actualGeneratedLocation = generatedLocation;
|
||||
}
|
||||
|
||||
setBreakpointForActorAtEntryPoints(actor, result.entryPoints);
|
||||
setBreakpointAtEntryPoints(actor, result.entryPoints);
|
||||
}
|
||||
|
||||
return Promise.resolve().then(() => {
|
||||
@ -3091,26 +3134,6 @@ SourceActor.prototype = {
|
||||
} else {
|
||||
return OriginalLocation.fromGeneratedLocation(actualGeneratedLocation);
|
||||
}
|
||||
}).then((actualOriginalLocation) => {
|
||||
if (!actualOriginalLocation.equals(originalLocation)) {
|
||||
// Check whether we already have a breakpoint actor for the actual
|
||||
// location. If we do have an existing actor, then the actor we created
|
||||
// above is redundant and must be destroyed. If we do not have an existing
|
||||
// actor, we need to update the breakpoint store with the new location.
|
||||
|
||||
let existingActor = this.breakpointActorMap.getActor(actualOriginalLocation);
|
||||
if (existingActor) {
|
||||
actor.onDelete();
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
actor = existingActor;
|
||||
} else {
|
||||
actor.originalLocation = actualOriginalLocation;
|
||||
this.breakpointActorMap.deleteActor(originalLocation);
|
||||
this.breakpointActorMap.setActor(actualOriginalLocation, actor);
|
||||
}
|
||||
}
|
||||
|
||||
return actor;
|
||||
});
|
||||
},
|
||||
|
||||
@ -5432,7 +5455,7 @@ function findEntryPointsForLine(scripts, line) {
|
||||
* @param Array entryPoints
|
||||
* An array of objects of the form `{ script, offsets }`.
|
||||
*/
|
||||
function setBreakpointForActorAtEntryPoints(actor, entryPoints) {
|
||||
function setBreakpointAtEntryPoints(actor, entryPoints) {
|
||||
for (let { script, offsets } of entryPoints) {
|
||||
actor.addScript(script);
|
||||
for (let offset of offsets) {
|
||||
|
@ -590,6 +590,17 @@ TabSources.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
getAllGeneratedLocations: function (originalLocation) {
|
||||
return this.getGeneratedLocation(originalLocation)
|
||||
.then((generatedLocation) => {
|
||||
if (generatedLocation.generatedLine === null &&
|
||||
generatedLocation.generatedColumn === null) {
|
||||
return [];
|
||||
}
|
||||
return [generatedLocation];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise of the location in the generated source corresponding to
|
||||
* the original source and line given.
|
||||
|
@ -330,7 +330,7 @@ BrowserTabList.prototype.getList = function() {
|
||||
// As a sanity check, make sure all the actors presently in our map get
|
||||
// picked up when we iterate over all windows' tabs.
|
||||
let initialMapSize = this._actorByBrowser.size;
|
||||
let foundCount = 0;
|
||||
this._foundCount = 0;
|
||||
|
||||
// To avoid mysterious behavior if tabs are closed or opened mid-iteration,
|
||||
// we update the map first, and then make a second pass over it to yield
|
||||
@ -340,27 +340,18 @@ BrowserTabList.prototype.getList = function() {
|
||||
let actorPromises = [];
|
||||
|
||||
for (let browser of this._getBrowsers()) {
|
||||
// Do we have an existing actor for this browser? If not, create one.
|
||||
let actor = this._actorByBrowser.get(browser);
|
||||
if (actor) {
|
||||
actorPromises.push(actor.update());
|
||||
foundCount++;
|
||||
} else if (this._isRemoteBrowser(browser)) {
|
||||
actor = new RemoteBrowserTabActor(this._connection, browser);
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
actorPromises.push(actor.connect());
|
||||
} else {
|
||||
actor = new BrowserTabActor(this._connection, browser,
|
||||
browser.getTabBrowser());
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
actorPromises.push(promise.resolve(actor));
|
||||
}
|
||||
|
||||
// Set the 'selected' properties on all actors correctly.
|
||||
actor.selected = browser === selectedBrowser;
|
||||
let selected = browser === selectedBrowser;
|
||||
actorPromises.push(
|
||||
this._getActorForBrowser(browser)
|
||||
.then(actor => {
|
||||
// Set the 'selected' properties on all actors correctly.
|
||||
actor.selected = selected;
|
||||
return actor;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this._testing && initialMapSize !== foundCount)
|
||||
if (this._testing && initialMapSize !== this._foundCount)
|
||||
throw Error("_actorByBrowser map contained actors for dead tabs");
|
||||
|
||||
this._mustNotify = true;
|
||||
@ -369,6 +360,68 @@ BrowserTabList.prototype.getList = function() {
|
||||
return promise.all(actorPromises);
|
||||
};
|
||||
|
||||
BrowserTabList.prototype._getActorForBrowser = function(browser) {
|
||||
// Do we have an existing actor for this browser? If not, create one.
|
||||
let actor = this._actorByBrowser.get(browser);
|
||||
if (actor) {
|
||||
this._foundCount++;
|
||||
return actor.update();
|
||||
} else if (this._isRemoteBrowser(browser)) {
|
||||
actor = new RemoteBrowserTabActor(this._connection, browser);
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
this._checkListening();
|
||||
return actor.connect();
|
||||
} else {
|
||||
actor = new BrowserTabActor(this._connection, browser,
|
||||
browser.getTabBrowser());
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
this._checkListening();
|
||||
return promise.resolve(actor);
|
||||
}
|
||||
};
|
||||
|
||||
BrowserTabList.prototype.getTab = function({ outerWindowID, tabId }) {
|
||||
if (typeof(outerWindowID) == "number") {
|
||||
// Tabs in parent process
|
||||
for (let browser of this._getBrowsers()) {
|
||||
if (browser.contentWindow) {
|
||||
let windowUtils = browser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
if (windowUtils.outerWindowID === outerWindowID) {
|
||||
return this._getActorForBrowser(browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return promise.reject({
|
||||
error: "noTab",
|
||||
message: "Unable to find tab with outerWindowID '" + outerWindowID + "'"
|
||||
});
|
||||
} else if (typeof(tabId) == "number") {
|
||||
// Tabs OOP
|
||||
for (let browser of this._getBrowsers()) {
|
||||
if (browser.frameLoader.tabParent &&
|
||||
browser.frameLoader.tabParent.tabId === tabId) {
|
||||
return this._getActorForBrowser(browser);
|
||||
}
|
||||
}
|
||||
return promise.reject({
|
||||
error: "noTab",
|
||||
message: "Unable to find tab with tabId '" + tabId + "'"
|
||||
});
|
||||
}
|
||||
|
||||
let topXULWindow = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
|
||||
if (topXULWindow) {
|
||||
let selectedBrowser = this._getSelectedBrowser(topXULWindow);
|
||||
return this._getActorForBrowser(selectedBrowser);
|
||||
}
|
||||
return promise.reject({
|
||||
error: "noTab",
|
||||
message: "Unable to find any selected browser"
|
||||
});
|
||||
};
|
||||
|
||||
Object.defineProperty(BrowserTabList.prototype, 'onListChanged', {
|
||||
enumerable: true, configurable:true,
|
||||
get: function() { return this._onListChanged; },
|
||||
|
@ -77,7 +77,7 @@ skip-if = buildapp == 'mulet'
|
||||
[test_settings.html]
|
||||
[test_connectToChild.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_attachProcess.html]
|
||||
[test_getProcess.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_director.html]
|
||||
[test_director_connectToChild.html]
|
||||
|