зеркало из https://github.com/mozilla/gecko-dev.git
Коммит
70ba843b03
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче