зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1480907
- Implement ability to bookmark a selection of tabs through drag and drop. r=Felipe
Differential Revision: https://phabricator.services.mozilla.com/D4589 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
e638e46d0b
Коммит
d7a8b85f4c
|
@ -826,28 +826,35 @@
|
|||
<parameter name="event"/>
|
||||
<body><![CDATA[
|
||||
var dt = event.dataTransfer;
|
||||
if (dt.mozItemCount == 1) {
|
||||
var types = dt.mozTypesAt(0);
|
||||
|
||||
let isMovingTabs = dt.mozItemCount > 0;
|
||||
for (let i = 0; i < dt.mozItemCount; i++) {
|
||||
// tabs are always added as the first type
|
||||
if (types[0] == TAB_DROP_TYPE) {
|
||||
let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (sourceNode instanceof XULElement &&
|
||||
sourceNode.localName == "tab" &&
|
||||
sourceNode.ownerGlobal.isChromeWindow &&
|
||||
sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
|
||||
sourceNode.ownerGlobal.gBrowser.tabContainer == sourceNode.parentNode) {
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window) !=
|
||||
PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerGlobal))
|
||||
return "none";
|
||||
let types = dt.mozTypesAt(0);
|
||||
if (types[0] != TAB_DROP_TYPE) {
|
||||
isMovingTabs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.gMultiProcessBrowser !=
|
||||
sourceNode.ownerGlobal.gMultiProcessBrowser)
|
||||
return "none";
|
||||
if (isMovingTabs) {
|
||||
let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (sourceNode instanceof XULElement &&
|
||||
sourceNode.localName == "tab" &&
|
||||
sourceNode.ownerGlobal.isChromeWindow &&
|
||||
sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
|
||||
sourceNode.ownerGlobal.gBrowser.tabContainer == sourceNode.parentNode) {
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window) !=
|
||||
PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerGlobal))
|
||||
return "none";
|
||||
|
||||
return dt.dropEffect == "copy" ? "copy" : "move";
|
||||
}
|
||||
if (window.gMultiProcessBrowser !=
|
||||
sourceNode.ownerGlobal.gMultiProcessBrowser)
|
||||
return "none";
|
||||
|
||||
return dt.dropEffect == "copy" ? "copy" : "move";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1199,14 +1206,22 @@
|
|||
if (!tab || this._isCustomizing)
|
||||
return;
|
||||
|
||||
let dt = event.dataTransfer;
|
||||
dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
|
||||
let browser = tab.linkedBrowser;
|
||||
let selectedTabs = gBrowser.selectedTabs;
|
||||
let otherSelectedTabs = selectedTabs.filter(selectedTab => selectedTab != tab);
|
||||
let dataTransferOrderedTabs = [tab].concat(otherSelectedTabs);
|
||||
|
||||
// We must not set text/x-moz-url or text/plain data here,
|
||||
// otherwise trying to deatch the tab by dropping it on the desktop
|
||||
// may result in an "internet shortcut"
|
||||
dt.mozSetDataAt("text/x-moz-text-internal", browser.currentURI.spec, 0);
|
||||
let dt = event.dataTransfer;
|
||||
for (let i = 0; i < dataTransferOrderedTabs.length; i++) {
|
||||
let dtTab = dataTransferOrderedTabs[i];
|
||||
|
||||
dt.mozSetDataAt(TAB_DROP_TYPE, dtTab, i);
|
||||
let dtBrowser = dtTab.linkedBrowser;
|
||||
|
||||
// We must not set text/x-moz-url or text/plain data here,
|
||||
// otherwise trying to detach the tab by dropping it on the desktop
|
||||
// may result in an "internet shortcut"
|
||||
dt.mozSetDataAt("text/x-moz-text-internal", dtBrowser.currentURI.spec, i);
|
||||
}
|
||||
|
||||
// Set the cursor to an arrow during tab drags.
|
||||
dt.mozCursor = "default";
|
||||
|
@ -1215,10 +1230,9 @@
|
|||
// node to deliver the `dragend` event. See bug 1345473.
|
||||
dt.addElement(tab);
|
||||
|
||||
// Regroup all selected tabs around the dragged tab
|
||||
// for multiple tabs dragging
|
||||
if (tab.multiselected) {
|
||||
let selectedTabs = gBrowser.selectedTabs;
|
||||
// Regroup all selected tabs around the dragged tab
|
||||
// for multiple tabs dragging
|
||||
let draggedTabPos = tab._tPos;
|
||||
|
||||
// Move left selected tabs
|
||||
|
@ -1257,6 +1271,7 @@
|
|||
canvas.height = 90 * scale;
|
||||
let toDrag = canvas;
|
||||
let dragImageOffset = -16;
|
||||
let browser = tab.linkedBrowser;
|
||||
if (gMultiProcessBrowser) {
|
||||
var context = canvas.getContext("2d");
|
||||
context.fillStyle = "white";
|
||||
|
|
|
@ -27,6 +27,7 @@ support-files =
|
|||
[browser_multiselect_tabs_close_using_shortcuts.js]
|
||||
[browser_multiselect_tabs_close.js]
|
||||
[browser_multiselect_tabs_copy_through_drag_and_drop.js]
|
||||
[browser_multiselect_tabs_drag_to_bookmarks_toolbar.js]
|
||||
[browser_multiselect_tabs_duplicate.js]
|
||||
[browser_multiselect_tabs_event.js]
|
||||
[browser_multiselect_tabs_move_to_another_window_drag.js]
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
|
||||
const PREF_ANIMATIONS_ENABLED = "toolkit.cosmeticAnimations.enabled";
|
||||
|
||||
add_task(async function setPref() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_MULTISELECT_TABS, true],
|
||||
[PREF_ANIMATIONS_ENABLED, false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test() {
|
||||
// Open Bookmarks Toolbar
|
||||
let bookmarksToolbar = document.getElementById("PersonalToolbar");
|
||||
setToolbarVisibility(bookmarksToolbar, true);
|
||||
ok(!bookmarksToolbar.collapsed, "bookmarksToolbar should be visible now");
|
||||
|
||||
let tab1 = await addTab("http://mochi.test:8888/1");
|
||||
let tab2 = await addTab("http://mochi.test:8888/2");
|
||||
let tab3 = await addTab("http://mochi.test:8888/3");
|
||||
let tab4 = await addTab("http://mochi.test:8888/4");
|
||||
let tab5 = await addTab("http://mochi.test:8888/5");
|
||||
|
||||
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
|
||||
|
||||
await BrowserTestUtils.switchTab(gBrowser, tab2);
|
||||
await triggerClickOn(tab1, { ctrlKey: true });
|
||||
|
||||
ok(tab1.multiselected, "Tab1 is multiselected");
|
||||
ok(tab2.multiselected, "Tab2 is multiselected");
|
||||
ok(!tab3.multiselected, "Tab3 is not multiselected");
|
||||
ok(!tab4.multiselected, "Tab4 is not multiselected");
|
||||
ok(!tab5.multiselected, "Tab5 is not multiselected");
|
||||
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
|
||||
|
||||
// Use getElementsByClassName so the list is live and will update as items change.
|
||||
let currentBookmarks = bookmarksToolbar.getElementsByClassName("bookmark-item");
|
||||
let startBookmarksLength = currentBookmarks.length;
|
||||
|
||||
let lastBookmark = currentBookmarks[currentBookmarks.length - 1];
|
||||
await EventUtils.synthesizePlainDragAndDrop({srcElement: tab1, destElement: lastBookmark});
|
||||
await TestUtils.waitForCondition(() => currentBookmarks.length == startBookmarksLength + 2,
|
||||
"waiting for 2 bookmarks");
|
||||
is(currentBookmarks.length, startBookmarksLength + 2, "Bookmark count should have increased by 2");
|
||||
|
||||
// Drag non-selection to the bookmarks toolbar
|
||||
startBookmarksLength = currentBookmarks.length;
|
||||
await EventUtils.synthesizePlainDragAndDrop({srcElement: tab3, destElement: lastBookmark});
|
||||
await TestUtils.waitForCondition(() => currentBookmarks.length == startBookmarksLength + 1,
|
||||
"waiting for 1 bookmark");
|
||||
is(currentBookmarks.length, startBookmarksLength + 1, "Bookmark count should have increased by 1");
|
||||
|
||||
BrowserTestUtils.removeTab(tab1);
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
BrowserTestUtils.removeTab(tab3);
|
||||
BrowserTestUtils.removeTab(tab4);
|
||||
BrowserTestUtils.removeTab(tab5);
|
||||
});
|
Загрузка…
Ссылка в новой задаче