Merge fx-team to m-c a=merge CLOSED TREE

This commit is contained in:
Wes Kocher 2015-03-26 17:22:05 -07:00
commit 037be54ca7
106 changed files with 1275 additions and 575 deletions

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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) => {

View File

@ -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",

View File

@ -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"
}, {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {

View File

@ -43,7 +43,7 @@ function test() {
}
function testChromeActor() {
gClient.attachProcess().then(aResponse => {
gClient.getProcess().then(aResponse => {
gClient.addListener("newGlobal", onNewGlobal);
gClient.addListener("newSource", onNewSource);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 () {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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')

View File

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

View File

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

View File

@ -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',

View File

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

View File

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

View File

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

View File

@ -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')

View File

@ -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 = [

View File

@ -230,6 +230,9 @@ function testInit() {
if (config.closeWhenDone) {
require("sdk/system").exit(failed == 0 ? 0 : 1);
}
else {
loaderModule.unload(loader, "shutdown");
}
}
function testNextModule() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More