dom/events/DataTransfer.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/ArrayUtils.h"
michael@0 8 #include "mozilla/BasicEvents.h"
michael@0 9
michael@0 10 #include "DataTransfer.h"
michael@0 11
michael@0 12 #include "nsIDOMDocument.h"
michael@0 13 #include "nsIVariant.h"
michael@0 14 #include "nsISupportsPrimitives.h"
michael@0 15 #include "nsIScriptSecurityManager.h"
michael@0 16 #include "mozilla/dom/DOMStringList.h"
michael@0 17 #include "nsError.h"
michael@0 18 #include "nsIDragService.h"
michael@0 19 #include "nsIClipboard.h"
michael@0 20 #include "nsContentUtils.h"
michael@0 21 #include "nsIContent.h"
michael@0 22 #include "nsCRT.h"
michael@0 23 #include "nsIScriptObjectPrincipal.h"
michael@0 24 #include "nsIScriptContext.h"
michael@0 25 #include "nsIDocument.h"
michael@0 26 #include "nsIScriptGlobalObject.h"
michael@0 27 #include "mozilla/dom/DataTransferBinding.h"
michael@0 28 #include "mozilla/dom/Element.h"
michael@0 29 #include "mozilla/dom/BindingUtils.h"
michael@0 30
michael@0 31 namespace mozilla {
michael@0 32 namespace dom {
michael@0 33
michael@0 34 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
michael@0 35
michael@0 36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
michael@0 37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
michael@0 38 if (tmp->mFiles) {
michael@0 39 tmp->mFiles->Disconnect();
michael@0 40 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
michael@0 41 }
michael@0 42 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
michael@0 43 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
michael@0 44 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
michael@0 45 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
michael@0 47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
michael@0 48 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
michael@0 49 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
michael@0 50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
michael@0 51 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 53 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
michael@0 54
michael@0 55 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
michael@0 56 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
michael@0 57
michael@0 58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
michael@0 59 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 60 NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
michael@0 61 NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
michael@0 62 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
michael@0 63 NS_INTERFACE_MAP_END
michael@0 64
michael@0 65 // the size of the array
michael@0 66 const char DataTransfer::sEffects[8][9] = {
michael@0 67 "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
michael@0 68 };
michael@0 69
michael@0 70 DataTransfer::DataTransfer(nsISupports* aParent, uint32_t aEventType,
michael@0 71 bool aIsExternal, int32_t aClipboardType)
michael@0 72 : mParent(aParent),
michael@0 73 mEventType(aEventType),
michael@0 74 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
michael@0 75 mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
michael@0 76 mCursorState(false),
michael@0 77 mReadOnly(true),
michael@0 78 mIsExternal(aIsExternal),
michael@0 79 mUserCancelled(false),
michael@0 80 mIsCrossDomainSubFrameDrop(false),
michael@0 81 mClipboardType(aClipboardType),
michael@0 82 mDragImageX(0),
michael@0 83 mDragImageY(0)
michael@0 84 {
michael@0 85 MOZ_ASSERT(mParent);
michael@0 86 SetIsDOMBinding();
michael@0 87 // For these events, we want to be able to add data to the data transfer, so
michael@0 88 // clear the readonly state. Otherwise, the data is already present. For
michael@0 89 // external usage, cache the data from the native clipboard or drag.
michael@0 90 if (aEventType == NS_CUT ||
michael@0 91 aEventType == NS_COPY ||
michael@0 92 aEventType == NS_DRAGDROP_START ||
michael@0 93 aEventType == NS_DRAGDROP_GESTURE) {
michael@0 94 mReadOnly = false;
michael@0 95 } else if (mIsExternal) {
michael@0 96 if (aEventType == NS_PASTE) {
michael@0 97 CacheExternalClipboardFormats();
michael@0 98 } else if (aEventType >= NS_DRAGDROP_EVENT_START && aEventType <= NS_DRAGDROP_LEAVE_SYNTH) {
michael@0 99 CacheExternalDragFormats();
michael@0 100 }
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 DataTransfer::DataTransfer(nsISupports* aParent,
michael@0 105 uint32_t aEventType,
michael@0 106 const uint32_t aEffectAllowed,
michael@0 107 bool aCursorState,
michael@0 108 bool aIsExternal,
michael@0 109 bool aUserCancelled,
michael@0 110 bool aIsCrossDomainSubFrameDrop,
michael@0 111 int32_t aClipboardType,
michael@0 112 nsTArray<nsTArray<TransferItem> >& aItems,
michael@0 113 Element* aDragImage,
michael@0 114 uint32_t aDragImageX,
michael@0 115 uint32_t aDragImageY)
michael@0 116 : mParent(aParent),
michael@0 117 mEventType(aEventType),
michael@0 118 mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
michael@0 119 mEffectAllowed(aEffectAllowed),
michael@0 120 mCursorState(aCursorState),
michael@0 121 mReadOnly(true),
michael@0 122 mIsExternal(aIsExternal),
michael@0 123 mUserCancelled(aUserCancelled),
michael@0 124 mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop),
michael@0 125 mClipboardType(aClipboardType),
michael@0 126 mItems(aItems),
michael@0 127 mDragImage(aDragImage),
michael@0 128 mDragImageX(aDragImageX),
michael@0 129 mDragImageY(aDragImageY)
michael@0 130 {
michael@0 131 MOZ_ASSERT(mParent);
michael@0 132 SetIsDOMBinding();
michael@0 133 // The items are copied from aItems into mItems. There is no need to copy
michael@0 134 // the actual data in the items as the data transfer will be read only. The
michael@0 135 // draggesture and dragstart events are the only times when items are
michael@0 136 // modifiable, but those events should have been using the first constructor
michael@0 137 // above.
michael@0 138 NS_ASSERTION(aEventType != NS_DRAGDROP_GESTURE &&
michael@0 139 aEventType != NS_DRAGDROP_START,
michael@0 140 "invalid event type for DataTransfer constructor");
michael@0 141 }
michael@0 142
michael@0 143 DataTransfer::~DataTransfer()
michael@0 144 {
michael@0 145 if (mFiles) {
michael@0 146 mFiles->Disconnect();
michael@0 147 }
michael@0 148 }
michael@0 149
michael@0 150 // static
michael@0 151 already_AddRefed<DataTransfer>
michael@0 152 DataTransfer::Constructor(const GlobalObject& aGlobal,
michael@0 153 const nsAString& aEventType, bool aIsExternal,
michael@0 154 ErrorResult& aRv)
michael@0 155 {
michael@0 156 nsAutoCString onEventType("on");
michael@0 157 AppendUTF16toUTF8(aEventType, onEventType);
michael@0 158 nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(onEventType);
michael@0 159 if (!eventTypeAtom) {
michael@0 160 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 161 return nullptr;
michael@0 162 }
michael@0 163
michael@0 164 uint32_t eventType = nsContentUtils::GetEventId(eventTypeAtom);
michael@0 165 nsRefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
michael@0 166 eventType, aIsExternal,
michael@0 167 -1);
michael@0 168 return transfer.forget();
michael@0 169 }
michael@0 170
michael@0 171 JSObject*
michael@0 172 DataTransfer::WrapObject(JSContext* aCx)
michael@0 173 {
michael@0 174 return DataTransferBinding::Wrap(aCx, this);
michael@0 175 }
michael@0 176
michael@0 177 NS_IMETHODIMP
michael@0 178 DataTransfer::GetDropEffect(nsAString& aDropEffect)
michael@0 179 {
michael@0 180 nsString dropEffect;
michael@0 181 GetDropEffect(dropEffect);
michael@0 182 aDropEffect = dropEffect;
michael@0 183 return NS_OK;
michael@0 184 }
michael@0 185
michael@0 186 NS_IMETHODIMP
michael@0 187 DataTransfer::SetDropEffect(const nsAString& aDropEffect)
michael@0 188 {
michael@0 189 // the drop effect can only be 'none', 'copy', 'move' or 'link'.
michael@0 190 for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
michael@0 191 if (aDropEffect.EqualsASCII(sEffects[e])) {
michael@0 192 // don't allow copyMove
michael@0 193 if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
michael@0 194 nsIDragService::DRAGDROP_ACTION_MOVE))
michael@0 195 mDropEffect = e;
michael@0 196 break;
michael@0 197 }
michael@0 198 }
michael@0 199
michael@0 200 return NS_OK;
michael@0 201 }
michael@0 202
michael@0 203 NS_IMETHODIMP
michael@0 204 DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
michael@0 205 {
michael@0 206 nsString effectAllowed;
michael@0 207 GetEffectAllowed(effectAllowed);
michael@0 208 aEffectAllowed = effectAllowed;
michael@0 209 return NS_OK;
michael@0 210 }
michael@0 211
michael@0 212 NS_IMETHODIMP
michael@0 213 DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
michael@0 214 {
michael@0 215 if (aEffectAllowed.EqualsLiteral("uninitialized")) {
michael@0 216 mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
michael@0 217 return NS_OK;
michael@0 218 }
michael@0 219
michael@0 220 static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
michael@0 221 "DRAGDROP_ACTION_NONE constant is wrong");
michael@0 222 static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
michael@0 223 "DRAGDROP_ACTION_COPY constant is wrong");
michael@0 224 static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
michael@0 225 "DRAGDROP_ACTION_MOVE constant is wrong");
michael@0 226 static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
michael@0 227 "DRAGDROP_ACTION_LINK constant is wrong");
michael@0 228
michael@0 229 for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
michael@0 230 if (aEffectAllowed.EqualsASCII(sEffects[e])) {
michael@0 231 mEffectAllowed = e;
michael@0 232 break;
michael@0 233 }
michael@0 234 }
michael@0 235
michael@0 236 return NS_OK;
michael@0 237 }
michael@0 238
michael@0 239 NS_IMETHODIMP
michael@0 240 DataTransfer::GetDropEffectInt(uint32_t* aDropEffect)
michael@0 241 {
michael@0 242 *aDropEffect = mDropEffect;
michael@0 243 return NS_OK;
michael@0 244 }
michael@0 245
michael@0 246 NS_IMETHODIMP
michael@0 247 DataTransfer::SetDropEffectInt(uint32_t aDropEffect)
michael@0 248 {
michael@0 249 mDropEffect = aDropEffect;
michael@0 250 return NS_OK;
michael@0 251 }
michael@0 252
michael@0 253 NS_IMETHODIMP
michael@0 254 DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed)
michael@0 255 {
michael@0 256 *aEffectAllowed = mEffectAllowed;
michael@0 257 return NS_OK;
michael@0 258 }
michael@0 259
michael@0 260 NS_IMETHODIMP
michael@0 261 DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed)
michael@0 262 {
michael@0 263 mEffectAllowed = aEffectAllowed;
michael@0 264 return NS_OK;
michael@0 265 }
michael@0 266
michael@0 267 NS_IMETHODIMP
michael@0 268 DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
michael@0 269 {
michael@0 270 *aUserCancelled = MozUserCancelled();
michael@0 271 return NS_OK;
michael@0 272 }
michael@0 273
michael@0 274 nsDOMFileList*
michael@0 275 DataTransfer::GetFiles(ErrorResult& aRv)
michael@0 276 {
michael@0 277 if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
michael@0 278 mEventType != NS_PASTE) {
michael@0 279 return nullptr;
michael@0 280 }
michael@0 281
michael@0 282 if (!mFiles) {
michael@0 283 mFiles = new nsDOMFileList(static_cast<nsIDOMDataTransfer*>(this));
michael@0 284
michael@0 285 uint32_t count = mItems.Length();
michael@0 286
michael@0 287 for (uint32_t i = 0; i < count; i++) {
michael@0 288 nsCOMPtr<nsIVariant> variant;
michael@0 289 aRv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant));
michael@0 290 if (aRv.Failed()) {
michael@0 291 return nullptr;
michael@0 292 }
michael@0 293
michael@0 294 if (!variant)
michael@0 295 continue;
michael@0 296
michael@0 297 nsCOMPtr<nsISupports> supports;
michael@0 298 nsresult rv = variant->GetAsISupports(getter_AddRefs(supports));
michael@0 299
michael@0 300 if (NS_FAILED(rv))
michael@0 301 continue;
michael@0 302
michael@0 303 nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
michael@0 304
michael@0 305 if (!file)
michael@0 306 continue;
michael@0 307
michael@0 308 nsRefPtr<nsDOMFileFile> domFile = new nsDOMFileFile(file);
michael@0 309
michael@0 310 if (!mFiles->Append(domFile)) {
michael@0 311 aRv.Throw(NS_ERROR_FAILURE);
michael@0 312 return nullptr;
michael@0 313 }
michael@0 314 }
michael@0 315 }
michael@0 316
michael@0 317 return mFiles;
michael@0 318 }
michael@0 319
michael@0 320 NS_IMETHODIMP
michael@0 321 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
michael@0 322 {
michael@0 323 ErrorResult rv;
michael@0 324 NS_IF_ADDREF(*aFileList = GetFiles(rv));
michael@0 325 return rv.ErrorCode();
michael@0 326 }
michael@0 327
michael@0 328 already_AddRefed<DOMStringList>
michael@0 329 DataTransfer::Types()
michael@0 330 {
michael@0 331 nsRefPtr<DOMStringList> types = new DOMStringList();
michael@0 332 if (mItems.Length()) {
michael@0 333 bool addFile = false;
michael@0 334 const nsTArray<TransferItem>& item = mItems[0];
michael@0 335 for (uint32_t i = 0; i < item.Length(); i++) {
michael@0 336 const nsString& format = item[i].mFormat;
michael@0 337 types->Add(format);
michael@0 338 if (!addFile) {
michael@0 339 addFile = format.EqualsASCII(kFileMime) ||
michael@0 340 format.EqualsASCII("application/x-moz-file-promise");
michael@0 341 }
michael@0 342 }
michael@0 343
michael@0 344 if (addFile) {
michael@0 345 types->Add(NS_LITERAL_STRING("Files"));
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 return types.forget();
michael@0 350 }
michael@0 351
michael@0 352 NS_IMETHODIMP
michael@0 353 DataTransfer::GetTypes(nsISupports** aTypes)
michael@0 354 {
michael@0 355 nsRefPtr<DOMStringList> types = Types();
michael@0 356 types.forget(aTypes);
michael@0 357
michael@0 358 return NS_OK;
michael@0 359 }
michael@0 360
michael@0 361 void
michael@0 362 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
michael@0 363 ErrorResult& aRv)
michael@0 364 {
michael@0 365 // return an empty string if data for the format was not found
michael@0 366 aData.Truncate();
michael@0 367
michael@0 368 nsCOMPtr<nsIVariant> data;
michael@0 369 nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data));
michael@0 370 if (NS_FAILED(rv)) {
michael@0 371 if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
michael@0 372 aRv.Throw(rv);
michael@0 373 }
michael@0 374 return;
michael@0 375 }
michael@0 376
michael@0 377 if (data) {
michael@0 378 nsAutoString stringdata;
michael@0 379 data->GetAsAString(stringdata);
michael@0 380
michael@0 381 // for the URL type, parse out the first URI from the list. The URIs are
michael@0 382 // separated by newlines
michael@0 383 nsAutoString lowercaseFormat;
michael@0 384 aRv = nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
michael@0 385 if (aRv.Failed()) {
michael@0 386 return;
michael@0 387 }
michael@0 388
michael@0 389 if (lowercaseFormat.EqualsLiteral("url")) {
michael@0 390 int32_t lastidx = 0, idx;
michael@0 391 int32_t length = stringdata.Length();
michael@0 392 while (lastidx < length) {
michael@0 393 idx = stringdata.FindChar('\n', lastidx);
michael@0 394 // lines beginning with # are comments
michael@0 395 if (stringdata[lastidx] == '#') {
michael@0 396 if (idx == -1)
michael@0 397 break;
michael@0 398 }
michael@0 399 else {
michael@0 400 if (idx == -1)
michael@0 401 aData.Assign(Substring(stringdata, lastidx));
michael@0 402 else
michael@0 403 aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
michael@0 404 aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
michael@0 405 return;
michael@0 406 }
michael@0 407 lastidx = idx + 1;
michael@0 408 }
michael@0 409 }
michael@0 410 else {
michael@0 411 aData = stringdata;
michael@0 412 }
michael@0 413 }
michael@0 414 }
michael@0 415
michael@0 416 NS_IMETHODIMP
michael@0 417 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
michael@0 418 {
michael@0 419 ErrorResult rv;
michael@0 420 GetData(aFormat, aData, rv);
michael@0 421 return rv.ErrorCode();
michael@0 422 }
michael@0 423
michael@0 424 void
michael@0 425 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
michael@0 426 ErrorResult& aRv)
michael@0 427 {
michael@0 428 nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
michael@0 429 if (!variant) {
michael@0 430 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 431 return;
michael@0 432 }
michael@0 433
michael@0 434 variant->SetAsAString(aData);
michael@0 435
michael@0 436 aRv = MozSetDataAt(aFormat, variant, 0);
michael@0 437 }
michael@0 438
michael@0 439 NS_IMETHODIMP
michael@0 440 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
michael@0 441 {
michael@0 442 ErrorResult rv;
michael@0 443 SetData(aFormat, aData, rv);
michael@0 444 return rv.ErrorCode();
michael@0 445 }
michael@0 446
michael@0 447 void
michael@0 448 DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
michael@0 449 {
michael@0 450 if (mReadOnly) {
michael@0 451 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
michael@0 452 return;
michael@0 453 }
michael@0 454
michael@0 455 if (mItems.Length() == 0) {
michael@0 456 return;
michael@0 457 }
michael@0 458
michael@0 459 if (aFormat.WasPassed()) {
michael@0 460 MozClearDataAtHelper(aFormat.Value(), 0, aRv);
michael@0 461 } else {
michael@0 462 MozClearDataAtHelper(EmptyString(), 0, aRv);
michael@0 463 }
michael@0 464 }
michael@0 465
michael@0 466 NS_IMETHODIMP
michael@0 467 DataTransfer::ClearData(const nsAString& aFormat)
michael@0 468 {
michael@0 469 Optional<nsAString> format;
michael@0 470 format = &aFormat;
michael@0 471 ErrorResult rv;
michael@0 472 ClearData(format, rv);
michael@0 473 return rv.ErrorCode();
michael@0 474 }
michael@0 475
michael@0 476 NS_IMETHODIMP
michael@0 477 DataTransfer::GetMozItemCount(uint32_t* aCount)
michael@0 478 {
michael@0 479 *aCount = MozItemCount();
michael@0 480 return NS_OK;
michael@0 481 }
michael@0 482
michael@0 483 NS_IMETHODIMP
michael@0 484 DataTransfer::GetMozCursor(nsAString& aCursorState)
michael@0 485 {
michael@0 486 nsString cursor;
michael@0 487 GetMozCursor(cursor);
michael@0 488 aCursorState = cursor;
michael@0 489 return NS_OK;
michael@0 490 }
michael@0 491
michael@0 492 NS_IMETHODIMP
michael@0 493 DataTransfer::SetMozCursor(const nsAString& aCursorState)
michael@0 494 {
michael@0 495 // Lock the cursor to an arrow during the drag.
michael@0 496 mCursorState = aCursorState.EqualsLiteral("default");
michael@0 497
michael@0 498 return NS_OK;
michael@0 499 }
michael@0 500
michael@0 501 already_AddRefed<nsINode>
michael@0 502 DataTransfer::GetMozSourceNode()
michael@0 503 {
michael@0 504 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
michael@0 505 if (!dragSession) {
michael@0 506 return nullptr;
michael@0 507 }
michael@0 508
michael@0 509 nsCOMPtr<nsIDOMNode> sourceNode;
michael@0 510 dragSession->GetSourceNode(getter_AddRefs(sourceNode));
michael@0 511 nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
michael@0 512 if (node && !nsContentUtils::CanCallerAccess(node)) {
michael@0 513 return nullptr;
michael@0 514 }
michael@0 515
michael@0 516 return node.forget();
michael@0 517 }
michael@0 518
michael@0 519 NS_IMETHODIMP
michael@0 520 DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
michael@0 521 {
michael@0 522 nsCOMPtr<nsINode> sourceNode = GetMozSourceNode();
michael@0 523 if (!sourceNode) {
michael@0 524 *aSourceNode = nullptr;
michael@0 525 return NS_OK;
michael@0 526 }
michael@0 527
michael@0 528 return CallQueryInterface(sourceNode, aSourceNode);
michael@0 529 }
michael@0 530
michael@0 531 already_AddRefed<DOMStringList>
michael@0 532 DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv)
michael@0 533 {
michael@0 534 // Only the first item is valid for clipboard events
michael@0 535 if (aIndex > 0 &&
michael@0 536 (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
michael@0 537 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
michael@0 538 return nullptr;
michael@0 539 }
michael@0 540
michael@0 541 nsRefPtr<DOMStringList> types = new DOMStringList();
michael@0 542 if (aIndex < mItems.Length()) {
michael@0 543 // note that you can retrieve the types regardless of their principal
michael@0 544 nsTArray<TransferItem>& item = mItems[aIndex];
michael@0 545 for (uint32_t i = 0; i < item.Length(); i++)
michael@0 546 types->Add(item[i].mFormat);
michael@0 547 }
michael@0 548
michael@0 549 return types.forget();
michael@0 550 }
michael@0 551
michael@0 552 NS_IMETHODIMP
michael@0 553 DataTransfer::MozTypesAt(uint32_t aIndex, nsISupports** aTypes)
michael@0 554 {
michael@0 555 ErrorResult rv;
michael@0 556 nsRefPtr<DOMStringList> types = MozTypesAt(aIndex, rv);
michael@0 557 types.forget(aTypes);
michael@0 558 return rv.ErrorCode();
michael@0 559 }
michael@0 560
michael@0 561 NS_IMETHODIMP
michael@0 562 DataTransfer::MozGetDataAt(const nsAString& aFormat, uint32_t aIndex,
michael@0 563 nsIVariant** aData)
michael@0 564 {
michael@0 565 *aData = nullptr;
michael@0 566
michael@0 567 if (aFormat.IsEmpty())
michael@0 568 return NS_OK;
michael@0 569
michael@0 570 if (aIndex >= mItems.Length()) {
michael@0 571 return NS_ERROR_DOM_INDEX_SIZE_ERR;
michael@0 572 }
michael@0 573
michael@0 574 // Only the first item is valid for clipboard events
michael@0 575 if (aIndex > 0 &&
michael@0 576 (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
michael@0 577 return NS_ERROR_DOM_INDEX_SIZE_ERR;
michael@0 578 }
michael@0 579
michael@0 580
michael@0 581 nsAutoString format;
michael@0 582 GetRealFormat(aFormat, format);
michael@0 583
michael@0 584 nsTArray<TransferItem>& item = mItems[aIndex];
michael@0 585
michael@0 586 // Check if the caller is allowed to access the drag data. Callers with
michael@0 587 // chrome privileges can always read the data. During the
michael@0 588 // drop event, allow retrieving the data except in the case where the
michael@0 589 // source of the drag is in a child frame of the caller. In that case,
michael@0 590 // we only allow access to data of the same principal. During other events,
michael@0 591 // only allow access to the data with the same principal.
michael@0 592 nsIPrincipal* principal = nullptr;
michael@0 593 if (mIsCrossDomainSubFrameDrop ||
michael@0 594 (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
michael@0 595 mEventType != NS_PASTE &&
michael@0 596 !nsContentUtils::IsCallerChrome())) {
michael@0 597 nsresult rv = NS_OK;
michael@0 598 principal = GetCurrentPrincipal(&rv);
michael@0 599 NS_ENSURE_SUCCESS(rv, rv);
michael@0 600 }
michael@0 601
michael@0 602 uint32_t count = item.Length();
michael@0 603 for (uint32_t i = 0; i < count; i++) {
michael@0 604 TransferItem& formatitem = item[i];
michael@0 605 if (formatitem.mFormat.Equals(format)) {
michael@0 606 bool subsumes;
michael@0 607 if (formatitem.mPrincipal && principal &&
michael@0 608 (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
michael@0 609 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 610
michael@0 611 if (!formatitem.mData) {
michael@0 612 FillInExternalData(formatitem, aIndex);
michael@0 613 } else {
michael@0 614 nsCOMPtr<nsISupports> data;
michael@0 615 formatitem.mData->GetAsISupports(getter_AddRefs(data));
michael@0 616 // Make sure the code that is calling us is same-origin with the data.
michael@0 617 nsCOMPtr<EventTarget> pt = do_QueryInterface(data);
michael@0 618 if (pt) {
michael@0 619 nsresult rv = NS_OK;
michael@0 620 nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
michael@0 621 NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
michael@0 622 nsIGlobalObject* go = c->GetGlobalObject();
michael@0 623 NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
michael@0 624 nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
michael@0 625 MOZ_ASSERT(sp, "This cannot fail on the main thread.");
michael@0 626 nsIPrincipal* dataPrincipal = sp->GetPrincipal();
michael@0 627 NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
michael@0 628 NS_ENSURE_TRUE(principal || (principal = GetCurrentPrincipal(&rv)),
michael@0 629 NS_ERROR_DOM_SECURITY_ERR);
michael@0 630 NS_ENSURE_SUCCESS(rv, rv);
michael@0 631 bool equals = false;
michael@0 632 NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals,
michael@0 633 NS_ERROR_DOM_SECURITY_ERR);
michael@0 634 }
michael@0 635 }
michael@0 636 *aData = formatitem.mData;
michael@0 637 NS_IF_ADDREF(*aData);
michael@0 638 return NS_OK;
michael@0 639 }
michael@0 640 }
michael@0 641
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 void
michael@0 646 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
michael@0 647 uint32_t aIndex,
michael@0 648 JS::MutableHandle<JS::Value> aRetval,
michael@0 649 mozilla::ErrorResult& aRv)
michael@0 650 {
michael@0 651 nsCOMPtr<nsIVariant> data;
michael@0 652 aRv = MozGetDataAt(aFormat, aIndex, getter_AddRefs(data));
michael@0 653 if (aRv.Failed()) {
michael@0 654 return;
michael@0 655 }
michael@0 656
michael@0 657 if (!data) {
michael@0 658 return;
michael@0 659 }
michael@0 660
michael@0 661 JS::Rooted<JS::Value> result(aCx);
michael@0 662 if (!VariantToJsval(aCx, data, aRetval)) {
michael@0 663 aRv = NS_ERROR_FAILURE;
michael@0 664 return;
michael@0 665 }
michael@0 666 }
michael@0 667
michael@0 668 NS_IMETHODIMP
michael@0 669 DataTransfer::MozSetDataAt(const nsAString& aFormat, nsIVariant* aData,
michael@0 670 uint32_t aIndex)
michael@0 671 {
michael@0 672 if (aFormat.IsEmpty()) {
michael@0 673 return NS_OK;
michael@0 674 }
michael@0 675
michael@0 676 if (mReadOnly) {
michael@0 677 return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
michael@0 678 }
michael@0 679
michael@0 680 // Specifying an index less than the current length will replace an existing
michael@0 681 // item. Specifying an index equal to the current length will add a new item.
michael@0 682 if (aIndex > mItems.Length()) {
michael@0 683 return NS_ERROR_DOM_INDEX_SIZE_ERR;
michael@0 684 }
michael@0 685
michael@0 686 // Only the first item is valid for clipboard events
michael@0 687 if (aIndex > 0 &&
michael@0 688 (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
michael@0 689 return NS_ERROR_DOM_INDEX_SIZE_ERR;
michael@0 690 }
michael@0 691
michael@0 692 // don't allow non-chrome to add file data
michael@0 693 // XXX perhaps this should also limit any non-string type as well
michael@0 694 if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
michael@0 695 aFormat.EqualsLiteral("application/x-moz-file")) &&
michael@0 696 !nsContentUtils::IsCallerChrome()) {
michael@0 697 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 698 }
michael@0 699
michael@0 700 nsresult rv = NS_OK;
michael@0 701 nsIPrincipal* principal = GetCurrentPrincipal(&rv);
michael@0 702 NS_ENSURE_SUCCESS(rv, rv);
michael@0 703
michael@0 704 return SetDataWithPrincipal(aFormat, aData, aIndex, principal);
michael@0 705 }
michael@0 706
michael@0 707 void
michael@0 708 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
michael@0 709 JS::Handle<JS::Value> aData,
michael@0 710 uint32_t aIndex, ErrorResult& aRv)
michael@0 711 {
michael@0 712 nsCOMPtr<nsIVariant> data;
michael@0 713 aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
michael@0 714 getter_AddRefs(data));
michael@0 715 if (!aRv.Failed()) {
michael@0 716 aRv = MozSetDataAt(aFormat, data, aIndex);
michael@0 717 }
michael@0 718 }
michael@0 719
michael@0 720 void
michael@0 721 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
michael@0 722 ErrorResult& aRv)
michael@0 723 {
michael@0 724 if (mReadOnly) {
michael@0 725 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
michael@0 726 return;
michael@0 727 }
michael@0 728
michael@0 729 if (aIndex >= mItems.Length()) {
michael@0 730 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
michael@0 731 return;
michael@0 732 }
michael@0 733
michael@0 734 // Only the first item is valid for clipboard events
michael@0 735 if (aIndex > 0 &&
michael@0 736 (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
michael@0 737 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
michael@0 738 return;
michael@0 739 }
michael@0 740
michael@0 741 MozClearDataAtHelper(aFormat, aIndex, aRv);
michael@0 742 }
michael@0 743
michael@0 744 void
michael@0 745 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
michael@0 746 ErrorResult& aRv)
michael@0 747 {
michael@0 748 MOZ_ASSERT(!mReadOnly);
michael@0 749 MOZ_ASSERT(aIndex < mItems.Length());
michael@0 750 MOZ_ASSERT(aIndex == 0 ||
michael@0 751 (mEventType != NS_CUT && mEventType != NS_COPY &&
michael@0 752 mEventType != NS_PASTE));
michael@0 753
michael@0 754 nsAutoString format;
michael@0 755 GetRealFormat(aFormat, format);
michael@0 756
michael@0 757 nsresult rv = NS_OK;
michael@0 758 nsIPrincipal* principal = GetCurrentPrincipal(&rv);
michael@0 759 if (NS_FAILED(rv)) {
michael@0 760 aRv = rv;
michael@0 761 return;
michael@0 762 }
michael@0 763
michael@0 764 // if the format is empty, clear all formats
michael@0 765 bool clearall = format.IsEmpty();
michael@0 766
michael@0 767 nsTArray<TransferItem>& item = mItems[aIndex];
michael@0 768 // count backwards so that the count and index don't have to be adjusted
michael@0 769 // after removing an element
michael@0 770 for (int32_t i = item.Length() - 1; i >= 0; i--) {
michael@0 771 TransferItem& formatitem = item[i];
michael@0 772 if (clearall || formatitem.mFormat.Equals(format)) {
michael@0 773 // don't allow removing data that has a stronger principal
michael@0 774 bool subsumes;
michael@0 775 if (formatitem.mPrincipal && principal &&
michael@0 776 (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes)) {
michael@0 777 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
michael@0 778 return;
michael@0 779 }
michael@0 780
michael@0 781 item.RemoveElementAt(i);
michael@0 782
michael@0 783 // if a format was specified, break out. Otherwise, loop around until
michael@0 784 // all formats have been removed
michael@0 785 if (!clearall)
michael@0 786 break;
michael@0 787 }
michael@0 788 }
michael@0 789
michael@0 790 // if the last format for an item is removed, remove the entire item
michael@0 791 if (!item.Length())
michael@0 792 mItems.RemoveElementAt(aIndex);
michael@0 793 }
michael@0 794
michael@0 795 NS_IMETHODIMP
michael@0 796 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
michael@0 797 {
michael@0 798 ErrorResult rv;
michael@0 799 MozClearDataAt(aFormat, aIndex, rv);
michael@0 800 return rv.ErrorCode();
michael@0 801 }
michael@0 802
michael@0 803 void
michael@0 804 DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY,
michael@0 805 ErrorResult& aRv)
michael@0 806 {
michael@0 807 if (mReadOnly) {
michael@0 808 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
michael@0 809 return;
michael@0 810 }
michael@0 811
michael@0 812 mDragImage = &aImage;
michael@0 813 mDragImageX = aX;
michael@0 814 mDragImageY = aY;
michael@0 815 }
michael@0 816
michael@0 817 NS_IMETHODIMP
michael@0 818 DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY)
michael@0 819 {
michael@0 820 ErrorResult rv;
michael@0 821 nsCOMPtr<Element> image = do_QueryInterface(aImage);
michael@0 822 if (image) {
michael@0 823 SetDragImage(*image, aX, aY, rv);
michael@0 824 }
michael@0 825 return rv.ErrorCode();
michael@0 826 }
michael@0 827
michael@0 828 void
michael@0 829 DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
michael@0 830 {
michael@0 831 if (mReadOnly) {
michael@0 832 aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
michael@0 833 return;
michael@0 834 }
michael@0 835
michael@0 836 mDragTarget = &aElement;
michael@0 837 }
michael@0 838
michael@0 839 NS_IMETHODIMP
michael@0 840 DataTransfer::AddElement(nsIDOMElement* aElement)
michael@0 841 {
michael@0 842 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
michael@0 843
michael@0 844 nsCOMPtr<Element> element = do_QueryInterface(aElement);
michael@0 845 NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG);
michael@0 846
michael@0 847 ErrorResult rv;
michael@0 848 AddElement(*element, rv);
michael@0 849 return rv.ErrorCode();
michael@0 850 }
michael@0 851
michael@0 852 nsresult
michael@0 853 DataTransfer::Clone(nsISupports* aParent, uint32_t aEventType,
michael@0 854 bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
michael@0 855 DataTransfer** aNewDataTransfer)
michael@0 856 {
michael@0 857 DataTransfer* newDataTransfer =
michael@0 858 new DataTransfer(aParent, aEventType, mEffectAllowed, mCursorState,
michael@0 859 mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
michael@0 860 mClipboardType, mItems, mDragImage, mDragImageX,
michael@0 861 mDragImageY);
michael@0 862
michael@0 863 *aNewDataTransfer = newDataTransfer;
michael@0 864 NS_ADDREF(*aNewDataTransfer);
michael@0 865 return NS_OK;
michael@0 866 }
michael@0 867
michael@0 868 already_AddRefed<nsISupportsArray>
michael@0 869 DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
michael@0 870 {
michael@0 871 MOZ_ASSERT(aDragTarget);
michael@0 872
michael@0 873 nsCOMPtr<nsISupportsArray> transArray =
michael@0 874 do_CreateInstance("@mozilla.org/supports-array;1");
michael@0 875 if (!transArray) {
michael@0 876 return nullptr;
michael@0 877 }
michael@0 878
michael@0 879
michael@0 880 nsCOMPtr<nsINode> dragNode = do_QueryInterface(aDragTarget);
michael@0 881 if (!dragNode) {
michael@0 882 return nullptr;
michael@0 883 }
michael@0 884
michael@0 885 nsIDocument* doc = dragNode->GetCurrentDoc();
michael@0 886 if (!doc) {
michael@0 887 return nullptr;
michael@0 888 }
michael@0 889
michael@0 890 nsILoadContext* loadContext = doc->GetLoadContext();
michael@0 891
michael@0 892 uint32_t count = mItems.Length();
michael@0 893 for (uint32_t i = 0; i < count; i++) {
michael@0 894 nsCOMPtr<nsITransferable> transferable = GetTransferable(i, loadContext);
michael@0 895 if (transferable) {
michael@0 896 transArray->AppendElement(transferable);
michael@0 897 }
michael@0 898 }
michael@0 899
michael@0 900 return transArray.forget();
michael@0 901 }
michael@0 902
michael@0 903 already_AddRefed<nsITransferable>
michael@0 904 DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
michael@0 905 {
michael@0 906 if (aIndex >= mItems.Length()) {
michael@0 907 return nullptr;
michael@0 908 }
michael@0 909
michael@0 910 nsTArray<TransferItem>& item = mItems[aIndex];
michael@0 911 uint32_t count = item.Length();
michael@0 912 if (!count) {
michael@0 913 return nullptr;
michael@0 914 }
michael@0 915
michael@0 916 nsCOMPtr<nsITransferable> transferable =
michael@0 917 do_CreateInstance("@mozilla.org/widget/transferable;1");
michael@0 918 if (!transferable) {
michael@0 919 return nullptr;
michael@0 920 }
michael@0 921 transferable->Init(aLoadContext);
michael@0 922
michael@0 923 bool added = false;
michael@0 924 for (uint32_t f = 0; f < count; f++) {
michael@0 925 const TransferItem& formatitem = item[f];
michael@0 926 if (!formatitem.mData) { // skip empty items
michael@0 927 continue;
michael@0 928 }
michael@0 929
michael@0 930 uint32_t length;
michael@0 931 nsCOMPtr<nsISupports> convertedData;
michael@0 932 if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length)) {
michael@0 933 continue;
michael@0 934 }
michael@0 935
michael@0 936 // the underlying drag code uses text/unicode, so use that instead of text/plain
michael@0 937 const char* format;
michael@0 938 NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
michael@0 939 if (utf8format.EqualsLiteral("text/plain")) {
michael@0 940 format = kUnicodeMime;
michael@0 941 } else {
michael@0 942 format = utf8format.get();
michael@0 943 }
michael@0 944
michael@0 945 // if a converter is set for a format, set the converter for the
michael@0 946 // transferable and don't add the item
michael@0 947 nsCOMPtr<nsIFormatConverter> converter = do_QueryInterface(convertedData);
michael@0 948 if (converter) {
michael@0 949 transferable->AddDataFlavor(format);
michael@0 950 transferable->SetConverter(converter);
michael@0 951 continue;
michael@0 952 }
michael@0 953
michael@0 954 nsresult rv = transferable->SetTransferData(format, convertedData, length);
michael@0 955 if (NS_FAILED(rv)) {
michael@0 956 return nullptr;
michael@0 957 }
michael@0 958
michael@0 959 added = true;
michael@0 960 }
michael@0 961
michael@0 962 // only return the transferable if data was successfully added to it
michael@0 963 if (added) {
michael@0 964 return transferable.forget();
michael@0 965 }
michael@0 966
michael@0 967 return nullptr;
michael@0 968 }
michael@0 969
michael@0 970 bool
michael@0 971 DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
michael@0 972 nsISupports** aSupports,
michael@0 973 uint32_t* aLength)
michael@0 974 {
michael@0 975 *aSupports = nullptr;
michael@0 976 *aLength = 0;
michael@0 977
michael@0 978 uint16_t type;
michael@0 979 aVariant->GetDataType(&type);
michael@0 980 if (type == nsIDataType::VTYPE_INTERFACE ||
michael@0 981 type == nsIDataType::VTYPE_INTERFACE_IS) {
michael@0 982 nsCOMPtr<nsISupports> data;
michael@0 983 if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data))))
michael@0 984 return false;
michael@0 985
michael@0 986 nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
michael@0 987 if (fdp) {
michael@0 988 // for flavour data providers, use kFlavorHasDataProvider (which has the
michael@0 989 // value 0) as the length.
michael@0 990 NS_ADDREF(*aSupports = fdp);
michael@0 991 *aLength = nsITransferable::kFlavorHasDataProvider;
michael@0 992 }
michael@0 993 else {
michael@0 994 // wrap the item in an nsISupportsInterfacePointer
michael@0 995 nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
michael@0 996 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
michael@0 997 if (!ptrSupports)
michael@0 998 return false;
michael@0 999
michael@0 1000 ptrSupports->SetData(data);
michael@0 1001 NS_ADDREF(*aSupports = ptrSupports);
michael@0 1002
michael@0 1003 *aLength = sizeof(nsISupportsInterfacePointer *);
michael@0 1004 }
michael@0 1005
michael@0 1006 return true;
michael@0 1007 }
michael@0 1008
michael@0 1009 char16_t* chrs;
michael@0 1010 uint32_t len = 0;
michael@0 1011 nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
michael@0 1012 if (NS_FAILED(rv))
michael@0 1013 return false;
michael@0 1014
michael@0 1015 nsAutoString str;
michael@0 1016 str.Adopt(chrs, len);
michael@0 1017
michael@0 1018 nsCOMPtr<nsISupportsString>
michael@0 1019 strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
michael@0 1020 if (!strSupports)
michael@0 1021 return false;
michael@0 1022
michael@0 1023 strSupports->SetData(str);
michael@0 1024
michael@0 1025 *aSupports = strSupports;
michael@0 1026 NS_ADDREF(*aSupports);
michael@0 1027
michael@0 1028 // each character is two bytes
michael@0 1029 *aLength = str.Length() << 1;
michael@0 1030
michael@0 1031 return true;
michael@0 1032 }
michael@0 1033
michael@0 1034 void
michael@0 1035 DataTransfer::ClearAll()
michael@0 1036 {
michael@0 1037 mItems.Clear();
michael@0 1038 }
michael@0 1039
michael@0 1040 nsresult
michael@0 1041 DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
michael@0 1042 nsIVariant* aData,
michael@0 1043 uint32_t aIndex,
michael@0 1044 nsIPrincipal* aPrincipal)
michael@0 1045 {
michael@0 1046 nsAutoString format;
michael@0 1047 GetRealFormat(aFormat, format);
michael@0 1048
michael@0 1049 // check if the item for the format already exists. In that case,
michael@0 1050 // just replace it.
michael@0 1051 TransferItem* formatitem;
michael@0 1052 if (aIndex < mItems.Length()) {
michael@0 1053 nsTArray<TransferItem>& item = mItems[aIndex];
michael@0 1054 uint32_t count = item.Length();
michael@0 1055 for (uint32_t i = 0; i < count; i++) {
michael@0 1056 TransferItem& itemformat = item[i];
michael@0 1057 if (itemformat.mFormat.Equals(format)) {
michael@0 1058 // don't allow replacing data that has a stronger principal
michael@0 1059 bool subsumes;
michael@0 1060 if (itemformat.mPrincipal && aPrincipal &&
michael@0 1061 (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes))
michael@0 1062 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 1063
michael@0 1064 itemformat.mPrincipal = aPrincipal;
michael@0 1065 itemformat.mData = aData;
michael@0 1066 return NS_OK;
michael@0 1067 }
michael@0 1068 }
michael@0 1069
michael@0 1070 // add a new format
michael@0 1071 formatitem = item.AppendElement();
michael@0 1072 }
michael@0 1073 else {
michael@0 1074 NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
michael@0 1075
michael@0 1076 // add a new index
michael@0 1077 nsTArray<TransferItem>* item = mItems.AppendElement();
michael@0 1078 NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
michael@0 1079
michael@0 1080 formatitem = item->AppendElement();
michael@0 1081 }
michael@0 1082
michael@0 1083 NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
michael@0 1084
michael@0 1085 formatitem->mFormat = format;
michael@0 1086 formatitem->mPrincipal = aPrincipal;
michael@0 1087 formatitem->mData = aData;
michael@0 1088
michael@0 1089 return NS_OK;
michael@0 1090 }
michael@0 1091
michael@0 1092 nsIPrincipal*
michael@0 1093 DataTransfer::GetCurrentPrincipal(nsresult* rv)
michael@0 1094 {
michael@0 1095 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
michael@0 1096
michael@0 1097 nsCOMPtr<nsIPrincipal> currentPrincipal;
michael@0 1098 *rv = ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal));
michael@0 1099 NS_ENSURE_SUCCESS(*rv, nullptr);
michael@0 1100
michael@0 1101 if (!currentPrincipal)
michael@0 1102 ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal));
michael@0 1103
michael@0 1104 return currentPrincipal.get();
michael@0 1105 }
michael@0 1106
michael@0 1107 void
michael@0 1108 DataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
michael@0 1109 {
michael@0 1110 // treat text/unicode as equivalent to text/plain
michael@0 1111 nsAutoString lowercaseFormat;
michael@0 1112 nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
michael@0 1113 if (lowercaseFormat.EqualsLiteral("text") || lowercaseFormat.EqualsLiteral("text/unicode"))
michael@0 1114 aOutFormat.AssignLiteral("text/plain");
michael@0 1115 else if (lowercaseFormat.EqualsLiteral("url"))
michael@0 1116 aOutFormat.AssignLiteral("text/uri-list");
michael@0 1117 else
michael@0 1118 aOutFormat.Assign(lowercaseFormat);
michael@0 1119 }
michael@0 1120
michael@0 1121 void
michael@0 1122 DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal)
michael@0 1123 {
michael@0 1124 if (strcmp(aFormat, kUnicodeMime) == 0) {
michael@0 1125 SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr, aIndex, aPrincipal);
michael@0 1126 } else {
michael@0 1127 if (strcmp(aFormat, kURLDataMime) == 0) {
michael@0 1128 SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr, aIndex, aPrincipal);
michael@0 1129 }
michael@0 1130 SetDataWithPrincipal(NS_ConvertUTF8toUTF16(aFormat), nullptr, aIndex, aPrincipal);
michael@0 1131 }
michael@0 1132 }
michael@0 1133
michael@0 1134 void
michael@0 1135 DataTransfer::CacheExternalDragFormats()
michael@0 1136 {
michael@0 1137 // Called during the constructor to cache the formats available from an
michael@0 1138 // external drag. The data associated with each format will be set to null.
michael@0 1139 // This data will instead only be retrieved in FillInExternalDragData when
michael@0 1140 // asked for, as it may be time consuming for the source application to
michael@0 1141 // generate it.
michael@0 1142
michael@0 1143 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
michael@0 1144 if (!dragSession)
michael@0 1145 return;
michael@0 1146
michael@0 1147 // make sure that the system principal is used for external drags
michael@0 1148 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
michael@0 1149 nsCOMPtr<nsIPrincipal> sysPrincipal;
michael@0 1150 ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
michael@0 1151
michael@0 1152 // there isn't a way to get a list of the formats that might be available on
michael@0 1153 // all platforms, so just check for the types that can actually be imported
michael@0 1154 // XXXndeakin there are some other formats but those are platform specific.
michael@0 1155 const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
michael@0 1156
michael@0 1157 uint32_t count;
michael@0 1158 dragSession->GetNumDropItems(&count);
michael@0 1159 for (uint32_t c = 0; c < count; c++) {
michael@0 1160 for (uint32_t f = 0; f < ArrayLength(formats); f++) {
michael@0 1161 // IsDataFlavorSupported doesn't take an index as an argument and just
michael@0 1162 // checks if any of the items support a particular flavor, even though
michael@0 1163 // the GetData method does take an index. Here, we just assume that
michael@0 1164 // every item being dragged has the same set of flavors.
michael@0 1165 bool supported;
michael@0 1166 dragSession->IsDataFlavorSupported(formats[f], &supported);
michael@0 1167 // if the format is supported, add an item to the array with null as
michael@0 1168 // the data. When retrieved, GetRealData will read the data.
michael@0 1169 if (supported) {
michael@0 1170 CacheExternalData(formats[f], c, sysPrincipal);
michael@0 1171 }
michael@0 1172 }
michael@0 1173 }
michael@0 1174 }
michael@0 1175
michael@0 1176 void
michael@0 1177 DataTransfer::CacheExternalClipboardFormats()
michael@0 1178 {
michael@0 1179 NS_ASSERTION(mEventType == NS_PASTE, "caching clipboard data for invalid event");
michael@0 1180
michael@0 1181 // Called during the constructor for paste events to cache the formats
michael@0 1182 // available on the clipboard. As with CacheExternalDragFormats, the
michael@0 1183 // data will only be retrieved when needed.
michael@0 1184
michael@0 1185 nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1");
michael@0 1186 if (!clipboard || mClipboardType < 0) {
michael@0 1187 return;
michael@0 1188 }
michael@0 1189
michael@0 1190 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
michael@0 1191 nsCOMPtr<nsIPrincipal> sysPrincipal;
michael@0 1192 ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
michael@0 1193
michael@0 1194 // there isn't a way to get a list of the formats that might be available on
michael@0 1195 // all platforms, so just check for the types that can actually be imported
michael@0 1196 const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
michael@0 1197
michael@0 1198 for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
michael@0 1199 // check each format one at a time
michael@0 1200 bool supported;
michael@0 1201 clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType, &supported);
michael@0 1202 // if the format is supported, add an item to the array with null as
michael@0 1203 // the data. When retrieved, GetRealData will read the data.
michael@0 1204 if (supported) {
michael@0 1205 CacheExternalData(formats[f], 0, sysPrincipal);
michael@0 1206 }
michael@0 1207 }
michael@0 1208 }
michael@0 1209
michael@0 1210 void
michael@0 1211 DataTransfer::FillInExternalData(TransferItem& aItem, uint32_t aIndex)
michael@0 1212 {
michael@0 1213 NS_PRECONDITION(mIsExternal, "Not an external data transfer");
michael@0 1214
michael@0 1215 if (aItem.mData) {
michael@0 1216 return;
michael@0 1217 }
michael@0 1218
michael@0 1219 // only drag and paste events should be calling FillInExternalData
michael@0 1220 NS_ASSERTION(mEventType != NS_CUT && mEventType != NS_COPY,
michael@0 1221 "clipboard event with empty data");
michael@0 1222
michael@0 1223 NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
michael@0 1224 const char* format = utf8format.get();
michael@0 1225 if (strcmp(format, "text/plain") == 0)
michael@0 1226 format = kUnicodeMime;
michael@0 1227 else if (strcmp(format, "text/uri-list") == 0)
michael@0 1228 format = kURLDataMime;
michael@0 1229
michael@0 1230 nsCOMPtr<nsITransferable> trans =
michael@0 1231 do_CreateInstance("@mozilla.org/widget/transferable;1");
michael@0 1232 if (!trans)
michael@0 1233 return;
michael@0 1234
michael@0 1235 trans->Init(nullptr);
michael@0 1236 trans->AddDataFlavor(format);
michael@0 1237
michael@0 1238 if (mEventType == NS_PASTE) {
michael@0 1239 MOZ_ASSERT(aIndex == 0, "index in clipboard must be 0");
michael@0 1240
michael@0 1241 nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1");
michael@0 1242 if (!clipboard || mClipboardType < 0) {
michael@0 1243 return;
michael@0 1244 }
michael@0 1245
michael@0 1246 clipboard->GetData(trans, mClipboardType);
michael@0 1247 } else {
michael@0 1248 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
michael@0 1249 if (!dragSession) {
michael@0 1250 return;
michael@0 1251 }
michael@0 1252
michael@0 1253 #ifdef DEBUG
michael@0 1254 // Since this is an external drag, the source document will always be null.
michael@0 1255 nsCOMPtr<nsIDOMDocument> domDoc;
michael@0 1256 dragSession->GetSourceDocument(getter_AddRefs(domDoc));
michael@0 1257 MOZ_ASSERT(!domDoc);
michael@0 1258 #endif
michael@0 1259
michael@0 1260 dragSession->GetData(trans, aIndex);
michael@0 1261 }
michael@0 1262
michael@0 1263 uint32_t length = 0;
michael@0 1264 nsCOMPtr<nsISupports> data;
michael@0 1265 trans->GetTransferData(format, getter_AddRefs(data), &length);
michael@0 1266 if (!data)
michael@0 1267 return;
michael@0 1268
michael@0 1269 nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
michael@0 1270 if (!variant)
michael@0 1271 return;
michael@0 1272
michael@0 1273 nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
michael@0 1274 if (supportsstr) {
michael@0 1275 nsAutoString str;
michael@0 1276 supportsstr->GetData(str);
michael@0 1277 variant->SetAsAString(str);
michael@0 1278 }
michael@0 1279 else {
michael@0 1280 variant->SetAsISupports(data);
michael@0 1281 }
michael@0 1282
michael@0 1283 aItem.mData = variant;
michael@0 1284 }
michael@0 1285
michael@0 1286 } // namespace dom
michael@0 1287 } // namespace mozilla

mercurial