/src/mozilla-central/dom/events/DataTransferItem.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "DataTransferItem.h" |
8 | | #include "DataTransferItemList.h" |
9 | | |
10 | | #include "mozilla/ContentEvents.h" |
11 | | #include "mozilla/EventForwards.h" |
12 | | #include "mozilla/dom/DataTransferItemBinding.h" |
13 | | #include "mozilla/dom/Directory.h" |
14 | | #include "mozilla/dom/Event.h" |
15 | | #include "mozilla/dom/FileSystem.h" |
16 | | #include "mozilla/dom/FileSystemDirectoryEntry.h" |
17 | | #include "mozilla/dom/FileSystemFileEntry.h" |
18 | | #include "nsIClipboard.h" |
19 | | #include "nsISupportsPrimitives.h" |
20 | | #include "nsIScriptObjectPrincipal.h" |
21 | | #include "nsNetUtil.h" |
22 | | #include "nsQueryObject.h" |
23 | | #include "nsContentUtils.h" |
24 | | #include "nsVariant.h" |
25 | | |
26 | | namespace { |
27 | | |
28 | | struct FileMimeNameData |
29 | | { |
30 | | const char* mMimeName; |
31 | | const char* mFileName; |
32 | | }; |
33 | | |
34 | | FileMimeNameData kFileMimeNameMap[] = { |
35 | | { kFileMime, "GenericFileName" }, |
36 | | { kPNGImageMime, "GenericImageNamePNG" }, |
37 | | }; |
38 | | |
39 | | } // anonymous namespace |
40 | | |
41 | | namespace mozilla { |
42 | | namespace dom { |
43 | | |
44 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DataTransferItem, mData, |
45 | | mPrincipal, mDataTransfer, mCachedFile) |
46 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransferItem) |
47 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransferItem) |
48 | | |
49 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransferItem) |
50 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
51 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
52 | 0 | NS_INTERFACE_MAP_END |
53 | | |
54 | | JSObject* |
55 | | DataTransferItem::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
56 | 0 | { |
57 | 0 | return DataTransferItem_Binding::Wrap(aCx, this, aGivenProto); |
58 | 0 | } |
59 | | |
60 | | already_AddRefed<DataTransferItem> |
61 | | DataTransferItem::Clone(DataTransfer* aDataTransfer) const |
62 | 0 | { |
63 | 0 | MOZ_ASSERT(aDataTransfer); |
64 | 0 |
|
65 | 0 | RefPtr<DataTransferItem> it = new DataTransferItem(aDataTransfer, mType); |
66 | 0 |
|
67 | 0 | // Copy over all of the fields |
68 | 0 | it->mKind = mKind; |
69 | 0 | it->mIndex = mIndex; |
70 | 0 | it->mData = mData; |
71 | 0 | it->mPrincipal = mPrincipal; |
72 | 0 | it->mChromeOnly = mChromeOnly; |
73 | 0 |
|
74 | 0 | return it.forget(); |
75 | 0 | } |
76 | | |
77 | | void |
78 | | DataTransferItem::SetData(nsIVariant* aData) |
79 | 0 | { |
80 | 0 | // Invalidate our file cache, we will regenerate it with the new data |
81 | 0 | mCachedFile = nullptr; |
82 | 0 |
|
83 | 0 | if (!aData) { |
84 | 0 | // We are holding a temporary null which will later be filled. |
85 | 0 | // These are provided by the system, and have guaranteed properties about |
86 | 0 | // their kind based on their type. |
87 | 0 | MOZ_ASSERT(!mType.IsEmpty()); |
88 | 0 |
|
89 | 0 | mKind = KIND_STRING; |
90 | 0 | for (uint32_t i = 0; i < ArrayLength(kFileMimeNameMap); ++i) { |
91 | 0 | if (mType.EqualsASCII(kFileMimeNameMap[i].mMimeName)) { |
92 | 0 | mKind = KIND_FILE; |
93 | 0 | break; |
94 | 0 | } |
95 | 0 | } |
96 | 0 |
|
97 | 0 | mData = nullptr; |
98 | 0 | return; |
99 | 0 | } |
100 | 0 |
|
101 | 0 | mData = aData; |
102 | 0 | mKind = KindFromData(mData); |
103 | 0 | } |
104 | | |
105 | | /* static */ DataTransferItem::eKind |
106 | | DataTransferItem::KindFromData(nsIVariant* aData) |
107 | 0 | { |
108 | 0 | nsCOMPtr<nsISupports> supports; |
109 | 0 | nsresult rv = aData->GetAsISupports(getter_AddRefs(supports)); |
110 | 0 | if (NS_SUCCEEDED(rv) && supports) { |
111 | 0 | // Check if we have one of the supported file data formats |
112 | 0 | if (RefPtr<Blob>(do_QueryObject(supports)) || |
113 | 0 | nsCOMPtr<BlobImpl>(do_QueryInterface(supports)) || |
114 | 0 | nsCOMPtr<nsIFile>(do_QueryInterface(supports))) { |
115 | 0 | return KIND_FILE; |
116 | 0 | } |
117 | 0 | } |
118 | 0 | |
119 | 0 | nsAutoString string; |
120 | 0 | // If we can't get the data type as a string, that means that the object |
121 | 0 | // should be considered to be of the "other" type. This is impossible |
122 | 0 | // through the APIs defined by the spec, but we provide extra Moz* APIs, |
123 | 0 | // which allow setting of non-string data. We determine whether we can |
124 | 0 | // consider it a string, by calling GetAsAString, and checking for success. |
125 | 0 | rv = aData->GetAsAString(string); |
126 | 0 | if (NS_SUCCEEDED(rv)) { |
127 | 0 | return KIND_STRING; |
128 | 0 | } |
129 | 0 | |
130 | 0 | return KIND_OTHER; |
131 | 0 | } |
132 | | |
133 | | void |
134 | | DataTransferItem::FillInExternalData() |
135 | 0 | { |
136 | 0 | if (mData) { |
137 | 0 | return; |
138 | 0 | } |
139 | 0 | |
140 | 0 | NS_ConvertUTF16toUTF8 utf8format(mType); |
141 | 0 | const char* format = utf8format.get(); |
142 | 0 | if (strcmp(format, "text/plain") == 0) { |
143 | 0 | format = kUnicodeMime; |
144 | 0 | } else if (strcmp(format, "text/uri-list") == 0) { |
145 | 0 | format = kURLDataMime; |
146 | 0 | } |
147 | 0 |
|
148 | 0 | nsCOMPtr<nsITransferable> trans = |
149 | 0 | do_CreateInstance("@mozilla.org/widget/transferable;1"); |
150 | 0 | if (NS_WARN_IF(!trans)) { |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | |
154 | 0 | trans->Init(nullptr); |
155 | 0 | trans->AddDataFlavor(format); |
156 | 0 |
|
157 | 0 | if (mDataTransfer->GetEventMessage() == ePaste) { |
158 | 0 | MOZ_ASSERT(mIndex == 0, "index in clipboard must be 0"); |
159 | 0 |
|
160 | 0 | nsCOMPtr<nsIClipboard> clipboard = |
161 | 0 | do_GetService("@mozilla.org/widget/clipboard;1"); |
162 | 0 | if (!clipboard || mDataTransfer->ClipboardType() < 0) { |
163 | 0 | return; |
164 | 0 | } |
165 | 0 | |
166 | 0 | nsresult rv = clipboard->GetData(trans, mDataTransfer->ClipboardType()); |
167 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
168 | 0 | return; |
169 | 0 | } |
170 | 0 | } else { |
171 | 0 | nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); |
172 | 0 | if (!dragSession) { |
173 | 0 | return; |
174 | 0 | } |
175 | 0 | |
176 | 0 | nsresult rv = dragSession->GetData(trans, mIndex); |
177 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
178 | 0 | return; |
179 | 0 | } |
180 | 0 | } |
181 | 0 | |
182 | 0 | uint32_t length = 0; |
183 | 0 | nsCOMPtr<nsISupports> data; |
184 | 0 | nsresult rv = trans->GetTransferData(format, getter_AddRefs(data), &length); |
185 | 0 | if (NS_WARN_IF(NS_FAILED(rv) || !data)) { |
186 | 0 | return; |
187 | 0 | } |
188 | 0 | |
189 | 0 | // Fill the variant |
190 | 0 | RefPtr<nsVariantCC> variant = new nsVariantCC(); |
191 | 0 |
|
192 | 0 | eKind oldKind = Kind(); |
193 | 0 | if (oldKind == KIND_FILE) { |
194 | 0 | // Because this is an external piece of data, mType is one of kFileMime, |
195 | 0 | // kPNGImageMime, kJPEGImageMime, or kGIFImageMime. Some of these types |
196 | 0 | // are passed in as a nsIInputStream which must be converted to a |
197 | 0 | // dom::File before storing. |
198 | 0 | if (nsCOMPtr<nsIInputStream> istream = do_QueryInterface(data)) { |
199 | 0 | RefPtr<File> file = CreateFileFromInputStream(istream); |
200 | 0 | if (NS_WARN_IF(!file)) { |
201 | 0 | return; |
202 | 0 | } |
203 | 0 | data = do_QueryObject(file); |
204 | 0 | } |
205 | 0 |
|
206 | 0 | variant->SetAsISupports(data); |
207 | 0 | } else { |
208 | 0 | // We have an external piece of string data. Extract it and store it in the variant |
209 | 0 | MOZ_ASSERT(oldKind == KIND_STRING); |
210 | 0 |
|
211 | 0 | nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data); |
212 | 0 | if (supportsstr) { |
213 | 0 | nsAutoString str; |
214 | 0 | supportsstr->GetData(str); |
215 | 0 | variant->SetAsAString(str); |
216 | 0 | } else { |
217 | 0 | nsCOMPtr<nsISupportsCString> supportscstr = do_QueryInterface(data); |
218 | 0 | if (supportscstr) { |
219 | 0 | nsAutoCString str; |
220 | 0 | supportscstr->GetData(str); |
221 | 0 | variant->SetAsACString(str); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } |
225 | 0 |
|
226 | 0 | SetData(variant); |
227 | 0 |
|
228 | 0 | if (oldKind != Kind()) { |
229 | 0 | NS_WARNING("Clipboard data provided by the OS does not match predicted kind"); |
230 | 0 | mDataTransfer->TypesListMayHaveChanged(); |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | void |
235 | | DataTransferItem::GetType(nsAString& aType) |
236 | 0 | { |
237 | 0 | // If we don't have a File, we can just put whatever our recorded internal |
238 | 0 | // type is. |
239 | 0 | if (Kind() != KIND_FILE) { |
240 | 0 | aType = mType; |
241 | 0 | return; |
242 | 0 | } |
243 | 0 | |
244 | 0 | // If we do have a File, then we need to look at our File object to discover |
245 | 0 | // what its mime type is. We can use the System Principal here, as this |
246 | 0 | // information should be avaliable even if the data is currently inaccessible |
247 | 0 | // (for example during a dragover). |
248 | 0 | // |
249 | 0 | // XXX: This seems inefficient, as it seems like we should be able to get this |
250 | 0 | // data without getting the entire File object, which may require talking to |
251 | 0 | // the OS. |
252 | 0 | ErrorResult rv; |
253 | 0 | RefPtr<File> file = GetAsFile(*nsContentUtils::GetSystemPrincipal(), rv); |
254 | 0 | MOZ_ASSERT(!rv.Failed(), "Failed to get file data with system principal"); |
255 | 0 |
|
256 | 0 | // If we don't actually have a file, fall back to returning the internal type. |
257 | 0 | if (NS_WARN_IF(!file)) { |
258 | 0 | aType = mType; |
259 | 0 | return; |
260 | 0 | } |
261 | 0 | |
262 | 0 | file->GetType(aType); |
263 | 0 | } |
264 | | |
265 | | already_AddRefed<File> |
266 | | DataTransferItem::GetAsFile(nsIPrincipal& aSubjectPrincipal, |
267 | | ErrorResult& aRv) |
268 | 0 | { |
269 | 0 | // This is done even if we have an mCachedFile, as it performs the necessary |
270 | 0 | // permissions checks to ensure that we are allowed to access this type. |
271 | 0 | nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv); |
272 | 0 | if (NS_WARN_IF(!data || aRv.Failed())) { |
273 | 0 | return nullptr; |
274 | 0 | } |
275 | 0 | |
276 | 0 | // We have to check our kind after getting the data, because if we have |
277 | 0 | // external data and the OS lied to us (which unfortunately does happen |
278 | 0 | // sometimes), then we might not have the same type of data as we did coming |
279 | 0 | // into this function. |
280 | 0 | if (NS_WARN_IF(mKind != KIND_FILE)) { |
281 | 0 | return nullptr; |
282 | 0 | } |
283 | 0 | |
284 | 0 | // Generate the dom::File from the stored data, caching it so that the |
285 | 0 | // same object is returned in the future. |
286 | 0 | if (!mCachedFile) { |
287 | 0 | nsCOMPtr<nsISupports> supports; |
288 | 0 | aRv = data->GetAsISupports(getter_AddRefs(supports)); |
289 | 0 | MOZ_ASSERT(!aRv.Failed() && supports, |
290 | 0 | "File objects should be stored as nsISupports variants"); |
291 | 0 | if (aRv.Failed() || !supports) { |
292 | 0 | return nullptr; |
293 | 0 | } |
294 | 0 | |
295 | 0 | if (RefPtr<Blob> blob = do_QueryObject(supports)) { |
296 | 0 | mCachedFile = blob->ToFile(); |
297 | 0 | } else if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) { |
298 | 0 | MOZ_ASSERT(blobImpl->IsFile()); |
299 | 0 | mCachedFile = File::Create(mDataTransfer, blobImpl); |
300 | 0 | } else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) { |
301 | 0 | mCachedFile = File::CreateFromFile(mDataTransfer, ifile); |
302 | 0 | } else { |
303 | 0 | MOZ_ASSERT(false, "One of the above code paths should be taken"); |
304 | 0 | return nullptr; |
305 | 0 | } |
306 | 0 | } |
307 | 0 |
|
308 | 0 | RefPtr<File> file = mCachedFile; |
309 | 0 | return file.forget(); |
310 | 0 | } |
311 | | |
312 | | already_AddRefed<FileSystemEntry> |
313 | | DataTransferItem::GetAsEntry(nsIPrincipal& aSubjectPrincipal, |
314 | | ErrorResult& aRv) |
315 | 0 | { |
316 | 0 | RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv); |
317 | 0 | if (NS_WARN_IF(aRv.Failed()) || !file) { |
318 | 0 | return nullptr; |
319 | 0 | } |
320 | 0 | |
321 | 0 | nsCOMPtr<nsIGlobalObject> global; |
322 | 0 | // This is annoying, but DataTransfer may have various things as parent. |
323 | 0 | nsCOMPtr<EventTarget> target = |
324 | 0 | do_QueryInterface(mDataTransfer->GetParentObject()); |
325 | 0 | if (target) { |
326 | 0 | global = target->GetOwnerGlobal(); |
327 | 0 | } else { |
328 | 0 | RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject()); |
329 | 0 | if (event) { |
330 | 0 | global = event->GetParentObject(); |
331 | 0 | } |
332 | 0 | } |
333 | 0 |
|
334 | 0 | if (!global) { |
335 | 0 | return nullptr; |
336 | 0 | } |
337 | 0 | |
338 | 0 | RefPtr<FileSystem> fs = FileSystem::Create(global); |
339 | 0 | RefPtr<FileSystemEntry> entry; |
340 | 0 | BlobImpl* impl = file->Impl(); |
341 | 0 | MOZ_ASSERT(impl); |
342 | 0 |
|
343 | 0 | if (impl->IsDirectory()) { |
344 | 0 | nsAutoString fullpath; |
345 | 0 | impl->GetMozFullPathInternal(fullpath, aRv); |
346 | 0 | if (aRv.Failed()) { |
347 | 0 | aRv.SuppressException(); |
348 | 0 | return nullptr; |
349 | 0 | } |
350 | 0 | |
351 | 0 | nsCOMPtr<nsIFile> directoryFile; |
352 | 0 | // fullPath is already in unicode, we don't have to use |
353 | 0 | // NS_NewNativeLocalFile. |
354 | 0 | nsresult rv = NS_NewLocalFile(fullpath, true, |
355 | 0 | getter_AddRefs(directoryFile)); |
356 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
357 | 0 | return nullptr; |
358 | 0 | } |
359 | 0 | |
360 | 0 | RefPtr<Directory> directory = Directory::Create(global, directoryFile); |
361 | 0 | entry = new FileSystemDirectoryEntry(global, directory, nullptr, fs); |
362 | 0 | } else { |
363 | 0 | entry = new FileSystemFileEntry(global, file, nullptr, fs); |
364 | 0 | } |
365 | 0 |
|
366 | 0 | Sequence<RefPtr<FileSystemEntry>> entries; |
367 | 0 | if (!entries.AppendElement(entry, fallible)) { |
368 | 0 | return nullptr; |
369 | 0 | } |
370 | 0 | |
371 | 0 | fs->CreateRoot(entries); |
372 | 0 | return entry.forget(); |
373 | 0 | } |
374 | | |
375 | | already_AddRefed<File> |
376 | | DataTransferItem::CreateFileFromInputStream(nsIInputStream* aStream) |
377 | 0 | { |
378 | 0 | const char* key = nullptr; |
379 | 0 | for (uint32_t i = 0; i < ArrayLength(kFileMimeNameMap); ++i) { |
380 | 0 | if (mType.EqualsASCII(kFileMimeNameMap[i].mMimeName)) { |
381 | 0 | key = kFileMimeNameMap[i].mFileName; |
382 | 0 | break; |
383 | 0 | } |
384 | 0 | } |
385 | 0 | if (!key) { |
386 | 0 | MOZ_ASSERT_UNREACHABLE("Unsupported mime type"); |
387 | 0 | key = "GenericFileName"; |
388 | 0 | } |
389 | 0 |
|
390 | 0 | nsAutoString fileName; |
391 | 0 | nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
392 | 0 | key, fileName); |
393 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
394 | 0 | return nullptr; |
395 | 0 | } |
396 | 0 | |
397 | 0 | uint64_t available; |
398 | 0 | void* data = nullptr; |
399 | 0 | rv = NS_ReadInputStreamToBuffer(aStream, &data, -1, &available); |
400 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
401 | 0 | return nullptr; |
402 | 0 | } |
403 | 0 | |
404 | 0 | return File::CreateMemoryFile(mDataTransfer, data, available, fileName, |
405 | 0 | mType, PR_Now()); |
406 | 0 | } |
407 | | |
408 | | void |
409 | | DataTransferItem::GetAsString(FunctionStringCallback* aCallback, |
410 | | nsIPrincipal& aSubjectPrincipal, |
411 | | ErrorResult& aRv) |
412 | 0 | { |
413 | 0 | if (!aCallback) { |
414 | 0 | return; |
415 | 0 | } |
416 | 0 | |
417 | 0 | // Theoretically this should be done inside of the runnable, as it might be an |
418 | 0 | // expensive operation on some systems, however we wouldn't get access to the |
419 | 0 | // NS_ERROR_DOM_SECURITY_ERROR messages which may be raised by this method. |
420 | 0 | nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv); |
421 | 0 | if (NS_WARN_IF(!data || aRv.Failed())) { |
422 | 0 | return; |
423 | 0 | } |
424 | 0 | |
425 | 0 | // We have to check our kind after getting the data, because if we have |
426 | 0 | // external data and the OS lied to us (which unfortunately does happen |
427 | 0 | // sometimes), then we might not have the same type of data as we did coming |
428 | 0 | // into this function. |
429 | 0 | if (NS_WARN_IF(mKind != KIND_STRING)) { |
430 | 0 | return; |
431 | 0 | } |
432 | 0 | |
433 | 0 | nsAutoString stringData; |
434 | 0 | nsresult rv = data->GetAsAString(stringData); |
435 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
436 | 0 | return; |
437 | 0 | } |
438 | 0 | |
439 | 0 | // Dispatch the callback to the main thread |
440 | 0 | class GASRunnable final : public Runnable |
441 | 0 | { |
442 | 0 | public: |
443 | 0 | GASRunnable(FunctionStringCallback* aCallback, const nsAString& aStringData) |
444 | 0 | : mozilla::Runnable("GASRunnable") |
445 | 0 | , mCallback(aCallback) |
446 | 0 | , mStringData(aStringData) |
447 | 0 | {} |
448 | 0 |
|
449 | 0 | NS_IMETHOD Run() override |
450 | 0 | { |
451 | 0 | ErrorResult rv; |
452 | 0 | mCallback->Call(mStringData, rv); |
453 | 0 | NS_WARNING_ASSERTION(!rv.Failed(), "callback failed"); |
454 | 0 | return rv.StealNSResult(); |
455 | 0 | } |
456 | 0 | private: |
457 | 0 | RefPtr<FunctionStringCallback> mCallback; |
458 | 0 | nsString mStringData; |
459 | 0 | }; |
460 | 0 |
|
461 | 0 | RefPtr<GASRunnable> runnable = new GASRunnable(aCallback, stringData); |
462 | 0 |
|
463 | 0 | // DataTransfer.mParent might be EventTarget, nsIGlobalObject, ClipboardEvent |
464 | 0 | // nsPIDOMWindowOuter, null |
465 | 0 | nsISupports* parent = mDataTransfer->GetParentObject(); |
466 | 0 | nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(parent); |
467 | 0 | if (parent && !global) { |
468 | 0 | if (nsCOMPtr<dom::EventTarget> target = do_QueryInterface(parent)) { |
469 | 0 | global = target->GetOwnerGlobal(); |
470 | 0 | } else if (RefPtr<Event> event = do_QueryObject(parent)) { |
471 | 0 | global = event->GetParentObject(); |
472 | 0 | } |
473 | 0 | } |
474 | 0 | if (global) { |
475 | 0 | rv = global->Dispatch(TaskCategory::Other, runnable.forget()); |
476 | 0 | } else { |
477 | 0 | rv = NS_DispatchToMainThread(runnable); |
478 | 0 | } |
479 | 0 | if (NS_FAILED(rv)) { |
480 | 0 | NS_WARNING("Dispatch to main thread Failed in " |
481 | 0 | "DataTransferItem::GetAsString!"); |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | already_AddRefed<nsIVariant> |
486 | | DataTransferItem::DataNoSecurityCheck() |
487 | 0 | { |
488 | 0 | if (!mData) { |
489 | 0 | FillInExternalData(); |
490 | 0 | } |
491 | 0 | nsCOMPtr<nsIVariant> data = mData; |
492 | 0 | return data.forget(); |
493 | 0 | } |
494 | | |
495 | | already_AddRefed<nsIVariant> |
496 | | DataTransferItem::Data(nsIPrincipal* aPrincipal, ErrorResult& aRv) |
497 | 0 | { |
498 | 0 | MOZ_ASSERT(aPrincipal); |
499 | 0 |
|
500 | 0 | // If the inbound principal is system, we can skip the below checks, as |
501 | 0 | // they will trivially succeed. |
502 | 0 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
503 | 0 | return DataNoSecurityCheck(); |
504 | 0 | } |
505 | 0 | |
506 | 0 | // We should not allow raw data to be accessed from a Protected DataTransfer. |
507 | 0 | // We don't prevent this access if the accessing document is Chrome. |
508 | 0 | if (mDataTransfer->IsProtected()) { |
509 | 0 | return nullptr; |
510 | 0 | } |
511 | 0 | |
512 | 0 | nsCOMPtr<nsIVariant> variant = DataNoSecurityCheck(); |
513 | 0 |
|
514 | 0 | MOZ_ASSERT(!ChromeOnly(), "Non-chrome code shouldn't see a ChromeOnly DataTransferItem"); |
515 | 0 | if (ChromeOnly()) { |
516 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
517 | 0 | return nullptr; |
518 | 0 | } |
519 | 0 | |
520 | 0 | bool checkItemPrincipal = mDataTransfer->IsCrossDomainSubFrameDrop() || |
521 | 0 | (mDataTransfer->GetEventMessage() != eDrop && |
522 | 0 | mDataTransfer->GetEventMessage() != ePaste); |
523 | 0 |
|
524 | 0 | // Check if the caller is allowed to access the drag data. Callers with |
525 | 0 | // chrome privileges can always read the data. During the |
526 | 0 | // drop event, allow retrieving the data except in the case where the |
527 | 0 | // source of the drag is in a child frame of the caller. In that case, |
528 | 0 | // we only allow access to data of the same principal. During other events, |
529 | 0 | // only allow access to the data with the same principal. |
530 | 0 | // |
531 | 0 | // We don't want to fail with an exception in this siutation, rather we want |
532 | 0 | // to just pretend as though the stored data is "nullptr". This is consistent |
533 | 0 | // with Chrome's behavior and is less surprising for web applications which |
534 | 0 | // don't expect execptions to be raised when performing certain operations. |
535 | 0 | if (Principal() && checkItemPrincipal && |
536 | 0 | !aPrincipal->Subsumes(Principal())) { |
537 | 0 | return nullptr; |
538 | 0 | } |
539 | 0 | |
540 | 0 | if (!variant) { |
541 | 0 | return nullptr; |
542 | 0 | } |
543 | 0 | |
544 | 0 | nsCOMPtr<nsISupports> data; |
545 | 0 | nsresult rv = variant->GetAsISupports(getter_AddRefs(data)); |
546 | 0 | if (NS_SUCCEEDED(rv) && data) { |
547 | 0 | nsCOMPtr<EventTarget> pt = do_QueryInterface(data); |
548 | 0 | if (pt) { |
549 | 0 | nsIGlobalObject* go = pt->GetOwnerGlobal(); |
550 | 0 | if (NS_WARN_IF(!go)) { |
551 | 0 | return nullptr; |
552 | 0 | } |
553 | 0 | |
554 | 0 | nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go); |
555 | 0 | MOZ_ASSERT(sp, "This cannot fail on the main thread."); |
556 | 0 |
|
557 | 0 | nsIPrincipal* dataPrincipal = sp->GetPrincipal(); |
558 | 0 | if (NS_WARN_IF(!dataPrincipal || !aPrincipal->Equals(dataPrincipal))) { |
559 | 0 | return nullptr; |
560 | 0 | } |
561 | 0 | } |
562 | 0 | } |
563 | 0 | |
564 | 0 | return variant.forget(); |
565 | 0 | } |
566 | | |
567 | | } // namespace dom |
568 | | } // namespace mozilla |