CLOSED TREE
This commit is contained in:
Ryan VanderMeulen 2016-05-05 10:06:54 -04:00
Родитель 3b28920d8e 0fb164e885
Коммит 70ba843b03
94 изменённых файлов: 1015 добавлений и 701 удалений

Просмотреть файл

@ -99,9 +99,16 @@ devtools/client/promisedebugger/**
devtools/client/responsivedesign/**
devtools/client/scratchpad/**
devtools/client/shadereditor/**
devtools/client/shared/**
devtools/client/shared/*.js
devtools/client/shared/*.jsm
!devtools/client/shared/css-color.js
!devtools/client/shared/css-color-db.js
!devtools/client/shared/css-parsing-utils.js
devtools/client/shared/components/**
devtools/client/shared/redux/**
devtools/client/shared/test/**
devtools/client/shared/vendor/**
devtools/client/shared/widgets/**
devtools/client/sourceeditor/**
devtools/client/webaudioeditor/**
devtools/client/webconsole/**

Просмотреть файл

@ -1379,6 +1379,19 @@ var BookmarkingUI = {
this._populateRecentBookmarks(aHeaderItem, aExtraCSSClass);
};
this._recentlyBookmarkedObserver = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver,
Ci.nsISupportsWeakReference
])
};
this._recentlyBookmarkedObserver.onItemRemoved = () => {
// Update the menu when a bookmark has been removed.
// The native menubar on Mac doesn't support live update, so this won't
// work there.
this._populateRecentBookmarks(aHeaderItem, aExtraCSSClass);
};
let updatePlacesContextMenu = (shouldHidePrefUI = false) => {
let prefEnabled = !shouldHidePrefUI && Services.prefs.getBoolPref(this.RECENTLY_BOOKMARKED_PREF);
document.getElementById("placesContext_showRecentlyBookmarked").hidden = shouldHidePrefUI || prefEnabled;
@ -1402,12 +1415,15 @@ var BookmarkingUI = {
updatePlacesContextMenu(true);
Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
PlacesUtils.bookmarks.removeObserver(this._recentlyBookmarkedObserver);
this._recentlyBookmarkedObserver = null;
placesContextMenu.removeEventListener("popupshowing", onPlacesContextMenuShowing);
bookmarksMenu.removeEventListener("popuphidden", onBookmarksMenuHidden);
}
};
Services.prefs.addObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
PlacesUtils.bookmarks.addObserver(this._recentlyBookmarkedObserver, true);
placesContextMenu.addEventListener("popupshowing", onPlacesContextMenuShowing);
bookmarksMenu.addEventListener("popuphidden", onBookmarksMenuHidden);
},

Просмотреть файл

@ -12,6 +12,17 @@ XPCOMUtils.defineLazyGetter(this, "History", () => {
extensions.registerSchemaAPI("history", "history", (extension, context) => {
return {
history: {
deleteAll: function() {
return History.clear();
},
deleteRange: function(filter) {
let newFilter = {
beginDate: new Date(filter.startTime),
endDate: new Date(filter.endTime),
};
// History.removeVisitsByFilter returns a boolean, but our API should return nothing
return History.removeVisitsByFilter(newFilter).then(() => undefined);
},
deleteUrl: function(details) {
let url = details.url;
// History.remove returns a boolean, but our API should return nothing

Просмотреть файл

@ -224,7 +224,6 @@
},
{
"name": "deleteRange",
"unsupported": true,
"type": "function",
"description": "Removes all items within the specified date range from the history. Pages will not be removed from the history unless all visits fall within the range.",
"async": "callback",
@ -252,7 +251,6 @@
},
{
"name": "deleteAll",
"unsupported": true,
"type": "function",
"description": "Deletes all items from the history.",
"async": "callback",

Просмотреть файл

@ -5,37 +5,33 @@
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
add_task(function* test_history_schema() {
add_task(function* test_delete() {
function background() {
browser.test.assertTrue(browser.history, "browser.history API exists");
browser.test.notifyPass("history-schema");
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["history"],
},
background: `(${background})()`,
});
yield extension.startup();
yield extension.awaitFinish("history-schema");
yield extension.unload();
});
add_task(function* test_delete_url() {
const TEST_URL = `http://example.com/${Math.random()}`;
function background() {
browser.test.onMessage.addListener((msg, url) => {
browser.history.deleteUrl({url: url}).then(result => {
browser.test.assertEq(undefined, result, "browser.history.deleteUrl returns nothing");
browser.test.sendMessage("url-deleted");
});
browser.test.onMessage.addListener((msg, arg) => {
if (msg === "delete-url") {
browser.history.deleteUrl({url: arg}).then(result => {
browser.test.assertEq(undefined, result, "browser.history.deleteUrl returns nothing");
browser.test.sendMessage("url-deleted");
});
} else if (msg === "delete-range") {
browser.history.deleteRange(arg).then(result => {
browser.test.assertEq(undefined, result, "browser.history.deleteUrl returns nothing");
browser.test.sendMessage("range-deleted");
});
} else if (msg === "delete-all") {
browser.history.deleteAll().then(result => {
browser.test.assertEq(undefined, result, "browser.history.deleteUrl returns nothing");
browser.test.sendMessage("urls-deleted");
});
}
});
browser.test.sendMessage("ready");
}
const REFERENCE_DATE = new Date(1999, 9, 9, 9, 9);
let {PlacesUtils} = Cu.import("resource://gre/modules/PlacesUtils.jsm", {});
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["history"],
@ -47,12 +43,62 @@ add_task(function* test_delete_url() {
yield PlacesTestUtils.clearHistory();
yield extension.awaitMessage("ready");
yield PlacesTestUtils.addVisits(TEST_URL);
ok(yield PlacesTestUtils.isPageInDB(TEST_URL), `${TEST_URL} found in history database`);
let visits = [];
extension.sendMessage("delete-url", TEST_URL);
// Add 5 visits for one uri and 3 visits for 3 others
for (let i = 0; i < 8; ++i) {
let baseUri = "http://mozilla.com/test_history/";
let uri = (i > 4) ? `${baseUri}${i}/` : baseUri;
let dbDate = (Number(REFERENCE_DATE) + 3600 * 1000 * i) * 1000;
let visit = {
uri,
title: "visit " + i,
visitDate: dbDate,
};
visits.push(visit);
}
yield PlacesTestUtils.addVisits(visits);
is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 5, "5 visits for uri found in history database");
let testUrl = visits[6].uri.spec;
ok(yield PlacesTestUtils.isPageInDB(testUrl), "expected url found in history database");
extension.sendMessage("delete-url", testUrl);
yield extension.awaitMessage("url-deleted");
ok(!(yield PlacesTestUtils.isPageInDB(TEST_URL)), `${TEST_URL} not found in history database`);
is(yield PlacesTestUtils.isPageInDB(testUrl), false, "expected url not found in history database");
let filter = {
startTime: visits[1].visitDate / 1000,
endTime: visits[3].visitDate / 1000,
};
extension.sendMessage("delete-range", filter);
yield extension.awaitMessage("range-deleted");
ok(yield PlacesTestUtils.isPageInDB(visits[0].uri), "expected uri found in history database");
is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 2, "2 visits for uri found in history database");
ok(yield PlacesTestUtils.isPageInDB(visits[5].uri), "expected uri found in history database");
is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 1, "1 visit for uri found in history database");
filter.startTime = visits[0].visitDate / 1000;
filter.endTime = visits[5].visitDate / 1000;
extension.sendMessage("delete-range", filter);
yield extension.awaitMessage("range-deleted");
is(yield PlacesTestUtils.isPageInDB(visits[0].uri), false, "expected uri not found in history database");
is(yield PlacesTestUtils.visitsInDB(visits[0].uri), 0, "0 visits for uri found in history database");
is(yield PlacesTestUtils.isPageInDB(visits[5].uri), false, "expected uri not found in history database");
is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 0, "0 visits for uri found in history database");
ok(yield PlacesTestUtils.isPageInDB(visits[7].uri), "expected uri found in history database");
extension.sendMessage("delete-all");
yield extension.awaitMessage("urls-deleted");
is(PlacesUtils.history.hasHistoryEntries, false, "history is empty");
yield extension.unload();
});

Просмотреть файл

@ -165,7 +165,9 @@ var gSecurityPane = {
if (enableSafeBrowsing.checked) {
blockDownloads.removeAttribute("disabled");
blockUncommonUnwanted.removeAttribute("disabled");
if (blockDownloads.checked) {
blockUncommonUnwanted.removeAttribute("disabled");
}
} else {
blockDownloads.setAttribute("disabled", "true");
blockUncommonUnwanted.setAttribute("disabled", "true");

Просмотреть файл

@ -43,8 +43,9 @@ add_task(function*() {
"safebrowsing.malware.enabled is set correctly");
// check if the other checkboxes have updated
is(blockDownloads.hasAttribute("disabled"), checked, "block downloads checkbox is set correctly");
is(blockUncommon.hasAttribute("disabled"), checked, "block uncommon checkbox is set correctly");
checked = checkbox.checked;
is(blockDownloads.hasAttribute("disabled"), !checked, "block downloads checkbox is set correctly");
is(blockUncommon.hasAttribute("disabled"), !checked || !blockDownloads.checked, "block uncommon checkbox is set correctly");
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
}

Просмотреть файл

@ -1871,6 +1871,16 @@ this.UITour = {
let props = ["defaultUpdateChannel", "version"];
let appinfo = {};
props.forEach(property => appinfo[property] = Services.appinfo[property]);
// Identifier of the partner repack, as stored in preference "distribution.id"
// and included in Firefox and other update pings. Note this is not the same as
// Services.appinfo.distributionID (value of MOZ_DISTRIBUTION_ID is set at build time).
let distribution = "default";
try {
distribution = Services.prefs.getDefaultBranch("distribution.").getCharPref("id");
} catch(e) {}
appinfo["distribution"] = distribution;
let isDefaultBrowser = null;
try {
let shell = aWindow.getShellService();

Просмотреть файл

@ -292,6 +292,22 @@ var tests = [
gContentAPI.getConfiguration("appinfo", callback);
},
function test_getConfigurationDistribution(done) {
gContentAPI.getConfiguration("appinfo", (result) => {
ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
is(result.distribution, "default", "Should be \"default\" without preference set.");
let defaults = Services.prefs.getDefaultBranch("distribution.");
let testDistributionID = "TestDistribution";
defaults.setCharPref("id", testDistributionID);
gContentAPI.getConfiguration("appinfo", (result) => {
ok(typeof(result.distribution) !== "undefined", "Check distribution isn't undefined.");
is(result.distribution, testDistributionID, "Should have the distribution as set in preference.");
done();
});
});
},
function test_addToolbarButton(done) {
let placement = CustomizableUI.getPlacementOfWidget("panic-button");
is(placement, null, "default UI has panic button in the palette");

Просмотреть файл

@ -40,8 +40,11 @@ var Pocket = {
window.pktUI.tryToSaveCurrentPage();
}
// pocketPanelDidHide in main.js set iframe to about:blank when it was
// hidden, make sure we're loading the save panel.
if (iframe.contentDocument &&
iframe.contentDocument.readyState == "complete") {
iframe.contentDocument.readyState == "complete" &&
iframe.contentDocument.documentURI != "about:blank") {
window.pktUI.pocketPanelDidShow();
} else {
// iframe didn't load yet. This seems to always be the case when in

Просмотреть файл

@ -16,26 +16,24 @@
:root[devtoolstheme="dark"] {
/* Chrome */
--chrome-background-color: #1C2126;
--chrome-background-color: #272b35;
--chrome-color: #F5F7FA;
--chrome-secondary-background-color: #39424D;
--chrome-secondary-background-color: #393F4C;
--chrome-navigator-toolbox-separator-color: rgba(0,0,0,.2);
--chrome-nav-bar-separator-color: rgba(0,0,0,.2);
--chrome-nav-buttons-background: #252C33;
--chrome-nav-buttons-hover-background: #1B2127;
--chrome-nav-bar-controls-border-color: #1D2328;
--chrome-selection-color: #fff;
--chrome-selection-background-color: #074D75;
--chrome-selection-background-color: #5675B9;
/* Tabs */
--tabs-toolbar-color: #F5F7FA;
--tab-background-color: #1C2126;
--tab-hover-background-color: #07090a;
--tab-selection-color: #f5f7fa;
--tab-selection-background-color: #1a4666;
--tab-selection-box-shadow: 0 2px 0 #D7F1FF inset,
0 -2px 0 rgba(0,0,0,.05) inset,
0 -1px 0 rgba(0,0,0,.3) inset;
--tab-selection-background-color: #5675B9;
--tab-selection-box-shadow: none;
--pinned-tab-glow: radial-gradient(22px at center calc(100% - 2px), rgba(76,158,217,0.9) 13%, rgba(0,0,0,0.4) 16%, transparent 70%);
/* Toolbar buttons */
@ -45,7 +43,7 @@
--toolbarbutton-active-background: rgba(25,33,38,1) linear-gradient(rgba(25,33,38,1), rgba(25,33,38,1)) border-box;
--toolbarbutton-active-boxshadow: none;
--toolbarbutton-active-bordercolor: rgba(25,33,38,.8);
--toolbarbutton-checkedhover-backgroundcolor: #1D4F73;
--toolbarbutton-checkedhover-backgroundcolor: #3C5283;
/* Url and search bars */
--url-and-searchbar-background-color: #171B1F;
@ -293,7 +291,6 @@ window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
.tabbrowser-tab[visuallyselected] {
color: var(--tab-selection-color) !important; /* Override color: inherit */
background-color: var(--tab-selection-background-color);
box-shadow: var(--tab-selection-box-shadow);
}
/* Don't need space for the tab curves (66px - 30px) */

Просмотреть файл

@ -8,6 +8,8 @@
"Cc": true,
"Ci": true,
"Components": true,
"clearInterval": true,
"clearTimeout": true,
"console": true,
"Cr": true,
"Cu": true,
@ -20,6 +22,8 @@
"module": true,
"reportError": true,
"require": true,
"setInterval": true,
"setTimeout": true,
"Services": true,
"Task": true,
"XPCNativeWrapper": true,

Просмотреть файл

@ -9,7 +9,6 @@ const Telemetry = require("devtools/client/shared/telemetry");
const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js");
const promise = require("promise");
const Services = require("Services");
const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
loader.lazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"]

Просмотреть файл

@ -4,7 +4,11 @@
"use strict";
const { Front, FrontClassWithSpec } = require("devtools/server/protocol.js");
const { originalSourceSpec } = require("devtools/shared/specs/stylesheets.js");
const {
originalSourceSpec,
mediaRuleSpec
} = require("devtools/shared/specs/stylesheets.js");
const events = require("sdk/event/core.js");
/**
* The client-side counterpart for an OriginalSourceActor.
@ -34,3 +38,49 @@ const OriginalSourceFront = FrontClassWithSpec(originalSourceSpec, {
});
exports.OriginalSourceFront = OriginalSourceFront;
/**
* Corresponding client-side front for a MediaRuleActor.
*/
const MediaRuleFront = FrontClassWithSpec(mediaRuleSpec, {
initialize: function (client, form) {
Front.prototype.initialize.call(this, client, form);
this._onMatchesChange = this._onMatchesChange.bind(this);
events.on(this, "matches-change", this._onMatchesChange);
},
_onMatchesChange: function (matches) {
this._form.matches = matches;
},
form: function (form, detail) {
if (detail === "actorid") {
this.actorID = form;
return;
}
this.actorID = form.actor;
this._form = form;
},
get mediaText() {
return this._form.mediaText;
},
get conditionText() {
return this._form.conditionText;
},
get matches() {
return this._form.matches;
},
get line() {
return this._form.line || -1;
},
get column() {
return this._form.column || -1;
},
get parentStyleSheet() {
return this.conn.getActor(this._form.parentStyleSheet);
}
});
exports.MediaRuleFront = MediaRuleFront;

Просмотреть файл

