Bug 1725487 - Show the Add Bookmark Dialog when dragging javascript url to create a bookmarklet. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D133813
This commit is contained in:
Evgenia Kotovich 2022-01-13 11:05:59 +00:00
parent f56a316fd8
commit 960d138fc9
3 changed files with 239 additions and 1 deletions

View File

@ -1542,7 +1542,7 @@ var PlacesControllerDragHelper = {
* @param {object} insertionPoint The insertion point where the items should
* be dropped.
* @param {object} dt The dataTransfer information for the drop.
* @param {object} view The view or the tree element. This allows
* @param {object} [view] The view or the tree element. This allows
* batching to take place.
*/
async onDrop(insertionPoint, dt, view) {
@ -1559,6 +1559,7 @@ var PlacesControllerDragHelper = {
// DataTransfer is only valid during the synchronous handling of the `drop`
// event handler callback.
let nodes = [];
let externalDrag = false;
for (let i = 0; i < dropCount; ++i) {
let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
if (!flavor) {
@ -1574,6 +1575,11 @@ var PlacesControllerDragHelper = {
handled.add(data);
}
// Check that the drag/drop is not internal
if (i == 0 && !flavor.startsWith("text/x-moz-place")) {
externalDrag = true;
}
if (flavor != TAB_DROP_TYPE) {
nodes = [...nodes, ...PlacesUtils.unwrapNodes(data, flavor)];
} else if (
@ -1593,6 +1599,41 @@ var PlacesControllerDragHelper = {
}
}
// If a single javascript url is being dropped from the urlbar or an external source,
// show the bookmark dialog as a speedbump protection against malicious cases.
if (
nodes.length == 1 &&
externalDrag &&
nodes[0].uri?.startsWith("javascript")
) {
let uri;
try {
uri = Services.io.newURI(nodes[0].uri);
} catch (ex) {
// Invalid uri, we skip this code and the entry will be discarded later.
}
if (uri) {
let bookmarkGuid = await PlacesUIUtils.showBookmarkDialog(
{
action: "add",
type: "bookmark",
defaultInsertionPoint: insertionPoint,
hiddenRows: ["folderPicker"],
title: nodes[0].title,
uri,
},
BrowserWindowTracker.getTopWindow() // `window` may be the Library.
);
if (bookmarkGuid && view) {
view.selectItems([bookmarkGuid], false);
}
return;
}
}
await PlacesUIUtils.handleTransferItems(
nodes,
insertionPoint,

View File

@ -129,6 +129,7 @@ skip-if = os == "linux" && (tsan || asan) # Bug 1714384
skip-if = (os == "mac" && debug) # Bug 1467049
[browser_sort_in_library.js]
[browser_stayopenmenu.js]
[browser_toolbar_drop_bookmarklet.js]
[browser_toolbar_drop_text.js]
skip-if = os == "linux" && (tsan || asan) # Bug 1714384
[browser_toolbar_library_open_recent.js]

View File

@ -0,0 +1,196 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
const sandbox = sinon.createSandbox();
const URL1 = "https://example.com/1/";
const URL2 = "https://example.com/2/";
const BOOKMARKLET_URL = `javascript: (() => {alert('Hello, World!');})();`;
let bookmarks;
registerCleanupFunction(async function() {
sandbox.restore();
});
add_task(async function test() {
// Make sure the bookmarks bar is visible and restore its state on cleanup.
let toolbar = document.getElementById("PersonalToolbar");
ok(toolbar, "PersonalToolbar should not be null");
await PlacesUtils.bookmarks.eraseEverything();
if (toolbar.collapsed) {
await promiseSetToolbarVisibility(toolbar, true);
registerCleanupFunction(function() {
return promiseSetToolbarVisibility(toolbar, false);
});
}
// Setup the node we will use to be dropped. The actual node used does not
// matter because we will set its data, effect, and mimeType manually.
let placesItems = document.getElementById("PlacesToolbarItems");
Assert.ok(placesItems, "PlacesToolbarItems should not be null");
/**
* Simulates a drop of a bookmarklet URI onto the bookmarks bar.
*
* @param {string} aEffect
* The effect to use for the drop operation: move, copy, or link.
* @param {string} aMimeType
* The mime type to use for the drop operation.
*/
let simulateDragDrop = async function(aEffect) {
info("Simulates drag/drop of a new javascript:URL to the bookmarks");
await withBookmarksDialog(
true,
function openDialog() {
EventUtils.synthesizeDrop(
toolbar,
placesItems,
[[{ type: "text/x-moz-url", data: BOOKMARKLET_URL }]],
aEffect,
window
);
},
async function testNameField(dialogWin) {
info("Checks that there is a javascript:URL in ShowBookmarksDialog");
let location = dialogWin.document.getElementById(
"editBMPanel_locationField"
).value;
Assert.equal(
location,
BOOKMARKLET_URL,
"Should have opened the ShowBookmarksDialog with the correct bookmarklet url to be bookmarked"
);
}
);
info("Simulates drag/drop of a new URL to the bookmarks");
let spy = sandbox
.stub(PlacesUIUtils, "showBookmarkDialog")
.returns(Promise.resolve());
let promise = PlacesTestUtils.waitForNotification(
"bookmark-added",
events => events.some(({ url }) => url == URL1),
"places"
);
EventUtils.synthesizeDrop(
toolbar,
placesItems,
[[{ type: "text/x-moz-url", data: URL1 }]],
aEffect,
window
);
await promise;
Assert.ok(spy.notCalled, "ShowBookmarksDialog on drop not called for url");
sandbox.restore();
};
let effects = ["copy", "link"];
for (let effect of effects) {
await simulateDragDrop(effect);
}
info("Move of existing bookmark / bookmarklet on toolbar");
//clean previous bookmarks to ensure right ids count
await PlacesUtils.bookmarks.eraseEverything();
info("Insert list of bookamrks to have bookamrks (ids) for moving");
bookmarks = await PlacesUtils.bookmarks.insertTree({
guid: PlacesUtils.bookmarks.toolbarGuid,
children: [
{
title: "bm1",
url: URL1,
},
{
title: "bm2",
url: URL2,
},
{
title: "bookmarklet",
url: BOOKMARKLET_URL,
},
],
});
let spy = sandbox
.stub(PlacesUIUtils, "showBookmarkDialog")
.returns(Promise.resolve());
info("Moving existing Bookmark from position [1] to [0] on Toolbar");
let urlMoveNotification = PlacesTestUtils.waitForNotification(
"bookmark-moved",
events =>
events.some(
e =>
e.parentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldParentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldIndex == 1 &&
e.index == 0
),
"places"
);
EventUtils.synthesizeDrop(
toolbar,
placesItems.childNodes[0],
[
[
{
type: "text/x-moz-place",
data: PlacesUtils.wrapNode(
placesItems.childNodes[1]._placesNode,
"text/x-moz-place"
),
},
],
],
"move",
window
);
await urlMoveNotification;
Assert.ok(spy.notCalled, "ShowBookmarksDialog not called on move for url");
info("Moving existing Bookmarklet from position [2] to [1] on Toolbar");
let bookmarkletMoveNotificatio = PlacesTestUtils.waitForNotification(
"bookmark-moved",
events =>
events.some(
e =>
e.parentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldParentGuid === PlacesUtils.bookmarks.toolbarGuid &&
e.oldIndex == 2 &&
e.index == 1
),
"places"
);
EventUtils.synthesizeDrop(
toolbar,
placesItems.childNodes[1],
[
[
{
type: "text/x-moz-place",
data: PlacesUtils.wrapNode(
placesItems.childNodes[2]._placesNode,
"text/x-moz-place"
),
},
],
],
"move",
window
);
await bookmarkletMoveNotificatio;
Assert.ok(spy.notCalled, "ShowBookmarksDialog not called on move for url");
sandbox.restore();
});