merge autoland to mozilla-inbound. r=merge a=merge
MozReview-Commit-ID: 1gVeCMsyp4B
|
@ -501,6 +501,9 @@ pref("browser.bookmarks.max_backups", 15);
|
|||
|
||||
pref("browser.bookmarks.showRecentlyBookmarked", true);
|
||||
|
||||
// Whether menu should close after Ctrl-click, middle-click, etc.
|
||||
pref("browser.bookmarks.openInTabClosesMenu", true);
|
||||
|
||||
// Scripts & Windows prefs
|
||||
pref("dom.disable_open_during_load", true);
|
||||
pref("javascript.options.showInConsole", true);
|
||||
|
@ -722,15 +725,9 @@ pref("browser.preferences.instantApply", true);
|
|||
// Toggling Search bar on and off in about:preferences
|
||||
pref("browser.preferences.search", true);
|
||||
|
||||
// Once the Storage Management is completed.
|
||||
// (The Storage Management-related prefs are browser.storageManager.* )
|
||||
// The Offline(Appcache) Group section in about:preferences will be hidden.
|
||||
// And the task to clear appcache will be done by Storage Management.
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
// We prefer the storage manager (see browser.storageManager.enabled)
|
||||
// over the old offlineGroup UI. Removing the offline group UI is bug 1399808.
|
||||
pref("browser.preferences.offlineGroup.enabled", false);
|
||||
#else
|
||||
pref("browser.preferences.offlineGroup.enabled", true);
|
||||
#endif
|
||||
|
||||
pref("browser.preferences.defaultPerformanceSettings.enabled", true);
|
||||
|
||||
|
@ -1692,10 +1689,13 @@ pref("browser.crashReports.unsubmittedCheck.chancesUntilSuppress", 4);
|
|||
pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
|
||||
|
||||
// Preferences for the form autofill system extension
|
||||
// The value of "extensions.formautofill.available" can be "on", "off" and "detect".
|
||||
// The "detect" means it's enabled if conditions defined in the extension are met.
|
||||
// The truthy values of "extensions.formautofill.available" are "on" and "detect",
|
||||
// any other value means autofill isn't available.
|
||||
// "detect" means it's enabled if conditions defined in the extension are met.
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("extensions.formautofill.available", "on");
|
||||
#elif MOZ_UPDATE_CHANNEL == release
|
||||
pref("extensions.formautofill.available", "staged-rollout");
|
||||
#else
|
||||
pref("extensions.formautofill.available", "detect");
|
||||
#endif
|
||||
|
|
|
@ -385,6 +385,7 @@
|
|||
#endif
|
||||
context="placesContext"
|
||||
openInTabs="children"
|
||||
onmouseup="BookmarksEventHandler.onMouseUp(event);"
|
||||
oncommand="BookmarksEventHandler.onCommand(event);"
|
||||
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
|
||||
onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
|
||||
|
|
|
@ -893,6 +893,22 @@ var BookmarksEventHandler = {
|
|||
* @param aView
|
||||
* The places view which aEvent should be associated with.
|
||||
*/
|
||||
|
||||
onMouseUp(aEvent) {
|
||||
// Handles left-click with modifier if not browser.bookmarks.openInTabClosesMenu.
|
||||
if (aEvent.button != 0 || PlacesUIUtils.openInTabClosesMenu)
|
||||
return;
|
||||
let target = aEvent.originalTarget;
|
||||
if (target.tagName != "menuitem")
|
||||
return;
|
||||
let modifKey = AppConstants.platform === "macosx" ? aEvent.metaKey
|
||||
: aEvent.ctrlKey;
|
||||
// Don't keep menu open for 'Open all in Tabs'.
|
||||
if (modifKey && !target.classList.contains("openintabs-menuitem")) {
|
||||
target.setAttribute("closemenu", "none");
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function BEH_onClick(aEvent, aView) {
|
||||
// Only handle middle-click or left-click with modifiers.
|
||||
let modifKey;
|
||||
|
@ -906,17 +922,22 @@ var BookmarksEventHandler = {
|
|||
return;
|
||||
|
||||
var target = aEvent.originalTarget;
|
||||
// If this event bubbled up from a menu or menuitem, close the menus.
|
||||
// Do this before opening tabs, to avoid hiding the open tabs confirm-dialog.
|
||||
if (target.localName == "menu" || target.localName == "menuitem") {
|
||||
for (let node = target.parentNode; node; node = node.parentNode) {
|
||||
if (node.localName == "menupopup")
|
||||
node.hidePopup();
|
||||
else if (node.localName != "menu" &&
|
||||
node.localName != "hbox" &&
|
||||
node.localName != "vbox" )
|
||||
break;
|
||||
}
|
||||
// If this event bubbled up from a menu or menuitem,
|
||||
// close the menus if browser.bookmarks.openInTabClosesMenu.
|
||||
if ((PlacesUIUtils.openInTabClosesMenu && target.tagName == "menuitem") ||
|
||||
target.tagName == "menu" ||
|
||||
target.classList.contains("openintabs-menuitem")) {
|
||||
closeMenus(aEvent.target);
|
||||
}
|
||||
// Command already precesssed so remove any closemenu attr set in onMouseUp.
|
||||
if (aEvent.button == 0 &&
|
||||
target.tagName == "menuitem" &&
|
||||
target.getAttribute("closemenu") == "none") {
|
||||
// On Mac we need to extend when we remove the flag, to avoid any pre-close
|
||||
// animations.
|
||||
setTimeout(() => {
|
||||
target.removeAttribute("closemenu");
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) {
|
||||
|
@ -1520,6 +1541,7 @@ var LibraryUI = {
|
|||
libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
|
||||
libraryButton.getAttribute("overflowedItem") == "true" ||
|
||||
!libraryButton.closest("#nav-bar") ||
|
||||
!window.toolbar.visible ||
|
||||
!this.COSMETIC_ANIMATIONS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -64,18 +64,24 @@ var SidebarUI = {
|
|||
},
|
||||
|
||||
uninit() {
|
||||
let enumerator = Services.wm.getEnumerator(null);
|
||||
// If this is the last browser window, persist various values that should be
|
||||
// remembered for after a restart / reopening a browser window.
|
||||
let enumerator = Services.wm.getEnumerator("navigator:browser");
|
||||
enumerator.getNext();
|
||||
if (!enumerator.hasMoreElements()) {
|
||||
document.persist("sidebar-box", "sidebarcommand");
|
||||
|
||||
let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
|
||||
|
||||
if (this._box.hasAttribute("positionend")) {
|
||||
document.persist("sidebar-box", "positionend");
|
||||
} else {
|
||||
xulStore.removeValue(document.documentURI, "sidebar-box", "positionend");
|
||||
}
|
||||
if (this._box.hasAttribute("checked")) {
|
||||
document.persist("sidebar-box", "checked");
|
||||
} else {
|
||||
xulStore.removeValue(document.documentURI, "sidebar-box", "checked");
|
||||
}
|
||||
|
||||
document.persist("sidebar-box", "width");
|
||||
document.persist("sidebar-title", "value");
|
||||
|
@ -180,13 +186,19 @@ var SidebarUI = {
|
|||
// no source UI or no _box means we also can't adopt the state.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set sidebar command even if hidden, so that we keep the same sidebar
|
||||
// even if it's currently closed.
|
||||
let commandID = sourceUI._box.getAttribute("sidebarcommand");
|
||||
if (commandID) {
|
||||
this._box.setAttribute("sidebarcommand", commandID);
|
||||
}
|
||||
|
||||
if (sourceUI._box.hidden) {
|
||||
// just hidden means we have adopted the hidden state.
|
||||
return true;
|
||||
}
|
||||
|
||||
let commandID = sourceUI._box.getAttribute("sidebarcommand");
|
||||
|
||||
// dynamically generated sidebars will fail this check, but we still
|
||||
// consider it adopted.
|
||||
if (!document.getElementById(commandID)) {
|
||||
|
@ -223,18 +235,22 @@ var SidebarUI = {
|
|||
}
|
||||
|
||||
// If we're not adopting settings from a parent window, set them now.
|
||||
let commandID = this._box.getAttribute("sidebarcommand");
|
||||
if (!commandID) {
|
||||
let wasOpen = this._box.getAttribute("checked");
|
||||
if (!wasOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getElementById(commandID)) {
|
||||
let commandID = this._box.getAttribute("sidebarcommand");
|
||||
if (commandID && document.getElementById(commandID)) {
|
||||
this.showInitially(commandID);
|
||||
} else {
|
||||
this._box.removeAttribute("checked");
|
||||
// Remove the |sidebarcommand| attribute, because the element it
|
||||
// refers to no longer exists, so we should assume this sidebar
|
||||
// panel has been uninstalled. (249883)
|
||||
this._box.removeAttribute("sidebarcommand");
|
||||
// We use setAttribute rather than removeAttribute so it persists
|
||||
// correctly.
|
||||
this._box.setAttribute("sidebarcommand", "");
|
||||
// On a startup in which the startup cache was invalidated (e.g. app update)
|
||||
// extensions will not be started prior to delayedLoad, thus the
|
||||
// sidebarcommand element will not exist yet. Store the commandID so
|
||||
|
@ -274,10 +290,9 @@ var SidebarUI = {
|
|||
|
||||
/**
|
||||
* The ID of the current sidebar (ie, the ID of the broadcaster being used).
|
||||
* This can be set even if the sidebar is hidden.
|
||||
*/
|
||||
get currentID() {
|
||||
return this._box.getAttribute("sidebarcommand");
|
||||
return this.isOpen ? this._box.getAttribute("sidebarcommand") : "";
|
||||
},
|
||||
|
||||
get title() {
|
||||
|
@ -308,8 +323,12 @@ var SidebarUI = {
|
|||
*/
|
||||
toggle(commandID = this.lastOpenedId, triggerNode) {
|
||||
// First priority for a default value is this.lastOpenedId which is set during show()
|
||||
// and not reset in hide(), unlike currentID. If show() hasn't been called or the command
|
||||
// doesn't exist anymore, then fallback to a default sidebar.
|
||||
// and not reset in hide(), unlike currentID. If show() hasn't been called and we don't
|
||||
// have a persisted command either, or the command doesn't exist anymore, then
|
||||
// fallback to a default sidebar.
|
||||
if (!commandID) {
|
||||
commandID = this._box.getAttribute("sidebarcommand");
|
||||
}
|
||||
if (!commandID || !this.getBroadcasterById(commandID)) {
|
||||
commandID = this.DEFAULT_SIDEBAR_ID;
|
||||
}
|
||||
|
@ -456,7 +475,6 @@ var SidebarUI = {
|
|||
this.browser.docShell.createAboutBlankContentViewer(null);
|
||||
|
||||
sidebarBroadcaster.removeAttribute("checked");
|
||||
this._box.setAttribute("sidebarcommand", "");
|
||||
this._box.removeAttribute("checked");
|
||||
this._box.hidden = this._splitter.hidden = true;
|
||||
|
||||
|
|
|
@ -1017,6 +1017,7 @@
|
|||
<hbox flex="1"
|
||||
id="PlacesToolbar"
|
||||
context="placesContext"
|
||||
onmouseup="BookmarksEventHandler.onMouseUp(event);"
|
||||
onclick="BookmarksEventHandler.onClick(event, this._placesView);"
|
||||
oncommand="BookmarksEventHandler.onCommand(event);"
|
||||
tooltip="bhTooltip"
|
||||
|
@ -1094,6 +1095,7 @@
|
|||
placespopup="true"
|
||||
context="placesContext"
|
||||
openInTabs="children"
|
||||
onmouseup="BookmarksEventHandler.onMouseUp(event);"
|
||||
oncommand="BookmarksEventHandler.onCommand(event);"
|
||||
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
|
||||
onpopupshowing="BookmarkingUI.onPopupShowing(event);
|
||||
|
|
|
@ -155,10 +155,10 @@ add_task(async function test_ignoring_window_opener() {
|
|||
"url(\"chrome://browser/skin/connection-secure.svg\")",
|
||||
"Using expected icon image in the identity block");
|
||||
is(securityViewBG,
|
||||
"url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
|
||||
"url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"Using expected icon image in the Control Center main view");
|
||||
is(securityContentBG,
|
||||
"url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
|
||||
"url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"Using expected icon image in the Control Center subview");
|
||||
|
||||
ok(Array.every(document.querySelectorAll("[when-loginforms=insecure]"),
|
||||
|
|
|
@ -232,9 +232,9 @@ async function assertMixedContentBlockingState(tabbrowser, states = {}) {
|
|||
}
|
||||
|
||||
if (stateSecure) {
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using secure icon");
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-secure\")",
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using secure icon");
|
||||
}
|
||||
|
||||
|
@ -245,15 +245,15 @@ async function assertMixedContentBlockingState(tabbrowser, states = {}) {
|
|||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/mcb-disabled.svg\")",
|
||||
"CC using active loaded icon");
|
||||
} else if (activeBlocked || passiveLoaded) {
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using degraded icon");
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using degraded icon");
|
||||
} else {
|
||||
// There is a case here with weak ciphers, but no bc tests are handling this yet.
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
|
||||
is(securityViewBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using degraded icon");
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg#connection-degraded\")",
|
||||
is(securityContentBG, "url(\"chrome://browser/skin/controlcenter/connection.svg\")",
|
||||
"CC using degraded icon");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -716,16 +716,16 @@
|
|||
label="&historyMenu.label;"
|
||||
closemenu="none"
|
||||
oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
|
||||
<toolbarbutton id="appMenu-library-remotetabs-button"
|
||||
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
|
||||
label="&appMenuRemoteTabs.label;"
|
||||
closemenu="none"
|
||||
oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
|
||||
<toolbarbutton id="appMenu-library-downloads-button"
|
||||
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
|
||||
label="&libraryDownloads.label;"
|
||||
closemenu="none"
|
||||
oncommand="DownloadsSubview.show(this);"/>
|
||||
<toolbarbutton id="appMenu-library-remotetabs-button"
|
||||
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
|
||||
label="&appMenuRemoteTabs.label;"
|
||||
closemenu="none"
|
||||
oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
|
||||
</vbox>
|
||||
</panelview>
|
||||
|
||||
|
|
|
@ -13,19 +13,19 @@ registerCleanupFunction(async function() {
|
|||
}
|
||||
});
|
||||
|
||||
var showSidebar = async function() {
|
||||
let button = document.getElementById("sidebar-button");
|
||||
let sidebarFocusedPromise = BrowserTestUtils.waitForEvent(document, "SidebarFocused");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {});
|
||||
var showSidebar = async function(win = window) {
|
||||
let button = win.document.getElementById("sidebar-button");
|
||||
let sidebarFocusedPromise = BrowserTestUtils.waitForEvent(win.document, "SidebarFocused");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, win);
|
||||
await sidebarFocusedPromise;
|
||||
ok(SidebarUI.isOpen, "Sidebar is opened");
|
||||
ok(win.SidebarUI.isOpen, "Sidebar is opened");
|
||||
ok(button.hasAttribute("checked"), "Toolbar button is checked");
|
||||
};
|
||||
|
||||
var hideSidebar = async function() {
|
||||
let button = document.getElementById("sidebar-button");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {});
|
||||
ok(!SidebarUI.isOpen, "Sidebar is closed");
|
||||
var hideSidebar = async function(win = window) {
|
||||
let button = win.document.getElementById("sidebar-button");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, win);
|
||||
ok(!win.SidebarUI.isOpen, "Sidebar is closed");
|
||||
ok(!button.hasAttribute("checked"), "Toolbar button isn't checked");
|
||||
};
|
||||
|
||||
|
@ -40,4 +40,12 @@ add_task(async function() {
|
|||
await hideSidebar();
|
||||
await showSidebar();
|
||||
is(SidebarUI.currentID, "viewHistorySidebar", "Selected sidebar remembered");
|
||||
|
||||
await hideSidebar();
|
||||
let otherWin = await BrowserTestUtils.openNewBrowserWindow({opener: window});
|
||||
await showSidebar(otherWin);
|
||||
is(otherWin.SidebarUI.currentID, "viewHistorySidebar", "Selected sidebar remembered across windows");
|
||||
await hideSidebar(otherWin);
|
||||
|
||||
await BrowserTestUtils.closeWindow(otherWin);
|
||||
});
|
||||
|
|
|
@ -130,8 +130,23 @@ const DownloadsButton = {
|
|||
this._getAnchorInternal();
|
||||
},
|
||||
|
||||
unhide() {
|
||||
/**
|
||||
* Unhide the button. Generally, this only needs to use the placeholder.
|
||||
* However, when starting customize mode, if the button is in the palette,
|
||||
* we need to unhide it before customize mode is entered, otherwise it
|
||||
* gets ignored by customize mode. To do this, we pass true for
|
||||
* `includePalette`. We don't always look in the palette because it's
|
||||
* inefficient (compared to getElementById), shouldn't be necessary, and
|
||||
* if _placeholder returned the node even if in the palette, other checks
|
||||
* would break.
|
||||
*
|
||||
* @param includePalette whether to search the palette, too. Defaults to false.
|
||||
*/
|
||||
unhide(includePalette = false) {
|
||||
let button = this._placeholder;
|
||||
if (!button && includePalette) {
|
||||
button = gNavToolbox.palette.querySelector("#downloads-button");
|
||||
}
|
||||
if (button && button.hasAttribute("hidden")) {
|
||||
button.removeAttribute("hidden");
|
||||
if (this._navBar.contains(button)) {
|
||||
|
@ -190,7 +205,7 @@ const DownloadsButton = {
|
|||
// during customization, even if requested using the getAnchor method.
|
||||
this._customizing = true;
|
||||
this._anchorRequested = false;
|
||||
this.unhide();
|
||||
this.unhide(true);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -308,7 +323,9 @@ const DownloadsIndicatorView = {
|
|||
|
||||
// If the view is initialized, we need to update the elements now that
|
||||
// they are finally available in the document.
|
||||
if (this._initialized) {
|
||||
// We need to re-check for the placeholder because it might have
|
||||
// disappeared since then.
|
||||
if (this._initialized && DownloadsButton._placeholder) {
|
||||
DownloadsCommon.getIndicatorData(window).refreshView(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -251,6 +251,41 @@ add_task(async function checkStateForDownloads() {
|
|||
ok(!downloadsButton.hasAttribute("hidden"),
|
||||
"Button should still not be hidden in the panel " +
|
||||
"when downloads count reaches 0 after being non-0.");
|
||||
|
||||
CustomizableUI.reset();
|
||||
});
|
||||
|
||||
/**
|
||||
* Check that if the button is moved to the palette, we unhide it
|
||||
* in customize mode even if it was always hidden. We use a new
|
||||
* window to test this.
|
||||
*/
|
||||
add_task(async function checkStateWhenHiddenInPalette() {
|
||||
ok(Services.prefs.getBoolPref(kDownloadAutoHidePref),
|
||||
"Pref should be causing us to autohide");
|
||||
gCustomizeMode.removeFromArea(document.getElementById("downloads-button"));
|
||||
// In a new window, the button will have been hidden
|
||||
let otherWin = await BrowserTestUtils.openNewBrowserWindow();
|
||||
ok(!otherWin.document.getElementById("downloads-button"),
|
||||
"Button shouldn't be visible in the window");
|
||||
|
||||
let paletteButton = otherWin.gNavToolbox.palette.querySelector("#downloads-button");
|
||||
ok(paletteButton, "Button should exist in the palette");
|
||||
if (paletteButton) {
|
||||
ok(paletteButton.hidden, "Button will still have the hidden attribute");
|
||||
await promiseCustomizeStart(otherWin);
|
||||
ok(!paletteButton.hidden,
|
||||
"Button should no longer be hidden in customize mode");
|
||||
ok(otherWin.document.getElementById("downloads-button"),
|
||||
"Button should be in the document now.");
|
||||
await promiseCustomizeEnd(otherWin);
|
||||
// We purposefully don't assert anything about what happens next.
|
||||
// It doesn't really matter if the button remains unhidden in
|
||||
// the palette, and if we move it we'll unhide it then (the other
|
||||
// tests check this).
|
||||
}
|
||||
await BrowserTestUtils.closeWindow(otherWin);
|
||||
CustomizableUI.reset();
|
||||
});
|
||||
|
||||
function promiseCustomizeStart(aWindow = window) {
|
||||
|
|
|
@ -499,18 +499,7 @@ class TabTracker extends TabTrackerBase {
|
|||
let windowId = windowTracker.getId(nativeTab.ownerGlobal);
|
||||
let tabId = this.getId(nativeTab);
|
||||
|
||||
// When addons run in-process, `window.close()` is synchronous. Most other
|
||||
// addon-invoked calls are asynchronous since they go through a proxy
|
||||
// context via the message manager. This includes event registrations such
|
||||
// as `tabs.onRemoved.addListener`.
|
||||
//
|
||||
// So, even if `window.close()` were to be called (in-process) after calling
|
||||
// `tabs.onRemoved.addListener`, then the tab would be closed before the
|
||||
// event listener is registered. To make sure that the event listener is
|
||||
// notified, we dispatch `tabs.onRemoved` asynchronously.
|
||||
Services.tm.dispatchToMainThread(() => {
|
||||
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
|
||||
});
|
||||
this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
|
||||
}
|
||||
|
||||
getBrowserData(browser) {
|
||||
|
|
|
@ -248,6 +248,8 @@ add_task(async function testTabEventsSize() {
|
|||
|
||||
add_task(async function testTabRemovalEvent() {
|
||||
async function background() {
|
||||
let events = [];
|
||||
|
||||
function awaitLoad(tabId) {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener(function listener(tabId_, changed, tab) {
|
||||
|
@ -260,12 +262,13 @@ add_task(async function testTabRemovalEvent() {
|
|||
}
|
||||
|
||||
chrome.tabs.onRemoved.addListener((tabId, info) => {
|
||||
browser.test.assertEq(0, events.length, "No events recorded before onRemoved.");
|
||||
events.push("onRemoved");
|
||||
browser.test.log("Make sure the removed tab is not available in the tabs.query callback.");
|
||||
chrome.tabs.query({}, tabs => {
|
||||
for (let tab of tabs) {
|
||||
browser.test.assertTrue(tab.id != tabId, "Tab query should not include removed tabId");
|
||||
}
|
||||
browser.test.notifyPass("tabs-events");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -274,6 +277,13 @@ add_task(async function testTabRemovalEvent() {
|
|||
let tab = await browser.tabs.create({url: url});
|
||||
await awaitLoad(tab.id);
|
||||
|
||||
chrome.tabs.onActivated.addListener(info => {
|
||||
browser.test.assertEq(1, events.length, "One event recorded before onActivated.");
|
||||
events.push("onActivated");
|
||||
browser.test.assertEq("onRemoved", events[0], "onRemoved fired before onActivated.");
|
||||
browser.test.notifyPass("tabs-events");
|
||||
});
|
||||
|
||||
await browser.tabs.remove(tab.id);
|
||||
} catch (e) {
|
||||
browser.test.fail(`${e} :: ${e.stack}`);
|
||||
|
|
|
@ -1576,6 +1576,8 @@ XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInBackground"
|
|||
PREF_LOAD_BOOKMARKS_IN_BACKGROUND, false);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInTabs",
|
||||
PREF_LOAD_BOOKMARKS_IN_TABS, false);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "openInTabClosesMenu",
|
||||
"browser.bookmarks.openInTabClosesMenu", false);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
|
||||
"@mozilla.org/docshell/urifixup;1",
|
||||
|
|
|
@ -94,6 +94,10 @@
|
|||
|
||||
<menupopup id="placesContext"
|
||||
onpopupshowing="this._view = PlacesUIUtils.getViewForNode(document.popupNode);
|
||||
if (!PlacesUIUtils.openInTabClosesMenu) {
|
||||
document.getElementById ('placesContext_open:newtab')
|
||||
.setAttribute('closemenu', 'single');
|
||||
}
|
||||
return this._view.buildContextMenu(this);"
|
||||
onpopuphiding="this._view.destroyContextMenu();">
|
||||
<menuitem id="placesContext_open"
|
||||
|
|
|
@ -57,6 +57,7 @@ subsuite = clipboard
|
|||
[browser_sidebarpanels_click.js]
|
||||
skip-if = true # temporarily disabled for breaking the treeview - bug 658744
|
||||
[browser_sort_in_library.js]
|
||||
[browser_stayopenmenu.js]
|
||||
[browser_toolbar_drop_text.js]
|
||||
[browser_toolbar_overflow.js]
|
||||
[browser_toolbarbutton_menu_context.js]
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Menus should stay open (if pref is set) after ctrl-click, middle-click,
|
||||
// and contextmenu's "Open in a new tab" click.
|
||||
|
||||
async function locateBookmarkAndTestCtrlClick(menupopup) {
|
||||
let menuitem = null;
|
||||
for (let node of menupopup.childNodes) {
|
||||
if (node.label == "Test1") {
|
||||
menuitem = node;
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem,
|
||||
AppConstants.platform === "macosx" ? {metaKey: true} : {ctrlKey: true});
|
||||
let newTab = await promiseTabOpened;
|
||||
ok(true, "Bookmark ctrl-click opened new tab.");
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return menuitem;
|
||||
}
|
||||
|
||||
async function testContextmenu(menuitem) {
|
||||
let doc = menuitem.ownerDocument;
|
||||
let cm = doc.getElementById("placesContext");
|
||||
let promiseEvent = BrowserTestUtils.waitForEvent(cm, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {type: "contextmenu", button: 2});
|
||||
await promiseEvent;
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
|
||||
BrowserTestUtils.waitForEvent(menuitem, "DOMMenuItemActive");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", {code: "ArrowDown"});
|
||||
BrowserTestUtils.waitForEvent(menuitem, "DOMMenuItemActive");
|
||||
EventUtils.sendKey("return");
|
||||
let newTab = await promiseTabOpened;
|
||||
return newTab;
|
||||
}
|
||||
|
||||
add_task(async function test_setup() {
|
||||
// Ensure BMB is available in UI.
|
||||
let origBMBlocation = CustomizableUI.getPlacementOfWidget("bookmarks-menu-button");
|
||||
if (!origBMBlocation) {
|
||||
CustomizableUI.addWidgetToArea("bookmarks-menu-button", CustomizableUI.AREA_NAVBAR);
|
||||
}
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [["browser.bookmarks.openInTabClosesMenu", false]]});
|
||||
// Ensure menubar visible.
|
||||
let menubar = document.getElementById("toolbar-menubar");
|
||||
let menubarVisible = isToolbarVisible(menubar);
|
||||
if (!menubarVisible) {
|
||||
setToolbarVisibility(menubar, true);
|
||||
info("Menubar made visible");
|
||||
}
|
||||
// Ensure Bookmarks Toolbar Visible.
|
||||
let toolbar = document.getElementById("PersonalToolbar");
|
||||
let toolbarHidden = toolbar.collapsed;
|
||||
if (toolbarHidden) {
|
||||
await promiseSetToolbarVisibility(toolbar, true);
|
||||
info("Bookmarks toolbar made visible");
|
||||
}
|
||||
// Create our test bookmarks.
|
||||
await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
url: "http://example.com/",
|
||||
title: "Test1"
|
||||
});
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
title: "TEST_TITLE",
|
||||
index: 0
|
||||
});
|
||||
await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: folder.guid,
|
||||
url: "http://example.com/",
|
||||
title: "Test1"
|
||||
});
|
||||
|
||||
registerCleanupFunction(async function() {
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
// if BMB was not originally in UI, remove it.
|
||||
if (!origBMBlocation) {
|
||||
CustomizableUI.removeWidgetFromArea("bookmarks-menu-button");
|
||||
}
|
||||
// Restore menubar to original visibility.
|
||||
setToolbarVisibility(menubar, menubarVisible);
|
||||
// Restore original bookmarks toolbar visibility.
|
||||
if (toolbarHidden) {
|
||||
await promiseSetToolbarVisibility(toolbar, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function testStayopenBookmarksClicks() {
|
||||
// Test Bookmarks Menu Button stayopen clicks - Ctrl-click.
|
||||
let BMB = document.getElementById("bookmarks-menu-button");
|
||||
let BMBpopup = document.getElementById("BMB_bookmarksPopup");
|
||||
let promiseEvent = BrowserTestUtils.waitForEvent(BMBpopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(BMB, {});
|
||||
await promiseEvent;
|
||||
info("Popupshown on Bookmarks-Menu-Button");
|
||||
var menuitem = await locateBookmarkAndTestCtrlClick(BMBpopup);
|
||||
ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
|
||||
|
||||
// Test Bookmarks Menu Button stayopen clicks: middle-click.
|
||||
let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
|
||||
let newTab = await promiseTabOpened;
|
||||
ok(true, "Bookmark middle-click opened new tab.");
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
|
||||
|
||||
// Test Bookmarks Menu Button stayopen clicks - 'Open in new tab' on context menu.
|
||||
newTab = await testContextmenu(menuitem);
|
||||
ok(true, "Bookmark contextmenu opened new tab.");
|
||||
ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(BMBpopup, "popuphidden");
|
||||
BMB.open = false;
|
||||
await promiseEvent;
|
||||
info("Closing menu");
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
// Disable the rest of the tests on Mac due to Mac's handling of menus being
|
||||
// slightly different to the other platforms.
|
||||
if (AppConstants.platform === "macosx") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Test Bookmarks Menu (menubar) stayopen clicks: Ctrl-click.
|
||||
let BM = document.getElementById("bookmarksMenu");
|
||||
let BMpopup = document.getElementById("bookmarksMenuPopup");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(BMpopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(BM, {});
|
||||
await promiseEvent;
|
||||
info("Popupshowing on Bookmarks Menu");
|
||||
menuitem = await locateBookmarkAndTestCtrlClick(BMpopup);
|
||||
ok(BM.open, "Bookmarks Menu's Popup should still be open.");
|
||||
|
||||
// Test Bookmarks Menu (menubar) stayopen clicks: middle-click.
|
||||
promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
|
||||
newTab = await promiseTabOpened;
|
||||
ok(true, "Bookmark middle-click opened new tab.");
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
ok(BM.open, "Bookmarks Menu's Popup should still be open.");
|
||||
|
||||
// Test Bookmarks Menu (menubar) stayopen clicks: 'Open in new tab' on context menu.
|
||||
newTab = await testContextmenu(menuitem);
|
||||
ok(true, "Bookmark contextmenu opened new tab.");
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
ok(BM.open, "Bookmarks Menu's Popup should still be open.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(BMpopup, "popuphidden");
|
||||
BM.open = false;
|
||||
await promiseEvent;
|
||||
|
||||
// Test Bookmarks Toolbar stayopen clicks - Ctrl-click.
|
||||
let BT = document.getElementById("PlacesToolbarItems");
|
||||
let toolbarbutton = BT.firstChild;
|
||||
ok(toolbarbutton, "Folder should be first item on Bookmarks Toolbar.");
|
||||
let buttonMenupopup = toolbarbutton.firstChild;
|
||||
ok(buttonMenupopup.tagName == "menupopup", "Found toolbar button's menupopup.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(toolbarbutton, {});
|
||||
await promiseEvent;
|
||||
ok(true, "Bookmarks toolbar folder's popup is open.");
|
||||
menuitem = buttonMenupopup.firstChild.nextSibling;
|
||||
promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {ctrlKey: true});
|
||||
newTab = await promiseTabOpened;
|
||||
ok(true, "Bookmark in folder on bookmark's toolbar ctrl-click opened new tab.");
|
||||
ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
|
||||
toolbarbutton.open = false;
|
||||
await promiseEvent;
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
// Test Bookmarks Toolbar stayopen clicks: middle-click.
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(toolbarbutton, {});
|
||||
await promiseEvent;
|
||||
ok(true, "Bookmarks toolbar folder's popup is open.");
|
||||
promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
|
||||
EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
|
||||
newTab = await promiseTabOpened;
|
||||
ok(true, "Bookmark in folder on Bookmarks Toolbar middle-click opened new tab.");
|
||||
ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
|
||||
toolbarbutton.open = false;
|
||||
await promiseEvent;
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
// Test Bookmarks Toolbar stayopen clicks: 'Open in new tab' on context menu.
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popupshown");
|
||||
EventUtils.synthesizeMouseAtCenter(toolbarbutton, {});
|
||||
await promiseEvent;
|
||||
ok(true, "Bookmarks toolbar folder's popup is open.");
|
||||
newTab = await testContextmenu(menuitem);
|
||||
ok(true, "Bookmark on Bookmarks Toolbar contextmenu opened new tab.");
|
||||
ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
|
||||
promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
|
||||
toolbarbutton.open = false;
|
||||
await promiseEvent;
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
});
|
|
@ -38,7 +38,7 @@
|
|||
accesskey="&searchFilter.accesskey;"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<label control="cookiesList" id="cookiesIntro">&cookiesonsystem.label;</label>
|
||||
<label control="cookiesList" id="cookiesIntro">&cookiesonsystem2.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="cookiesList" flex="1" style="height: 10em;"
|
||||
onkeypress="gCookiesWindow.onCookieKeyPress(event)"
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
name="browser.search.hiddenOneOffs"
|
||||
type="unichar"/>
|
||||
|
||||
<preference id="browser.search.widget.inNavBar"
|
||||
name="browser.search.widget.inNavBar"
|
||||
type="bool"/>
|
||||
|
||||
</preferences>
|
||||
|
||||
<script type="application/javascript"
|
||||
|
@ -26,6 +30,16 @@
|
|||
<label class="header-name" flex="1">&paneSearch.title;</label>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="searchbarGroup" data-category="paneSearch">
|
||||
<caption><label id="searchbarLabel">&searchBar.label;</label></caption>
|
||||
<radiogroup id="searchBarVisibleGroup" aria-labelledby="searchbarLabel" preference="browser.search.widget.inNavBar">
|
||||
<radio id="searchBarHiddenRadio" value="false" label="&searchBar.hidden.label;"/>
|
||||
<image class="searchBarImage searchBarHiddenImage" role="presentation"/>
|
||||
<radio id="searchBarShownRadio" value="true" label="&searchBar.shown.label;"/>
|
||||
<image class="searchBarImage searchBarShownImage" role="presentation"/>
|
||||
</radiogroup>
|
||||
</groupbox>
|
||||
|
||||
<!-- Default Search Engine -->
|
||||
<groupbox id="defaultEngineGroup" data-category="paneSearch">
|
||||
<caption><label>&defaultSearchEngine.label;</label></caption>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<separator />
|
||||
|
||||
<vbox flex="1">
|
||||
<label>&siteTree.label;</label>
|
||||
<label>&siteTree2.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" seltype="single" hidecolumnpicker="true">
|
||||
<treecols>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<vbox class="largeDialogContainer">
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages.label;</label>
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages2.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="languagesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
|
@ -57,7 +57,7 @@
|
|||
</hbox>
|
||||
<separator/>
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForSites.label;</label>
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForSites2.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
|
|
|
@ -3075,8 +3075,9 @@ var SessionStoreInternal = {
|
|||
else if (winData.hidden)
|
||||
delete winData.hidden;
|
||||
|
||||
var sidebar = aWindow.document.getElementById("sidebar-box").getAttribute("sidebarcommand");
|
||||
if (sidebar)
|
||||
let sidebarBox = aWindow.document.getElementById("sidebar-box");
|
||||
let sidebar = sidebarBox.getAttribute("sidebarcommand");
|
||||
if (sidebar && sidebarBox.getAttribute("checked") == "true")
|
||||
winData.sidebar = sidebar;
|
||||
else if (winData.sidebar)
|
||||
delete winData.sidebar;
|
||||
|
@ -4089,8 +4090,9 @@ var SessionStoreInternal = {
|
|||
break;
|
||||
}
|
||||
}
|
||||
var sidebar = aWindow.document.getElementById("sidebar-box");
|
||||
if (sidebar.getAttribute("sidebarcommand") != aSidebar) {
|
||||
let sidebarBox = aWindow.document.getElementById("sidebar-box");
|
||||
if (aSidebar && (sidebarBox.getAttribute("sidebarcommand") != aSidebar ||
|
||||
!sidebarBox.getAttribute("checked"))) {
|
||||
aWindow.SidebarUI.showInitially(aSidebar);
|
||||
}
|
||||
// since resizing/moving a window brings it to the foreground,
|
||||
|
|
|
@ -57,6 +57,7 @@ for (const type of [
|
|||
"SAVE_SESSION_PERF_DATA",
|
||||
"SAVE_TO_POCKET",
|
||||
"SCREENSHOT_UPDATED",
|
||||
"SEARCH_BOX_FOCUSED",
|
||||
"SECTION_DEREGISTER",
|
||||
"SECTION_DISABLE",
|
||||
"SECTION_ENABLE",
|
||||
|
|
|
@ -94,7 +94,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
|
|||
// UNINIT: "UNINIT"
|
||||
// }
|
||||
const actionTypes = {};
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "INIT", "LOCALE_UPDATED", "MIGRATION_CANCEL", "MIGRATION_COMPLETED", "MIGRATION_START", "NEW_TAB_INIT", "NEW_TAB_INITIAL_STATE", "NEW_TAB_LOAD", "NEW_TAB_REHYDRATED", "NEW_TAB_STATE_REQUEST", "NEW_TAB_UNLOAD", "OPEN_LINK", "OPEN_NEW_WINDOW", "OPEN_PRIVATE_WINDOW", "PINNED_SITES_UPDATED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINK_BLOCKED", "PLACES_LINK_DELETED", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SEARCH_BOX_FOCUSED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_DATA", "SNIPPETS_RESET", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_ADD", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
|
@ -1242,7 +1242,7 @@ const TopSites = props => {
|
|||
intl: props.intl })),
|
||||
placeholderCount > 0 && [...Array(placeholderCount)].map((_, i) => React.createElement(TopSitePlaceholder, { key: i }))
|
||||
),
|
||||
realTopSites.length > 0 && React.createElement(TopSitesEdit, props)
|
||||
React.createElement(TopSitesEdit, props)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -1948,7 +1948,7 @@ module.exports.CheckPinTopSite = (site, index) => site.isPinned ? module.exports
|
|||
const React = __webpack_require__(1);
|
||||
const { connect } = __webpack_require__(3);
|
||||
const { FormattedMessage, injectIntl } = __webpack_require__(2);
|
||||
const { actionCreators: ac } = __webpack_require__(0);
|
||||
const { actionCreators: ac, actionTypes: at } = __webpack_require__(0);
|
||||
const { IS_NEWTAB } = __webpack_require__(20);
|
||||
|
||||
class Search extends React.Component {
|
||||
|
@ -1991,6 +1991,14 @@ class Search extends React.Component {
|
|||
// In the future, when activity stream is default about:home, this can be renamed
|
||||
window.gContentSearchController = new ContentSearchUIController(input, input.parentNode, healthReportKey, searchSource);
|
||||
addEventListener("ContentSearchClient", this);
|
||||
|
||||
// Focus the search box if we are on about:home
|
||||
if (!IS_NEWTAB) {
|
||||
input.focus();
|
||||
// Tell the addon side that search box is focused in case the browser
|
||||
// needs to be focused too.
|
||||
this.props.dispatch(ac.SendToMain({ type: at.SEARCH_BOX_FOCUSED }));
|
||||
}
|
||||
} else {
|
||||
window.gContentSearchController = null;
|
||||
removeEventListener("ContentSearchClient", this);
|
||||
|
@ -2719,14 +2727,20 @@ class Card extends React.Component {
|
|||
link.description
|
||||
)
|
||||
),
|
||||
icon && React.createElement(
|
||||
React.createElement(
|
||||
"div",
|
||||
{ className: "card-context" },
|
||||
React.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
|
||||
React.createElement(
|
||||
icon && !link.context && React.createElement("span", { className: `card-context-icon icon icon-${icon}` }),
|
||||
link.icon && link.context && React.createElement("span", { className: "card-context-icon icon", style: { backgroundImage: `url('${link.icon}')` } }),
|
||||
intlID && !link.context && React.createElement(
|
||||
"div",
|
||||
{ className: "card-context-label" },
|
||||
React.createElement(FormattedMessage, { id: intlID, defaultMessage: "Visited" })
|
||||
),
|
||||
link.context && React.createElement(
|
||||
"div",
|
||||
{ className: "card-context-label" },
|
||||
link.context
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -3351,6 +3365,10 @@ class SnippetsProvider {
|
|||
throw new Error("No remote snippets were found in gSnippetsMap.");
|
||||
}
|
||||
|
||||
if (typeof payload !== "string") {
|
||||
throw new Error("Snippet payload was incorrectly formatted");
|
||||
}
|
||||
|
||||
// Note that injecting snippets can throw if they're invalid XML.
|
||||
snippetsEl.innerHTML = payload;
|
||||
|
||||
|
|
|
@ -969,14 +969,14 @@ main {
|
|||
transition: box-shadow 150ms; }
|
||||
.card-outer > a.active .card-title, .card-outer > a:focus .card-title {
|
||||
color: #0060DF; }
|
||||
.card-outer:hover, .card-outer:focus, .card-outer.active {
|
||||
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
.card-outer:hover .context-menu-button, .card-outer:focus .context-menu-button, .card-outer.active .context-menu-button {
|
||||
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .context-menu-button {
|
||||
transform: scale(1);
|
||||
opacity: 1; }
|
||||
.card-outer:hover .card-title, .card-outer:focus .card-title, .card-outer.active .card-title {
|
||||
.card-outer:-moz-any(:hover, :focus, .active):not(.placeholder) .card-title {
|
||||
color: #0060DF; }
|
||||
.card-outer .card-preview-image-outer {
|
||||
position: relative;
|
||||
|
|
|
@ -3727,6 +3727,36 @@
|
|||
"time_label_hour": "{number} ម៉ោង",
|
||||
"time_label_day": "{number} ថ្ងៃ"
|
||||
},
|
||||
"kn": {
|
||||
"newtab_page_title": "ಹೊಸ ಹಾಳೆ",
|
||||
"default_label_loading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ…",
|
||||
"header_top_sites": "ಪ್ರಮುಖ ತಾಣಗಳು",
|
||||
"header_stories": "ಪ್ರಮುಖ ಸುದ್ದಿಗಳು",
|
||||
"header_highlights": "ಮುಖ್ಯಾಂಶಗಳು",
|
||||
"header_visit_again": "ಮತ್ತೆ ಭೇಟಿಕೊಡು",
|
||||
"header_bookmarks": "ಇತ್ತೀಚಿಗೆ ಮಾಡಲಾದ ಬುಕ್ಮಾರ್ಕುಗಳು",
|
||||
"header_stories_from": "ಯಿಂದ",
|
||||
"type_label_visited": "ಭೇಟಿ ನೀಡಲಾದ",
|
||||
"type_label_bookmarked": "ಪುಟಗುರುತು ಮಾಡಲಾದ",
|
||||
"type_label_open": "ತೆರೆ",
|
||||
"type_label_topic": "ವಿಷಯ",
|
||||
"type_label_now": "ಈಗ",
|
||||
"menu_action_bookmark": "ಪುಟ ಗುರುತು",
|
||||
"menu_action_remove_bookmark": "ಪುಟ ಗುರುತು ತೆಗೆ",
|
||||
"menu_action_copy_address": "ವಿಳಾಸವನ್ನು ನಕಲಿಸು",
|
||||
"menu_action_email_link": "ಇಮೈಲ್ ಕೊಂಡಿ…",
|
||||
"menu_action_open_new_window": "ಹೊಸ ಕಿಟಕಿಯಲ್ಲಿ ತೆರೆ",
|
||||
"search_button": "ಹುಡುಕು",
|
||||
"search_settings": "ಹುಡುಕು ಸಿದ್ಧತೆಗಳನ್ನು ಬದಲಾಯಿಸು",
|
||||
"settings_pane_search_header": "ಹುಡುಕು",
|
||||
"settings_pane_topsites_options_showmore": "ಎರಡು ಸಾಲುಗಳನ್ನು ಪ್ರದರ್ಶಿಸು",
|
||||
"edit_topsites_button_text": "ತಿದ್ದು",
|
||||
"edit_topsites_showmore_button": "ಹೆಚ್ಚು ತೋರಿಸು",
|
||||
"edit_topsites_add_button": "ಸೇರಿಸು",
|
||||
"topsites_form_add_button": "ಸೇರಿಸು",
|
||||
"topsites_form_save_button": "ಉಳಿಸು",
|
||||
"topsites_form_cancel_button": "ರದ್ದು ಮಾಡು"
|
||||
},
|
||||
"ko": {
|
||||
"newtab_page_title": "새 탭",
|
||||
"default_label_loading": "읽는 중…",
|
||||
|
@ -4743,7 +4773,7 @@
|
|||
"type_label_visited": "Odwiedzone",
|
||||
"type_label_bookmarked": "Zakładka",
|
||||
"type_label_synced": "Z innego urządzenia",
|
||||
"type_label_recommended": "Polecane",
|
||||
"type_label_recommended": "Na czasie",
|
||||
"type_label_open": "Otwarte",
|
||||
"type_label_topic": "Temat",
|
||||
"type_label_now": "Teraz",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
<em:version>2017.09.12.1376-781e5de5</em:version>
|
||||
<em:version>2017.09.14.0590-7fa80d82</em:version>
|
||||
<em:name>Activity Stream</em:name>
|
||||
<em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
|
||||
<em:multiprocessCompatible>true</em:multiprocessCompatible>
|
||||
|
|
|
@ -62,9 +62,14 @@ const PREFS_CONFIG = new Map([
|
|||
stories_referrer: "https://getpocket.com/recommendations",
|
||||
info_link: "https://www.mozilla.org/privacy/firefox/#pocketstories",
|
||||
topics_endpoint: `https://getpocket.cdn.mozilla.net/v3/firefox/trending-topics?version=2&consumer_key=$apiKey&locale_lang=${args.locale}`,
|
||||
show_spocs: false,
|
||||
personalized: false
|
||||
})
|
||||
}],
|
||||
["filterAdult", {
|
||||
title: "Remove adult pages from sites, highlights, etc.",
|
||||
value: true
|
||||
}],
|
||||
["migrationExpired", {
|
||||
title: "Boolean flag that decides whether to show the migration message or not.",
|
||||
value: false
|
||||
|
@ -113,6 +118,10 @@ const PREFS_CONFIG = new Map([
|
|||
["telemetry.ping.endpoint", {
|
||||
title: "Telemetry server endpoint",
|
||||
value: "https://tiles.services.mozilla.com/v4/links/activity-stream"
|
||||
}],
|
||||
["aboutHome.autoFocus", {
|
||||
title: "Focus the about:home search box on load",
|
||||
value: true
|
||||
}]
|
||||
]);
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ const {SectionsManager} = Cu.import("resource://activity-stream/lib/SectionsMana
|
|||
const {TOP_SITES_SHOWMORE_LENGTH} = Cu.import("resource://activity-stream/common/Reducers.jsm", {});
|
||||
const {Dedupe} = Cu.import("resource://activity-stream/common/Dedupe.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "filterAdult",
|
||||
"resource://activity-stream/lib/FilterAdult.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshots",
|
||||
|
@ -41,6 +43,7 @@ this.HighlightsFeed = class HighlightsFeed {
|
|||
|
||||
postInit() {
|
||||
SectionsManager.enableSection(SECTION_ID);
|
||||
this.fetchHighlights(true);
|
||||
}
|
||||
|
||||
uninit() {
|
||||
|
@ -48,12 +51,28 @@ this.HighlightsFeed = class HighlightsFeed {
|
|||
}
|
||||
|
||||
async fetchHighlights(broadcast = false) {
|
||||
// We need TopSites to have been initialised for deduping
|
||||
if (!this.store.getState().TopSites.initialized) {
|
||||
await new Promise(resolve => {
|
||||
const unsubscribe = this.store.subscribe(() => {
|
||||
if (this.store.getState().TopSites.initialized) {
|
||||
unsubscribe();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Request more than the expected length to allow for items being removed by
|
||||
// deduping against Top Sites or multiple history from the same domain, etc.
|
||||
const manyPages = await NewTabUtils.activityStreamLinks.getHighlights({numItems: MANY_EXTRA_LENGTH});
|
||||
|
||||
// Remove adult highlights if we need to
|
||||
const checkedAdult = this.store.getState().Prefs.values.filterAdult ?
|
||||
filterAdult(manyPages) : manyPages;
|
||||
|
||||
// Remove any Highlights that are in Top Sites already
|
||||
const deduped = this.dedupe.group(this.store.getState().TopSites.rows, manyPages)[1];
|
||||
const [, deduped] = this.dedupe.group(this.store.getState().TopSites.rows, checkedAdult);
|
||||
|
||||
// Keep all "bookmark"s and at most one (most recent) "history" per host
|
||||
this.highlights = [];
|
||||
|
|
|
@ -37,6 +37,11 @@ this.NewTabInit = class NewTabInit {
|
|||
this._queue.clear();
|
||||
}
|
||||
break;
|
||||
case at.SEARCH_BOX_FOCUSED:
|
||||
if (action._target.url === "about:home" && this.store.getState().Prefs.values["aboutHome.autoFocus"]) {
|
||||
action._target.browser.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -201,9 +201,6 @@ this.TelemetryFeed = class TelemetryFeed {
|
|||
perf: {load_trigger_type: "unexpected"}
|
||||
};
|
||||
|
||||
if (url) {
|
||||
session.page = url;
|
||||
}
|
||||
this.sessions.set(id, session);
|
||||
return session;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ const {insertPinned, TOP_SITES_SHOWMORE_LENGTH} = Cu.import("resource://activity
|
|||
const {Dedupe} = Cu.import("resource://activity-stream/common/Dedupe.jsm", {});
|
||||
const {shortURL} = Cu.import("resource://activity-stream/lib/ShortURL.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "filterAdult",
|
||||
"resource://activity-stream/lib/FilterAdult.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Screenshots",
|
||||
|
@ -74,12 +76,17 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
});
|
||||
}
|
||||
|
||||
// Remove any duplicates from frecent and default sites then insert the
|
||||
// original pinned sites into the deduped frecent ([1]) and defaults ([2])
|
||||
const deduped = this.dedupe.group(pinned, frecent, notBlockedDefaultSites);
|
||||
pinned = insertPinned([...deduped[1], ...deduped[2]], pinned);
|
||||
// Remove any duplicates from frecent and default sites
|
||||
const [, dedupedFrecent, dedupedDefaults] = this.dedupe.group(
|
||||
pinned, frecent, notBlockedDefaultSites);
|
||||
const dedupedUnpinned = [...dedupedFrecent, ...dedupedDefaults];
|
||||
|
||||
return pinned.slice(0, TOP_SITES_SHOWMORE_LENGTH);
|
||||
// Remove adult sites if we need to
|
||||
const checkedAdult = this.store.getState().Prefs.values.filterAdult ?
|
||||
filterAdult(dedupedUnpinned) : dedupedUnpinned;
|
||||
|
||||
// Insert the original pinned sites into the deduped frecent and defaults
|
||||
return insertPinned(checkedAdult, pinned).slice(0, TOP_SITES_SHOWMORE_LENGTH);
|
||||
}
|
||||
async refresh(target = null) {
|
||||
if (!this._tippyTopProvider.initialized) {
|
||||
|
|
|
@ -9,14 +9,14 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/NewTabUtils.jsm");
|
||||
Cu.importGlobalProperties(["fetch"]);
|
||||
|
||||
const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
|
||||
const {actionTypes: at, actionCreators: ac} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
|
||||
const {Prefs} = Cu.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
|
||||
const {shortURL} = Cu.import("resource://activity-stream/lib/ShortURL.jsm", {});
|
||||
const {SectionsManager} = Cu.import("resource://activity-stream/lib/SectionsManager.jsm", {});
|
||||
|
||||
const {UserDomainAffinityProvider} = Cu.import("resource://activity-stream/lib/UserDomainAffinityProvider.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "perfService", "resource://activity-stream/common/PerfService.jsm");
|
||||
|
||||
const STORIES_UPDATE_TIME = 30 * 60 * 1000; // 30 minutes
|
||||
const TOPICS_UPDATE_TIME = 3 * 60 * 60 * 1000; // 3 hours
|
||||
const DOMAIN_AFFINITY_UPDATE_TIME = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
@ -24,32 +24,36 @@ const STORIES_NOW_THRESHOLD = 24 * 60 * 60 * 1000; // 24 hours
|
|||
const SECTION_ID = "topstories";
|
||||
|
||||
this.TopStoriesFeed = class TopStoriesFeed {
|
||||
|
||||
init() {
|
||||
constructor() {
|
||||
this.storiesLastUpdated = 0;
|
||||
this.topicsLastUpdated = 0;
|
||||
this.affinityLastUpdated = 0;
|
||||
|
||||
SectionsManager.onceInitialized(this.parseOptions.bind(this));
|
||||
this.spocsPerNewTabs = 0;
|
||||
this.newTabsSinceSpoc = 0;
|
||||
this.contentUpdateQueue = [];
|
||||
}
|
||||
|
||||
parseOptions() {
|
||||
SectionsManager.enableSection(SECTION_ID);
|
||||
const options = SectionsManager.sections.get(SECTION_ID).options;
|
||||
try {
|
||||
const apiKey = this._getApiKeyFromPref(options.api_key_pref);
|
||||
this.stories_endpoint = this._produceFinalEndpointUrl(options.stories_endpoint, apiKey);
|
||||
this.topics_endpoint = this._produceFinalEndpointUrl(options.topics_endpoint, apiKey);
|
||||
this.read_more_endpoint = options.read_more_endpoint;
|
||||
this.stories_referrer = options.stories_referrer;
|
||||
this.personalized = options.personalized;
|
||||
this.maxHistoryQueryResults = options.maxHistoryQueryResults;
|
||||
init() {
|
||||
const initFeed = () => {
|
||||
SectionsManager.enableSection(SECTION_ID);
|
||||
try {
|
||||
const options = SectionsManager.sections.get(SECTION_ID).options;
|
||||
const apiKey = this.getApiKeyFromPref(options.api_key_pref);
|
||||
this.stories_endpoint = this.produceFinalEndpointUrl(options.stories_endpoint, apiKey);
|
||||
this.topics_endpoint = this.produceFinalEndpointUrl(options.topics_endpoint, apiKey);
|
||||
this.read_more_endpoint = options.read_more_endpoint;
|
||||
this.stories_referrer = options.stories_referrer;
|
||||
this.personalized = options.personalized;
|
||||
this.show_spocs = options.show_spocs;
|
||||
this.maxHistoryQueryResults = options.maxHistoryQueryResults;
|
||||
|
||||
this.fetchStories();
|
||||
this.fetchTopics();
|
||||
} catch (e) {
|
||||
Cu.reportError(`Problem initializing top stories feed: ${e.message}`);
|
||||
}
|
||||
this.fetchStories();
|
||||
this.fetchTopics();
|
||||
} catch (e) {
|
||||
Cu.reportError(`Problem initializing top stories feed: ${e.message}`);
|
||||
}
|
||||
};
|
||||
SectionsManager.onceInitialized(initFeed);
|
||||
}
|
||||
|
||||
uninit() {
|
||||
|
@ -62,38 +66,48 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
}
|
||||
try {
|
||||
const response = await fetch(this.stories_endpoint);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Stories endpoint returned unexpected status: ${response.status}`);
|
||||
}
|
||||
|
||||
const body = await response.json();
|
||||
this.updateDomainAffinities(body.settings);
|
||||
this.updateSettings(body.settings);
|
||||
this.stories = this.rotate(this.transform(body.recommendations));
|
||||
this.spocs = this.show_spocs && this.transform(body.spocs).filter(s => s.score >= s.min_score);
|
||||
|
||||
const recommendations = body.recommendations
|
||||
.filter(s => !NewTabUtils.blockedLinks.isBlocked({"url": s.url}))
|
||||
.map(s => ({
|
||||
"guid": s.id,
|
||||
"hostname": shortURL(Object.assign({}, s, {url: s.url})),
|
||||
"type": (Date.now() - (s.published_timestamp * 1000)) <= STORIES_NOW_THRESHOLD ? "now" : "trending",
|
||||
"title": s.title,
|
||||
"description": s.excerpt,
|
||||
"image": this._normalizeUrl(s.image_src),
|
||||
"referrer": this.stories_referrer,
|
||||
"url": s.url,
|
||||
"score": this.personalized ? this.affinityProvider.calculateItemRelevanceScore(s) : 1
|
||||
}))
|
||||
.sort(this.personalized ? this.compareScore : (a, b) => 0);
|
||||
|
||||
const rows = this.rotate(recommendations);
|
||||
|
||||
this.dispatchUpdateEvent(this.storiesLastUpdated, {rows});
|
||||
this.dispatchUpdateEvent(this.storiesLastUpdated, {rows: this.stories});
|
||||
this.storiesLastUpdated = Date.now();
|
||||
// This is filtered so an update function can return true to retry on the next run
|
||||
this.contentUpdateQueue = this.contentUpdateQueue.filter(update => update());
|
||||
} catch (error) {
|
||||
Cu.reportError(`Failed to fetch content: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
transform(items) {
|
||||
if (!items) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return items
|
||||
.filter(s => !NewTabUtils.blockedLinks.isBlocked({"url": s.url}))
|
||||
.map(s => ({
|
||||
"guid": s.id,
|
||||
"hostname": shortURL(Object.assign({}, s, {url: s.url})),
|
||||
"type": (Date.now() - (s.published_timestamp * 1000)) <= STORIES_NOW_THRESHOLD ? "now" : "trending",
|
||||
"context": s.context,
|
||||
"icon": s.icon,
|
||||
"title": s.title,
|
||||
"description": s.excerpt,
|
||||
"image": this.normalizeUrl(s.image_src),
|
||||
"referrer": this.stories_referrer,
|
||||
"url": s.url,
|
||||
"min_score": s.min_score || 0,
|
||||
"score": this.personalized ? this.affinityProvider.calculateItemRelevanceScore(s) : 1
|
||||
}))
|
||||
.sort(this.personalized ? this.compareScore : (a, b) => 0);
|
||||
}
|
||||
|
||||
async fetchTopics() {
|
||||
if (!this.topics_endpoint) {
|
||||
return;
|
||||
|
@ -121,16 +135,24 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
return b.score - a.score;
|
||||
}
|
||||
|
||||
updateDomainAffinities(settings) {
|
||||
updateSettings(settings) {
|
||||
if (!this.personalized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.spocsPerNewTabs = settings.spocsPerNewTabs;
|
||||
|
||||
if (!this.affinityProvider || (Date.now() - this.affinityLastUpdated >= DOMAIN_AFFINITY_UPDATE_TIME)) {
|
||||
const start = perfService.absNow();
|
||||
this.affinityProvider = new UserDomainAffinityProvider(
|
||||
settings.timeSegments,
|
||||
settings.domainAffinityParameterSets,
|
||||
this.maxHistoryQueryResults);
|
||||
|
||||
this.store.dispatch(ac.PerfEvent({
|
||||
event: "topstories.domain.affinity.calculation.ms",
|
||||
value: Math.round(perfService.absNow() - start)
|
||||
}));
|
||||
this.affinityLastUpdated = Date.now();
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +175,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
return items;
|
||||
}
|
||||
|
||||
_getApiKeyFromPref(apiKeyPref) {
|
||||
getApiKeyFromPref(apiKeyPref) {
|
||||
if (!apiKeyPref) {
|
||||
return apiKeyPref;
|
||||
}
|
||||
|
@ -161,7 +183,7 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
return new Prefs().get(apiKeyPref) || Services.prefs.getCharPref(apiKeyPref);
|
||||
}
|
||||
|
||||
_produceFinalEndpointUrl(url, apiKey) {
|
||||
produceFinalEndpointUrl(url, apiKey) {
|
||||
if (!url) {
|
||||
return url;
|
||||
}
|
||||
|
@ -173,13 +195,49 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
|
||||
// Need to remove parenthesis from image URLs as React will otherwise
|
||||
// fail to render them properly as part of the card template.
|
||||
_normalizeUrl(url) {
|
||||
normalizeUrl(url) {
|
||||
if (url) {
|
||||
return url.replace(/\(/g, "%28").replace(/\)/g, "%29");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
maybeAddSpoc(target) {
|
||||
if (!this.show_spocs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.newTabsSinceSpoc === 0 || this.newTabsSinceSpoc === this.spocsPerNewTabs) {
|
||||
const updateContent = () => {
|
||||
if (!this.spocs || !this.spocs.length) {
|
||||
// We have stories but no spocs so there's nothing to do and this update can be
|
||||
// removed from the queue.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new array with a spoc inserted at index 2
|
||||
// For now we're using the top scored spoc until we can support viewability based rotation
|
||||
let rows = this.stories.slice(0, this.stories.length);
|
||||
rows.splice(2, 0, this.spocs[0]);
|
||||
|
||||
// Send a content update to the target tab
|
||||
const action = {type: at.SECTION_UPDATE, meta: {skipMain: true}, data: Object.assign({rows}, {id: SECTION_ID})};
|
||||
this.store.dispatch(ac.SendToContent(action, target));
|
||||
return false;
|
||||
};
|
||||
|
||||
if (this.stories) {
|
||||
updateContent();
|
||||
} else {
|
||||
// Delay updating tab content until initial data has been fetched
|
||||
this.contentUpdateQueue.push(updateContent);
|
||||
}
|
||||
|
||||
this.newTabsSinceSpoc = 0;
|
||||
}
|
||||
this.newTabsSinceSpoc++;
|
||||
}
|
||||
|
||||
onAction(action) {
|
||||
switch (action.type) {
|
||||
case at.INIT:
|
||||
|
@ -196,6 +254,9 @@ this.TopStoriesFeed = class TopStoriesFeed {
|
|||
case at.UNINIT:
|
||||
this.uninit();
|
||||
break;
|
||||
case at.NEW_TAB_REHYDRATED:
|
||||
this.maybeAddSpoc(action.meta.fromTarget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
describe("filterAdult", () => {
|
||||
let filterAdult;
|
||||
let hashStub;
|
||||
let hashValue;
|
||||
|
||||
beforeEach(() => {
|
||||
hashStub = {
|
||||
finish: sinon.stub().callsFake(() => hashValue),
|
||||
init: sinon.stub(),
|
||||
update: sinon.stub()
|
||||
};
|
||||
global.Components.classes["@mozilla.org/security/hash;1"] = {
|
||||
createInstance() {
|
||||
return hashStub;
|
||||
}
|
||||
};
|
||||
filterAdult = require("lib/FilterAdult.jsm").filterAdult;
|
||||
});
|
||||
|
||||
it("should default to include on unexpected urls", () => {
|
||||
const empty = {};
|
||||
|
||||
const result = filterAdult([empty]);
|
||||
|
||||
assert.equal(result.length, 1);
|
||||
assert.equal(result[0], empty);
|
||||
});
|
||||
it("should not filter out non-adult urls", () => {
|
||||
const link = {url: "https://mozilla.org/"};
|
||||
|
||||
const result = filterAdult([link]);
|
||||
|
||||
assert.equal(result.length, 1);
|
||||
assert.equal(result[0], link);
|
||||
});
|
||||
it("should filter out adult urls", () => {
|
||||
// Use a hash value that is in the adult set
|
||||
hashValue = "+/UCpAhZhz368iGioEO8aQ==";
|
||||
const link = {url: "https://some-adult-site/"};
|
||||
|
||||
const result = filterAdult([link]);
|
||||
|
||||
assert.equal(result.length, 0);
|
||||
});
|
||||
});
|
|
@ -18,6 +18,7 @@ describe("Highlights Feed", () => {
|
|||
let clock;
|
||||
let fakeScreenshot;
|
||||
let fakeNewTabUtils;
|
||||
let filterAdultStub;
|
||||
let sectionsManagerStub;
|
||||
let shortURLStub;
|
||||
|
||||
|
@ -34,16 +35,29 @@ describe("Highlights Feed", () => {
|
|||
sections: new Map([["highlights", {}]])
|
||||
};
|
||||
fakeScreenshot = {getScreenshotForURL: sandbox.spy(() => Promise.resolve(FAKE_IMAGE))};
|
||||
filterAdultStub = sinon.stub().returns([]);
|
||||
shortURLStub = sinon.stub().callsFake(site => site.url.match(/\/([^/]+)/)[1]);
|
||||
globals.set("NewTabUtils", fakeNewTabUtils);
|
||||
({HighlightsFeed, HIGHLIGHTS_UPDATE_TIME, SECTION_ID} = injector({
|
||||
"lib/FilterAdult.jsm": {filterAdult: filterAdultStub},
|
||||
"lib/ShortURL.jsm": {shortURL: shortURLStub},
|
||||
"lib/SectionsManager.jsm": {SectionsManager: sectionsManagerStub},
|
||||
"lib/Screenshots.jsm": {Screenshots: fakeScreenshot},
|
||||
"common/Dedupe.jsm": {Dedupe}
|
||||
}));
|
||||
feed = new HighlightsFeed();
|
||||
feed.store = {dispatch: sinon.spy(), getState() { return {TopSites: {rows: Array(12).fill(null).map((v, i) => ({url: `http://www.topsite${i}.com`}))}}; }};
|
||||
feed.store = {
|
||||
dispatch: sinon.spy(),
|
||||
getState() { return this.state; },
|
||||
state: {
|
||||
Prefs: {values: {filterAdult: false}},
|
||||
TopSites: {
|
||||
initialized: true,
|
||||
rows: Array(12).fill(null).map((v, i) => ({url: `http://www.topsite${i}.com`}))
|
||||
}
|
||||
},
|
||||
subscribe: sinon.stub().callsFake(cb => { cb(); return () => {}; })
|
||||
};
|
||||
links = FAKE_LINKS;
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
@ -65,15 +79,34 @@ describe("Highlights Feed", () => {
|
|||
assert.calledOnce(sectionsManagerStub.enableSection);
|
||||
assert.calledWith(sectionsManagerStub.enableSection, SECTION_ID);
|
||||
});
|
||||
it("should *not* fetch highlights on init to avoid loading Places too early", () => {
|
||||
it("should fetch highlights on postInit", () => {
|
||||
feed.fetchHighlights = sinon.spy();
|
||||
|
||||
feed.onAction({type: at.INIT});
|
||||
|
||||
assert.notCalled(feed.fetchHighlights);
|
||||
feed.postInit();
|
||||
assert.calledOnce(feed.fetchHighlights);
|
||||
});
|
||||
});
|
||||
describe("#fetchHighlights", () => {
|
||||
it("should wait for TopSites to be initialised", async () => {
|
||||
feed.store.getState = () => ({TopSites: {initialized: false}});
|
||||
// Initially TopSites is uninitialised and fetchHighlights should wait
|
||||
feed.fetchHighlights();
|
||||
assert.calledOnce(feed.store.subscribe);
|
||||
assert.notCalled(fakeNewTabUtils.activityStreamLinks.getHighlights);
|
||||
|
||||
// Initialisation causes the subscribe callback to be called and
|
||||
// fetchHighlights should continue
|
||||
feed.store.getState = () => ({TopSites: {initialized: true}});
|
||||
const subscribeCallback = feed.store.subscribe.firstCall.args[0];
|
||||
await subscribeCallback();
|
||||
assert.calledOnce(fakeNewTabUtils.activityStreamLinks.getHighlights);
|
||||
|
||||
// If TopSites is initialised in the first place it shouldn't wait
|
||||
feed.store.subscribe.reset();
|
||||
fakeNewTabUtils.activityStreamLinks.getHighlights.reset();
|
||||
feed.fetchHighlights();
|
||||
assert.notCalled(feed.store.subscribe);
|
||||
assert.calledOnce(fakeNewTabUtils.activityStreamLinks.getHighlights);
|
||||
});
|
||||
it("should add hostname and hasImage to each link", async () => {
|
||||
links = [{url: "https://mozilla.org"}];
|
||||
await feed.fetchHighlights();
|
||||
|
@ -141,6 +174,20 @@ describe("Highlights Feed", () => {
|
|||
await feed.fetchHighlights();
|
||||
assert.equal(feed.imageCache.size, 0);
|
||||
});
|
||||
it("should not filter out adult pages when pref is false", async() => {
|
||||
await feed.fetchHighlights();
|
||||
|
||||
assert.notCalled(filterAdultStub);
|
||||
});
|
||||
it("should filter out adult pages when pref is true", async() => {
|
||||
feed.store.state.Prefs.values.filterAdult = true;
|
||||
|
||||
await feed.fetchHighlights();
|
||||
|
||||
// The stub filters out everything
|
||||
assert.calledOnce(filterAdultStub);
|
||||
assert.equal(feed.highlights.length, 0);
|
||||
});
|
||||
});
|
||||
describe("#fetchImage", () => {
|
||||
const FAKE_URL = "https://mozilla.org";
|
||||
|
|
|
@ -59,4 +59,40 @@ describe("NewTabInit", () => {
|
|||
instance.onAction({type: at.LOCALE_UPDATED});
|
||||
assert.notCalled(store.dispatch);
|
||||
});
|
||||
it("should focus the content browser when SEARCH_BOX_FOCUSED", () => {
|
||||
STATE.Prefs = {values: {"aboutHome.autoFocus": true}};
|
||||
const action = {
|
||||
type: at.SEARCH_BOX_FOCUSED,
|
||||
_target: {
|
||||
url: "about:home",
|
||||
browser: {focus: sinon.spy()}
|
||||
}
|
||||
};
|
||||
instance.onAction(action);
|
||||
assert.calledOnce(action._target.browser.focus);
|
||||
});
|
||||
it("should NOT focus the content browser when SEARCH_BOX_FOCUSED for about:newtab", () => {
|
||||
STATE.Prefs = {values: {"aboutHome.autoFocus": true}};
|
||||
const action = {
|
||||
type: at.SEARCH_BOX_FOCUSED,
|
||||
_target: {
|
||||
url: "about:newtab",
|
||||
browser: {focus: sinon.spy()}
|
||||
}
|
||||
};
|
||||
instance.onAction(action);
|
||||
assert.notCalled(action._target.browser.focus);
|
||||
});
|
||||
it("should NOT focus the content browser when SEARCH_BOX_FOCUSED when autoFocus pref is off", () => {
|
||||
STATE.Prefs = {values: {"aboutHome.autoFocus": false}};
|
||||
const action = {
|
||||
type: at.SEARCH_BOX_FOCUSED,
|
||||
_target: {
|
||||
url: "about:newtab",
|
||||
browser: {focus: sinon.spy()}
|
||||
}
|
||||
};
|
||||
instance.onAction(action);
|
||||
assert.notCalled(action._target.browser.focus);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ describe("Top Sites Feed", () => {
|
|||
let clock;
|
||||
let fakeNewTabUtils;
|
||||
let fakeScreenshot;
|
||||
let filterAdultStub;
|
||||
let shortURLStub;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -48,6 +49,7 @@ describe("Top Sites Feed", () => {
|
|||
}
|
||||
};
|
||||
fakeScreenshot = {getScreenshotForURL: sandbox.spy(() => Promise.resolve(FAKE_SCREENSHOT))};
|
||||
filterAdultStub = sinon.stub().returns([]);
|
||||
shortURLStub = sinon.stub().callsFake(site => site.url);
|
||||
const fakeDedupe = function() {};
|
||||
globals.set("NewTabUtils", fakeNewTabUtils);
|
||||
|
@ -56,12 +58,20 @@ describe("Top Sites Feed", () => {
|
|||
"lib/ActivityStreamPrefs.jsm": {Prefs: FakePrefs},
|
||||
"common/Dedupe.jsm": {Dedupe: fakeDedupe},
|
||||
"common/Reducers.jsm": {insertPinned, TOP_SITES_SHOWMORE_LENGTH},
|
||||
"lib/FilterAdult.jsm": {filterAdult: filterAdultStub},
|
||||
"lib/Screenshots.jsm": {Screenshots: fakeScreenshot},
|
||||
"lib/TippyTopProvider.jsm": {TippyTopProvider: FakeTippyTopProvider},
|
||||
"lib/ShortURL.jsm": {shortURL: shortURLStub}
|
||||
}));
|
||||
feed = new TopSitesFeed();
|
||||
feed.store = {dispatch: sinon.spy(), getState() { return {TopSites: {rows: Array(12).fill("site")}}; }};
|
||||
feed.store = {
|
||||
dispatch: sinon.spy(),
|
||||
getState() { return this.state; },
|
||||
state: {
|
||||
Prefs: {values: {filterAdult: false}},
|
||||
TopSites: {rows: Array(12).fill("site")}
|
||||
}
|
||||
};
|
||||
feed.dedupe.group = (...sites) => sites;
|
||||
links = FAKE_LINKS;
|
||||
clock = sinon.useFakeTimers();
|
||||
|
@ -130,6 +140,22 @@ describe("Top Sites Feed", () => {
|
|||
assert.notEqual(result[1].url, links[1].url);
|
||||
assert.notEqual(result[1].url, links[2].url);
|
||||
});
|
||||
it("should not filter out adult sites when pref is false", async() => {
|
||||
await feed.getLinksWithDefaults();
|
||||
|
||||
assert.notCalled(filterAdultStub);
|
||||
});
|
||||
it("should filter out non-pinned adult sites when pref is true", async() => {
|
||||
feed.store.state.Prefs.values.filterAdult = true;
|
||||
fakeNewTabUtils.pinnedLinks.links = [{url: "https://foo.com/"}];
|
||||
|
||||
const result = await feed.getLinksWithDefaults();
|
||||
|
||||
// The stub filters out everything
|
||||
assert.calledOnce(filterAdultStub);
|
||||
assert.equal(result.length, 1);
|
||||
assert.equal(result[0].url, fakeNewTabUtils.pinnedLinks.links[0].url);
|
||||
});
|
||||
it("should filter out the defaults that have been blocked", async () => {
|
||||
// make sure we only have one top site, and we block the only default site we have to show
|
||||
const url = "www.myonlytopsite.com";
|
||||
|
@ -194,7 +220,7 @@ describe("Top Sites Feed", () => {
|
|||
"lib/Screenshots.jsm": {Screenshots: fakeScreenshot}
|
||||
}));
|
||||
sandbox.stub(global.Services.eTLD, "getPublicSuffix").returns("com");
|
||||
feed = new TopSitesFeed();
|
||||
feed = Object.assign(new TopSitesFeed(), {store: feed.store});
|
||||
});
|
||||
it("should not dedupe pinned sites", async () => {
|
||||
fakeNewTabUtils.pinnedLinks.links = [
|
||||
|
@ -263,8 +289,7 @@ describe("Top Sites Feed", () => {
|
|||
});
|
||||
it("should reuse screenshots for existing links, and call feed.getScreenshot for others", async () => {
|
||||
sandbox.stub(feed, "getScreenshot");
|
||||
const rows = [{url: FAKE_LINKS[0].url, screenshot: "foo.jpg"}];
|
||||
feed.store.getState = () => ({TopSites: {rows}});
|
||||
feed.store.state.TopSites.rows = [{url: FAKE_LINKS[0].url, screenshot: "foo.jpg"}];
|
||||
await feed.refresh(action);
|
||||
|
||||
const results = feed.store.dispatch.firstCall.args[0].data;
|
||||
|
|
|
@ -141,18 +141,23 @@ describe("Top Stories Feed", () => {
|
|||
"excerpt": "description",
|
||||
"image_src": "image-url",
|
||||
"url": "rec-url",
|
||||
"published_timestamp": "123"
|
||||
"published_timestamp": "123",
|
||||
"context": "trending",
|
||||
"icon": "icon"
|
||||
}]
|
||||
};
|
||||
const stories = [{
|
||||
"guid": "1",
|
||||
"type": "now",
|
||||
"title": "title",
|
||||
"context": "trending",
|
||||
"icon": "icon",
|
||||
"description": "description",
|
||||
"image": "image-url",
|
||||
"referrer": "referrer",
|
||||
"url": "rec-url",
|
||||
"hostname": "rec-url",
|
||||
"min_score": 0,
|
||||
"score": 1
|
||||
}];
|
||||
|
||||
|
@ -356,6 +361,108 @@ describe("Top Stories Feed", () => {
|
|||
const rotated = instance.rotate(items);
|
||||
assert.deepEqual(items, rotated);
|
||||
});
|
||||
it("should insert spoc at provided interval", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
|
||||
|
||||
const response = {
|
||||
"settings": {"spocsPerNewTabs": 2},
|
||||
"recommendations": [{"id": "rec1"}, {"id": "rec2"}, {"id": "rec3"}],
|
||||
"spocs": [{"id": "spoc1"}, {"id": "spoc2"}]
|
||||
};
|
||||
|
||||
instance.personalized = true;
|
||||
instance.show_spocs = true;
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
|
||||
await instance.fetchStories();
|
||||
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
let action = instance.store.dispatch.firstCall.args[0];
|
||||
assert.equal(at.SECTION_UPDATE, action.type);
|
||||
assert.equal(true, action.meta.skipMain);
|
||||
assert.equal(action.data.rows[0].guid, "rec1");
|
||||
assert.equal(action.data.rows[1].guid, "rec2");
|
||||
assert.equal(action.data.rows[2].guid, "spoc1");
|
||||
|
||||
// Second new tab shouldn't trigger a section update event (spocsPerNewTab === 2)
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.calledTwice(instance.store.dispatch);
|
||||
action = instance.store.dispatch.secondCall.args[0];
|
||||
assert.equal(at.SECTION_UPDATE, action.type);
|
||||
assert.equal(true, action.meta.skipMain);
|
||||
assert.equal(action.data.rows[0].guid, "rec1");
|
||||
assert.equal(action.data.rows[1].guid, "rec2");
|
||||
assert.equal(action.data.rows[2].guid, "spoc1");
|
||||
});
|
||||
it("should delay inserting spoc if stories haven't been fetched", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
|
||||
|
||||
const response = {
|
||||
"settings": {"spocsPerNewTabs": 2},
|
||||
"recommendations": [{"id": "rec1"}, {"id": "rec2"}, {"id": "rec3"}],
|
||||
"spocs": [{"id": "spoc1"}, {"id": "spoc2"}]
|
||||
};
|
||||
|
||||
instance.personalized = true;
|
||||
instance.show_spocs = true;
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
|
||||
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.notCalled(instance.store.dispatch);
|
||||
assert.equal(instance.contentUpdateQueue.length, 1);
|
||||
|
||||
await instance.fetchStories();
|
||||
assert.equal(instance.contentUpdateQueue.length, 0);
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
let action = instance.store.dispatch.firstCall.args[0];
|
||||
assert.equal(action.type, at.SECTION_UPDATE);
|
||||
});
|
||||
it("should not insert spoc if preffed off", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
|
||||
|
||||
const response = {
|
||||
"settings": {"spocsPerNewTabs": 2},
|
||||
"recommendations": [{"id": "rec1"}, {"id": "rec2"}, {"id": "rec3"}],
|
||||
"spocs": [{"id": "spoc1"}, {"id": "spoc2"}]
|
||||
};
|
||||
|
||||
instance.show_spocs = false;
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
|
||||
await instance.fetchStories();
|
||||
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.notCalled(instance.store.dispatch);
|
||||
});
|
||||
it("should not fail if there is no spoc", async () => {
|
||||
let fetchStub = globals.sandbox.stub();
|
||||
globals.set("fetch", fetchStub);
|
||||
globals.set("NewTabUtils", {blockedLinks: {isBlocked: globals.sandbox.spy()}});
|
||||
|
||||
const response = {
|
||||
"settings": {"spocsPerNewTabs": 2},
|
||||
"recommendations": [{"id": "rec1"}, {"id": "rec2"}, {"id": "rec3"}]
|
||||
};
|
||||
|
||||
instance.show_spocs = true;
|
||||
instance.stories_endpoint = "stories-endpoint";
|
||||
fetchStub.resolves({ok: true, status: 200, json: () => Promise.resolve(response)});
|
||||
await instance.fetchStories();
|
||||
|
||||
instance.onAction({type: at.NEW_TAB_REHYDRATED, meta: {fromTarget: {}}});
|
||||
assert.notCalled(instance.store.dispatch);
|
||||
});
|
||||
});
|
||||
describe("#update", () => {
|
||||
it("should fetch stories after update interval", () => {
|
||||
|
@ -384,12 +491,24 @@ describe("Top Stories Feed", () => {
|
|||
const fakeSettings = {timeSegments: {}, parameterSets: {}};
|
||||
instance.affinityProvider = {status: "not_changed"};
|
||||
|
||||
instance.updateDomainAffinities(fakeSettings);
|
||||
instance.updateSettings(fakeSettings);
|
||||
assert.equal("not_changed", instance.affinityProvider.status);
|
||||
|
||||
clock.tick(DOMAIN_AFFINITY_UPDATE_TIME);
|
||||
instance.updateDomainAffinities(fakeSettings);
|
||||
instance.updateSettings(fakeSettings);
|
||||
assert.isUndefined(instance.affinityProvider.status);
|
||||
});
|
||||
it("should send performance telemetry when updating domain affinities", () => {
|
||||
instance.init();
|
||||
instance.personalized = true;
|
||||
const fakeSettings = {timeSegments: {}, parameterSets: {}};
|
||||
|
||||
clock.tick(DOMAIN_AFFINITY_UPDATE_TIME);
|
||||
instance.updateSettings(fakeSettings);
|
||||
assert.calledOnce(instance.store.dispatch);
|
||||
let action = instance.store.dispatch.firstCall.args[0];
|
||||
assert.equal(action.type, at.TELEMETRY_PERFORMANCE_EVENT);
|
||||
assert.equal(action.data.event, "topstories.domain.affinity.calculation.ms");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -62,8 +62,11 @@ overrider.set({
|
|||
}
|
||||
},
|
||||
tm: {dispatchToMainThread: cb => cb()},
|
||||
eTLD: {getPublicSuffix() {}},
|
||||
io: {newURI() {}},
|
||||
eTLD: {
|
||||
getBaseDomain({spec}) { return spec.match(/\/([^/]+)/)[1]; },
|
||||
getPublicSuffix() {}
|
||||
},
|
||||
io: {newURI(url) { return {spec: url}; }},
|
||||
search: {
|
||||
init(cb) { cb(); },
|
||||
getVisibleEngines: () => [{identifier: "google"}, {identifier: "bing"}],
|
||||
|
|
|
@ -589,6 +589,12 @@ FormAutofillHandler.prototype = {
|
|||
delete data.creditCard;
|
||||
}
|
||||
|
||||
// If both address and credit card exists, skip this metrics because it not a
|
||||
// general case and each specific histogram might contains insufficient data set.
|
||||
if (data.address && data.creditCard) {
|
||||
this.timeStartedFillingMS = null;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
|
|
|
@ -415,7 +415,7 @@ FormAutofillParent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
async _onCreditCardSubmit(creditCard, target) {
|
||||
async _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) {
|
||||
// We'll show the credit card doorhanger if:
|
||||
// - User applys autofill and changed
|
||||
// - User fills form manually
|
||||
|
@ -423,13 +423,18 @@ FormAutofillParent.prototype = {
|
|||
Object.keys(creditCard.record).every(key => creditCard.untouchedFields.includes(key))) {
|
||||
// Add probe to record credit card autofill(without modification).
|
||||
Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill", 1);
|
||||
this._recordFormFillingTime("creditCard", "autofill", timeStartedFillingMS);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the probe to record credit card manual filling or autofill but modified case.
|
||||
let ccScalar = creditCard.guid ? "formautofill.creditCards.fill_type_autofill_modified" :
|
||||
"formautofill.creditCards.fill_type_manual";
|
||||
Services.telemetry.scalarAdd(ccScalar, 1);
|
||||
if (creditCard.guid) {
|
||||
Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill_modified", 1);
|
||||
this._recordFormFillingTime("creditCard", "autofill-update", timeStartedFillingMS);
|
||||
} else {
|
||||
Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_manual", 1);
|
||||
this._recordFormFillingTime("creditCard", "manual", timeStartedFillingMS);
|
||||
}
|
||||
|
||||
let state = await FormAutofillDoorhanger.show(target, "creditCard");
|
||||
if (state == "cancel") {
|
||||
|
@ -452,7 +457,7 @@ FormAutofillParent.prototype = {
|
|||
this._onAddressSubmit(address, target, timeStartedFillingMS);
|
||||
}
|
||||
if (creditCard) {
|
||||
this._onCreditCardSubmit(creditCard, target);
|
||||
this._onCreditCardSubmit(creditCard, target, timeStartedFillingMS);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -463,10 +468,13 @@ FormAutofillParent.prototype = {
|
|||
* 3 type of form (address/creditcard/address-creditcard).
|
||||
* @param {string} fillingType
|
||||
* 3 filling type (manual/autofill/autofill-update).
|
||||
* @param {int} startedFillingMS
|
||||
* Time that form started to filling in ms.
|
||||
* @param {int|null} startedFillingMS
|
||||
* Time that form started to filling in ms. Early return if start time is null.
|
||||
*/
|
||||
_recordFormFillingTime(formType, fillingType, startedFillingMS) {
|
||||
if (!startedFillingMS) {
|
||||
return;
|
||||
}
|
||||
let histogram = Services.telemetry.getKeyedHistogramById("FORM_FILLING_REQUIRED_TIME_MS");
|
||||
histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS);
|
||||
},
|
||||
|
|
|
@ -10,6 +10,7 @@ DIRS += [
|
|||
'clicktoplay-rollout',
|
||||
'e10srollout',
|
||||
'followonsearch',
|
||||
'formautofill',
|
||||
'onboarding',
|
||||
'pdfjs',
|
||||
'pocket',
|
||||
|
@ -25,12 +26,6 @@ if not CONFIG['RELEASE_OR_BETA']:
|
|||
'presentation',
|
||||
]
|
||||
|
||||
# formautofill will be rolled out via balrog in release
|
||||
if CONFIG['MOZ_UPDATE_CHANNEL'] != 'release':
|
||||
DIRS += [
|
||||
'formautofill',
|
||||
]
|
||||
|
||||
# Only include the following system add-ons if building DevEdition or Nightly
|
||||
if CONFIG['MOZ_DEV_EDITION'] or CONFIG['NIGHTLY_BUILD']:
|
||||
DIRS += [
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<!ENTITY window.width "36em">
|
||||
|
||||
<!ENTITY cookiesonsystem.label "The following cookies are stored on your computer:">
|
||||
<!ENTITY cookiesonsystem2.label "The following cookies are stored on your computer">
|
||||
<!ENTITY cookiename.label "Cookie Name">
|
||||
<!ENTITY cookiedomain.label "Site">
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
- 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/. -->
|
||||
|
||||
<!ENTITY searchBar.label "Search Bar">
|
||||
|
||||
<!ENTITY searchBar.hidden.label "Use the address bar for search and navigation">
|
||||
<!ENTITY searchBar.shown.label "Add search bar in toolbar">
|
||||
|
||||
<!ENTITY defaultSearchEngine.label "Default Search Engine">
|
||||
|
||||
<!ENTITY chooseYourDefaultSearchEngine2.label "Choose the default search engine to use in the address bar and search bar.">
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
<!ENTITY cancel.accesskey "C">
|
||||
<!ENTITY removingDialog.title "Removing Site Data">
|
||||
<!ENTITY removingSelected.description "Removing site data will also remove related cookies and offline web content. This may log you out of websites. Are you sure you want to make the changes?">
|
||||
<!ENTITY siteTree.label "The following website cookies will be removed:">
|
||||
<!ENTITY siteTree2.label "The following website cookies will be removed">
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<!ENTITY window.width "36em">
|
||||
<!ENTITY windowClose.key "w">
|
||||
|
||||
<!ENTITY noTranslationForLanguages.label "Translation will not be offered for the following languages:">
|
||||
<!ENTITY noTranslationForLanguages2.label "Translation will not be offered for the following languages">
|
||||
<!ENTITY treehead.languageName.label "Languages">
|
||||
<!ENTITY removeLanguage.label "Remove Language">
|
||||
<!ENTITY removeLanguage.accesskey "R">
|
||||
<!ENTITY removeAllLanguages.label "Remove All Languages">
|
||||
<!ENTITY removeAllLanguages.accesskey "e">
|
||||
|
||||
<!ENTITY noTranslationForSites.label "Translation will not be offered for the following sites:">
|
||||
<!ENTITY noTranslationForSites2.label "Translation will not be offered for the following sites">
|
||||
<!ENTITY treehead.siteName2.label "Websites">
|
||||
<!ENTITY removeSite.label "Remove Site">
|
||||
<!ENTITY removeSite.accesskey "S">
|
||||
|
|
|
@ -296,16 +296,6 @@ menuitem.bookmark-item {
|
|||
|
||||
/* identity box */
|
||||
|
||||
#identity-box:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 1.5px;
|
||||
border-bottom-left-radius: 1.5px;
|
||||
}
|
||||
|
||||
#identity-box:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 1.5px;
|
||||
border-bottom-right-radius: 1.5px;
|
||||
}
|
||||
|
||||
#identity-box:not(:active):-moz-focusring {
|
||||
outline: 1px dotted;
|
||||
outline-offset: -3px;
|
||||
|
|
|
@ -9,7 +9,7 @@ textbox,
|
|||
description,
|
||||
.tab-text,
|
||||
caption > label {
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.11rem;
|
||||
}
|
||||
|
||||
/* Create a separate rule to unset these styles on .tree-input instead of
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
%include ../../../shared/incontentprefs/preferences.inc.css
|
||||
|
||||
html *,
|
||||
page *,
|
||||
window * {
|
||||
font-size: 1.11rem;
|
||||
}
|
||||
|
||||
caption > label:not(.dialogTitle) {
|
||||
font-size: 1.27rem;
|
||||
}
|
||||
|
||||
.tip-caption,
|
||||
.help-button {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.treecol-sortdirection {
|
||||
/* override the Linux only toolkit rule */
|
||||
-moz-appearance: none;
|
||||
|
|
|
@ -358,16 +358,6 @@
|
|||
|
||||
%include ../shared/identity-block/identity-block.inc.css
|
||||
|
||||
#identity-box:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
|
||||
#identity-box:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
#identity-box:not(:active):-moz-focusring {
|
||||
box-shadow: var(--focus-ring-box-shadow);
|
||||
border-inline-end-style: none;
|
||||
|
|
|
@ -14,7 +14,7 @@ textbox,
|
|||
description,
|
||||
.tab-text,
|
||||
caption > label {
|
||||
font-size: 1.3rem;
|
||||
font-size: 1.36rem;
|
||||
}
|
||||
|
||||
/* Create a separate rule to unset these styles on .tree-input instead of
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
%include ../../../shared/incontentprefs/preferences.inc.css
|
||||
|
||||
html *,
|
||||
page *,
|
||||
window * {
|
||||
font-size: 1.36rem;
|
||||
}
|
||||
|
||||
caption > label:not(.dialogTitle) {
|
||||
font-size: 1.55rem;
|
||||
}
|
||||
|
||||
.tip-caption,
|
||||
.help-button {
|
||||
font-size: 1.18rem;
|
||||
}
|
||||
|
||||
.actionsMenu > .menulist-label-box > .menulist-icon {
|
||||
margin-top: 2px;
|
||||
margin-inline-start: 2px;
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
|
||||
%include ../../../../toolkit/themes/osx/global/shared.inc
|
||||
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||
@namespace html url("http://www.w3.org/1999/xhtml");
|
||||
|
||||
.windowDialog {
|
||||
padding: 12px;
|
||||
font: -moz-dialog;
|
||||
|
@ -17,9 +14,6 @@
|
|||
|
||||
/* ----- APPLICATIONS PREFPANE ----- */
|
||||
description {
|
||||
font: small-caption;
|
||||
font-weight: normal;
|
||||
line-height: 1.3em;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
overflow: -moz-hidden-unscrollable;
|
||||
max-height: 4em;
|
||||
transition: min-height 170ms ease-out, max-height 170ms ease-out;
|
||||
padding: 0 4px 1px;
|
||||
padding: 0 6px 1px;
|
||||
}
|
||||
|
||||
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar)[collapsed=true] {
|
||||
|
|
|
@ -1,29 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="24" height="24" viewBox="0 0 24 24">
|
||||
#include ../icon-colors.inc.svg
|
||||
<style>
|
||||
svg > rect:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<rect id="shape-lock-clasp-outer" x="5" y="1" width="14" height="20" rx="7" ry="7" />
|
||||
<rect id="shape-lock-clasp-inner" x="8" y="4" width="8" height="14" rx="4" ry="4" />
|
||||
<rect id="shape-lock-base" x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />
|
||||
<mask id="mask-clasp-cutout">
|
||||
<rect width="24" height="24" fill="#000" />
|
||||
<use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
|
||||
<use xlink:href="#shape-lock-clasp-inner" fill="#000" />
|
||||
</mask>
|
||||
<mask id="mask-lock">
|
||||
<use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" fill="#fff"/>
|
||||
<use xlink:href="#shape-lock-base" fill="#fff"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect id="connection-degraded" class="fieldtext" width="24" height="24" mask="url(#mask-lock)"/>
|
||||
<rect id="connection-secure" width="24" height="24" mask="url(#mask-lock)" fill="context-fill"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M18.75 9.977H18V7A6 6 0 0 0 6 7v2.977h-.75A2.25 2.25 0 0 0 3 12.227v7.523A2.25 2.25 0 0 0 5.25 22h13.5A2.25 2.25 0 0 0 21 19.75v-7.523a2.25 2.25 0 0 0-2.25-2.25zM9 7a3 3 0 0 1 6 0v2.977H9z"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.4 KiB После Ширина: | Высота: | Размер: 562 B |
|
@ -1,34 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="24" height="24" viewBox="0 0 24 24">
|
||||
#include ../icon-colors.inc.svg
|
||||
<defs>
|
||||
<rect id="shape-lock-clasp-outer" x="5" y="1" width="14" height="20" rx="7" ry="7" />
|
||||
<rect id="shape-lock-clasp-inner" x="8" y="4" width="8" height="14" rx="4" ry="4" />
|
||||
<rect id="shape-lock-base" x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />
|
||||
|
||||
<mask id="mask-clasp-cutout">
|
||||
<rect width="24" height="24" fill="#000" />
|
||||
<use xlink:href="#shape-lock-clasp-outer" fill="#fff" />
|
||||
<use xlink:href="#shape-lock-clasp-inner" fill="#000" />
|
||||
<line x1="3" y1="21" x2="21.5" y2="0.5" stroke="#000" stroke-width="3" />
|
||||
<line x1="3" y1="25" x2="21.5" y2="4.5" stroke="#000" stroke-width="3" />
|
||||
<rect x="3" y="10" width="18" height="13" rx="1.5" ry="1.5" />
|
||||
</mask>
|
||||
|
||||
<mask id="mask-base-cutout">
|
||||
<rect width="24" height="24" fill="#000" />
|
||||
<use xlink:href="#shape-lock-base" fill="#fff" />
|
||||
<line x1="2.25" y1="24.75" x2="21.5" y2="4.5" stroke="#000" stroke-width="3" />
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
<use class="fieldtext" xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)"/>
|
||||
<use class="fieldtext" xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)"/>
|
||||
|
||||
<line x1="2.25" y1="22.75" x2="21.5" y2="2.5" stroke="#d92d21" stroke-width="3" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="context-fill" fill-opacity="context-fill-opacity">
|
||||
<path d="M18.75 9.977h-.727L6 22h12.75A2.25 2.25 0 0 0 21 19.75v-7.523a2.25 2.25 0 0 0-2.25-2.25zm-9.75 0V7a3 3 0 0 1 6 0v1.5l2.838-2.838A5.994 5.994 0 0 0 6 7v2.977h-.75A2.25 2.25 0 0 0 3 12.227v7.523a2.224 2.224 0 0 0 .105.645L13.523 9.977z"/>
|
||||
<path d="M2.5 23a1.5 1.5 0 0 1-1.061-2.561l19-19A1.5 1.5 0 0 1 22.56 3.56l-19 19A1.5 1.5 0 0 1 2.5 23z" fill="#ff0039" fill-opacity="1"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.6 KiB После Ширина: | Высота: | Размер: 747 B |
|
@ -235,7 +235,7 @@
|
|||
}
|
||||
#identity-popup[connection^=secure] #identity-popup-securityView,
|
||||
#identity-popup[connection^=secure] #identity-popup-security-content {
|
||||
background-image: url(chrome://browser/skin/controlcenter/connection.svg#connection-secure);
|
||||
background-image: url(chrome://browser/skin/controlcenter/connection.svg);
|
||||
-moz-context-properties: fill;
|
||||
fill: #12BC00;
|
||||
}
|
||||
|
@ -244,7 +244,8 @@
|
|||
#identity-popup[ciphers=weak] #identity-popup-security-content,
|
||||
#identity-popup[mixedcontent~=passive-loaded][isbroken] #identity-popup-securityView,
|
||||
#identity-popup[mixedcontent~=passive-loaded][isbroken] #identity-popup-security-content {
|
||||
background-image: url(chrome://browser/skin/controlcenter/connection.svg#connection-degraded);
|
||||
background-image: url(chrome://browser/skin/controlcenter/connection.svg);
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
}
|
||||
|
||||
#identity-popup[connection=secure-cert-user-overridden] #identity-popup-securityView,
|
||||
|
@ -258,6 +259,7 @@
|
|||
#identity-popup[mixedcontent~=active-loaded][isbroken] #identity-popup-securityView,
|
||||
#identity-popup[mixedcontent~=active-loaded][isbroken] #identity-popup-security-content {
|
||||
background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
|
||||
-moz-context-properties: fill, fill-opacity;
|
||||
}
|
||||
|
||||
#identity-popup[connection=extension] #identity-popup-securityView,
|
||||
|
|
|
@ -63,11 +63,15 @@
|
|||
|
||||
.customizationmode-checkbox,
|
||||
.customizationmode-button {
|
||||
color: rgb(71, 71, 71);
|
||||
margin: 6px 10px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.customizationmode-checkbox:not(:-moz-lwtheme),
|
||||
.customizationmode-button {
|
||||
color: rgb(71, 71, 71);
|
||||
}
|
||||
|
||||
#customization-reset-button,
|
||||
#customization-undo-reset-button,
|
||||
#customization-done-button {
|
||||
|
|
|
@ -1,36 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16" viewBox="0 0 16 16">
|
||||
|
||||
<defs>
|
||||
<rect id="shape-lock-clasp-outer" x="4" y="2" width="8" height="10" rx="4" ry="4" />
|
||||
<rect id="shape-lock-clasp-inner" x="6" y="4" width="4" height="6" rx="2" ry="2" />
|
||||
<rect id="shape-lock-base" x="3" y="7" width="10" height="7" rx="1" ry="1" />
|
||||
|
||||
<mask id="mask-clasp-cutout">
|
||||
<use href="#shape-lock-clasp-outer" fill="#fff"/>
|
||||
<use href="#shape-lock-clasp-inner" fill="#000"/>
|
||||
<line x1="2" y1="13" x2="14" y2="1.5" stroke="#000" stroke-width="2" />
|
||||
<line x1="2" y1="15" x2="14" y2="3.5" stroke="#000" stroke-width="2" />
|
||||
<rect x="3" y="7" width="10" height="7" rx="1" ry="1" fill="#000" />
|
||||
</mask>
|
||||
|
||||
<mask id="mask-base-cutout">
|
||||
<use href="#shape-lock-base" fill="#fff"/>
|
||||
<line x1="2" y1="14.8" x2="14" y2="3.2" stroke="#000" stroke-width="1.8" />
|
||||
</mask>
|
||||
|
||||
<g id="lock">
|
||||
<use href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)"/>
|
||||
<use href="#shape-lock-base" mask="url(#mask-base-cutout)"/>
|
||||
</g>
|
||||
|
||||
<line id="strike-through-red" x1="2" y1="14.1" x2="14" y2="2.5" stroke="#d92d21" stroke-width="1.8"/>
|
||||
</defs>
|
||||
|
||||
<use fill="context-fill" fill-opacity="context-fill-opacity" href="#lock"/>
|
||||
<use href="#strike-through-red"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M12.5 6.984h-.484L4 15h8.5a1.5 1.5 0 0 0 1.5-1.5V8.484a1.5 1.5 0 0 0-1.5-1.5zm-6.5 0V5a2 2 0 0 1 4 0v1l1.892-1.892A4 4 0 0 0 4 5v1.984h-.5a1.5 1.5 0 0 0-1.5 1.5V13.5a1.483 1.483 0 0 0 .07.43l6.946-6.946z" fill="context-fill" fill-opacity="context-fill-opacity"/>
|
||||
<path d="M2 15a1 1 0 0 1-.707-1.707l12-12a1 1 0 0 1 1.414 1.414l-12 12A1 1 0 0 1 2 15z" fill="#ff0039"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.5 KiB После Ширина: | Высота: | Размер: 684 B |
|
@ -1,34 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16" viewBox="0 0 16 16">
|
||||
|
||||
<defs>
|
||||
<rect id="shape-lock-clasp-outer" x="2" y="1" width="8" height="10" rx="4" ry="4" />
|
||||
<rect id="shape-lock-clasp-inner" x="4" y="3" width="4" height="6" rx="2" ry="2" />
|
||||
<rect id="shape-lock-base" x="1" y="6" width="10" height="7" rx="1" ry="1" />
|
||||
|
||||
<mask id="mask-clasp-cutout">
|
||||
<rect width="16" height="16" fill="#000" />
|
||||
<use href="#shape-lock-clasp-outer" fill="#fff"/>
|
||||
<use href="#shape-lock-clasp-inner" fill="#000"/>
|
||||
</mask>
|
||||
|
||||
<mask id="mask-lock">
|
||||
<use href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" fill="#fff"/>
|
||||
<use href="#shape-lock-base" fill="#fff"/>
|
||||
</mask>
|
||||
|
||||
<g id="warning-triangle">
|
||||
<path fill="#fff" d="M10.5,5C9.8,5,9.1,5.4,8.8,6.2l-3.5,6.8c-0.4,0.7-0.4,1.4,0,2c0.4,0.6,1,1,1.8,1H14c0.8,0,1.4-0.4,1.8-1 c0.3-0.6,0.3-1.4,0-2l-3.5-6.8C11.9,5.4,11.2,5,10.5,5L10.5,5z"/>
|
||||
<path fill="#ffbf00" d="M14.8,13.4l-3.5-6.8C11.2,6.2,10.9,6,10.5,6c-0.3,0-0.7,0.2-0.9,0.6l-3.5,6.8c-0.2,0.4-0.2,0.8,0,1.1C6.3,14.8,6.6,15,7,15 H14c0.4,0,0.7-0.2,0.9-0.5C15.1,14.2,15,13.8,14.8,13.4z"/>
|
||||
<path fill="#fff" d="M10,8.5C10,8.2,10.2,8,10.5,8S11,8.2,11,8.5L10.8,11h-0.6L10,8.5z"/>
|
||||
<circle fill="#fff" cx="10.5" cy="12.5" r=".75"/>
|
||||
</g>
|
||||
</defs>
|
||||
|
||||
<rect fill="context-fill" fill-opacity="context-fill-opacity" width="16" height="16" mask="url(#mask-lock)"/>
|
||||
<use href="#warning-triangle"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path d="M8 1a4 4 0 0 0-4 4v1.984h-.5a1.5 1.5 0 0 0-1.5 1.5V13.5A1.5 1.5 0 0 0 3.5 15h2.535a2.274 2.274 0 0 1 .207-1.318L9.43 7.27a2.266 2.266 0 0 1 .2-.286H6V5a2 2 0 0 1 4 0v1.568A2.255 2.255 0 0 1 11.478 6a2.283 2.283 0 0 1 .522.073V5a4 4 0 0 0-4-4z" fill="context-fill" fill-opacity="context-fill-opacity"/>
|
||||
<path d="M15.818 14.127l-3.189-6.411a1.285 1.285 0 0 0-2.3 0l-3.192 6.411A1.294 1.294 0 0 0 8.289 16h6.377a1.294 1.294 0 0 0 1.152-1.873z" fill="#ffbf00"/>
|
||||
<path d="M11.478 8a.272.272 0 0 1 .256.161l3.188 6.412a.291.291 0 0 1-.013.291.275.275 0 0 1-.243.137H8.289a.275.275 0 0 1-.243-.137.29.29 0 0 1-.013-.291l3.188-6.412A.272.272 0 0 1 11.478 8m0-1a1.272 1.272 0 0 0-1.152.716l-3.189 6.411A1.294 1.294 0 0 0 8.289 16h6.377a1.294 1.294 0 0 0 1.152-1.873l-3.189-6.411A1.272 1.272 0 0 0 11.478 7z" fill="#d76e00" opacity=".35"/>
|
||||
<path d="M11.5 12a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-1 0v2a.5.5 0 0 0 .5.5zm0 .809a.691.691 0 1 0 .691.691.691.691 0 0 0-.691-.691z" fill="#fff"/>
|
||||
</svg>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.7 KiB После Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -17,13 +17,13 @@
|
|||
}
|
||||
|
||||
#identity-box:hover:not(.no-hover):not([open=true]) {
|
||||
background-color: hsla(240,5%,5%,.05);
|
||||
background-color: hsla(0,0%,70%,.2);
|
||||
fill-opacity: .8;
|
||||
}
|
||||
|
||||
#identity-box:hover:active:not(.no-hover),
|
||||
#identity-box[open=true] {
|
||||
background-color: hsla(240,5%,5%,.1);
|
||||
background-color: hsla(0,0%,70%,.3);
|
||||
fill-opacity: .8;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="631" height="36" viewBox="0 0 631 36"><style>.addressBarOutline{stroke:#b1b1b3;stroke-linejoin:round;stroke-width:1px}.addressBarIcons{fill:#b1b1b3;fill-rule:evenodd}</style><rect class="addressBarOutline" fill="#fff" x=".5" y=".5" width="630" height="35" rx="4" ry="4"/><rect class="addressBarOutline" fill="none" x="110.5" y="6.5" width="434" height="23" rx="4" ry="4"/><path class="addressBarIcons" d="M604 .5h.5v34h-.5V.5zM126.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574zM620.75 17.25h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0 3.75h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0-7.5h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5M585.77 13.5a.75.75 0 0 0-.52 1.28l3.18 3.22-3.18 3.22a.746.746 0 1 0 1.05 1.06l3.7-3.75a.774.774 0 0 0 0-1.06l-3.7-3.75a.754.754 0 0 0-.53-.22m4.44 0a.715.715 0 0 0-.52.22.754.754 0 0 0 0 1.06l3.17 3.22-3.17 3.22a.754.754 0 0 0 0 1.06.715.715 0 0 0 .52.22.754.754 0 0 0 .53-.22l3.69-3.75a.754.754 0 0 0 0-1.06l-3.69-3.75a.754.754 0 0 0-.53-.22M567.37 15.75h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm2.63-3h-9a1.5 1.5 0 0 0-1.5 1.5v7.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5v-7.5a1.5 1.5 0 0 0-1.5-1.5zm-4.5 9H561v-7.5h4.5v7.5zm4.5 0h-3.75v-7.5H570v7.5zm-2.63-4.5h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm0 1.5h.75a.375.375 0 1 0 0-.75h-.75a.375.375 0 0 0 0 .75zM89.83 21.25a.375.375 0 1 1 .37-.375.356.356 0 0 1-.37.375m-2.6 1.5a.7.7 0 0 1-.742-.75v-4.95l2.961-3 2.97 3V22a.706.706 0 0 1-.74.75h-.74V19a.706.706 0 0 0-.74-.75h-1.49a.706.706 0 0 0-.74.75v3.75h-.739m2.219-10.5a.7.7 0 0 0-.51.225l-5.193 5.25a.738.738 0 1 0 1.037 1.05l.223-.225v4.2a1.5 1.5 0 0 0 1.482 1.5h5.931a1.491 1.491 0 0 0 1.48-1.5v-4.2l.22.225a.678.678 0 0 0 .52.225.663.663 0 0 0 .52-.225.725.725 0 0 0 0-1.05l-5.19-5.25a.709.709 0 0 0-.52-.225M70.375 13a.749.749 0 0 0-.75.75v1.808a5.245 5.245 0 1 0-.788 6.4.75.75 0 0 0-1.061-1.061 3.755 3.755 0 1 1 .776-4.151h-1.927a.75.75 0 0 0 0 1.5h3.75a.749.749 0 0 0 .75-.75v-3.75a.749.749 0 0 0-.75-.75M36.217 17.292h8.649l-3.206-3.2a.738.738 0 0 1 1.044-1.043l4.474 4.47a.72.72 0 0 1 0 1.043l-4.474 4.47a.72.72 0 0 1-1.044 0 .72.72 0 0 1 0-1.043l3.206-3.2h-8.649a.749.749 0 1 1 0-1.497z"/><circle class="addressBarOutline" fill="#f9f9fa" cx="18.5" cy="18" r="12"/><path class="addressBarIcons" d="M23.783 17.292h-8.649l3.206-3.2a.738.738 0 0 0-1.044-1.043l-4.474 4.47a.72.72 0 0 0 0 1.043l4.474 4.47a.72.72 0 0 0 1.044 0 .72.72 0 0 0 0-1.043l-3.206-3.2h8.649a.749.749 0 1 0 0-1.497z"/></svg>
|
После Ширина: | Высота: | Размер: 2.8 KiB |
|
@ -0,0 +1,4 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="631" height="36" viewBox="0 0 631 36"><style>.addressBarOutline{stroke:#b1b1b3;stroke-linejoin:round;stroke-width:1px}.addressBarIcons{fill:#b1b1b3}.addressBarIcons,.searchBarIcons{fill-rule:evenodd}.searchBarFill{fill:#0a84ff}.searchBarOutline{fill-opacity:.2;stroke:#0a84ff}</style><rect class="addressBarOutline" fill="#fff" x=".5" y=".5" width="630" height="35" rx="4" ry="4"/><rect class="addressBarOutline" fill="none" x="110.5" y="6.5" width="314" height="23" rx="4" ry="4"/><path class="addressBarIcons" d="M604 .5h.5v34h-.5V.5zM126.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574z"/><rect class="addressBarOutline searchBarFill searchBarOutline" x="429.5" y="6.5" width="115" height="23" rx="4" ry="4"/><path class="searchBarFill searchBarIcons" d="M445.41 22l-3.09-3.085a3.881 3.881 0 0 0-.64-5.2 3.855 3.855 0 0 0-5.4 5.462 3.958 3.958 0 0 0 5.21.643l3.08 3.085a.622.622 0 0 0 .9 0 .677.677 0 0 0-.06-.9zm-6.23-2.764a2.571 2.571 0 1 1 2.57-2.57 2.652 2.652 0 0 1-2.57 2.574z"/><path class="addressBarIcons" d="M620.75 17.25h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0 3.75h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5m0-7.5h-7.5a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5M585.77 13.5a.75.75 0 0 0-.52 1.28l3.18 3.22-3.18 3.22a.746.746 0 1 0 1.05 1.06l3.7-3.75a.774.774 0 0 0 0-1.06l-3.7-3.75a.754.754 0 0 0-.53-.22m4.44 0a.715.715 0 0 0-.52.22.754.754 0 0 0 0 1.06l3.17 3.22-3.17 3.22a.754.754 0 0 0 0 1.06.715.715 0 0 0 .52.22.754.754 0 0 0 .53-.22l3.69-3.75a.754.754 0 0 0 0-1.06l-3.69-3.75a.754.754 0 0 0-.53-.22M567.37 15.75h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm2.63-3h-9a1.5 1.5 0 0 0-1.5 1.5v7.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5v-7.5a1.5 1.5 0 0 0-1.5-1.5zm-4.5 9H561v-7.5h4.5v7.5zm4.5 0h-3.75v-7.5H570v7.5zm-2.63-4.5h1.5a.375.375 0 1 0 0-.75h-1.5a.375.375 0 0 0 0 .75zm0 1.5h.75a.375.375 0 1 0 0-.75h-.75a.375.375 0 0 0 0 .75zM89.83 21.25a.375.375 0 1 1 .37-.375.356.356 0 0 1-.37.375m-2.6 1.5a.7.7 0 0 1-.742-.75v-4.95l2.961-3 2.97 3V22a.706.706 0 0 1-.74.75h-.74V19a.706.706 0 0 0-.74-.75h-1.49a.706.706 0 0 0-.74.75v3.75h-.739m2.219-10.5a.7.7 0 0 0-.51.225l-5.193 5.25a.738.738 0 1 0 1.037 1.05l.223-.225v4.2a1.5 1.5 0 0 0 1.482 1.5h5.931a1.491 1.491 0 0 0 1.48-1.5v-4.2l.22.225a.678.678 0 0 0 .52.225.663.663 0 0 0 .52-.225.725.725 0 0 0 0-1.05l-5.19-5.25a.709.709 0 0 0-.52-.225M70.375 13a.749.749 0 0 0-.75.75v1.808a5.245 5.245 0 1 0-.788 6.4.75.75 0 0 0-1.061-1.061 3.755 3.755 0 1 1 .776-4.151h-1.927a.75.75 0 0 0 0 1.5h3.75a.749.749 0 0 0 .75-.75v-3.75a.749.749 0 0 0-.75-.75M36.217 17.292h8.649l-3.206-3.2a.738.738 0 0 1 1.044-1.043l4.474 4.47a.72.72 0 0 1 0 1.043l-4.474 4.47a.72.72 0 0 1-1.044 0 .72.72 0 0 1 0-1.043l3.206-3.2h-8.649a.749.749 0 1 1 0-1.497z"/><circle class="addressBarOutline" fill="#f9f9fa" cx="18.5" cy="18" r="12"/><path class="addressBarIcons" d="M23.783 17.292h-8.649l3.206-3.2a.738.738 0 0 0-1.044-1.043l-4.474 4.47a.72.72 0 0 0 0 1.043l4.474 4.47a.72.72 0 0 0 1.044 0 .72.72 0 0 0 0-1.043l-3.206-3.2h8.649a.749.749 0 1 0 0-1.497z"/></svg>
|
После Ширина: | Высота: | Размер: 3.4 KiB |
|
@ -2,9 +2,28 @@
|
|||
* 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/. */
|
||||
|
||||
#defaultEngine {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
.searchBarImage {
|
||||
height: 36px;
|
||||
width: 631px;
|
||||
margin-left: 33px;
|
||||
}
|
||||
|
||||
.searchBarHiddenImage {
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/no-search-bar.svg");
|
||||
}
|
||||
|
||||
#searchBarShownRadio {
|
||||
/* Allow a little visual space to separate the radio from the image above it. */
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.searchBarShownImage {
|
||||
list-style-image: url("chrome://browser/skin/preferences/in-content/search-bar.svg");
|
||||
}
|
||||
|
||||
#defaultEngine {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
#defaultEngine > .menulist-label-box > .menulist-icon {
|
||||
height: 16px;
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
skin/classic/browser/addons/addon-install-restart.svg (../shared/addons/addon-install-restart.svg)
|
||||
skin/classic/browser/addons/addon-install-warning.svg (../shared/addons/addon-install-warning.svg)
|
||||
* skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg)
|
||||
* skin/classic/browser/controlcenter/connection.svg (../shared/controlcenter/connection.svg)
|
||||
* skin/classic/browser/controlcenter/mcb-disabled.svg (../shared/controlcenter/mcb-disabled.svg)
|
||||
skin/classic/browser/controlcenter/connection.svg (../shared/controlcenter/connection.svg)
|
||||
skin/classic/browser/controlcenter/mcb-disabled.svg (../shared/controlcenter/mcb-disabled.svg)
|
||||
skin/classic/browser/controlcenter/extension.svg (../shared/controlcenter/extension.svg)
|
||||
* skin/classic/browser/controlcenter/permissions.svg (../shared/controlcenter/permissions.svg)
|
||||
* skin/classic/browser/controlcenter/tracking-protection.svg (../shared/controlcenter/tracking-protection.svg)
|
||||
|
@ -97,10 +97,12 @@
|
|||
skin/classic/browser/preferences/in-content/general.svg (../shared/incontentprefs/general.svg)
|
||||
skin/classic/browser/preferences/in-content/logo-android.svg (../shared/incontentprefs/logo-android.svg)
|
||||
skin/classic/browser/preferences/in-content/logo-ios.svg (../shared/incontentprefs/logo-ios.svg)
|
||||
skin/classic/browser/preferences/in-content/no-search-bar.svg (../shared/incontentprefs/no-search-bar.svg)
|
||||
skin/classic/browser/preferences/in-content/no-search-results.svg (../shared/incontentprefs/no-search-results.svg)
|
||||
skin/classic/browser/preferences/in-content/privacy-security.svg (../shared/incontentprefs/privacy-security.svg)
|
||||
skin/classic/browser/preferences/in-content/privacy.css (../shared/incontentprefs/privacy.css)
|
||||
skin/classic/browser/preferences/in-content/search-arrow-indicator.svg (../shared/incontentprefs/search-arrow-indicator.svg)
|
||||
skin/classic/browser/preferences/in-content/search-bar.svg (../shared/incontentprefs/search-bar.svg)
|
||||
skin/classic/browser/preferences/in-content/search.css (../shared/incontentprefs/search.css)
|
||||
skin/classic/browser/preferences/in-content/search.svg (../shared/incontentprefs/search.svg)
|
||||
skin/classic/browser/preferences/in-content/siteDataSettings.css (../shared/incontentprefs/siteDataSettings.css)
|
||||
|
|
|
@ -162,6 +162,10 @@
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tabbrowser-tab:not([visuallyselected=true]) {
|
||||
--tab-loading-fill: #fff;
|
||||
}
|
||||
|
||||
.tab-sharing-icon-overlay,
|
||||
.tab-icon-image {
|
||||
height: 16px;
|
||||
|
|
|
@ -124,8 +124,6 @@ toolbar .toolbarbutton-1 > .toolbarbutton-text,
|
|||
toolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
|
||||
padding: var(--toolbarbutton-inner-padding);
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: var(--toolbarbutton-hover-transition-duration);
|
||||
}
|
||||
|
||||
toolbar .toolbarbutton-1 > .toolbarbutton-icon {
|
||||
|
@ -228,6 +226,8 @@ toolbar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
|
|||
border-radius: 10000px;
|
||||
width: 32px;
|
||||
padding: 7px;
|
||||
transition-property: box-shadow;
|
||||
transition-duration: var(--toolbarbutton-hover-transition-duration);
|
||||
}
|
||||
|
||||
:root[uidensity=touch] #back-button {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
padding: 0;
|
||||
margin: 0 5px;
|
||||
min-height: 30px;
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
}
|
||||
|
||||
#urlbar:hover,
|
||||
|
@ -180,12 +181,6 @@
|
|||
padding: 7px;
|
||||
}
|
||||
|
||||
.urlbar-icon,
|
||||
.urlbar-icon-wrapper {
|
||||
transition-property: background-color;
|
||||
transition-duration: var(--toolbarbutton-hover-transition-duration);
|
||||
}
|
||||
|
||||
.urlbar-icon:hover,
|
||||
.urlbar-icon-wrapper:hover {
|
||||
background-color: hsla(0,0%,80%,.4);
|
||||
|
@ -194,7 +189,6 @@
|
|||
.urlbar-icon:hover:active,
|
||||
.urlbar-icon-wrapper:hover:active {
|
||||
background-color: hsla(0,0%,80%,.45);
|
||||
transition-duration: var(--toolbarbutton-active-transition-duration);
|
||||
}
|
||||
|
||||
.urlbar-icon-wrapper > .urlbar-icon:hover,
|
||||
|
|
|
@ -593,8 +593,6 @@ html|*.urlbar-input:-moz-lwtheme::placeholder,
|
|||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
/* page proxy icon */
|
||||
|
||||
%include ../shared/identity-block/identity-block.inc.css
|
||||
|
||||
/* autocomplete */
|
||||
|
|
|
@ -9,7 +9,7 @@ textbox,
|
|||
description,
|
||||
.tab-text,
|
||||
caption > label {
|
||||
font-size: 1.2rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Create a separate rule to unset these styles on .tree-input instead of
|
||||
|
|
|
@ -4,6 +4,21 @@
|
|||
|
||||
%include ../../../shared/incontentprefs/preferences.inc.css
|
||||
|
||||
html *,
|
||||
page *,
|
||||
window * {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
caption > label:not(.dialogTitle) {
|
||||
font-size: 1.42rem;
|
||||
}
|
||||
|
||||
.tip-caption,
|
||||
.help-button {
|
||||
font-size: 1.08rem;
|
||||
}
|
||||
|
||||
.actionsMenu > .menulist-label-box > .menulist-icon {
|
||||
margin-inline-end: 9px;
|
||||
}
|
||||
|
|
|
@ -827,13 +827,6 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
|
|||
# define SQLITE_TCLAPI
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Make sure that rand_s() is available on Windows systems with MSVC 2005
|
||||
** or higher.
|
||||
*/
|
||||
#if defined(_MSC_VER) && _MSC_VER>=1400
|
||||
# define _CRT_RAND_S
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Include the header file used to customize the compiler options for MSVC.
|
||||
|
@ -43927,9 +43920,6 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
|||
EntropyGatherer e;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
memset(zBuf, 0, nBuf);
|
||||
#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
|
||||
rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
|
||||
#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
|
||||
e.a = (unsigned char*)zBuf;
|
||||
e.na = nBuf;
|
||||
e.nXor = 0;
|
||||
|
|
|
@ -43,39 +43,39 @@ module.exports = envConfig => {
|
|||
{
|
||||
test: /event-emitter/,
|
||||
exclude: /node_modules/,
|
||||
loaders: [path.join(__dirname, "./webpack/rewrite-event-emitter")],
|
||||
loaders: ["rewrite-event-emitter"],
|
||||
}, {
|
||||
test: /client(\/|\\)inspector(\/|\\).*\.js$/,
|
||||
loaders: [
|
||||
// Replace all references to this.browserRequire() by require()
|
||||
path.join(__dirname, "./webpack/rewrite-browser-require"),
|
||||
"rewrite-browser-require",
|
||||
// Replace all references to loader.lazyRequire() by require()
|
||||
path.join(__dirname, "./webpack/rewrite-lazy-require"),
|
||||
"rewrite-lazy-require",
|
||||
],
|
||||
}, {
|
||||
test: /shared(\/|\\)inspector(\/|\\)css-logic\.js$/,
|
||||
loaders: [
|
||||
// Replace a very specific lazy importer, which should really be moved to
|
||||
// /server ...
|
||||
path.join(__dirname, "./webpack/rewrite-css-logic-importer"),
|
||||
"rewrite-css-logic-importer",
|
||||
],
|
||||
}, {
|
||||
test: /react-redux\.js$/,
|
||||
loaders: [
|
||||
// Replace dynamic paths in react-redux file
|
||||
path.join(__dirname, "./webpack/rewrite-react-redux"),
|
||||
"rewrite-react-redux",
|
||||
],
|
||||
}, {
|
||||
// Replace all references sdk's lazyRequire by require()
|
||||
test: /sdk(\/|\\).*\.js$/,
|
||||
loaders: [path.join(__dirname, "./webpack/rewrite-sdk-lazy-require")],
|
||||
loaders: ["rewrite-sdk-lazy-require"],
|
||||
}
|
||||
]
|
||||
},
|
||||
resolveLoader: {
|
||||
root: [
|
||||
path.resolve("./node_modules"),
|
||||
path.resolve("./webpack"),
|
||||
path.resolve("../shared/webpack"),
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
|
|
|
@ -17,6 +17,7 @@ const EventEmitter = require("devtools-modules/src/utils/event-emitter");
|
|||
const { Services: { appinfo, pref }} = require("devtools-modules");
|
||||
|
||||
// Initialize preferences as early as possible
|
||||
pref("devtools.theme", "light");
|
||||
pref("devtools.cache.disabled", false);
|
||||
pref("devtools.netmonitor.enabled", true);
|
||||
pref("devtools.netmonitor.filters", "[\"all\"]");
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
"codemirror": "^5.24.2",
|
||||
"devtools-config": "=0.0.12",
|
||||
"devtools-contextmenu": "=0.0.3",
|
||||
"devtools-launchpad": "=0.0.88",
|
||||
"devtools-modules": "=0.0.27",
|
||||
"devtools-launchpad": "=0.0.96",
|
||||
"devtools-modules": "=0.0.31",
|
||||
"devtools-source-editor": "=0.0.3",
|
||||
"immutable": "^3.8.1",
|
||||
"jszip": "^3.1.3",
|
||||
|
|
|
@ -18,11 +18,39 @@ let webpackConfig = {
|
|||
},
|
||||
|
||||
module: {
|
||||
loaders: [
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|svg)$/,
|
||||
loader: "file-loader?name=[path][name].[ext]",
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The version of webpack used in the launchpad seems to have trouble
|
||||
* with the require("raw!${file}") that we use for the properties
|
||||
* file in l10.js.
|
||||
* This loader goes through the whole code and remove the "raw!" prefix
|
||||
* so the raw-loader declared in devtools-launchpad config can load
|
||||
* those files.
|
||||
*/
|
||||
test: /\.js$/,
|
||||
loader: "rewrite-raw",
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loaders: [
|
||||
// Replace all references to this.browserRequire() by require()
|
||||
"rewrite-browser-require",
|
||||
// Replace all references to loader.lazyRequire() by require()
|
||||
"rewrite-lazy-require",
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
resolveLoader: {
|
||||
modules: [
|
||||
path.resolve("./node_modules"),
|
||||
path.resolve("../shared/webpack"),
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -33,16 +61,22 @@ let webpackConfig = {
|
|||
libraryTarget: "umd",
|
||||
},
|
||||
|
||||
// Fallback compatibility for npm link
|
||||
resolve: {
|
||||
fallback: path.join(__dirname, "node_modules"),
|
||||
modules: [
|
||||
// Make sure webpack is always looking for modules in
|
||||
// `webconsole/node_modules` directory first.
|
||||
path.resolve(__dirname, "node_modules"), "node_modules"
|
||||
],
|
||||
alias: {
|
||||
"Services": "devtools-modules/src/Services",
|
||||
"react": path.join(__dirname, "node_modules/react"),
|
||||
|
||||
"devtools/client/framework/devtools": path.join(__dirname, "../../client/shims/devtools"),
|
||||
"devtools/client/framework/menu": "devtools-modules/src/menu",
|
||||
"devtools/client/framework/menu-item": path.join(__dirname, "../../client/framework/menu-item"),
|
||||
"devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"),
|
||||
"devtools/client/netmonitor/src/utils/menu": "devtools-contextmenu",
|
||||
|
||||
"devtools/client/shared/components/autocomplete-popup": path.join(__dirname, "../../client/shared/components/autocomplete-popup"),
|
||||
"devtools/client/shared/components/reps/reps": path.join(__dirname, "../../client/shared/components/reps/reps"),
|
||||
"devtools/client/shared/components/search-box": path.join(__dirname, "../../client/shared/components/search-box"),
|
||||
|
@ -60,6 +94,7 @@ let webpackConfig = {
|
|||
"devtools/client/shared/prefs": path.join(__dirname, "../../client/shared/prefs"),
|
||||
"devtools/client/shared/scroll": path.join(__dirname, "../../client/shared/scroll"),
|
||||
"devtools/client/shared/source-utils": path.join(__dirname, "../../client/shared/source-utils"),
|
||||
"devtools/client/shared/theme": path.join(__dirname, "../../client/shared/theme"),
|
||||
"devtools/client/shared/vendor/immutable": "immutable",
|
||||
"devtools/client/shared/vendor/react": "react",
|
||||
"devtools/client/shared/vendor/react-dom": "react-dom",
|
||||
|
@ -71,7 +106,10 @@ let webpackConfig = {
|
|||
"devtools/client/shared/widgets/tooltip/ImageTooltipHelper": path.join(__dirname, "../../client/shared/widgets/tooltip/ImageTooltipHelper"),
|
||||
"devtools/client/shared/widgets/tooltip/TooltipToggle": path.join(__dirname, "../../client/shared/widgets/tooltip/TooltipToggle"),
|
||||
"devtools/client/shared/widgets/Chart": path.join(__dirname, "../../client/shared/widgets/Chart"),
|
||||
|
||||
"devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
|
||||
"devtools/client/themes/variables.css": path.join(__dirname, "../../client/themes/variables.css"),
|
||||
|
||||
"devtools/shared/async-utils": path.join(__dirname, "../../shared/async-utils"),
|
||||
"devtools/shared/defer": path.join(__dirname, "../../shared/defer"),
|
||||
"devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter",
|
||||
|
@ -80,9 +118,16 @@ let webpackConfig = {
|
|||
"devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
|
||||
"devtools/shared/platform/clipboard": path.join(__dirname, "../../shared/platform/content/clipboard"),
|
||||
"devtools/shared/plural-form": path.join(__dirname, "../../shared/plural-form"),
|
||||
"devtools/shared/css/color": path.join(__dirname, "../../shared/css/color"),
|
||||
"devtools/shared/css/color-db": path.join(__dirname, "../../shared/css/color-db"),
|
||||
"devtools/shared/css/lexer": path.join(__dirname, "../../shared/css/lexer"),
|
||||
"devtools/shared/css/parsing-utils": path.join(__dirname, "../../shared/css/parsing-utils"),
|
||||
"devtools/shared/css/properties-db": path.join(__dirname, "../../shared/css/properties-db"),
|
||||
"devtools/shared/css/generated/properties-db": path.join(__dirname, "../../shared/css/generated/properties-db"),
|
||||
"devtools/shared/task": path.join(__dirname, "../../shared/task"),
|
||||
|
||||
"devtools/shim/locales": path.join(__dirname, "../../shared/locales/en-US"),
|
||||
"toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"),
|
||||
"Services": "devtools-modules/src/Services",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -114,19 +159,18 @@ const mappings = [
|
|||
webpackConfig.plugins = mappings.map(([regex, res]) =>
|
||||
new NormalModuleReplacementPlugin(regex, res));
|
||||
|
||||
// Exclude to transpile all scripts in devtools/ but not for this folder
|
||||
const basePath = path.join(__dirname, "../../").replace(/\\/g, "\\\\");
|
||||
const baseName = path.basename(__dirname);
|
||||
webpackConfig.babelExcludes = new RegExp(`^${basePath}(.(?!${baseName}))*$`);
|
||||
|
||||
let config = toolboxConfig(webpackConfig, getConfig());
|
||||
let config = toolboxConfig(webpackConfig, getConfig(), {
|
||||
// Exclude to transpile all scripts in devtools/ but not for this folder
|
||||
babelExcludes: new RegExp(`^${basePath}(.(?!${baseName}))*$`)
|
||||
});
|
||||
|
||||
// Remove loaders from devtools-launchpad's webpack.config.js
|
||||
// * For svg-inline loader:
|
||||
// Netmonitor uses file loader to bundle image assets instead of svg-inline loader
|
||||
// * For raw loader:
|
||||
// devtools/shared/l10n has preloaded raw loader in require.context
|
||||
config.module.loaders = config.module.loaders
|
||||
.filter((loader) => !["svg-inline", "raw"].includes(loader.loader));
|
||||
// Netmonitor uses file loader to bundle image assets instead of svg-inline-loader
|
||||
config.module.rules = config.module.rules
|
||||
.filter((rule) => !["svg-inline-loader"].includes(rule.loader));
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
const Services = require("Services");
|
||||
|
||||
const variableFileContents = require("raw!devtools/client/themes/variables.css");
|
||||
const variableFileContents = require("theme-loader!devtools/client/themes/variables.css");
|
||||
|
||||
const THEME_SELECTOR_STRINGS = {
|
||||
light: ":root.theme-light {",
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Webpack Support
|
||||
This directory contains modules intended to support and customize
|
||||
DevTools source bundling.
|
||||
|
||||
DevTools use Webpack to generate bundles for individual tools,
|
||||
which allow e.g. running them on top of the Launchpad (within
|
||||
a browser tab).
|
||||
|
||||
Custom loaders implemented in this directory are mostly used to
|
||||
rewrite existing code, so it's understandable for Webpack.
|
||||
|
||||
For example:
|
||||
|
||||
The following piece of code is using `lazyRequireGetter` that
|
||||
is unknown to Webpack.
|
||||
|
||||
```
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
"devtools/shared/old-event-emitter");
|
||||
```
|
||||
|
||||
In order to properly bundle `devtools/shared/old-event-emitter` module
|
||||
the code needs to be translated into:
|
||||
|
||||
```
|
||||
let EventEmitter = require("devtools/shared/old-event-emitter");
|
||||
```
|
||||
|
||||
See more in `rewrite-lazy-require`
|
|
@ -0,0 +1,29 @@
|
|||
/* 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";
|
||||
|
||||
var fs = require("fs");
|
||||
|
||||
/**
|
||||
* Make sure that content of the file is loaded as a text
|
||||
* (no parsing by other loaders)
|
||||
*
|
||||
* Used e.g. for runtime access to colors defined in variables.css
|
||||
*/
|
||||
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
|
||||
if (this.cacheable) {
|
||||
this.cacheable();
|
||||
}
|
||||
|
||||
let request = remainingRequest.split("!");
|
||||
let rawUrl = request[request.length - 1];
|
||||
let content = fs.readFileSync(rawUrl, "utf8");
|
||||
|
||||
// Avoid mix of single & double quotes in a string
|
||||
// (use only double quotes), so we can stringify.
|
||||
content = content.replace("'", '"');
|
||||
|
||||
return "module.exports = " + JSON.stringify(content) + ";";
|
||||
};
|
|
@ -235,5 +235,5 @@
|
|||
}
|
||||
|
||||
.show-presets #toggle-presets {
|
||||
filter: url(chrome://devtools/skin/images/filters.svg#checked-icon-state);
|
||||
filter: var(--theme-icon-checked-filter);
|
||||
}
|
||||
|
|
|
@ -358,6 +358,7 @@ StyleSheetEditor.prototype = {
|
|||
// We just applied an edit in the editor, so we can drop this
|
||||
// notification.
|
||||
this._isUpdating = false;
|
||||
this.emit("style-applied");
|
||||
} else if (this.sourceEditor) {
|
||||
this._getSourceTextAndPrettify().then((newText) => {
|
||||
this._justSetText = true;
|
||||
|
@ -742,6 +743,9 @@ StyleSheetEditor.prototype = {
|
|||
let decoder = new TextDecoder();
|
||||
let text = decoder.decode(array);
|
||||
|
||||
// Ensure we don't re-fetch the text from the original source
|
||||
// actor when we're notified that the style sheet changed.
|
||||
this._isUpdating = true;
|
||||
let relatedSheet = this.styleSheet.relatedStyleSheet;
|
||||
relatedSheet.update(text, this.transitionsEnabled);
|
||||
}, this.markLinkedFileBroken);
|
||||
|
|
|
@ -38,6 +38,7 @@ support-files =
|
|||
sourcemap-css/test-bootstrap-scss.css
|
||||
sourcemap-css/test-stylus.css
|
||||
sourcemap-sass/sourcemaps.scss
|
||||
sourcemap-sass/sourcemaps.scss^headers^
|
||||
sourcemap-sass/media-rules.scss
|
||||
sourcemap-styl/test-stylus.styl
|
||||
sourcemaps.html
|
||||
|
|
|
@ -17,9 +17,9 @@ waitForExplicitFinish();
|
|||
add_task(function* () {
|
||||
Services.prefs.setBoolPref(MAP_PREF, true);
|
||||
|
||||
let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
|
||||
let { ui, onMediaListChanged } = yield openStyleEditorForURL(TESTCASE_URI);
|
||||
|
||||
yield listenForMediaChange(ui);
|
||||
yield onMediaListChanged;
|
||||
|
||||
is(ui.editors.length, 1, "correct number of editors");
|
||||
|
||||
|
@ -58,14 +58,6 @@ function openEditor(editor) {
|
|||
return editor.getSourceEditor();
|
||||
}
|
||||
|
||||
function listenForMediaChange(UI) {
|
||||
return new Promise(resolve => {
|
||||
UI.once("media-list-changed", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getLinkFor(editor) {
|
||||
return editor.summary.querySelector(".stylesheet-name");
|
||||
}
|
||||
|
|
|
@ -73,6 +73,9 @@ add_task(function* () {
|
|||
|
||||
color = yield getComputedStyleProperty({selector: "div", name: "color"});
|
||||
is(color, "rgb(0, 0, 255)", "div is blue after saving file");
|
||||
|
||||
// Ensure that the editor didn't revert. Bug 1346662.
|
||||
is(editor.sourceEditor.getText(), CSS_TEXT, "edits remain applied");
|
||||
});
|
||||
|
||||
function editSCSS(editor) {
|
||||
|
|
|
@ -82,7 +82,15 @@ var openStyleEditor = Task.async(function* (tab) {
|
|||
let panel = toolbox.getPanel("styleeditor");
|
||||
let ui = panel.UI;
|
||||
|
||||
return { toolbox, panel, ui };
|
||||
// This event is sometimes needed by tests, but may be emitted before the list
|
||||
// animation is done. So we listen to it here so tests don't have to and can't miss it.
|
||||
let onMediaListChanged = ui.once("media-list-changed");
|
||||
|
||||
// The stylesheet list appears with an animation. Let this animation finish.
|
||||
let animations = ui._root.getAnimations({subtree: true});
|
||||
yield Promise.all(animations.map(a => a.finished));
|
||||
|
||||
return { toolbox, panel, ui, onMediaListChanged };
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
X-Content-Type-Options: nosniff
|
||||
Content-Type: text/plain
|
|
@ -536,12 +536,12 @@ body {
|
|||
}
|
||||
|
||||
.animation-target .node-highlighter:hover {
|
||||
filter: url(images/filters.svg#checked-icon-state);
|
||||
filter: var(--theme-icon-checked-filter);
|
||||
}
|
||||
|
||||
.animation-target .node-highlighter:active,
|
||||
.animation-target .node-highlighter.selected {
|
||||
filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
|
||||
filter: var(--theme-icon-checked-filter) brightness(0.9);
|
||||
}
|
||||
|
||||
/* Inline keyframes info in the timeline */
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
font: message-box;
|
||||
|
||||
--tab-line-selected-color: var(--blue-50);
|
||||
/* Introduce a specific variable here to be able to special-case firebug theme */
|
||||
--toolbar-icon-checked-filter: var(--theme-icon-checked-filter);
|
||||
}
|
||||
|
||||
:root.theme-light {
|
||||
|
@ -34,6 +36,9 @@
|
|||
|
||||
:root.theme-firebug {
|
||||
--proportional-font-family: Lucida Grande, Tahoma, sans-serif;
|
||||
|
||||
/* Do not use filters for Firebug checked icons: they are images, not SVGs */
|
||||
--toolbar-icon-checked-filter: none;
|
||||
}
|
||||
|
||||
.devtools-monospace {
|
||||
|
@ -310,7 +315,7 @@ checkbox:-moz-focusring {
|
|||
.devtools-button.checked::before,
|
||||
.devtools-toolbarbutton:not([label])[checked=true] > image,
|
||||
.devtools-toolbarbutton:not([label])[open=true] > image {
|
||||
filter: var(--theme-icon-checked-filter);
|
||||
filter: var(--toolbar-icon-checked-filter);
|
||||
}
|
||||
|
||||
/* Button states */
|
||||
|
|
|
@ -601,14 +601,14 @@
|
|||
}
|
||||
|
||||
.ruleview-selectorhighlighter:hover {
|
||||
filter: url(images/filters.svg#checked-icon-state);
|
||||
filter: var(--theme-icon-checked-filter);
|
||||
}
|
||||
|
||||
.ruleview-grid.active,
|
||||
.ruleview-selectorhighlighter:active,
|
||||
.ruleview-selectorhighlighter.highlighted,
|
||||
.ruleview-shape.active {
|
||||
filter: url(images/filters.svg#checked-icon-state) brightness(0.9);
|
||||
filter: var(--theme-icon-checked-filter) brightness(0.9);
|
||||
}
|
||||
|
||||
#ruleview-add-rule-button::before {
|
||||
|
|
|
@ -229,7 +229,7 @@
|
|||
/* Icon filters */
|
||||
--theme-icon-filter: none;
|
||||
--theme-icon-selected-filter: none;
|
||||
--theme-icon-checked-filter: none;
|
||||
--theme-icon-checked-filter: url(chrome://devtools/skin/images/filters.svg#icon-checked-light);
|
||||
|
||||
/* Font size */
|
||||
--theme-toolbar-font-size: 12px;
|
||||
|
|