@ -13,8 +13,10 @@ const promise = require("promise");
const FocusManager = Services.focus;
const {waitForTick} = require("devtools/shared/DevToolsUtils");
const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
const ENSURE_SELECTION_VISIBLE_DELAY_MS = 50;
const ELLIPSIS = Services.prefs.getComplexValue(
"intl.ellipsis",
Ci.nsIPrefLocalizedString).data;
const MAX_LABEL_LENGTH = 40;
const LOW_PRIORITY_ELEMENTS = {
"HEAD": true,
@ -57,7 +59,7 @@ HTMLBreadcrumbs.prototype = {
return this.inspector.walker;
},
_init: function() {
_init: function () {
this.container = this.chromeDoc.getElementById("inspector-breadcrumbs");
// These separators are used for CSS purposes only, and are positioned
@ -70,7 +72,7 @@ HTMLBreadcrumbs.prototype = {
"<box id='breadcrumb-separator-normal'></box>";
this.container.parentNode.appendChild(this.separators);
this.container.addEventListener("mousedown", this, true);
this.container.addEventListener("click", this, true);
this.container.addEventListener("keypress", this, true);
this.container.addEventListener("mouseover", this, true);
this.container.addEventListener("mouseleave", this, true);
@ -113,7 +115,7 @@ HTMLBreadcrumbs.prototype = {
* when the breadcrumbs' selection has changed while the promise
* was outstanding.
*/
selectionGuard: function() {
selectionGuard: function () {
let selection = this.selection.nodeFront;
return result => {
if (selection != this.selection.nodeFront) {
@ -127,7 +129,7 @@ HTMLBreadcrumbs.prototype = {
* Warn if rejection was caused by selection change, print an error otherwise.
* @param {Error} err
*/
selectionGuardEnd: function(err) {
selectionGuardEnd: function (err) {
// If the error is selection-changed, this is expected, the selection
// changed while we were waiting for a promise to resolve, so there's no
// need to proceed with the current update, and we should be silent.
@ -141,7 +143,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node The node to pretty-print
* @return {String}
*/
prettyPrintNodeAsText: function(node) {
prettyPrintNodeAsText: function (node) {
let text = node.tagName.toLowerCase();
if (node.isPseudoElement) {
text = node.isBeforePseudoElement ? "::before" : "::after";
@ -173,7 +175,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node The node to pretty-print
* @returns {DocumentFragment}
*/
prettyPrintNodeAsXUL: function(node) {
prettyPrintNodeAsXUL: function (node) {
let fragment = this.chromeDoc.createDocumentFragment();
let tagLabel = this.chromeDoc.createElement("label");
@ -232,62 +234,13 @@ HTMLBreadcrumbs.prototype = {
return fragment;
},
/**
* Open the sibling menu.
* @param {DOMNode} button the button representing the node.
* @param {NodeFront} node the node we want the siblings from.
*/
openSiblingMenu: function(button, node) {
// We make sure that the targeted node is selected
// because we want to use the nodemenu that only works
// for inspector.selection
this.navigateTo(node);
// Build a list of extra menu items that will be appended at the end of the
// inspector node context menu.
let items = [this.chromeDoc.createElement("menuseparator")];
this.walker.siblings(node, {
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
}).then(siblings => {
let nodes = siblings.nodes;
for (let i = 0; i < nodes.length; i++) {
// Skip siblings of the documentElement node.
if (nodes[i].nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
continue;
}
let item = this.chromeDoc.createElement("menuitem");
if (nodes[i] === node) {
item.setAttribute("disabled", "true");
item.setAttribute("checked", "true");
}
item.setAttribute("type", "radio");
item.setAttribute("label", this.prettyPrintNodeAsText(nodes[i]));
let self = this;
item.onmouseup = (function(node) {
return function() {
self.navigateTo(node);
};
})(nodes[i]);
items.push(item);
}
// Append the items to the inspector node context menu and show the menu.
this.inspector.showNodeMenu(button, "before_start", items);
});
},
/**
* Generic event handler.
* @param {DOMEvent} event.
*/
handleEvent: function(event) {
if (event.type == "mousedown" && event.button == 0) {
this.handleMouseDown(event);
handleEvent: function (event) {
if (event.type == "click" && event.button == 0) {
this.handleClick(event);
} else if (event.type == "keypress" && this.selection.isElementNode()) {
this.handleKeyPress(event);
} else if (event.type == "mouseover") {
@ -304,7 +257,7 @@ HTMLBreadcrumbs.prototype = {
* already selected breadcrumb, move focus to it.
* @param {DOMEvent} event.
*/
handleFocus: function(event) {
handleFocus: function (event) {
let control = this.container.querySelector(
".breadcrumbs-widget-item[checked]");
if (control && control !== event.target) {
@ -316,46 +269,21 @@ HTMLBreadcrumbs.prototype = {
},
/**
* On click and hold, open the siblings menu.
* On click navigate to the correct node.
* @param {DOMEvent} event.
*/
handleMouseDown: function(event) {
let timer;
let container = this.container;
function openMenu(event) {
cancelHold();
let target = event.originalTarget;
if (target.tagName == "button") {
target.onBreadcrumbsHold();
}
handleClick: function (event) {
let target = event.originalTarget;
if (target.tagName == "button") {
target.onBreadcrumbsClick();
}
function handleClick(event) {
cancelHold();
let target = event.originalTarget;
if (target.tagName == "button") {
target.onBreadcrumbsClick();
}
}
let window = this.chromeWin;
function cancelHold(event) {
window.clearTimeout(timer);
container.removeEventListener("mouseout", cancelHold, false);
container.removeEventListener("mouseup", handleClick, false);
}
container.addEventListener("mouseout", cancelHold, false);
container.addEventListener("mouseup", handleClick, false);
timer = window.setTimeout(openMenu, 500, event);
},
/**
* On mouse over, highlight the corresponding content DOM Node.
* @param {DOMEvent} event.
*/
handleMouseOver: function(event) {
handleMouseOver: function (event) {
let target = event.originalTarget;
if (target.tagName == "button") {
target.onBreadcrumbsHover();
@ -366,7 +294,7 @@ HTMLBreadcrumbs.prototype = {
* On mouse leave, make sure to unhighlight.
* @param {DOMEvent} event.
*/
handleMouseLeave: function(event) {
handleMouseLeave: function (event) {
this.inspector.toolbox.highlighterUtils.unhighlight();
},
@ -374,7 +302,7 @@ HTMLBreadcrumbs.prototype = {
* On key press, navigate the node hierarchy.
* @param {DOMEvent} event.
*/
handleKeyPress: function(event) {
handleKeyPress: function (event) {
let navigate = promise.resolve(null);
this._keyPromise = (this._keyPromise || promise.resolve(null)).then(() => {
@ -433,15 +361,17 @@ HTMLBreadcrumbs.prototype = {
/**
* Remove nodes and clean up.
*/
destroy: function() {
destroy: function () {
this.selection.off("new-node-front", this.update);
this.selection.off("pseudoclass", this.updateSelectors);
this.selection.off("attribute-changed", this.updateSelectors);
this.inspector.off("markupmutation", this.update);
this.container.removeEventListener("underflow", this.onscrollboxreflow, false);
this.container.removeEventListener("overflow", this.onscrollboxreflow, false);
this.container.removeEventListener("mousedown", this, true);
this.container.removeEventListener("underflow",
this.onscrollboxreflow, false);
this.container.removeEventListener("overflow",
this.onscrollboxreflow, false);
this.container.removeEventListener("click", this, true);
this.container.removeEventListener("keypress", this, true);
this.container.removeEventListener("mouseover", this, true);
this.container.removeEventListener("mouseleave", this, true);
@ -461,7 +391,7 @@ HTMLBreadcrumbs.prototype = {
/**
* Empty the breadcrumbs container.
*/
empty: function() {
empty: function () {
while (this.container.hasChildNodes()) {
this.container.firstChild.remove();
}
@ -471,9 +401,10 @@ HTMLBreadcrumbs.prototype = {
* Set which button represent the selected node.
* @param {Number} index Index of the displayed-button to select.
*/
setCursor: function(index) {
setCursor: function (index) {
// Unselect the previously selected button
if (this.currentIndex > -1 && this.currentIndex < this.nodeHierarchy.length) {
if (this.currentIndex > -1
&& this.currentIndex < this.nodeHierarchy.length) {
this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked");
}
if (index > -1) {
@ -490,7 +421,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node.
* @returns {Number} The index for this node or -1 if not found.
*/
indexOf: function(node) {
indexOf: function (node) {
for (let i = this.nodeHierarchy.length - 1; i >= 0; i--) {
if (this.nodeHierarchy[i].node === node) {
return i;
@ -504,14 +435,14 @@ HTMLBreadcrumbs.prototype = {
* index.
* @param {Number} index.
*/
cutAfter: function(index) {
cutAfter: function (index) {
while (this.nodeHierarchy.length > (index + 1)) {
let toRemove = this.nodeHierarchy.pop();
this.container.removeChild(toRemove.button);
}
},
navigateTo: function(node) {
navigateTo: function (node) {
if (node) {
this.selection.setNodeFront(node, "breadcrumbs");
} else {
@ -524,7 +455,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node The node from the page.
* @return {DOMNode} The <button> for this node.
*/
buildButton: function(node) {
buildButton: function (node) {
let button = this.chromeDoc.createElement("button");
button.appendChild(this.prettyPrintNodeAsXUL(node));
button.className = "breadcrumbs-widget-item";
@ -538,6 +469,10 @@ HTMLBreadcrumbs.prototype = {
}
};
button.onclick = () => {
button.focus();
};
button.onBreadcrumbsClick = () => {
this.navigateTo(node);
};
@ -546,16 +481,6 @@ HTMLBreadcrumbs.prototype = {
this.inspector.toolbox.highlighterUtils.highlightNodeFront(node);
};
button.onclick = (function _onBreadcrumbsRightClick(event) {
button.focus();
if (event.button == 2) {
this.openSiblingMenu(button, node);
}
}).bind(this);
button.onBreadcrumbsHold = (function _onBreadcrumbsHold() {
this.openSiblingMenu(button, node);
}).bind(this);
return button;
},
@ -563,7 +488,7 @@ HTMLBreadcrumbs.prototype = {
* Connecting the end of the breadcrumbs to a node.
* @param {NodeFront} node The node to reach.
*/
expand: function(node) {
expand: function (node) {
let fragment = this.chromeDoc.createDocumentFragment();
let lastButtonInserted = null;
let originalLength = this.nodeHierarchy.length;
@ -593,7 +518,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node The parent node.
* @return {Promise} Resolves to the NodeFront.
*/
getInterestingFirstNode: function(node) {
getInterestingFirstNode: function (node) {
let deferred = promise.defer();
let fallback = null;
@ -605,15 +530,15 @@ HTMLBreadcrumbs.prototype = {
maxNodes: 10,
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
}).then(this.selectionGuard()).then(response => {
for (let node of response.nodes) {
if (!(node.tagName in LOW_PRIORITY_ELEMENTS)) {
deferred.resolve(node);
for (let childNode of response.nodes) {
if (!(childNode.tagName in LOW_PRIORITY_ELEMENTS)) {
deferred.resolve(childNode);
return;
}
if (!fallback) {
fallback = node;
fallback = childNode;
}
lastNode = node;
lastNode = childNode;
}
if (response.hasLast) {
deferred.resolve(fallback);
@ -632,7 +557,7 @@ HTMLBreadcrumbs.prototype = {
* @param {NodeFront} node.
* @return {Number} Index of the ancestor in the cache, or -1 if not found.
*/
getCommonAncestor: function(node) {
getCommonAncestor: function (node) {
while (node) {
let idx = this.indexOf(node);
if (idx > -1) {
@ -648,7 +573,7 @@ HTMLBreadcrumbs.prototype = {
* if the selected node still has children.
* @return {Promise}
*/
ensureFirstChild: function() {
ensureFirstChild: function () {
// If the last displayed node is the selected node
if (this.currentIndex == this.nodeHierarchy.length - 1) {
let node = this.nodeHierarchy[this.currentIndex].node;
@ -667,7 +592,7 @@ HTMLBreadcrumbs.prototype = {
/**
* Ensure the selected node is visible.
*/
scroll: function() {
scroll: function () {
// FIXME bug 684352: make sure its immediate neighbors are visible too.
let scrollbox = this.container;
@ -676,15 +601,15 @@ HTMLBreadcrumbs.prototype = {
// Repeated calls to ensureElementIsVisible would interfere with each other
// and may sometimes result in incorrect scroll positions.
this.chromeWin.clearTimeout(this._ensureVisibleTimeout);
this._ensureVisibleTimeout = this.chromeWin.setTimeout(function() {
this._ensureVisibleTimeout = this.chromeWin.setTimeout(function () {
scrollbox.ensureElementIsVisible(element);
}, ENSURE_SELECTION_VISIBLE_DELAY);
}, ENSURE_SELECTION_VISIBLE_DELAY_MS);
},
/**
* Update all button outputs.
*/
updateSelectors: function() {
updateSelectors: function () {
if (this.isDestroyed) {
return;
}
@ -717,7 +642,7 @@ HTMLBreadcrumbs.prototype = {
* @param {Array} mutations The mutations array.
* @return {Boolean}
*/
_hasInterestingMutations: function(mutations) {
_hasInterestingMutations: function (mutations) {
if (!mutations || !mutations.length) {
return false;
}
@ -748,7 +673,7 @@ HTMLBreadcrumbs.prototype = {
* @param {Array} mutations An array of mutations in case this was called as
* the "markupmutation" event listener.
*/
update: function(reason, mutations) {
update: function (reason, mutations) {
if (this.isDestroyed) {
return;
}
@ -791,8 +716,8 @@ HTMLBreadcrumbs.prototype = {
// No. We drop all the element that are not direct ancestors
// of the selection
let parent = this.selection.nodeFront.parentNode();
let idx = this.getCommonAncestor(parent);
this.cutAfter(idx);
let ancestorIdx = this.getCommonAncestor(parent);
this.cutAfter(ancestorIdx);
}
// we append the missing button between the end of the breadcrumbs display
// and the current node.
@ -807,7 +732,7 @@ HTMLBreadcrumbs.prototype = {
// Add the first child of the very last node of the breadcrumbs if possible.
this.ensureFirstChild().then(this.selectionGuard()).then(() => {
if (this.isDestroyed) {
return;
return null;
}
this.updateSelectors();

Просмотреть файл

@ -15,7 +15,6 @@ const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
const promise = require("promise");
const Services = require("Services");
const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
const {OutputParser} = require("devtools/client/shared/output-parser");
const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
const {createChild} = require("devtools/client/inspector/shared/utils");

Просмотреть файл

@ -7,8 +7,6 @@
"use strict";
const {Cu} = require("chrome");
const {setTimeout, clearTimeout} =
Cu.import("resource://gre/modules/Timer.jsm", {});
const {gDevTools} = require("devtools/client/framework/devtools");
const Services = require("Services");

Просмотреть файл

@ -38,7 +38,7 @@
</keyset>
<popupset id="inspectorPopupSet">
<!-- Used by the Markup Panel, the Highlighter and the Breadcrumbs -->
<!-- Used by the Markup Panel and the Highlighter -->
<menupopup id="inspector-node-popup">
<menuitem id="node-menu-edithtml"
label="&inspectorHTMLEdit.label;"

Просмотреть файл

@ -37,8 +37,6 @@ const Services = require("Services");
const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
const EventEmitter = require("devtools/shared/event-emitter");
const Heritage = require("sdk/core/heritage");
const {setTimeout, clearTimeout, setInterval, clearInterval} =
require("sdk/timers");
const {parseAttribute} =
require("devtools/client/shared/node-attribute-parser");
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",

Просмотреть файл

@ -38,12 +38,12 @@ const TEST_DATA = [
["VK_TAB", "style=\"dominant-baseline", 24, 24, true],
["VK_TAB", "style=\"direction", 16, 16, true],
["click_1", "style=\"display", 14, 14, false],
[":", "style=\"display:-moz-box", 15, 23, true],
[":", "style=\"display:block", 15, 20, true],
["n", "style=\"display:none", 16, 19, false],
["VK_BACK_SPACE", "style=\"display:n", 16, 16, false],
["VK_BACK_SPACE", "style=\"display:", 15, 15, false],
[" ", "style=\"display: -moz-box", 16, 24, true],
[" ", "style=\"display: -moz-box", 17, 25, true],
[" ", "style=\"display: block", 16, 21, true],
[" ", "style=\"display: block", 17, 22, true],
["i", "style=\"display: inherit", 18, 24, true],
["VK_RIGHT", "style=\"display: inherit", 24, 24, false],
[";", "style=\"display: inherit;", 25, 25, false],
@ -59,8 +59,8 @@ const TEST_DATA = [
["c", "style=\"display: inherit; color :cadetblue ", 34, 42, true],
["VK_DOWN", "style=\"display: inherit; color :chartreuse ", 34, 43, true],
["VK_RIGHT", "style=\"display: inherit; color :chartreuse ", 43, 43, false],
[" ", "style=\"display: inherit; color :chartreuse !important; ",
44, 55, true],
[" ", "style=\"display: inherit; color :chartreuse aliceblue ",
44, 53, true],
["!", "style=\"display: inherit; color :chartreuse !important; ",
45, 55, false],
["VK_RIGHT", "style=\"display: inherit; color :chartreuse !important; ",

Просмотреть файл

@ -76,9 +76,10 @@ const TEST_DATA_INNER = [
["e", "style", 5, 5, false],
["=", "style=", 6, 6, false],
["\"", "style=\"", 7, 7, false],
["b", "style=\"background", 8, 17, true],
["b", "style=\"border", 8, 13, true],
["a", "style=\"background", 9, 17, true],
["VK_RIGHT", "style=\"background", 17, 17, false],
[":", "style=\"background:-moz-element", 18, 30, true],
[":", "style=\"background:aliceblue", 18, 27, true],
["u", "style=\"background:unset", 19, 23, true],
["r", "style=\"background:url", 20, 21, false],
["l", "style=\"background:url", 21, 21, false],

Просмотреть файл

@ -7,7 +7,9 @@
// Test that markup-containers in the markup-view do flash when their
// corresponding DOM nodes mutate
const {clearTimeout} = require("sdk/timers");
// Have to use the same timer functions used by the inspector.
const {clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
const TEST_URL = URL_ROOT + "doc_markup_flashing.html";
// The test data contains a list of mutations to test.

Просмотреть файл

@ -91,7 +91,6 @@ function clickOnSuggestion(index, editor) {
info("Clicking on item " + index + " in the list");
editor.once("after-suggest", () => executeSoon(resolve));
editor.popup._list.childNodes[index].click();
editor.input.blur();
});
}

Просмотреть файл

@ -11,8 +11,6 @@ const {Cc, Ci, Cu} = require("chrome");
const promise = require("promise");
const Services = require("Services");
const {Tools} = require("devtools/client/definitions");
const {setTimeout, clearTimeout} =
Cu.import("resource://gre/modules/Timer.jsm", {});
const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
const {OutputParser} = require("devtools/client/shared/output-parser");

Просмотреть файл

@ -7,8 +7,6 @@
// Tests that CSS property names are autocompleted and cycled correctly when
// editing an existing property in the rule view.
const MAX_ENTRIES = 10;
// format :
// [
// what key to press,
@ -18,7 +16,7 @@ const MAX_ENTRIES = 10;
// ]
var testData = [
["VK_RIGHT", "font", -1, 0],
["-", "font-size", 4, MAX_ENTRIES],
["-", "font-size", 4, 17],
["f", "font-family", 0, 2],
["VK_BACK_SPACE", "font-f", -1, 0],
["VK_BACK_SPACE", "font-", -1, 0],
@ -45,7 +43,7 @@ var testData = [
["VK_END", "", -1, 0],
["VK_PAGE_UP", "", -1, 0],
["VK_PAGE_DOWN", "", -1, 0],
["f", "filter", 3, MAX_ENTRIES],
["f", "font-size", 19, 32],
["i", "filter", 3, 4],
["VK_LEFT", "filter", -1, 0],
["VK_LEFT", "filter", -1, 0],

Просмотреть файл

@ -22,7 +22,7 @@ var testData = [
["VK_DOWN", {}, "blanchedalmond", 1, 4, true],
["VK_DOWN", {}, "blue", 2, 4, true],
["VK_RIGHT", {}, "blue", -1, 0, false],
[" ", {}, "blue !important", 0, 10, true],
[" ", {}, "blue aliceblue", 0, 158, true],
["!", {}, "blue !important", 0, 0, true],
["VK_BACK_SPACE", {}, "blue !", -1, 0, true],
["VK_BACK_SPACE", {}, "blue ", -1, 0, true],

Просмотреть файл

@ -7,8 +7,6 @@
// Tests that CSS property names are autocompleted and cycled correctly when
// creating a new property in the rule view.
const MAX_ENTRIES = 10;
// format :
// [
// what key to press,
@ -31,7 +29,7 @@ var testData = [
["VK_BACK_SPACE", "di", -1, 0],
["VK_BACK_SPACE", "d", -1, 0],
["VK_BACK_SPACE", "", -1, 0],
["f", "filter", 3, MAX_ENTRIES],
["f", "font-size", 19, 32],
["i", "filter", 3, 4],
["VK_ESCAPE", null, -1, 0],
];

Просмотреть файл

@ -18,12 +18,12 @@
// ]
var testData = [
["d", {}, "display", 1, 3, false],
["VK_TAB", {}, "", -1, 10, true],
["VK_DOWN", {}, "-moz-box", 0, 10, true],
["VK_TAB", {}, "", -1, 41, true],
["VK_DOWN", {}, "block", 0, 41, true],
["n", {}, "none", -1, 0, true],
["VK_TAB", {shiftKey: true}, "display", -1, 0, true],
["VK_BACK_SPACE", {}, "", -1, 0, false],
["o", {}, "opacity", 6, 10, false],
["o", {}, "overflow", 13, 16, false],
["u", {}, "outline", 0, 5, false],
["VK_DOWN", {}, "outline-color", 1, 5, false],
["VK_TAB", {}, "none", -1, 0, true],
@ -34,7 +34,7 @@ var testData = [
["VK_DOWN", {}, "rosybrown", 4, 6, true],
["VK_DOWN", {}, "royalblue", 5, 6, true],
["VK_RIGHT", {}, "royalblue", -1, 0, false],
[" ", {}, "royalblue !important", 0, 10, true],
[" ", {}, "royalblue aliceblue", 0, 159, true],
["!", {}, "royalblue !important", 0, 0, true],
["VK_ESCAPE", {}, null, -1, 0, true]
];

Просмотреть файл

@ -38,10 +38,10 @@ add_task(function* () {
editor.popup.selectedIndex = itemIndex;
info("Select the background-color suggestion with a mouse click.");
let onInputFocus = once(editor.input, "focus", true);
let onSuggest = editor.once("after-suggest");
let node = editor.popup._list.childNodes[itemIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
yield onInputFocus;
yield onSuggest;
is(editor.input.value, "background-color", "Correct value is autocompleted");
info("Press RETURN to move the focus to a property value editor.");

Просмотреть файл

@ -94,10 +94,10 @@ add_task(function* () {
info("Select the background-color suggestion with a mouse click.");
let onRuleviewChanged = view.once("ruleview-changed");
let onInputFocus = once(editor.input, "focus", true);
let onSuggest = editor.once("after-suggest");
let node = editor.popup._list.childNodes[editor.popup.selectedIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
yield onInputFocus;
yield onSuggest;
yield onRuleviewChanged;
is(editor.input.value, EXPECTED_CSS_VALUE,

Просмотреть файл

@ -4,7 +4,7 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
const {Cc, Ci} = require("chrome");
const {CssLogic} = require("devtools/shared/inspector/css-logic");
const {InplaceEditor, editableField} =
require("devtools/client/shared/inplace-editor");
@ -19,7 +19,6 @@ const {
parseDeclarations,
parseSingleValue,
} = require("devtools/client/shared/css-parsing-utils");
const {setTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
const HTML_NS = "http://www.w3.org/1999/xhtml";
const IOService = Cc["@mozilla.org/network/io-service;1"]

Просмотреть файл

@ -6,9 +6,7 @@
"use strict";
const {Ci, Cu} = require("chrome");
const {setTimeout, clearTimeout} =
Cu.import("resource://gre/modules/Timer.jsm", {});
const {Ci} = require("chrome");
const {parseDeclarations} =
require("devtools/client/shared/css-parsing-utils");
const promise = require("promise");

Просмотреть файл

@ -45,7 +45,6 @@ support-files =
[browser_inspector_breadcrumbs_keybinding.js]
[browser_inspector_breadcrumbs_keyboard_trap.js]
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_inspector_breadcrumbs_menu.js]
[browser_inspector_breadcrumbs_mutations.js]
[browser_inspector_delete-selected-node-01.js]
[browser_inspector_delete-selected-node-02.js]

Просмотреть файл

@ -1,51 +0,0 @@
/* 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 inspector node context menu appears when right-clicking on the
// breadcrumbs nodes.
const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html";
add_task(function*() {
let {inspector} = yield openInspectorForURL(TEST_URI);
let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
info("Select a test node and try to right-click on the selected breadcrumb");
yield selectNode("#i1", inspector);
let button = container.querySelector("button[checked]");
let onMenuShown = once(inspector.nodemenu, "popupshown");
button.onclick({button: 2});
yield onMenuShown;
ok(true, "The context menu appeared on right-click");
info("Right-click on a non selected crumb (the body node)");
button = button.previousSibling;
onMenuShown = once(inspector.nodemenu, "popupshown");
let onInspectorUpdated = inspector.once("inspector-updated");
button.onclick({button: 2});
yield onMenuShown;
ok(true, "The context menu appeared on right-click");
yield onInspectorUpdated;
is(inspector.selection.nodeFront.tagName.toLowerCase(), "body",
"The body node was selected when right-clicking in the breadcrumbs");
info("Right-click on the html node");
button = button.previousSibling;
onMenuShown = once(inspector.nodemenu, "popupshown");
onInspectorUpdated = inspector.once("inspector-updated");
button.onclick({button: 2});
yield onMenuShown;
ok(true, "The context menu appeared on right-click");
yield onInspectorUpdated;
is(inspector.selection.nodeFront.tagName.toLowerCase(), "html",
"The html node was selected when right-clicking in the breadcrumbs");
});

Просмотреть файл

@ -3,7 +3,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { clearTimeout, setTimeout } = require("sdk/timers");
const { actions } = require("../constants");
const { refresh } = require("./refresh");

Просмотреть файл

@ -5,7 +5,6 @@
const { Cu } = require("chrome");
const { defer, all } = require("promise");
const { setTimeout, clearTimeout } = require("sdk/timers");
const { makeInfallible } = require("devtools/shared/DevToolsUtils");
const Services = require("Services");

Просмотреть файл

@ -14,7 +14,6 @@ const promise = require("promise");
const Services = require("Services");
const { on, forget } = require("devtools/client/projecteditor/lib/helpers/event");
const { FileResource } = require("devtools/client/projecteditor/lib/stores/resource");
const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
const CHECK_LINKED_DIRECTORY_DELAY = 5000;
const SHOULD_LIVE_REFRESH = true;

Просмотреть файл

@ -36,9 +36,9 @@ function AutocompletePopup(document, options = {}) {
this.position = options.position || "after_start";
this.direction = options.direction || "ltr";
this.onSelect = options.onSelect;
this.onClick = options.onClick;
this.onKeypress = options.onKeypress;
this.onSelectCallback = options.onSelect;
this.onClickCallback = options.onClick;
this.onKeypressCallback = options.onKeypress;
let id = options.panelId || "devtools_autoCompletePopup";
let theme = options.theme || "dark";
@ -93,17 +93,13 @@ function AutocompletePopup(document, options = {}) {
}
this._list.className = "devtools-autocomplete-listbox " + theme + "-theme";
if (this.onSelect) {
this._list.addEventListener("select", this.onSelect, false);
}
this.onSelect = this.onSelect.bind(this);
this.onClick = this.onClick.bind(this);
this.onKeypress = this.onKeypress.bind(this);
this._list.addEventListener("select", this.onSelect, false);
this._list.addEventListener("click", this.onClick, false);
this._list.addEventListener("keypress", this.onKeypress, false);
if (this.onClick) {
this._list.addEventListener("click", this.onClick, false);
}
if (this.onKeypress) {
this._list.addEventListener("keypress", this.onKeypress, false);
}
this._itemIdCounter = 0;
events.decorate(this);
@ -117,9 +113,26 @@ AutocompletePopup.prototype = {
__scrollbarWidth: null,
// Event handlers.
onSelect: null,
onClick: null,
onKeypress: null,
onSelect: function (e) {
this.emit("popup-select");
if (this.onSelectCallback) {
this.onSelectCallback(e);
}
},
onClick: function (e) {
this.emit("popup-click");
if (this.onClickCallback) {
this.onClickCallback(e);
}
},
onKeypress: function (e) {
this.emit("popup-keypress");
if (this.onKeypressCallback) {
this.onKeypressCallback(e);
}
},
/**
* Open the autocomplete popup panel.
@ -191,17 +204,9 @@ AutocompletePopup.prototype = {
this.hidePopup();
}
if (this.onSelect) {
this._list.removeEventListener("select", this.onSelect, false);
}
if (this.onClick) {
this._list.removeEventListener("click", this.onClick, false);
}
if (this.onKeypress) {
this._list.removeEventListener("keypress", this.onKeypress, false);
}
this._list.removeEventListener("select", this.onSelect, false);
this._list.removeEventListener("click", this.onClick, false);
this._list.removeEventListener("keypress", this.onKeypress, false);
if (this.autoThemeEnabled) {
gDevTools.off("pref-changed", this._handleThemeChange);

Просмотреть файл

@ -6,137 +6,94 @@ const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/cl
const { ViewHelpers } = require("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
const AUTO_EXPAND_DEPTH = 0; // depth
/**
* An arrow that displays whether its node is expanded () or collapsed
* (). When its node has no children, it is hidden.
*/
const ArrowExpander = createFactory(createClass({
displayName: "ArrowExpander",
shouldComponentUpdate(nextProps, nextState) {
return this.props.item !== nextProps.item
|| this.props.visible !== nextProps.visible
|| this.props.expanded !== nextProps.expanded;
},
render() {
const attrs = {
className: "arrow theme-twisty",
onClick: this.props.expanded
? () => this.props.onCollapse(this.props.item)
: e => this.props.onExpand(this.props.item, e.altKey)
};
if (this.props.expanded) {
attrs.className += " open";
}
if (!this.props.visible) {
attrs.style = {
visibility: "hidden"
};
}
return dom.div(attrs);
}
}));
const TreeNode = createFactory(createClass({
componentDidMount() {
if (this.props.focused) {
this.refs.button.focus();
}
},
componentDidUpdate() {
if (this.props.focused) {
this.refs.button.focus();
}
},
render() {
const arrow = ArrowExpander({
item: this.props.item,
expanded: this.props.expanded,
visible: this.props.hasChildren,
onExpand: this.props.onExpand,
onCollapse: this.props.onCollapse,
});
let isOddRow = this.props.index % 2;
return dom.div(
{
className: `tree-node div ${isOddRow ? "tree-node-odd" : ""}`,
onFocus: this.props.onFocus,
onClick: this.props.onFocus,
onBlur: this.props.onBlur,
style: {
padding: 0,
margin: 0
}
},
this.props.renderItem(this.props.item,
this.props.depth,
this.props.focused,
arrow,
this.props.expanded),
// XXX: OSX won't focus/blur regular elements even if you set tabindex
// unless there is an input/button child.
dom.button(this._buttonAttrs)
);
},
_buttonAttrs: {
ref: "button",
style: {
opacity: 0,
width: "0 !important",
height: "0 !important",
padding: "0 !important",
outline: "none",
MozAppearance: "none",
// XXX: Despite resetting all of the above properties (and margin), the
// button still ends up with ~79px width, so we set a large negative
// margin to completely hide it.
MozMarginStart: "-1000px !important",
}
}
}));
/**
* Create a function that calls the given function `fn` only once per animation
* frame.
*
* @param {Function} fn
* @returns {Function}
*/
function oncePerAnimationFrame(fn) {
let animationId = null;
let argsToPass = null;
return function (...args) {
argsToPass = args;
if (animationId !== null) {
return;
}
animationId = requestAnimationFrame(() => {
fn.call(this, ...argsToPass);
animationId = null;
argsToPass = null;
});
};
}
const NUMBER_OF_OFFSCREEN_ITEMS = 1;
/**
* A generic tree component. See propTypes for the public API.
* A fast, generic, expandable and collapsible tree component.
*
* @see `devtools/client/memory/components/test/mochitest/head.js` for usage
* @see `devtools/client/memory/components/heap.js` for usage
* This tree component is fast: it can handle trees with *many* items. It only
* renders the subset of those items which are visible in the viewport. It's
* been battle tested on huge trees in the memory panel. We've optimized tree
* traversal and rendering, even in the presence of cross-compartment wrappers.
*
* This tree component doesn't make any assumptions about the structure of your
* tree data. Whether children are computed on demand, or stored in an array in
* the parent's `_children` property, it doesn't matter. We only require the
* implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
* functions.
*
* This tree component is well tested and reliable. See
* devtools/client/shared/components/test/mochitest/test_tree_* and its usage in
* the performance and memory panels.
*
* This tree component doesn't make any assumptions about how to render items in
* the tree. You provide a `renderItem` function, and this component will ensure
* that only those items whose parents are expanded and which are visible in the
* viewport are rendered. The `renderItem` function could render the items as a
* "traditional" tree or as rows in a table or anything else. It doesn't
* restrict you to only one certain kind of tree.
*
* The only requirement is that every item in the tree render as the same
* height. This is required in order to compute which items are visible in the
* viewport in constant time.
*
* ### Example Usage
*
* Suppose we have some tree data where each item has this form:
*
* {
* id: Number,
* label: String,
* parent: Item or null,
* children: Array of child items,
* expanded: bool,
* }
*
* Here is how we could render that data with this component:
*
* const MyTree = createClass({
* displayName: "MyTree",
*
* propTypes: {
* // The root item of the tree, with the form described above.
* root: PropTypes.object.isRequired
* },
*
* render() {
* return Tree({
* itemHeight: 20, // px
*
* getRoots: () => [this.props.root],
*
* getParent: item => item.parent,
* getChildren: item => item.children,
* getKey: item => item.id,
* isExpanded: item => item.expanded,
*
* renderItem: (item, depth, isFocused, arrow, isExpanded) => {
* let className = "my-tree-item";
* if (isFocused) {
* className += " focused";
* }
* return dom.div(
* {
* className,
* // Apply 10px nesting per expansion depth.
* style: { marginLeft: depth * 10 + "px" }
* },
* // Here is the expando arrow so users can toggle expansion and
* // collapse state.
* arrow,
* // And here is the label for this item.
* dom.span({ className: "my-tree-item-label" }, item.label)
* );
* },
*
* onExpand: item => dispatchExpandActionToRedux(item),
* onCollapse: item => dispatchCollapseActionToRedux(item),
* });
* }
* });
*/
const Tree = module.exports = createClass({
displayName: "Tree",
@ -145,19 +102,85 @@ const Tree = module.exports = createClass({
// Required props
// A function to get an item's parent, or null if it is a root.
//
// Type: getParent(item: Item) -> Maybe<Item>
//
// Example:
//
// // The parent of this item is stored in its `parent` property.
// getParent: item => item.parent
getParent: PropTypes.func.isRequired,
// A function to get an item's children.
//
// Type: getChildren(item: Item) -> [Item]
//
// Example:
//
// // This item's children are stored in its `children` property.
// getChildren: item => item.children
getChildren: PropTypes.func.isRequired,
// A function which takes an item and ArrowExpander and returns a
// component.
// A function which takes an item and ArrowExpander component instance and
// returns a component, or text, or anything else that React considers
// renderable.
//
// Type: renderItem(item: Item,
// depth: Number,
// isFocused: Boolean,
// arrow: ReactComponent,
// isExpanded: Boolean) -> ReactRenderable
//
// Example:
//
// renderItem: (item, depth, isFocused, arrow, isExpanded) => {
// let className = "my-tree-item";
// if (isFocused) {
// className += " focused";
// }
// return dom.div(
// {
// className,
// style: { marginLeft: depth * 10 + "px" }
// },
// arrow,
// dom.span({ className: "my-tree-item-label" }, item.label)
// );
// },
renderItem: PropTypes.func.isRequired,
// A function which returns the roots of the tree (forest).
//
// Type: getRoots() -> [Item]
//
// Example:
//
// // In this case, we only have one top level, root item. You could
// // return multiple items if you have many top level items in your
// // tree.
// getRoots: () => [this.props.rootOfMyTree]
getRoots: PropTypes.func.isRequired,
// A function to get a unique key for the given item.
// A function to get a unique key for the given item. This helps speed up
// React's rendering a *TON*.
//
// Type: getKey(item: Item) -> String
//
// Example:
//
// getKey: item => `my-tree-item-${item.uniqueId}`
getKey: PropTypes.func.isRequired,
// A function to get whether an item is expanded or not. If an item is not
// expanded, then it must be collapsed.
//
// Type: isExpanded(item: Item) -> Boolean
//
// Example:
//
// isExpanded: item => item.expanded,
isExpanded: PropTypes.func.isRequired,
// The height of an item in the tree including margin and padding, in
// pixels.
itemHeight: PropTypes.number.isRequired,
@ -166,11 +189,24 @@ const Tree = module.exports = createClass({
// The currently focused item, if any such item exists.
focused: PropTypes.any,
// Handle when a new item is focused.
onFocus: PropTypes.func,
// The depth to which we should automatically expand new items.
autoExpandDepth: PropTypes.number,
// Optional event handlers for when items are expanded or collapsed.
// Optional event handlers for when items are expanded or collapsed. Useful
// for dispatching redux events and updating application state, maybe lazily
// loading subtrees from a worker, etc.
//
// Type:
// onExpand(item: Item)
// onCollapse(item: Item)
//
// Example:
//
// onExpand: item => dispatchExpandActionToRedux(item)
onExpand: PropTypes.func,
onCollapse: PropTypes.func,
},
@ -574,3 +610,126 @@ const Tree = module.exports = createClass({
this._focus(parentIndex, parent);
}),
});
/**
* An arrow that displays whether its node is expanded () or collapsed
* (). When its node has no children, it is hidden.
*/
const ArrowExpander = createFactory(createClass({
displayName: "ArrowExpander",
shouldComponentUpdate(nextProps, nextState) {
return this.props.item !== nextProps.item
|| this.props.visible !== nextProps.visible
|| this.props.expanded !== nextProps.expanded;
},
render() {
const attrs = {
className: "arrow theme-twisty",
onClick: this.props.expanded
? () => this.props.onCollapse(this.props.item)
: e => this.props.onExpand(this.props.item, e.altKey)
};
if (this.props.expanded) {
attrs.className += " open";
}
if (!this.props.visible) {
attrs.style = {
visibility: "hidden"
};
}
return dom.div(attrs);
}
}));
const TreeNode = createFactory(createClass({
componentDidMount() {
if (this.props.focused) {
this.refs.button.focus();
}
},
componentDidUpdate() {
if (this.props.focused) {
this.refs.button.focus();
}
},
render() {
const arrow = ArrowExpander({
item: this.props.item,
expanded: this.props.expanded,
visible: this.props.hasChildren,
onExpand: this.props.onExpand,
onCollapse: this.props.onCollapse,
});
let isOddRow = this.props.index % 2;
return dom.div(
{
className: `tree-node div ${isOddRow ? "tree-node-odd" : ""}`,
onFocus: this.props.onFocus,
onClick: this.props.onFocus,
onBlur: this.props.onBlur,
style: {
padding: 0,
margin: 0
}
},
this.props.renderItem(this.props.item,
this.props.depth,
this.props.focused,
arrow,
this.props.expanded),
// XXX: OSX won't focus/blur regular elements even if you set tabindex
// unless there is an input/button child.
dom.button(this._buttonAttrs)
);
},
_buttonAttrs: {
ref: "button",
style: {
opacity: 0,
width: "0 !important",
height: "0 !important",
padding: "0 !important",
outline: "none",
MozAppearance: "none",
// XXX: Despite resetting all of the above properties (and margin), the
// button still ends up with ~79px width, so we set a large negative
// margin to completely hide it.
MozMarginStart: "-1000px !important",
}
}
}));
/**
* Create a function that calls the given function `fn` only once per animation
* frame.
*
* @param {Function} fn
* @returns {Function}
*/
function oncePerAnimationFrame(fn) {
let animationId = null;
let argsToPass = null;
return function (...args) {
argsToPass = args;
if (animationId !== null) {
return;
}
animationId = requestAnimationFrame(() => {
fn.call(this, ...argsToPass);
animationId = null;
argsToPass = null;
});
};
}

Просмотреть файл

@ -514,7 +514,7 @@ RuleRewriter.prototype = {
*
* @param {Number} index The index of the property to modify
*/
completeInitialization: function(index) {
completeInitialization: function (index) {
if (index < 0) {
throw new Error("Invalid index " + index + ". Expected positive integer");
}
@ -540,7 +540,7 @@ RuleRewriter.prototype = {
* @param {Number} offset the offset at which to compute the indentation
* @return {String} the indentation at the indicated position
*/
getIndentation: function(string, offset) {
getIndentation: function (string, offset) {
let originalOffset = offset;
for (--offset; offset >= 0; --offset) {
let c = string[offset];
@ -573,7 +573,7 @@ RuleRewriter.prototype = {
* where |text| is the text that has been rewritten
* to be "lexically safe".
*/
sanitizePropertyValue: function(text) {
sanitizePropertyValue: function (text) {
let lexer = DOMUtils.getCSSLexer(text);
let result = "";
@ -636,7 +636,7 @@ RuleRewriter.prototype = {
* @param {Number} index the index at which to start
* @return {Number} index of the first non-whitespace character, or -1
*/
skipWhitespaceBackward: function(string, index) {
skipWhitespaceBackward: function (string, index) {
for (--index;
index >= 0 && (string[index] === " " || string[index] === "\t");
--index) {
@ -652,7 +652,7 @@ RuleRewriter.prototype = {
* terminate. It might be invalid, so this
* function must check for that.
*/
maybeTerminateDecl: function(index) {
maybeTerminateDecl: function (index) {
if (index < 0 || index >= this.declarations.length
// No need to rewrite declarations in comments.
|| ("commentOffsets" in this.declarations[index])) {
@ -700,7 +700,7 @@ RuleRewriter.prototype = {
* @param {Number} index The index of the property.
* @return {String} The sanitized text.
*/
sanitizeText: function(text, index) {
sanitizeText: function (text, index) {
let [anySanitized, sanitizedText] = this.sanitizePropertyValue(text);
if (anySanitized) {
this.changedDeclarations[index] = sanitizedText;
@ -715,7 +715,7 @@ RuleRewriter.prototype = {
* @param {String} name current name of the property
* @param {String} newName new name of the property
*/
renameProperty: function(index, name, newName) {
renameProperty: function (index, name, newName) {
this.completeInitialization(index);
this.result += CSS.escape(newName);
// We could conceivably compute the name offsets instead so we
@ -731,7 +731,7 @@ RuleRewriter.prototype = {
* @param {Boolean} isEnabled true if the property should be enabled;
* false if it should be disabled
*/
setPropertyEnabled: function(index, name, isEnabled) {
setPropertyEnabled: function (index, name, isEnabled) {
this.completeInitialization(index);
const decl = this.decl;
let copyOffset = decl.offsets[1];
@ -785,7 +785,7 @@ RuleRewriter.prototype = {
* that holds the default indentation that should be used
* for edits to the rule.
*/
getDefaultIndentation: function() {
getDefaultIndentation: function () {
return this.rule.parentStyleSheet.guessIndentation();
},
@ -801,7 +801,7 @@ RuleRewriter.prototype = {
* @return {Promise} a promise that is resolved when the edit has
* completed
*/
internalCreateProperty: Task.async(function*(index, name, value, priority) {
internalCreateProperty: Task.async(function* (index, name, value, priority) {
this.completeInitialization(index);
let newIndentation = "";
if (this.hasNewLine) {
@ -859,7 +859,7 @@ RuleRewriter.prototype = {
* @param {String} priority priority of the new property; either
* the empty string or "important"
*/
createProperty: function(index, name, value, priority) {
createProperty: function (index, name, value, priority) {
this.editPromise = this.internalCreateProperty(index, name, value,
priority);
},
@ -877,12 +877,13 @@ RuleRewriter.prototype = {
* @param {String} priority the property's priority, either the empty
* string or "important"
*/
setProperty: function(index, name, value, priority) {
setProperty: function (index, name, value, priority) {
this.completeInitialization(index);
// We might see a "set" on a previously non-existent property; in
// that case, act like "create".
if (!this.decl) {
return this.createProperty(index, name, value, priority);
this.createProperty(index, name, value, priority);
return;
}
// Note that this assumes that "set" never operates on disabled
@ -904,7 +905,7 @@ RuleRewriter.prototype = {
* @param {Number} index index of the property in the rule.
* @param {String} name the name of the property to remove
*/
removeProperty: function(index, name) {
removeProperty: function (index, name) {
this.completeInitialization(index);
let copyOffset = this.decl.offsets[1];
// Maybe removing this rule left us with a completely blank
@ -934,7 +935,7 @@ RuleRewriter.prototype = {
* @param {Number} copyOffset Offset into |inputString| of the
* final text to copy to the output string.
*/
completeCopying: function(copyOffset) {
completeCopying: function (copyOffset) {
// Add the trailing text.
this.result += this.inputString.substring(copyOffset);
},
@ -945,7 +946,7 @@ RuleRewriter.prototype = {
* @return {Promise} A promise which will be resolved when the modifications
* are complete.
*/
apply: function() {
apply: function () {
return promise.resolve(this.editPromise).then(() => {
return this.rule.setRuleText(this.result);
});
@ -961,7 +962,7 @@ RuleRewriter.prototype = {
* whose value is the new text of the property.
* |text| is the rewritten text of the rule.
*/
getResult: function() {
getResult: function () {
return {changed: this.changedDeclarations, text: this.result};
},
};

Просмотреть файл

@ -9,7 +9,6 @@ const Services = require("Services");
const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
const { Task } = require("resource://gre/modules/Task.jsm");
const { Promise } = require("resource://gre/modules/Promise.jsm");
const { setTimeout } = require("sdk/timers");
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

Просмотреть файл

@ -33,7 +33,11 @@ const CONTENT_TYPES = {
CSS_MIXED: 2,
CSS_PROPERTY: 3,
};
const MAX_POPUP_ENTRIES = 10;
const AUTOCOMPLETE_POPUP_CLASSNAME = "inplace-editor-autocomplete-popup";
// The limit of 500 autocomplete suggestions should not be reached but is kept
// for safety.
const MAX_POPUP_ENTRIES = 500;
const FOCUS_FORWARD = Ci.nsIFocusManager.MOVEFOCUS_FORWARD;
const FOCUS_BACKWARD = Ci.nsIFocusManager.MOVEFOCUS_BACKWARD;
@ -241,6 +245,7 @@ function InplaceEditor(options, event) {
this._onKeyPress = this._onKeyPress.bind(this);
this._onInput = this._onInput.bind(this);
this._onKeyup = this._onKeyup.bind(this);
this._onAutocompletePopupClick = this._onAutocompletePopupClick.bind(this);
this._createInput();
@ -728,6 +733,8 @@ InplaceEditor.prototype = {
};
}
}
return null;
},
/**
@ -943,59 +950,70 @@ InplaceEditor.prototype = {
},
/**
* Handle loss of focus by calling done if it hasn't been called yet.
* Event handler called when the inplace-editor's input loses focus.
*/
_onBlur: function(event) {
if (event && this.popup && this.popup.isOpen &&
this.popup.selectedIndex >= 0) {
let label, preLabel;
this.popup.selectedIndex >= 0) {
this._acceptPopupSuggestion();
} else {
this._apply();
this._clear();
}
},
if (this._selectedIndex === undefined) {
({label, preLabel} =
this.popup.getItemAtIndex(this.popup.selectedIndex));
} else {
({label, preLabel} = this.popup.getItemAtIndex(this._selectedIndex));
}
/**
* Event handler called by the autocomplete popup when receiving a click
* event.
*/
_onAutocompletePopupClick: function() {
this._acceptPopupSuggestion();
},
let input = this.input;
let pre = "";
_acceptPopupSuggestion: function() {
let label, preLabel;
// CSS_MIXED needs special treatment here to make it so that
// multiple presses of tab will cycle through completions, but
// without selecting the completed text. However, this same
// special treatment will do the wrong thing for other editing
// styles.
if (input.selectionStart < input.selectionEnd ||
this.contentType !== CONTENT_TYPES.CSS_MIXED) {
pre = input.value.slice(0, input.selectionStart);
} else {
pre = input.value.slice(0, input.selectionStart - label.length +
preLabel.length);
}
let post = input.value.slice(input.selectionEnd, input.value.length);
let item = this.popup.selectedItem;
this._selectedIndex = this.popup.selectedIndex;
let toComplete = item.label.slice(item.preLabel.length);
input.value = pre + toComplete + post;
input.setSelectionRange(pre.length + toComplete.length,
pre.length + toComplete.length);
this._updateSize();
// Wait for the popup to hide and then focus input async otherwise it does
// not work.
let onPopupHidden = () => {
this.popup._panel.removeEventListener("popuphidden", onPopupHidden);
this.doc.defaultView.setTimeout(()=> {
input.focus();
this.emit("after-suggest");
}, 0);
};
this.popup._panel.addEventListener("popuphidden", onPopupHidden);
this.popup.hidePopup();
return;
if (this._selectedIndex === undefined) {
({label, preLabel} =
this.popup.getItemAtIndex(this.popup.selectedIndex));
} else {
({label, preLabel} = this.popup.getItemAtIndex(this._selectedIndex));
}
this._apply();
this._clear();
let input = this.input;
let pre = "";
// CSS_MIXED needs special treatment here to make it so that
// multiple presses of tab will cycle through completions, but
// without selecting the completed text. However, this same
// special treatment will do the wrong thing for other editing
// styles.
if (input.selectionStart < input.selectionEnd ||
this.contentType !== CONTENT_TYPES.CSS_MIXED) {
pre = input.value.slice(0, input.selectionStart);
} else {
pre = input.value.slice(0, input.selectionStart - label.length +
preLabel.length);
}
let post = input.value.slice(input.selectionEnd, input.value.length);
let item = this.popup.selectedItem;
this._selectedIndex = this.popup.selectedIndex;
let toComplete = item.label.slice(item.preLabel.length);
input.value = pre + toComplete + post;
input.setSelectionRange(pre.length + toComplete.length,
pre.length + toComplete.length);
this._updateSize();
// Wait for the popup to hide and then focus input async otherwise it does
// not work.
let onPopupHidden = () => {
this.popup._panel.removeEventListener("popuphidden", onPopupHidden);
this.doc.defaultView.setTimeout(()=> {
input.focus();
this.emit("after-suggest");
}, 0);
};
this.popup._panel.addEventListener("popuphidden", onPopupHidden);
this._hideAutocompletePopup();
},
/**
@ -1037,7 +1055,7 @@ InplaceEditor.prototype = {
if (isKeyIn(key, "BACK_SPACE", "DELETE", "LEFT", "RIGHT")) {
if (isPopupOpen) {
this.popup.hidePopup();
this._hideAutocompletePopup();
}
} else if (!cycling && !multilineNavigation &&
!event.metaKey && !event.altKey && !event.ctrlKey) {
@ -1088,7 +1106,7 @@ InplaceEditor.prototype = {
// Close the popup if open
if (this.popup && this.popup.isOpen) {
this.popup.hidePopup();
this._hideAutocompletePopup();
}
if (direction !== null && focusManager.focusedElement === input) {
@ -1112,7 +1130,7 @@ InplaceEditor.prototype = {
this._preventSuggestions = true;
// Close the popup if open
if (this.popup && this.popup.isOpen) {
this.popup.hidePopup();
this._hideAutocompletePopup();
}
prevent = true;
this.cancelled = true;
@ -1131,6 +1149,31 @@ InplaceEditor.prototype = {
}
},
/**
* Open the autocomplete popup, adding a custom click handler and classname.
*
* @param {Number} offset
* X-offset relative to the input starting edge.
* @param {Number} selectedIndex
* The index of the item that should be selected. Use -1 to have no
* item selected.
*/
_openAutocompletePopup: function(offset, selectedIndex) {
this.popup._panel.classList.add(AUTOCOMPLETE_POPUP_CLASSNAME);
this.popup.on("popup-click", this._onAutocompletePopupClick);
this.popup.openPopup(this.input, offset, 0, selectedIndex);
},
/**
* Remove the custom classname and click handler and close the autocomplete
* popup.
*/
_hideAutocompletePopup: function() {
this.popup._panel.classList.remove(AUTOCOMPLETE_POPUP_CLASSNAME);
this.popup.off("popup-click", this._onAutocompletePopupClick);
this.popup.hidePopup();
},
/**
* Get the increment/decrement step to use for the provided key event.
*/
@ -1337,13 +1380,28 @@ InplaceEditor.prototype = {
}
}
// Pick the best first suggestion from the provided list of suggestions.
let cssValues = finalList.map(item => item.label);
let mostRelevantIndex = findMostRelevantCssPropertyIndex(cssValues);
// Sort items starting with [a-z0-9] first, to make sure vendor-prefixed
// values and "!important" are suggested only after standard values.
finalList.sort((item1, item2) => {
// Get the expected alphabetical comparison between the items.
let comparison = item1.label.localeCompare(item2.label);
if (/^\w/.test(item1.label) != /^\w/.test(item2.label)) {
// One starts with [a-z0-9], one does not: flip the comparison.
comparison = -1 * comparison;
}
return comparison;
});
let index = 0;
if (startCheckQuery) {
// Only select a "best" suggestion when the user started a query.
let cssValues = finalList.map(item => item.label);
index = findMostRelevantCssPropertyIndex(cssValues);
}
// Insert the most relevant item from the final list as the input value.
if (autoInsert && finalList[mostRelevantIndex]) {
let item = finalList[mostRelevantIndex].label;
if (autoInsert && finalList[index]) {
let item = finalList[index].label;
input.value = query + item.slice(startCheckQuery.length) +
input.value.slice(query.length);
input.setSelectionRange(query.length, query.length + item.length -
@ -1359,13 +1417,13 @@ InplaceEditor.prototype = {
offset = this._isSingleLine() ? offset : 0;
// Select the most relevantItem if autoInsert is allowed
let selectedIndex = autoInsert ? mostRelevantIndex : -1;
let selectedIndex = autoInsert ? index : -1;
// Open the suggestions popup.
this.popup.setItems(finalList);
this.popup.openPopup(this.input, offset, 0, selectedIndex);
this._openAutocompletePopup(offset, selectedIndex);
} else {
this.popup.hidePopup();
this._hideAutocompletePopup();
}
// This emit is mainly for the purpose of making the test flow simpler.
this.emit("after-suggest");

Просмотреть файл

@ -3,8 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
loader.lazyRequireGetter(this, "timers",
"resource://gre/modules/Timer.jsm");
loader.lazyRequireGetter(this, "defer",
"promise", true);
@ -67,7 +65,7 @@ Poller.prototype.on = function pollerOn () {
Poller.prototype.off = function pollerOff () {
let { resolve, promise } = defer();
if (this._timer) {
timers.clearTimeout(this._timer);
clearTimeout(this._timer);
this._timer = null;
}
@ -93,7 +91,7 @@ Poller.prototype.destroy = function pollerDestroy () {
};
Poller.prototype._preparePoll = function pollerPrepare () {
this._timer = timers.setTimeout(this._poll, this._wait);
this._timer = setTimeout(this._poll, this._wait);
};
Poller.prototype._poll = function pollerPoll () {

Просмотреть файл

@ -26,7 +26,6 @@
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const {setTimeout, clearTimeout} = require("sdk/timers");
const {
PREDEFINED,
PRESETS,

Просмотреть файл

@ -51,7 +51,11 @@ support-files =
doc_xulpage.xul
sync.html
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/inspector/shared/test/head.js
!/devtools/client/inspector/test/head.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
[browser_styleeditor_autocomplete.js]
[browser_styleeditor_autocomplete-disabled.js]

Просмотреть файл

@ -15,10 +15,10 @@
}
:root[devtoolstheme="dark"] {
--gcli-background-color: #343c45; /* --theme-toolbar-background */
--gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
--gcli-background-color: #272b35; /* --theme-toolbar-background */
--gcli-input-focused-background: #272b35; /* --theme-tab-toolbar-background */
--gcli-input-color: #b6babf; /* --theme-body-color-alt */
--gcli-border-color: black; /* --theme-splitter-color */
--gcli-border-color: #454d5d; /* --theme-splitter-color */
}
.gcli-body {

Просмотреть файл

@ -20,12 +20,12 @@
}
:root[devtoolstheme="dark"] #developer-toolbar {
--gcli-background-color: #343c45; /* --theme-toolbar-background */
--gcli-input-background: rgba(37, 44, 51, .6); /* --theme-tab-toolbar-background */
--gcli-input-focused-background: #252c33; /* --theme-tab-toolbar-background */
--gcli-background-color: #272b35; /* --theme-toolbar-background */
--gcli-input-background: #272b35; /* --theme-tab-toolbar-background */
--gcli-input-focused-background: #272b35; /* --theme-tab-toolbar-background */
--gcli-input-color: #b6babf; /* --theme-body-color-alt */
--gcli-border-color: black; /* --theme-splitter-color */
--selection-background: #1d4f73; /* --theme-selection-background */
--gcli-border-color: #454d5d; /* --theme-splitter-color */
--selection-background: #5675b9; /* --theme-selection-background */
--selection-color: #f5f7fa; /* --theme-selection-color */
}

Просмотреть файл

@ -42,17 +42,29 @@
background-color: transparent;
border-radius: 3px;
overflow-x: hidden;
max-height: 40rem;
max-height: 20rem;
}
:root[platform="linux"] .devtools-autocomplete-popup {
max-height: 32rem;
/* Root font size is bigger on Linux, adjust rem-based values. */
max-height: 16rem;
}
.devtools-autocomplete-listbox {
-moz-appearance: none !important;
background-color: transparent;
border-width: 0px !important;
margin: 0;
}
.devtools-autocomplete-listbox > scrollbox {
padding: 2px;
}
.inplace-editor-autocomplete-popup .devtools-autocomplete-listbox {
/* Inplace editor closes the autocomplete popup on blur, the autocomplete
popup should not steal the focus here.*/
-moz-user-focus: ignore;
}
.devtools-autocomplete-listbox > richlistitem,

Просмотреть файл

@ -42,8 +42,9 @@ body {
}
.theme-link,
.cm-s-mozilla .cm-link {
color: var(--theme-highlight-blue);
.cm-s-mozilla .cm-link,
.cm-s-mozilla .cm-keyword {
color: var(--theme-highlight-green);
}
/*
@ -82,7 +83,7 @@ body {
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
color: var(--theme-highlight-green);
color: var(--theme-highlight-red);
}
.CodeMirror-Tern-completion-number:before {
@ -91,12 +92,11 @@ body {
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-variable,
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-property,
.cm-s-mozilla .cm-qualifier,
.variables-view-variable > .title > .name {
color: var(--theme-highlight-blue);
color: var(--theme-highlight-purple);
}
.CodeMirror-Tern-completion-object:before {
@ -118,7 +118,7 @@ body {
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.variables-view-property > .title > .name {
color: var(--theme-highlight-pink);
color: var(--theme-highlight-green);
}
.CodeMirror-Tern-completion-array:before {
@ -129,17 +129,17 @@ body {
color: var(--theme-highlight-purple);
}
.theme-fg-color5,
.cm-s-mozilla .cm-keyword {
color: var(--theme-highlight-lightorange);
.theme-fg-color5 {
color: var(--theme-highlight-purple);
}
.theme-fg-color6,
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2,
.variable-or-property .token-string,
.cm-s-mozilla .cm-variable,
.CodeMirror-Tern-farg {
color: var(--theme-highlight-orange);
color: var(--theme-highlight-gray);
}
.CodeMirror-Tern-completion-string:before,

Просмотреть файл

@ -288,11 +288,11 @@
}
.theme-dark .trace-item.selected-matching {
background-color: rgba(29,79,115,.4); /* Select highlight blue at 40% alpha */
background-color: rgba(86, 117, 185, .4); /* Select highlight blue at 40% alpha */
}
.theme-dark .selected > .trace-item {
background-color: rgba(29,79,115,.6); /* Select highlight blue at 60% alpha */
background-color: rgba(86, 117, 185, .6); /* Select highlight blue at 60% alpha */
}
.trace-call {
@ -550,7 +550,7 @@
}
.theme-dark .dbg-search-result:hover {
background-color: rgba(29,79,115,.2); /* Select highlight blue at 40% alpha */
background-color: rgba(86, 117, 185, .2); /* Select highlight blue at 40% alpha */
}
.theme-light .dbg-search-result:hover {

Просмотреть файл

@ -90,7 +90,7 @@ body {
.variable-or-property .token-number,
.variable-or-property[return] > .title > .name,
.variable-or-property[scope] > .title > .name {
color: var(--theme-highlight-green);
color: var(--theme-highlight-purple);
}
.CodeMirror-Tern-completion-number:before {
@ -100,11 +100,13 @@ body {
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-property,
.cm-s-mozilla .cm-qualifier,
.variables-view-variable > .title > .name {
color: var(--theme-highlight-blue);
color: var(--theme-highlight-red);
}
.cm-s-mozilla .cm-def {
color: var(--theme-body-color);
}
.CodeMirror-Tern-completion-object:before {
@ -116,8 +118,9 @@ body {
.cm-s-mozilla .cm-tag,
.cm-s-mozilla .cm-header,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-qualifier,
.variables-view-property > .title > .name {
color: var(--theme-highlight-bluegrey);
color: var(--theme-highlight-blue);
}
.CodeMirror-Tern-completion-array:before {
@ -130,7 +133,7 @@ body {
.theme-fg-color5,
.cm-s-mozilla .cm-keyword {
color: var(--theme-highlight-lightorange);
color: var(--theme-highlight-red);
}
.theme-fg-color6,
@ -138,7 +141,7 @@ body {
.cm-s-mozilla .cm-string-2,
.variable-or-property .token-string,
.CodeMirror-Tern-farg {
color: var(--theme-highlight-orange);
color: var(--theme-highlight-purple);
}
.CodeMirror-Tern-completion-string:before,

Просмотреть файл

@ -7,8 +7,8 @@
--cell-border-color: rgba(255,255,255,0.15);
--cell-border-color-light: rgba(255,255,255,0.1);
--focus-cell-border-color: rgba(255,255,255,0.5);
--row-alt-background-color: rgba(29,79,115,0.15);
--row-hover-background-color: rgba(29,79,115,0.25);
--row-alt-background-color: rgba(86, 117, 185, 0.15);
--row-hover-background-color: rgba(86, 117, 185, 0.25);
}
.theme-light {

Просмотреть файл

@ -8,8 +8,8 @@
--cell-border-color: rgba(255,255,255,0.15);
--cell-border-color-light: rgba(255,255,255,0.1);
--focus-cell-border-color: rgba(255,255,255,0.5);
--row-alt-background-color: rgba(29,79,115,0.15);
--row-hover-background-color: rgba(29,79,115,0.25);
--row-alt-background-color: rgba(86, 117, 185, 0.15);
--row-hover-background-color: rgba(86, 117, 185, 0.25);
--filter-image: url(chrome://devtools/skin/images/timeline-filter.svg);
}

Просмотреть файл

@ -343,7 +343,7 @@
.theme-dark .devtools-toolbarbutton:not([disabled])[label][open],
.theme-dark .devtools-button:not(:empty)[checked=true],
.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked=true] {
background: rgba(29, 79, 115, .7); /* Select highlight blue */
background: var(--theme-selection-background-semitransparent);
color: var(--theme-selection-color);
}
.theme-light .devtools-toolbarbutton:not([disabled])[label][checked=true],
@ -860,7 +860,7 @@
}
.theme-dark .devtools-tabbar {
box-shadow: 0 -2px 0 rgba(0,0,0,.1) inset;
box-shadow: 0 -0.5px 0 var(--theme-splitter-color) inset;
}
#toolbox-tabs {
@ -976,12 +976,6 @@
background-color: var(--theme-selection-background);
}
.theme-dark .devtools-tabbar .devtools-tab[selected] {
box-shadow: 0 2px 0 #d7f1ff inset,
0 8px 3px -5px #2b82bf inset,
0 -2px 0 rgba(0,0,0,.2) inset;
}
#toolbox-tabs .devtools-tab[selected],
#toolbox-tabs .devtools-tab[highlighted] {
border-width: 0;

Просмотреть файл

@ -42,6 +42,7 @@
--theme-highlight-orange: #f13c00;
--theme-highlight-red: #ed2655;
--theme-highlight-pink: #b82ee5;
--theme-highlight-gray: #dde1e4;
/* For accessibility purposes we want to enhance the focus styling. This
* should improve keyboard navigation usability. */
@ -65,16 +66,16 @@
}
:root.theme-dark {
--theme-body-background: #14171a;
--theme-sidebar-background: #181d20;
--theme-contrast-background: #b28025;
--theme-body-background: #393f4c;
--theme-sidebar-background: #393f4c;
--theme-contrast-background: #ffb35b;
--theme-tab-toolbar-background: #252c33;
--theme-toolbar-background: #343c45;
--theme-selection-background: #1d4f73;
--theme-selection-background-semitransparent: rgba(29, 79, 115, .5);
--theme-tab-toolbar-background: #272b35;
--theme-toolbar-background: #272b35;
--theme-selection-background: #5675B9;
--theme-selection-background-semitransparent: rgba(86, 117, 185, 0.5);
--theme-selection-color: #f5f7fa;
--theme-splitter-color: black;
--theme-splitter-color: #454d5d;
--theme-comment: #757873;
--theme-body-color: #8fa1b2;
@ -84,14 +85,15 @@
--theme-content-color2: #8fa1b2;
--theme-content-color3: #5f7387;
--theme-highlight-green: #70bf53;
--theme-highlight-green: #00ff7f;
--theme-highlight-blue: #46afe3;
--theme-highlight-bluegrey: #5e88b0;
--theme-highlight-purple: #6b7abb;
--theme-highlight-purple: #bcb8db;
--theme-highlight-lightorange: #d99b28;
--theme-highlight-orange: #d96629;
--theme-highlight-red: #eb5368;
--theme-highlight-pink: #df80ff;
--theme-highlight-gray: #e9f4fe;
/* For accessibility purposes we want to enhance the focus styling. This
* should improve keyboard navigation usability. */
@ -134,13 +136,14 @@
--theme-content-color3: #667380;
--theme-highlight-green: #2cbb0f;
--theme-highlight-blue: #0088cc;
--theme-highlight-blue: #3455db;
--theme-highlight-bluegrey: #0072ab;
--theme-highlight-purple: #5b5fff;
--theme-highlight-purple: #887ce6;
--theme-highlight-lightorange: #d97e00;
--theme-highlight-orange: #f13c00;
--theme-highlight-red: #ed2655;
--theme-highlight-red: #e22f6f;
--theme-highlight-pink: #b82ee5;
--theme-highlight-gray: #dde1e4;
/* Colors used in Graphs, like performance tools. Similar colors to Chrome's timeline. */
--theme-graphs-green: #85d175;

Просмотреть файл

@ -2981,7 +2981,7 @@ Widgets.ObjectRenderers.add({
if (!this.options.concise) {
this._text(" ");
this.element.appendChild(this.el("span.console-string",
this.element.appendChild(this.el("span.theme-fg-color6",
VariablesView.getString(preview.text)));
}
},

Просмотреть файл

@ -12,6 +12,7 @@ const {
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
const ConsoleOutput = createClass({
@ -61,7 +62,7 @@ function isScrolledToBottom(outputNode, scrollNode) {
function mapStateToProps(state) {
return {
messages: state.messages
messages: getAllMessages(state)
};
}

Просмотреть файл

@ -7,6 +7,7 @@ DIRS += [
'actions',
'components',
'reducers',
'selectors',
'utils',
]

Просмотреть файл

@ -0,0 +1,12 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function getAllMessages(state) {
return state.messages;
}
exports.getAllMessages = getAllMessages;

Просмотреть файл

@ -0,0 +1,8 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'messages.js',
)

Просмотреть файл

@ -8,6 +8,7 @@ const {
getRepeatId,
prepareMessage
} = require("devtools/client/webconsole/new-console-output/utils/messages");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
function run_test() {
run_next_test();
@ -23,7 +24,7 @@ add_task(function*() {
const expectedMessage = prepareMessage(packet);
deepEqual(getState().messages, [expectedMessage],
deepEqual(getAllMessages(getState()), [expectedMessage],
"MESSAGE_ADD action adds a message");
});
@ -40,7 +41,7 @@ add_task(function*() {
const expectedMessage = prepareMessage(packet);
expectedMessage.repeat = 3;
deepEqual(getState().messages, [expectedMessage],
deepEqual(getAllMessages(getState()), [expectedMessage],
"Adding same message to the store twice results in repeated message");
});

Просмотреть файл

@ -30,7 +30,6 @@ loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-o
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "system", "devtools/shared/system");
loader.lazyRequireGetter(this, "Timers", "sdk/timers");
loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/jsterm", true);
loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/jsterm", true);
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
@ -996,10 +995,10 @@ WebConsoleFrame.prototype = {
Services.prefs.setBoolPref(this._filterPrefsPrefix + toggleType, state);
if (this._updateListenersTimeout) {
Timers.clearTimeout(this._updateListenersTimeout);
clearTimeout(this._updateListenersTimeout);
}
this._updateListenersTimeout = Timers.setTimeout(
this._updateListenersTimeout = setTimeout(
this._onUpdateListeners, 200);
},

Просмотреть файл

@ -18,7 +18,6 @@ const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
const {getDeviceFront} = require("devtools/server/actors/device");
const {getPreferenceFront} = require("devtools/server/actors/preference");
const {getSettingsFront} = require("devtools/server/actors/settings");
const {setTimeout} = require("sdk/timers");
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const {RuntimeScanners, RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});

Просмотреть файл

@ -30,7 +30,6 @@ const protocol = require("devtools/server/protocol");
const {method, Arg} = protocol;
const events = require("sdk/event/core");
const Heritage = require("sdk/core/heritage");
const {setTimeout, clearTimeout} = require("sdk/timers");
const EventEmitter = require("devtools/shared/event-emitter");
/**

Просмотреть файл

@ -7,7 +7,6 @@ const {Ci,Cu,Cc} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");
var {setTimeout,clearTimeout} = require("sdk/timers");
function MonitorActor(aConnection) {
this.conn = aConnection;

Просмотреть файл

@ -34,7 +34,7 @@ loader.lazyGetter(this, "Debugger", () => {
});
loader.lazyRequireGetter(this, "CssLogic", "devtools/shared/inspector/css-logic", true);
loader.lazyRequireGetter(this, "events", "sdk/event/core");
loader.lazyRequireGetter(this, "setTimeout", "sdk/timers", true);
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
/**
* A BreakpointActorMap is a map from locations to instances of BreakpointActor.

Просмотреть файл

@ -12,7 +12,6 @@ const {DebuggerServer} = require("devtools/server/main");
const Services = require("Services");
const promise = require("promise");
const {isWindowIncluded} = require("devtools/shared/layout/utils");
const {setTimeout, clearTimeout} = require("sdk/timers");
const specs = require("devtools/shared/specs/storage");
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");

Просмотреть файл

@ -14,13 +14,13 @@ Cu.import("resource://gre/modules/Task.jsm");
const promise = require("promise");
const events = require("sdk/event/core");
const {OriginalSourceFront} = require("devtools/client/fronts/stylesheets");
const {OriginalSourceFront, MediaRuleFront} = require("devtools/client/fronts/stylesheets");
const protocol = require("devtools/server/protocol");
const {Arg, Option, method, RetVal, types} = protocol;
const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
const {fetch} = require("devtools/shared/DevToolsUtils");
const {listenOnce} = require("devtools/shared/async-utils");
const {originalSourceSpec} = require("devtools/shared/specs/stylesheets");
const {originalSourceSpec, mediaRuleSpec} = require("devtools/shared/specs/stylesheets");
const {SourceMapConsumer} = require("source-map");
loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
@ -121,16 +121,7 @@ var OriginalSourceActor = protocol.ActorClassWithSpec(originalSourceSpec, {
* A MediaRuleActor lives on the server and provides access to properties
* of a DOM @media rule and emits events when it changes.
*/
var MediaRuleActor = protocol.ActorClass({
typeName: "mediarule",
events: {
"matches-change" : {
type: "matchesChange",
matches: Arg(0, "boolean"),
}
},
var MediaRuleActor = protocol.ActorClassWithSpec(mediaRuleSpec, {
get window() {
return this.parentActor.window;
},
@ -197,50 +188,6 @@ var MediaRuleActor = protocol.ActorClass({
}
});
/**
* Corresponding client-side front for a MediaRuleActor.
*/
var MediaRuleFront = protocol.FrontClass(MediaRuleActor, {
initialize: function(client, form) {
protocol.Front.prototype.initialize.call(this, client, form);
this._onMatchesChange = this._onMatchesChange.bind(this);
events.on(this, "matches-change", this._onMatchesChange);
},
_onMatchesChange: function(matches) {
this._form.matches = matches;
},
form: function(form, detail) {
if (detail === "actorid") {
this.actorID = form;
return;
}
this.actorID = form.actor;
this._form = form;
},
get mediaText() {
return this._form.mediaText;
},
get conditionText() {
return this._form.conditionText;
},
get matches() {
return this._form.matches;
},
get line() {
return this._form.line || -1;
},
get column() {
return this._form.column || -1;
},
get parentStyleSheet() {
return this.conn.getActor(this._form.parentStyleSheet);
}
});
types.addActorType("stylesheet");
/**

Просмотреть файл

@ -25,7 +25,6 @@ const { Class } = require("sdk/core/heritage");
// Be aggressive about lazy loading, as this will run on every
// toolbox startup
loader.lazyRequireGetter(this, "events", "sdk/event/core");
loader.lazyRequireGetter(this, "Timers", "sdk/timers");
loader.lazyRequireGetter(this, "Task", "resource://gre/modules/Task.jsm", true);
loader.lazyRequireGetter(this, "Memory", "devtools/server/performance/memory", true);
loader.lazyRequireGetter(this, "Framerate", "devtools/server/performance/framerate", true);
@ -174,7 +173,7 @@ var Timeline = exports.Timeline = Class({
}
}
this._dataPullTimeout = Timers.setTimeout(() => {
this._dataPullTimeout = setTimeout(() => {
this._pullTimelineData();
}, DEFAULT_TIMELINE_DATA_PULL_TIMEOUT);
},
@ -308,7 +307,7 @@ var Timeline = exports.Timeline = Class({
this._withDocLoadingEvents = false;
this._withGCEvents = false;
Timers.clearTimeout(this._dataPullTimeout);
clearTimeout(this._dataPullTimeout);
return endTime;
}),

Просмотреть файл

@ -12,7 +12,6 @@ var promise = require("promise");
loader.lazyRequireGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm", true);
loader.lazyRequireGetter(this, "setTimeout", "Timer", true);
// Re-export the thread-safe utils.
const ThreadSafeDevToolsUtils = require("./ThreadSafeDevToolsUtils.js");

Просмотреть файл

@ -280,10 +280,24 @@ DevToolsLoader.prototype = {
factory(this.require, this.exports, this.module);
},
};
// Lazy define console in order to load Console.jsm only when it is used
// Lazily define a few things so that the corresponding jsms are
// only loaded when used.
XPCOMUtils.defineLazyGetter(this._provider.globals, "console", () => {
return Cu.import("resource://gre/modules/Console.jsm", {}).console;
});
XPCOMUtils.defineLazyGetter(this._provider.globals, "clearTimeout", () => {
return Cu.import("resource://gre/modules/Timer.jsm", {}).clearTimeout;
});
XPCOMUtils.defineLazyGetter(this._provider.globals, "setTimeout", () => {
return Cu.import("resource://gre/modules/Timer.jsm", {}).setTimeout;
});
XPCOMUtils.defineLazyGetter(this._provider.globals, "clearInterval", () => {
return Cu.import("resource://gre/modules/Timer.jsm", {}).clearInterval;
});
XPCOMUtils.defineLazyGetter(this._provider.globals, "setInterval", () => {
return Cu.import("resource://gre/modules/Timer.jsm", {}).setInterval;
});
this._provider.load();
this.require = Loader.Require(this._provider.loader, { id: "devtools" });

Просмотреть файл

@ -7,7 +7,6 @@
"use strict";
const {Cc, Ci, Cu, Cr} = require("chrome");
const {setTimeout, clearTimeout} = require('sdk/timers');
const EventEmitter = require("devtools/shared/event-emitter");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DebuggerServer } = require("devtools/server/main");

Просмотреть файл

@ -33,7 +33,6 @@
const { Cu, CC, Cc, Ci } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const { setTimeout, clearTimeout } = require("sdk/timers");
const Services = require("Services");
const UDPSocket = CC("@mozilla.org/network/udp-socket;1",

Просмотреть файл

@ -27,8 +27,6 @@ loader.lazyRequireGetter(this, "Authenticators",
"devtools/shared/security/auth", true);
loader.lazyRequireGetter(this, "AuthenticationResult",
"devtools/shared/security/auth", true);
loader.lazyRequireGetter(this, "setTimeout", "Timer", true);
loader.lazyRequireGetter(this, "clearTimeout", "Timer", true);
DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");

Просмотреть файл

@ -3,7 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { RetVal, generateActorSpec } = require("devtools/server/protocol.js");
const {
Arg,
RetVal,
generateActorSpec
} = require("devtools/server/protocol.js");
const originalSourceSpec = generateActorSpec({
typeName: "originalsource",
@ -18,3 +22,16 @@ const originalSourceSpec = generateActorSpec({
});
exports.originalSourceSpec = originalSourceSpec;
const mediaRuleSpec = generateActorSpec({
typeName: "mediarule",
events: {
"matches-change": {
type: "matchesChange",
matches: Arg(0, "boolean"),
}
}
});
exports.mediaRuleSpec = mediaRuleSpec;

Просмотреть файл

@ -7,8 +7,6 @@ package org.mozilla.gecko;
import android.Manifest;
import android.app.DownloadManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
@ -24,6 +22,8 @@ import org.mozilla.gecko.cleanup.FileCleanupController;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.SuggestedSites;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import org.mozilla.gecko.delegates.ScreenshotDelegate;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.distribution.DistributionStoreCallback;
import org.mozilla.gecko.dlc.DownloadContentService;
@ -59,10 +59,9 @@ import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.promotion.AddToHomeScreenPromotion;
import org.mozilla.gecko.promotion.BookmarkStateChangeDelegate;
import org.mozilla.gecko.delegates.BookmarkStateChangeDelegate;
import org.mozilla.gecko.promotion.ReaderViewBookmarkPromotion;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.reader.SavedReaderViewHelper;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.reader.ReadingListHelper;
@ -88,7 +87,6 @@ import org.mozilla.gecko.trackingprotection.TrackingProtectionPrompt;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.DrawableUtil;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.Experiments;
import org.mozilla.gecko.util.FloatUtils;

Просмотреть файл

@ -553,7 +553,7 @@ public class Tab {
});
if (AboutPages.isAboutReader(url)) {
ReadingListHelper.cacheReaderItem(pageUrl, mAppContext);
ReadingListHelper.cacheReaderItem(pageUrl, mId, mAppContext);
}
}

Просмотреть файл

@ -3,7 +3,7 @@
* 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/. */
package org.mozilla.gecko.promotion;
package org.mozilla.gecko.delegates;
import android.content.Intent;
import android.content.SharedPreferences;
@ -21,7 +21,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.EditBookmarkDialog;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoSharedPrefs;
@ -32,6 +31,7 @@ import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.home.HomeConfig;
import org.mozilla.gecko.promotion.SimpleHelperUI;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.util.DrawableUtil;
@ -45,16 +45,9 @@ import java.lang.ref.WeakReference;
* This is responsible for showing snackbars and helper UIs related to the addition/removal
* of bookmarks, or reader view bookmarks.
*/
public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements Tabs.OnTabsChangedListener {
public class BookmarkStateChangeDelegate extends BrowserAppDelegateWithReference implements Tabs.OnTabsChangedListener {
private static final String LOGTAG = "BookmarkDelegate";
private WeakReference<BrowserApp> mBrowserApp;
@Override
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
mBrowserApp = new WeakReference<>(browserApp);
}
@Override
public void onResume(BrowserApp browserApp) {
Tabs.registerOnTabsChangedListener(this);
@ -100,7 +93,7 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements T
}
private boolean promoteReaderViewBookmarkAdded() {
final BrowserApp browserApp = mBrowserApp.get();
final BrowserApp browserApp = getBrowserApp();
if (browserApp == null) {
return false;
}
@ -130,7 +123,7 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements T
}
private void showBookmarkAddedSnackbar() {
final BrowserApp browserApp = mBrowserApp.get();
final BrowserApp browserApp = getBrowserApp();
if (browserApp == null) {
return;
}
@ -153,7 +146,7 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements T
}
private void showBookmarkRemovedSnackbar() {
final BrowserApp browserApp = mBrowserApp.get();
final BrowserApp browserApp = getBrowserApp();
if (browserApp == null) {
return;
}
@ -213,7 +206,7 @@ public class BookmarkStateChangeDelegate extends BrowserAppDelegate implements T
}
private void showReaderModeBookmarkAddedSnackbar() {
final BrowserApp browserApp = mBrowserApp.get();
final BrowserApp browserApp = getBrowserApp();
if (browserApp == null) {
return;
}

Просмотреть файл

@ -3,11 +3,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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.delegates;
import android.content.Intent;
import android.os.Bundle;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.tabs.TabsPanel;
/**

Просмотреть файл

@ -0,0 +1,29 @@
package org.mozilla.gecko.delegates;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import org.mozilla.gecko.BrowserApp;
import java.lang.ref.WeakReference;
/**
* BrowserAppDelegate that stores a reference to the parent BrowserApp.
*/
public abstract class BrowserAppDelegateWithReference extends BrowserAppDelegate {
private WeakReference<BrowserApp> browserApp;
@Override
@CallSuper
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
this.browserApp = new WeakReference<>(browserApp);
}
/**
* Obtain the referenced BrowserApp. May return <code>null</code> if the BrowserApp no longer
* exists.
*/
protected BrowserApp getBrowserApp() {
return browserApp.get();
}
}

Просмотреть файл

@ -3,27 +3,37 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.delegates;
import android.app.Activity;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.util.Log;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.ScreenshotObserver;
import org.mozilla.gecko.SnackbarHelper;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import java.lang.ref.WeakReference;
/**
* Delegate for observing screenshots being taken.
*/
public class ScreenshotDelegate extends BrowserAppDelegate implements ScreenshotObserver.OnScreenshotListener {
public class ScreenshotDelegate extends BrowserAppDelegateWithReference implements ScreenshotObserver.OnScreenshotListener {
private static final String LOGTAG = "GeckoScreenshotDelegate";
private WeakReference<Activity> activityReference;
private final ScreenshotObserver mScreenshotObserver = new ScreenshotObserver();
@Override
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
activityReference = new WeakReference<Activity>(browserApp);
super.onCreate(browserApp, savedInstanceState);
mScreenshotObserver.setListener(browserApp, this);
}
@ -43,7 +53,7 @@ public class ScreenshotDelegate extends BrowserAppDelegate implements Screenshot
return;
}
final Activity activity = activityReference.get();
final Activity activity = getBrowserApp();
if (activity == null) {
return;
}

Просмотреть файл

@ -7,15 +7,14 @@ package org.mozilla.gecko.feeds;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import java.util.List;

Просмотреть файл

@ -7,12 +7,10 @@ package org.mozilla.gecko.feeds.action;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
@ -21,7 +19,6 @@ import android.text.format.DateFormat;
import org.json.JSONException;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Telemetry;
@ -37,7 +34,6 @@ import org.mozilla.gecko.feeds.subscriptions.FeedSubscription;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.util.StringUtils;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

Просмотреть файл

@ -224,13 +224,14 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
@Override
public HomeContextMenuInfo makeContextMenuInfoFromPosition(View view, int position) {
if (position == 0) {
// No context menu for smartfolders.
return null;
final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
if (itemType == CombinedHistoryItem.ItemType.HISTORY) {
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, -1);
historyCursor.moveToPosition(transformAdapterPositionForDataStructure(CombinedHistoryItem.ItemType.HISTORY, position));
return populateHistoryInfoFromCursor(info, historyCursor);
}
HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, -1);
historyCursor.moveToPosition(transformAdapterPositionForDataStructure(CombinedHistoryItem.ItemType.HISTORY, position));
return populateHistoryInfoFromCursor(info, historyCursor);
return null;
}
protected static HomeContextMenuInfo populateHistoryInfoFromCursor(HomeContextMenuInfo info, Cursor cursor) {

Просмотреть файл

@ -17,6 +17,7 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
@ -146,11 +147,23 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
animator.setChangeDuration(100);
animator.setMoveDuration(100);
animator.setRemoveDuration(100);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.setItemAnimator(animator);
mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext()));
mRecyclerView.setOnHistoryClickedListener(mUrlOpenListener);
mRecyclerView.setOnPanelLevelChangeListener(new OnLevelChangeListener());
mRecyclerView.setHiddenClientsDialogBuilder(new HiddenClientsHelper());
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final LinearLayoutManager llm = (LinearLayoutManager) recyclerView.getLayoutManager();
if ((mPanelLevel == OnPanelLevelChangeListener.PanelLevel.PARENT) && (llm.findLastCompletelyVisibleItemPosition() == HistoryCursorLoader.HISTORY_LIMIT)) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.LIST, "history_scroll_max");
}
}
});
registerForContextMenu(mRecyclerView);
}
@ -186,6 +199,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
syncSetupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "history_syncsetup");
// This Activity will redirect to the correct Activity as needed.
final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
startActivity(intent);
@ -221,7 +235,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
private static class HistoryCursorLoader extends SimpleCursorLoader {
// Max number of history results
private static final int HISTORY_LIMIT = 100;
public static final int HISTORY_LIMIT = 100;
private final BrowserDB mDB;
public HistoryCursorLoader(Context context) {
@ -402,7 +416,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.PANEL, "hint-private-browsing");
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "hint_private_browsing");
try {
final JSONObject json = new JSONObject();
json.put("type", "Menu:Open");

Просмотреть файл

@ -17,7 +17,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;

Просмотреть файл

@ -12,25 +12,24 @@ import android.os.Bundle;
import com.keepsafe.switchboard.SwitchBoard;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.BrowserAppDelegate;
import org.mozilla.gecko.delegates.BrowserAppDelegate;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.delegates.BrowserAppDelegateWithReference;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.util.Experiments;
import java.lang.ref.WeakReference;
public class ReaderViewBookmarkPromotion extends BrowserAppDelegate implements Tabs.OnTabsChangedListener {
private WeakReference<BrowserApp> mBrowserApp;
public class ReaderViewBookmarkPromotion extends BrowserAppDelegateWithReference implements Tabs.OnTabsChangedListener {
private int mTimesEnteredReaderMode;
private boolean mExperimentEnabled;
@Override
public void onCreate(BrowserApp browserApp, Bundle savedInstanceState) {
mBrowserApp = new WeakReference<>(browserApp);
super.onCreate(browserApp, savedInstanceState);
mExperimentEnabled = SwitchBoard.isInExperiment(browserApp, Experiments.TRIPLE_READERVIEW_BOOKMARK_PROMPT);
}
@ -82,7 +81,7 @@ public class ReaderViewBookmarkPromotion extends BrowserAppDelegate implements T
}
private void promoteBookmarking() {
final BrowserApp browserApp = mBrowserApp.get();
final BrowserApp browserApp = getBrowserApp();
if (browserApp == null) {
return;
}

Просмотреть файл

@ -91,7 +91,7 @@ public final class ReadingListHelper implements NativeEventListener {
rch.put(url, path, size);
}
public static void cacheReaderItem(final String url, Context context) {
public static void cacheReaderItem(final String url, final int tabID, Context context) {
if (AboutPages.isAboutReader(url)) {
throw new IllegalArgumentException("Page url must be original (not about:reader) url");
}
@ -99,7 +99,7 @@ public final class ReadingListHelper implements NativeEventListener {
SavedReaderViewHelper rch = SavedReaderViewHelper.getSavedReaderViewHelper(context);
if (!rch.isURLCached(url)) {
GeckoAppShell.notifyObservers("Reader:AddToCache", url);
GeckoAppShell.notifyObservers("Reader:AddToCache", Integer.toString(tabID));
}
}

Просмотреть файл

@ -206,7 +206,6 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'BaseGeckoInterface.java',
'BootReceiver.java',
'BrowserApp.java',
'BrowserAppDelegate.java',
'BrowserLocaleManager.java',
'cleanup/FileCleanupController.java',
'cleanup/FileCleanupService.java',
@ -247,6 +246,10 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'db/UrlAnnotations.java',
'db/URLMetadata.java',
'db/URLMetadataTable.java',
'delegates/BookmarkStateChangeDelegate.java',
'delegates/BrowserAppDelegate.java',
'delegates/BrowserAppDelegateWithReference.java',
'delegates/ScreenshotDelegate.java',
'DevToolsAuthHelper.java',
'distribution/Distribution.java',
'distribution/DistributionStoreCallback.java',
@ -509,7 +512,6 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'PrintHelper.java',
'PrivateTab.java',
'promotion/AddToHomeScreenPromotion.java',
'promotion/BookmarkStateChangeDelegate.java',
'promotion/HomeScreenPrompt.java',
'promotion/ReaderViewBookmarkPromotion.java',
'promotion/SimpleHelperUI.java',
@ -536,7 +538,6 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'restrictions/RestrictionConfiguration.java',
'restrictions/RestrictionProvider.java',
'restrictions/Restrictions.java',
'ScreenshotDelegate.java',
'ScreenshotObserver.java',
'search/SearchEngine.java',
'search/SearchEngineManager.java',

Просмотреть файл

@ -82,8 +82,13 @@ var Reader = {
}
case "Reader:AddToCache": {
let tab = BrowserApp.getTabForId(aData);
if (!tab) {
throw new Error("No tab for tabID = " + aData + " when trying to save reader view article");
}
// If the article is coming from reader mode, we must have fetched it already.
this._getArticle(aData).then((article) => {
this._getArticleData(tab.browser).then((article) => {
ReaderMode.storeArticleInCache(article);
}).catch(e => Cu.reportError("Error storing article in cache: " + e));
break;
@ -281,6 +286,23 @@ var Reader = {
});
}),
_getArticleData: function(browser) {
return new Promise((resolve, reject) => {
if (browser == null) {
reject("_getArticleData needs valid browser");
}
let mm = browser.messageManager;
let listener = (message) => {
mm.removeMessageListener("Reader:StoredArticleData", listener);
resolve(message.data.article);
};
mm.addMessageListener("Reader:StoredArticleData", listener);
mm.sendAsyncMessage("Reader:GetStoredArticleData");
});
},
/**
* Migrates old indexedDB reader mode cache to new JSON cache.
*/

Просмотреть файл

@ -29,6 +29,8 @@ function backgroundScript(token) {
browser.test.assertEq(port.name, token, "token matches");
port.postMessage(token + "-done");
});
browser.test.sendMessage("background-ready");
}
function contentScript(token) {
@ -80,7 +82,9 @@ add_task(function* test_contentscript() {
let extension1 = ExtensionTestUtils.loadExtension(makeExtension());
let extension2 = ExtensionTestUtils.loadExtension(makeExtension());
yield Promise.all([extension1.startup(), extension2.startup()]);
info("extensions loaded");
yield extension1.awaitMessage("background-ready");
yield extension2.awaitMessage("background-ready");
let win = window.open("file_sample.html");
@ -92,7 +96,6 @@ add_task(function* test_contentscript() {
yield extension1.unload();
yield extension2.unload();
info("extensions unloaded");
});
</script>

Просмотреть файл

@ -37,6 +37,7 @@ var AboutReader = function(mm, win, articlePromise) {
this._mm.addMessageListener("Reader:CloseDropdown", this);
this._mm.addMessageListener("Reader:AddButton", this);
this._mm.addMessageListener("Reader:RemoveButton", this);
this._mm.addMessageListener("Reader:GetStoredArticleData", this);
this._docRef = Cu.getWeakReference(doc);
this._winRef = Cu.getWeakReference(win);
@ -205,6 +206,9 @@ AboutReader.prototype = {
}
break;
}
case "Reader:GetStoredArticleData": {
this._mm.sendAsyncMessage("Reader:StoredArticleData", { article: this._article });
}
}
},
@ -253,6 +257,7 @@ AboutReader.prototype = {
this._mm.removeMessageListener("Reader:CloseDropdown", this);
this._mm.removeMessageListener("Reader:AddButton", this);
this._mm.removeMessageListener("Reader:RemoveButton", this);
this._mm.removeMessageListener("Reader:GetStoredArticleData", this);
this._windowUnloaded = true;
break;
}

Просмотреть файл

@ -8705,7 +8705,7 @@
"kind": "enumerated",
"n_values": 8,
"releaseChannelCollection": "opt-out",
"description": "Number of times a room URL is shared (0=COPY_FROM_PANEL, 1=COPY_FROM_CONVERSATION, 2=EMAIL_FROM_CALLFAILED, 3=EMAIL_FROM_CONVERSATION, 4=FACEBOOK_FROM_CONVERSATION)"
"description": "Number of times a room URL is shared (0=COPY_FROM_PANEL, 1=COPY_FROM_CONVERSATION, 2=EMAIL_FROM_CALLFAILED, 3=EMAIL_FROM_CONVERSATION, 4=FACEBOOK_FROM_CONVERSATION, 5=EMAIL_FROM_PANEL)"
},
"LOOP_ROOM_CREATE": {
"alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],

Просмотреть файл

@ -105,7 +105,7 @@ xul|groupbox {
font-size: 1.25rem;
}
xul|groupbox xul|label:not(.menu-accel):not(.menu-text),
xul|groupbox xul|label:not(.menu-accel):not(.menu-text):not(.indent),
xul|groupbox xul|description {
/* !important needed to override toolkit !important rule */
-moz-margin-start: 0 !important;