Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE

This commit is contained in:
Ciure Andrei 2018-03-02 12:20:45 +02:00
Родитель 4b4fc7eab7 199c24a789
Коммит 2099a2a47b
380 изменённых файлов: 8242 добавлений и 14645 удалений

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

@ -464,13 +464,6 @@ HandlerProvider::MarshalAs(REFIID aIid)
return aIid;
}
HRESULT
HandlerProvider::DisconnectHandlerRemotes()
{
IUnknown* unk = static_cast<IGeckoBackChannel*>(this);
return ::CoDisconnectObject(unk, 0);
}
REFIID
HandlerProvider::GetEffectiveOutParamIid(REFIID aCallIid,
ULONG aCallMethod)

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

@ -47,7 +47,6 @@ public:
STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
NotNull<IStream*> aStream) override;
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
STDMETHODIMP DisconnectHandlerRemotes() override;
STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
ULONG aCallMethod) override;
STDMETHODIMP NewInstance(REFIID aIid,

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

@ -46,7 +46,6 @@
#include "mozilla/ReverseIterator.h"
#include "nsIXULRuntime.h"
#include "mozilla/mscom/AsyncInvoker.h"
#include "mozilla/mscom/Interceptor.h"
#include "oleacc.h"
@ -104,24 +103,6 @@ AccessibleWrap::Shutdown()
}
}
if (XRE_IsContentProcess()) {
// Bug 1434822: To improve performance for cross-process COM, we disable COM
// garbage collection. However, this means we never receive Release calls
// from clients, so defunct accessibles can never be deleted. Since we
// know when an accessible is shutting down, we can work around this by
// forcing COM to disconnect this object from all of its remote clients,
// which will cause associated references to be released.
IUnknown* unk = static_cast<IAccessible*>(this);
mscom::Interceptor::DisconnectRemotesForTarget(unk);
// If an accessible was retrieved via IAccessibleHypertext::hyperlink*,
// it will have a different Interceptor that won't be matched by the above
// call, even though it's the same object. Therefore, call it again with
// the IAccessibleHyperlink pointer. We can remove this horrible hack once
// bug 1440267 is fixed.
unk = static_cast<IAccessibleHyperlink*>(this);
mscom::Interceptor::DisconnectRemotesForTarget(unk);
}
Accessible::Shutdown();
}

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

@ -458,7 +458,15 @@ pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
// Tabbed browser
pref("browser.tabs.closeWindowWithLastTab", true);
// Open related links to a tab, e.g., link in current tab, at next to the
// current tab if |insertRelatedAfterCurrent| is true. Otherwise, always
// append new tab to the end.
pref("browser.tabs.insertRelatedAfterCurrent", true);
// Open all links, e.g., bookmarks, history items at next to current tab
// if |insertAfterCurrent| is true. Otherwise, append new tab to the end
// for non-related links. Note that if this is set to true, it will trump
// the value of browser.tabs.insertRelatedAfterCurrent.
pref("browser.tabs.insertAfterCurrent", false);
pref("browser.tabs.warnOnClose", true);
pref("browser.tabs.warnOnCloseOtherTabs", true);
pref("browser.tabs.warnOnOpen", true);
@ -1279,9 +1287,6 @@ pref("browser.newtabpage.rows", 3);
// number of columns of newtab grid
pref("browser.newtabpage.columns", 5);
// directory tiles download URL
pref("browser.newtabpage.directory.source", "https://tiles.services.mozilla.com/v3/links/fetch/%LOCALE%/%CHANNEL%");
// Activity Stream prefs that control to which page to redirect
pref("browser.newtabpage.activity-stream.prerender", true);
#ifndef RELEASE_OR_BETA

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

@ -1371,6 +1371,13 @@ var gBrowserInit = {
this._boundDelayedStartup = this._delayedStartup.bind(this);
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
if (!PrivateBrowsingUtils.enabled) {
document.getElementById("Tools:PrivateBrowsing").hidden = true;
// Setting disabled doesn't disable the shortcut, so we just remove
// the keybinding.
document.getElementById("key_privatebrowsing").remove();
}
this._loadHandled = true;
},
@ -1972,6 +1979,9 @@ if (AppConstants.platform == "macosx") {
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
document.getElementById("macDockMenuNewWindow").hidden = true;
}
if (!PrivateBrowsingUtils.enabled) {
document.getElementById("macDockMenuNewPrivateWindow").hidden = true;
}
this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0);
};
@ -4140,7 +4150,7 @@ function OpenBrowserWindow(options) {
var wintype = document.documentElement.getAttribute("windowtype");
var extraFeatures = "";
if (options && options.private) {
if (options && options.private && PrivateBrowsingUtils.enabled) {
extraFeatures = ",private";
if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
// Force the new window to load about:privatebrowsing instead of the default home page

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

@ -58,7 +58,8 @@
the application. -->
<menuitem label="&newNavigatorCmd.label;" oncommand="OpenBrowserWindowFromDockMenu();"
id="macDockMenuNewWindow" />
<menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});" />
<menuitem label="&newPrivateWindow.label;" oncommand="OpenBrowserWindowFromDockMenu({private: true});"
id="macDockMenuNewPrivateWindow" />
</menupopup>
</popupset>

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

@ -8,7 +8,6 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/PageThumbs.jsm");
ChromeUtils.import("resource://gre/modules/BackgroundPageThumbs.jsm");
ChromeUtils.import("resource:///modules/DirectoryLinksProvider.jsm");
ChromeUtils.import("resource://gre/modules/NewTabUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Rect",

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

@ -135,7 +135,7 @@ Site.prototype = {
*/
_render: function Site_render() {
// setup display variables
let enhanced = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link);
let enhanced = gAllPages.enhanced;
let url = this.url;
let title = enhanced && enhanced.title ? enhanced.title :
this.link.type == "history" ? this.link.baseDomain :
@ -178,8 +178,7 @@ Site.prototype = {
*/
refreshThumbnail: function Site_refreshThumbnail() {
// Only enhance tiles if that feature is turned on
let link = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link) ||
this.link;
let link = gAllPages.enhanced || this.link;
let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail");
if (link.bgColor) {

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

@ -359,7 +359,7 @@ nsContextMenu.prototype = {
var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
this.showItem("context-openlink", shouldShow && !isWindowPrivate);
this.showItem("context-openlinkprivate", shouldShow);
this.showItem("context-openlinkprivate", shouldShow && PrivateBrowsingUtils.enabled);
this.showItem("context-openlinkintab", shouldShow && !inContainer);
this.showItem("context-openlinkincontainertab", shouldShow && inContainer);
this.showItem("context-openlinkinusercontext-menu", shouldShow && !isWindowPrivate && showContainers);

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

@ -2480,20 +2480,22 @@ class TabBrowser {
}
}
// If we're opening a tab related to the an existing tab, move it
// to a position after that tab.
if (openerTab &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
// Move the new tab after another tab if needed.
if ((openerTab &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) ||
Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
if (lastRelatedTab)
lastRelatedTab.owner = null;
else
t.owner = openerTab;
this.moveTabTo(t, newTabPos, true);
let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab || this.mCurrentTab)._tPos + 1;
if (lastRelatedTab)
lastRelatedTab.owner = null;
else if (openerTab)
t.owner = openerTab;
this.moveTabTo(t, newTabPos, true);
if (openerTab)
this._lastRelatedTabMap.set(openerTab, t);
}
}
// This field is updated regardless if we actually animate
// since it's important that we keep this count correct in all cases.

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

@ -2,7 +2,6 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
@ -17,7 +16,6 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
DirectoryLinksProvider: "resource:///modules/DirectoryLinksProvider.jsm",
PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
Sanitizer: "resource:///modules/Sanitizer.jsm",
});
@ -94,31 +92,12 @@ add_task(async function setupWindowSize() {
registerCleanupFunction(function() {
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gOrigDirectorySource);
return watchLinksChangeOnce();
});
function pushPrefs(...aPrefs) {
return SpecialPowers.pushPrefEnv({"set": aPrefs});
}
/**
* Resolves promise when directory links are downloaded and written to disk
*/
function watchLinksChangeOnce() {
return new Promise(resolve => {
let observer = {
onManyLinksChanged: () => {
DirectoryLinksProvider.removeObserver(observer);
resolve();
}
};
observer.onDownloadFail = observer.onManyLinksChanged;
DirectoryLinksProvider.addObserver(observer);
});
}
add_task(async function setup() {
registerCleanupFunction(function() {
return new Promise(resolve => {
@ -139,15 +118,7 @@ add_task(async function setup() {
});
});
let promiseReady = (async function() {
await watchLinksChangeOnce();
await whenPagesUpdated();
})();
// Save the original directory source (which is set globally for tests)
gOrigDirectorySource = Services.prefs.getCharPref(PREF_NEWTAB_DIRECTORYSOURCE);
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, gDirectorySource);
await promiseReady;
await whenPagesUpdated();
});
/** Perform an action on a cell within the newtab page.

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

@ -68,7 +68,6 @@ const startupPhases = {
"resource:///modules/BrowserUITelemetry.jsm",
"resource:///modules/BrowserUsageTelemetry.jsm",
"resource:///modules/ContentCrashHandlers.jsm",
"resource:///modules/DirectoryLinksProvider.jsm",
"resource://gre/modules/NewTabUtils.jsm",
"resource://gre/modules/PageThumbs.jsm",
"resource://gre/modules/PlacesUtils.jsm",

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

@ -23,6 +23,8 @@ skip-if = !e10s # Test only relevant for e10s.
skip-if = !e10s # Pref and test only relevant for e10s.
[browser_newwindow_tabstrip_overflow.js]
[browser_opened_file_tab_navigated_to_web.js]
[browser_new_tab_insert_position.js]
support-files = file_new_tab_page.html
[browser_overflowScroll.js]
[browser_pinnedTabs.js]
[browser_pinnedTabs_closeByKeyboard.js]

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

@ -0,0 +1,94 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
async function doTest(aInsertRelatedAfterCurrent, aInsertAfterCurrent) {
const kDescription = "(aInsertRelatedAfterCurrent=" + aInsertRelatedAfterCurrent +
", aInsertAfterCurrent=" + aInsertAfterCurrent + "): ";
is(gBrowser.tabs.length, 1, kDescription + "one tab is open initially");
await SpecialPowers.pushPrefEnv({set: [
["browser.tabs.opentabfor.middleclick", true],
["browser.tabs.loadBookmarksInBackground", false],
["browser.tabs.insertRelatedAfterCurrent", aInsertRelatedAfterCurrent],
["browser.tabs.insertAfterCurrent", aInsertAfterCurrent]
]});
// Add a few tabs.
let tabs = [];
function addTab(aURL, aReferrer) {
let tab = BrowserTestUtils.addTab(gBrowser, aURL, {referrerURI: aReferrer});
tabs.push(tab);
return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
}
await addTab("http://mochi.test:8888/#0");
await addTab("http://mochi.test:8888/#1");
await addTab("http://mochi.test:8888/#2");
await addTab("http://mochi.test:8888/#3");
// Create a new tab page which has a link to "example.com".
let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
pageURL = `${pageURL}file_new_tab_page.html`;
let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageURL);
let newTabURISpec = newTab.linkedBrowser.currentURI.spec;
const kNewTabIndex = 1;
gBrowser.moveTabTo(newTab, kNewTabIndex);
let openTabIndex = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
kNewTabIndex + 1 : gBrowser.tabs.length;
let openTabDescription = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
"immediately to the right" : "at rightmost";
// Middle click on the cell should open example.com in a new tab.
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/", true);
await BrowserTestUtils.synthesizeMouseAtCenter("#link_to_example_com",
{button: 1}, gBrowser.selectedBrowser);
let openTab = await newTabPromise;
is(openTab.linkedBrowser.currentURI.spec, "http://example.com/",
"Middle click should open site to correct url.");
is(openTab._tPos, openTabIndex,
kDescription + "Middle click should open site in a new tab " + openTabDescription);
// Remove the new opened tab which loaded example.com.
gBrowser.removeTab(gBrowser.tabs[openTabIndex]);
// Go back to the new tab.
gBrowser.selectedTab = newTab;
is(gBrowser.selectedBrowser.currentURI.spec, newTabURISpec,
kDescription + "New tab URI shouldn't be changed");
openTabIndex = aInsertAfterCurrent ? kNewTabIndex + 1 : gBrowser.tabs.length;
openTabDescription = aInsertAfterCurrent ? "immediately to the right" : "at rightmost";
// Open about:mozilla in new tab from the URL bar.
gURLBar.focus();
gURLBar.select();
newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:mozilla");
EventUtils.sendString("about:mozilla");
EventUtils.synthesizeKey("KEY_Alt", { altKey: true, code: "AltLeft", type: "keydown" });
EventUtils.synthesizeKey("KEY_Enter", { altKey: true, code: "Enter" });
EventUtils.synthesizeKey("KEY_Alt", { altKey: false, code: "AltLeft", type: "keyup" });
openTab = await newTabPromise;
is(newTab.linkedBrowser.currentURI.spec, newTabURISpec,
kDescription + "example.com should be loaded in current tab.");
is(openTab.linkedBrowser.currentURI.spec, "about:mozilla",
kDescription + "about:mozilla should be loaded in the new tab.");
is(openTab._tPos, openTabIndex,
kDescription + "Alt+Enter in the URL bar should open page in a new tab " + openTabDescription);
// Remove all tabs opened by this test.
while (gBrowser.tabs[1]) {
gBrowser.removeTab(gBrowser.tabs[1]);
}
}
add_task(async function() {
// Current default settings.
await doTest(true, false);
// Perhaps, some users would love this settings.
await doTest(true, true);
// Maybe, unrealistic cases, but we should test these cases too.
await doTest(false, true);
await doTest(false, false);
});

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

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a href="http://example.com/" id="link_to_example_com">go to example.com</a>
</body>
</html>

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

@ -3884,6 +3884,86 @@ var CustomizableUI = {
createSpecialWidget(aId, aDocument) {
return CustomizableUIInternal.createSpecialWidget(aId, aDocument);
},
/**
* Fills a submenu with menu items.
* @param aMenuItems the menu items to display.
* @param aSubview the subview to fill.
*/
fillSubviewFromMenuItems(aMenuItems, aSubview) {
let attrs = ["oncommand", "onclick", "label", "key", "disabled",
"command", "observes", "hidden", "class", "origin",
"image", "checked", "style"];
let doc = aSubview.ownerDocument;
let fragment = doc.createDocumentFragment();
for (let menuChild of aMenuItems) {
if (menuChild.hidden)
continue;
let subviewItem;
if (menuChild.localName == "menuseparator") {
// Don't insert duplicate or leading separators. This can happen if there are
// menus (which we don't copy) above the separator.
if (!fragment.lastChild || fragment.lastChild.localName == "menuseparator") {
continue;
}
subviewItem = doc.createElementNS(kNSXUL, "menuseparator");
} else if (menuChild.localName == "menuitem") {
subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton");
CustomizableUI.addShortcut(menuChild, subviewItem);
let item = menuChild;
if (!item.hasAttribute("onclick")) {
subviewItem.addEventListener("click", event => {
let newEvent = new doc.defaultView.MouseEvent(event.type, event);
item.dispatchEvent(newEvent);
});
}
if (!item.hasAttribute("oncommand")) {
subviewItem.addEventListener("command", event => {
let newEvent = doc.createEvent("XULCommandEvent");
newEvent.initCommandEvent(
event.type, event.bubbles, event.cancelable, event.view,
event.detail, event.ctrlKey, event.altKey, event.shiftKey,
event.metaKey, event.sourceEvent, 0);
item.dispatchEvent(newEvent);
});
}
} else {
continue;
}
for (let attr of attrs) {
let attrVal = menuChild.getAttribute(attr);
if (attrVal)
subviewItem.setAttribute(attr, attrVal);
}
// We do this after so the .subviewbutton class doesn't get overriden.
if (menuChild.localName == "menuitem") {
subviewItem.classList.add("subviewbutton");
}
fragment.appendChild(subviewItem);
}
aSubview.appendChild(fragment);
},
/**
* A helper function for clearing subviews.
* @param aSubview the subview to clear.
*/
clearSubview(aSubview) {
let parent = aSubview.parentNode;
// We'll take the container out of the document before cleaning it out
// to avoid reflowing each time we remove something.
parent.removeChild(aSubview);
while (aSubview.firstChild) {
aSubview.firstChild.remove();
}
parent.appendChild(aSubview);
},
};
Object.freeze(this.CustomizableUI);
Object.freeze(this.CustomizableUI.windows);

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

@ -75,77 +75,6 @@ function setAttributes(aNode, aAttrs) {
}
}
function fillSubviewFromMenuItems(aMenuItems, aSubview) {
let attrs = ["oncommand", "onclick", "label", "key", "disabled",
"command", "observes", "hidden", "class", "origin",
"image", "checked", "style"];
let doc = aSubview.ownerDocument;
let fragment = doc.createDocumentFragment();
for (let menuChild of aMenuItems) {
if (menuChild.hidden)
continue;
let subviewItem;
if (menuChild.localName == "menuseparator") {
// Don't insert duplicate or leading separators. This can happen if there are
// menus (which we don't copy) above the separator.
if (!fragment.lastChild || fragment.lastChild.localName == "menuseparator") {
continue;
}
subviewItem = doc.createElementNS(kNSXUL, "menuseparator");
} else if (menuChild.localName == "menuitem") {
subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton");
CustomizableUI.addShortcut(menuChild, subviewItem);
let item = menuChild;
if (!item.hasAttribute("onclick")) {
subviewItem.addEventListener("click", event => {
let newEvent = new doc.defaultView.MouseEvent(event.type, event);
item.dispatchEvent(newEvent);
});
}
if (!item.hasAttribute("oncommand")) {
subviewItem.addEventListener("command", event => {
let newEvent = doc.createEvent("XULCommandEvent");
newEvent.initCommandEvent(
event.type, event.bubbles, event.cancelable, event.view,
event.detail, event.ctrlKey, event.altKey, event.shiftKey,
event.metaKey, event.sourceEvent, 0);
item.dispatchEvent(newEvent);
});
}
} else {
continue;
}
for (let attr of attrs) {
let attrVal = menuChild.getAttribute(attr);
if (attrVal)
subviewItem.setAttribute(attr, attrVal);
}
// We do this after so the .subviewbutton class doesn't get overriden.
if (menuChild.localName == "menuitem") {
subviewItem.classList.add("subviewbutton");
}
fragment.appendChild(subviewItem);
}
aSubview.appendChild(fragment);
}
function clearSubview(aSubview) {
let parent = aSubview.parentNode;
// We'll take the container out of the document before cleaning it out
// to avoid reflowing each time we remove something.
parent.removeChild(aSubview);
while (aSubview.firstChild) {
aSubview.firstChild.remove();
}
parent.appendChild(aSubview);
}
const CustomizableWidgets = [
{
id: "history-panelmenu",
@ -238,13 +167,6 @@ const CustomizableWidgets = [
panelview.appendChild(body);
panelview.appendChild(footer);
}
}, {
id: "privatebrowsing-button",
shortcutId: "key_privatebrowsing",
onCommand(e) {
let win = e.target.ownerGlobal;
win.OpenBrowserWindow({private: true});
}
}, {
id: "save-page-button",
shortcutId: "key_savePage",
@ -948,3 +870,14 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
},
});
}
if (PrivateBrowsingUtils.enabled) {
CustomizableWidgets.push({
id: "privatebrowsing-button",
shortcutId: "key_privatebrowsing",
onCommand(e) {
let win = e.target.ownerGlobal;
win.OpenBrowserWindow({private: true});
}
});
}

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

