зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound r=merge a=merge on a CLOSED TREE
This commit is contained in:
Коммит
e2b89c47ae
|
@ -6609,7 +6609,7 @@ var CanvasPermissionPromptHelper = {
|
|||
let message = gNavigatorBundle.getFormattedString("canvas.siteprompt", [ uri.asciiHost ]);
|
||||
|
||||
function setCanvasPermission(aURI, aPerm, aPersistent) {
|
||||
Services.perms.add(aURI, "canvas/extractData", aPerm,
|
||||
Services.perms.add(aURI, "canvas", aPerm,
|
||||
aPersistent ? Ci.nsIPermissionManager.EXPIRE_NEVER
|
||||
: Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
}
|
||||
|
|
|
@ -815,6 +815,8 @@
|
|||
tooltiptext="&urlbar.persistentStorageBlocked.tooltip;"/>
|
||||
<image data-permission-id="popup" class="blocked-permission-icon popup-icon" role="button"
|
||||
tooltiptext="&urlbar.popupBlocked.tooltip;"/>
|
||||
<image data-permission-id="canvas" class="blocked-permission-icon canvas-icon" role="button"
|
||||
tooltiptext="&urlbar.canvasBlocked.tooltip;"/>
|
||||
</box>
|
||||
<box id="notification-popup-box"
|
||||
hidden="true"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
const kUrl = "https://example.com/";
|
||||
const kPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(Services.io.newURI(kUrl), {});
|
||||
const kPermission = "canvas/extractData";
|
||||
const kPermission = "canvas";
|
||||
|
||||
function initTab() {
|
||||
let contentWindow = content.wrappedJSObject;
|
||||
|
|
|
@ -1053,9 +1053,11 @@ this.PanelMultiView = class {
|
|||
let keyCode = event.code;
|
||||
switch (keyCode) {
|
||||
case "ArrowDown":
|
||||
case "ArrowUp": {
|
||||
case "ArrowUp":
|
||||
case "Tab": {
|
||||
stop();
|
||||
let isDown = (keyCode == "ArrowDown");
|
||||
let isDown = (keyCode == "ArrowDown") ||
|
||||
(keyCode == "Tab" && !event.shiftKey);
|
||||
let button = this._updateSelectedKeyNav(navMap, buttons, isDown);
|
||||
button.focus();
|
||||
break;
|
||||
|
|
|
@ -142,3 +142,97 @@ add_task(async function testLeftRightKeys() {
|
|||
await promise;
|
||||
});
|
||||
|
||||
add_task(async function testTabKey() {
|
||||
let promise = promisePanelShown(window);
|
||||
PanelUI.show();
|
||||
await promise;
|
||||
|
||||
let buttons = gHelperInstance._getNavigableElements(PanelUI.mainView);
|
||||
|
||||
for (let button of buttons) {
|
||||
if (button.disabled)
|
||||
continue;
|
||||
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
|
||||
Assert.equal(document.commandDispatcher.focusedElement, button,
|
||||
"The correct button should be focused after tabbing");
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
|
||||
Assert.equal(document.commandDispatcher.focusedElement, buttons[0],
|
||||
"Pressing tab should cycle around and select the first button again");
|
||||
|
||||
for (let i = buttons.length - 1; i >= 0; --i) {
|
||||
let button = buttons[i];
|
||||
if (button.disabled)
|
||||
continue;
|
||||
EventUtils.synthesizeKey("Tab", { code: "Tab", shiftKey: true });
|
||||
Assert.equal(document.commandDispatcher.focusedElement, button,
|
||||
"The correct button should be focused after shift + tabbing");
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab", shiftKey: true });
|
||||
Assert.equal(document.commandDispatcher.focusedElement, buttons[buttons.length - 1],
|
||||
"Pressing shift + tab should cycle around and select the last button again");
|
||||
|
||||
promise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
await promise;
|
||||
});
|
||||
|
||||
add_task(async function testInterleavedTabAndArrowKeys() {
|
||||
let promise = promisePanelShown(window);
|
||||
PanelUI.show();
|
||||
await promise;
|
||||
|
||||
let buttons = gHelperInstance._getNavigableElements(PanelUI.mainView);
|
||||
let tab = false;
|
||||
|
||||
for (let button of buttons) {
|
||||
if (button.disabled)
|
||||
continue;
|
||||
if (tab) {
|
||||
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
|
||||
} else {
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
|
||||
}
|
||||
tab = !tab;
|
||||
}
|
||||
|
||||
Assert.equal(document.commandDispatcher.focusedElement, buttons[buttons.length - 1],
|
||||
"The last button should be focused after a mix of Tab and ArrowDown");
|
||||
|
||||
promise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
await promise;
|
||||
});
|
||||
|
||||
add_task(async function testSpaceDownAfterTabNavigation() {
|
||||
let promise = promisePanelShown(window);
|
||||
PanelUI.show();
|
||||
await promise;
|
||||
|
||||
let buttons = gHelperInstance._getNavigableElements(PanelUI.mainView);
|
||||
let button;
|
||||
|
||||
for (button of buttons) {
|
||||
if (button.disabled)
|
||||
continue;
|
||||
EventUtils.synthesizeKey("KEY_Tab", { code: "Tab" });
|
||||
if (button.id == kHelpButtonId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.equal(document.commandDispatcher.focusedElement, button,
|
||||
"Help button should be focused after tabbing to it.");
|
||||
|
||||
// Pressing down space on a button that points to a subview should navigate us
|
||||
// there, before keyup.
|
||||
promise = BrowserTestUtils.waitForEvent(PanelUI.helpView, "ViewShown");
|
||||
EventUtils.synthesizeKey(" ", { code: "Space", type: "keydown" });
|
||||
await promise;
|
||||
|
||||
promise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
await promise;
|
||||
});
|
||||
|
|
|
@ -33,18 +33,7 @@ async function testThemeWithInvalidProperties(invalidProps) {
|
|||
});
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({manifest});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
let waitForConsole = new Promise(resolve => {
|
||||
SimpleTest.monitorConsole(resolve, [{
|
||||
message: /Reading manifest: Themes defined in the manifest may only contain static resources/,
|
||||
}]);
|
||||
});
|
||||
|
||||
await Assert.rejects(extension.startup(), null, "Theme should fail to load if it contains invalid properties");
|
||||
|
||||
SimpleTest.endMonitorConsole();
|
||||
await waitForConsole;
|
||||
}
|
||||
|
||||
add_task(async function test_that_theme_with_invalid_properties_fails_to_load() {
|
||||
|
|
|
@ -14,8 +14,6 @@ export MOZILLA_OFFICIAL=1
|
|||
# Enable Telemetry
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
|
||||
# Bug 1283438
|
||||
mk_add_options AUTOCLOBBER=
|
||||
|
|
|
@ -14,8 +14,6 @@ export MOZILLA_OFFICIAL=1
|
|||
# Enable Telemetry
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
|
||||
# Bug 1283438
|
||||
mk_add_options AUTOCLOBBER=
|
||||
|
|
|
@ -9,8 +9,6 @@ export MOZILLA_OFFICIAL=1
|
|||
# Enable Telemetry
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
|
||||
# Bug 1283438
|
||||
mk_add_options AUTOCLOBBER=
|
||||
|
|
|
@ -9,8 +9,6 @@ export MOZILLA_OFFICIAL=1
|
|||
# Enable Telemetry
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
# Don't autoclobber l10n, as this can lead to missing binaries and broken builds
|
||||
# Bug 1283438
|
||||
mk_add_options AUTOCLOBBER=
|
||||
|
|
|
@ -10,7 +10,7 @@ ac_add_options --enable-valgrind
|
|||
. $topsrcdir/build/unix/mozconfig.fuzzing
|
||||
|
||||
ac_add_options --enable-fuzzing
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
unset MOZ_STDCXX_COMPAT
|
||||
|
||||
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
|
||||
. $topsrcdir/build/unix/mozconfig.gtk
|
||||
|
|
|
@ -78,9 +78,9 @@ for (const type of [
|
|||
"TELEMETRY_PERFORMANCE_EVENT",
|
||||
"TELEMETRY_UNDESIRED_EVENT",
|
||||
"TELEMETRY_USER_EVENT",
|
||||
"TOP_SITES_ADD",
|
||||
"TOP_SITES_CANCEL_EDIT",
|
||||
"TOP_SITES_EDIT",
|
||||
"TOP_SITES_INSERT",
|
||||
"TOP_SITES_PIN",
|
||||
"TOP_SITES_UNPIN",
|
||||
"TOP_SITES_UPDATED",
|
||||
|
|
|
@ -51,7 +51,7 @@ this.PrerenderData = new _PrerenderData({
|
|||
"migrationExpired": true,
|
||||
"showTopSites": true,
|
||||
"showSearch": true,
|
||||
"topSitesCount": 6,
|
||||
"topSitesCount": 12,
|
||||
"collapseTopSites": false,
|
||||
"section.highlights.collapsed": false,
|
||||
"section.topstories.collapsed": false,
|
||||
|
@ -67,6 +67,7 @@ this.PrerenderData = new _PrerenderData({
|
|||
validation: [
|
||||
"showTopSites",
|
||||
"showSearch",
|
||||
"topSitesCount",
|
||||
"collapseTopSites",
|
||||
"section.highlights.collapsed",
|
||||
"section.topstories.collapsed",
|
||||
|
|
|
@ -28,7 +28,7 @@ const INITIAL_STATE = {
|
|||
// context menu to TopSitesEdit.
|
||||
editForm: {
|
||||
visible: false,
|
||||
site: null
|
||||
index: -1
|
||||
}
|
||||
},
|
||||
Prefs: {
|
||||
|
@ -95,7 +95,7 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
|||
}
|
||||
return Object.assign({}, prevState, {initialized: true, rows: action.data});
|
||||
case at.TOP_SITES_EDIT:
|
||||
return Object.assign({}, prevState, {editForm: {visible: true, site: action.data}});
|
||||
return Object.assign({}, prevState, {editForm: {visible: true, index: action.data.index}});
|
||||
case at.TOP_SITES_CANCEL_EDIT:
|
||||
return Object.assign({}, prevState, {editForm: {visible: false}});
|
||||
case at.SCREENSHOT_UPDATED:
|
||||
|
|
|
@ -242,9 +242,8 @@ main {
|
|||
|
||||
.top-sites-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin: 0 -16px;
|
||||
margin-bottom: -18px;
|
||||
margin-inline-end: -32px;
|
||||
padding: 0; }
|
||||
@media (max-width: 416px) {
|
||||
.top-sites-list :nth-child(2n+1) .context-menu {
|
||||
|
@ -290,17 +289,18 @@ main {
|
|||
offset-inline-start: auto; } }
|
||||
.top-sites-list li {
|
||||
display: inline-block;
|
||||
margin: 0 0 8px;
|
||||
margin-inline-end: 32px; }
|
||||
margin: 0 0 8px; }
|
||||
.top-sites-list .top-site-outer {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
padding: 0 16px; }
|
||||
.top-sites-list .top-site-outer .top-site-inner {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
.top-sites-list .top-site-outer .context-menu-button {
|
||||
background-clip: padding-box;
|
||||
background-color: #FFF;
|
||||
|
@ -347,10 +347,6 @@ main {
|
|||
text-transform: uppercase; }
|
||||
.top-sites-list .top-site-outer .tile::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer .screenshot {
|
||||
background-color: #FFF;
|
||||
background-position: top left;
|
||||
|
@ -446,6 +442,13 @@ main {
|
|||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
|
||||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer.placeholder .edit-menu:last-child button {
|
||||
background-size: 13px;
|
||||
width: 23px; }
|
||||
|
||||
.edit-topsites-wrapper .edit-topsites-button {
|
||||
border-right: 1px solid #D7D7DB;
|
||||
|
|
|
@ -242,9 +242,8 @@ main {
|
|||
|
||||
.top-sites-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin: 0 -16px;
|
||||
margin-bottom: -18px;
|
||||
margin-inline-end: -32px;
|
||||
padding: 0; }
|
||||
@media (max-width: 416px) {
|
||||
.top-sites-list :nth-child(2n+1) .context-menu {
|
||||
|
@ -290,17 +289,18 @@ main {
|
|||
offset-inline-start: auto; } }
|
||||
.top-sites-list li {
|
||||
display: inline-block;
|
||||
margin: 0 0 8px;
|
||||
margin-inline-end: 32px; }
|
||||
margin: 0 0 8px; }
|
||||
.top-sites-list .top-site-outer {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
padding: 0 16px; }
|
||||
.top-sites-list .top-site-outer .top-site-inner {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
.top-sites-list .top-site-outer .context-menu-button {
|
||||
background-clip: padding-box;
|
||||
background-color: #FFF;
|
||||
|
@ -347,10 +347,6 @@ main {
|
|||
text-transform: uppercase; }
|
||||
.top-sites-list .top-site-outer .tile::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer .screenshot {
|
||||
background-color: #FFF;
|
||||
background-position: top left;
|
||||
|
@ -446,6 +442,13 @@ main {
|
|||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
|
||||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer.placeholder .edit-menu:last-child button {
|
||||
background-size: 13px;
|
||||
width: 23px; }
|
||||
|
||||
.edit-topsites-wrapper .edit-topsites-button {
|
||||
border-right: 1px solid #D7D7DB;
|
||||
|
|
|
@ -242,9 +242,8 @@ main {
|
|||
|
||||
.top-sites-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin: 0 -16px;
|
||||
margin-bottom: -18px;
|
||||
margin-inline-end: -32px;
|
||||
padding: 0; }
|
||||
@media (max-width: 416px) {
|
||||
.top-sites-list :nth-child(2n+1) .context-menu {
|
||||
|
@ -290,17 +289,18 @@ main {
|
|||
offset-inline-start: auto; } }
|
||||
.top-sites-list li {
|
||||
display: inline-block;
|
||||
margin: 0 0 8px;
|
||||
margin-inline-end: 32px; }
|
||||
margin: 0 0 8px; }
|
||||
.top-sites-list .top-site-outer {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
padding: 0 16px; }
|
||||
.top-sites-list .top-site-outer .top-site-inner {
|
||||
position: relative; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a {
|
||||
color: inherit;
|
||||
display: block;
|
||||
outline: none; }
|
||||
.top-sites-list .top-site-outer .top-site-inner > a:-moz-any(.active, :focus) .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1), 0 0 0 5px #D7D7DB;
|
||||
transition: box-shadow 150ms; }
|
||||
.top-sites-list .top-site-outer .context-menu-button {
|
||||
background-clip: padding-box;
|
||||
background-color: #FFF;
|
||||
|
@ -347,10 +347,6 @@ main {
|
|||
text-transform: uppercase; }
|
||||
.top-sites-list .top-site-outer .tile::before {
|
||||
content: attr(data-fallback); }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer .screenshot {
|
||||
background-color: #FFF;
|
||||
background-position: top left;
|
||||
|
@ -446,6 +442,13 @@ main {
|
|||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer .edit-menu button:first-child:dir(rtl) {
|
||||
border-right: 0; }
|
||||
.top-sites-list .top-site-outer.placeholder .tile {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); }
|
||||
.top-sites-list .top-site-outer.placeholder .screenshot {
|
||||
display: none; }
|
||||
.top-sites-list .top-site-outer.placeholder .edit-menu:last-child button {
|
||||
background-size: 13px;
|
||||
width: 23px; }
|
||||
|
||||
.edit-topsites-wrapper .edit-topsites-button {
|
||||
border-right: 1px solid #D7D7DB;
|
||||
|
|
|
@ -94,7 +94,7 @@ const globalImportContext = typeof Window === "undefined" ? BACKGROUND_PROCESS :
|
|||
// UNINIT: "UNINIT"
|
||||
// }
|
||||
const actionTypes = {};
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "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", "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_ADD", "TOP_SITES_CANCEL_EDIT", "TOP_SITES_EDIT", "TOP_SITES_PIN", "TOP_SITES_UNPIN", "TOP_SITES_UPDATED", "UNINIT"]) {
|
||||
for (const type of ["BLOCK_URL", "BOOKMARK_URL", "DELETE_BOOKMARK_BY_ID", "DELETE_HISTORY_URL", "DELETE_HISTORY_URL_CONFIRM", "DIALOG_CANCEL", "DIALOG_OPEN", "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", "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"]) {
|
||||
actionTypes[type] = type;
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ const INITIAL_STATE = {
|
|||
// context menu to TopSitesEdit.
|
||||
editForm: {
|
||||
visible: false,
|
||||
site: null
|
||||
index: -1
|
||||
}
|
||||
},
|
||||
Prefs: {
|
||||
|
@ -437,7 +437,7 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
|
|||
}
|
||||
return Object.assign({}, prevState, { initialized: true, rows: action.data });
|
||||
case at.TOP_SITES_EDIT:
|
||||
return Object.assign({}, prevState, { editForm: { visible: true, site: action.data } });
|
||||
return Object.assign({}, prevState, { editForm: { visible: true, index: action.data.index } });
|
||||
case at.TOP_SITES_CANCEL_EDIT:
|
||||
return Object.assign({}, prevState, { editForm: { visible: false } });
|
||||
case at.SCREENSHOT_UPDATED:
|
||||
|
@ -884,12 +884,12 @@ const LinkMenuOptions = {
|
|||
}),
|
||||
userEvent: "SAVE_TO_POCKET"
|
||||
}),
|
||||
EditTopSite: site => ({
|
||||
EditTopSite: (site, index) => ({
|
||||
id: "edit_topsites_button_text",
|
||||
icon: "edit",
|
||||
action: {
|
||||
type: Actions["actionTypes"].TOP_SITES_EDIT,
|
||||
data: { url: site.url, label: site.label }
|
||||
data: { index }
|
||||
}
|
||||
}),
|
||||
CheckBookmark: site => site.bookmarkGuid ? LinkMenuOptions.RemoveBookmark(site) : LinkMenuOptions.AddBookmark(site),
|
||||
|
@ -902,7 +902,7 @@ const LinkMenuOptions = {
|
|||
|
||||
|
||||
|
||||
const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
|
||||
const DEFAULT_SITE_MENU_OPTIONS = ["CheckPinTopSite", "EditTopSite", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"];
|
||||
|
||||
class LinkMenu__LinkMenu extends external__React__default.a.PureComponent {
|
||||
getOptions() {
|
||||
|
@ -2502,71 +2502,125 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < argument
|
|||
|
||||
|
||||
|
||||
const TopSiteLink = props => {
|
||||
const { link, title } = props;
|
||||
const topSiteOuterClassName = `top-site-outer${props.className ? ` ${props.className}` : ""}`;
|
||||
const { tippyTopIcon, faviconSize } = link;
|
||||
const letterFallback = title[0];
|
||||
let imageClassName;
|
||||
let imageStyle;
|
||||
let showSmallFavicon = false;
|
||||
let smallFaviconStyle;
|
||||
let smallFaviconFallback;
|
||||
if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
|
||||
// styles and class names for top sites with rich icons
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon || link.favicon})`
|
||||
};
|
||||
} else {
|
||||
// styles and class names for top sites with screenshot + small icon in top left corner
|
||||
imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
|
||||
imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
|
||||
|
||||
// only show a favicon in top left if it's greater than 16x16
|
||||
if (faviconSize >= MIN_CORNER_FAVICON_SIZE) {
|
||||
showSmallFavicon = true;
|
||||
smallFaviconStyle = { backgroundImage: `url(${link.favicon})` };
|
||||
} else if (link.screenshot) {
|
||||
// Don't show a small favicon if there is no screenshot, because that
|
||||
// would result in two fallback icons
|
||||
showSmallFavicon = true;
|
||||
smallFaviconFallback = true;
|
||||
class TopSite_TopSiteLink extends external__React__default.a.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onDragEvent = this.onDragEvent.bind(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to determine whether the drop zone should allow a drop. We only allow
|
||||
* dropping top sites for now.
|
||||
*/
|
||||
_allowDrop(e, index) {
|
||||
let draggedIndex = parseInt(e.dataTransfer.getData("text/topsite-index"), 10);
|
||||
if (!isNaN(draggedIndex) && draggedIndex !== index) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
onDragEvent(event) {
|
||||
switch (event.type) {
|
||||
case "dragstart":
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("text/topsite-index", this.props.index);
|
||||
event.target.blur();
|
||||
this.props.onDragEvent(event, this.props.index, this.props.link, this.props.title);
|
||||
break;
|
||||
case "dragend":
|
||||
this.props.onDragEvent(event);
|
||||
break;
|
||||
case "dragover":
|
||||
case "dragenter":
|
||||
case "dragleave":
|
||||
case "drop":
|
||||
if (this._allowDrop(event, this.props.index)) {
|
||||
event.preventDefault();
|
||||
this.props.onDragEvent(event, this.props.index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return external__React__default.a.createElement(
|
||||
"li",
|
||||
{ className: topSiteOuterClassName, key: link.guid || link.url },
|
||||
external__React__default.a.createElement(
|
||||
"a",
|
||||
{ href: link.url, onClick: props.onClick },
|
||||
render() {
|
||||
const { children, className, isDraggable, link, onClick, title } = this.props;
|
||||
const topSiteOuterClassName = `top-site-outer${className ? ` ${className}` : ""}`;
|
||||
const { tippyTopIcon, faviconSize } = link;
|
||||
const letterFallback = title[0];
|
||||
let imageClassName;
|
||||
let imageStyle;
|
||||
let showSmallFavicon = false;
|
||||
let smallFaviconStyle;
|
||||
let smallFaviconFallback;
|
||||
if (tippyTopIcon || faviconSize >= MIN_RICH_FAVICON_SIZE) {
|
||||
// styles and class names for top sites with rich icons
|
||||
imageClassName = "top-site-icon rich-icon";
|
||||
imageStyle = {
|
||||
backgroundColor: link.backgroundColor,
|
||||
backgroundImage: `url(${tippyTopIcon || link.favicon})`
|
||||
};
|
||||
} else {
|
||||
// styles and class names for top sites with screenshot + small icon in top left corner
|
||||
imageClassName = `screenshot${link.screenshot ? " active" : ""}`;
|
||||
imageStyle = { backgroundImage: link.screenshot ? `url(${link.screenshot})` : "none" };
|
||||
|
||||
// only show a favicon in top left if it's greater than 16x16
|
||||
if (faviconSize >= MIN_CORNER_FAVICON_SIZE) {
|
||||
showSmallFavicon = true;
|
||||
smallFaviconStyle = { backgroundImage: `url(${link.favicon})` };
|
||||
} else if (link.screenshot) {
|
||||
// Don't show a small favicon if there is no screenshot, because that
|
||||
// would result in two fallback icons
|
||||
showSmallFavicon = true;
|
||||
smallFaviconFallback = true;
|
||||
}
|
||||
}
|
||||
let draggableProps = {};
|
||||
if (isDraggable) {
|
||||
draggableProps = {
|
||||
draggable: true,
|
||||
onDragStart: this.onDragEvent,
|
||||
onDragEnd: this.onDragEvent
|
||||
};
|
||||
}
|
||||
return external__React__default.a.createElement(
|
||||
"li",
|
||||
_extends({ className: topSiteOuterClassName, key: link.guid || link.url, onDrop: this.onDragEvent, onDragOver: this.onDragEvent, onDragEnter: this.onDragEvent, onDragLeave: this.onDragEvent }, draggableProps),
|
||||
external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: "tile", "aria-hidden": true, "data-fallback": letterFallback },
|
||||
external__React__default.a.createElement("div", { className: imageClassName, style: imageStyle }),
|
||||
showSmallFavicon && external__React__default.a.createElement("div", {
|
||||
className: "top-site-icon default-icon",
|
||||
"data-fallback": smallFaviconFallback && letterFallback,
|
||||
style: smallFaviconStyle })
|
||||
),
|
||||
external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: `title ${link.isPinned ? "pinned" : ""}` },
|
||||
link.isPinned && external__React__default.a.createElement("div", { className: "icon icon-pin-small" }),
|
||||
{ className: "top-site-inner" },
|
||||
external__React__default.a.createElement(
|
||||
"span",
|
||||
{ dir: "auto" },
|
||||
title
|
||||
)
|
||||
"a",
|
||||
{ href: link.url, onClick: onClick },
|
||||
external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: "tile", "aria-hidden": true, "data-fallback": letterFallback },
|
||||
external__React__default.a.createElement("div", { className: imageClassName, style: imageStyle }),
|
||||
showSmallFavicon && external__React__default.a.createElement("div", {
|
||||
className: "top-site-icon default-icon",
|
||||
"data-fallback": smallFaviconFallback && letterFallback,
|
||||
style: smallFaviconStyle })
|
||||
),
|
||||
external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: `title ${link.isPinned ? "pinned" : ""}` },
|
||||
link.isPinned && external__React__default.a.createElement("div", { className: "icon icon-pin-small" }),
|
||||
external__React__default.a.createElement(
|
||||
"span",
|
||||
{ dir: "auto" },
|
||||
title
|
||||
)
|
||||
)
|
||||
),
|
||||
children
|
||||
)
|
||||
),
|
||||
props.children
|
||||
);
|
||||
};
|
||||
TopSiteLink.defaultProps = {
|
||||
);
|
||||
}
|
||||
}
|
||||
TopSite_TopSiteLink.defaultProps = {
|
||||
title: "",
|
||||
link: {}
|
||||
link: {},
|
||||
isDraggable: true
|
||||
};
|
||||
|
||||
class TopSite_TopSite extends external__React__default.a.PureComponent {
|
||||
|
@ -2579,6 +2633,7 @@ class TopSite_TopSite extends external__React__default.a.PureComponent {
|
|||
this.onDismissButtonClick = this.onDismissButtonClick.bind(this);
|
||||
this.onPinButtonClick = this.onPinButtonClick.bind(this);
|
||||
this.onEditButtonClick = this.onEditButtonClick.bind(this);
|
||||
this.onDragEvent = this.onDragEvent.bind(this);
|
||||
}
|
||||
toggleContextMenu(event, index) {
|
||||
this.setState({
|
||||
|
@ -2641,14 +2696,23 @@ class TopSite_TopSite extends external__React__default.a.PureComponent {
|
|||
onEditButtonClick() {
|
||||
this.props.onEdit(this.props.index);
|
||||
}
|
||||
onDragEvent(event, index, link, title) {
|
||||
if (event.type === "dragstart") {
|
||||
this.setState({
|
||||
activeTile: null,
|
||||
showContextMenu: false
|
||||
});
|
||||
}
|
||||
this.props.onDragEvent(event, index, link, title);
|
||||
}
|
||||
render() {
|
||||
const { props } = this;
|
||||
const { link } = props;
|
||||
const isContextMenuOpen = this.state.showContextMenu && this.state.activeTile === props.index;
|
||||
const title = link.label || link.hostname;
|
||||
return external__React__default.a.createElement(
|
||||
TopSiteLink,
|
||||
_extends({}, props, { onClick: this.onLinkClick, className: isContextMenuOpen ? "active" : "", title: title }),
|
||||
TopSite_TopSiteLink,
|
||||
_extends({}, props, { onClick: this.onLinkClick, onDragEvent: this.onDragEvent, className: isContextMenuOpen ? "active" : "", title: title }),
|
||||
!props.onEdit && external__React__default.a.createElement(
|
||||
"div",
|
||||
null,
|
||||
|
@ -2689,29 +2753,180 @@ class TopSite_TopSite extends external__React__default.a.PureComponent {
|
|||
);
|
||||
}
|
||||
}
|
||||
TopSite_TopSite.defaultProps = { link: {} };
|
||||
|
||||
const TopSitePlaceholder = () => external__React__default.a.createElement(TopSiteLink, { className: "placeholder" });
|
||||
|
||||
const TopSiteList = props => {
|
||||
const topSites = props.TopSites.rows.slice(0, props.TopSitesCount);
|
||||
const topSitesUI = [];
|
||||
for (let i = 0, l = props.TopSitesCount; i < l; i++) {
|
||||
const link = topSites[i];
|
||||
topSitesUI.push(!link ? external__React__default.a.createElement(TopSitePlaceholder, { key: i }) : external__React__default.a.createElement(TopSite_TopSite, {
|
||||
key: link.guid || link.url,
|
||||
dispatch: props.dispatch,
|
||||
link: link,
|
||||
index: i,
|
||||
intl: props.intl,
|
||||
onEdit: props.onEdit }));
|
||||
}
|
||||
return external__React__default.a.createElement(
|
||||
"ul",
|
||||
{ className: "top-sites-list" },
|
||||
topSitesUI
|
||||
);
|
||||
TopSite_TopSite.defaultProps = {
|
||||
link: {},
|
||||
onDragStart() {}
|
||||
};
|
||||
|
||||
class TopSite_TopSitePlaceholder extends external__React__default.a.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onEditButtonClick = this.onEditButtonClick.bind(this);
|
||||
}
|
||||
onEditButtonClick() {
|
||||
this.props.dispatch({
|
||||
type: Actions["actionTypes"].TOP_SITES_EDIT,
|
||||
data: { index: this.props.index }
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return external__React__default.a.createElement(
|
||||
TopSite_TopSiteLink,
|
||||
_extends({ className: "placeholder", isDraggable: false }, this.props),
|
||||
external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: "edit-menu" },
|
||||
external__React__default.a.createElement("button", {
|
||||
className: "icon icon-edit",
|
||||
title: this.props.intl.formatMessage({ id: "edit_topsites_edit_button" }),
|
||||
onClick: this.onEditButtonClick })
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TopSite__TopSiteList extends external__React__default.a.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this.DEFAULT_STATE = {
|
||||
draggedIndex: null,
|
||||
draggedSite: null,
|
||||
draggedTitle: null,
|
||||
topSitesPreview: null
|
||||
};
|
||||
this.onDragEvent = this.onDragEvent.bind(this);
|
||||
}
|
||||
componentWillUpdate(nextProps) {
|
||||
if (this.state.draggedSite) {
|
||||
const prevTopSites = this.props.TopSites && this.props.TopSites.rows;
|
||||
const newTopSites = nextProps.TopSites && nextProps.TopSites.rows;
|
||||
if (prevTopSites && prevTopSites[this.state.draggedIndex] && prevTopSites[this.state.draggedIndex].url === this.state.draggedSite.url && (!newTopSites[this.state.draggedIndex] || newTopSites[this.state.draggedIndex].url !== this.state.draggedSite.url)) {
|
||||
// We got the new order from the redux store via props. We can clear state now.
|
||||
this.setState(this.DEFAULT_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
userEvent(event, index) {
|
||||
this.props.dispatch(Actions["actionCreators"].UserEvent({
|
||||
event,
|
||||
source: TOP_SITES_SOURCE,
|
||||
action_position: index
|
||||
}));
|
||||
}
|
||||
onDragEvent(event, index, link, title) {
|
||||
switch (event.type) {
|
||||
case "dragstart":
|
||||
this.setState({
|
||||
draggedIndex: index,
|
||||
draggedSite: link,
|
||||
draggedTitle: title
|
||||
});
|
||||
this.userEvent("DRAG", index);
|
||||
break;
|
||||
case "dragend":
|
||||
this.setState(this.DEFAULT_STATE);
|
||||
break;
|
||||
case "dragenter":
|
||||
this.setState({ topSitesPreview: this._makeTopSitesPreview(index) });
|
||||
break;
|
||||
case "dragleave":
|
||||
this.setState({ topSitesPreview: null });
|
||||
break;
|
||||
case "drop":
|
||||
this.props.dispatch(Actions["actionCreators"].SendToMain({
|
||||
type: Actions["actionTypes"].TOP_SITES_INSERT,
|
||||
data: { site: { url: this.state.draggedSite.url, label: this.state.draggedTitle }, index }
|
||||
}));
|
||||
this.userEvent("DROP", index);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_getTopSites() {
|
||||
return this.props.TopSites.rows.slice(0, this.props.TopSitesCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a preview of the topsites that will be the result of dropping the currently
|
||||
* dragged site at the specified index.
|
||||
*/
|
||||
_makeTopSitesPreview(index) {
|
||||
const preview = this._getTopSites();
|
||||
this._fillOrLeaveHole(preview, this.state.draggedIndex);
|
||||
this._insertSite(preview, Object.assign({}, this.state.draggedSite, { isPinned: true }), index);
|
||||
return preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the slot at the specified index with a non pinned site further down the
|
||||
* list, if any. Otherwise leave an empty slot.
|
||||
*/
|
||||
_fillOrLeaveHole(sites, index) {
|
||||
let slotIndex = index;
|
||||
sites[slotIndex] = null;
|
||||
for (let i = slotIndex + 1; i < sites.length; i++) {
|
||||
const site = sites[i];
|
||||
if (site && !site.isPinned) {
|
||||
sites[i] = null;
|
||||
sites[slotIndex] = site;
|
||||
// Update the index to fill to be the spot we just grabbed a site from
|
||||
slotIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the given site in the slot at the specified index. If the slot is occupied,
|
||||
* move it appropriately.
|
||||
*/
|
||||
_insertSite(sites, site, index) {
|
||||
const replacedSite = sites[index];
|
||||
if (replacedSite && index < this.props.TopSitesCount - 1) {
|
||||
if (replacedSite.isPinned) {
|
||||
// If the replaced site is pinned, it goes into the next slot no matter what.
|
||||
this._insertSite(sites, replacedSite, index + 1);
|
||||
} else {
|
||||
// If the replaced site isn't pinned, it goes into the next slot that doesn't havea pinned site;
|
||||
for (let i = index + 1, l = sites.length; i < l; i++) {
|
||||
if (!sites[i] || !sites[i].isPinned) {
|
||||
this._insertSite(sites, replacedSite, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sites[index] = site;
|
||||
}
|
||||
render() {
|
||||
const { props } = this;
|
||||
const topSites = this.state.topSitesPreview || this._getTopSites();
|
||||
const topSitesUI = [];
|
||||
const commonProps = {
|
||||
onDragEvent: this.onDragEvent,
|
||||
dispatch: props.dispatch,
|
||||
intl: props.intl
|
||||
};
|
||||
for (let i = 0, l = props.TopSitesCount; i < l; i++) {
|
||||
const link = topSites[i];
|
||||
const slotProps = {
|
||||
key: i,
|
||||
index: i
|
||||
};
|
||||
topSitesUI.push(!link ? external__React__default.a.createElement(TopSite_TopSitePlaceholder, _extends({}, slotProps, commonProps)) : external__React__default.a.createElement(TopSite_TopSite, _extends({
|
||||
link: link,
|
||||
onEdit: props.onEdit
|
||||
}, slotProps, commonProps)));
|
||||
}
|
||||
return external__React__default.a.createElement(
|
||||
"ul",
|
||||
{ className: "top-sites-list" },
|
||||
topSitesUI
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const TopSiteList = Object(external__ReactIntl_["injectIntl"])(TopSite__TopSiteList);
|
||||
// CONCATENATED MODULE: ./system-addon/content-src/components/TopSites/TopSiteForm.jsx
|
||||
|
||||
|
||||
|
@ -2753,7 +2968,7 @@ class TopSiteForm_TopSiteForm extends external__React__default.a.PureComponent {
|
|||
site.label = this.state.label;
|
||||
}
|
||||
this.props.dispatch(Actions["actionCreators"].SendToMain({
|
||||
type: Actions["actionTypes"].TOP_SITES_ADD,
|
||||
type: Actions["actionTypes"].TOP_SITES_INSERT,
|
||||
data: { site }
|
||||
}));
|
||||
this.props.dispatch(Actions["actionCreators"].UserEvent({
|
||||
|
@ -2956,12 +3171,13 @@ class TopSitesEdit__TopSitesEdit extends external__React__default.a.PureComponen
|
|||
}));
|
||||
}
|
||||
render() {
|
||||
const showEditForm = this.props.TopSites.editForm && this.props.TopSites.editForm.visible || this.state.showEditModal && this.state.showEditForm;
|
||||
const { editForm } = this.props.TopSites;
|
||||
const showEditForm = editForm && editForm.visible || this.state.showEditModal && this.state.showEditForm;
|
||||
let editIndex = this.state.editIndex;
|
||||
if (showEditForm && this.props.TopSites.editForm.visible) {
|
||||
const targetURL = this.props.TopSites.editForm.site.url;
|
||||
editIndex = this.props.TopSites.rows.findIndex(s => s.url === targetURL);
|
||||
if (editIndex < 0 && editForm) {
|
||||
editIndex = editForm.index;
|
||||
}
|
||||
const editSite = this.props.TopSites.rows[editIndex] || {};
|
||||
return external__React__default.a.createElement(
|
||||
"div",
|
||||
{ className: "edit-topsites-wrapper" },
|
||||
|
@ -3038,8 +3254,8 @@ class TopSitesEdit__TopSitesEdit extends external__React__default.a.PureComponen
|
|||
"div",
|
||||
{ className: "modal" },
|
||||
external__React__default.a.createElement(TopSiteForm_TopSiteForm, {
|
||||
label: this.props.TopSites.rows[editIndex].label || this.props.TopSites.rows[editIndex].hostname,
|
||||
url: this.props.TopSites.rows[editIndex].url,
|
||||
label: editSite.label || editSite.hostname || "",
|
||||
url: editSite.url || "",
|
||||
index: editIndex,
|
||||
editMode: true,
|
||||
onClose: this.onFormClose,
|
||||
|
@ -3341,7 +3557,7 @@ var PrerenderData = new _PrerenderData({
|
|||
"migrationExpired": true,
|
||||
"showTopSites": true,
|
||||
"showSearch": true,
|
||||
"topSitesCount": 6,
|
||||
"topSitesCount": 12,
|
||||
"collapseTopSites": false,
|
||||
"section.highlights.collapsed": false,
|
||||
"section.topstories.collapsed": false,
|
||||
|
@ -3354,7 +3570,7 @@ var PrerenderData = new _PrerenderData({
|
|||
// too different for the prerendered version to be used. Unfortunately, this
|
||||
// will result in users who have modified some of their preferences not being
|
||||
// able to get the benefits of prerendering.
|
||||
validation: ["showTopSites", "showSearch", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
|
||||
validation: ["showTopSites", "showSearch", "topSitesCount", "collapseTopSites", "section.highlights.collapsed", "section.topstories.collapsed",
|
||||
// This means if either of these are set to their default values,
|
||||
// prerendering can be used.
|
||||
{ oneOf: ["feeds.section.topstories", "feeds.section.highlights"] }],
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>false</em:unpack>
|
||||
<em:version>2017.12.20.1328-00d79b97</em:version>
|
||||
<em:version>2017.12.22.0055-8fe1055e</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>
|
||||
|
|
|
@ -108,7 +108,7 @@ const PREFS_CONFIG = new Map([
|
|||
}],
|
||||
["topSitesCount", {
|
||||
title: "Number of Top Sites to display",
|
||||
value: 6
|
||||
value: 12
|
||||
}],
|
||||
["telemetry", {
|
||||
title: "Enable system error and usage data collection",
|
||||
|
|
|
@ -241,6 +241,13 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
* Insert a site to pin at a position shifting over any other pinned sites.
|
||||
*/
|
||||
_insertPin(site, index) {
|
||||
// Don't insert any pins past the end of the visible top sites. Otherwise,
|
||||
// we can end up with a bunch of pinned sites that can never be unpinned again
|
||||
// from the UI.
|
||||
if (index >= this.store.getState().Prefs.values.topSitesCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For existing sites, recursively push it and others to the next positions
|
||||
let pinned = NewTabUtils.pinnedLinks.links;
|
||||
if (pinned.length > index && pinned[index]) {
|
||||
|
@ -250,12 +257,12 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle an add action of a site.
|
||||
* Handle an insert (drop/add) action of a site.
|
||||
*/
|
||||
add(action) {
|
||||
// Adding a top site pins it in the first slot, pushing over any link already
|
||||
// pinned in the slot.
|
||||
this._insertPin(action.data.site, 0);
|
||||
insert(action) {
|
||||
// Inserting a top site pins it in the specified slot, pushing over any link already
|
||||
// pinned in the slot (unless it's the last slot, then it replaces).
|
||||
this._insertPin(action.data.site, action.data.index || 0);
|
||||
this._broadcastPinnedSitesUpdated();
|
||||
}
|
||||
|
||||
|
@ -293,8 +300,8 @@ this.TopSitesFeed = class TopSitesFeed {
|
|||
case at.TOP_SITES_UNPIN:
|
||||
this.unpin(action);
|
||||
break;
|
||||
case at.TOP_SITES_ADD:
|
||||
this.add(action);
|
||||
case at.TOP_SITES_INSERT:
|
||||
this.insert(action);
|
||||
break;
|
||||
case at.UNINIT:
|
||||
this.uninit();
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче