author | Neil Deakin <neil@mozilla.com> |
Wed, 11 May 2016 10:04:19 -0400 (2016-05-11) | |
changeset 297004 | d4e621e02edccfff3eb4cb6b2a47655eca845280 |
parent 297003 | ccf7dc442e1fea2b7ae1c1f28a6c75cbd54a3102 |
child 297005 | bb36d2769fe3689817a8b33f9a6c0eb6d63ad1d2 |
push id | 30251 |
push user | cbook@mozilla.com |
push date | Thu, 12 May 2016 09:54:48 +0000 (2016-05-12) |
treeherder | mozilla-central@c3f5e6079284 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1249522 |
milestone | 49.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -21,16 +21,17 @@ support-files = browser_web_channel.html browser_web_channel_iframe.html bug1262648_string_with_newlines.dtd bug592338.html bug792517-2.html bug792517.html bug792517.sjs bug839103.css + clipboard_pastefile.html contextmenu_common.js ctxmenu-image.png discovery.html domplate_test.js download_page.html dummy_page.html feed_tab.html file_generic_favicon.ico @@ -272,16 +273,17 @@ tags = mcb [browser_mixedContentFramesOnHttp.js] tags = mcb [browser_bug970746.js] [browser_bug1015721.js] skip-if = os == 'win' [browser_bug1064280_changeUrlInPinnedTab.js] [browser_accesskeys.js] [browser_clipboard.js] +[browser_clipboard_pastefile.js] [browser_contentAreaClick.js] [browser_contextmenu.js] tags = fullscreen skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558 [browser_contextmenu_input.js] skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558 [browser_ctrlTab.js] [browser_datachoices_notification.js]
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/browser_clipboard_pastefile.js @@ -0,0 +1,59 @@ +// This test is used to check that pasting files removes all non-file data from +// event.clipboardData. + +add_task(function*() { + var searchbar = document.getElementById("searchbar"); + + searchbar.focus(); + searchbar.value = "Text"; + searchbar.select(); + + yield new Promise((resolve, reject) => { + searchbar.addEventListener("copy", function copyEvent(event) { + searchbar.removeEventListener("copy", copyEvent, true); + event.clipboardData.setData("text/plain", "Alternate"); + // For this test, it doesn't matter that the file isn't actually a file. + event.clipboardData.setData("application/x-moz-file", "Sample"); + event.preventDefault(); + resolve(); + }, true) + + EventUtils.synthesizeKey("c", { accelKey: true }); + }); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, + "https://example.com/browser/browser/base/content/test/general/clipboard_pastefile.html"); + let browser = tab.linkedBrowser; + + yield ContentTask.spawn(browser, { }, function* (arg) { + content.document.getElementById("input").focus(); + }); + + yield BrowserTestUtils.synthesizeKey("v", { accelKey: true }, browser); + + let output = yield ContentTask.spawn(browser, { }, function* (arg) { + return content.document.getElementById("output").textContent; + }); + is (output, "Passed", "Paste file"); + + searchbar.focus(); + + yield new Promise((resolve, reject) => { + searchbar.addEventListener("paste", function copyEvent(event) { + searchbar.removeEventListener("paste", copyEvent, true); + + let dt = event.clipboardData; + is(dt.types.length, 3, "number of types"); + ok(dt.types.contains("text/plain"), "text/plain exists in types"); + ok(dt.mozTypesAt(0).contains("text/plain"), "text/plain exists in mozTypesAt"); + is(dt.getData("text/plain"), "Alternate", "text/plain returned in getData"); + is(dt.mozGetDataAt("text/plain", 0), "Alternate", "text/plain returned in mozGetDataAt"); + + resolve(); + }, true); + + EventUtils.synthesizeKey("v", { accelKey: true }); + }); + + yield BrowserTestUtils.removeTab(tab); +});
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/general/clipboard_pastefile.html @@ -0,0 +1,37 @@ +<html><body> +<script> +function checkPaste(event) +{ + let output = document.getElementById("output"); + output.textContent = checkPasteHelper(event); +} + +function checkPasteHelper(event) +{ + let dt = event.clipboardData; + if (dt.types.length != 2) + return "Wrong number of types; got " + dt.types.length; + + for (let type of dt.types) { + if (type != "Files" && type != "application/x-moz-file") + return "Invalid type for types; got" + type; + } + + for (let type of dt.mozTypesAt(0)) { + if (type != "Files" && type != "application/x-moz-file") + return "Invalid type for mozTypesAt; got" + type; + } + + if (dt.getData("text/plain")) + return "text/plain found with getData"; + if (dt.mozGetDataAt("text/plain", 0)) + return "text/plain found with mozGetDataAt"; + + return "Passed"; +} +</script> + +<input id="input" onpaste="checkPaste(event)"> +<div id="output"></div> + +</body></html>
--- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -373,35 +373,18 @@ DataTransfer::GetFiles(nsIDOMFileList** NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal())); return rv.StealNSResult(); } already_AddRefed<DOMStringList> DataTransfer::Types() const { - RefPtr<DOMStringList> types = new DOMStringList(); - if (mItems.Length()) { - bool addFile = false; - const nsTArray<TransferItem>& item = mItems[0]; - for (uint32_t i = 0; i < item.Length(); i++) { - const nsString& format = item[i].mFormat; - types->Add(format); - if (!addFile) { - addFile = format.EqualsASCII(kFileMime) || - format.EqualsASCII("application/x-moz-file-promise"); - } - } - - if (addFile) { - types->Add(NS_LITERAL_STRING("Files")); - } - } - - return types.forget(); + ErrorResult rv; + return MozTypesAt(0, rv); } NS_IMETHODIMP DataTransfer::GetTypes(nsISupports** aTypes) { RefPtr<DOMStringList> types = Types(); types.forget(aTypes); @@ -565,32 +548,49 @@ DataTransfer::GetMozSourceNode(nsIDOMNod *aSourceNode = nullptr; return NS_OK; } return CallQueryInterface(sourceNode, aSourceNode); } already_AddRefed<DOMStringList> -DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) +DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) const { // Only the first item is valid for clipboard events if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy || mEventMessage == ePaste)) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } RefPtr<DOMStringList> types = new DOMStringList(); if (aIndex < mItems.Length()) { + bool addFile = false; // note that you can retrieve the types regardless of their principal - nsTArray<TransferItem>& item = mItems[aIndex]; + const nsTArray<TransferItem>& item = mItems[aIndex]; for (uint32_t i = 0; i < item.Length(); i++) { - types->Add(item[i].mFormat); + const nsString& format = item[i].mFormat; + types->Add(format); + if (!addFile) { + addFile = format.EqualsASCII(kFileMime); + } + } + + if (addFile) { + // If this is a content caller, and a file is in the data transfer, remove + // the non-file types. This prevents alternate text forms of the file + // from being returned. + if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { + types->Clear(); + types->Add(NS_LITERAL_STRING(kFileMime)); + } + + types->Add(NS_LITERAL_STRING("Files")); } } return types.forget(); } NS_IMETHODIMP DataTransfer::MozTypesAt(uint32_t aIndex, nsISupports** aTypes) @@ -627,22 +627,33 @@ DataTransfer::GetDataAtInternal(const ns // Only the first item is valid for clipboard events if (aIndex > 0 && (mEventMessage == eCut || mEventMessage == eCopy || mEventMessage == ePaste)) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } - nsAutoString format; GetRealFormat(aFormat, format); nsTArray<TransferItem>& item = mItems[aIndex]; + // If this is a content caller, and a file is in the data transfer, only + // return the file type. + if (!format.EqualsLiteral(kFileMime) && + !nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) { + uint32_t count = item.Length(); + for (uint32_t i = 0; i < count; i++) { + if (item[i].mFormat.EqualsLiteral(kFileMime)) { + return NS_OK; + } + } + } + // Check if the caller is allowed to access the drag data. Callers with // chrome privileges can always read the data. During the // drop event, allow retrieving the data except in the case where the // source of the drag is in a child frame of the caller. In that case, // we only allow access to data of the same principal. During other events, // only allow access to the data with the same principal. bool checkFormatItemPrincipal = mIsCrossDomainSubFrameDrop || (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop && @@ -741,18 +752,18 @@ DataTransfer::SetDataAtInternal(const ns // Don't allow the custom type to be assigned. if (aFormat.EqualsLiteral(kCustomTypesMime)) { return NS_ERROR_TYPE_ERR; } // Don't allow non-chrome to add non-string or file data. We'll block file // promises as well which are used internally for drags to the desktop. if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) { - if (aFormat.EqualsLiteral("application/x-moz-file-promise") || - aFormat.EqualsLiteral("application/x-moz-file")) { + if (aFormat.EqualsLiteral(kFilePromiseMime) || + aFormat.EqualsLiteral(kFileMime)) { return NS_ERROR_DOM_SECURITY_ERR; } uint16_t type; aData->GetDataType(&type); if (type == nsIDataType::VTYPE_INTERFACE || type == nsIDataType::VTYPE_INTERFACE_IS) { return NS_ERROR_DOM_SECURITY_ERR;
--- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -175,17 +175,17 @@ public: if (mCursorState) { aCursor.AssignLiteral("default"); } else { aCursor.AssignLiteral("auto"); } } already_AddRefed<DOMStringList> MozTypesAt(uint32_t aIndex, - mozilla::ErrorResult& aRv); + mozilla::ErrorResult& aRv) const; void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex, mozilla::ErrorResult& aRv); void MozSetDataAt(JSContext* aCx, const nsAString& aFormat, JS::Handle<JS::Value> aData, uint32_t aIndex, mozilla::ErrorResult& aRv);
--- a/testing/mochitest/chrome/test_sanityChromeUtils.xul +++ b/testing/mochitest/chrome/test_sanityChromeUtils.xul @@ -60,17 +60,19 @@ { type : "text/uri-list", data : "http://www.mozilla.org/" }, { type : "text/plain", data : "this is text/plain" } ]]; var dragfile = [[ { type : "application/x-moz-file", data : testFile, - eqTest : function(actualData, expectedData) {return expectedData.equals(actualData);} } + eqTest : function(actualData, expectedData) {return expectedData.equals(actualData);} }, + { type : "Files", + data : null } ]]; function doOnDrop(aEvent) { gData = aEvent.dataTransfer.getData(dragDrop[0][0].type); aEvent.preventDefault(); // cancels event and keeps dropEffect // as was before event. }