@ -172,6 +172,17 @@ var Policies = {
}
},
"DisablePrivateBrowsing": {
onBeforeAddons(manager, param) {
if (param) {
manager.disallowFeature("privatebrowsing");
manager.disallowFeature("about:privatebrowsing", true);
setAndLockPref("browser.privatebrowsing.autostart", false);
}
}
},
"DisplayBookmarksToolbar": {
onBeforeUIStartup(manager, param) {
if (param) {
@ -210,6 +221,30 @@ var Policies = {
}
},
"Homepage": {
onBeforeUIStartup(manager, param) {
// |homepages| will be a string containing a pipe-separated ('|') list of
// URLs because that is what the "Home page" section of about:preferences
// (and therefore what the pref |browser.startup.homepage|) accepts.
let homepages = param.URL.spec;
if (param.Additional && param.Additional.length > 0) {
homepages += "|" + param.Additional.map(url => url.spec).join("|");
}
if (param.Locked) {
setAndLockPref("browser.startup.homepage", homepages);
setAndLockPref("browser.startup.page", 1);
setAndLockPref("pref.browser.homepage.disable_button.current_page", true);
setAndLockPref("pref.browser.homepage.disable_button.bookmark_page", true);
setAndLockPref("pref.browser.homepage.disable_button.restore_default", true);
} else {
runOncePerModification("setHomepage", homepages, () => {
Services.prefs.setStringPref("browser.startup.homepage", homepages);
Services.prefs.setIntPref("browser.startup.page", 1);
});
}
}
},
"InstallAddons": {
onBeforeUIStartup(manager, param) {
addAllowDenyPermissions("install", param.Allow, null);
@ -326,6 +361,36 @@ function runOnce(actionName, callback) {
log.debug(`Not running action ${actionName} again because it has already run.`);
return;
}
callback();
Services.prefs.setBoolPref(prefName, true);
callback();
}
/**
* runOncePerModification
*
* Helper function similar to runOnce. The difference is that runOnce runs the
* callback once when the policy is set, then never again.
* runOncePerModification runs the callback once each time the policy value
* changes from its previous value.
*
* @param {string} actionName
* A given name which will be used to track if this callback has run.
* This string will be part of a pref name.
* @param {string} policyValue
* The current value of the policy. This will be compared to previous
* values given to this function to determine if the policy value has
* changed. Regardless of the data type of the policy, this must be a
* string.
* @param {Function} callback
* The callback to be run when the pref value changes
*/
function runOncePerModification(actionName, policyValue, callback) {
let prefName = `browser.policies.runOncePerModification.${actionName}`;
let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
if (policyValue === oldPolicyValue) {
log.debug(`Not running action ${actionName} again because the policy's value is unchanged`);
return;
}
Services.prefs.setStringPref(prefName, policyValue);
callback();
}

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

@ -153,6 +153,14 @@
"enum": [true]
},
"DisablePrivateBrowsing": {
"description": "Disables private browsing.",
"first_available": "60.0",
"type": "boolean",
"enum": [true]
},
"DisplayBookmarksToolbar": {
"description": "Causes the bookmarks toolbar to be displayed by default.",
"first_available": "60.0",
@ -199,6 +207,29 @@
}
},
"Homepage": {
"description": "Set and optionally lock the homepage.",
"first_available": "60.0",
"enterprise_only": true,
"type": "object",
"properties": {
"URL": {
"type": "URL"
},
"Locked": {
"type": "boolean"
},
"Additional": {
"type": "array",
"items": {
"type": "URL"
}
}
},
"required": ["URL"]
},
"InstallAddons": {
"description": "Allow or deny popup websites to install webextensions.",
"first_available": "60.0",

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

@ -25,7 +25,9 @@ support-files =
[browser_policy_disable_fxscreenshots.js]
[browser_policy_disable_masterpassword.js]
[browser_policy_disable_pocket.js]
[browser_policy_disable_privatebrowsing.js]
[browser_policy_disable_shield.js]
[browser_policy_display_bookmarks.js]
[browser_policy_display_menu.js]
[browser_policy_remember_passwords.js]
[browser_policy_set_homepage.js]

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

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
add_task(async function setup() {
await setupPolicyEngineWithJson({
"policies": {
"DisablePrivateBrowsing": true
}
});
});
add_task(async function test_menu_shown() {
is(PrivateBrowsingUtils.enabled, false, "Private browsing should be disabled");
let newWin = await BrowserTestUtils.openNewBrowserWindow();
let privateBrowsingCommand = newWin.document.getElementById("Tools:PrivateBrowsing");
is(privateBrowsingCommand.hidden, true, "The private browsing command should be hidden");
await BrowserTestUtils.closeWindow(newWin);
});

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

@ -0,0 +1,170 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Prefs that will be touched by the test and need to be reset when the test
// cleans up.
const TOUCHED_PREFS = {
"browser.startup.homepage": "String",
"browser.startup.page": "Int",
"pref.browser.homepage.disable_button.current_page": "Bool",
"pref.browser.homepage.disable_button.bookmark_page": "Bool",
"pref.browser.homepage.disable_button.restore_default": "Bool",
"browser.policies.runOncePerModification.setHomepage": "String",
};
let ORIGINAL_PREF_VALUE = {};
add_task(function read_original_pref_values() {
for (let pref in TOUCHED_PREFS) {
let prefType = TOUCHED_PREFS[pref];
ORIGINAL_PREF_VALUE[pref] =
Services.prefs[`get${prefType}Pref`](pref, undefined);
}
});
registerCleanupFunction(function restore_pref_values() {
let defaults = Services.prefs.getDefaultBranch("");
for (let pref in TOUCHED_PREFS) {
Services.prefs.unlockPref(pref);
Services.prefs.clearUserPref(pref);
let originalValue = ORIGINAL_PREF_VALUE[pref];
let prefType = TOUCHED_PREFS[pref];
if (originalValue !== undefined) {
defaults[`set${prefType}Pref`](pref, originalValue);
}
}
});
add_task(async function homepage_test_simple() {
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example1.com/"
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example1.com/", "Homepage URL should have been set");
is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
"Homepage should be used instead of blank page or previous tabs");
});
add_task(async function homepage_test_repeat_same_policy_value() {
// Simulate homepage change after policy applied
Services.prefs.setStringPref("browser.startup.homepage",
"http://example2.com/");
Services.prefs.setIntPref("browser.startup.page", 3);
// Policy should have no effect. Homepage has not been locked and policy value
// has not changed. We should be respecting the homepage that the user gave.
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example1.com/"
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example2.com/",
"Homepage URL should not have been overridden by pre-existing policy value");
is(Services.prefs.getIntPref("browser.startup.page", -1), 3,
"Start page type should not have been overridden by pre-existing policy value");
});
add_task(async function homepage_test_different_policy_value() {
// This policy is a change from the policy's previous value. This should
// override the user's homepage
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example3.com/"
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example3.com/",
"Homepage URL should have been overridden by the policy value");
is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
"Start page type should have been overridden by setting the policy value");
});
add_task(async function homepage_test_empty_additional() {
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example1.com/",
"Additional": []
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example1.com/", "Homepage URL should have been set properly");
});
add_task(async function homepage_test_single_additional() {
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example1.com/",
"Additional": ["http://example2.com/"]
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example1.com/|http://example2.com/",
"Homepage URL should have been set properly");
});
add_task(async function homepage_test_multiple_additional() {
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example1.com/",
"Additional": ["http://example2.com/",
"http://example3.com/"]
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example1.com/|http://example2.com/|http://example3.com/",
"Homepage URL should have been set properly");
});
add_task(async function homepage_test_locked() {
await setupPolicyEngineWithJson({
"policies": {
"Homepage": {
"URL": "http://example4.com/",
"Additional": ["http://example5.com/",
"http://example6.com/"],
"Locked": true
}
}
});
is(Services.prefs.getStringPref("browser.startup.homepage", ""),
"http://example4.com/|http://example5.com/|http://example6.com/",
"Homepage URL should have been set properly");
is(Services.prefs.getIntPref("browser.startup.page", -1), 1,
"Homepage should be used instead of blank page or previous tabs");
is(Services.prefs.prefIsLocked("browser.startup.homepage"), true,
"Homepage pref should be locked");
is(Services.prefs.prefIsLocked("browser.startup.page"), true,
"Start page type pref should be locked");
// Test that UI is disabled when the Locked property is enabled
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
is(content.document.getElementById("browserStartupPage").disabled, true,
"When homepage is locked, the startup page choice controls should be locked");
is(content.document.getElementById("browserHomePage").disabled, true,
"Homepage input should be disabled");
is(content.document.getElementById("useCurrent").disabled, true,
"\"Use Current Page\" button should be disabled");
is(content.document.getElementById("useBookmark").disabled, true,
"\"Use Bookmark...\" button should be disabled");
is(content.document.getElementById("restoreDefaultHomePage").disabled, true,
"\"Restore to Default\" button should be disabled");
});
await BrowserTestUtils.removeTab(tab);
});

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

