Source code
Revision control
Copy as Markdown
Other Tools
<?xml version="1.0"?>
<window title="Browser Drop Tests"
<script>
<![CDATA[
const { ContentTask } = ChromeUtils.importESModule(
);
function dropOnRemoteBrowserAsync(browser, data, shouldExpectStateChange) {
ContentTask.setTestScope(window); // Need this so is/isnot/ok are available inside the contenttask
return ContentTask.spawn(browser, {data, shouldExpectStateChange}, async function({data, shouldExpectStateChange}) {
if (!content.document.documentElement) {
// Wait until the testing document gets loaded.
await new Promise(resolve => {
let onload = function() {
content.window.removeEventListener("load", onload);
resolve();
};
content.window.addEventListener("load", onload);
});
}
let dataTransfer = new content.DataTransfer();
for (let i = 0; i < data.length; i++) {
let types = data[i];
for (let j = 0; j < types.length; j++) {
dataTransfer.mozSetDataAt(types[j].type, types[j].data, i);
}
}
let event = content.document.createEvent("DragEvent");
event.initDragEvent("drop", true, true, content, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
content.document.body.dispatchEvent(event);
let links = [];
try {
links = Services.droppedLinkHandler.dropLinks(event, true);
} catch (ex) {
if (shouldExpectStateChange) {
ok(false, "Should not have gotten an exception from the dropped link handler, but got: " + ex);
console.error(ex);
}
}
return links;
});
}
async function expectLink(browser, expectedLinks, data, testid, onbody=false) {
let lastLinks = [];
let lastLinksPromise = new Promise(resolve => {
browser.droppedLinkHandler = function(event, links) {
info(`droppedLinkHandler called, received links ${JSON.stringify(links)}`);
if (!expectedLinks.length) {
ok(false, `droppedLinkHandler called for ${JSON.stringify(links)} which we didn't expect.`);
}
lastLinks = links;
resolve(links);
};
});
function dropOnBrowserSync() {
let dropEl = onbody ? browser.contentDocument.body : browser;
synthesizeDrop(dropEl, dropEl, data, null, dropEl.ownerGlobal);
}
let links;
if (browser.isRemoteBrowser) {
let remoteLinks = await dropOnRemoteBrowserAsync(browser, data, expectedLinks.length);
is(remoteLinks.length, expectedLinks.length, testid + " remote links length");
for (let i = 0, length = remoteLinks.length; i < length; i++) {
is(remoteLinks[i].url, expectedLinks[i].url, testid + "[" + i + "] remote link");
is(remoteLinks[i].name, expectedLinks[i].name, testid + "[" + i + "] remote name");
}
if (!expectedLinks.length) {
// There is no way to check if nothing happens asynchronously.
return;
}
links = await lastLinksPromise;
} else {
dropOnBrowserSync();
links = lastLinks;
}
is(links.length, expectedLinks.length, testid + " links length");
for (let i = 0, length = links.length; i < length; i++) {
is(links[i].url, expectedLinks[i].url, testid + "[" + i + "] link");
is(links[i].name, expectedLinks[i].name, testid + "[" + i + "] name");
}
};
async function dropLinksOnBrowser(browser, type) {
// Dropping single text/plain item with single link should open single
// page.
await expectLink(browser,
[ [ { type: "text/plain",
"text/plain drop on browser " + type);
// Dropping single text/plain item with multiple links should open
// multiple pages.
await expectLink(browser,
[ [ { type: "text/plain",
"text/plain with 2 URLs drop on browser " + type);
// Dropping sinlge unsupported type item should not open anything.
await expectLink(browser,
[],
[ [ { type: "text/link",
"text/link drop on browser " + type);
// Dropping single text/uri-list item with single link should open single
// page.
await expectLink(browser,
[ [ { type: "text/uri-list",
"text/uri-list drop on browser " + type);
// Dropping single text/uri-list item with multiple links should open
// multiple pages.
await expectLink(browser,
[ [ { type: "text/uri-list",
"text/uri-list with 2 URLs drop on browser " + type);
// Name in text/x-moz-url should be handled.
await expectLink(browser,
name: "Example.com" } ],
[ [ { type: "text/x-moz-url",
"text/x-moz-url drop on browser " + type);
await expectLink(browser,
name: "Mozilla.org" },
name: "Example.com" } ],
[ [ { type: "text/x-moz-url",
"text/x-moz-url with 2 URLs drop on browser " + type);
// Dropping single item with multiple types should open single page.
await expectLink(browser,
name: "Example.com" } ],
[ [ { type: "text/plain",
{ type: "text/x-moz-url",
"text/plain and text/x-moz-url drop on browser " + type);
// Dropping javascript or data: URLs should fail:
await expectLink(browser,
[],
[ [ { type: "text/plain",
data: "javascript:'bad'" } ] ],
"text/plain javascript url drop on browser " + type);
await expectLink(browser,
[],
[ [ { type: "text/plain",
data: "jAvascript:'also bad'" } ] ],
"text/plain mixed-case javascript url drop on browser " + type);
await expectLink(browser,
[],
[ [ { type: "text/plain",
data: "data:text/html,bad" } ] ],
"text/plain data url drop on browser " + type);
// Dropping a chrome url should fail as we don't have a source node set,
await expectLink(browser,
[],
[ [ { type: "text/x-moz-url",
data: "chrome://browser/content/browser.xhtml" } ] ],
"text/x-moz-url chrome url drop on browser " + type);
if (browser.type == "content") {
await SpecialPowers.spawn(browser, [], function() {
content.window.stopMode = true;
});
// stopPropagation should not prevent the browser link handling from occuring
await expectLink(browser,
[ [ { type: "text/uri-list",
"text/x-moz-url drop on browser with stopPropagation drop event", true);
await SpecialPowers.spawn(browser, [], function() {
content.window.cancelMode = true;
});
// Canceling the event, however, should prevent the link from being handled.
await expectLink(browser,
[],
"text/x-moz-url drop on browser with cancelled drop event", true);
}
}
function info(msg) { window.arguments[0].SimpleTest.info(msg); }
function is(l, r, n) { window.arguments[0].SimpleTest.is(l,r,n); }
function ok(v, n) { window.arguments[0].SimpleTest.ok(v,n); }
]]>
</script>
<html:style>
* Without this there's one subtest that fails, but the browser
* elements were zero-sized before so... */
browser {
min-width: 0;
min-height: 0;
}
</html:style>
<browser id="chromechild" src="about:blank"/>
<browser id="contentchild" type="content" style="width: 100px; height: 100px"
src="data:text/html,<html draggable='true'><body draggable='true' style='width: 100px; height: 100px;' ondragover='event.preventDefault()' ondrop='if (window.stopMode) event.stopPropagation(); if (window.cancelMode) event.preventDefault();'></body></html>"/>
<browser id="remote-contentchild" type="content" remote="true" style="width: 100px; height: 100px"
src="data:text/html,<html draggable='true'><body draggable='true' style='width: 100px; height: 100px;' ondragover='event.preventDefault()' ondrop='if (window.stopMode) event.stopPropagation(); if (window.cancelMode) event.preventDefault();'></body></html>"/>
</window>