--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -28,16 +28,17 @@
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "BatteryManager.h"
#include "mozilla/dom/CredentialsContainer.h"
+#include "mozilla/dom/Clipboard.h"
#include "mozilla/dom/GamepadServiceTest.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/MIDIAccessManager.h"
#include "mozilla/dom/MIDIOptionsBinding.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/Presentation.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
@@ -1790,16 +1791,25 @@ CredentialsContainer*
Navigator::Credentials()
{
if (!mCredentials) {
mCredentials = new CredentialsContainer(GetWindow());
}
return mCredentials;
}
+Clipboard*
+Navigator::Clipboard()
+{
+ if (!mClipboard) {
+ mClipboard = new dom::Clipboard(GetWindow());
+ }
+ return mClipboard;
+}
+
/* static */
bool
Navigator::Webdriver()
{
return Preferences::GetBool("marionette.enabled", false);
}
} // namespace dom
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -35,16 +35,17 @@ class Geolocation;
class systemMessageCallback;
class MediaDevices;
struct MediaStreamConstraints;
class WakeLock;
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class ServiceWorkerContainer;
class DOMRequest;
class CredentialsContainer;
+class Clipboard;
} // namespace dom
} // namespace mozilla
//*****************************************************************************
// Navigator: Script "navigator" object
//*****************************************************************************
namespace mozilla {
@@ -201,16 +202,17 @@ public:
NavigatorUserMediaErrorCallback& aOnError,
uint64_t aInnerWindowID,
const nsAString& aCallID,
ErrorResult& aRv);
already_AddRefed<ServiceWorkerContainer> ServiceWorker();
mozilla::dom::CredentialsContainer* Credentials();
+ mozilla::dom::Clipboard* Clipboard();
static bool Webdriver();
void GetLanguages(nsTArray<nsString>& aLanguages);
StorageManager* Storage();
static void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
@@ -260,16 +262,17 @@ private:
RefPtr<nsMimeTypeArray> mMimeTypes;
RefPtr<nsPluginArray> mPlugins;
RefPtr<Permissions> mPermissions;
RefPtr<Geolocation> mGeolocation;
RefPtr<battery::BatteryManager> mBatteryManager;
RefPtr<Promise> mBatteryPromise;
RefPtr<network::Connection> mConnection;
RefPtr<CredentialsContainer> mCredentials;
+ RefPtr<dom::Clipboard> mClipboard;
RefPtr<MediaDevices> mMediaDevices;
RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<Presentation> mPresentation;
RefPtr<GamepadServiceTest> mGamepadServiceTest;
nsTArray<RefPtr<Promise> > mVRGetDisplaysPromises;
RefPtr<VRServiceTest> mVRServiceTest;
nsTArray<uint32_t> mRequestedVibrationPattern;
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -9,16 +9,19 @@
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "mozilla/dom/NameSpaceConstants.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIdentifierMapEntry.h"
#include "nsTHashtable.h"
+#include "nsStubMutationObserver.h"
+#include "mozilla/dom/ShadowRootBinding.h"
+#include "mozilla/ServoBindingTypes.h"
class nsAtom;
class nsIContent;
class nsXBLPrototypeBinding;
namespace mozilla {
class EventChainPreVisitor;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -134,16 +134,20 @@ DOMInterfaces = {
'ChannelWrapper': {
'nativeType': 'mozilla::extensions::ChannelWrapper',
},
'CharacterData': {
'concrete': False
},
+'Clipboard' : {
+ 'implicitJSContext' : ['write', 'writeText', 'read', 'readText'],
+},
+
'console': {
'nativeType': 'mozilla::dom::Console',
},
'ConsoleInstance': {
'implicitJSContext': ['clear', 'count', 'groupEnd', 'time', 'timeEnd'],
},
new file mode 100644
--- /dev/null
+++ b/dom/events/Clipboard.cpp
@@ -0,0 +1,238 @@
+#include "mozilla/dom/Clipboard.h"
+#include "mozilla/dom/ClipboardBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "nsIClipboard.h"
+#include "nsISupportsPrimitives.h"
+#include "nsComponentManagerUtils.h"
+#include "nsITransferable.h"
+#include "nsArrayUtils.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DataTransferItemList.h"
+#include "mozilla/dom/DataTransferItem.h"
+
+
+static mozilla::LazyLogModule gClipboardLog("Clipboard");
+
+#define ASYNC_CLIPBOARD_TESTING_PREF "dom.events.testing.asyncClipboard"
+
+namespace mozilla {
+namespace dom {
+
+Clipboard::Clipboard(nsPIDOMWindowInner* aWindow)
+: DOMEventTargetHelper(aWindow)
+{
+
+}
+
+Clipboard::~Clipboard()
+{
+
+}
+
+already_AddRefed<Promise>
+Clipboard::ReadHelper(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv, ClipboardReadType aClipboardReadType)
+{
+ // Create a new promise
+ RefPtr<Promise> p = dom::Promise::Create(GetOwnerGlobal(), aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ // We want to disable security check for automated tests that have the pref
+ // dom.events.testing.asyncClipboard set to true
+ if (!IsTestingPrefEnabled() && !nsContentUtils::PrincipalHasPermission(&aSubjectPrincipal,
+ nsGkAtoms::clipboardRead)) {
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug, ("Clipboard, ReadHelper, "
+ "Don't have permissions for reading\n"));
+ p->MaybeRejectWithUndefined();
+ return p.forget();
+ }
+
+ // Want isExternal = true in order to use the data transfer object to perform a read
+ RefPtr<DataTransfer> dataTransfer = new DataTransfer(this, ePaste, /* is external */ true,
+ nsIClipboard::kGlobalClipboard);
+
+ // Create a new runnable
+ RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "Clipboard::Read",
+ [p, dataTransfer, &aSubjectPrincipal, aClipboardReadType]() {
+ IgnoredErrorResult ier;
+ dataTransfer->FillInExternalCustomTypes(/* index */ 0, &aSubjectPrincipal);
+ if (dataTransfer->Items()->Length() > 0) {
+ bool found;
+ DataTransferItem * item = dataTransfer->Items()->IndexedGetter(0, found);
+ if (found) { // We found an item at index 0 in the data transfer after reading the clipboard
+ switch (aClipboardReadType) {
+ case eRead:
+ // Return the item as is in the promise
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, ReadHelper, read case\n"));
+ p->MaybeResolve(dataTransfer);
+ return;
+ case eReadText:
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, ReadHelper, read text case\n"));
+ // Attempt to retrieve data as a string
+ nsCOMPtr<nsIVariant> data = item->Data(&aSubjectPrincipal, ier);
+ if (ier.Failed()) {
+ p->MaybeRejectWithUndefined();
+ return;
+ }
+ nsAutoString str;
+ data->GetAsDOMString(str);
+ p->MaybeResolve(str);
+ return;
+ }
+ } else { // We did not find an item at index 0
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, ReadHelper, Could not find an item at index 0 in the clipboard\n"));
+ p->MaybeRejectWithUndefined();
+ return;
+ }
+ } else {
+ // If there are no items in the data transfer after reading the clipboard,
+ // reject the promise
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, ReadHelper, No items in the clipboard\n"));
+ p->MaybeRejectWithUndefined();
+ return;
+ }
+ });
+ // Dispatch the runnable
+ GetParentObject()->Dispatch(TaskCategory::Other, r.forget());
+ return p.forget();
+}
+
+already_AddRefed<Promise>
+Clipboard::Read(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
+{
+ return ReadHelper(aCx, aSubjectPrincipal, aRv, eRead);
+}
+
+already_AddRefed<Promise>
+Clipboard::ReadText(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
+{
+ return ReadHelper(aCx, aSubjectPrincipal, aRv, eReadText);
+}
+
+already_AddRefed<Promise>
+Clipboard::Write(JSContext* aCx, DataTransfer& aData, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv)
+{
+ // Create a promise
+ RefPtr<Promise> p = dom::Promise::Create(GetOwnerGlobal(), aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ // We want to disable security check for automated tests that have the pref
+ // dom.events.testing.asyncClipboard set to true
+ if (!IsTestingPrefEnabled() && !nsContentUtils::IsCutCopyAllowed(&aSubjectPrincipal)) {
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, Write, Not allowed to write to clipboard\n"));
+ p->MaybeRejectWithUndefined();
+ return p.forget();
+ }
+
+ // Get the clipboard service
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
+ if (!clipboard) {
+ p->MaybeRejectWithUndefined();
+ return p.forget();
+ }
+
+ nsPIDOMWindowInner* owner = GetOwner();
+ nsIDocument* doc = owner ? owner->GetDoc() : nullptr;
+ nsILoadContext* context = doc ? doc->GetLoadContext() : nullptr;
+ if (!context) {
+ p->MaybeRejectWithUndefined();
+ return p.forget();
+ }
+
+ // Get the transferable
+ RefPtr<nsITransferable> transferable = aData.GetTransferable(0, context);
+ if (!transferable) {
+ p->MaybeRejectWithUndefined();
+ return p.forget();
+ }
+
+ // Create a runnable
+ RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "Clipboard::Write",
+ [transferable, p, clipboard]() {
+ nsresult rv = clipboard->SetData(transferable,
+ /* owner of the transferable */ nullptr,
+ nsIClipboard::kGlobalClipboard);
+ if (NS_FAILED(rv)) {
+ p->MaybeRejectWithUndefined();
+ return;
+ }
+ p->MaybeResolveWithUndefined();
+ return;
+ });
+ // Dispatch the runnable
+ GetParentObject()->Dispatch(TaskCategory::Other, r.forget());
+ return p.forget();
+}
+
+already_AddRefed<Promise>
+Clipboard::WriteText(JSContext* aCx, const nsAString& aData,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
+{
+ // We create a data transfer with text/plain format so that
+ // we can reuse Clipboard::Write(...) member function
+ RefPtr<DataTransfer> dataTransfer = new DataTransfer(this, eCopy,
+ /* is external */ true,
+ /* clipboard type */ -1);
+ NS_NAMED_LITERAL_STRING(format, "text/plain");
+ dataTransfer->SetData(format, aData, aSubjectPrincipal, aRv);
+ return Write(aCx, *dataTransfer, aSubjectPrincipal, aRv);
+}
+
+JSObject*
+Clipboard::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return ClipboardBinding::Wrap(aCx, this, aGivenProto);
+}
+
+/* static */ LogModule*
+Clipboard::GetClipboardLog()
+{
+ return gClipboardLog;
+}
+
+bool
+Clipboard::IsTestingPrefEnabled()
+{
+ static bool sPrefCached = false;
+ static bool sPrefCacheValue = false;
+
+ if (!sPrefCached) {
+ sPrefCached = true;
+ Preferences::AddBoolVarCache(&sPrefCacheValue, ASYNC_CLIPBOARD_TESTING_PREF);
+ }
+ MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
+ ("Clipboard, Is testing enabled? %d\n", sPrefCacheValue));
+ return sPrefCacheValue;
+}
+
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Clipboard)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Clipboard,
+ DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Clipboard,
+ DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Clipboard)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(Clipboard, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(Clipboard, DOMEventTargetHelper)
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/events/Clipboard.h
@@ -0,0 +1,60 @@
+#ifndef mozilla_dom_Clipboard_h_
+#define mozilla_dom_Clipboard_h_
+
+#include "nsString.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/Logging.h"
+
+namespace mozilla {
+namespace dom {
+
+enum ClipboardReadType {
+ eRead,
+ eReadText,
+};
+
+class Promise;
+
+// https://www.w3.org/TR/clipboard-apis/#clipboard-interface
+class Clipboard : public DOMEventTargetHelper
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Clipboard,
+ DOMEventTargetHelper)
+
+ IMPL_EVENT_HANDLER(message)
+ IMPL_EVENT_HANDLER(messageerror)
+
+ Clipboard(nsPIDOMWindowInner* aWindow);
+ already_AddRefed<Promise> Read(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv);
+ already_AddRefed<Promise> ReadText(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv);
+ already_AddRefed<Promise> Write(JSContext* aCx, DataTransfer& aData,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
+ already_AddRefed<Promise> WriteText(JSContext* aCx, const nsAString& aData,
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
+
+ static LogModule* GetClipboardLog();
+
+
+ virtual JSObject*
+ WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+private:
+ // Checks if dom.events.testing.asyncClipboard pref is enabled.
+ // The aforementioned pref allows automated tests to bypass the security checks when writing to
+ // or reading from the clipboard.
+ bool IsTestingPrefEnabled();
+
+ already_AddRefed<Promise> ReadHelper(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
+ ErrorResult& aRv, ClipboardReadType aClipboardReadType);
+ ~Clipboard();
+
+
+};
+
+} // namespace dom
+} // namespace mozilla
+#endif // mozilla_dom_Clipboard_h_
\ No newline at end of file
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -1136,38 +1136,34 @@ DataTransfer::ConvertFromVariant(nsIVari
ptrSupports.forget(aSupports);
*aLength = sizeof(nsISupportsInterfacePointer *);
}
return true;
}
- char16_t* chrs;
- uint32_t len = 0;
- nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
+ nsAutoString str;
+ nsresult rv = aVariant->GetAsAString(str);
if (NS_FAILED(rv)) {
return false;
}
- nsAutoString str;
- str.Adopt(chrs, len);
-
nsCOMPtr<nsISupportsString>
strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (!strSupports) {
return false;
}
strSupports->SetData(str);
strSupports.forget(aSupports);
// each character is two bytes
- *aLength = str.Length() << 1;
+ *aLength = str.Length() * 2;
return true;
}
void
DataTransfer::Disconnect()
{
SetMode(Mode::Protected);
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -437,16 +437,17 @@ protected:
nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
nsIPrincipal* aSubjectPrincipal,
nsIVariant** aData);
nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
uint32_t aIndex, nsIPrincipal* aSubjectPrincipal);
friend class ContentParent;
+ friend class Clipboard;
void FillAllExternalData();
void FillInExternalCustomTypes(uint32_t aIndex, nsIPrincipal* aPrincipal);
void FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
nsIPrincipal* aPrincipal);
--- a/dom/events/DataTransferItemList.cpp
+++ b/dom/events/DataTransferItemList.cpp
@@ -147,17 +147,18 @@ DataTransferItemList::Add(const nsAStrin
const nsAString& aType,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
return nullptr;
}
- nsCOMPtr<nsIVariant> data(new storage::TextVariant(aData));
+ RefPtr<nsVariantCC> data(new nsVariantCC());
+ data->SetAsAString(aData);
nsAutoString format;
mDataTransfer->GetRealFormat(aType, format);
if (!DataTransfer::PrincipalMaySetData(format, data, &aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -4,16 +4,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessCheck.h"
#include "base/basictypes.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/EventDispatcher.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
@@ -30,16 +31,17 @@
#include "nsIContentInlines.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsIScrollableFrame.h"
#include "nsJSEnvironment.h"
#include "nsLayoutUtils.h"
#include "nsPIWindowRoot.h"
#include "nsRFPService.h"
+#include "nsStubMutationObserver.h"
namespace mozilla {
namespace dom {
static char *sPopupAllowedEvents;
static bool sReturnHighResTimeStamp = false;
static bool sReturnHighResTimeStampIsSet = false;
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -42,16 +42,17 @@ EXPORTS.mozilla += [
'TextComposition.h',
'VirtualKeyCodeList.h',
'WheelHandlingHelper.h',
]
EXPORTS.mozilla.dom += [
'AnimationEvent.h',
'BeforeUnloadEvent.h',
+ 'Clipboard.h',
'ClipboardEvent.h',
'CommandEvent.h',
'CompositionEvent.h',
'ConstructibleEventTarget.h',
'CustomEvent.h',
'DataTransfer.h',
'DataTransferItem.h',
'DataTransferItemList.h',
@@ -85,16 +86,17 @@ EXPORTS.mozilla.dom += [
if CONFIG['MOZ_WEBSPEECH']:
EXPORTS.mozilla.dom += ['SpeechRecognitionError.h']
UNIFIED_SOURCES += [
'AnimationEvent.cpp',
'AsyncEventDispatcher.cpp',
'BeforeUnloadEvent.cpp',
+ 'Clipboard.cpp',
'ClipboardEvent.cpp',
'CommandEvent.cpp',
'CompositionEvent.cpp',
'ConstructibleEventTarget.cpp',
'ContentEventHandler.cpp',
'CustomEvent.cpp',
'DataTransfer.cpp',
'DataTransferItem.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Clipboard.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/geolocation-API
+ *
+ * Copyright © 2018 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+
+[SecureContext, Exposed=Window] interface Clipboard : EventTarget {
+ [Pref="dom.events.asyncClipboard.dataTransfer", Throws, NeedsSubjectPrincipal]
+ Promise<DataTransfer> read();
+ [Throws, NeedsSubjectPrincipal]
+ Promise<DOMString> readText();
+ [Pref="dom.events.asyncClipboard.dataTransfer", Throws, NeedsSubjectPrincipal]
+ Promise<void> write(DataTransfer data);
+ [Throws, NeedsSubjectPrincipal]
+ Promise<void> writeText(DOMString data);
+};
\ No newline at end of file
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -318,8 +318,14 @@ partial interface Navigator {
};
// https://w3c.github.io/webdriver/webdriver-spec.html#interface
[NoInterfaceObject]
interface NavigatorAutomationInformation {
[Pref="dom.webdriver.enabled"]
readonly attribute boolean webdriver;
};
+
+// https://www.w3.org/TR/clipboard-apis/#navigator-interface
+partial interface Navigator {
+ [Pref="dom.events.asyncClipboard", SecureContext, SameObject]
+ readonly attribute Clipboard clipboard;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -53,16 +53,19 @@ with Files("Caret*"):
BUG_COMPONENT = ("Core", "Editor")
with Files("Channel*"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("Client*"):
BUG_COMPONENT = ("Core", "DOM: Service Workers")
+with Files("Clipboard.webidl"):
+ BUG_COMPONENT = ("Core", "DOM: Events")
+
with Files("ClipboardEvent.webidl"):
BUG_COMPONENT = ("Core", "DOM: Events")
with Files("ConstantSourceNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
with Files("ConvolverNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
@@ -408,16 +411,17 @@ WEBIDL_FILES = [
'ChannelSplitterNode.webidl',
'CharacterData.webidl',
'CheckerboardReportService.webidl',
'ChildNode.webidl',
'ChildSHistory.webidl',
'ChromeNodeList.webidl',
'Client.webidl',
'Clients.webidl',
+ 'Clipboard.webidl',
'ClipboardEvent.webidl',
'CommandEvent.webidl',
'Comment.webidl',
'CompositionEvent.webidl',
'Console.webidl',
'ConstantSourceNode.webidl',
'ConvolverNode.webidl',
'Coordinates.webidl',
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5778,8 +5778,14 @@ pref("layers.omtp.dump-capture", false);
// Limits the depth of recursive conversion of data when opening
// a content to view. This is mostly intended to prevent infinite
// loops with faulty converters involved.
pref("general.document_open_conversion_depth_limit", 20);
// If true, touchstart and touchmove listeners on window, document,
// documentElement and document.body are passive by default.
pref("dom.event.default_to_passive_touch_listeners", true);
+
+// Disables clipboard reads and writes by default.
+pref("dom.events.asyncClipboard", false);
+pref("dom.events.asyncClipboard.dataTransfer", false);
+// Should only be enabled in tests
+pref("dom.events.testing.asyncClipboard", false);
\ No newline at end of file
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -409317,17 +409317,17 @@
"829a1adb041ade08a029dcd66d90249ea4ffc923",
"support"
],
"clipboard-apis/OWNERS": [
"bae753666bda85e9805a5e2fad80ba71459b8aba",
"support"
],
"clipboard-apis/async-interfaces.https.html": [
- "ffc8726b7afa6dc3e58745bd0dac525a570970ef",
+ "ac36117a97fa34f96451c92915e6dbef2d3257f2",
"testharness"
],
"clipboard-apis/async-navigator-clipboard-basics.https.html": [
"59f25a9968d47079857989146e26562c3784be34",
"testharness"
],
"clipboard-apis/async-write-dttext-read-dttext-manual.https.html": [
"7b2a4d7f1e1e918f8a96694f6619875b746d0255",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/clipboard-apis/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.events.asyncClipboard:true, dom.events.asyncClipboard.dataTransfer:true, dom.events.testing.asyncClipboard:true]
deleted file mode 100644
--- a/testing/web-platform/meta/clipboard-apis/async-interfaces.https.html.ini
+++ /dev/null
@@ -1,79 +0,0 @@
-[async-interfaces.https.html]
- [Navigator interface: attribute clipboard]
- expected: FAIL
-
- [Navigator interface: navigator must inherit property "clipboard" with the proper type (0)]
- expected: FAIL
-
- [Clipboard interface: existence and properties of interface object]
- expected: FAIL
-
- [Clipboard interface object length]
- expected: FAIL
-
- [Clipboard interface object name]
- expected: FAIL
-
- [Clipboard interface: existence and properties of interface prototype object]
- expected: FAIL
-
- [Clipboard interface: existence and properties of interface prototype object's "constructor" property]
- expected: FAIL
-
- [Clipboard interface: existence and properties of interface prototype object's @@unscopables property]
- expected: FAIL
-
- [Clipboard interface: operation read()]
- expected: FAIL
-
- [Clipboard interface: operation readText()]
- expected: FAIL
-
- [Clipboard interface: operation write(DataTransfer)]
- expected: FAIL
-
- [Clipboard interface: operation writeText(DOMString)]
- expected: FAIL
-
- [Clipboard must be primary interface of navigator.clipboard]
- expected: FAIL
-
- [Stringification of navigator.clipboard]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "read" with the proper type (0)]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "readText" with the proper type (1)]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "write" with the proper type (2)]
- expected: FAIL
-
- [Clipboard interface: calling write(DataTransfer) on navigator.clipboard with too few arguments must throw TypeError]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "writeText" with the proper type (3)]
- expected: FAIL
-
- [Clipboard interface: calling writeText(DOMString) on navigator.clipboard with too few arguments must throw TypeError]
- expected: FAIL
-
- [Navigator interface: navigator must inherit property "clipboard" with the proper type]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "read()" with the proper type]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "readText()" with the proper type]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "write(DataTransfer)" with the proper type]
- expected: FAIL
-
- [Clipboard interface: navigator.clipboard must inherit property "writeText(DOMString)" with the proper type]
- expected: FAIL
-
- [ClipboardEvent interface: new ClipboardEvent("x") must inherit property "clipboardData" with the proper type]
- expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/clipboard-apis/async-navigator-clipboard-basics.https.html.ini
+++ /dev/null
@@ -1,28 +0,0 @@
-[async-navigator-clipboard-basics.https.html]
- [navigator.clipboard exists]
- expected: FAIL
-
- [navigator.clipboard.write(DataTransfer) succeeds]
- expected: FAIL
-
- [navigator.clipboard.write() fails (expect DataTransfer)]
- expected: FAIL
-
- [navigator.clipboard.write(null) fails (expect DataTransfer)]
- expected: FAIL
-
- [navigator.clipboard.write(DOMString) fails (expect DataTransfer)]
- expected: FAIL
-
- [navigator.clipboard.writeText(DOMString) succeeds]
- expected: FAIL
-
- [navigator.clipboard.writeText() fails (expect DOMString)]
- expected: FAIL
-
- [navigator.clipboard.read() succeeds]
- expected: FAIL
-
- [navigator.clipboard.readText() succeeds]
- expected: FAIL
-
--- a/testing/web-platform/tests/clipboard-apis/async-interfaces.https.html
+++ b/testing/web-platform/tests/clipboard-apis/async-interfaces.https.html
@@ -10,16 +10,17 @@
'use strict';
function doTest(idl, dom) {
var idl_array = new IdlArray();
idl_array.add_untested_idls('interface Navigator {};');
idl_array.add_untested_idls('interface EventTarget {};');
idl_array.add_untested_idls('dictionary PermissionDescriptor {};');
idl_array.add_untested_idls(dom, { only: ['Event', 'EventInit'] });
+ idl_array.add_untested_idls('interface DataTransfer {};');
idl_array.add_idls(idl);
idl_array.add_objects({
Navigator: ['navigator'],
Clipboard: ['navigator.clipboard'],
ClipboardEvent: ['new ClipboardEvent("x")'],
});
idl_array.test();
};
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -49,16 +49,17 @@ support-files =
slow_response.sjs
webrequest_worker.js
!/dom/tests/mochitest/geolocation/network_geolocation.sjs
!/toolkit/components/passwordmgr/test/authenticate.sjs
file_redirect_data_uri.html
prefs =
security.mixed_content.upgrade_display_content=false
+[test_ext_async_clipboard.html]
[test_ext_background_canvas.html]
[test_ext_background_page.html]
skip-if = (toolkit == 'android') # android doesn't have devtools
[test_ext_clipboard.html]
[test_ext_clipboard_image.html]
skip-if = headless # disabled test case with_permission_allow_copy, see inline comment. Headless: Bug 1405872
[test_ext_contentscript_about_blank.html]
skip-if = os == 'android' # bug 1369440
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Async Clipboard permissions tests</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/AddTask.js"></script>
+ <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <script src="head.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+/* globals clipboardWriteText, clipboardReadText */
+function shared() {
+ this.clipboardWriteText = function(txt) {
+ return navigator.clipboard.writeText(txt);
+ };
+
+ this.clipboardReadText = function() {
+ return navigator.clipboard.readText();
+ };
+}
+
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.events.asyncClipboard", true]]});
+ await SpecialPowers.pushPrefEnv({"set": [["dom.events.asyncClipboard.dataTransfer", true]]});
+});
+
+// Test that without enough permissions, we are NOT allowed to use writeText and readText in background script
+add_task(async function test_background_async_clipboard_no_permissions() {
+ function backgroundScript() {
+ browser.test.assertRejects(clipboardWriteText("blabla"), undefined, "WriteText should be denied without permission");
+ browser.test.assertRejects(clipboardReadText(), undefined, "ReadText should be denied without permission");
+ browser.test.sendMessage("ready");
+ }
+ let extensionData = {
+ background: [shared, backgroundScript],
+ };
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+ await extension.awaitMessage("ready");
+ await extension.unload();
+});
+
+// Test that without enough permissions, we are NOT allowed to use writeText and readText in content script
+add_task(async function test_contentscript_async_clipboard_no_permission() {
+ function contentScript() {
+ browser.test.assertRejects(clipboardWriteText("blabla"), undefined, "WriteText should be denied without permission");
+ browser.test.assertRejects(clipboardReadText(), undefined, "ReadText should be denied without permission");
+ browser.test.sendMessage("ready");
+ }
+ let extensionData = {
+ manifest: {
+ content_scripts: [{
+ js: ["shared.js", "contentscript.js"],
+ matches: ["https://example.com/*/file_sample.html"],
+ }],
+ },
+ files: {
+ "shared.js": shared,
+ "contentscript.js": contentScript,
+ },
+ };
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+ let win = window.open("https://example.com/tests/toolkit/components/extensions/test/mochitest/test-oop-extensions/file_sample.html");
+ await extension.awaitMessage("ready");
+ await extension.unload();
+ win.close();
+});
+
+// Test that with enough permissions, we are allowed to use writeText and readText in content script
+add_task(async function test_contentscript_clipboard_permission() {
+ function contentScript() {
+ let str = "blabla";
+ clipboardWriteText(str).then(function() {
+ browser.test.succeed("WriteText promise successfully resolved");
+ }, function(err) {
+ browser.test.fail("Write promise rejected");
+ });
+ // XXX No other modification of the clipboard should happen meanwhile
+ clipboardReadText().then(function(strData) {
+ if (strData != str) {
+ browser.test.fail("ReadText read the wrong thing from clipboard");
+ } else {
+ browser.test.succeed("ReadText promise successfully resolved");
+ }
+ }, function(err) {
+ browser.test.fail("Read text promise rejected" + err);
+ });
+ browser.test.sendMessage("ready");
+ }
+ let extensionData = {
+ manifest: {
+ content_scripts: [{
+ js: ["shared.js", "contentscript.js"],
+ matches: ["https://example.com/*/file_sample.html"],
+ }],
+ permissions: [
+ "clipboardWrite",
+ "clipboardRead",
+ ],
+ },
+ files: {
+ "shared.js": shared,
+ "contentscript.js": contentScript,
+ },
+ };
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+ let win = window.open("https://example.com/tests/toolkit/components/extensions/test/mochitest/test-oop-extensions/file_sample.html");
+ await extension.awaitMessage("ready");
+ await extension.unload();
+ win.close();
+});
+</script>
+</body>
+</html>
\ No newline at end of file