@ -659,6 +659,10 @@ class Tab extends TabBase {
return this.nativeTab.selected;
}
get highlighted() {
return this.nativeTab.selected;
}
get selected() {
return this.nativeTab.selected;
}

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

@ -163,6 +163,7 @@
"url": "chrome://browser/content/ext-tabs.js",
"schema": "chrome://browser/content/schemas/tabs.json",
"scopes": ["addon_parent"],
"events": ["update", "disable"],
"paths": [
["tabs"]
]

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

@ -24,6 +24,23 @@ var {
const TABHIDE_PREFNAME = "extensions.webextensions.tabhide.enabled";
function showHiddenTabs(id) {
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
while (windowsEnum.hasMoreElements()) {
let win = windowsEnum.getNext();
if (win.closed || !win.gBrowser) {
continue;
}
for (let tab of win.gBrowser.tabs) {
if (tab.hidden && tab.ownerGlobal &&
SessionStore.getTabValue(tab, "hiddenBy") === id) {
win.gBrowser.showTab(tab);
}
}
}
}
let tabListener = {
tabReadyInitialized: false,
tabReadyPromises: new WeakMap(),
@ -81,25 +98,16 @@ let tabListener = {
};
this.tabs = class extends ExtensionAPI {
onShutdown(reason) {
if (!this.extension.hasPermission("tabHide")) {
return;
}
if (reason == "ADDON_DISABLE" ||
reason == "ADDON_UNINSTALL") {
// Show all hidden tabs if a tab managing extension is uninstalled or
// disabled. If a user has more than one, the extensions will need to
// self-manage re-hiding tabs.
for (let tab of this.extension.tabManager.query()) {
let nativeTab = tabTracker.getTab(tab.id);
if (nativeTab.hidden && nativeTab.ownerGlobal &&
SessionStore.getTabValue(nativeTab, "hiddenBy") === this.extension.id) {
nativeTab.ownerGlobal.gBrowser.showTab(nativeTab);
}
}
static onUpdate(id, manifest) {
if (!manifest.permissions || !manifest.permissions.includes("tabHide")) {
showHiddenTabs(id);
}
}
static onDisable(id) {
showHiddenTabs(id);
}
getAPI(context) {
let {extension} = context;

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

@ -128,6 +128,9 @@ this.windows = class extends ExtensionAPI {
if (createData.incognito !== null && createData.incognito != incognito) {
return Promise.reject({message: "`incognito` property must match the incognito state of tab"});
}
if (createData.incognito && !PrivateBrowsingUtils.enabled) {
return Promise.reject({message: "`incognito` cannot be used if incognito mode is disabled"});
}
createData.incognito = incognito;
args.appendElement(tab);

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

@ -164,6 +164,7 @@ skip-if = !e10s
[browser_ext_tabs_executeScript_runAt.js]
[browser_ext_tabs_getCurrent.js]
[browser_ext_tabs_hide.js]
[browser_ext_tabs_hide_update.js]
[browser_ext_tabs_insertCSS.js]
[browser_ext_tabs_lastAccessed.js]
skip-if = os == 'win' # Bug 1434590

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

@ -0,0 +1,143 @@
"use strict";
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", this);
AddonTestUtils.initMochitest(this);
const ID = "@test-tabs-addon";
async function updateExtension(ID, options) {
let xpi = AddonTestUtils.createTempWebExtensionFile(options);
await Promise.all([
AddonTestUtils.promiseWebExtensionStartup(ID),
AddonManager.installTemporaryAddon(xpi),
]);
}
async function disableExtension(ID) {
let disabledPromise = awaitEvent("shutdown", ID);
let addon = await AddonManager.getAddonByID(ID);
addon.userDisabled = true;
await disabledPromise;
}
function getExtension() {
async function background() {
let tabs = await browser.tabs.query({url: "http://example.com/"});
let testTab = tabs[0];
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if ("hidden" in changeInfo) {
browser.test.assertEq(tabId, testTab.id, "correct tab was hidden");
browser.test.assertTrue(changeInfo.hidden, "tab is hidden");
browser.test.sendMessage("changeInfo");
}
});
let hidden = await browser.tabs.hide(testTab.id);
browser.test.assertEq(hidden[0], testTab.id, "tabs.hide hide the tab");
tabs = await browser.tabs.query({hidden: true});
browser.test.assertEq(tabs[0].id, testTab.id, "tabs.query result was hidden");
browser.test.sendMessage("ready");
}
let extdata = {
manifest: {
version: "1.0",
"applications": {
"gecko": {
"id": ID,
},
},
permissions: ["tabs", "tabHide"],
},
background,
useAddonManager: "temporary",
};
return ExtensionTestUtils.loadExtension(extdata);
}
// Test our update handling. Currently this means any hidden tabs will be
// shown when a tabHide extension is shutdown. We additionally test the
// tabs.onUpdated listener gets called with hidden state changes.
add_task(async function test_tabs_update() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.webextensions.tabhide.enabled", true]],
});
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
await BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[0]);
const extension = getExtension();
await extension.startup();
// test onUpdated
await Promise.all([
extension.awaitMessage("ready"),
extension.awaitMessage("changeInfo"),
]);
Assert.ok(tab.hidden, "Tab is hidden by extension");
// Test that update doesn't hide tabs when tabHide permission is present.
let extdata = {
manifest: {
version: "2.0",
"applications": {
"gecko": {
"id": ID,
},
},
permissions: ["tabs", "tabHide"],
},
};
await updateExtension(ID, extdata);
Assert.ok(tab.hidden, "Tab is hidden hidden after update");
// Test that update does hide tabs when tabHide permission is removed.
extdata.manifest = {
version: "3.0",
"applications": {
"gecko": {
"id": ID,
},
},
permissions: ["tabs"],
};
await updateExtension(ID, extdata);
Assert.ok(!tab.hidden, "Tab is not hidden hidden after update");
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});
// Test our update handling. Currently this means any hidden tabs will be
// shown when a tabHide extension is shutdown. We additionally test the
// tabs.onUpdated listener gets called with hidden state changes.
add_task(async function test_tabs_disable() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.webextensions.tabhide.enabled", true]],
});
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
await BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[0]);
const extension = getExtension();
await extension.startup();
// test onUpdated
await Promise.all([
extension.awaitMessage("ready"),
extension.awaitMessage("changeInfo"),
]);
Assert.ok(tab.hidden, "Tab is hidden by extension");
// Test that disable does hide tabs.
await disableExtension(ID);
Assert.ok(!tab.hidden, "Tab is not hidden hidden after disable");
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});

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

@ -170,6 +170,31 @@ add_task(async function() {
await extension.awaitFinish("tabs.query");
await extension.unload();
// match highlighted
extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"],
},
background: async function() {
let tabs1 = await browser.tabs.query({highlighted: false});
browser.test.assertEq(3, tabs1.length, "should have three non-highlighted tabs");
let tabs2 = await browser.tabs.query({highlighted: true});
browser.test.assertEq(1, tabs2.length, "should have one highlighted tab");
for (let tab of [...tabs1, ...tabs2]) {
browser.test.assertEq(tab.active, tab.highlighted, "highlighted and active are equal in tab " + tab.index);
}
browser.test.notifyPass("tabs.query");
},
});
await extension.startup();
await extension.awaitFinish("tabs.query");
await extension.unload();
// test width and height
extension = ExtensionTestUtils.loadExtension({
manifest: {

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

@ -74,7 +74,7 @@ function checkButtons(icons, iconInfo, area) {
async function runTestWithIcons(icons) {
const FRAME_COLOR = [71, 105, 91];
const BACKGROUND_TAB_TEXT_COLOR = [207, 221, 192, .9];
const TAB_BACKGROUND_TEXT_COLOR = [207, 221, 192, .9];
let manifest = {
"theme": {
"images": {
@ -82,7 +82,7 @@ async function runTestWithIcons(icons) {
},
"colors": {
"frame": FRAME_COLOR,
"background_tab_text": BACKGROUND_TAB_TEXT_COLOR,
"tab_background_text": TAB_BACKGROUND_TEXT_COLOR,
},
"icons": {},
},

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

@ -381,7 +381,7 @@ nsBrowserContentHandler.prototype = {
};
if (isLocal(resolvedURI)) {
// If the URI is local, we are sure it won't wrongly inherit chrome privs
var features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
let features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
openWindow(null, resolvedURI.spec, "_blank", features);
cmdLine.preventDefault = true;
} else {
@ -402,10 +402,18 @@ nsBrowserContentHandler.prototype = {
try {
var privateWindowParam = cmdLine.handleFlagWithParam("private-window", false);
if (privateWindowParam) {
let resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
handURIToExistingBrowser(resolvedURI, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine, true,
let forcePrivate = true;
let resolvedURI;
if (!PrivateBrowsingUtils.enabled) {
// Load about:privatebrowsing in a normal tab, which will display an error indicating
// access to private browsing has been disabled.
forcePrivate = false;
resolvedURI = Services.io.newURI("about:privatebrowsing");
} else {
resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
}
handURIToExistingBrowser(resolvedURI, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine, forcePrivate,
Services.scriptSecurityManager.getSystemPrincipal());
cmdLine.preventDefault = true;
}
} catch (e) {
if (e.result != Cr.NS_ERROR_INVALID_ARG) {
@ -413,8 +421,12 @@ nsBrowserContentHandler.prototype = {
}
// NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param.
if (cmdLine.handleFlag("private-window", false)) {
let features = "chrome,dialog=no,all";
if (PrivateBrowsingUtils.enabled) {
features += ",private";
}
openWindow(null, this.chromeURL, "_blank",
"chrome,dialog=no,private,all" + this.getFeatures(cmdLine),
features + this.getFeatures(cmdLine),
"about:privatebrowsing");
cmdLine.preventDefault = true;
}
@ -428,7 +440,7 @@ nsBrowserContentHandler.prototype = {
// The global PB Service consumes this flag, so only eat it in per-window
// PB builds.
if (cmdLine.handleFlag("private", false)) {
if (cmdLine.handleFlag("private", false) && PrivateBrowsingUtils.enabled) {
PrivateBrowsingUtils.enterTemporaryAutoStartMode();
}

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

@ -95,7 +95,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm",
DirectoryLinksProvider: "resource:///modules/DirectoryLinksProvider.jsm",
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
Feeds: "resource:///modules/Feeds.jsm",
FileUtils: "resource://gre/modules/FileUtils.jsm",
@ -998,9 +997,7 @@ BrowserGlue.prototype = {
PageThumbs.init();
DirectoryLinksProvider.init();
NewTabUtils.init();
NewTabUtils.links.addProvider(DirectoryLinksProvider);
PageActions.init();
@ -1820,7 +1817,7 @@ BrowserGlue.prototype = {
// eslint-disable-next-line complexity
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 62;
const UI_VERSION = 64;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
let currentUIVersion;
@ -2316,6 +2313,11 @@ BrowserGlue.prototype = {
}
}
if (currentUIVersion < 64) {
OS.File.remove(OS.Path.join(OS.Constants.Path.profileDir,
"directoryLinks.json"), {ignoreAbsent: true});
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},

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

@ -465,6 +465,10 @@ PlacesController.prototype = {
* and the item can be displayed, false otherwise.
*/
_shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
if (aMenuItem.hasAttribute("hideifprivatebrowsing") && !PrivateBrowsingUtils.enabled) {
return false;
}
var selectiontype = aMenuItem.getAttribute("selectiontype");
if (!selectiontype) {
selectiontype = "single|multiple";

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

@ -459,6 +459,11 @@ var gPrivacyPane = {
bundlePrefs.getString("removeSelectedCookies.label"),
]);
if (!PrivateBrowsingUtils.enabled) {
document.getElementById("privateBrowsingAutoStart").hidden = true;
document.querySelector("menuitem[value='dontremember']").hidden = true;
}
// Notify observers that the UI is now ready
Services.obs.notifyObservers(window, "privacy-pane-loaded");
},

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

@ -6,6 +6,9 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
let { getChromeWindow } = ChromeUtils.import("resource:///modules/syncedtabs/util.js", {});
let log = ChromeUtils.import("resource://gre/modules/Log.jsm", {})
@ -520,8 +523,10 @@ TabListView.prototype = {
while (el) {
let show = false;
if (showTabOptions) {
if (el.getAttribute("id") != "syncedTabsOpenAllInTabs" &&
el.getAttribute("id") != "syncedTabsManageDevices") {
if (el.getAttribute("id") == "syncedTabsOpenSelectedInPrivateWindow") {
show = PrivateBrowsingUtils.enabled;
} else if (el.getAttribute("id") != "syncedTabsOpenAllInTabs" &&
el.getAttribute("id") != "syncedTabsManageDevices") {
show = true;
}
} else if (el.getAttribute("id") == "syncedTabsOpenAllInTabs") {

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

@ -1,107 +0,0 @@
=============================================
Directory Links Architecture and Data Formats
=============================================
Directory links are enhancements to the new tab experience that combine content
Firefox already knows about from user browsing with external content. There is 1
kind of links:
- directory links fill in additional tiles on the new tab page if there would
have been empty tiles because the user has a clean profile or cleared history
To power the above features, DirectoryLinksProvider module downloads, at most
once per 24 hours, the directory source links as JSON with enough data for
Firefox to determine what should be shown or not.
For the directory source endpoint, the default preference values point
to Mozilla key-pinned servers with encryption. No cookies are set by the servers
and Firefox enforces this by making anonymous requests.
- default directory source endpoint:
https://tiles.services.mozilla.com/v3/links/fetch/%LOCALE%/%CHANNEL%
Preferences
===========
There is one main preference that controls downloading links.
``browser.newtabpage.directory.source``
---------------------------------------
This endpoint tells Firefox where to download directory source file as a GET
request. It should return JSON of the appropriate format containing the relevant
links data. The value can be a data URI, e.g., an empty JSON object effectively
turns off remote downloading: ``data:text/plain,{}``
The preference value will have %LOCALE% and %CHANNEL% replaced by the
appropriate values for the build of Firefox, e.g.,
- directory source endpoint:
https://tiles.services.mozilla.com/v3/links/fetch/en-US/release
Data Flow
=========
When Firefox starts, it checks for a cached directory source file. If one
exists, it checks for its timestamp to determine if a new file should be
downloaded.
If a directory source file needs to be downloaded, a GET request is made then
cached and unpacked the JSON into the different types of links. Various checks
filter out invalid links, e.g., those with http-hosted images or those that
don't fit the allowed suggestions.
When a new tab page is built, DirectoryLinksProvider module provides additional
link data that is combined with history link data to determine which links can
be displayed or not.
As the new tab page is rendered, any images for tiles are downloaded if not
already cached. The default servers hosting the images are Mozilla CDN that
don't use cookies: https://tiles.cdn.mozilla.net/ and Firefox enforces that the
images come from mozilla.net or data URIs when using the default directory
source.
Source JSON Format
==================
Firefox expects links data in a JSON object with a top level "directory" key
providing an array of tile objects.
Example
-------
Below is an example directory source file::
{
"directory": [
{
"bgColor": "",
"directoryId": 498,
"enhancedImageURI": "https://tiles.cdn.mozilla.net/images/d11ba0b3095bb19d8092cd29be9cbb9e197671ea.28088.png",
"imageURI": "https://tiles.cdn.mozilla.net/images/1332a68badf11e3f7f69bf7364e79c0a7e2753bc.5316.png",
"title": "Mozilla Community",
"type": "affiliate",
"url": "http://contribute.mozilla.org/"
}
]
}
Link Object
-----------
Each link object has various values that Firefox uses to display a tile:
- ``url`` - string url for the page to be loaded when the tile is clicked. Only
https and http URLs are allowed.
- ``title`` - string that appears below the tile.
- ``type`` - string relationship of the link to Mozilla. Expected values:
affiliate.
- ``imageURI`` - string url for the tile image to show. Only https and data URIs
are allowed.
- ``enhancedImageURI`` - string url for the image to be shown before the user
hovers. Only https and data URIs are allowed.
- ``bgColor`` - string css color for additional fill background color.
- ``directoryId`` - id of the tile to be used during ping reporting

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

@ -7,6 +7,5 @@ This is the nascent documentation of the Firefox front-end code.
.. toctree::
:maxdepth: 1
DirectoryLinksProvider
UITelemetry
BrowserUsageTelemetry

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

@ -3,11 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var MAIN_MESSAGE_TYPE = "ActivityStream:Main";
var CONTENT_MESSAGE_TYPE = "ActivityStream:Content";
var PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser";
var UI_CODE = 1;
var BACKGROUND_PROCESS = 2;
this.MAIN_MESSAGE_TYPE = "ActivityStream:Main";
this.CONTENT_MESSAGE_TYPE = "ActivityStream:Content";
this.PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser";
this.UI_CODE = 1;
this.BACKGROUND_PROCESS = 2;
/**
* globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process?
@ -88,6 +88,8 @@ for (const type of [
"TOP_SITES_PIN",
"TOP_SITES_UNPIN",
"TOP_SITES_UPDATED",
"TOTAL_BOOKMARKS_REQUEST",
"TOTAL_BOOKMARKS_RESPONSE",
"UNINIT",
"WEBEXT_CLICK",
"WEBEXT_DISMISS"
@ -277,7 +279,7 @@ function WebExtEvent(type, data, importContext = globalImportContext) {
this.actionTypes = actionTypes;
var actionCreators = {
this.actionCreators = {
BroadcastToContent,
UserEvent,
UndesiredEvent,
@ -293,7 +295,7 @@ var actionCreators = {
};
// These are helpers to test for certain kinds of actions
var actionUtils = {
this.actionUtils = {
isSendToMain(action) {
if (!action.meta) {
return false;
@ -338,7 +340,7 @@ var actionUtils = {
_RouteMessage
};
var EXPORTED_SYMBOLS = [
const EXPORTED_SYMBOLS = [
"actionTypes",
"actionCreators",
"actionUtils",

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

@ -1,4 +1,4 @@
var Dedupe = class Dedupe {
this.Dedupe = class Dedupe {
constructor(createKey) {
this.createKey = createKey || this.defaultCreateKey;
}
@ -31,4 +31,4 @@ var Dedupe = class Dedupe {
}
};
var EXPORTED_SYMBOLS = ["Dedupe"];
const EXPORTED_SYMBOLS = ["Dedupe"];

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

@ -121,5 +121,5 @@ _PerfService.prototype = {
}
};
var perfService = new _PerfService();
var EXPORTED_SYMBOLS = ["_PerfService", "perfService"];
this.perfService = new _PerfService();
const EXPORTED_SYMBOLS = ["_PerfService", "perfService"];

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

@ -46,7 +46,7 @@ class _PrerenderData {
}
}
var PrerenderData = new _PrerenderData({
this.PrerenderData = new _PrerenderData({
initialPrefs: {
"migrationExpired": true,
"showTopSites": true,
@ -96,4 +96,4 @@ var PrerenderData = new _PrerenderData({
});
this._PrerenderData = _PrerenderData;
var EXPORTED_SYMBOLS = ["PrerenderData", "_PrerenderData"];
const EXPORTED_SYMBOLS = ["PrerenderData", "_PrerenderData"];

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

@ -207,6 +207,19 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
// If the action is updating rows, we should consider initialized to be true.
// This can be overridden if initialized is defined in the action.data
const initialized = action.data.rows ? {initialized: true} : {};
// Make sure pinned cards stay at their current position when rows are updated.
// Disabling a section (SECTION_UPDATE with empty rows) does not retain pinned cards.
if (action.data.rows && action.data.rows.length > 0 && section.rows.find(card => card.pinned)) {
const rows = Array.from(action.data.rows);
section.rows.forEach((card, index) => {
if (card.pinned) {
rows.splice(index, 0, card);
}
});
return Object.assign({}, section, initialized, Object.assign({}, action.data, {rows}));
}
return Object.assign({}, section, initialized, action.data);
}
return section;
@ -273,6 +286,7 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
rows: section.rows.map(item => {
if (item.url === action.data.url) {
return Object.assign({}, item, {
open_url: action.data.open_url,
pocket_id: action.data.pocket_id,
title: action.data.title,
type: "pocket"
@ -342,6 +356,6 @@ this.INITIAL_STATE = INITIAL_STATE;
this.TOP_SITES_DEFAULT_ROWS = TOP_SITES_DEFAULT_ROWS;
this.TOP_SITES_MAX_SITES_PER_ROW = TOP_SITES_MAX_SITES_PER_ROW;
var reducers = {TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane};
this.reducers = {TopSites, App, Snippets, Prefs, Dialog, Sections, PreferencesPane};
var EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_ROWS", "TOP_SITES_MAX_SITES_PER_ROW"];
const EXPORTED_SYMBOLS = ["reducers", "INITIAL_STATE", "insertPinned", "TOP_SITES_DEFAULT_ROWS", "TOP_SITES_MAX_SITES_PER_ROW"];

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

@ -1196,7 +1196,7 @@ main {
background: url("chrome://browser/skin/page-action.svg") no-repeat right center;
border: 0;
cursor: pointer;
fill: #D7D7DB;
fill: #737373;
height: 27px;
offset-inline-end: 0;
opacity: 0;
@ -1206,21 +1206,22 @@ main {
transition-property: opacity;
width: 27px; }
.collapsible-section .section-top-bar .context-menu-button:-moz-any(:active, :focus, :hover) {
fill: rgba(12, 12, 13, 0.8); }
fill: #0C0C0D; }
.collapsible-section .section-top-bar .context-menu {
top: 16px; }
@media (max-width: 1458px) {
.collapsible-section .section-top-bar .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
.collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button {
opacity: 1; }
.collapsible-section.active {
background: rgba(237, 237, 240, 0.2); }
background: rgba(237, 237, 240, 0.6);
border-radius: 4px; }
.collapsible-section.active .section-top-bar .context-menu-button {
fill: rgba(12, 12, 13, 0.8); }
@media (max-width: 1458px) {
.collapsible-section .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
fill: #0C0C0D; }
.collapsible-section .section-disclaimer {
color: #4A4A4F;
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -1196,7 +1196,7 @@ main {
background: url("chrome://browser/skin/page-action.svg") no-repeat right center;
border: 0;
cursor: pointer;
fill: #D7D7DB;
fill: #737373;
height: 27px;
offset-inline-end: 0;
opacity: 0;
@ -1206,21 +1206,22 @@ main {
transition-property: opacity;
width: 27px; }
.collapsible-section .section-top-bar .context-menu-button:-moz-any(:active, :focus, :hover) {
fill: rgba(12, 12, 13, 0.8); }
fill: #0C0C0D; }
.collapsible-section .section-top-bar .context-menu {
top: 16px; }
@media (max-width: 1458px) {
.collapsible-section .section-top-bar .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
.collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button {
opacity: 1; }
.collapsible-section.active {
background: rgba(237, 237, 240, 0.2); }
background: rgba(237, 237, 240, 0.6);
border-radius: 4px; }
.collapsible-section.active .section-top-bar .context-menu-button {
fill: rgba(12, 12, 13, 0.8); }
@media (max-width: 1458px) {
.collapsible-section .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
fill: #0C0C0D; }
.collapsible-section .section-disclaimer {
color: #4A4A4F;
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -1196,7 +1196,7 @@ main {
background: url("chrome://browser/skin/page-action.svg") no-repeat right center;
border: 0;
cursor: pointer;
fill: #D7D7DB;
fill: #737373;
height: 27px;
offset-inline-end: 0;
opacity: 0;
@ -1206,21 +1206,22 @@ main {
transition-property: opacity;
width: 27px; }
.collapsible-section .section-top-bar .context-menu-button:-moz-any(:active, :focus, :hover) {
fill: rgba(12, 12, 13, 0.8); }
fill: #0C0C0D; }
.collapsible-section .section-top-bar .context-menu {
top: 16px; }
@media (max-width: 1458px) {
.collapsible-section .section-top-bar .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
.collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button {
opacity: 1; }
.collapsible-section.active {
background: rgba(237, 237, 240, 0.2); }
background: rgba(237, 237, 240, 0.6);
border-radius: 4px; }
.collapsible-section.active .section-top-bar .context-menu-button {
fill: rgba(12, 12, 13, 0.8); }
@media (max-width: 1458px) {
.collapsible-section .context-menu {
margin-inline-end: 5px;
margin-inline-start: auto;
offset-inline-end: 0;
offset-inline-start: auto; } }
fill: #0C0C0D; }
.collapsible-section .section-disclaimer {
color: #4A4A4F;
font-size: 13px;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -106,7 +106,7 @@ const actionTypes = {};
/* harmony export (immutable) */ __webpack_exports__["b"] = actionTypes;
for (const type of ["ARCHIVE_FROM_POCKET", "BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "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", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PLACES_SAVED_TO_POCKET", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
for (const type of ["ARCHIVE_FROM_POCKET", "BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_FROM_POCKET", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "DISABLE_ONBOARDING", "INIT", "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", "PAGE_PRERENDERED", "PLACES_BOOKMARK_ADDED", "PLACES_BOOKMARK_CHANGED", "PLACES_BOOKMARK_REMOVED", "PLACES_HISTORY_CLEARED", "PLACES_LINKS_DELETED", "PLACES_LINK_BLOCKED", "PLACES_SAVED_TO_POCKET", "PREFS_INITIAL_VALUES", "PREF_CHANGED", "RICH_ICON_MISSING", "SAVE_SESSION_PERF_DATA", "SAVE_TO_POCKET", "SCREENSHOT_UPDATED", "SECTION_DEREGISTER", "SECTION_DISABLE", "SECTION_ENABLE", "SECTION_OPTIONS_CHANGED", "SECTION_REGISTER", "SECTION_UPDATE", "SECTION_UPDATE_CARD", "SETTINGS_CLOSE", "SETTINGS_OPEN", "SET_PREF", "SHOW_FIREFOX_ACCOUNTS", "SNIPPETS_BLOCKLIST_UPDATED", "SNIPPETS_DATA", "SNIPPETS_RESET", "SNIPPET_BLOCKED", "SYSTEM_TICK", "TELEMETRY_IMPRESSION_STATS", "TELEMETRY_PERFORMANCE_EVENT", "TELEMETRY_UNDESIRED_EVENT", "TELEMETRY_USER_EVENT", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_INSERT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "TOTAL_BOOKMARKS_REQUEST", "TOTAL_BOOKMARKS_RESPONSE", "UNINIT", "WEBEXT_CLICK", "WEBEXT_DISMISS"]) {
actionTypes[type] = type;
}
@ -679,6 +679,19 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
// If the action is updating rows, we should consider initialized to be true.
// This can be overridden if initialized is defined in the action.data
const initialized = action.data.rows ? { initialized: true } : {};
// Make sure pinned cards stay at their current position when rows are updated.
// Disabling a section (SECTION_UPDATE with empty rows) does not retain pinned cards.
if (action.data.rows && action.data.rows.length > 0 && section.rows.find(card => card.pinned)) {
const rows = Array.from(action.data.rows);
section.rows.forEach((card, index) => {
if (card.pinned) {
rows.splice(index, 0, card);
}
});
return Object.assign({}, section, initialized, Object.assign({}, action.data, { rows }));
}
return Object.assign({}, section, initialized, action.data);
}
return section;
@ -745,6 +758,7 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
rows: section.rows.map(item => {
if (item.url === action.data.url) {
return Object.assign({}, item, {
open_url: action.data.open_url,
pocket_id: action.data.pocket_id,
title: action.data.title,
type: "pocket"
@ -906,6 +920,10 @@ ErrorBoundary.defaultProps = { FallbackComponent: ErrorBoundaryFallback };
// EXTERNAL MODULE: ./system-addon/common/Actions.jsm
var Actions = __webpack_require__(0);
// EXTERNAL MODULE: external "ReactRedux"
var external__ReactRedux_ = __webpack_require__(4);
var external__ReactRedux__default = /*#__PURE__*/__webpack_require__.n(external__ReactRedux_);
// EXTERNAL MODULE: ./system-addon/content-src/components/ContextMenu/ContextMenu.jsx
var ContextMenu = __webpack_require__(9);
@ -916,6 +934,16 @@ var external__ReactIntl__default = /*#__PURE__*/__webpack_require__.n(external__
// CONCATENATED MODULE: ./system-addon/content-src/lib/link-menu-options.js
const _OpenInPrivateWindow = site => ({
id: "menu_action_open_private_window",
icon: "new-window-private",
action: Actions["a" /* actionCreators */].OnlyToMain({
type: Actions["b" /* actionTypes */].OPEN_PRIVATE_WINDOW,
data: { url: site.url, referrer: site.referrer }
}),
userEvent: "OPEN_PRIVATE_WINDOW"
});
/**
* List of functions that return items that can be included as menu options in a
* LinkMenu. All functions take the site as the first parameter, and optionally
@ -951,15 +979,6 @@ const LinkMenuOptions = {
}),
userEvent: "OPEN_NEW_WINDOW"
}),
OpenInPrivateWindow: site => ({
id: "menu_action_open_private_window",
icon: "new-window-private",
action: Actions["a" /* actionCreators */].AlsoToMain({
type: Actions["b" /* actionTypes */].OPEN_PRIVATE_WINDOW,
data: { url: site.url, referrer: site.referrer }
}),
userEvent: "OPEN_PRIVATE_WINDOW"
}),
BlockUrl: (site, index, eventSource) => ({
id: "menu_action_dismiss",
icon: "dismiss",
@ -1065,7 +1084,8 @@ const LinkMenuOptions = {
CheckPinTopSite: (site, index) => site.isPinned ? LinkMenuOptions.UnpinTopSite(site) : LinkMenuOptions.PinTopSite(site, index),
CheckSavedToPocket: (site, index) => site.pocket_id ? LinkMenuOptions.DeleteFromPocket(site) : LinkMenuOptions.SaveToPocket(site, index),
CheckBookmarkOrArchive: site => site.pocket_id ? LinkMenuOptions.ArchiveFromPocket(site) : LinkMenuOptions.CheckBookmark(site),
CheckDeleteHistoryOrEmpty: (site, index, eventSource) => site.pocket_id ? LinkMenuOptions.EmptyItem() : LinkMenuOptions.DeleteUrl(site, index, eventSource)
CheckDeleteHistoryOrEmpty: (site, index, eventSource) => site.pocket_id ? LinkMenuOptions.EmptyItem() : LinkMenuOptions.DeleteUrl(site, index, eventSource),
OpenInPrivateWindow: (site, index, eventSource, isEnabled) => isEnabled ? _OpenInPrivateWindow(site) : LinkMenuOptions.EmptyItem()
};
// EXTERNAL MODULE: external "React"
var external__React_ = __webpack_require__(1);
@ -1078,17 +1098,18 @@ var external__React__default = /*#__PURE__*/__webpack_require__.n(external__Reac
const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
class LinkMenu__LinkMenu extends external__React__default.a.PureComponent {
getOptions() {
const { props } = this;
const { site, index, source } = props;
const { site, index, source, isPrivateBrowsingEnabled } = props;
// Handle special case of default site
const propOptions = !site.isDefault ? props.options : DEFAULT_SITE_MENU_OPTIONS;
const options = propOptions.map(o => LinkMenuOptions[o](site, index, source)).map(option => {
const options = propOptions.map(o => LinkMenuOptions[o](site, index, source, isPrivateBrowsingEnabled)).map(option => {
const { action, impression, id, string_id, type, userEvent } = option;
if (!type && id) {
option.label = props.intl.formatMessage({ id: string_id || id });
@ -1119,7 +1140,6 @@ class LinkMenu__LinkMenu extends external__React__default.a.PureComponent {
render() {
return external__React__default.a.createElement(ContextMenu["a" /* ContextMenu */], {
visible: this.props.visible,
onUpdate: this.props.onUpdate,
options: this.getOptions() });
}
@ -1127,7 +1147,8 @@ class LinkMenu__LinkMenu extends external__React__default.a.PureComponent {
/* unused harmony export _LinkMenu */
const LinkMenu = Object(external__ReactIntl_["injectIntl"])(LinkMenu__LinkMenu);
const getState = state => ({ isPrivateBrowsingEnabled: state.Prefs.values.isPrivateBrowsingEnabled });
const LinkMenu = Object(external__ReactRedux_["connect"])(getState)(Object(external__ReactIntl_["injectIntl"])(LinkMenu__LinkMenu));
/* harmony export (immutable) */ __webpack_exports__["a"] = LinkMenu;
@ -1136,7 +1157,7 @@ const LinkMenu = Object(external__ReactIntl_["injectIntl"])(LinkMenu__LinkMenu);
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react__ = __webpack_require__(1);
/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_react__);
@ -1150,29 +1171,20 @@ class ContextMenu extends __WEBPACK_IMPORTED_MODULE_0_react___default.a.PureComp
this.props.onUpdate(false);
}
componentWillMount() {
this.hideContext();
}
componentDidUpdate(prevProps) {
if (this.props.visible && !prevProps.visible) {
setTimeout(() => {
window.addEventListener("click", this.hideContext);
}, 0);
}
if (!this.props.visible && prevProps.visible) {
window.removeEventListener("click", this.hideContext);
}
componentDidMount() {
setTimeout(() => {
global.addEventListener("click", this.hideContext);
}, 0);
}
componentWillUnmount() {
window.removeEventListener("click", this.hideContext);
global.removeEventListener("click", this.hideContext);
}
render() {
return __WEBPACK_IMPORTED_MODULE_0_react___default.a.createElement(
"span",
{ hidden: !this.props.visible, className: "context-menu" },
{ className: "context-menu" },
__WEBPACK_IMPORTED_MODULE_0_react___default.a.createElement(
"ul",
{ role: "menu", className: "context-menu-list" },
@ -1230,6 +1242,7 @@ class ContextMenuItem extends __WEBPACK_IMPORTED_MODULE_0_react___default.a.Pure
}
/* unused harmony export ContextMenuItem */
/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(3)))
/***/ }),
/* 10 */
@ -1439,7 +1452,7 @@ class _CollapsibleSection extends __WEBPACK_IMPORTED_MODULE_3_react___default.a.
__WEBPACK_IMPORTED_MODULE_3_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_0_react_intl__["FormattedMessage"], { id: "section_context_menu_button_sr" })
)
),
__WEBPACK_IMPORTED_MODULE_3_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_4_content_src_components_SectionMenu_SectionMenu__["a" /* SectionMenu */], {
showContextMenu && __WEBPACK_IMPORTED_MODULE_3_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_4_content_src_components_SectionMenu_SectionMenu__["a" /* SectionMenu */], {
extraOptions: extraMenuOptions,
eventSource: eventSource,
showPrefName: showPrefName,
@ -1447,7 +1460,6 @@ class _CollapsibleSection extends __WEBPACK_IMPORTED_MODULE_3_react___default.a.
privacyNoticeURL: privacyNoticeURL,
isCollapsed: isCollapsed,
onUpdate: this.onMenuUpdate,
visible: showContextMenu,
dispatch: dispatch })
)
),
@ -1997,14 +2009,13 @@ class TopSite extends __WEBPACK_IMPORTED_MODULE_4_react___default.a.PureComponen
__WEBPACK_IMPORTED_MODULE_4_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_1_react_intl__["FormattedMessage"], { id: "context_menu_button_sr", values: { title } })
)
),
__WEBPACK_IMPORTED_MODULE_4_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_3_content_src_components_LinkMenu_LinkMenu__["a" /* LinkMenu */], {
isContextMenuOpen && __WEBPACK_IMPORTED_MODULE_4_react___default.a.createElement(__WEBPACK_IMPORTED_MODULE_3_content_src_components_LinkMenu_LinkMenu__["a" /* LinkMenu */], {
dispatch: props.dispatch,
index: props.index,
onUpdate: this.onMenuUpdate,
options: __WEBPACK_IMPORTED_MODULE_2__TopSitesConstants__["c" /* TOP_SITES_CONTEXT_MENU_OPTIONS */],
site: link,
source: __WEBPACK_IMPORTED_MODULE_2__TopSitesConstants__["d" /* TOP_SITES_SOURCE */],
visible: isContextMenuOpen })
source: __WEBPACK_IMPORTED_MODULE_2__TopSitesConstants__["d" /* TOP_SITES_SOURCE */] })
)
);
}
@ -2346,6 +2357,18 @@ class SnippetsMap extends Map {
this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].AlsoToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].SHOW_FIREFOX_ACCOUNTS }));
}
getTotalBookmarksCount() {
return new Promise(resolve => {
this._dispatch(__WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["a" /* actionCreators */].OnlyToMain({ type: __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].TOTAL_BOOKMARKS_REQUEST }));
global.addMessageListener("ActivityStream:MainToContent", function onMessage({ data: action }) {
if (action.type === __WEBPACK_IMPORTED_MODULE_0_common_Actions_jsm__["b" /* actionTypes */].TOTAL_BOOKMARKS_RESPONSE) {
resolve(action.data);
global.removeMessageListener("ActivityStream:MainToContent", onMessage);
}
});
});
}
/**
* connect - Attaches an indexedDB back-end to the Map so that any set values
* are also cached in a store. It also restores any existing values
@ -3776,7 +3799,7 @@ class Card_Card extends external__React__default.a.PureComponent {
{ className: `card-outer${isContextMenuOpen ? " active" : ""}${props.placeholder ? " placeholder" : ""}` },
external__React__default.a.createElement(
"a",
{ href: link.url, onClick: !props.placeholder ? this.onLinkClick : undefined },
{ href: link.type === "pocket" ? link.open_url : link.url, onClick: !props.placeholder ? this.onLinkClick : undefined },
external__React__default.a.createElement(
"div",
{ className: "card" },
@ -3836,14 +3859,13 @@ class Card_Card extends external__React__default.a.PureComponent {
`Open context menu for ${link.title}`
)
),
!props.placeholder && external__React__default.a.createElement(LinkMenu["a" /* LinkMenu */], {
isContextMenuOpen && external__React__default.a.createElement(LinkMenu["a" /* LinkMenu */], {
dispatch: dispatch,
index: index,
source: eventSource,
onUpdate: this.onMenuUpdate,
options: link.contextMenuOptions || contextMenuOptions,
site: link,
visible: isContextMenuOpen,
shouldSendImpressionStats: shouldSendImpressionStats })
);
}
@ -3976,7 +3998,6 @@ class SectionMenu__SectionMenu extends external__React__default.a.PureComponent
render() {
return external__React__default.a.createElement(ContextMenu["a" /* ContextMenu */], {
visible: this.props.visible,
onUpdate: this.props.onUpdate,
options: this.getOptions() });
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -8,7 +8,7 @@
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
<em:version>2018.02.23.1351-10fafea4</em:version>
<em:version>2018.03.01.1281-6a7c8294</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>

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

@ -5,6 +5,9 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
// NB: Eagerly load modules that will be loaded/constructed/initialized in the
// common case to avoid the overhead of wrapping and detecting lazy loading.
const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
@ -113,6 +116,11 @@ const PREFS_CONFIG = new Map([
value: true,
value_local_dev: false
}],
["telemetry.ut.events", {
title: "Enable Unified Telemetry event data collection",
value: !AppConstants.RELEASE_OR_BETA,
value_local_dev: false
}],
["telemetry.ping.endpoint", {
title: "Telemetry server endpoint",
value: "https://tiles.services.mozilla.com/v4/links/activity-stream"
@ -234,7 +242,7 @@ for (const config of FEEDS_DATA) {
PREFS_CONFIG.set(pref, config);
}
var ActivityStream = class ActivityStream {
this.ActivityStream = class ActivityStream {
/**
* constructor - Initializes an instance of ActivityStream
*
@ -339,4 +347,4 @@ var ActivityStream = class ActivityStream {
}
};
var EXPORTED_SYMBOLS = ["ActivityStream", "PREFS_CONFIG"];
const EXPORTED_SYMBOLS = ["ActivityStream", "PREFS_CONFIG"];

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

@ -21,7 +21,7 @@ const DEFAULT_OPTIONS = {
incomingMessageName: "ActivityStream:ContentToMain"
};
var ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
this.ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
/**
* ActivityStreamMessageChannel - This module connects a Redux store to a RemotePageManager in Firefox.
* Call .createChannel to start the connection, and .destroyChannel to destroy it.
@ -256,4 +256,4 @@ var ActivityStreamMessageChannel = class ActivityStreamMessageChannel {
};
this.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
var EXPORTED_SYMBOLS = ["ActivityStreamMessageChannel", "DEFAULT_OPTIONS"];
const EXPORTED_SYMBOLS = ["ActivityStreamMessageChannel", "DEFAULT_OPTIONS"];

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

@ -9,7 +9,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
const ACTIVITY_STREAM_PREF_BRANCH = "browser.newtabpage.activity-stream.";
var Prefs = class Prefs extends Preferences {
this.Prefs = class Prefs extends Preferences {
/**
* Prefs - A wrapper around Preferences that always sets the branch to
* ACTIVITY_STREAM_PREF_BRANCH
@ -39,7 +39,7 @@ var Prefs = class Prefs extends Preferences {
}
};
var DefaultPrefs = class DefaultPrefs {
this.DefaultPrefs = class DefaultPrefs {
/**
* DefaultPrefs - A helper for setting and resetting default prefs for the add-on
*
@ -103,4 +103,4 @@ var DefaultPrefs = class DefaultPrefs {
}
};
var EXPORTED_SYMBOLS = ["DefaultPrefs", "Prefs"];
const EXPORTED_SYMBOLS = ["DefaultPrefs", "Prefs"];

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

@ -21,7 +21,7 @@ const ONE_DAY = 24 * 60 * 60 * 1000;
const TIPPYTOP_UPDATE_TIME = ONE_DAY;
const TIPPYTOP_RETRY_DELAY = FIVE_MINUTES;
var FaviconFeed = class FaviconFeed {
this.FaviconFeed = class FaviconFeed {
constructor() {
this.tippyTopNextUpdate = 0;
this.cache = new PersistentCache("tippytop", true);
@ -124,9 +124,7 @@ var FaviconFeed = class FaviconFeed {
if (domain in sitesByDomain) {
let iconUri = Services.io.newURI(sitesByDomain[domain].image_url);
// The #tippytop is to be able to identify them for telemetry.
iconUri = iconUri.mutate()
.setRef("tippytop")
.finalize();
iconUri = iconUri.mutate().setRef("tippytop").finalize();
PlacesUtils.favicons.setAndFetchFaviconForPage(
Services.io.newURI(url),
iconUri,
@ -160,4 +158,4 @@ var FaviconFeed = class FaviconFeed {
}
};
var EXPORTED_SYMBOLS = ["FaviconFeed"];
const EXPORTED_SYMBOLS = ["FaviconFeed"];

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

@ -46,7 +46,7 @@ function filterAdult(links) {
});
}
var EXPORTED_SYMBOLS = ["filterAdult"];
const EXPORTED_SYMBOLS = ["filterAdult"];
// These are md5 hashes of base domains to be filtered out. Originally from:
// https://hg.mozilla.org/mozilla-central/log/default/browser/base/content/newtab/newTab.inadjacent.json

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

@ -27,7 +27,7 @@ const HIGHLIGHTS_MAX_LENGTH = 9;
const MANY_EXTRA_LENGTH = HIGHLIGHTS_MAX_LENGTH * 5 + TOP_SITES_DEFAULT_ROWS * TOP_SITES_MAX_SITES_PER_ROW;
const SECTION_ID = "highlights";
var HighlightsFeed = class HighlightsFeed {
this.HighlightsFeed = class HighlightsFeed {
constructor() {
this.dedupe = new Dedupe(this._dedupeKey);
this.linksCache = new LinksCache(NewTabUtils.activityStreamLinks,
@ -224,6 +224,4 @@ var HighlightsFeed = class HighlightsFeed {
}
};
var EXPORTED_SYMBOLS = ["HighlightsFeed", "SECTION_ID", "MANY_EXTRA_LENGTH"];
const EXPORTED_SYMBOLS = ["HighlightsFeed", "SECTION_ID", "MANY_EXTRA_LENGTH"];

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

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["LinksCache"];
const EXPORTED_SYMBOLS = ["LinksCache"];
// This should be slightly less than SYSTEM_TICK_INTERVAL as timer
// comparisons are too exact while the async/await functionality will make the
@ -18,7 +18,7 @@ const EXPIRATION_TIME = 4.5 * 60 * 1000; // 4.5 minutes
* amount of time has passed. Allows for migrating data from previously cached
* links to the new links with the same url.
*/
var LinksCache = class LinksCache {
this.LinksCache = class LinksCache {
/**
* Create a links cache for a given object property.
*

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

@ -14,7 +14,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "MigrationUtils", "resource:///modules/MigrationUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge", "resource://gre/modules/ProfileAge.jsm");
var ManualMigration = class ManualMigration {
this.ManualMigration = class ManualMigration {
constructor() {
Services.obs.addObserver(this, MIGRATION_ENDED_EVENT);
this._prefs = new Prefs();
@ -96,4 +96,4 @@ var ManualMigration = class ManualMigration {
}
};
var EXPORTED_SYMBOLS = ["ManualMigration"];
const EXPORTED_SYMBOLS = ["ManualMigration"];

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

@ -9,7 +9,7 @@ const {actionCreators: ac, actionTypes: at} = ChromeUtils.import("resource://act
* NewTabInit - A placeholder for now. This will send a copy of the state to all
* newly opened tabs.
*/
var NewTabInit = class NewTabInit {
this.NewTabInit = class NewTabInit {
constructor() {
this._repliedEarlyTabs = new Map();
}
@ -49,4 +49,4 @@ var NewTabInit = class NewTabInit {
}
};
var EXPORTED_SYMBOLS = ["NewTabInit"];
const EXPORTED_SYMBOLS = ["NewTabInit"];

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

@ -11,7 +11,7 @@ XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => new TextDecoder());
/**
* A file (disk) based persistent cache of a JSON serializable object.
*/
var PersistentCache = class PersistentCache {
this.PersistentCache = class PersistentCache {
/**
* Create a cache object based on a name.
*
@ -79,4 +79,4 @@ var PersistentCache = class PersistentCache {
}
};
var EXPORTED_SYMBOLS = ["PersistentCache"];
const EXPORTED_SYMBOLS = ["PersistentCache"];

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

@ -90,6 +90,7 @@ class HistoryObserver extends Observer {
class BookmarksObserver extends Observer {
constructor(dispatch) {
super(dispatch, Ci.nsINavBookmarkObserver);
this.skipTags = true;
}
/**
@ -147,48 +148,6 @@ class BookmarksObserver extends Observer {
}
}
/**
* onItemChanged - Called when a bookmark is modified
*
* @param {str} id description
* @param {str} property The property that was modified (e.g. uri, title)
* @param {bool} isAnnotation
* @param {any} value
* @param {int} lastModified
* @param {int} type Indicates if the bookmark is an actual bookmark,
* a folder, or a separator.
* @param {int} parent
* @param {str} guid The unique id of the bookmark
*/
async onItemChanged(...args) {
/*
// Disabled due to performance cost, see Issue 3203 /
// https://bugzilla.mozilla.org/show_bug.cgi?id=1392267.
//
// If this is used, please consider avoiding the call to
// NewTabUtils.activityStreamProvider.getBookmark which performs an additional
// fetch to the database.
// If you need more fields, please talk to the places team.
const property = args[1];
const type = args[5];
const guid = args[7];
// Only process this event if it is a TYPE_BOOKMARK, and uri or title was the property changed.
if (type !== PlacesUtils.bookmarks.TYPE_BOOKMARK || !["uri", "title"].includes(property)) {
return;
}
try {
// bookmark: {bookmarkGuid, bookmarkTitle, lastModified, url}
const bookmark = await NewTabUtils.activityStreamProvider.getBookmark(guid);
this.dispatch({type: at.PLACES_BOOKMARK_CHANGED, data: bookmark});
} catch (e) {
Cu.reportError(e);
}
*/
}
// Empty functions to make xpconnect happy
onBeginUpdateBatch() {}
@ -197,6 +156,10 @@ class BookmarksObserver extends Observer {
onItemVisited() {}
onItemMoved() {}
// Disabled due to performance cost, see Issue 3203 /
// https://bugzilla.mozilla.org/show_bug.cgi?id=1392267.
onItemChanged() {}
}
class PlacesFeed {
@ -257,7 +220,10 @@ class PlacesFeed {
}
const win = action._target.browser.ownerGlobal;
win.openLinkIn(action.data.url, where || win.whereToOpenLink(event), params);
// Pocket gives us a special reader URL to open their stories in
const urlToOpen = action.data.type === "pocket" ? action.data.open_url : action.data.url;
win.openLinkIn(urlToOpen, where || win.whereToOpenLink(event), params);
}
async saveToPocket(site, browser) {
@ -267,7 +233,7 @@ class PlacesFeed {
if (data) {
this.store.dispatch(ac.BroadcastToContent({
type: at.PLACES_SAVED_TO_POCKET,
data: {url, title, pocket_id: data.item.item_id}
data: {url, open_url: data.item.open_url, title, pocket_id: data.item.item_id}
}));
}
} catch (err) {
@ -326,4 +292,4 @@ this.PlacesFeed = PlacesFeed;
PlacesFeed.HistoryObserver = HistoryObserver;
PlacesFeed.BookmarksObserver = BookmarksObserver;
var EXPORTED_SYMBOLS = ["PlacesFeed"];
const EXPORTED_SYMBOLS = ["PlacesFeed"];

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

@ -8,9 +8,12 @@ const {Prefs} = ChromeUtils.import("resource://activity-stream/lib/ActivityStrea
const {PrerenderData} = ChromeUtils.import("resource://activity-stream/common/PrerenderData.jsm", {});
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
const ONBOARDING_FINISHED_PREF = "browser.onboarding.notification.finished";
var PrefsFeed = class PrefsFeed {
this.PrefsFeed = class PrefsFeed {
constructor(prefMap) {
this._prefMap = prefMap;
this._prefs = new Prefs();
@ -63,6 +66,9 @@ var PrefsFeed = class PrefsFeed {
values[name] = this._prefs.get(name);
}
// Not a pref, but we need this to determine whether to show private-browsing-related stuff
values.isPrivateBrowsingEnabled = PrivateBrowsingUtils.enabled;
// Set the initial state of all prefs in redux
this.store.dispatch(ac.BroadcastToContent({type: at.PREFS_INITIAL_VALUES, data: values}));
@ -93,4 +99,4 @@ var PrefsFeed = class PrefsFeed {
}
};
var EXPORTED_SYMBOLS = ["PrefsFeed"];
const EXPORTED_SYMBOLS = ["PrefsFeed"];

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

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["Screenshots"];
const EXPORTED_SYMBOLS = ["Screenshots"];
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
@ -24,7 +24,7 @@ ChromeUtils.defineModuleGetter(this, "Services",
const GREY_10 = "#F9F9FA";
var Screenshots = {
this.Screenshots = {
/**
* Convert bytes to a string using extremely fast String.fromCharCode without
* exceeding the max number of arguments that can be provided to a function.

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

@ -348,4 +348,4 @@ class SectionsFeed {
this.SectionsFeed = SectionsFeed;
this.SectionsManager = SectionsManager;
var EXPORTED_SYMBOLS = ["SectionsFeed", "SectionsManager"];
const EXPORTED_SYMBOLS = ["SectionsFeed", "SectionsManager"];

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

@ -33,7 +33,6 @@ function getETLD(host) {
}
}
/**
* shortURL - Creates a short version of a link's url, used for display purposes
* e.g. {url: http://www.foosite.com} => "foosite"
@ -68,4 +67,4 @@ function shortURL({url}) {
parsed.pathname || parsed.href;
}
var EXPORTED_SYMBOLS = ["shortURL", "getETLD"];
const EXPORTED_SYMBOLS = ["shortURL", "getETLD"];

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

@ -3,16 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
const {actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
ChromeUtils.defineModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "ShellService",
"resource:///modules/ShellService.jsm");
ChromeUtils.defineModuleGetter(this, "ProfileAge",
"resource://gre/modules/ProfileAge.jsm");
ChromeUtils.defineModuleGetter(this, "FxAccounts",
"resource://gre/modules/FxAccounts.jsm");
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
"resource://gre/modules/NewTabUtils.jsm");
// Url to fetch snippets, in the urlFormatter service format.
const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
@ -27,11 +30,14 @@ const SEARCH_ENGINE_OBSERVER_TOPIC = "browser-search-engine-modified";
// Should be bumped up if the snippets content format changes.
const STARTPAGE_VERSION = 5;
const ONE_WEEK = 7 * 24 * 60 * 60 * 1000;
const ONE_DAY = 24 * 60 * 60 * 1000;
const ONE_WEEK = 7 * ONE_DAY;
var SnippetsFeed = class SnippetsFeed {
this.SnippetsFeed = class SnippetsFeed {
constructor() {
this._refresh = this._refresh.bind(this);
this._totalBookmarks = null;
this._totalBookmarksLastUpdated = null;
}
get snippetsURL() {
@ -49,6 +55,10 @@ var SnippetsFeed = class SnippetsFeed {
return null;
}
isDevtoolsUser() {
return Services.prefs.getIntPref("devtools.selfxss.count") >= 5;
}
async getProfileInfo() {
const profileAge = new ProfileAge(null, null);
const createdDate = await profileAge.created;
@ -79,6 +89,39 @@ var SnippetsFeed = class SnippetsFeed {
});
}
async getAddonInfo() {
const {addons, fullData} = await AddonManager.getActiveAddons(["extension", "service"]);
const info = {};
for (const addon of addons) {
info[addon.id] = {
version: addon.version,
type: addon.type,
isSystem: addon.isSystem,
isWebExtension: addon.isWebExtension
};
if (fullData) {
Object.assign(info[addon.id], {
name: addon.name,
userDisabled: addon.userDisabled,
installDate: addon.installDate
});
}
}
return info;
}
async getTotalBookmarksCount(target) {
if (!this._totalBookmarks || (Date.now() - this._totalBookmarksLastUpdated > ONE_DAY)) {
this._totalBookmarksLastUpdated = Date.now();
try {
this._totalBookmarks = await NewTabUtils.activityStreamProvider.getTotalBookmarksCount();
} catch (e) {
Cu.reportError(e);
}
}
this.store.dispatch(ac.OnlyToOneContent({type: at.TOTAL_BOOKMARKS_RESPONSE, data: this._totalBookmarks}, target));
}
_dispatchChanges(data) {
this.store.dispatch(ac.BroadcastToContent({type: at.SNIPPETS_DATA, data}));
}
@ -94,7 +137,9 @@ var SnippetsFeed = class SnippetsFeed {
onboardingFinished: Services.prefs.getBoolPref(ONBOARDING_FINISHED_PREF),
fxaccount: Services.prefs.prefHasUserValue(FXA_USERNAME_PREF),
selectedSearchEngine: await this.getSelectedSearchEngine(),
defaultBrowser: this.isDefaultBrowser()
defaultBrowser: this.isDefaultBrowser(),
isDevtoolsUser: this.isDevtoolsUser(),
addonInfo: await this.getAddonInfo()
};
this._dispatchChanges(data);
}
@ -144,8 +189,11 @@ var SnippetsFeed = class SnippetsFeed {
case at.SNIPPETS_BLOCKLIST_UPDATED:
this.store.dispatch(ac.BroadcastToContent({type: at.SNIPPET_BLOCKED, data: action.data}));
break;
case at.TOTAL_BOOKMARKS_REQUEST:
this.getTotalBookmarksCount(action._target.browser);
break;
}
}
};
var EXPORTED_SYMBOLS = ["SnippetsFeed"];
const EXPORTED_SYMBOLS = ["SnippetsFeed"];

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

@ -15,7 +15,7 @@ const {redux} = ChromeUtils.import("resource://activity-stream/vendor/Redux.jsm"
* It also accepts an array of "Feeds" on inititalization, which
* can listen for any action that is dispatched through the store.
*/
var Store = class Store {
this.Store = class Store {
/**
* constructor - The redux store and message manager are created here,
* but no listeners are added until "init" is called.
@ -157,4 +157,4 @@ var Store = class Store {
}
};
var EXPORTED_SYMBOLS = ["Store"];
const EXPORTED_SYMBOLS = ["Store"];

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

@ -13,7 +13,7 @@ ChromeUtils.defineModuleGetter(this, "clearInterval", "resource://gre/modules/Ti
// Frequency at which SYSTEM_TICK events are fired
const SYSTEM_TICK_INTERVAL = 5 * 60 * 1000;
var SystemTickFeed = class SystemTickFeed {
this.SystemTickFeed = class SystemTickFeed {
init() {
this.intervalId = setInterval(() => this.store.dispatch({type: at.SYSTEM_TICK}), SYSTEM_TICK_INTERVAL);
}
@ -31,4 +31,4 @@ var SystemTickFeed = class SystemTickFeed {
};
this.SYSTEM_TICK_INTERVAL = SYSTEM_TICK_INTERVAL;
var EXPORTED_SYMBOLS = ["SystemTickFeed", "SYSTEM_TICK_INTERVAL"];
const EXPORTED_SYMBOLS = ["SystemTickFeed", "SYSTEM_TICK_INTERVAL"];

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

@ -37,16 +37,20 @@ const USER_PREFS_ENCODING = {
const PREF_IMPRESSION_ID = "impressionId";
const TELEMETRY_PREF = "telemetry";
const EVENTS_TELEMETRY_PREF = "telemetry.ut.events";
var TelemetryFeed = class TelemetryFeed {
this.TelemetryFeed = class TelemetryFeed {
constructor(options) {
this.sessions = new Map();
this._prefs = new Prefs();
this._impressionId = this.getOrCreateImpressionId();
this.telemetryEnabled = this._prefs.get(TELEMETRY_PREF);
this.eventTelemetryEnabled = this._prefs.get(EVENTS_TELEMETRY_PREF);
this._aboutHomeSeen = false;
this._onTelemetryPrefChange = this._onTelemetryPrefChange.bind(this);
this._prefs.observe(TELEMETRY_PREF, this._onTelemetryPrefChange);
this._onEventsTelemetryPrefChange = this._onEventsTelemetryPrefChange.bind(this);
this._prefs.observe(EVENTS_TELEMETRY_PREF, this._onEventsTelemetryPrefChange);
}
init() {
@ -105,6 +109,10 @@ var TelemetryFeed = class TelemetryFeed {
this.telemetryEnabled = prefVal;
}
_onEventsTelemetryPrefChange(prefVal) {
this.eventTelemetryEnabled = prefVal;
}
/**
* Lazily initialize PingCentre to send pings
*/
@ -229,7 +237,7 @@ var TelemetryFeed = class TelemetryFeed {
let sessionEndEvent = this.createSessionEndEvent(session);
this.sendEvent(sessionEndEvent);
this.utEvents.sendSessionEndEvent(sessionEndEvent);
this.sendUTEvent(sessionEndEvent, this.utEvents.sendSessionEndEvent);
this.sessions.delete(portID);
}
@ -351,6 +359,12 @@ var TelemetryFeed = class TelemetryFeed {
}
}
sendUTEvent(event_object, eventFunction) {
if (this.eventTelemetryEnabled) {
eventFunction(event_object);
}
}
handleImpressionStats(action) {
this.sendEvent(this.createImpressionStats(action));
}
@ -358,7 +372,7 @@ var TelemetryFeed = class TelemetryFeed {
handleUserEvent(action) {
let userEvent = this.createUserEvent(action);
this.sendEvent(userEvent);
this.utEvents.sendUserEvent(userEvent);
this.sendUTEvent(userEvent, this.utEvents.sendUserEvent);
}
handleUndesiredEvent(action) {
@ -450,6 +464,7 @@ var TelemetryFeed = class TelemetryFeed {
try {
this._prefs.ignore(TELEMETRY_PREF, this._onTelemetryPrefChange);
this._prefs.ignore(EVENTS_TELEMETRY_PREF, this._onEventsTelemetryPrefChange);
} catch (e) {
Cu.reportError(e);
}
@ -457,9 +472,10 @@ var TelemetryFeed = class TelemetryFeed {
}
};
var EXPORTED_SYMBOLS = [
const EXPORTED_SYMBOLS = [
"TelemetryFeed",
"USER_PREFS_ENCODING",
"PREF_IMPRESSION_ID",
"TELEMETRY_PREF"
"TELEMETRY_PREF",
"EVENTS_TELEMETRY_PREF"
];

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

@ -18,7 +18,7 @@ function getDomain(url) {
return domain;
}
var TippyTopProvider = class TippyTopProvider {
this.TippyTopProvider = class TippyTopProvider {
constructor() {
this._sitesByDomain = new Map();
this.initialized = false;
@ -50,4 +50,4 @@ var TippyTopProvider = class TippyTopProvider {
}
};
var EXPORTED_SYMBOLS = ["TippyTopProvider", "getDomain"];
const EXPORTED_SYMBOLS = ["TippyTopProvider", "getDomain"];

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

@ -29,7 +29,7 @@ const MIN_FAVICON_SIZE = 96;
const CACHED_LINK_PROPS_TO_MIGRATE = ["screenshot"];
const PINNED_FAVICON_PROPS_TO_MIGRATE = ["favicon", "faviconRef", "faviconSize"];
var TopSitesFeed = class TopSitesFeed {
this.TopSitesFeed = class TopSitesFeed {
constructor() {
this._tippyTopProvider = new TippyTopProvider();
this.dedupe = new Dedupe(this._dedupeKey);
@ -347,4 +347,4 @@ var TopSitesFeed = class TopSitesFeed {
};
this.DEFAULT_TOP_SITES = DEFAULT_TOP_SITES;
var EXPORTED_SYMBOLS = ["TopSitesFeed", "DEFAULT_TOP_SITES"];
const EXPORTED_SYMBOLS = ["TopSitesFeed", "DEFAULT_TOP_SITES"];

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

@ -27,7 +27,7 @@ const SPOC_IMPRESSION_TRACKING_PREF = "feeds.section.topstories.spoc.impressions
const REC_IMPRESSION_TRACKING_PREF = "feeds.section.topstories.rec.impressions";
const MAX_LIFETIME_CAP = 100; // Guard against misconfiguration on the server
var TopStoriesFeed = class TopStoriesFeed {
this.TopStoriesFeed = class TopStoriesFeed {
constructor() {
this.spocCampaignMap = new Map();
this.contentUpdateQueue = [];
@ -311,7 +311,7 @@ var TopStoriesFeed = class TopStoriesFeed {
// Create a new array with a spoc inserted at index 2
const position = SectionsManager.sections.get(SECTION_ID).order;
let rows = this.store.getState().Sections[position].rows.slice(0, this.stories.length);
rows.splice(2, 0, spocs[0]);
rows.splice(2, 0, Object.assign(spocs[0], {pinned: true}));
// Send a content update to the target tab
const action = {type: at.SECTION_UPDATE, data: Object.assign({rows}, {id: SECTION_ID})};
@ -506,4 +506,4 @@ this.SPOC_IMPRESSION_TRACKING_PREF = SPOC_IMPRESSION_TRACKING_PREF;
this.REC_IMPRESSION_TRACKING_PREF = REC_IMPRESSION_TRACKING_PREF;
this.MIN_DOMAIN_AFFINITIES_UPDATE_TIME = MIN_DOMAIN_AFFINITIES_UPDATE_TIME;
this.DEFAULT_RECS_EXPIRE_TIME = DEFAULT_RECS_EXPIRE_TIME;
var EXPORTED_SYMBOLS = ["TopStoriesFeed", "STORIES_UPDATE_TIME", "TOPICS_UPDATE_TIME", "SECTION_ID", "SPOC_IMPRESSION_TRACKING_PREF", "MIN_DOMAIN_AFFINITIES_UPDATE_TIME", "REC_IMPRESSION_TRACKING_PREF", "DEFAULT_RECS_EXPIRE_TIME"];
const EXPORTED_SYMBOLS = ["TopStoriesFeed", "STORIES_UPDATE_TIME", "TOPICS_UPDATE_TIME", "SECTION_ID", "SPOC_IMPRESSION_TRACKING_PREF", "MIN_DOMAIN_AFFINITIES_UPDATE_TIME", "REC_IMPRESSION_TRACKING_PREF", "DEFAULT_RECS_EXPIRE_TIME"];

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

@ -13,7 +13,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
*/
const EXTRAS_FIELD_NAMES = ["addon_version", "session_id", "page", "user_prefs", "action_position"];
var UTEventReporting = class UTEventReporting {
this.UTEventReporting = class UTEventReporting {
constructor() {
Services.telemetry.setEventRecordingEnabled("activity_stream", true);
}
@ -56,4 +56,4 @@ var UTEventReporting = class UTEventReporting {
}
};
var EXPORTED_SYMBOLS = ["UTEventReporting"];
const EXPORTED_SYMBOLS = ["UTEventReporting"];

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

@ -72,7 +72,7 @@ function merge(...args) {
* needs to be calculated every time the feed updates. Therefore allowing cache
* lookups of scores[domain][parameterSet] is beneficial
*/
var UserDomainAffinityProvider = class UserDomainAffinityProvider {
this.UserDomainAffinityProvider = class UserDomainAffinityProvider {
constructor(
timeSegments = DEFAULT_TIME_SEGMENTS,
parameterSets = DEFAULT_PARAMETER_SETS,
@ -326,4 +326,4 @@ var UserDomainAffinityProvider = class UserDomainAffinityProvider {
}
};
var EXPORTED_SYMBOLS = ["UserDomainAffinityProvider"];
const EXPORTED_SYMBOLS = ["UserDomainAffinityProvider"];

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -16,7 +16,7 @@ window.gActivityStreamStrings = {
"type_label_bookmarked": "У закладках",
"type_label_synced": "Сінхранізаванае з іншай прылады",
"type_label_recommended": "Тэндэнцыі",
"type_label_pocket": "Saved to Pocket",
"type_label_pocket": "Захавана ў Pocket",
"type_label_open": "Адкрыта",
"type_label_topic": "Тэма",
"type_label_now": "Зараз",
@ -33,7 +33,7 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Вы сапраўды жадаеце выдаліць усе запісы аб гэтай старонцы з гісторыі?",
"confirm_history_delete_notice_p2": "Гэта дзеянне немагчыма адмяніць.",
"menu_action_save_to_pocket": "Захаваць у Pocket",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_delete_pocket": "Выдаліць з Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"search_for_something_with": "Шукаць {search_term} у:",
"search_button": "Шукаць",
@ -85,7 +85,7 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "Дадаць",
"topsites_form_add_header": "Новы папулярны сайт",
"topsites_form_edit_header": "Рэдагаваць папулярны сайт",
"topsites_form_title_label": "Title",
"topsites_form_title_label": "Загаловак",
"topsites_form_title_placeholder": "Увядзіце назву",
"topsites_form_url_label": "URL",
"topsites_form_url_placeholder": "Увядзіце або ўстаўце URL",
@ -111,6 +111,5 @@ window.gActivityStreamStrings = {
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_move_up": "Move Up",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"edit_topsites_add_button_tooltip": "Дадаць папулярны сайт"
"section_menu_action_privacy_notice": "Паведамленне аб прыватнасці"
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -11,12 +11,12 @@ window.gActivityStreamStrings = {
"header_bookmarks_placeholder": "Zatím nemáte uložené žádné záložky.",
"header_stories_from": "ze šlužby",
"context_menu_button_sr": "Otevřít kontextovou nabídku pro {title}",
"section_context_menu_button_sr": "Open the section context menu",
"section_context_menu_button_sr": "Otevřít kontextovou nabídku sekce",
"type_label_visited": "Navštívené",
"type_label_bookmarked": "V záložkách",
"type_label_synced": "Synchronizované z jiného zařízení",
"type_label_recommended": "Populární",
"type_label_pocket": "Saved to Pocket",
"type_label_pocket": "Uloženo do služby Pocket",
"type_label_open": "Otevřené",
"type_label_topic": "Téma",
"type_label_now": "Teď",
@ -33,8 +33,8 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Opravdu chcete smazat všechny výskyty této stránky z vaší historie?",
"confirm_history_delete_notice_p2": "Tuto akci nelze vzít zpět.",
"menu_action_save_to_pocket": "Uložit do služby Pocket",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"menu_action_delete_pocket": "Smazat ze služby Pocket",
"menu_action_archive_pocket": "Archivovat do služby Pocket",
"search_for_something_with": "Vyhledat {search_term} pomocí:",
"search_button": "Vyhledat",
"search_header": "Vyhledat pomocí {search_engine_name}",
@ -85,7 +85,7 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "Přidat",
"topsites_form_add_header": "Nová top stránka",
"topsites_form_edit_header": "Upravit top stránku",
"topsites_form_title_label": "Title",
"topsites_form_title_label": "Nadpis",
"topsites_form_title_placeholder": "Zadejte název",
"topsites_form_url_label": "URL",
"topsites_form_url_placeholder": "Zadejte nebo vložte URL adresu",
@ -102,15 +102,14 @@ window.gActivityStreamStrings = {
"manual_migration_explanation2": "Vyzkoušejte Firefox se záložkami, historií a hesly z jiného vašeho prohlížeče.",
"manual_migration_cancel_button": "Ne, děkuji",
"manual_migration_import_button": "Importovat nyní",
"error_fallback_default_info": "Oops, something went wrong loading this content.",
"error_fallback_default_refresh_suggestion": "Refresh page to try again.",
"section_menu_action_remove_section": "Remove Section",
"section_menu_action_collapse_section": "Collapse Section",
"section_menu_action_expand_section": "Expand Section",
"section_menu_action_manage_section": "Manage Section",
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_move_up": "Move Up",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"edit_topsites_add_button_tooltip": "Přidat top stránku"
"error_fallback_default_info": "Jejda, při načítání tohoto obsahu se něco pokazilo.",
"error_fallback_default_refresh_suggestion": "Obnovte prosím stránku a zkuste to znovu.",
"section_menu_action_remove_section": "Odebrat sekci",
"section_menu_action_collapse_section": "Sbalit sekci",
"section_menu_action_expand_section": "Rozbalit sekci",
"section_menu_action_manage_section": "Nastavení sekce",
"section_menu_action_add_topsite": "Přidat mezi top stránky",
"section_menu_action_move_up": "Posunout nahoru",
"section_menu_action_move_down": "Posunout dolů",
"section_menu_action_privacy_notice": "Zásady ochrany soukromí"
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -11,12 +11,12 @@ window.gActivityStreamStrings = {
"header_bookmarks_placeholder": "Nid oes gennych unrhyw nodau tudalen eto.",
"header_stories_from": "oddi wrth",
"context_menu_button_sr": "Agor dewislen cynnwys {title}",
"section_context_menu_button_sr": "Open the section context menu",
"section_context_menu_button_sr": "Agor dewislen cyd-destun yr adran",
"type_label_visited": "Ymwelwyd",
"type_label_bookmarked": "Nod Tudalen",
"type_label_synced": "Cydweddwyd o ddyfais arall",
"type_label_recommended": "Trendio",
"type_label_pocket": "Saved to Pocket",
"type_label_pocket": "Wedi ei gadw i Pocket",
"type_label_open": "Ar Agor",
"type_label_topic": "Pwnc",
"type_label_now": "Nawr",
@ -33,8 +33,8 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Ydych chi'n siŵr eich bod chi am ddileu pob enghraifft o'r dudalen hon o'ch hanes?",
"confirm_history_delete_notice_p2": "Nid oes modd dadwneud hyn.",
"menu_action_save_to_pocket": "Cadw i Pocket",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"menu_action_delete_pocket": "Dileu o Pocket",
"menu_action_archive_pocket": "Archifo i Pocket",
"search_for_something_with": "Chwilio am {search_term} gyda:",
"search_button": "Chwilio",
"search_header": "{search_engine_name} Chwilio",
@ -85,7 +85,7 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "Ychwanegu",
"topsites_form_add_header": "Hoff Wefan Newydd",
"topsites_form_edit_header": "Golygu'r Hoff Wefan",
"topsites_form_title_label": "Title",
"topsites_form_title_label": "Teitl",
"topsites_form_title_placeholder": "Rhoi teitl",
"topsites_form_url_label": "URL",
"topsites_form_url_placeholder": "Teipio neu ludo URL",
@ -102,15 +102,14 @@ window.gActivityStreamStrings = {
"manual_migration_explanation2": "Profwch Firefox gyda nodau tudalen, hanes a chyfrineiriau o borwr arall.",
"manual_migration_cancel_button": "Dim Diolch",
"manual_migration_import_button": "Mewnforio Nawr",
"error_fallback_default_info": "Oops, something went wrong loading this content.",
"error_fallback_default_refresh_suggestion": "Refresh page to try again.",
"section_menu_action_remove_section": "Remove Section",
"section_menu_action_collapse_section": "Collapse Section",
"section_menu_action_expand_section": "Expand Section",
"section_menu_action_manage_section": "Manage Section",
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_move_up": "Move Up",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"edit_topsites_add_button_tooltip": "Ychwanegu Prif Wefan"
"error_fallback_default_info": "Wps, aeth rhywbeth o'i le wrth llwytho'r cynnwys hwn.",
"error_fallback_default_refresh_suggestion": "Adnewyddu'r dudalen i geisio eto.",
"section_menu_action_remove_section": "Tynnu'r Adran",
"section_menu_action_collapse_section": "Cau'r Adran",
"section_menu_action_expand_section": "Estyn yr Adran",
"section_menu_action_manage_section": "Rheoli'r Adran",
"section_menu_action_add_topsite": "Ychwanegu Hoff Wefan",
"section_menu_action_move_up": "Symud i Fyny",
"section_menu_action_move_down": "Symud i Lawr",
"section_menu_action_privacy_notice": "Hysbysiad Preifatrwydd"
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -11,12 +11,12 @@ window.gActivityStreamStrings = {
"header_bookmarks_placeholder": "Sie haben noch keine Lesezeichen.",
"header_stories_from": "von",
"context_menu_button_sr": "Kontextmenü für {title} öffnen",
"section_context_menu_button_sr": "Open the section context menu",
"section_context_menu_button_sr": "Kontextmenü des Abschnitts öffnen",
"type_label_visited": "Besucht",
"type_label_bookmarked": "Lesezeichen",
"type_label_synced": "Von anderem Gerät synchronisiert",
"type_label_recommended": "Beliebt",
"type_label_pocket": "Saved to Pocket",
"type_label_pocket": "Bei Pocket gespeichert",
"type_label_open": "Geöffnet",
"type_label_topic": "Thema",
"type_label_now": "Jetzt",
@ -33,8 +33,8 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Soll wirklich jede Instanz dieser Seite aus Ihrer Chronik gelöscht werden?",
"confirm_history_delete_notice_p2": "Diese Aktion kann nicht rückgängig gemacht werden.",
"menu_action_save_to_pocket": "Bei Pocket speichern",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"menu_action_delete_pocket": "Aus Pocket löschen",
"menu_action_archive_pocket": "In Pocket archivieren",
"search_for_something_with": "Nach {search_term} suchen mit:",
"search_button": "Suchen",
"search_header": "{search_engine_name}-Suche",
@ -85,9 +85,9 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "Hinzufügen",
"topsites_form_add_header": "Neue wichtige Seite",
"topsites_form_edit_header": "Wichtige Seite bearbeiten",
"topsites_form_title_label": "Title",
"topsites_form_title_label": "Titel",
"topsites_form_title_placeholder": "Name eingeben",
"topsites_form_url_label": "URL",
"topsites_form_url_label": "Adresse",
"topsites_form_url_placeholder": "Eine Adresse eingeben oder einfügen",
"topsites_form_add_button": "Hinzufügen",
"topsites_form_save_button": "Speichern",
@ -102,15 +102,14 @@ window.gActivityStreamStrings = {
"manual_migration_explanation2": "Probieren Sie Firefox aus und importieren Sie die Lesezeichen, Chronik und Passwörter eines anderen Browsers.",
"manual_migration_cancel_button": "Nein, danke",
"manual_migration_import_button": "Jetzt importieren",
"error_fallback_default_info": "Oops, something went wrong loading this content.",
"error_fallback_default_refresh_suggestion": "Refresh page to try again.",
"section_menu_action_remove_section": "Remove Section",
"section_menu_action_collapse_section": "Collapse Section",
"section_menu_action_expand_section": "Expand Section",
"section_menu_action_manage_section": "Manage Section",
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_move_up": "Move Up",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"edit_topsites_add_button_tooltip": "Wichtige Seite hinzufügen"
"error_fallback_default_info": "Beim Laden dieses Inhalts ist ein Fehler aufgetreten.",
"error_fallback_default_refresh_suggestion": "Aktualisieren Sie die Seite, um es erneut zu versuchen.",
"section_menu_action_remove_section": "Abschnitt entfernen",
"section_menu_action_collapse_section": "Abschnitt einklappen",
"section_menu_action_expand_section": "Abschnitt ausklappen",
"section_menu_action_manage_section": "Abschnitt verwalten",
"section_menu_action_add_topsite": "Wichtige Seite hinzufügen",
"section_menu_action_move_up": "Nach oben schieben",
"section_menu_action_move_down": "Nach unten schieben",
"section_menu_action_privacy_notice": "Datenschutzhinweis"
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -11,12 +11,12 @@ window.gActivityStreamStrings = {
"header_bookmarks_placeholder": "Hyšći cytańske znamjenja njamaśo.",
"header_stories_from": "wót",
"context_menu_button_sr": "Kontekstowy meni za {title} wócyniś",
"section_context_menu_button_sr": "Open the section context menu",
"section_context_menu_button_sr": "Kontekstowy meni wótrězka wócyniś",
"type_label_visited": "Woglědany",
"type_label_bookmarked": "Ako cytańske znamje skłaźony",
"type_label_synced": "Z drugego rěda synchronizěrowany",
"type_label_recommended": "Popularny",
"type_label_pocket": "Saved to Pocket",
"type_label_pocket": "Do Pocket skłaźony",
"type_label_open": "Wócynjony",
"type_label_topic": "Tema",
"type_label_now": "Něnto",
@ -33,8 +33,8 @@ window.gActivityStreamStrings = {
"confirm_history_delete_p1": "Cośo napšawdu kuždu instancu toś togo boka ze swójeje historije lašowaś?",
"confirm_history_delete_notice_p2": "Toś ta akcija njedajo se anulěrowaś.",
"menu_action_save_to_pocket": "Pla Pocket składowaś",
"menu_action_delete_pocket": "Delete from Pocket",
"menu_action_archive_pocket": "Archive in Pocket",
"menu_action_delete_pocket": "Z Pocket wulašowaś",
"menu_action_archive_pocket": "W Pocket archiwěrowaś",
"search_for_something_with": "Za {search_term} pytaś z:",
"search_button": "Pytaś",
"search_header": "Z {search_engine_name} pytaś",
@ -85,7 +85,7 @@ window.gActivityStreamStrings = {
"edit_topsites_add_button": "Pśidaś",
"topsites_form_add_header": "Nowe nejcesćej woglědane sedło",
"topsites_form_edit_header": "Nejcesćej woglědane sedło wobźěłaś",
"topsites_form_title_label": "Title",
"topsites_form_title_label": "Titel",
"topsites_form_title_placeholder": "Titel zapódaś",
"topsites_form_url_label": "URL",
"topsites_form_url_placeholder": "URL zapódaś abo zasajźiś",
@ -102,15 +102,14 @@ window.gActivityStreamStrings = {
"manual_migration_explanation2": "Wopytajśo Firefox z cytanskimi znamjenjami, historiju a gronidłami z drugego wobglědowaka.",
"manual_migration_cancel_button": "Ně, źěkujom se",
"manual_migration_import_button": "Něnto importěrowaś",
"error_fallback_default_info": "Oops, something went wrong loading this content.",
"error_fallback_default_refresh_suggestion": "Refresh page to try again.",
"section_menu_action_remove_section": "Remove Section",
"section_menu_action_collapse_section": "Collapse Section",
"section_menu_action_expand_section": "Expand Section",
"section_menu_action_manage_section": "Manage Section",
"section_menu_action_add_topsite": "Add Top Site",
"section_menu_action_move_up": "Move Up",
"section_menu_action_move_down": "Move Down",
"section_menu_action_privacy_notice": "Privacy Notice",
"edit_topsites_add_button_tooltip": "Woblubowane sedło pśidaś"
"error_fallback_default_info": "Hopla, pśi cytanju toś togo wopśimjeśa njejo se něco raźiło.",
"error_fallback_default_refresh_suggestion": "Aktualizěrujśo bok, aby hyšći raz wopytał.",
"section_menu_action_remove_section": "Wótrězk wótwónoźeś",
"section_menu_action_collapse_section": "Wótrězk schowaś",
"section_menu_action_expand_section": "Wótrězk pokazaś",
"section_menu_action_manage_section": "Wótrězk zastojaś",
"section_menu_action_add_topsite": "Woblubowane sedło pśidaś",
"section_menu_action_move_up": "Górjej",
"section_menu_action_move_down": "Dołoj",
"section_menu_action_privacy_notice": "Powěźeńka priwatnosći"
};

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше