From 03310cda94c89764fb3e7e02ec43c6c9054b6dda Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Fri, 18 Mar 2016 13:47:50 +0800 Subject: [PATCH 01/28] Bug 1249556 - Rename override to disallowOverridingFocusability. r=bz Rename 'override' to 'disallowOverridingFocusability' in nsGenericHTMLElement::IsHTMLFocusable() to make the implementation reflect the semantic that the subclass is disallow to override the value returned in aIsFocusable described in nsGenericHTMLElement.h. Also 'override' is a new specifier since C++11. Rename it make the syntax highlight looks right. MozReview-Commit-ID: CShdChjBv7j --HG-- extra : rebase_source : 6cf17cf1771c3f75fb31d1d6f882daffa015ab1c --- dom/html/nsGenericHTMLElement.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index eb1098a76fa4..706a01816de4 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2722,11 +2722,12 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, } int32_t tabIndex = TabIndex(); + bool disabled = false; + bool disallowOverridingFocusability = true; - bool override, disabled = false; if (IsEditableRoot()) { // Editable roots should always be focusable. - override = true; + disallowOverridingFocusability = true; // Ignore the disabled attribute in editable contentEditable/designMode // roots. @@ -2737,7 +2738,7 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, } } else { - override = false; + disallowOverridingFocusability = false; // Just check for disabled attribute on form controls disabled = IsDisabled(); @@ -2751,10 +2752,10 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse, } // If a tabindex is specified at all, or the default tabindex is 0, we're focusable - *aIsFocusable = + *aIsFocusable = (tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex))); - return override; + return disallowOverridingFocusability; } void From 22224242ba2863038dfef1d793361a56be431ce0 Mon Sep 17 00:00:00 2001 From: Ting-Yu Lin Date: Sat, 19 Mar 2016 20:37:09 +0800 Subject: [PATCH 02/28] Bug 1249556 - Implement toggling details by keyboard. r=smaug The user can switch to the main by tab key, and toggle the
by either 'space' key or 'enter' key. 'return' key is handled with 'keypress', and the 'space' key is handled with 'keyup' like the HTMLInputElement. MozReview-Commit-ID: HE6IduUGCpj --HG-- extra : rebase_source : 34598d95f35bf6b5bd927457ee09e42eb6ec0a68 --- dom/html/HTMLButtonElement.cpp | 11 +- dom/html/HTMLInputElement.cpp | 23 +--- dom/html/HTMLSummaryElement.cpp | 107 ++++++++++++++---- dom/html/HTMLSummaryElement.h | 5 + dom/html/nsGenericHTMLElement.cpp | 19 ++-- dom/html/nsGenericHTMLElement.h | 7 ++ .../key-enter-open-second-summary.html | 21 ++++ .../key-enter-prevent-default.html | 24 ++++ .../key-enter-single-summary.html | 20 ++++ .../key-space-single-summary.html | 21 ++++ layout/reftests/details-summary/reftest.list | 6 + 11 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 layout/reftests/details-summary/key-enter-open-second-summary.html create mode 100644 layout/reftests/details-summary/key-enter-prevent-default.html create mode 100644 layout/reftests/details-summary/key-enter-single-summary.html create mode 100644 layout/reftests/details-summary/key-space-single-summary.html diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index ad8c42694761..e2c8f3fff395 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -292,15 +292,8 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor) eKeyPress == aVisitor.mEvent->mMessage) || (keyEvent->keyCode == NS_VK_SPACE && eKeyUp == aVisitor.mEvent->mMessage)) { - nsEventStatus status = nsEventStatus_eIgnore; - - WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted, - eMouseClick, nullptr, - WidgetMouseEvent::eReal); - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; - EventDispatcher::Dispatch(static_cast(this), - aVisitor.mPresContext, &event, nullptr, - &status); + DispatchSimulatedClick(this, aVisitor.mEvent->mFlags.mIsTrusted, + aVisitor.mPresContext); aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; } } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 64bfebea7366..b25da9f3d465 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3824,15 +3824,8 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) case NS_FORM_INPUT_IMAGE: // Bug 34418 case NS_FORM_INPUT_COLOR: { - WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted, - eMouseClick, nullptr, - WidgetMouseEvent::eReal); - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; - nsEventStatus status = nsEventStatus_eIgnore; - - EventDispatcher::Dispatch(static_cast(this), - aVisitor.mPresContext, &event, - nullptr, &status); + DispatchSimulatedClick(this, aVisitor.mEvent->mFlags.mIsTrusted, + aVisitor.mPresContext); aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; } // case } // switch @@ -3859,15 +3852,9 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) if (selectedRadioButton) { rv = selectedRadioButton->Focus(); if (NS_SUCCEEDED(rv)) { - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted, - eMouseClick, nullptr, - WidgetMouseEvent::eReal); - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; - rv = - EventDispatcher::Dispatch(ToSupports(selectedRadioButton), - aVisitor.mPresContext, - &event, nullptr, &status); + rv = DispatchSimulatedClick(selectedRadioButton, + aVisitor.mEvent->mFlags.mIsTrusted, + aVisitor.mPresContext); if (NS_SUCCEEDED(rv)) { aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; } diff --git a/dom/html/HTMLSummaryElement.cpp b/dom/html/HTMLSummaryElement.cpp index 96641b9be813..3bce6eaf6079 100644 --- a/dom/html/HTMLSummaryElement.cpp +++ b/dom/html/HTMLSummaryElement.cpp @@ -10,6 +10,8 @@ #include "mozilla/dom/HTMLUnknownElement.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" +#include "mozilla/TextEvents.h" +#include "nsFocusManager.h" // Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Summary) to add pref check. nsGenericHTMLElement* @@ -44,34 +46,99 @@ HTMLSummaryElement::PostHandleEvent(EventChainPostVisitor& aVisitor) return rv; } - auto toggleDetails = false; - auto* event = aVisitor.mEvent; - - if (event->HasMouseEventMessage()) { - auto* mouseEvent = event->AsMouseEvent(); - toggleDetails = mouseEvent->IsLeftClickEvent(); - } - - // Todo: Bug 634004: Implement toggle details by keyboard. - - if (!toggleDetails || !IsMainSummary()) { + if (!IsMainSummary()) { return rv; } - auto* details = GetDetails(); - MOZ_ASSERT(details, "Expected to find details since this is the main summary!"); + WidgetEvent* const event = aVisitor.mEvent; - // When dispatching a synthesized mouse click event to a details with - // 'display: none', both Chrome and Safari do not toggle the 'open' attribute. - // We follow them by checking whether details has a frame or not. - if (details->GetPrimaryFrame()) { - details->ToggleOpen(); - aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; - } + if (event->HasMouseEventMessage()) { + WidgetMouseEvent* mouseEvent = event->AsMouseEvent(); + + if (mouseEvent->IsLeftClickEvent()) { + RefPtr details = GetDetails(); + MOZ_ASSERT(details, + "Expected to find details since this is the main summary!"); + + // When dispatching a synthesized mouse click event to a details element + // with 'display: none', both Chrome and Safari do not toggle the 'open' + // attribute. We follow them by checking whether the details element has a + // frame or not. + if (details->GetPrimaryFrame(Flush_Frames)) { + details->ToggleOpen(); + aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; + return NS_OK; + } + } + } // event->HasMouseEventMessage() + + if (event->HasKeyEventMessage()) { + WidgetKeyboardEvent* keyboardEvent = event->AsKeyboardEvent(); + bool dispatchClick = false; + + switch (event->mMessage) { + case eKeyPress: + if (keyboardEvent->charCode == nsIDOMKeyEvent::DOM_VK_SPACE) { + // Consume 'space' key to prevent scrolling the page down. + aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; + } + + dispatchClick = keyboardEvent->keyCode == nsIDOMKeyEvent::DOM_VK_RETURN; + break; + + case eKeyUp: + dispatchClick = keyboardEvent->keyCode == nsIDOMKeyEvent::DOM_VK_SPACE; + break; + + default: + break; + } + + if (dispatchClick) { + rv = DispatchSimulatedClick(this, event->mFlags.mIsTrusted, + aVisitor.mPresContext); + if (NS_SUCCEEDED(rv)) { + aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault; + } + } + } // event->HasKeyEventMessage() return rv; } +bool +HTMLSummaryElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, + int32_t* aTabIndex) +{ + bool disallowOverridingFocusability = + nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex); + + if (disallowOverridingFocusability || !IsMainSummary()) { + return disallowOverridingFocusability; + } + +#ifdef XP_MACOSX + // The parent does not have strong opinion about the focusability of this main + // summary element, but we'd like to override it when mouse clicking on Mac OS + // like other form elements. + *aIsFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl; +#else + // The main summary element is focusable on other platforms. + *aIsFocusable = true; +#endif + + // Give a chance to allow the subclass to override aIsFocusable. + return false; +} + +int32_t +HTMLSummaryElement::TabIndexDefault() +{ + // Make the main summary be able to navigate via tab, and be focusable. + // See nsGenericHTMLElement::IsHTMLFocusable(). + return IsMainSummary() ? 0 : nsGenericHTMLElement::TabIndexDefault(); +} + bool HTMLSummaryElement::IsMainSummary() const { diff --git a/dom/html/HTMLSummaryElement.h b/dom/html/HTMLSummaryElement.h index f58b9e5a32e3..587383bc68b0 100644 --- a/dom/html/HTMLSummaryElement.h +++ b/dom/html/HTMLSummaryElement.h @@ -33,6 +33,11 @@ public: nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override; + bool IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, + int32_t* aTabIndex) override; + + int32_t TabIndexDefault() override; + // Return true if this is the first summary element child of a details or the // default summary element generated by DetailsFrame. bool IsMainSummary() const; diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 706a01816de4..665b21cf44d5 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2805,20 +2805,25 @@ nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation, if (aKeyCausesActivation) { // Click on it if the users prefs indicate to do so. - WidgetMouseEvent event(aIsTrustedEvent, eMouseClick, nullptr, - WidgetMouseEvent::eReal); - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; - nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ? openAllowed : openAbused); - - EventDispatcher::Dispatch(static_cast(this), - presContext, &event); + DispatchSimulatedClick(this, aIsTrustedEvent, presContext); } return focused; } +nsresult +nsGenericHTMLElement::DispatchSimulatedClick(nsGenericHTMLElement* aElement, + bool aIsTrusted, + nsPresContext* aPresContext) +{ + WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr, + WidgetMouseEvent::eReal); + event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD; + return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event); +} + const nsAttrName* nsGenericHTMLElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const { diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index 2f6e3e29fab4..7afebb20c789 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -1027,6 +1027,13 @@ protected: virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const override; + /** + * Dispatch a simulated mouse click by keyboard to the given element. + */ + nsresult DispatchSimulatedClick(nsGenericHTMLElement* aElement, + bool aIsTrusted, + nsPresContext* aPresContext); + /** * Create a URI for the given aURISpec string. * Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty diff --git a/layout/reftests/details-summary/key-enter-open-second-summary.html b/layout/reftests/details-summary/key-enter-open-second-summary.html new file mode 100644 index 000000000000..4d5899787f74 --- /dev/null +++ b/layout/reftests/details-summary/key-enter-open-second-summary.html @@ -0,0 +1,21 @@ + + + + + + +
+ Summary + Second Summary +

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/key-enter-prevent-default.html b/layout/reftests/details-summary/key-enter-prevent-default.html new file mode 100644 index 000000000000..6e657a64343d --- /dev/null +++ b/layout/reftests/details-summary/key-enter-prevent-default.html @@ -0,0 +1,24 @@ + + + + + + +
+ Summary +

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/key-enter-single-summary.html b/layout/reftests/details-summary/key-enter-single-summary.html new file mode 100644 index 000000000000..668573e90e03 --- /dev/null +++ b/layout/reftests/details-summary/key-enter-single-summary.html @@ -0,0 +1,20 @@ + + + + + + +
+ Summary +

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/key-space-single-summary.html b/layout/reftests/details-summary/key-space-single-summary.html new file mode 100644 index 000000000000..8f8cecdb18b2 --- /dev/null +++ b/layout/reftests/details-summary/key-space-single-summary.html @@ -0,0 +1,21 @@ + + + + + + +
+ Summary +

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/reftest.list b/layout/reftests/details-summary/reftest.list index 54cf691025e9..0480364f1d54 100644 --- a/layout/reftests/details-summary/reftest.list +++ b/layout/reftests/details-summary/reftest.list @@ -72,3 +72,9 @@ pref(dom.details_element.enabled,true) == mouse-click-fixed-summary.html open-fi pref(dom.details_element.enabled,true) == mouse-click-twice-fixed-summary.html fixed-summary.html pref(dom.details_element.enabled,true) == mouse-click-float-details.html open-float-details.html pref(dom.details_element.enabled,true) == mouse-click-twice-float-details.html float-details.html + +# Dispatch keyboard event to summary +pref(dom.details_element.enabled,true) == key-enter-single-summary.html open-single-summary.html +pref(dom.details_element.enabled,true) == key-enter-open-second-summary.html open-multiple-summary.html +pref(dom.details_element.enabled,true) == key-enter-prevent-default.html single-summary.html +pref(dom.details_element.enabled,true) == key-space-single-summary.html open-single-summary.html From 96a413ffed74a3fb710bb7077b6fc431518b3938 Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Sun, 20 Mar 2016 10:08:57 +0100 Subject: [PATCH 03/28] Bug 1248915 - TSan: data race on global 'mozilla::net::CacheObserver::sDiskCacheCapacity'. r=honzab.moz, n.nethercote. --- modules/libpref/Preferences.cpp | 34 ++++++++++++++++++++++++++++++++ modules/libpref/Preferences.h | 4 ++++ netwerk/cache2/CacheObserver.cpp | 5 +++-- netwerk/cache2/CacheObserver.h | 2 +- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 017f68ebadc7..cb41867a2011 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -1788,6 +1788,40 @@ Preferences::AddUintVarCache(uint32_t* aCache, return RegisterCallback(UintVarChanged, aPref, data); } +template +static void AtomicUintVarChanged(const char* aPref, void* aClosure) +{ + CacheData* cache = static_cast(aClosure); + *((Atomic*)cache->cacheLocation) = + Preferences::GetUint(aPref, cache->defaultValueUint); +} + +template +// static +nsresult +Preferences::AddAtomicUintVarCache(Atomic* aCache, + const char* aPref, + uint32_t aDefault) +{ + NS_ASSERTION(aCache, "aCache must not be NULL"); +#ifdef DEBUG + AssertNotAlreadyCached("uint", aPref, aCache); +#endif + *aCache = Preferences::GetUint(aPref, aDefault); + CacheData* data = new CacheData(); + data->cacheLocation = aCache; + data->defaultValueUint = aDefault; + gCacheData->AppendElement(data); + return RegisterCallback(AtomicUintVarChanged, aPref, data); +} + +// Since the definition of this template function is not in a header file, +// we need to explicitly specify the instantiations that are required. +// Currently only the order=Relaxed variant is needed. +template +nsresult Preferences::AddAtomicUintVarCache(Atomic*, + const char*, uint32_t); + static void FloatVarChanged(const char* aPref, void* aClosure) { CacheData* cache = static_cast(aClosure); diff --git a/modules/libpref/Preferences.h b/modules/libpref/Preferences.h index 82869a357e7a..8415bb03392f 100644 --- a/modules/libpref/Preferences.h +++ b/modules/libpref/Preferences.h @@ -274,6 +274,10 @@ public: static nsresult AddUintVarCache(uint32_t* aVariable, const char* aPref, uint32_t aDefault = 0); + template + static nsresult AddAtomicUintVarCache(Atomic* aVariable, + const char* aPref, + uint32_t aDefault = 0); static nsresult AddFloatVarCache(float* aVariable, const char* aPref, float aDefault = 0.0f); diff --git a/netwerk/cache2/CacheObserver.cpp b/netwerk/cache2/CacheObserver.cpp index 2b772bb3546b..2d2b9aff2f97 100644 --- a/netwerk/cache2/CacheObserver.cpp +++ b/netwerk/cache2/CacheObserver.cpp @@ -51,7 +51,8 @@ int32_t CacheObserver::sMemoryCacheCapacity = kDefaultMemoryCacheCapacity; int32_t CacheObserver::sAutoMemoryCacheCapacity = -1; static uint32_t const kDefaultDiskCacheCapacity = 250 * 1024; // 250 MB -uint32_t CacheObserver::sDiskCacheCapacity = kDefaultDiskCacheCapacity; +Atomic CacheObserver::sDiskCacheCapacity + (kDefaultDiskCacheCapacity); static uint32_t const kDefaultDiskFreeSpaceSoftLimit = 5 * 1024; // 5MB uint32_t CacheObserver::sDiskFreeSpaceSoftLimit = kDefaultDiskFreeSpaceSoftLimit; @@ -158,7 +159,7 @@ CacheObserver::AttachToPreferences() mozilla::Preferences::AddUintVarCache( &sMetadataMemoryLimit, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit); - mozilla::Preferences::AddUintVarCache( + mozilla::Preferences::AddAtomicUintVarCache( &sDiskCacheCapacity, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity); mozilla::Preferences::AddBoolVarCache( &sSmartCacheSizeEnabled, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled); diff --git a/netwerk/cache2/CacheObserver.h b/netwerk/cache2/CacheObserver.h index c428e8951106..46f89a671441 100644 --- a/netwerk/cache2/CacheObserver.h +++ b/netwerk/cache2/CacheObserver.h @@ -87,7 +87,7 @@ private: static uint32_t sMetadataMemoryLimit; static int32_t sMemoryCacheCapacity; static int32_t sAutoMemoryCacheCapacity; - static uint32_t sDiskCacheCapacity; + static Atomic sDiskCacheCapacity; static uint32_t sDiskFreeSpaceSoftLimit; static uint32_t sDiskFreeSpaceHardLimit; static bool sSmartCacheSizeEnabled; From 18858bf14460dda43ba794d2bffcb5eb1c071a0b Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 04/28] Bug 1173320 - patch 1/8 - Implement Directory object as string and not as BlobImpl, r=smaug --- dom/base/File.cpp | 22 +- dom/base/File.h | 88 +----- dom/base/nsContentUtils.cpp | 1 - dom/events/DataTransfer.cpp | 56 ++-- dom/events/DataTransfer.h | 7 +- dom/filehandle/ActorsChild.cpp | 3 +- dom/filesystem/CreateDirectoryTask.cpp | 125 ++++++-- dom/filesystem/CreateDirectoryTask.h | 36 ++- dom/filesystem/CreateFileTask.cpp | 167 ++++++---- dom/filesystem/CreateFileTask.h | 43 ++- dom/filesystem/DeviceStorageFileSystem.cpp | 20 +- dom/filesystem/Directory.cpp | 246 ++++++++++----- dom/filesystem/Directory.h | 52 +++- dom/filesystem/FileSystemBase.cpp | 36 ++- dom/filesystem/FileSystemBase.h | 7 +- dom/filesystem/FileSystemRequestParent.cpp | 7 +- dom/filesystem/FileSystemTaskBase.cpp | 27 +- dom/filesystem/FileSystemTaskBase.h | 8 +- dom/filesystem/FileSystemUtils.cpp | 26 ++ dom/filesystem/FileSystemUtils.h | 6 + dom/filesystem/GetDirectoryListingTask.cpp | 245 +++++++++++---- dom/filesystem/GetDirectoryListingTask.h | 55 ++-- dom/filesystem/GetFileOrDirectoryTask.cpp | 146 ++++++--- dom/filesystem/GetFileOrDirectoryTask.h | 46 ++- dom/filesystem/PFileSystemRequest.ipdl | 19 +- dom/filesystem/RemoveTask.cpp | 156 +++++++--- dom/filesystem/RemoveTask.h | 48 ++- dom/html/HTMLInputElement.cpp | 337 +++++++++++++-------- dom/html/HTMLInputElement.h | 30 +- dom/indexedDB/FileSnapshot.h | 24 -- dom/indexedDB/IDBObjectStore.cpp | 3 +- dom/ipc/Blob.cpp | 64 +--- dom/ipc/BlobChild.h | 5 +- dom/ipc/DOMTypes.ipdlh | 1 - dom/ipc/FilePickerParent.cpp | 98 ++++-- dom/ipc/FilePickerParent.h | 31 +- dom/ipc/PContent.ipdl | 2 + dom/ipc/PFilePicker.ipdl | 14 +- dom/webidl/Directory.webidl | 3 + widget/nsBaseFilePicker.cpp | 75 +++-- widget/nsFilePickerProxy.cpp | 66 +++- widget/nsFilePickerProxy.h | 11 +- 42 files changed, 1579 insertions(+), 883 deletions(-) diff --git a/dom/base/File.cpp b/dom/base/File.cpp index a82995231559..c365912eb382 100644 --- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -227,12 +227,6 @@ Blob::IsFile() const return mImpl->IsFile(); } -bool -Blob::IsDirectory() const -{ - return mImpl->IsDirectory(); -} - const nsTArray>* Blob::GetSubBlobImpls() const { @@ -420,11 +414,10 @@ File::Create(nsISupports* aParent, BlobImpl* aImpl) /* static */ already_AddRefed File::Create(nsISupports* aParent, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, BlobDirState aDirState) + int64_t aLastModifiedDate) { RefPtr file = new File(aParent, - new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate, - aDirState)); + new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)); return file.forget(); } @@ -946,17 +939,6 @@ BlobImplFile::SetPath(const nsAString& aPath) mPath = aPath; } -void -BlobImplFile::LookupAndCacheIsDirectory() -{ - MOZ_ASSERT(mIsFile, - "This should only be called when this object has been created " - "from an nsIFile to note that the nsIFile is a directory"); - bool isDir; - mFile->IsDirectory(&isDir); - mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir; -} - //////////////////////////////////////////////////////////////////////////// // EmptyBlobImpl implementation diff --git a/dom/base/File.h b/dom/base/File.h index c60627b2a3e3..5f444b15ec28 100644 --- a/dom/base/File.h +++ b/dom/base/File.h @@ -44,18 +44,6 @@ class BlobImpl; class File; class OwningArrayBufferOrArrayBufferViewOrBlobOrString; -/** - * Used to indicate when a Blob/BlobImpl that was created from an nsIFile - * (when IsFile() will return true) was from an nsIFile for which - * nsIFile::IsDirectory() returned true. This is a tri-state to enable us to - * assert that the state is always set when callers request it. - */ -enum BlobDirState : uint32_t { - eIsDir, - eIsNotDir, - eUnknownIfDir -}; - class Blob : public nsIDOMBlob , public nsIXHRSendable , public nsIMutable @@ -101,12 +89,6 @@ public: bool IsFile() const; - /** - * This may return true if the Blob was created from an nsIFile that is a - * directory. - */ - bool IsDirectory() const; - const nsTArray>* GetSubBlobImpls() const; // This method returns null if this Blob is not a File; it returns @@ -114,9 +96,6 @@ public: // otherwise it returns a new File object with the same BlobImpl. already_AddRefed ToFile(); - // XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem - // object complicates that though. - // This method creates a new File object with the given name and the same // BlobImpl. already_AddRefed ToFile(const nsAString& aName, @@ -194,7 +173,7 @@ public: static already_AddRefed Create(nsISupports* aParent, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, BlobDirState aDirState); + int64_t aLastModifiedDate); // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be // freed by free so it must be allocated by malloc or something @@ -339,28 +318,6 @@ public: virtual bool IsFile() const = 0; - /** - * Called when this BlobImpl was created from an nsIFile in order to call - * nsIFile::IsDirectory() and cache the result so that when the BlobImpl is - * copied to another process that informaton is available. - * nsIFile::IsDirectory() does synchronous I/O, and BlobImpl objects may be - * created on the main thread or in a non-chrome process (where I/O is not - * allowed). Do not call this on a non-chrome process, and preferably do not - * call it on the main thread. - * - * Not all creators of BlobImplFile will call this method, in which case - * calling IsDirectory will MOZ_ASSERT. - */ - virtual void LookupAndCacheIsDirectory() = 0; - virtual void SetIsDirectory(bool aIsDir) = 0; - virtual bool IsDirectory() const = 0; - - /** - * Prefer IsDirectory(). This exists to help consumer code pass on state from - * one BlobImpl when creating another. - */ - virtual BlobDirState GetDirState() const = 0; - // True if this implementation can be sent to other threads. virtual bool MayBeClonedToOtherThreads() const { @@ -377,11 +334,9 @@ class BlobImplBase : public BlobImpl { public: BlobImplBase(const nsAString& aName, const nsAString& aContentType, - uint64_t aLength, int64_t aLastModifiedDate, - BlobDirState aDirState = BlobDirState::eUnknownIfDir) + uint64_t aLength, int64_t aLastModifiedDate) : mIsFile(true) , mImmutable(false) - , mDirState(aDirState) , mContentType(aContentType) , mName(aName) , mStart(0) @@ -397,7 +352,6 @@ public: uint64_t aLength) : mIsFile(true) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mName(aName) , mStart(0) @@ -412,7 +366,6 @@ public: BlobImplBase(const nsAString& aContentType, uint64_t aLength) : mIsFile(false) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mStart(0) , mLength(aLength) @@ -427,7 +380,6 @@ public: uint64_t aLength) : mIsFile(false) , mImmutable(false) - , mDirState(BlobDirState::eUnknownIfDir) , mContentType(aContentType) , mStart(aStart) , mLength(aLength) @@ -518,36 +470,6 @@ public: return mIsFile; } - virtual void LookupAndCacheIsDirectory() override - { - MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?"); - } - - virtual void SetIsDirectory(bool aIsDir) override - { - MOZ_ASSERT(mIsFile, - "This should only be called when this object has been created " - "from an nsIFile to note that the nsIFile is a directory"); - mDirState = aIsDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir; - } - - /** - * Returns true if the nsIFile that this object wraps is a directory. - */ - virtual bool IsDirectory() const override - { - MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir, - "Must only be used by callers for whom the code paths are " - "know to call LookupAndCacheIsDirectory() or " - "SetIsDirectory()"); - return mDirState == BlobDirState::eIsDir; - } - - virtual BlobDirState GetDirState() const override - { - return mDirState; - } - virtual bool IsSizeUnknown() const override { return mLength == UINT64_MAX; @@ -565,7 +487,6 @@ protected: bool mIsFile; bool mImmutable; - BlobDirState mDirState; nsString mContentType; nsString mName; @@ -590,8 +511,7 @@ public: BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate) - : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate, - BlobDirState::eIsNotDir) + : BlobImplBase(aName, aContentType, aLength, aLastModifiedDate) , mDataOwner(new DataOwner(aMemoryBuffer, aLength)) { NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); @@ -792,8 +712,6 @@ public: void SetPath(const nsAString& aFullPath); - virtual void LookupAndCacheIsDirectory() override; - // We always have size and date for this kind of blob. virtual bool IsSizeUnknown() const override { return false; } virtual bool IsDateUnknown() const override { return false; } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index bfe977dd4a1e..5a199208fba5 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7625,7 +7625,6 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable, // has this data available to it when passed over: blobImpl->GetSize(rv); blobImpl->GetLastModified(rv); - blobImpl->LookupAndCacheIsDirectory(); } else { if (aInSyncMessage) { // Can't do anything. diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index b1ce9c9f2a4f..17fb01005aad 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -49,7 +49,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList) NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage) @@ -57,7 +57,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage) @@ -281,11 +281,12 @@ DataTransfer::GetMozUserCancelled(bool* aUserCancelled) FileList* DataTransfer::GetFiles(ErrorResult& aRv) { - return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal()); + return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal()); } FileList* -DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal) +DataTransfer::GetFileListInternal(ErrorResult& aRv, + nsIPrincipal* aSubjectPrincipal) { if (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop && @@ -293,14 +294,15 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal return nullptr; } - if (!mFiles) { - mFiles = new FileList(static_cast(this)); + if (!mFileList) { + mFileList = new FileList(static_cast(this)); uint32_t count = mItems.Length(); for (uint32_t i = 0; i < count; i++) { nsCOMPtr variant; - aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, aSubjectPrincipal, getter_AddRefs(variant)); + aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, + aSubjectPrincipal, getter_AddRefs(variant)); if (aRv.Failed()) { return nullptr; } @@ -338,21 +340,21 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal MOZ_ASSERT(domFile); } - if (!mFiles->Append(domFile)) { + if (!mFileList->Append(domFile)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } } - return mFiles; + return mFileList; } NS_IMETHODIMP DataTransfer::GetFiles(nsIDOMFileList** aFileList) { ErrorResult rv; - NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal())); + NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal())); return rv.StealNSResult(); } @@ -854,7 +856,7 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) return nullptr; } - if (!mFiles) { + if (!mFileList) { GetFiles(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; @@ -863,38 +865,14 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) Sequence filesAndDirsSeq; - if (mFiles && mFiles->Length()) { - if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) { + if (mFileList && mFileList->Length()) { + if (!filesAndDirsSeq.SetLength(mFileList->Length(), mozilla::fallible_t())) { p->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return p.forget(); } - for (uint32_t i = 0; i < mFiles->Length(); ++i) { - if (mFiles->Item(i)->Impl()->IsDirectory()) { -#if defined(ANDROID) || defined(MOZ_B2G) - MOZ_ASSERT(false, - "Directory picking should have been redirected to normal " - "file picking for platforms that don't have a directory " - "picker"); -#endif - nsAutoString path; - mFiles->Item(i)->GetMozFullPathInternal(path, aRv); - if (aRv.Failed()) { - return nullptr; - } - int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR); - nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex); - nsDependentSubstring basename = Substring(path, leafSeparatorIndex); - - RefPtr fs = new OSFileSystem(dirname); - fs->Init(parentNode->OwnerDoc()->GetInnerWindow()); - - RefPtr directory = new Directory(fs, basename); - directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); - filesAndDirsSeq[i].SetAsDirectory() = directory; - } else { - filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i); - } + for (uint32_t i = 0; i < mFileList->Length(); ++i) { + filesAndDirsSeq[i].SetAsFile() = mFileList->Item(i); } } diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h index 2b1f90a51ba5..f65a78d0c780 100644 --- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -252,7 +252,7 @@ protected: void FillInExternalData(TransferItem& aItem, uint32_t aIndex); - FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal); + FileList* GetFileListInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal); nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex, nsIPrincipal* aSubjectPrincipal, nsIVariant** aData); nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex, @@ -300,8 +300,9 @@ protected: // array of items, each containing an array of format->data pairs nsTArray > mItems; - // array of files, containing only the files present in the dataTransfer - RefPtr mFiles; + // array of files and directories, containing only the files present in the + // dataTransfer + RefPtr mFileList; // the target of the drag. The drag and dragend events will fire at this. nsCOMPtr mDragTarget; diff --git a/dom/filehandle/ActorsChild.cpp b/dom/filehandle/ActorsChild.cpp index 8ade9df479cf..20885683d643 100644 --- a/dom/filehandle/ActorsChild.cpp +++ b/dom/filehandle/ActorsChild.cpp @@ -318,8 +318,7 @@ ConvertActorToFile(FileHandleBase* aFileHandle, actor->SetMysteryBlobInfo(mutableFile->Name(), mutableFile->Type(), size.get_uint64_t(), - lastModified.get_int64_t(), - BlobDirState::eUnknownIfDir); + lastModified.get_int64_t()); RefPtr blobImpl = actor->GetBlobImpl(); MOZ_ASSERT(blobImpl); diff --git a/dom/filesystem/CreateDirectoryTask.cpp b/dom/filesystem/CreateDirectoryTask.cpp index b929c5038556..3cade3a232b0 100644 --- a/dom/filesystem/CreateDirectoryTask.cpp +++ b/dom/filesystem/CreateDirectoryTask.cpp @@ -16,33 +16,73 @@ namespace mozilla { namespace dom { -CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aPath) +/* static */ already_AddRefed +CreateDirectoryTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateDirectoryTask(aFileSystem, aTargetPath); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); } -CreateDirectoryTask::CreateDirectoryTask( - FileSystemBase* aFileSystem, - const FileSystemCreateDirectoryParams& aParam, - FileSystemRequestParent* aParent) - : FileSystemTaskBase(aFileSystem, aParam, aParent) +/* static */ already_AddRefed +CreateDirectoryTask::Create(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateDirectoryTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); +} + +CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent) + : FileSystemTaskBase(aFileSystem, aParam, aParent) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - mTargetRealPath = aParam.realPath(); } CreateDirectoryTask::~CreateDirectoryTask() @@ -59,25 +99,44 @@ CreateDirectoryTask::GetPromise() } FileSystemParams -CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const +CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemCreateDirectoryParams(); + } + + return FileSystemCreateDirectoryParams(aSerializedDOMPath, path); } FileSystemResponseValue -CreateDirectoryTask::GetSuccessRequestResult() const +CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemDirectoryResponse(mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); + } + + return FileSystemDirectoryResponse(path); } void -CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemDirectoryResponse r = aValue; - mTargetRealPath = r.realPath(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + NS_WARN_IF(aRv.Failed()); } nsresult @@ -91,13 +150,8 @@ CreateDirectoryTask::Work() return NS_ERROR_FAILURE; } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool fileExists; - nsresult rv = file->Exists(&fileExists); + nsresult rv = mTargetPath->Exists(&fileExists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -106,8 +160,12 @@ CreateDirectoryTask::Work() return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR; } - rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770); - return rv; + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; } void @@ -124,7 +182,12 @@ CreateDirectoryTask::HandlerCallback() mPromise = nullptr; return; } - RefPtr dir = new Directory(mFileSystem, mTargetRealPath); + RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + mTargetPath, + Directory::eNotDOMRootDirectory, + mFileSystem); + MOZ_ASSERT(dir); + mPromise->MaybeResolve(dir); mPromise = nullptr; } diff --git a/dom/filesystem/CreateDirectoryTask.h b/dom/filesystem/CreateDirectoryTask.h index fb342c94bac1..837730629df3 100644 --- a/dom/filesystem/CreateDirectoryTask.h +++ b/dom/filesystem/CreateDirectoryTask.h @@ -16,16 +16,19 @@ namespace dom { class Promise; -class CreateDirectoryTask final - : public FileSystemTaskBase +class CreateDirectoryTask final : public FileSystemTaskBase { public: - CreateDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - ErrorResult& aRv); - CreateDirectoryTask(FileSystemBase* aFileSystem, - const FileSystemCreateDirectoryParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~CreateDirectoryTask(); @@ -38,13 +41,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -53,8 +58,15 @@ protected: HandlerCallback() override; private: + CreateDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath); + + CreateDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemCreateDirectoryParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; }; } // namespace dom diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 871bf209d372..6a0bfd12d553 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -25,34 +25,104 @@ namespace dom { uint32_t CreateFileTask::sOutputBufferSize = 0; -CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - Blob* aBlobData, - InfallibleTArray& aArrayData, - bool replace, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aPath) - , mReplace(replace) +/* static */ already_AddRefed +CreateFileTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Blob* aBlobData, + InfallibleTArray& aArrayData, + bool aReplace, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - GetOutputBufferSize(); + + RefPtr task = + new CreateFileTask(aFileSystem, aTargetPath, aReplace); + + // aTargetPath can be null. In this case SetError will be called. + + task->GetOutputBufferSize(); + if (aBlobData) { if (XRE_IsParentProcess()) { - aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv); - NS_WARN_IF(aRv.Failed()); + aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } } else { - mBlobData = aBlobData; + task->mBlobData = aBlobData; } } - mArrayData.SwapElements(aArrayData); + + task->mArrayData.SwapElements(aArrayData); + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +CreateFileTask::Create(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new CreateFileTask(aFileSystem, aParam, aParent); + + task->GetOutputBufferSize(); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mReplace = aParam.replace(); + + auto& data = aParam.data(); + + if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { + task->mArrayData = data; + return task.forget(); + } + + BlobParent* bp = static_cast(static_cast(data)); + RefPtr blobImpl = bp->GetBlobImpl(); + MOZ_ASSERT(blobImpl, "blobData should not be null."); + + ErrorResult rv; + blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + + return task.forget(); +} + +CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + bool aReplace) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mReplace(aReplace) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); } CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, @@ -61,32 +131,9 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, : FileSystemTaskBase(aFileSystem, aParam, aParent) , mReplace(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - GetOutputBufferSize(); - - mTargetRealPath = aParam.realPath(); - - mReplace = aParam.replace(); - - auto& data = aParam.data(); - - if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) { - mArrayData = data; - return; - } - - BlobParent* bp = static_cast(static_cast(data)); - RefPtr blobImpl = bp->GetBlobImpl(); - MOZ_ASSERT(blobImpl, "blobData should not be null."); - - ErrorResult rv; - blobImpl->GetInternalStream(getter_AddRefs(mBlobStream), rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - } } CreateFileTask::~CreateFileTask() @@ -107,12 +154,18 @@ CreateFileTask::GetPromise() } FileSystemParams -CreateFileTask::GetRequestParams(const nsString& aFileSystem) const +CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemCreateFileParams param; - param.filesystem() = aFileSystem; - param.realPath() = mTargetRealPath; + param.filesystem() = aSerializedDOMPath; + + aRv = mTargetPath->GetPath(param.realPath()); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + param.replace() = mReplace; if (mBlobData) { BlobChild* actor @@ -127,7 +180,7 @@ CreateFileTask::GetRequestParams(const nsString& aFileSystem) const } FileSystemResponseValue -CreateFileTask::GetSuccessRequestResult() const +CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); BlobParent* actor = GetBlobParent(mTargetBlobImpl); @@ -140,7 +193,8 @@ CreateFileTask::GetSuccessRequestResult() const } void -CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemFileResponse r = aValue; @@ -176,24 +230,19 @@ CreateFileTask::Work() return NS_ERROR_FAILURE; } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - - if (!mFileSystem->IsSafeFile(file)) { + if (!mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } bool exists = false; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (exists) { bool isFile = false; - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -207,19 +256,19 @@ CreateFileTask::Work() } // Remove the old file before creating. - rv = file->Remove(false); + rv = mTargetPath->Remove(false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr outputStream; - rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file); + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -262,7 +311,7 @@ CreateFileTask::Work() return NS_ERROR_FAILURE; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -281,7 +330,7 @@ CreateFileTask::Work() return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } diff --git a/dom/filesystem/CreateFileTask.h b/dom/filesystem/CreateFileTask.h index 7adf49f14f58..fc0ba14ceb0b 100644 --- a/dom/filesystem/CreateFileTask.h +++ b/dom/filesystem/CreateFileTask.h @@ -20,19 +20,22 @@ class Blob; class BlobImpl; class Promise; -class CreateFileTask final - : public FileSystemTaskBase +class CreateFileTask final : public FileSystemTaskBase { public: - CreateFileTask(FileSystemBase* aFileSystem, - const nsAString& aPath, - Blob* aBlobData, - InfallibleTArray& aArrayData, - bool replace, - ErrorResult& aRv); - CreateFileTask(FileSystemBase* aFileSystem, - const FileSystemCreateFileParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aFile, + Blob* aBlobData, + InfallibleTArray& aArrayData, + bool replace, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~CreateFileTask(); @@ -45,13 +48,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -60,12 +65,20 @@ protected: HandlerCallback() override; private: + CreateFileTask(FileSystemBase* aFileSystem, + nsIFile* aFile, + bool aReplace); + + CreateFileTask(FileSystemBase* aFileSystem, + const FileSystemCreateFileParams& aParam, + FileSystemRequestParent* aParent); + void GetOutputBufferSize() const; static uint32_t sOutputBufferSize; RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; // Not thread-safe and should be released on main thread. RefPtr mBlobData; diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 86a86351c4b9..4d6053489cfd 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -46,17 +46,17 @@ DeviceStorageFileSystem::DeviceStorageFileSystem( NS_WARN_IF(NS_FAILED(rv)); // Get the local path of the file system root. - // Since the child process is not allowed to access the file system, we only - // do this from the parent process. - if (!XRE_IsParentProcess()) { - return; - } nsCOMPtr rootFile; DeviceStorageFile::GetRootDirectoryForType(aStorageType, aStorageName, getter_AddRefs(rootFile)); NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath))); + + if (!XRE_IsParentProcess()) { + return; + } + FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath, mNormalizedLocalRootPath); @@ -132,8 +132,14 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aDir); - RefPtr fs = aDir->GetFileSystem(); - MOZ_ASSERT(fs); + + ErrorResult rv; + RefPtr fs = aDir->GetFileSystem(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + // Check if the given directory is from this storage. return fs->ToString() == mString; } diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index 999e39302005..a3ff29b14e6d 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" +#include "mozilla/dom/OSFileSystem.h" // Resolve the name collision of Microsoft's API name with macros defined in // Windows header files. Undefine the macro of CreateDirectory to avoid @@ -37,12 +38,19 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(Directory) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory) - tmp->mFileSystem->Unlink(); + if (tmp->mFileSystem) { + tmp->mFileSystem->Unlink(); + tmp->mFileSystem = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory) - tmp->mFileSystem->Traverse(cb); + if (tmp->mFileSystem) { + tmp->mFileSystem->Traverse(cb); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -59,23 +67,57 @@ NS_INTERFACE_MAP_END already_AddRefed Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) { - RefPtr task = new GetFileOrDirectoryTask( - aFileSystem, EmptyString(), true, aRv); - if (aRv.Failed()) { + MOZ_ASSERT(aFileSystem); + + nsCOMPtr path; + aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()), + true, getter_AddRefs(path)); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + GetFileOrDirectoryTask::Create(aFileSystem, path, eDOMRootDirectory, true, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); } -Directory::Directory(FileSystemBase* aFileSystem, - const nsAString& aPath) - : mFileSystem(aFileSystem) - , mPath(aPath) +/* static */ already_AddRefed +Directory::Create(nsPIDOMWindowInner* aWindow, nsIFile* aFile, + DirectoryType aType, FileSystemBase* aFileSystem) { - MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); - // Remove the trailing "/". - mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aFile); + +#ifdef DEBUG + bool isDir; + nsresult rv = aFile->IsDirectory(&isDir); + MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir); +#endif + + RefPtr directory = + new Directory(aWindow, aFile, aType, aFileSystem); + return directory.forget(); +} + +Directory::Directory(nsPIDOMWindowInner* aWindow, + nsIFile* aFile, + DirectoryType aType, + FileSystemBase* aFileSystem) + : mWindow(aWindow) + , mFileSystem(aFileSystem) + , mFile(aFile) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFile); + + // aFileSystem can be null. In this case we create a OSFileSystem when needed. } Directory::~Directory() @@ -85,7 +127,7 @@ Directory::~Directory() nsPIDOMWindowInner* Directory::GetParentObject() const { - return mFileSystem->GetWindow(); + return mWindow; } JSObject* @@ -95,25 +137,28 @@ Directory::WrapObject(JSContext* aCx, JS::Handle aGivenProto) } void -Directory::GetName(nsAString& aRetval) const +Directory::GetName(nsAString& aRetval, ErrorResult& aRv) { aRetval.Truncate(); - if (mPath.IsEmpty()) { - mFileSystem->GetRootName(aRetval); + if (mType == eDOMRootDirectory) { + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + fs->GetRootName(aRetval); return; } - aRetval = Substring(mPath, - mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1); + aRv = mFile->GetLeafName(aRetval); + NS_WARN_IF(aRv.Failed()); } already_AddRefed Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; RefPtr blobData; InfallibleTArray arrayData; bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace); @@ -138,15 +183,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, } } - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } RefPtr task = - new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv); - if (aRv.Failed()) { + CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -155,16 +205,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, already_AddRefed Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - RefPtr task = new CreateDirectoryTask( - mFileSystem, realPath, aRv); - if (aRv.Failed()) { + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + CreateDirectoryTask::Create(fs, realPath, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -173,16 +227,21 @@ Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv) already_AddRefed Directory::Get(const nsAString& aPath, ErrorResult& aRv) { - nsresult error = NS_OK; - nsAutoString realPath; - if (!DOMPathToRealPath(aPath, realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - RefPtr task = new GetFileOrDirectoryTask( - mFileSystem, realPath, false, aRv); - if (aRv.Failed()) { + nsCOMPtr realPath; + nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath)); + + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + + RefPtr task = + GetFileOrDirectoryTask::Create(fs, realPath, eNotDOMRootDirectory, false, + aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + task->SetError(error); FileSystemPermissionRequest::RequestForTask(task); return task->GetPromise(); @@ -205,30 +264,33 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, ErrorResult& aRv) { nsresult error = NS_OK; - nsAutoString realPath; + nsCOMPtr realPath; RefPtr blob; // Check and get the target path. + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + if (aPath.IsFile()) { blob = aPath.GetAsFile().Impl(); } else if (aPath.IsString()) { - if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) { - error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - } else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) { + error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath)); + } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) { error = NS_ERROR_DOM_SECURITY_ERR; } else { - realPath = aPath.GetAsDirectory().mPath; + realPath = aPath.GetAsDirectory().mFile; // The target must be a descendant of this directory. - if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) { + if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) { error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; } } - RefPtr task = new RemoveTask(mFileSystem, mPath, blob, realPath, - aRecursive, aRv); - if (aRv.Failed()) { + RefPtr task = + RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } task->SetError(error); @@ -237,23 +299,38 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, } void -Directory::GetPath(nsAString& aRetval) const +Directory::GetPath(nsAString& aRetval, ErrorResult& aRv) { - if (mPath.IsEmpty()) { - // The Directory ctor removes any trailing '/'; this is the root directory. + if (mType == eDOMRootDirectory) { aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); } else { - aRetval = mPath; + // TODO: this should be a bit different... + GetName(aRetval, aRv); } } -already_AddRefed -Directory::GetFilesAndDirectories() +nsresult +Directory::GetFullRealPath(nsAString& aPath) { - ErrorResult rv; + nsresult rv = mFile->GetPath(aPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +already_AddRefed +Directory::GetFilesAndDirectories(ErrorResult& aRv) +{ + RefPtr fs = GetFileSystem(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + RefPtr task = - new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv); - if (NS_WARN_IF(rv.Failed())) { + GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -268,16 +345,38 @@ Directory::SetContentFilters(const nsAString& aFilters) } FileSystemBase* -Directory::GetFileSystem() const +Directory::GetFileSystem(ErrorResult& aRv) { - return mFileSystem.get(); + if (!mFileSystem) { + nsCOMPtr parent; + aRv = mFile->GetParent(getter_AddRefs(parent)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + // Parent can be null if mFile is pointing to the top directory. + if (!parent) { + parent = mFile; + } + + nsAutoString path; + aRv = parent->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr fs = new OSFileSystem(path); + fs->Init(mWindow); + + mFileSystem = fs; + } + + return mFileSystem; } -bool -Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const +nsresult +Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const { - aRealPath.Truncate(); - nsString relativePath; relativePath = aPath; @@ -286,13 +385,22 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const relativePath.Trim(kWhitespace); if (!IsValidRelativePath(relativePath)) { - return false; + return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; } - aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) + - relativePath; + nsCOMPtr file; + nsresult rv = mFile->Clone(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - return true; + rv = file->AppendRelativePath(relativePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + file.forget(aFile); + return NS_OK; } // static diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index f9c6e641294f..3d8a420723e5 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -41,14 +41,37 @@ class Directory final , public nsWrapperCache { public: + struct BlobImplOrDirectoryPath + { + RefPtr mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory) -public: static already_AddRefed GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv); - Directory(FileSystemBase* aFileSystem, const nsAString& aPath); + enum DirectoryType { + // When a directory is selected using a HTMLInputElement, that will be the + // DOM root directory and its name will be '/'. All the sub directory will + // be called with they real name. We use this enum to mark what we must + // consider the '/' of this DOM filesystem. + eDOMRootDirectory, + + // All the sub directories of the '/' will be marked using this other value. + eNotDOMRootDirectory + }; + + static already_AddRefed + Create(nsPIDOMWindowInner* aWindow, nsIFile* aDirectory, + DirectoryType aType, FileSystemBase* aFileSystem = 0); // ========= Begin WebIDL bindings. =========== @@ -59,7 +82,7 @@ public: WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; void - GetName(nsAString& aRetval) const; + GetName(nsAString& aRetval, ErrorResult& aRv); already_AddRefed CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions, @@ -80,10 +103,13 @@ public: // From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface : void - GetPath(nsAString& aRetval) const; + GetPath(nsAString& aRetval, ErrorResult& aRv); + + nsresult + GetFullRealPath(nsAString& aPath); already_AddRefed - GetFilesAndDirectories(); + GetFilesAndDirectories(ErrorResult& aRv); // =========== End WebIDL bindings.============ @@ -113,8 +139,12 @@ public: SetContentFilters(const nsAString& aFilters); FileSystemBase* - GetFileSystem() const; + GetFileSystem(ErrorResult& aRv); + private: + Directory(nsPIDOMWindowInner* aWindow, + nsIFile* aFile, DirectoryType aType, + FileSystemBase* aFileSystem = nullptr); ~Directory(); static bool @@ -122,17 +152,19 @@ private: /* * Convert relative DOM path to the absolute real path. - * @return true if succeed. false if the DOM path is invalid. */ - bool - DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const; + nsresult + DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const; already_AddRefed RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, ErrorResult& aRv); + nsCOMPtr mWindow; RefPtr mFileSystem; - nsString mPath; + nsCOMPtr mFile; + DirectoryType mType; + nsString mFilters; }; diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index e62706b62b74..3f0046525f76 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -68,26 +68,41 @@ FileSystemBase::GetLocalFile(const nsAString& aRealPath) const { MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); + + // Let's convert the input path to /path nsAutoString localPath; - FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath); - localPath = mLocalRootPath + localPath; + if (!aRealPath.IsEmpty() && + !StringBeginsWith(aRealPath, + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR))) { + localPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); + } + localPath.Append(aRealPath); + + // We have to normalize the path string in order to follow the separator + // schema of this OS. + nsAutoString normalizedPath; + FileSystemUtils::NormalizedPathToLocalPath(localPath, normalizedPath); + + // The full path is mLocalRootPath + normalizedPath. + nsAutoString path(mLocalRootPath); + path.Append(normalizedPath); + nsCOMPtr file; - nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file)); + nsresult rv = NS_NewLocalFile(path, false, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } + return file.forget(); } bool -FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const +FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const { MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); MOZ_ASSERT(aFile, "aFile Should not be null."); - aRealPath.Truncate(); - nsAutoString filePath; ErrorResult rv; aFile->GetMozFullPathInternal(filePath, rv); @@ -95,7 +110,13 @@ FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const return false; } - return LocalPathToRealPath(filePath, aRealPath); + rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath), + true, aPath); + if (NS_WARN_IF(rv.Failed())) { + return false; + } + + return true; } bool @@ -120,6 +141,7 @@ FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath, aRealPath.Truncate(); return false; } + aRealPath = Substring(path, mNormalizedLocalRootPath.Length()); return true; } diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index e48380663939..5437e2357be3 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -73,13 +73,8 @@ public: virtual bool IsSafeDirectory(Directory* aDir) const; - /* - * Get the real path (absolute DOM path) of the DOM file in the file system. - * If succeeded, returns true. Otherwise, returns false and set aRealPath to - * empty string. - */ bool - GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const; + GetRealPath(BlobImpl* aFile, nsIFile** aPath) const; /* * Get the permission name required to access this file system. diff --git a/dom/filesystem/FileSystemRequestParent.cpp b/dom/filesystem/FileSystemRequestParent.cpp index b43c7bb1c012..c730a0dd4e93 100644 --- a/dom/filesystem/FileSystemRequestParent.cpp +++ b/dom/filesystem/FileSystemRequestParent.cpp @@ -29,7 +29,10 @@ FileSystemRequestParent::~FileSystemRequestParent() case FileSystemParams::TFileSystem##name##Params: { \ const FileSystem##name##Params& p = aParams; \ mFileSystem = FileSystemBase::FromString(p.filesystem()); \ - task = new name##Task(mFileSystem, p, this); \ + task = name##Task::Create(mFileSystem, p, this, rv); \ + if (NS_WARN_IF(rv.Failed())) { \ + return false; \ + } \ break; \ } @@ -39,6 +42,8 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent, { MOZ_ASSERT(aParent, "aParent should not be null."); RefPtr task; + ErrorResult rv; + switch (aParams.type()) { FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory) diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index e77cf251262a..b1bfdc6abc36 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -106,12 +106,19 @@ FileSystemTaskBase::Start() return; } + ErrorResult rv; + FileSystemParams params = GetRequestParams(mFileSystem->ToString(), rv); + if (NS_WARN_IF(rv.Failed())) { + return; + } + // Retain a reference so the task object isn't deleted without IPDL's // knowledge. The reference will be released by // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild. NS_ADDREF_THIS(); + ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this, - GetRequestParams(mFileSystem->ToString())); + params); } NS_IMETHODIMP @@ -154,11 +161,17 @@ FileSystemTaskBase::GetRequestResult() const MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - if (HasError()) { - return FileSystemErrorResponse(mErrorValue); - } else { - return GetSuccessRequestResult(); + if (!HasError()) { + ErrorResult rv; + FileSystemResponseValue value = GetSuccessRequestResult(rv); + if (NS_WARN_IF(rv.Failed())) { + return FileSystemErrorResponse(rv.StealNSResult()); + } + + return value; } + + return FileSystemErrorResponse(mErrorValue); } void @@ -171,7 +184,9 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue) FileSystemErrorResponse r = aValue; mErrorValue = r.error(); } else { - SetSuccessRequestResult(aValue); + ErrorResult rv; + SetSuccessRequestResult(aValue, rv); + mErrorValue = rv.StealNSResult(); } } diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index 43dcceb93056..3da16d9b251d 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -176,7 +176,8 @@ protected: * @param filesystem The string representation of the file system. */ virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const = 0; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const = 0; /* * Wrap the task success result to FileSystemResponseValue for sending it @@ -185,7 +186,7 @@ protected: * send the task success result back to the child process. */ virtual FileSystemResponseValue - GetSuccessRequestResult() const = 0; + GetSuccessRequestResult(ErrorResult& aRv) const = 0; /* * Unwrap the IPC message to get the task success result. @@ -194,7 +195,8 @@ protected: * success result. */ virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) = 0; bool HasError() const { return mErrorValue != NS_OK; } diff --git a/dom/filesystem/FileSystemUtils.cpp b/dom/filesystem/FileSystemUtils.cpp index cf175bcaa24c..1e62e4c56bce 100644 --- a/dom/filesystem/FileSystemUtils.cpp +++ b/dom/filesystem/FileSystemUtils.cpp @@ -65,5 +65,31 @@ FileSystemUtils::IsDescendantPath(const nsAString& aPath, return true; } +// static +bool +FileSystemUtils::IsDescendantPath(nsIFile* aFile, + nsIFile* aDescendantFile) +{ + nsAutoString path; + nsresult rv = aFile->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoString descendantPath; + rv = aDescendantFile->GetPath(descendantPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + // Check the sub-directory path to see if it has the parent path as prefix. + if (descendantPath.Length() <= path.Length() || + !StringBeginsWith(descendantPath, path)) { + return false; + } + + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/FileSystemUtils.h b/dom/filesystem/FileSystemUtils.h index abf30c3cf5f1..cadead05abea 100644 --- a/dom/filesystem/FileSystemUtils.h +++ b/dom/filesystem/FileSystemUtils.h @@ -34,6 +34,12 @@ public: static void NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal); + /* + * Return true if aDescendantPath is a descendant of aPath. + */ + static bool + IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath); + /* * Return true if aDescendantPath is a descendant of aPath. Both aPath and * aDescendantPath are absolute DOM path. diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 7a450753a3b0..153691506d93 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -8,7 +8,6 @@ #include "HTMLSplitOnSpacesTokenizer.h" #include "js/Value.h" -#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" @@ -21,33 +20,80 @@ namespace mozilla { namespace dom { -GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - const nsAString& aFilters, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aTargetPath) - , mFilters(aFilters) +/* static */ already_AddRefed +GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetDirectoryListingTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mType = aParam.isRoot() + ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + return task.forget(); +} + +GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mFilters(aFilters) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); } GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, const FileSystemGetDirectoryListingParams& aParam, FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) - , mTargetRealPath(aParam.realPath()) , mFilters(aParam.filters()) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); } @@ -66,43 +112,88 @@ GetDirectoryListingTask::GetPromise() } FileSystemParams -GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const +GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath, + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemGetDirectoryListingParams(); + } + + return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path, + mType == Directory::eDOMRootDirectory, mFilters); } +void +GetDirectoryListingTask::CreateNormalizedRelativePath(const nsAString& aPath, + nsAString& aRelativePath) const +{ + uint32_t rootPathLen = mFileSystem->GetLocalRootPath().Length(); + FileSystemUtils::LocalPathToNormalizedPath( + Substring(aPath, rootPathLen, aPath.Length() - rootPathLen), aRelativePath); +} + FileSystemResponseValue -GetDirectoryListingTask::GetSuccessRequestResult() const +GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); InfallibleTArray blobs; - for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) { - BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]); - if (blobParent) { - blobs.AppendElement(blobParent); + nsTArray inputs; + + for (unsigned i = 0; i < mTargetData.Length(); i++) { + if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { + BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl); + if (!blobParent) { + continue; + } + + FileSystemDirectoryListingResponseBlob blobData; + blobData.blobParent() = blobParent; + inputs.AppendElement(blobData); + } else { + MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + FileSystemDirectoryListingResponseDirectory directoryData; + directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath; + inputs.AppendElement(directoryData); } } + FileSystemDirectoryListingResponse response; - response.blobsParent().SwapElements(blobs); + response.data().SwapElements(inputs); return response; } void -GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aValue.type() == FileSystemResponseValue::TFileSystemDirectoryListingResponse); FileSystemDirectoryListingResponse r = aValue; - nsTArray& blobs = r.blobsChild(); + for (uint32_t i = 0; i < r.data().Length(); ++i) { + const FileSystemDirectoryListingResponseData& data = r.data()[i]; - for (unsigned i = 0; i < blobs.Length(); i++) { - mTargetBlobImpls.AppendElement(static_cast(blobs[i])->GetBlobImpl()); + if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) { + PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild(); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + element->mBlobImpl = static_cast(blob)->GetBlobImpl(); + } else { + MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + element->mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath(); + } } } @@ -117,27 +208,19 @@ GetDirectoryListingTask::Work() return NS_ERROR_FAILURE; } - // Whether we want to get the root directory. - bool getRoot = mTargetRealPath.IsEmpty(); - - nsCOMPtr dir = mFileSystem->GetLocalFile(mTargetRealPath); - if (!dir) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists; - nsresult rv = dir->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { - if (!getRoot) { + if (mType == Directory::eNotRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } // If the root directory doesn't exit, create it. - rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777); + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -145,7 +228,7 @@ GetDirectoryListingTask::Work() // Get isDirectory. bool isDir; - rv = dir->IsDirectory(&isDir); + rv = mTargetPath->IsDirectory(&isDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -155,7 +238,7 @@ GetDirectoryListingTask::Work() } nsCOMPtr entries; - rv = dir->GetDirectoryEntries(getter_AddRefs(entries)); + rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -186,7 +269,7 @@ GetDirectoryListingTask::Work() nsCOMPtr currFile = do_QueryInterface(supp); MOZ_ASSERT(currFile); - + bool isLink, isSpecial, isFile; if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) || NS_FAILED(currFile->IsSpecial(&isSpecial))) || @@ -213,9 +296,22 @@ GetDirectoryListingTask::Work() } } - BlobImplFile* impl = new BlobImplFile(currFile); - impl->LookupAndCacheIsDirectory(); - mTargetBlobImpls.AppendElement(impl); + if (isDir) { + nsAutoString path; + if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) { + continue; + } + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + element->mDirectoryPath = path; + } else { + BlobImplFile* impl = new BlobImplFile(currFile); + + Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement(); + element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + element->mBlobImpl = impl; + } } return NS_OK; } @@ -235,7 +331,7 @@ GetDirectoryListingTask::HandlerCallback() return; } - size_t count = mTargetBlobImpls.Length(); + size_t count = mTargetData.Length(); Sequence listing; @@ -246,26 +342,65 @@ GetDirectoryListingTask::HandlerCallback() } for (unsigned i = 0; i < count; i++) { - if (mTargetBlobImpls[i]->IsDirectory()) { - nsAutoString name; - mTargetBlobImpls[i]->GetName(name); - nsAutoString path(mTargetRealPath); - path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); - path.Append(name); + if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) { #ifdef DEBUG if (XRE_IsParentProcess()) { - nsCOMPtr file = mFileSystem->GetLocalFile(path); + nsCOMPtr file; + nsresult rv = NS_NewLocalFile(mTargetData[i].mDirectoryPath, false, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + bool exist; - file->Exists(&exist); - MOZ_ASSERT(exist); + rv = file->Exists(&exist); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + + // We cannot assert here because the file can be done in the meantime. + NS_ASSERTION(exist, "The file doesn't exist anymore?!?"); + + nsAutoString normalizedLocalRootPath; + FileSystemUtils::NormalizedPathToLocalPath(mFileSystem->GetLocalRootPath(), + normalizedLocalRootPath); + + nsAutoString directoryPath; + FileSystemUtils::NormalizedPathToLocalPath(mTargetData[i].mDirectoryPath, + directoryPath); + + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(normalizedLocalRootPath, + directoryPath)); } #endif - RefPtr directory = new Directory(mFileSystem, path); + + nsCOMPtr directoryPath; + NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath); + nsresult rv = NS_NewNativeLocalFile(path, true, + getter_AddRefs(directoryPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + + RefPtr directory = Directory::Create(mFileSystem->GetWindow(), + directoryPath, + Directory::eNotRootDirectory, + mFileSystem); + MOZ_ASSERT(directory); + // Propogate mFilter onto sub-Directory object: directory->SetContentFilters(mFilters); listing[i].SetAsDirectory() = directory; } else { - listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]); + MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl); + listing[i].SetAsFile() = + File::Create(mFileSystem->GetWindow(), mTargetData[i].mBlobImpl); } } diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index 4a6dc7b045ab..ddf774e96350 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_GetDirectoryListing_h #define mozilla_dom_GetDirectoryListing_h +#include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemTaskBase.h" #include "mozilla/ErrorResult.h" #include "nsAutoPtr.h" @@ -16,18 +17,21 @@ namespace dom { class BlobImpl; -class GetDirectoryListingTask final - : public FileSystemTaskBase +class GetDirectoryListingTask final : public FileSystemTaskBase { public: - // If aDirectoryOnly is set, we should ensure that the target is a directory. - GetDirectoryListingTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - const nsAString& aFilters, - ErrorResult& aRv); - GetDirectoryListingTask(FileSystemBase* aFileSystem, - const FileSystemGetDirectoryListingParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~GetDirectoryListingTask(); @@ -37,15 +41,28 @@ public: virtual void GetPermissionAccessType(nsCString& aAccess) const override; -protected: + +private: + // If aDirectoryOnly is set, we should ensure that the target is a directory. + GetDirectoryListingTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + const nsAString& aFilters); + + GetDirectoryListingTask(FileSystemBase* aFileSystem, + const FileSystemGetDirectoryListingParams& aParam, + FileSystemRequestParent* aParent); + virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -53,14 +70,18 @@ protected: virtual void HandlerCallback() override; -private: + void + CreateNormalizedRelativePath(const nsAString& aPath, + nsAString& aRelativePath) const; + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; nsString mFilters; + Directory::DirectoryType mType; // We cannot store File or Directory objects bacause this object is created // on a different thread and File and Directory are not thread-safe. - nsTArray> mTargetBlobImpls; + nsTArray mTargetData; }; } // namespace dom diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 5d210243fcd7..4cbc443e5038 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -7,7 +7,6 @@ #include "GetFileOrDirectoryTask.h" #include "js/Value.h" -#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" @@ -20,37 +19,82 @@ namespace mozilla { namespace dom { -GetFileOrDirectoryTask::GetFileOrDirectoryTask( - FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - bool aDirectoryOnly, - ErrorResult& aRv) - : FileSystemTaskBase(aFileSystem) - , mTargetRealPath(aTargetPath) - , mIsDirectory(aDirectoryOnly) +/* static */ already_AddRefed +GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly); + + // aTargetPath can be null. In this case SetError will be called. + nsCOMPtr globalObject = do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; } - mPromise = Promise::Create(globalObject, aRv); + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); } -GetFileOrDirectoryTask::GetFileOrDirectoryTask( - FileSystemBase* aFileSystem, - const FileSystemGetFileOrDirectoryParams& aParam, - FileSystemRequestParent* aParent) +/* static */ already_AddRefed +GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new GetFileOrDirectoryTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 path(aParam.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mType = aParam.isRoot() + ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + return task.forget(); +} + +GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly) + : FileSystemTaskBase(aFileSystem) + , mTargetPath(aTargetPath) + , mIsDirectory(aDirectoryOnly) + , mType(aType) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); +} + +GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) , mIsDirectory(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - mTargetRealPath = aParam.realPath(); } GetFileOrDirectoryTask::~GetFileOrDirectoryTask() @@ -67,18 +111,33 @@ GetFileOrDirectoryTask::GetPromise() } FileSystemParams -GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const +GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath); + + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemGetFileOrDirectoryParams(); + } + + return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path, + mType == Directory::eDOMRootDirectory); } FileSystemResponseValue -GetFileOrDirectoryTask::GetSuccessRequestResult() const +GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (mIsDirectory) { - return FileSystemDirectoryResponse(mTargetRealPath); + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return FileSystemDirectoryResponse(); + } + + return FileSystemDirectoryResponse(path); } BlobParent* actor = GetBlobParent(mTargetBlobImpl); @@ -91,7 +150,8 @@ GetFileOrDirectoryTask::GetSuccessRequestResult() const } void -GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); switch (aValue.type()) { @@ -104,7 +164,13 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a } case FileSystemResponseValue::TFileSystemDirectoryResponse: { FileSystemDirectoryResponse r = aValue; - mTargetRealPath = r.realPath(); + + NS_ConvertUTF16toUTF8 path(r.realPath()); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + mIsDirectory = true; break; } @@ -127,33 +193,26 @@ GetFileOrDirectoryTask::Work() } // Whether we want to get the root directory. - bool getRoot = mTargetRealPath.IsEmpty(); - - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { - if (!getRoot) { + if (mType == Directory::eNotRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } // If the root directory doesn't exit, create it. - rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777); + rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } // Get isDirectory. - rv = file->IsDirectory(&mIsDirectory); + rv = mTargetPath->IsDirectory(&mIsDirectory); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -163,13 +222,13 @@ GetFileOrDirectoryTask::Work() } // Check if the root is a directory. - if (getRoot) { + if (mType == Directory::eDOMRootDirectory) { return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; } bool isFile; // Get isFile - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -179,11 +238,11 @@ GetFileOrDirectoryTask::Work() return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR; } - if (!mFileSystem->IsSafeFile(file)) { + if (!mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } - mTargetBlobImpl = new BlobImplFile(file); + mTargetBlobImpl = new BlobImplFile(mTargetPath); return NS_OK; } @@ -204,7 +263,12 @@ GetFileOrDirectoryTask::HandlerCallback() } if (mIsDirectory) { - RefPtr dir = new Directory(mFileSystem, mTargetRealPath); + RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + mTargetPath, + mType, + mFileSystem); + MOZ_ASSERT(dir); + mPromise->MaybeResolve(dir); mPromise = nullptr; return; diff --git a/dom/filesystem/GetFileOrDirectoryTask.h b/dom/filesystem/GetFileOrDirectoryTask.h index 3d76bdca980b..4430e567d7e5 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.h +++ b/dom/filesystem/GetFileOrDirectoryTask.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_GetFileOrDirectory_h #define mozilla_dom_GetFileOrDirectory_h +#include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemTaskBase.h" #include "nsAutoPtr.h" #include "mozilla/ErrorResult.h" @@ -16,18 +17,21 @@ namespace dom { class BlobImpl; -class GetFileOrDirectoryTask final - : public FileSystemTaskBase +class GetFileOrDirectoryTask final : public FileSystemTaskBase { public: - // If aDirectoryOnly is set, we should ensure that the target is a directory. - GetFileOrDirectoryTask(FileSystemBase* aFileSystem, - const nsAString& aTargetPath, - bool aDirectoryOnly, - ErrorResult& aRv); - GetFileOrDirectoryTask(FileSystemBase* aFileSystem, - const FileSystemGetFileOrDirectoryParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~GetFileOrDirectoryTask(); @@ -39,13 +43,15 @@ public: GetPermissionAccessType(nsCString& aAccess) const override; protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -54,10 +60,22 @@ protected: HandlerCallback() override; private: + // If aDirectoryOnly is set, we should ensure that the target is a directory. + GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + nsIFile* aTargetPath, + Directory::DirectoryType aType, + bool aDirectoryOnly); + + GetFileOrDirectoryTask(FileSystemBase* aFileSystem, + const FileSystemGetFileOrDirectoryParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; + // Whether we get a directory. bool mIsDirectory; + Directory::DirectoryType mType; // This cannot be a File bacause this object is created on a different // thread and File is not thread-safe. Let's use the BlobImpl instead. diff --git a/dom/filesystem/PFileSystemRequest.ipdl b/dom/filesystem/PFileSystemRequest.ipdl index 9e9a7c71c0ca..6d2e934930e1 100644 --- a/dom/filesystem/PFileSystemRequest.ipdl +++ b/dom/filesystem/PFileSystemRequest.ipdl @@ -20,9 +20,26 @@ struct FileSystemDirectoryResponse nsString realPath; }; +struct FileSystemDirectoryListingResponseBlob +{ + PBlob blob; +}; + +struct FileSystemDirectoryListingResponseDirectory +{ + // This is the full real path for the directory that we are sending via IPC. + nsString directoryRealPath; +}; + +union FileSystemDirectoryListingResponseData +{ + FileSystemDirectoryListingResponseBlob; + FileSystemDirectoryListingResponseDirectory; +}; + struct FileSystemDirectoryListingResponse { - PBlob[] blobs; + FileSystemDirectoryListingResponseData[] data; }; struct FileSystemErrorResponse diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 1e3df8f7bd28..8b806b72eee6 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -18,27 +18,94 @@ namespace mozilla { namespace dom { +/* static */ already_AddRefed +RemoveTask::Create(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + MOZ_ASSERT(aDirPath); + + RefPtr task = + new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive); + + // aTargetPath can be null. In this case SetError will be called. + + nsCOMPtr globalObject = + do_QueryInterface(aFileSystem->GetWindow()); + if (NS_WARN_IF(!globalObject)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + task->mPromise = Promise::Create(globalObject, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); +} + +/* static */ already_AddRefed +RemoveTask::Create(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv) +{ + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); + MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); + MOZ_ASSERT(aFileSystem); + + RefPtr task = + new RemoveTask(aFileSystem, aParam, aParent); + + NS_ConvertUTF16toUTF8 directoryPath(aParam.directory()); + aRv = NS_NewNativeLocalFile(directoryPath, true, + getter_AddRefs(task->mDirPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + task->mRecursive = aParam.recursive(); + + const FileSystemPathOrFileValue& target = aParam.target(); + + if (target.type() == FileSystemPathOrFileValue::TnsString) { + NS_ConvertUTF16toUTF8 path(target); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return task.forget(); + } + + BlobParent* bp = static_cast(static_cast(target)); + task->mTargetBlobImpl = bp->GetBlobImpl(); + MOZ_ASSERT(task->mTargetBlobImpl); + + return task.forget(); +} + RemoveTask::RemoveTask(FileSystemBase* aFileSystem, - const nsAString& aDirPath, + nsIFile* aDirPath, BlobImpl* aTargetBlob, - const nsAString& aTargetPath, - bool aRecursive, - ErrorResult& aRv) + nsIFile* aTargetPath, + bool aRecursive) : FileSystemTaskBase(aFileSystem) - , mDirRealPath(aDirPath) + , mDirPath(aDirPath) , mTargetBlobImpl(aTargetBlob) - , mTargetRealPath(aTargetPath) + , mTargetPath(aTargetPath) , mRecursive(aRecursive) , mReturnValue(false) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); - if (!globalObject) { - return; - } - mPromise = Promise::Create(globalObject, aRv); + MOZ_ASSERT(aDirPath); } RemoveTask::RemoveTask(FileSystemBase* aFileSystem, @@ -48,25 +115,9 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem, , mRecursive(false) , mReturnValue(false) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only call from parent process!"); + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - - mDirRealPath = aParam.directory(); - - mRecursive = aParam.recursive(); - - const FileSystemPathOrFileValue& target = aParam.target(); - - if (target.type() == FileSystemPathOrFileValue::TnsString) { - mTargetRealPath = target; - return; - } - - BlobParent* bp = static_cast(static_cast(target)); - mTargetBlobImpl = bp->GetBlobImpl(); - MOZ_ASSERT(mTargetBlobImpl); } RemoveTask::~RemoveTask() @@ -83,36 +134,49 @@ RemoveTask::GetPromise() } FileSystemParams -RemoveTask::GetRequestParams(const nsString& aFileSystem) const +RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemRemoveParams param; - param.filesystem() = aFileSystem; - param.directory() = mDirRealPath; + param.filesystem() = aSerializedDOMPath; + + aRv = mDirPath->GetPath(param.directory()); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + param.recursive() = mRecursive; if (mTargetBlobImpl) { RefPtr blob = Blob::Create(mFileSystem->GetWindow(), - mTargetBlobImpl); + mTargetBlobImpl); BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob); if (actor) { param.target() = actor; } } else { - param.target() = mTargetRealPath; + nsAutoString path; + aRv = mTargetPath->GetPath(path); + if (NS_WARN_IF(aRv.Failed())) { + return param; + } + + param.target() = path; } return param; } FileSystemResponseValue -RemoveTask::GetSuccessRequestResult() const +RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); return FileSystemBooleanResponse(mReturnValue); } void -RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) +RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); FileSystemBooleanResponse r = aValue; @@ -130,23 +194,19 @@ RemoveTask::Work() return NS_ERROR_FAILURE; } - // Get the DOM path if a File is passed as the target. + // Get the path if a File is passed as the target. if (mTargetBlobImpl) { - if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) { + if (!mFileSystem->GetRealPath(mTargetBlobImpl, + getter_AddRefs(mTargetPath))) { return NS_ERROR_DOM_SECURITY_ERR; } - if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) { + if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) { return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR; } } - nsCOMPtr file = mFileSystem->GetLocalFile(mTargetRealPath); - if (!file) { - return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; - } - bool exists = false; - nsresult rv = file->Exists(&exists); + nsresult rv = mTargetPath->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -157,16 +217,16 @@ RemoveTask::Work() } bool isFile = false; - rv = file->IsFile(&isFile); + rv = mTargetPath->IsFile(&isFile); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (isFile && !mFileSystem->IsSafeFile(file)) { + if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) { return NS_ERROR_DOM_SECURITY_ERR; } - rv = file->Remove(mRecursive); + rv = mTargetPath->Remove(mRecursive); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } diff --git a/dom/filesystem/RemoveTask.h b/dom/filesystem/RemoveTask.h index 0f75d9f9eb3c..d3fbfd1ebbe7 100644 --- a/dom/filesystem/RemoveTask.h +++ b/dom/filesystem/RemoveTask.h @@ -17,19 +17,22 @@ namespace dom { class BlobImpl; class Promise; -class RemoveTask final - : public FileSystemTaskBase +class RemoveTask final : public FileSystemTaskBase { public: - RemoveTask(FileSystemBase* aFileSystem, - const nsAString& aDirPath, - BlobImpl* aTargetBlob, - const nsAString& aTargetPath, - bool aRecursive, - ErrorResult& aRv); - RemoveTask(FileSystemBase* aFileSystem, - const FileSystemRemoveParams& aParam, - FileSystemRequestParent* aParent); + static already_AddRefed + Create(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive, + ErrorResult& aRv); + + static already_AddRefed + Create(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent, + ErrorResult& aRv); virtual ~RemoveTask(); @@ -42,13 +45,15 @@ public: protected: virtual FileSystemParams - GetRequestParams(const nsString& aFileSystem) const override; + GetRequestParams(const nsString& aSerializedDOMPath, + ErrorResult& aRv) const override; virtual FileSystemResponseValue - GetSuccessRequestResult() const override; + GetSuccessRequestResult(ErrorResult& aRv) const override; virtual void - SetSuccessRequestResult(const FileSystemResponseValue& aValue) override; + SetSuccessRequestResult(const FileSystemResponseValue& aValue, + ErrorResult& aRv) override; virtual nsresult Work() override; @@ -57,12 +62,23 @@ protected: HandlerCallback() override; private: + RemoveTask(FileSystemBase* aFileSystem, + nsIFile* aDirPath, + BlobImpl* aTargetBlob, + nsIFile* aTargetPath, + bool aRecursive); + + RemoveTask(FileSystemBase* aFileSystem, + const FileSystemRemoveParams& aParam, + FileSystemRequestParent* aParent); + RefPtr mPromise; - nsString mDirRealPath; + nsCOMPtr mDirPath; + // This cannot be a File because this object will be used on a different // thread and File is not thread-safe. Let's use the BlobImpl instead. RefPtr mTargetBlobImpl; - nsString mTargetRealPath; + nsCOMPtr mTargetPath; bool mRecursive; bool mReturnValue; }; diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index b25da9f3d465..e5d9ba798f97 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -251,16 +251,63 @@ class HTMLInputElementState final : public nsISupports mValue = aValue; } - const nsTArray>& GetBlobImpls() + void + GetFilesOrDirectories(nsPIDOMWindowInner* aWindow, + nsTArray& aResult) const { - return mBlobImpls; + for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) { + if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) { + RefPtr file = + File::Create(aWindow, + mBlobImplsOrDirectoryPaths[i].mBlobImpl); + MOZ_ASSERT(file); + + OwningFileOrDirectory* element = aResult.AppendElement(); + element->SetAsFile() = file; + } else { + MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath); + + nsCOMPtr file; + NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath); + nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + RefPtr directory = Directory::Create(aWindow, file, + Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + OwningFileOrDirectory* element = aResult.AppendElement(); + element->SetAsDirectory() = directory; + } + } } - void SetBlobImpls(const nsTArray>& aFile) + void SetFilesOrDirectories(const nsTArray& aArray) { - mBlobImpls.Clear(); - for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) { - mBlobImpls.AppendElement(aFile[i]->Impl()); + mBlobImplsOrDirectoryPaths.Clear(); + for (uint32_t i = 0; i < aArray.Length(); ++i) { + if (aArray[i].IsFile()) { + Directory::BlobImplOrDirectoryPath* data = + mBlobImplsOrDirectoryPaths.AppendElement(); + + data->mBlobImpl = aArray[i].GetAsFile()->Impl(); + data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl; + } else { + MOZ_ASSERT(aArray[i].IsDirectory()); + nsAutoString fullPath; + nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + Directory::BlobImplOrDirectoryPath* data = + mBlobImplsOrDirectoryPaths.AppendElement(); + + data->mDirectoryPath = fullPath; + data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath; + } } } @@ -274,7 +321,9 @@ class HTMLInputElementState final : public nsISupports ~HTMLInputElementState() {} nsString mValue; - nsTArray> mBlobImpls; + + nsTArray mBlobImplsOrDirectoryPaths; + bool mChecked; bool mCheckedSet; }; @@ -340,33 +389,65 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error) namespace { /** - * This may return nullptr if aDomFile's implementation of + * This may return nullptr if the DOM File's implementation of * File::mozFullPathInternal does not successfully return a non-empty * string that is a valid path. This can happen on Firefox OS, for example, * where the file picker can create Blobs. */ static already_AddRefed -DOMFileToLocalFile(File* aDomFile) +DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData) { nsString path; - ErrorResult rv; - aDomFile->GetMozFullPathInternal(path, rv); - if (rv.Failed() || path.IsEmpty()) { - rv.SuppressException(); - return nullptr; + + if (aData.IsFile()) { + ErrorResult rv; + aData.GetAsFile()->GetMozFullPathInternal(path, rv); + if (rv.Failed() || path.IsEmpty()) { + rv.SuppressException(); + return nullptr; + } + } else { + MOZ_ASSERT(aData.IsDirectory()); + aData.GetAsDirectory()->GetFullRealPath(path); } nsCOMPtr localFile; - rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, - getter_AddRefs(localFile)); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); + nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, + getter_AddRefs(localFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } return localFile.forget(); } +void +GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData, + nsAString& aName) +{ + if (aData.IsFile()) { + aData.GetAsFile()->GetName(aName); + } else { + MOZ_ASSERT(aData.IsDirectory()); + ErrorResult rv; + aData.GetAsDirectory()->GetName(aName, rv); + NS_WARN_IF(rv.Failed()); + } +} + +void +GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData, + nsAString& aPath, + ErrorResult& aRv) +{ + if (aData.IsFile()) { + aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv); + } else { + MOZ_ASSERT(aData.IsDirectory()); + aData.GetAsDirectory()->GetFullRealPath(aPath); + } +} + } // namespace @@ -383,7 +464,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) mFilePicker->GetMode(&mode); // Collect new selected filenames - nsTArray> newFiles; + nsTArray newFilesOrDirectories; if (mode == static_cast(nsIFilePicker::modeOpenMultiple)) { nsCOMPtr iter; nsresult rv = @@ -402,9 +483,12 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) nsCOMPtr domBlob = do_QueryInterface(tmp); MOZ_ASSERT(domBlob, "Null file object from FilePicker's file enumerator?"); - if (domBlob) { - newFiles.AppendElement(static_cast(domBlob.get())); + if (!domBlob) { + continue; } + + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsFile() = static_cast(domBlob.get()); } } else { MOZ_ASSERT(mode == static_cast(nsIFilePicker::modeOpen) || @@ -416,16 +500,25 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) nsCOMPtr blob = do_QueryInterface(tmp); if (blob) { RefPtr file = static_cast(blob.get())->ToFile(); - newFiles.AppendElement(file); + MOZ_ASSERT(file); + + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; + } else if (tmp) { + RefPtr directory = static_cast(tmp.get()); + OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement(); + element->SetAsDirectory() = directory; } } - if (newFiles.IsEmpty()) { + if (newFilesOrDirectories.IsEmpty()) { return NS_OK; } // Store the last used directory using the content pref service: - nsCOMPtr file = DOMFileToLocalFile(newFiles[0]); + nsCOMPtr file = + DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]); + if (file) { nsCOMPtr lastUsedDir; file->GetParent(getter_AddRefs(lastUsedDir)); @@ -436,7 +529,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult) // The text control frame (if there is one) isn't going to send a change // event because it will think this is done by a script. // So, we can safely send one by ourself. - mInput->SetFiles(newFiles, true); + mInput->SetFilesOrDirectories(newFilesOrDirectories, true); return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(), static_cast(mInput.get()), NS_LITERAL_STRING("change"), true, @@ -672,7 +765,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) // Set default directry and filename nsAutoString defaultName; - const nsTArray>& oldFiles = GetFilesInternal(); + const nsTArray& oldFiles = + GetFilesOrDirectoriesInternal(); nsCOMPtr callback = new HTMLInputElement::nsFilePickerShownCallback(this, filePicker); @@ -681,18 +775,10 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) aType != FILE_PICKER_DIRECTORY) { nsString path; - ErrorResult error; - oldFiles[0]->GetMozFullPathInternal(path, error); - if (NS_WARN_IF(error.Failed())) { - return error.StealNSResult(); - } - - nsCOMPtr localFile; - rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile)); - - if (NS_SUCCEEDED(rv)) { + nsCOMPtr localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]); + if (localFile) { nsCOMPtr parentFile; - rv = localFile->GetParent(getter_AddRefs(parentFile)); + nsresult rv = localFile->GetParent(getter_AddRefs(parentFile)); if (NS_SUCCEEDED(rv)) { filePicker->SetDisplayDirectory(parentFile); } @@ -703,7 +789,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType) // one file was selected before. if (oldFiles.Length() == 1) { nsAutoString leafName; - oldFiles[0]->GetName(leafName); + GetDOMFileOrDirectoryName(oldFiles[0], leafName); + if (!leafName.IsEmpty()) { filePicker->SetDefaultString(leafName); } @@ -756,7 +843,7 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc, NS_PRECONDITION(docURI, "docURI is null"); nsCOMPtr loadContext = aDoc->GetLoadContext(); - nsCOMPtr prefCallback = + nsCOMPtr prefCallback = new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback); #ifdef MOZ_B2G @@ -935,7 +1022,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement, if (tmp->IsSingleLineTextControl(false)) { tmp->mInputData.mState->Traverse(cb); } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -944,7 +1031,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement, nsGenericHTMLFormElementWithState) NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity) NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise) if (tmp->IsSingleLineTextControl(false)) { @@ -1003,8 +1090,8 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co // we can just grab the pretty string and use it as wallpaper GetDisplayFileName(it->mStaticDocFileList); } else { - it->mFiles.Clear(); - it->mFiles.AppendElements(mFiles); + it->mFilesOrDirectories.Clear(); + it->mFilesOrDirectories.AppendElements(mFilesOrDirectories); } break; case VALUE_MODE_DEFAULT_ON: @@ -1442,9 +1529,9 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const #else // XXX We'd love to assert that this can't happen, but some mochitests // use SpecialPowers to circumvent our more sane security model. - if (!mFiles.IsEmpty()) { + if (!mFilesOrDirectories.IsEmpty()) { ErrorResult rv; - mFiles[0]->GetMozFullPath(aValue, rv); + GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } @@ -1456,10 +1543,10 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const #endif } else { // Just return the leaf name - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { aValue.Truncate(); } else { - mFiles[0]->GetName(aValue); + GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue); } } @@ -1494,8 +1581,8 @@ HTMLInputElement::IsValueEmpty() const void HTMLInputElement::ClearFiles(bool aSetValueChanged) { - nsTArray> files; - SetFiles(files, aSetValueChanged); + nsTArray data; + SetFilesOrDirectories(data, aSetValueChanged); } /* static */ Decimal @@ -2065,9 +2152,9 @@ void HTMLInputElement::MozGetFileNameArray(nsTArray& aArray, ErrorResult& aRv) { - for (uint32_t i = 0; i < mFiles.Length(); i++) { - nsString str; - mFiles[i]->GetMozFullPathInternal(str, aRv); + for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) { + nsAutoString str; + GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -2117,25 +2204,29 @@ HTMLInputElement::MozSetFileArray(const Sequence>& aFiles) if (!global) { return; } - nsTArray> files; + + nsTArray files; for (uint32_t i = 0; i < aFiles.Length(); ++i) { RefPtr file = File::Create(global, aFiles[i].get()->Impl()); MOZ_ASSERT(file); - files.AppendElement(file); + OwningFileOrDirectory* element = files.AppendElement(); + element->SetAsFile() = file; } - SetFiles(files, true); + + SetFilesOrDirectories(files, true); } void -HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv) +HTMLInputElement::MozSetFileNameArray(const Sequence& aFileNames, + ErrorResult& aRv) { if (XRE_IsContentProcess()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } - nsTArray> files; + nsTArray files; for (uint32_t i = 0; i < aFileNames.Length(); ++i) { nsCOMPtr file; @@ -2152,21 +2243,28 @@ HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, Er NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file)); } - if (file) { - nsCOMPtr global = OwnerDoc()->GetScopeObject(); - RefPtr domFile = File::CreateFromFile(global, file); - files.AppendElement(domFile); - } else { + if (!file) { continue; // Not much we can do if the file doesn't exist } + nsCOMPtr global = OwnerDoc()->GetScopeObject(); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + RefPtr domFile = File::CreateFromFile(global, file); + + OwningFileOrDirectory* element = files.AppendElement(); + element->SetAsFile() = domFile; } - SetFiles(files, true); + SetFilesOrDirectories(files, true); } NS_IMETHODIMP -HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLength) +HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, + uint32_t aLength) { if (!nsContentUtils::IsCallerChrome()) { // setting the value of a "FILE" input widget requires chrome privilege @@ -2379,14 +2477,14 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const return; } - if (mFiles.Length() == 1) { - mFiles[0]->GetName(aValue); + if (mFilesOrDirectories.Length() == 1) { + GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue); return; } nsXPIDLString value; - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) { nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES, "NoFilesSelected", value); @@ -2396,7 +2494,7 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } } else { nsString count; - count.AppendInt(int(mFiles.Length())); + count.AppendInt(int(mFilesOrDirectories.Length())); const char16_t* params[] = { count.get() }; nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES, @@ -2407,13 +2505,13 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const } void -HTMLInputElement::SetFiles(const nsTArray>& aFiles, - bool aSetValueChanged) +HTMLInputElement::SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, + bool aSetValueChanged) { - mFiles.Clear(); - mFiles.AppendElements(aFiles); + mFilesOrDirectories.Clear(); + mFilesOrDirectories.AppendElements(aFilesOrDirectories); - AfterSetFiles(aSetValueChanged); + AfterSetFilesOrDirectories(aSetValueChanged); } void @@ -2421,22 +2519,24 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged) { RefPtr files = static_cast(aFiles); - mFiles.Clear(); + mFilesOrDirectories.Clear(); if (aFiles) { uint32_t listLength; aFiles->GetLength(&listLength); for (uint32_t i = 0; i < listLength; i++) { RefPtr file = files->Item(i); - mFiles.AppendElement(file); + + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; } } - AfterSetFiles(aSetValueChanged); + AfterSetFilesOrDirectories(aSetValueChanged); } void -HTMLInputElement::AfterSetFiles(bool aSetValueChanged) +HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged) { // No need to flush here, if there's no frame at this point we // don't need to force creation of one just to tell it about this @@ -2454,11 +2554,11 @@ HTMLInputElement::AfterSetFiles(bool aSetValueChanged) // call under GetMozFullPath won't be rejected for not being urgent. // XXX Protected by the ifndef because the blob code doesn't allow us to send // this message in b2g. - if (mFiles.IsEmpty()) { + if (mFilesOrDirectories.IsEmpty()) { mFirstFilePath.Truncate(); } else { ErrorResult rv; - mFiles[0]->GetMozFullPath(mFirstFilePath, rv); + GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } @@ -2538,9 +2638,11 @@ HTMLInputElement::UpdateFileList() if (mFileList) { mFileList->Clear(); - const nsTArray>& files = GetFilesInternal(); - for (uint32_t i = 0; i < files.Length(); ++i) { - if (!mFileList->Append(files[i])) { + const nsTArray& array = + GetFilesOrDirectoriesInternal(); + + for (uint32_t i = 0; i < array.Length(); ++i) { + if (array[i].IsFile() && !mFileList->Append(array[i].GetAsFile())) { return NS_ERROR_FAILURE; } } @@ -3975,7 +4077,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); if (numberControlFrame) { - if (aVisitor.mEvent->mMessage == eMouseDown && + if (aVisitor.mEvent->mMessage == eMouseDown && IsMutable()) { switch (numberControlFrame->GetSpinButtonForPointerEvent( aVisitor.mEvent->AsMouseEvent())) { @@ -4526,7 +4628,7 @@ HTMLInputElement::GetValueAsDate(const nsAString& aValue, } uint32_t endOfYearOffset = aValue.Length() - 6; - + if (aValue[endOfYearOffset] != '-' || aValue[endOfYearOffset + 3] != '-') { return false; @@ -4894,45 +4996,38 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv) return nullptr; } - const nsTArray>& filesAndDirs = GetFilesInternal(); + const nsTArray& filesAndDirs = + GetFilesOrDirectoriesInternal(); Sequence filesAndDirsSeq; - if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) { + if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), + mozilla::fallible_t())) { p->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return p.forget(); } for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) { - if (filesAndDirs[i]->IsDirectory()) { + if (filesAndDirs[i].IsDirectory()) { #if defined(ANDROID) || defined(MOZ_B2G) MOZ_ASSERT(false, "Directory picking should have been redirected to normal " "file picking for platforms that don't have a directory " "picker"); #endif - nsAutoString path; - filesAndDirs[i]->GetMozFullPathInternal(path, aRv); - if (aRv.Failed()) { - return nullptr; - } - int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR); - nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex); - RefPtr fs = new OSFileSystem(dirname); - fs->Init(OwnerDoc()->GetInnerWindow()); + RefPtr directory = filesAndDirs[i].GetAsDirectory(); - nsAutoString dompath(NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR)); - dompath.Append(Substring(path, leafSeparatorIndex + 1)); - RefPtr directory = new Directory(fs, dompath); // In future we could refactor SetFilePickerFiltersFromAccept to return a // semicolon separated list of file extensions and include that in the // filter string passed here. directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive")); filesAndDirsSeq[i].SetAsDirectory() = directory; } else { + MOZ_ASSERT(filesAndDirs[i].IsFile()); + // This file was directly selected by the user, so don't filter it. - filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i]; + filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile(); } } @@ -5566,13 +5661,18 @@ HTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission) if (mType == NS_FORM_INPUT_FILE) { // Submit files - const nsTArray>& files = GetFilesInternal(); + const nsTArray& files = + GetFilesOrDirectoriesInternal(); + bool hasBlobs = false; for (uint32_t i = 0; i < files.Length(); ++i) { - aFormSubmission->AddNameBlobOrNullPair(name, files[i]); + if (files[i].IsFile()) { + hasBlobs = true; + aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile()); + } } - if (files.IsEmpty()) { + if (!hasBlobs) { aFormSubmission->AddNameBlobOrNullPair(name, nullptr); } @@ -5606,9 +5706,9 @@ HTMLInputElement::SaveState() } break; case VALUE_MODE_FILENAME: - if (!mFiles.IsEmpty()) { + if (!mFilesOrDirectories.IsEmpty()) { inputState = new HTMLInputElementState(); - inputState->SetBlobImpls(mFiles); + inputState->SetFilesOrDirectories(mFilesOrDirectories); } break; case VALUE_MODE_VALUE: @@ -5777,7 +5877,7 @@ HTMLInputElement::AddStates(EventStates aStates) } } } - nsGenericHTMLFormElementWithState::AddStates(aStates); + nsGenericHTMLFormElementWithState::AddStates(aStates); } void @@ -5814,20 +5914,13 @@ HTMLInputElement::RestoreState(nsPresState* aState) break; case VALUE_MODE_FILENAME: { - const nsTArray>& blobImpls = inputState->GetBlobImpls(); + nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow(); + if (window) { + nsTArray array; + inputState->GetFilesOrDirectories(window, array); - nsCOMPtr global = OwnerDoc()->GetScopeObject(); - MOZ_ASSERT(global); - - nsTArray> files; - for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) { - RefPtr file = File::Create(global, blobImpls[i]); - MOZ_ASSERT(file); - - files.AppendElement(file); + SetFilesOrDirectories(array, true); } - - SetFiles(files, true); } break; case VALUE_MODE_VALUE: @@ -6337,15 +6430,15 @@ HTMLInputElement::IsValueMissing() const switch (GetValueMode()) { case VALUE_MODE_VALUE: return IsValueEmpty(); + case VALUE_MODE_FILENAME: - { - const nsTArray>& files = GetFilesInternal(); - return files.IsEmpty(); - } + return GetFilesOrDirectoriesInternal().IsEmpty(); + case VALUE_MODE_DEFAULT_ON: // This should not be used for type radio. // See the MOZ_ASSERT at the beginning of the method. return !mChecked; + case VALUE_MODE_DEFAULT: default: return false; diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 98d35e64a045..1988ce4b21d2 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -20,6 +20,7 @@ #include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit() #include "mozilla/dom/HTMLInputElementBinding.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/UnionTypes.h" #include "nsIFilePicker.h" #include "nsIContentPrefService2.h" #include "mozilla/Decimal.h" @@ -220,12 +221,13 @@ public: void GetDisplayFileName(nsAString& aFileName) const; - const nsTArray>& GetFilesInternal() const + const nsTArray& GetFilesOrDirectoriesInternal() const { - return mFiles; + return mFilesOrDirectories; } - void SetFiles(const nsTArray>& aFiles, bool aSetValueChanged); + void SetFilesOrDirectories(const nsTArray& aFilesOrDirectories, + bool aSetValueChanged); void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged); // Called when a nsIFilePicker or a nsIColorPicker terminate. @@ -925,9 +927,9 @@ protected: nsresult UpdateFileList(); /** - * Called after calling one of the SetFiles() functions. + * Called after calling one of the SetFilesOrDirectories() functions. */ - void AfterSetFiles(bool aSetValueChanged); + void AfterSetFilesOrDirectories(bool aSetValueChanged); /** * Determine whether the editor needs to be initialized explicitly for @@ -1266,16 +1268,16 @@ protected: } mInputData; /** - * The value of the input if it is a file input. This is the list of filenames - * used when uploading a file. It is vital that this is kept separate from - * mValue so that it won't be possible to 'leak' the value from a text-input - * to a file-input. Additionally, the logic for this value is kept as simple - * as possible to avoid accidental errors where the wrong filename is used. - * Therefor the list of filenames is always owned by this member, never by - * the frame. Whenever the frame wants to change the filename it has to call - * SetFileNames to update this member. + * The value of the input if it is a file input. This is the list of files or + * directories DOM objects used when uploading a file. It is vital that this + * is kept separate from mValue so that it won't be possible to 'leak' the + * value from a text-input to a file-input. Additionally, the logic for this + * value is kept as simple as possible to avoid accidental errors where the + * wrong filename is used. Therefor the list of filenames is always owned by + * this member, never by the frame. Whenever the frame wants to change the + * filename it has to call SetFilesOrDirectories to update this member. */ - nsTArray> mFiles; + nsTArray mFilesOrDirectories; #ifndef MOZ_CHILD_PERMISSIONS /** diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index d6a3dad00186..a5351d8d2b58 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -187,30 +187,6 @@ private: return mBlobImpl->IsFile(); } - virtual void - LookupAndCacheIsDirectory() override - { - mBlobImpl->LookupAndCacheIsDirectory(); - } - - virtual void - SetIsDirectory(bool aIsDir) override - { - return mBlobImpl->SetIsDirectory(aIsDir); - } - - virtual bool - IsDirectory() const override - { - return mBlobImpl->IsDirectory(); - } - - virtual BlobDirState - GetDirState() const override - { - return mBlobImpl->GetDirState(); - } - virtual bool MayBeClonedToOtherThreads() const override { diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 963676c0a44a..0b2a09f65add 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -444,8 +444,7 @@ ResolveMysteryFile(BlobImpl* aImpl, BlobChild* actor = ActorFromRemoteBlobImpl(aImpl); if (actor) { return actor->SetMysteryBlobInfo(aName, aContentType, - aSize, aLastModifiedDate, - BlobDirState::eUnknownIfDir); + aSize, aLastModifiedDate); } return true; } diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index ea59a50e8fdd..f3d7fbcdf919 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -670,8 +670,7 @@ public: EmptyBlobImpl(const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate) - : BlobImplBase(aName, aContentType, 0, aLastModifiedDate, - BlobDirState::eIsNotDir) + : BlobImplBase(aName, aContentType, 0, aLastModifiedDate) { mImmutable = true; } @@ -716,14 +715,12 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata final nsString mName; uint64_t mLength; int64_t mLastModifiedDate; - BlobDirState mDirState; bool mHasRecursed; const bool mIsSameProcessActor; explicit CreateBlobImplMetadata(bool aIsSameProcessActor) : mLength(0) , mLastModifiedDate(0) - , mDirState(BlobDirState::eUnknownIfDir) , mHasRecursed(false) , mIsSameProcessActor(aIsSameProcessActor) { @@ -965,7 +962,6 @@ CreateBlobImpl(const ParentBlobConstructorParams& aParams, metadata.mName = params.name(); metadata.mLength = params.length(); metadata.mLastModifiedDate = params.modDate(); - metadata.mDirState = BlobDirState(params.dirState()); } RefPtr blobImpl = @@ -1775,7 +1771,6 @@ public: const nsAString& aContentType, uint64_t aLength, int64_t aModDate, - BlobDirState aDirState, bool aIsSameProcessBlob); // For Blob. @@ -2053,18 +2048,6 @@ public: virtual bool IsFile() const override; - virtual void - LookupAndCacheIsDirectory() override; - - virtual void - SetIsDirectory(bool aIsDir) override; - - virtual bool - IsDirectory() const override; - - virtual BlobDirState - GetDirState() const override; - virtual bool MayBeClonedToOtherThreads() const override; @@ -2096,9 +2079,8 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, const nsAString& aContentType, uint64_t aLength, int64_t aModDate, - BlobDirState aDirState, bool aIsSameProcessBlob) - : BlobImplBase(aName, aContentType, aLength, aModDate, aDirState) + : BlobImplBase(aName, aContentType, aLength, aModDate) , mIsSlice(false) { if (aIsSameProcessBlob) { @@ -2814,34 +2796,6 @@ RemoteBlobImpl::IsFile() const return mBlobImpl->IsFile(); } -void -BlobParent:: -RemoteBlobImpl::LookupAndCacheIsDirectory() -{ - return mBlobImpl->LookupAndCacheIsDirectory(); -} - -void -BlobParent:: -RemoteBlobImpl::SetIsDirectory(bool aIsDir) -{ - return mBlobImpl->SetIsDirectory(aIsDir); -} - -bool -BlobParent:: -RemoteBlobImpl::IsDirectory() const -{ - return mBlobImpl->IsDirectory(); -} - -BlobDirState -BlobParent:: -RemoteBlobImpl::GetDirState() const -{ - return mBlobImpl->GetDirState(); -} - bool BlobParent:: RemoteBlobImpl::MayBeClonedToOtherThreads() const @@ -3033,8 +2987,7 @@ BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl) MOZ_ASSERT(!rv.Failed()); remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length, - modDate, otherImpl->GetDirState(), - false /* SameProcessBlobImpl */); + modDate, false /* SameProcessBlobImpl */); } else { remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length, false /* SameProcessBlobImpl */); @@ -3086,7 +3039,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) params.contentType(), params.length(), params.modDate(), - BlobDirState(params.dirState()), false /* SameProcessBlobImpl */); break; } @@ -3122,7 +3074,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) contentType, size, lastModifiedDate, - blobImpl->GetDirState(), true /* SameProcessBlobImpl */); } else { remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size, @@ -3303,8 +3254,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager, MOZ_ASSERT(!rv.Failed()); blobParams = - FileBlobConstructorParams(name, contentType, length, modDate, - aBlobImpl->GetDirState(), blobData); + FileBlobConstructorParams(name, contentType, length, modDate, blobData); } else { blobParams = NormalBlobConstructorParams(contentType, length, blobData); } @@ -3477,8 +3427,7 @@ bool BlobChild::SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, - BlobDirState aDirState) + int64_t aLastModifiedDate) { AssertIsOnOwningThread(); MOZ_ASSERT(mBlobImpl); @@ -3491,7 +3440,6 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName, aContentType, aLength, aLastModifiedDate, - aDirState, void_t() /* optionalBlobData */); return SendResolveMystery(params); } @@ -3854,7 +3802,7 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager, blobParams = FileBlobConstructorParams(name, contentType, length, modDate, - aBlobImpl->GetDirState(), void_t()); + void_t()); } else { blobParams = NormalBlobConstructorParams(contentType, length, void_t()); } diff --git a/dom/ipc/BlobChild.h b/dom/ipc/BlobChild.h index 35853f307dfb..affa9934aa85 100644 --- a/dom/ipc/BlobChild.h +++ b/dom/ipc/BlobChild.h @@ -31,8 +31,6 @@ class ContentChild; class nsIContentChild; class PBlobStreamChild; -enum BlobDirState : uint32_t; - class BlobChild final : public PBlobChild { @@ -117,8 +115,7 @@ public: SetMysteryBlobInfo(const nsString& aName, const nsString& aContentType, uint64_t aLength, - int64_t aLastModifiedDate, - BlobDirState aDirState); + int64_t aLastModifiedDate); // Use this for non-file blobs. bool diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index 49b4f3f24207..2778642ef9fa 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -73,7 +73,6 @@ struct FileBlobConstructorParams nsString contentType; uint64_t length; int64_t modDate; - uint32_t dirState; // This must be of type BlobData in a child->parent message, and will always // be of type void_t in a parent->child message. diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp index 114001ae104d..94c84709f11f 100644 --- a/dom/ipc/FilePickerParent.cpp +++ b/dom/ipc/FilePickerParent.cpp @@ -53,15 +53,18 @@ FilePickerParent::~FilePickerParent() // 2. The stream transport thread stat()s the file in Run() and then dispatches // the same runnable on the main thread. // 3. The main thread sends the results over IPC. -FilePickerParent::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent, - nsTArray>& aBlobs) +FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent, + nsTArray>& aFiles, + bool aIsDirectory) : mFilePickerParent(aFPParent) + , mIsDirectory(aIsDirectory) { - mBlobs.SwapElements(aBlobs); + mFiles.SwapElements(aFiles); + MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); } bool -FilePickerParent::FileSizeAndDateRunnable::Dispatch() +FilePickerParent::IORunnable::Dispatch() { MOZ_ASSERT(NS_IsMainThread()); @@ -75,23 +78,49 @@ FilePickerParent::FileSizeAndDateRunnable::Dispatch() } NS_IMETHODIMP -FilePickerParent::FileSizeAndDateRunnable::Run() +FilePickerParent::IORunnable::Run() { // If we're on the main thread, then that means we're done. Just send the // results. if (NS_IsMainThread()) { if (mFilePickerParent) { - mFilePickerParent->SendFiles(mBlobs); + mFilePickerParent->SendFilesOrDirectories(mResults); } return NS_OK; } - // We're not on the main thread, so do the stat(). - for (unsigned i = 0; i < mBlobs.Length(); i++) { - ErrorResult rv; - mBlobs[i]->GetSize(rv); - mBlobs[i]->GetLastModified(rv); - mBlobs[i]->LookupAndCacheIsDirectory(); + // We're not on the main thread, so do the IO. + + for (uint32_t i = 0; i < mFiles.Length(); ++i) { + if (mIsDirectory) { + nsAutoString path; + nsresult rv = mFiles[i]->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eDirectoryPath; + data->mDirectoryPath = path; + continue; + } + + RefPtr blobImpl = new BlobImplFile(mFiles[i]); + + ErrorResult error; + blobImpl->GetSize(error); + if (NS_WARN_IF(error.Failed())) { + continue; + } + + blobImpl->GetLastModified(error); + if (NS_WARN_IF(error.Failed())) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eBlobImpl; + data->mBlobImpl = blobImpl; } // Dispatch ourselves back on the main thread. @@ -101,29 +130,46 @@ FilePickerParent::FileSizeAndDateRunnable::Run() // thread. MOZ_CRASH(); } + return NS_OK; } void -FilePickerParent::FileSizeAndDateRunnable::Destroy() +FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; } void -FilePickerParent::SendFiles(const nsTArray>& aBlobs) +FilePickerParent::SendFilesOrDirectories(const nsTArray& aData) { + if (mMode == nsIFilePicker::modeGetFolder) { + MOZ_ASSERT(aData.Length() <= 1); + if (aData.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); + + InputDirectory input; + input.directoryPath() = aData[0].mDirectoryPath; + Unused << Send__delete__(this, input, mResult); + return; + } + nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager(); InfallibleTArray blobs; - for (unsigned i = 0; i < aBlobs.Length(); i++) { - BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]); + for (unsigned i = 0; i < aData.Length(); i++) { + MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); + BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl); if (blobParent) { blobs.AppendElement(blobParent); } } - InputFiles inblobs; + InputBlobs inblobs; inblobs.blobsParent().SwapElements(blobs); Unused << Send__delete__(this, inblobs, mResult); } @@ -138,7 +184,7 @@ FilePickerParent::Done(int16_t aResult) return; } - nsTArray> blobs; + nsTArray> files; if (mMode == nsIFilePicker::modeOpenMultiple) { nsCOMPtr iter; NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); @@ -149,22 +195,26 @@ FilePickerParent::Done(int16_t aResult) iter->GetNext(getter_AddRefs(supports)); if (supports) { nsCOMPtr file = do_QueryInterface(supports); - - RefPtr blobimpl = new BlobImplFile(file); - blobs.AppendElement(blobimpl); + MOZ_ASSERT(file); + files.AppendElement(file); } } } else { nsCOMPtr file; mFilePicker->GetFile(getter_AddRefs(file)); if (file) { - RefPtr blobimpl = new BlobImplFile(file); - blobs.AppendElement(blobimpl); + files.AppendElement(file); } } + if (files.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + MOZ_ASSERT(!mRunnable); - mRunnable = new FileSizeAndDateRunnable(this, blobs); + mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder); + // Dispatch to background thread to do I/O: if (!mRunnable->Dispatch()) { Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); diff --git a/dom/ipc/FilePickerParent.h b/dom/ipc/FilePickerParent.h index b27fa3822cc0..a11b89c7a343 100644 --- a/dom/ipc/FilePickerParent.h +++ b/dom/ipc/FilePickerParent.h @@ -14,6 +14,8 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/PFilePickerParent.h" +class nsIFile; + namespace mozilla { namespace dom { @@ -29,7 +31,19 @@ class FilePickerParent : public PFilePickerParent virtual ~FilePickerParent(); void Done(int16_t aResult); - void SendFiles(const nsTArray>& aDomBlobs); + + struct BlobImplOrString + { + RefPtr mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + + void SendFilesOrDirectories(const nsTArray& aData); virtual bool RecvOpen(const int16_t& aSelectedType, const bool& aAddToRecentDocs, @@ -61,21 +75,26 @@ class FilePickerParent : public PFilePickerParent private: bool CreateFilePicker(); - class FileSizeAndDateRunnable : public nsRunnable + // This runnable is used to do some I/O operation on a separate thread. + class IORunnable : public nsRunnable { FilePickerParent* mFilePickerParent; - nsTArray> mBlobs; + nsTArray> mFiles; + nsTArray mResults; nsCOMPtr mEventTarget; + bool mIsDirectory; public: - FileSizeAndDateRunnable(FilePickerParent *aFPParent, - nsTArray>& aBlobs); + IORunnable(FilePickerParent *aFPParent, + nsTArray>& aFiles, + bool aIsDirectory); + bool Dispatch(); NS_IMETHOD Run(); void Destroy(); }; - RefPtr mRunnable; + RefPtr mRunnable; RefPtr mCallback; nsCOMPtr mFilePicker; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 21d3879ba24a..0f9a0e7458c2 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -289,6 +289,7 @@ struct FileSystemGetDirectoryListingParams { nsString filesystem; nsString realPath; + bool isRoot; // 'filters' could be an array rather than a semicolon separated string // (we'd then use InfallibleTArray internally), but that is // wasteful. E10s requires us to pass the filters over as a string anyway, @@ -305,6 +306,7 @@ struct FileSystemGetFileOrDirectoryParams { nsString filesystem; nsString realPath; + bool isRoot; }; union FileSystemPathOrFileValue diff --git a/dom/ipc/PFilePicker.ipdl b/dom/ipc/PFilePicker.ipdl index aff58b011da8..6184c5b9d55f 100644 --- a/dom/ipc/PFilePicker.ipdl +++ b/dom/ipc/PFilePicker.ipdl @@ -12,14 +12,20 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; namespace mozilla { namespace dom { -struct InputFiles +struct InputBlobs { PBlob[] blobs; }; -union MaybeInputFiles +struct InputDirectory { - InputFiles; + nsString directoryPath; +}; + +union MaybeInputData +{ + InputBlobs; + InputDirectory; void_t; }; @@ -33,7 +39,7 @@ parent: nsString displayDirectory); child: - async __delete__(MaybeInputFiles files, int16_t result); + async __delete__(MaybeInputData data, int16_t result); }; } // namespace dom diff --git a/dom/webidl/Directory.webidl b/dom/webidl/Directory.webidl index 7ea8e02693cc..6504dd02e04f 100644 --- a/dom/webidl/Directory.webidl +++ b/dom/webidl/Directory.webidl @@ -20,6 +20,7 @@ interface Directory { /* * The leaf name of the directory. */ + [Throws] readonly attribute DOMString name; /* @@ -105,11 +106,13 @@ partial interface Directory { * to obtain this Directory. Full filesystem paths are not exposed to * unprivilaged content. */ + [Throws] readonly attribute DOMString path; /* * Getter for the immediate children of this directory. */ + [Throws] Promise> getFilesAndDirectories(); }; diff --git a/widget/nsBaseFilePicker.cpp b/widget/nsBaseFilePicker.cpp index 08935999d22d..71c00cbb1db3 100644 --- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -17,6 +17,7 @@ #include "nsCOMArray.h" #include "nsIFile.h" #include "nsEnumeratorUtils.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/Services.h" #include "WidgetUtils.h" @@ -30,6 +31,36 @@ using namespace mozilla::dom; #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties" #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties" +namespace { + +nsresult +LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow, + bool aIsDirectory, + nsIFile* aFile, + nsISupports** aResult) +{ + if (aIsDirectory) { +#ifdef DEBUG + bool isDir; + aFile->IsDirectory(&isDir); + MOZ_ASSERT(isDir); +#endif + + RefPtr directory = + Directory::Create(aWindow, aFile, Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + directory.forget(aResult); + return NS_OK; + } + + nsCOMPtr blob = File::CreateFromFile(aWindow, aFile); + blob.forget(aResult); + return NS_OK; +} + +} // anonymous namespace + /** * A runnable to dispatch from the main thread to the main thread to display * the file picker while letting the showAsync method return right away. @@ -74,9 +105,9 @@ class nsBaseFilePickerEnumerator : public nsISimpleEnumerator public: NS_DECL_ISUPPORTS - explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent, - nsISimpleEnumerator* iterator, - int16_t aMode) + nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent, + nsISimpleEnumerator* iterator, + int16_t aMode) : mIterator(iterator) , mParent(aParent->GetCurrentInnerWindow()) , mMode(aMode) @@ -98,32 +129,10 @@ public: return NS_ERROR_FAILURE; } - RefPtr domFile = File::CreateFromFile(mParent, localFile); - - // Right now we're on the main thread of the chrome process. We need - // to call SetIsDirectory on the BlobImpl, but it's preferable not to - // call nsIFile::IsDirectory to determine what argument to pass since - // IsDirectory does synchronous I/O. It's true that since we've just - // been called synchronously directly after nsIFilePicker::Show blocked - // the main thread while the picker was being shown and the OS did file - // system access, doing more I/O to stat the selected files probably - // wouldn't be the end of the world. However, we can simply check - // mMode and avoid calling IsDirectory. - // - // In future we may take advantage of OS X's ability to allow both - // files and directories to be picked at the same time, so we do assert - // in debug builds that the mMode trick produces the correct results. - // If we do add that support maybe it's better to use IsDirectory - // directly, but in an nsRunnable punted off to a background thread. -#ifdef DEBUG - bool isDir; - localFile->IsDirectory(&isDir); - MOZ_ASSERT(isDir == (mMode == nsIFilePicker::modeGetFolder)); -#endif - domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder); - - nsCOMPtr(domFile).forget(aResult); - return NS_OK; + return LocalFileToDirectoryOrBlob(mParent, + mMode == nsIFilePicker::modeGetFolder, + localFile, + aResult); } NS_IMETHOD @@ -349,10 +358,10 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr; - RefPtr domFile = File::CreateFromFile(innerParent, localFile); - domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder); - nsCOMPtr(domFile).forget(aValue); - return NS_OK; + return LocalFileToDirectoryOrBlob(innerParent, + mMode == nsIFilePicker::modeGetFolder, + localFile, + aValue); } NS_IMETHODIMP diff --git a/widget/nsFilePickerProxy.cpp b/widget/nsFilePickerProxy.cpp index 830159de1e42..1dcbcf7ee5c8 100644 --- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -7,6 +7,7 @@ #include "nsFilePickerProxy.h" #include "nsComponentManagerUtils.h" #include "nsIFile.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/ipc/BlobChild.h" @@ -142,11 +143,11 @@ nsFilePickerProxy::Open(nsIFilePickerShownCallback* aCallback) } bool -nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles, +nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) { - if (aFiles.type() == MaybeInputFiles::TInputFiles) { - const InfallibleTArray& blobs = aFiles.get_InputFiles().blobsChild(); + if (aData.type() == MaybeInputData::TInputBlobs) { + const InfallibleTArray& blobs = aData.get_InputBlobs().blobsChild(); for (uint32_t i = 0; i < blobs.Length(); ++i) { BlobChild* actor = static_cast(blobs[i]); RefPtr blobImpl = actor->GetBlobImpl(); @@ -161,8 +162,24 @@ nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles, RefPtr file = File::Create(inner, blobImpl); MOZ_ASSERT(file); - mFilesOrDirectories.AppendElement(file); + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsFile() = file; } + } else if (aData.type() == MaybeInputData::TInputDirectory) { + nsCOMPtr file; + NS_ConvertUTF16toUTF8 path(aData.get_InputDirectory().directoryPath()); + nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + RefPtr directory = + Directory::Create(mParent->GetCurrentInnerWindow(), file, + Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsDirectory() = directory; } if (mCallback) { @@ -182,8 +199,16 @@ nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue) } MOZ_ASSERT(mFilesOrDirectories.Length() == 1); - nsCOMPtr blob = mFilesOrDirectories[0].get(); - blob.forget(aValue); + + if (mFilesOrDirectories[0].IsFile()) { + nsCOMPtr blob = mFilesOrDirectories[0].GetAsFile().get(); + blob.forget(aValue); + return NS_OK; + } + + MOZ_ASSERT(mFilesOrDirectories[0].IsDirectory()); + RefPtr directory = mFilesOrDirectories[0].GetAsDirectory(); + directory.forget(aValue); return NS_OK; } @@ -194,8 +219,9 @@ class SimpleEnumerator final : public nsISimpleEnumerator public: NS_DECL_ISUPPORTS - explicit SimpleEnumerator(const nsTArray>& aFiles) - : mFiles(aFiles) + explicit + SimpleEnumerator(const nsTArray& aFilesOrDirectories) + : mFilesOrDirectories(aFilesOrDirectories) , mIndex(0) {} @@ -203,17 +229,26 @@ public: HasMoreElements(bool* aRetvalue) override { MOZ_ASSERT(aRetvalue); - *aRetvalue = mIndex < mFiles.Length(); + *aRetvalue = mIndex < mFilesOrDirectories.Length(); return NS_OK; } NS_IMETHOD - GetNext(nsISupports** aSupports) override + GetNext(nsISupports** aValue) override { - NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE); + NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE); - nsCOMPtr blob = mFiles[mIndex++].get(); - blob.forget(aSupports); + uint32_t index = mIndex++; + + if (mFilesOrDirectories[index].IsFile()) { + nsCOMPtr blob = mFilesOrDirectories[index].GetAsFile().get(); + blob.forget(aValue); + return NS_OK; + } + + MOZ_ASSERT(mFilesOrDirectories[index].IsDirectory()); + RefPtr directory = mFilesOrDirectories[index].GetAsDirectory(); + directory.forget(aValue); return NS_OK; } @@ -221,7 +256,7 @@ private: ~SimpleEnumerator() {} - nsTArray> mFiles; + nsTArray mFilesOrDirectories; uint32_t mIndex; }; @@ -232,7 +267,8 @@ NS_IMPL_ISUPPORTS(SimpleEnumerator, nsISimpleEnumerator) NS_IMETHODIMP nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aDomfiles) { - RefPtr enumerator = new SimpleEnumerator(mFilesOrDirectories); + RefPtr enumerator = + new SimpleEnumerator(mFilesOrDirectories); enumerator.forget(aDomfiles); return NS_OK; } diff --git a/widget/nsFilePickerProxy.h b/widget/nsFilePickerProxy.h index 393944d6c882..093678a698ed 100644 --- a/widget/nsFilePickerProxy.h +++ b/widget/nsFilePickerProxy.h @@ -13,17 +13,12 @@ #include "nsCOMArray.h" #include "mozilla/dom/PFilePickerChild.h" +#include "mozilla/dom/UnionTypes.h" class nsIWidget; class nsIFile; class nsPIDOMWindowInner; -namespace mozilla { -namespace dom { -class File; -} // namespace dom -} // namespace mozilla - /** This class creates a proxy file picker to be used in content processes. The file picker just collects the initialization data and when Show() is @@ -59,13 +54,13 @@ public: // PFilePickerChild virtual bool - Recv__delete__(const MaybeInputFiles& aFiles, const int16_t& aResult) override; + Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override; private: ~nsFilePickerProxy(); void InitNative(nsIWidget*, const nsAString&) override; - nsTArray> mFilesOrDirectories; + nsTArray mFilesOrDirectories; nsCOMPtr mCallback; int16_t mSelectedType; From 4c6e58864582e89dd4ddf563704a15ad829ad818 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 05/28] Bug 1173320 - patch 2/8 - Proper naming for the FileSystem path serialization, r=smaug --- dom/filesystem/DeviceStorageFileSystem.cpp | 24 +++++++++++++++------- dom/filesystem/DeviceStorageFileSystem.h | 4 ++++ dom/filesystem/FileSystemBase.cpp | 3 ++- dom/filesystem/FileSystemBase.h | 14 ++++--------- dom/filesystem/FileSystemRequestParent.cpp | 2 +- dom/filesystem/FileSystemTaskBase.cpp | 5 ++++- dom/filesystem/GetDirectoryListingTask.cpp | 6 +++--- dom/filesystem/GetFileOrDirectoryTask.cpp | 4 ++-- dom/filesystem/OSFileSystem.cpp | 8 ++++++-- dom/filesystem/OSFileSystem.h | 3 +++ 10 files changed, 46 insertions(+), 27 deletions(-) diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 4d6053489cfd..d96301db0329 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -31,12 +31,6 @@ DeviceStorageFileSystem::DeviceStorageFileSystem( mStorageType = aStorageType; mStorageName = aStorageName; - // Generate the string representation of the file system. - mString.AppendLiteral("devicestorage-"); - mString.Append(mStorageType); - mString.Append('-'); - mString.Append(mStorageName); - mRequiresPermissionChecks = !mozilla::Preferences::GetBool("device.storage.prompt.testing", false); @@ -140,8 +134,24 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const return false; } + nsAutoString fsSerialization; + fs->SerializeDOMPath(fsSerialization); + + nsAutoString thisSerialization; + SerializeDOMPath(thisSerialization); + // Check if the given directory is from this storage. - return fs->ToString() == mString; + return fsSerialization == thisSerialization; +} + +void +DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const +{ + // Generate the string representation of the file system. + aString.AssignLiteral("devicestorage-"); + aString.Append(mStorageType); + aString.Append('-'); + aString.Append(mStorageName); } } // namespace dom diff --git a/dom/filesystem/DeviceStorageFileSystem.h b/dom/filesystem/DeviceStorageFileSystem.h index dca34e335fc6..c73b9099b181 100644 --- a/dom/filesystem/DeviceStorageFileSystem.h +++ b/dom/filesystem/DeviceStorageFileSystem.h @@ -41,6 +41,10 @@ public: virtual bool IsSafeDirectory(Directory* aDir) const override; + + virtual void + SerializeDOMPath(nsAString& aSerializedString) const override; + private: virtual ~DeviceStorageFileSystem(); diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index 3f0046525f76..a750fa7839db 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -15,7 +15,7 @@ namespace dom { // static already_AddRefed -FileSystemBase::FromString(const nsAString& aString) +FileSystemBase::DeserializeDOMPath(const nsAString& aString) { if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) { // The string representation of devicestorage file system is of the format: @@ -38,6 +38,7 @@ FileSystemBase::FromString(const nsAString& aString) new DeviceStorageFileSystem(storageType, storageName); return f.forget(); } + return RefPtr(new OSFileSystem(aString)).forget(); } diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index 5437e2357be3..0ac36f009d5f 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -25,19 +25,16 @@ public: // Create file system object from its string representation. static already_AddRefed - FromString(const nsAString& aString); + DeserializeDOMPath(const nsAString& aString); FileSystemBase(); virtual void Shutdown(); - // Get the string representation of the file system. - const nsString& - ToString() const - { - return mString; - } + // SerializeDOMPath the FileSystem to string. + virtual void + SerializeDOMPath(nsAString& aOutput) const = 0; virtual nsPIDOMWindowInner* GetWindow() const; @@ -110,9 +107,6 @@ protected: // The same, but with path separators normalized to "/". nsString mNormalizedLocalRootPath; - // The string representation of the file system. - nsString mString; - bool mShutdown; // The permission name required to access the file system. diff --git a/dom/filesystem/FileSystemRequestParent.cpp b/dom/filesystem/FileSystemRequestParent.cpp index c730a0dd4e93..d2742a261c8a 100644 --- a/dom/filesystem/FileSystemRequestParent.cpp +++ b/dom/filesystem/FileSystemRequestParent.cpp @@ -28,7 +28,7 @@ FileSystemRequestParent::~FileSystemRequestParent() #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \ case FileSystemParams::TFileSystem##name##Params: { \ const FileSystem##name##Params& p = aParams; \ - mFileSystem = FileSystemBase::FromString(p.filesystem()); \ + mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \ task = name##Task::Create(mFileSystem, p, this, rv); \ if (NS_WARN_IF(rv.Failed())) { \ return false; \ diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index b1bfdc6abc36..df11f3512c01 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -106,8 +106,11 @@ FileSystemTaskBase::Start() return; } + nsAutoString serialization; + mFileSystem->SerializeDOMPath(serialization); + ErrorResult rv; - FileSystemParams params = GetRequestParams(mFileSystem->ToString(), rv); + FileSystemParams params = GetRequestParams(serialization, rv); if (NS_WARN_IF(rv.Failed())) { return; } diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 153691506d93..ee65e54324a9 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -70,7 +70,7 @@ GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, } task->mType = aParam.isRoot() - ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory; return task.forget(); } @@ -215,7 +215,7 @@ GetDirectoryListingTask::Work() } if (!exists) { - if (mType == Directory::eNotRootDirectory) { + if (mType == Directory::eNotDOMRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } @@ -390,7 +390,7 @@ GetDirectoryListingTask::HandlerCallback() RefPtr directory = Directory::Create(mFileSystem->GetWindow(), directoryPath, - Directory::eNotRootDirectory, + Directory::eNotDOMRootDirectory, mFileSystem); MOZ_ASSERT(directory); diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 4cbc443e5038..5056590bae16 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -69,7 +69,7 @@ GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, } task->mType = aParam.isRoot() - ? Directory::eDOMRootDirectory : Directory::eNotRootDirectory; + ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory; return task.forget(); } @@ -200,7 +200,7 @@ GetFileOrDirectoryTask::Work() } if (!exists) { - if (mType == Directory::eNotRootDirectory) { + if (mType == Directory::eNotDOMRootDirectory) { return NS_ERROR_DOM_FILE_NOT_FOUND_ERR; } diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index e85e8e472b6e..8fc9e88cf6fe 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -27,8 +27,6 @@ OSFileSystem::OSFileSystem(const nsAString& aRootDir) // access different parts of devices storage like Pictures, or Videos, etc. mRequiresPermissionChecks = false; - mString = mLocalRootPath; - #ifdef DEBUG mPermission.AssignLiteral("never-used"); #endif @@ -89,5 +87,11 @@ OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow); } +void +OSFileSystem::SerializeDOMPath(nsAString& aOutput) const +{ + aOutput = mLocalRootPath; +} + } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/OSFileSystem.h b/dom/filesystem/OSFileSystem.h index 45873f6c2e09..476611bcb3af 100644 --- a/dom/filesystem/OSFileSystem.h +++ b/dom/filesystem/OSFileSystem.h @@ -34,6 +34,9 @@ public: virtual bool IsSafeDirectory(Directory* aDir) const override; + virtual void + SerializeDOMPath(nsAString& aOutput) const override; + // CC methods virtual void Unlink() override; virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override; From f11df067d2a76b13338c922041fcd274ee5d5f6d Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 06/28] Bug 1173320 - patch 3/8 - Improve the Windows path management, r=smaug --- .../test/test_fs_createDirectory.html | 4 +- dom/filesystem/Directory.cpp | 88 ++++++++++--------- dom/filesystem/Directory.h | 3 - dom/filesystem/FileSystemBase.cpp | 33 ------- dom/filesystem/FileSystemBase.h | 6 -- dom/filesystem/FileSystemUtils.cpp | 2 +- dom/filesystem/FileSystemUtils.h | 5 +- dom/filesystem/GetDirectoryListingTask.cpp | 48 +++------- dom/filesystem/OSFileSystem.cpp | 2 +- 9 files changed, 67 insertions(+), 124 deletions(-) diff --git a/dom/devicestorage/test/test_fs_createDirectory.html b/dom/devicestorage/test/test_fs_createDirectory.html index d34905140a96..3b573b086f42 100644 --- a/dom/devicestorage/test/test_fs_createDirectory.html +++ b/dom/devicestorage/test/test_fs_createDirectory.html @@ -34,14 +34,14 @@ function testCreateDirectory(rootDir, path) { } function createDirectorySuccess(d) { - ok(d.name === gName, "Failed to create directory: name mismatch."); + is(d.name, gName, "Failed to create directory: name mismatch."); // Get the new created directory from the root. gRoot.get(gPath).then(getSuccess, cbError); } function getSuccess(d) { - ok(d.name === gName, "Should get directory - " + (gPath || "[root]") + "."); + is(d.name, gName, "Should get directory - " + (gPath || "[root]") + "."); switch (gTestCount) { case 0: gRoot = d; diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index a3ff29b14e6d..28d1ed170a46 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -35,6 +35,46 @@ namespace mozilla { namespace dom { +namespace { + +bool +IsValidRelativeDOMPath(const nsString& aPath, nsTArray& aParts) +{ + // We don't allow empty relative path to access the root. + if (aPath.IsEmpty()) { + return false; + } + + // Leading and trailing "/" are not allowed. + if (aPath.First() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR || + aPath.Last() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR) { + return false; + } + + NS_NAMED_LITERAL_STRING(kCurrentDir, "."); + NS_NAMED_LITERAL_STRING(kParentDir, ".."); + + // Split path and check each path component. + nsCharSeparatedTokenizer tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR); + while (tokenizer.hasMoreTokens()) { + nsDependentSubstring pathComponent = tokenizer.nextToken(); + // The path containing empty components, such as "foo//bar", is invalid. + // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar", + // to walk up the directory. + if (pathComponent.IsEmpty() || + pathComponent.Equals(kCurrentDir) || + pathComponent.Equals(kParentDir)) { + return false; + } + + aParts.AppendElement(pathComponent); + } + + return true; +} + +} // anonymous namespace + NS_IMPL_CYCLE_COLLECTION_CLASS(Directory) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory) @@ -302,7 +342,7 @@ void Directory::GetPath(nsAString& aRetval, ErrorResult& aRv) { if (mType == eDOMRootDirectory) { - aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); + aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); } else { // TODO: this should be a bit different... GetName(aRetval, aRv); @@ -384,7 +424,8 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const static const char kWhitespace[] = "\b\t\r\n "; relativePath.Trim(kWhitespace); - if (!IsValidRelativePath(relativePath)) { + nsTArray parts; + if (!IsValidRelativeDOMPath(relativePath, parts)) { return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR; } @@ -394,49 +435,16 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const return rv; } - rv = file->AppendRelativePath(relativePath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + for (uint32_t i = 0; i < parts.Length(); ++i) { + rv = file->AppendRelativePath(parts[i]); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } file.forget(aFile); return NS_OK; } -// static -bool -Directory::IsValidRelativePath(const nsString& aPath) -{ - // We don't allow empty relative path to access the root. - if (aPath.IsEmpty()) { - return false; - } - - // Leading and trailing "/" are not allowed. - if (aPath.First() == FileSystemUtils::kSeparatorChar || - aPath.Last() == FileSystemUtils::kSeparatorChar) { - return false; - } - - NS_NAMED_LITERAL_STRING(kCurrentDir, "."); - NS_NAMED_LITERAL_STRING(kParentDir, ".."); - - // Split path and check each path component. - nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar); - while (tokenizer.hasMoreTokens()) { - nsDependentSubstring pathComponent = tokenizer.nextToken(); - // The path containing empty components, such as "foo//bar", is invalid. - // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar", - // to walk up the directory. - if (pathComponent.IsEmpty() || - pathComponent.Equals(kCurrentDir) || - pathComponent.Equals(kParentDir)) { - return false; - } - } - - return true; -} - } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index 3d8a420723e5..a71f02930302 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -147,9 +147,6 @@ private: FileSystemBase* aFileSystem = nullptr); ~Directory(); - static bool - IsValidRelativePath(const nsString& aPath); - /* * Convert relative DOM path to the absolute real path. */ diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index a750fa7839db..2b1d52608ebc 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -64,39 +64,6 @@ FileSystemBase::GetWindow() const return nullptr; } -already_AddRefed -FileSystemBase::GetLocalFile(const nsAString& aRealPath) const -{ - MOZ_ASSERT(XRE_IsParentProcess(), - "Should be on parent process!"); - - // Let's convert the input path to /path - nsAutoString localPath; - if (!aRealPath.IsEmpty() && - !StringBeginsWith(aRealPath, - NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR))) { - localPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); - } - localPath.Append(aRealPath); - - // We have to normalize the path string in order to follow the separator - // schema of this OS. - nsAutoString normalizedPath; - FileSystemUtils::NormalizedPathToLocalPath(localPath, normalizedPath); - - // The full path is mLocalRootPath + normalizedPath. - nsAutoString path(mLocalRootPath); - path.Append(normalizedPath); - - nsCOMPtr file; - nsresult rv = NS_NewLocalFile(path, false, getter_AddRefs(file)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - return file.forget(); -} - bool FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const { diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index 0ac36f009d5f..6a715dee9741 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -39,12 +39,6 @@ public: virtual nsPIDOMWindowInner* GetWindow() const; - /** - * Create nsIFile object from the given real path (absolute DOM path). - */ - already_AddRefed - GetLocalFile(const nsAString& aRealPath) const; - /* * Get the virtual name of the root directory. This name will be exposed to * the content page. diff --git a/dom/filesystem/FileSystemUtils.cpp b/dom/filesystem/FileSystemUtils.cpp index 1e62e4c56bce..c5a8817f7cd5 100644 --- a/dom/filesystem/FileSystemUtils.cpp +++ b/dom/filesystem/FileSystemUtils.cpp @@ -54,7 +54,7 @@ FileSystemUtils::IsDescendantPath(const nsAString& aPath, { // The descendant path should begin with its ancestor path. nsAutoString prefix; - prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR); + prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); // Check the sub-directory path to see if it has the parent path as prefix. if (aDescendantPath.Length() < prefix.Length() || diff --git a/dom/filesystem/FileSystemUtils.h b/dom/filesystem/FileSystemUtils.h index cadead05abea..a2572bbf1775 100644 --- a/dom/filesystem/FileSystemUtils.h +++ b/dom/filesystem/FileSystemUtils.h @@ -12,7 +12,8 @@ namespace mozilla { namespace dom { -#define FILESYSTEM_DOM_PATH_SEPARATOR "/" +#define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/" +#define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/' /* * This class is for error handling. @@ -46,8 +47,6 @@ public: */ static bool IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath); - - static const char16_t kSeparatorChar = char16_t('/'); }; } // namespace dom diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index ee65e54324a9..f80a3b7eea36 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -343,41 +343,6 @@ GetDirectoryListingTask::HandlerCallback() for (unsigned i = 0; i < count; i++) { if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) { -#ifdef DEBUG - if (XRE_IsParentProcess()) { - nsCOMPtr file; - nsresult rv = NS_NewLocalFile(mTargetData[i].mDirectoryPath, false, - getter_AddRefs(file)); - if (NS_WARN_IF(NS_FAILED(rv))) { - mPromise->MaybeReject(rv); - mPromise = nullptr; - return; - } - - bool exist; - rv = file->Exists(&exist); - if (NS_WARN_IF(NS_FAILED(rv))) { - mPromise->MaybeReject(rv); - mPromise = nullptr; - return; - } - - // We cannot assert here because the file can be done in the meantime. - NS_ASSERTION(exist, "The file doesn't exist anymore?!?"); - - nsAutoString normalizedLocalRootPath; - FileSystemUtils::NormalizedPathToLocalPath(mFileSystem->GetLocalRootPath(), - normalizedLocalRootPath); - - nsAutoString directoryPath; - FileSystemUtils::NormalizedPathToLocalPath(mTargetData[i].mDirectoryPath, - directoryPath); - - MOZ_ASSERT(FileSystemUtils::IsDescendantPath(normalizedLocalRootPath, - directoryPath)); - } -#endif - nsCOMPtr directoryPath; NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath); nsresult rv = NS_NewNativeLocalFile(path, true, @@ -388,6 +353,19 @@ GetDirectoryListingTask::HandlerCallback() return; } +#ifdef DEBUG + nsCOMPtr rootPath; + rv = NS_NewLocalFile(mFileSystem->GetLocalRootPath(), false, + getter_AddRefs(rootPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + mPromise = nullptr; + return; + } + + MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath)); +#endif + RefPtr directory = Directory::Create(mFileSystem->GetWindow(), directoryPath, Directory::eNotDOMRootDirectory, diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index 8fc9e88cf6fe..9ce96b0e0f04 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -51,7 +51,7 @@ OSFileSystem::GetWindow() const void OSFileSystem::GetRootName(nsAString& aRetval) const { - return aRetval.AssignLiteral("/"); + aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); } bool From 548dc7f03a7bc0f55dd0d108958ef43f7547b21d Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 07/28] Bug 1173320 - patch 4/8 - Directory in FileList, r=smaug --- dom/base/FileList.cpp | 75 ++++++++++- dom/base/FileList.h | 42 +++--- dom/base/StructuredCloneHolder.cpp | 125 ++++++++++++------ dom/base/test/test_postMessages.html | 39 ++++-- dom/events/DataTransfer.cpp | 18 +-- dom/filesystem/CreateDirectoryTask.cpp | 4 +- dom/filesystem/CreateFileTask.cpp | 5 +- dom/filesystem/DeviceStorageFileSystem.cpp | 4 +- dom/filesystem/DeviceStorageFileSystem.h | 4 +- dom/filesystem/Directory.cpp | 20 +-- dom/filesystem/Directory.h | 14 +- dom/filesystem/FileSystemBase.cpp | 4 +- dom/filesystem/FileSystemBase.h | 6 +- .../FileSystemPermissionRequest.cpp | 4 +- dom/filesystem/GetDirectoryListingTask.cpp | 6 +- dom/filesystem/GetFileOrDirectoryTask.cpp | 7 +- dom/filesystem/OSFileSystem.cpp | 25 ++-- dom/filesystem/OSFileSystem.h | 8 +- dom/filesystem/RemoveTask.cpp | 4 +- dom/html/HTMLInputElement.cpp | 17 +-- dom/html/HTMLInputElement.h | 2 +- dom/webidl/FileList.webidl | 4 +- embedding/browser/nsDocShellTreeOwner.cpp | 4 +- 23 files changed, 285 insertions(+), 156 deletions(-) diff --git a/dom/base/FileList.cpp b/dom/base/FileList.cpp index 5b807e7deac3..854c13ebdacd 100644 --- a/dom/base/FileList.cpp +++ b/dom/base/FileList.cpp @@ -4,6 +4,7 @@ * 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 "mozilla/dom/Directory.h" #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" #include "mozilla/dom/File.h" @@ -11,7 +12,7 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFilesOrDirectories, mParent) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -28,6 +29,20 @@ FileList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto); } +void +FileList::Append(File* aFile) +{ + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsFile() = aFile; +} + +void +FileList::Append(Directory* aDirectory) +{ + OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); + element->SetAsDirectory() = aDirectory; +} + NS_IMETHODIMP FileList::GetLength(uint32_t* aLength) { @@ -37,12 +52,64 @@ FileList::GetLength(uint32_t* aLength) } NS_IMETHODIMP -FileList::Item(uint32_t aIndex, nsISupports** aFile) +FileList::Item(uint32_t aIndex, nsISupports** aValue) { - nsCOMPtr file = Item(aIndex); - file.forget(aFile); + if (aIndex >= mFilesOrDirectories.Length()) { + return NS_ERROR_FAILURE; + } + + if (mFilesOrDirectories[aIndex].IsFile()) { + nsCOMPtr file = mFilesOrDirectories[aIndex].GetAsFile(); + file.forget(aValue); + return NS_OK; + } + + MOZ_ASSERT(mFilesOrDirectories[aIndex].IsDirectory()); + RefPtr directory = mFilesOrDirectories[aIndex].GetAsDirectory(); + directory.forget(aValue); return NS_OK; } +void +FileList::Item(uint32_t aIndex, Nullable& aValue, + ErrorResult& aRv) const +{ + if (aIndex >= mFilesOrDirectories.Length()) { + aValue.SetNull(); + return; + } + + aValue.SetValue(mFilesOrDirectories[aIndex]); +} + +void +FileList::IndexedGetter(uint32_t aIndex, bool& aFound, + Nullable& aFileOrDirectory, + ErrorResult& aRv) const +{ + aFound = aIndex < mFilesOrDirectories.Length(); + Item(aIndex, aFileOrDirectory, aRv); +} + +void +FileList::ToSequence(Sequence& aSequence, + ErrorResult& aRv) const +{ + MOZ_ASSERT(aSequence.IsEmpty()); + if (mFilesOrDirectories.IsEmpty()) { + return; + } + + if (!aSequence.SetLength(mFilesOrDirectories.Length(), + mozilla::fallible_t())) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) { + aSequence[i] = mFilesOrDirectories[i]; + } +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/FileList.h b/dom/base/FileList.h index 3e262153c253..ca63f57a5fc0 100644 --- a/dom/base/FileList.h +++ b/dom/base/FileList.h @@ -8,6 +8,7 @@ #define mozilla_dom_FileList_h #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/UnionTypes.h" #include "nsCycleCollectionParticipant.h" #include "nsIDOMFileList.h" #include "nsWrapperCache.h" @@ -39,15 +40,13 @@ public: return mParent; } - bool Append(File* aFile) - { - return mFiles.AppendElement(aFile); - } + void Append(File* aFile); + void Append(Directory* aDirectory); bool Remove(uint32_t aIndex) { - if (aIndex < mFiles.Length()) { - mFiles.RemoveElementAt(aIndex); + if (aIndex < mFilesOrDirectories.Length()) { + mFilesOrDirectories.RemoveElementAt(aIndex); return true; } @@ -56,7 +55,7 @@ public: void Clear() { - return mFiles.Clear(); + return mFilesOrDirectories.Clear(); } static FileList* FromSupports(nsISupports* aSupports) @@ -76,29 +75,32 @@ public: return static_cast(aSupports); } - File* Item(uint32_t aIndex) + const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const { - return mFiles.SafeElementAt(aIndex); + MOZ_ASSERT(aIndex < Length()); + return mFilesOrDirectories[aIndex]; } - File* IndexedGetter(uint32_t aIndex, bool& aFound) + void Item(uint32_t aIndex, + Nullable& aFileOrDirectory, + ErrorResult& aRv) const; + + void IndexedGetter(uint32_t aIndex, bool& aFound, + Nullable& aFileOrDirectory, + ErrorResult& aRv) const; + + uint32_t Length() const { - aFound = aIndex < mFiles.Length(); - if (!aFound) { - return nullptr; - } - return mFiles.ElementAt(aIndex); + return mFilesOrDirectories.Length(); } - uint32_t Length() - { - return mFiles.Length(); - } + void ToSequence(Sequence& aSequence, + ErrorResult& aRv) const; private: ~FileList() {} - nsTArray> mFiles; + nsTArray mFilesOrDirectories; nsCOMPtr mParent; }; diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index f0da5380931b..9b734368edaa 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -10,6 +10,7 @@ #include "mozilla/AutoRestore.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/CryptoKey.h" +#include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "mozilla/dom/FileListBinding.h" @@ -723,36 +724,59 @@ ReadFileList(JSContext* aCx, { RefPtr fileList = new FileList(aHolder->ParentDuringRead()); - uint32_t tag, offset; - // Offset is the index of the blobImpl from which we can find the blobImpl - // for this FileList. - if (!JS_ReadUint32Pair(aReader, &tag, &offset)) { - return nullptr; - } - - MOZ_ASSERT(tag == 0); - - // |aCount| is the number of BlobImpls to use from the |offset|. + // |aCount| is the number of Files or Directory for this FileList. for (uint32_t i = 0; i < aCount; ++i) { - uint32_t index = offset + i; - MOZ_ASSERT(index < aHolder->BlobImpls().Length()); - - RefPtr blobImpl = aHolder->BlobImpls()[index]; - MOZ_ASSERT(blobImpl->IsFile()); - - ErrorResult rv; - blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); + uint32_t tagOrDirectoryType, indexOrLengthOfString; + if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType, + &indexOrLengthOfString)) { return nullptr; } - MOZ_ASSERT(blobImpl); + MOZ_ASSERT(tagOrDirectoryType == SCTAG_DOM_BLOB || + tagOrDirectoryType == Directory::eDOMRootDirectory || + tagOrDirectoryType == Directory::eNotDOMRootDirectory); - RefPtr file = File::Create(aHolder->ParentDuringRead(), blobImpl); - if (!fileList->Append(file)) { + if (tagOrDirectoryType == SCTAG_DOM_BLOB) { + MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length()); + + RefPtr blobImpl = + aHolder->BlobImpls()[indexOrLengthOfString]; + MOZ_ASSERT(blobImpl->IsFile()); + + ErrorResult rv; + blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return nullptr; + } + + RefPtr file = + File::Create(aHolder->ParentDuringRead(), blobImpl); + MOZ_ASSERT(file); + + fileList->Append(file); + continue; + } + + nsAutoString path; + path.SetLength(indexOrLengthOfString); + size_t charSize = sizeof(nsString::char_type); + if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(), + indexOrLengthOfString * charSize)) { return nullptr; } + + nsCOMPtr file; + nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + RefPtr directory = + Directory::Create(aHolder->ParentDuringRead(), file, + (Directory::DirectoryType) tagOrDirectoryType); + fileList->Append(directory); } if (!ToJSValue(aCx, fileList, &val)) { @@ -765,7 +789,13 @@ ReadFileList(JSContext* aCx, // The format of the FileList serialization is: // - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList -// - pair of ints: 0, The offset of the BlobImpl array +// - for each element of the FileList: +// - if it's a blob: +// - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array +// mBlobImplArray. +// - else: +// - pair of ints: 0/1 is root, string length +// - value string bool WriteFileList(JSStructuredCloneWriter* aWriter, FileList* aFileList, @@ -775,13 +805,8 @@ WriteFileList(JSStructuredCloneWriter* aWriter, MOZ_ASSERT(aFileList); MOZ_ASSERT(aHolder); - // A FileList is serialized writing the X number of elements and the offset - // from mBlobImplArray. The Read will take X elements from mBlobImplArray - // starting from the offset. if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, - aFileList->Length()) || - !JS_WriteUint32Pair(aWriter, 0, - aHolder->BlobImpls().Length())) { + aFileList->Length())) { return false; } @@ -789,18 +814,39 @@ WriteFileList(JSStructuredCloneWriter* aWriter, nsTArray> blobImpls; for (uint32_t i = 0; i < aFileList->Length(); ++i) { - RefPtr blobImpl = - EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - return false; + const OwningFileOrDirectory& data = aFileList->UnsafeItem(i); + + if (data.IsFile()) { + RefPtr blobImpl = + EnsureBlobForBackgroundManager(data.GetAsFile()->Impl(), nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + + if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, + aHolder->BlobImpls().Length())) { + return false; + } + + aHolder->BlobImpls().AppendElement(blobImpl); + continue; } - MOZ_ASSERT(blobImpl); - blobImpls.AppendElement(blobImpl); + MOZ_ASSERT(data.IsDirectory()); + + nsAutoString path; + data.GetAsDirectory()->GetFullRealPath(path); + + size_t charSize = sizeof(nsString::char_type); + if (!JS_WriteUint32Pair(aWriter, + (uint32_t)data.GetAsDirectory()->Type(), + path.Length()) || + !JS_WriteBytes(aWriter, path.get(), path.Length() * charSize)) { + return false; + } } - aHolder->BlobImpls().AppendElements(blobImpls); return true; } @@ -962,6 +1008,7 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, } if (aTag == SCTAG_DOM_FILELIST) { + MOZ_ASSERT(mSupportedContext == SameProcessSameThread); return ReadFileList(aCx, aReader, aIndex, this); } @@ -1002,7 +1049,7 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx, } // See if this is a FileList object. - { + if (mSupportedContext == SameProcessSameThread) { FileList* fileList = nullptr; if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) { return WriteFileList(aWriter, fileList, this); diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html index e2d146eac9f5..f90a80660d52 100644 --- a/dom/base/test/test_postMessages.html +++ b/dom/base/test/test_postMessages.html @@ -61,15 +61,15 @@ function compare(a, b) { } var clonableObjects = [ - 'hello world', - 123, - null, - true, - new Date(), - [ 1, 'test', true, new Date() ], - { a: true, b: null, c: new Date(), d: [ true, false, {} ] }, - new Blob([123], { type: 'plain/text' }), - new ImageData(2, 2), + { crossThreads: true, data: 'hello world' }, + { crossThreads: true, data: 123 }, + { crossThreads: true, data: null }, + { crossThreads: true, data: true }, + { crossThreads: true, data: new Date() }, + { crossThreads: true, data: [ 1, 'test', true, new Date() ] }, + { crossThreads: true, data: { a: true, b: null, c: new Date(), d: [ true, false, {} ] } }, + { crossThreads: true, data: new Blob([123], { type: 'plain/text' }) }, + { crossThreads: true, data: new ImageData(2, 2) }, ]; function create_fileList() { @@ -84,7 +84,7 @@ function create_fileList() { var domFile = fileList.files[0]; is(domFile.name, "prefs.js", "fileName should be prefs.js"); - clonableObjects.push(fileList.files); + clonableObjects.push({ crossThreads: false, data: fileList.files }); script.destroy(); next(); } @@ -113,8 +113,16 @@ function runTests(obj) { } var object = clonableObjects[clonableObjectsId++]; - obj.send(object, []).then(function(received) { - compare(received.data, object); + + // If this test requires a cross-thread structured clone algorithm, maybe + // we have to skip it. + if (!object.crossThread && obj.crossThread) { + runClonableTest(); + return; + } + + obj.send(object.data, []).then(function(received) { + compare(received.data, object.data); runClonableTest(); }); } @@ -203,6 +211,7 @@ function test_windowToWindow() { runTests({ clonableObjects: true, transferableObjects: true, + crossThread: false, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; @@ -256,6 +265,7 @@ function test_windowToIframeURL(url) { runTests({ clonableObjects: true, transferableObjects: true, + crossThread: false, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; @@ -303,6 +313,7 @@ function test_workers() { runTests({ clonableObjects: true, transferableObjects: true, + crossThread: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; @@ -346,6 +357,7 @@ function test_broadcastChannel() { runTests({ clonableObjects: true, transferableObjects: false, + crossThread: true, send: function(what, ports) { return new Promise(function(r, rr) { if (ports.length) { @@ -391,6 +403,7 @@ function test_broadcastChannel_inWorkers() { runTests({ clonableObjects: true, transferableObjects: false, + crossThread: true, send: function(what, ports) { return new Promise(function(r, rr) { if (ports.length) { @@ -432,6 +445,7 @@ function test_messagePort() { runTests({ clonableObjects: true, transferableObjects: true, + crossThread: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; @@ -477,6 +491,7 @@ function test_messagePort_inWorkers() { runTests({ clonableObjects: true, transferableObjects: true, + crossThread: true, send: function(what, ports) { return new Promise(function(r, rr) { resolve = r; diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index 17fb01005aad..37ed8c33683e 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -340,10 +340,7 @@ DataTransfer::GetFileListInternal(ErrorResult& aRv, MOZ_ASSERT(domFile); } - if (!mFileList->Append(domFile)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } + mFileList->Append(domFile); } } @@ -864,16 +861,9 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv) } Sequence filesAndDirsSeq; - - if (mFileList && mFileList->Length()) { - if (!filesAndDirsSeq.SetLength(mFileList->Length(), mozilla::fallible_t())) { - p->MaybeReject(NS_ERROR_OUT_OF_MEMORY); - return p.forget(); - } - - for (uint32_t i = 0; i < mFileList->Length(); ++i) { - filesAndDirsSeq[i].SetAsFile() = mFileList->Item(i); - } + mFileList->ToSequence(filesAndDirsSeq, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; } p->MaybeResolve(filesAndDirsSeq); diff --git a/dom/filesystem/CreateDirectoryTask.cpp b/dom/filesystem/CreateDirectoryTask.cpp index 3cade3a232b0..57a1f432c408 100644 --- a/dom/filesystem/CreateDirectoryTask.cpp +++ b/dom/filesystem/CreateDirectoryTask.cpp @@ -30,7 +30,7 @@ CreateDirectoryTask::Create(FileSystemBase* aFileSystem, // aTargetPath can be null. In this case SetError will be called. nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); + do_QueryInterface(aFileSystem->GetParentObject()); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -182,7 +182,7 @@ CreateDirectoryTask::HandlerCallback() mPromise = nullptr; return; } - RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + RefPtr dir = Directory::Create(mFileSystem->GetParentObject(), mTargetPath, Directory::eNotDOMRootDirectory, mFileSystem); diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 6a0bfd12d553..3df2ac818bbc 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -57,7 +57,7 @@ CreateFileTask::Create(FileSystemBase* aFileSystem, task->mArrayData.SwapElements(aArrayData); nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); + do_QueryInterface(aFileSystem->GetParentObject()); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -351,7 +351,8 @@ CreateFileTask::HandlerCallback() return; } - RefPtr blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl); + RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), + mTargetBlobImpl); mPromise->MaybeResolve(blob); mPromise = nullptr; mBlobData = nullptr; diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index d96301db0329..8f25b3eaa5c0 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -83,8 +83,8 @@ DeviceStorageFileSystem::Shutdown() mShutdown = true; } -nsPIDOMWindowInner* -DeviceStorageFileSystem::GetWindow() const +nsISupports* +DeviceStorageFileSystem::GetParentObject() const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId); diff --git a/dom/filesystem/DeviceStorageFileSystem.h b/dom/filesystem/DeviceStorageFileSystem.h index c73b9099b181..e14202ae0a8e 100644 --- a/dom/filesystem/DeviceStorageFileSystem.h +++ b/dom/filesystem/DeviceStorageFileSystem.h @@ -30,8 +30,8 @@ public: virtual void Shutdown() override; - virtual nsPIDOMWindowInner* - GetWindow() const override; + virtual nsISupports* + GetParentObject() const override; virtual void GetRootName(nsAString& aRetval) const override; diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index 28d1ed170a46..aebcba1a0d2d 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -82,7 +82,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory) tmp->mFileSystem->Unlink(); tmp->mFileSystem = nullptr; } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -90,7 +90,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory) if (tmp->mFileSystem) { tmp->mFileSystem->Traverse(cb); } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -127,11 +127,11 @@ Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv) } /* static */ already_AddRefed -Directory::Create(nsPIDOMWindowInner* aWindow, nsIFile* aFile, +Directory::Create(nsISupports* aParent, nsIFile* aFile, DirectoryType aType, FileSystemBase* aFileSystem) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWindow); + MOZ_ASSERT(aParent); MOZ_ASSERT(aFile); #ifdef DEBUG @@ -141,15 +141,15 @@ Directory::Create(nsPIDOMWindowInner* aWindow, nsIFile* aFile, #endif RefPtr directory = - new Directory(aWindow, aFile, aType, aFileSystem); + new Directory(aParent, aFile, aType, aFileSystem); return directory.forget(); } -Directory::Directory(nsPIDOMWindowInner* aWindow, +Directory::Directory(nsISupports* aParent, nsIFile* aFile, DirectoryType aType, FileSystemBase* aFileSystem) - : mWindow(aWindow) + : mParent(aParent) , mFileSystem(aFileSystem) , mFile(aFile) , mType(aType) @@ -164,10 +164,10 @@ Directory::~Directory() { } -nsPIDOMWindowInner* +nsISupports* Directory::GetParentObject() const { - return mWindow; + return mParent; } JSObject* @@ -406,7 +406,7 @@ Directory::GetFileSystem(ErrorResult& aRv) } RefPtr fs = new OSFileSystem(path); - fs->Init(mWindow); + fs->Init(mParent); mFileSystem = fs; } diff --git a/dom/filesystem/Directory.h b/dom/filesystem/Directory.h index a71f02930302..5e6ac2deef62 100644 --- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -13,7 +13,6 @@ #include "mozilla/dom/File.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" -#include "nsPIDOMWindow.h" #include "nsWrapperCache.h" // Resolve the name collision of Microsoft's API name with macros defined in @@ -70,12 +69,12 @@ public: }; static already_AddRefed - Create(nsPIDOMWindowInner* aWindow, nsIFile* aDirectory, + Create(nsISupports* aParent, nsIFile* aDirectory, DirectoryType aType, FileSystemBase* aFileSystem = 0); // ========= Begin WebIDL bindings. =========== - nsPIDOMWindowInner* + nsISupports* GetParentObject() const; virtual JSObject* @@ -141,8 +140,13 @@ public: FileSystemBase* GetFileSystem(ErrorResult& aRv); + DirectoryType Type() const + { + return mType; + } + private: - Directory(nsPIDOMWindowInner* aWindow, + Directory(nsISupports* aParent, nsIFile* aFile, DirectoryType aType, FileSystemBase* aFileSystem = nullptr); ~Directory(); @@ -157,7 +161,7 @@ private: RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive, ErrorResult& aRv); - nsCOMPtr mWindow; + nsCOMPtr mParent; RefPtr mFileSystem; nsCOMPtr mFile; DirectoryType mType; diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index 2b1d52608ebc..b1147759acea 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -58,8 +58,8 @@ FileSystemBase::Shutdown() mShutdown = true; } -nsPIDOMWindowInner* -FileSystemBase::GetWindow() const +nsISupports* +FileSystemBase::GetParentObject() const { return nullptr; } diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index 6a715dee9741..a01c84cdd11c 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -10,8 +10,6 @@ #include "nsAutoPtr.h" #include "nsString.h" -class nsPIDOMWindowInner; - namespace mozilla { namespace dom { @@ -36,8 +34,8 @@ public: virtual void SerializeDOMPath(nsAString& aOutput) const = 0; - virtual nsPIDOMWindowInner* - GetWindow() const; + virtual nsISupports* + GetParentObject() const; /* * Get the virtual name of the root directory. This name will be exposed to diff --git a/dom/filesystem/FileSystemPermissionRequest.cpp b/dom/filesystem/FileSystemPermissionRequest.cpp index 363a9981f73c..488b0c14de2e 100644 --- a/dom/filesystem/FileSystemPermissionRequest.cpp +++ b/dom/filesystem/FileSystemPermissionRequest.cpp @@ -44,8 +44,8 @@ FileSystemPermissionRequest::FileSystemPermissionRequest( mPermissionType = filesystem->GetPermission(); - mWindow = filesystem->GetWindow(); - if (!mWindow) { + mWindow = do_QueryInterface(filesystem->GetParentObject()); + if (NS_WARN_IF(!mWindow)) { return; } diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index f80a3b7eea36..04406aedbf3f 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -36,7 +36,7 @@ GetDirectoryListingTask::Create(FileSystemBase* aFileSystem, // aTargetPath can be null. In this case SetError will be called. nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); + do_QueryInterface(aFileSystem->GetParentObject()); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -366,7 +366,7 @@ GetDirectoryListingTask::HandlerCallback() MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath)); #endif - RefPtr directory = Directory::Create(mFileSystem->GetWindow(), + RefPtr directory = Directory::Create(mFileSystem->GetParentObject(), directoryPath, Directory::eNotDOMRootDirectory, mFileSystem); @@ -378,7 +378,7 @@ GetDirectoryListingTask::HandlerCallback() } else { MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl); listing[i].SetAsFile() = - File::Create(mFileSystem->GetWindow(), mTargetData[i].mBlobImpl); + File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl); } } diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 5056590bae16..ccb4b851b453 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -35,7 +35,7 @@ GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem, // aTargetPath can be null. In this case SetError will be called. nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); + do_QueryInterface(aFileSystem->GetParentObject()); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -263,7 +263,7 @@ GetFileOrDirectoryTask::HandlerCallback() } if (mIsDirectory) { - RefPtr dir = Directory::Create(mFileSystem->GetWindow(), + RefPtr dir = Directory::Create(mFileSystem->GetParentObject(), mTargetPath, mType, mFileSystem); @@ -274,7 +274,8 @@ GetFileOrDirectoryTask::HandlerCallback() return; } - RefPtr blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl); + RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), + mTargetBlobImpl); mPromise->MaybeResolve(blob); mPromise = nullptr; } diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index 9ce96b0e0f04..1a7f947280f3 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -9,10 +9,10 @@ #include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileSystemUtils.h" +#include "nsIGlobalObject.h" #include "nsCOMPtr.h" #include "nsDebug.h" #include "nsIFile.h" -#include "nsPIDOMWindow.h" namespace mozilla { namespace dom { @@ -33,19 +33,24 @@ OSFileSystem::OSFileSystem(const nsAString& aRootDir) } void -OSFileSystem::Init(nsPIDOMWindowInner* aWindow) +OSFileSystem::Init(nsISupports* aParent) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - MOZ_ASSERT(!mWindow, "No duple Init() calls"); - MOZ_ASSERT(aWindow); - mWindow = aWindow; + MOZ_ASSERT(!mParent, "No duple Init() calls"); + MOZ_ASSERT(aParent); + mParent = aParent; + +#ifdef DEBUG + nsCOMPtr obj = do_QueryInterface(aParent); + MOZ_ASSERT(obj); +#endif } -nsPIDOMWindowInner* -OSFileSystem::GetWindow() const +nsISupports* +OSFileSystem::GetParentObject() const { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); - return mWindow; + return mParent; } void @@ -77,14 +82,14 @@ OSFileSystem::IsSafeDirectory(Directory* aDir) const void OSFileSystem::Unlink() { - mWindow = nullptr; + mParent = nullptr; } void OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb) { OSFileSystem* tmp = this; - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent); } void diff --git a/dom/filesystem/OSFileSystem.h b/dom/filesystem/OSFileSystem.h index 476611bcb3af..f3c329a22ea4 100644 --- a/dom/filesystem/OSFileSystem.h +++ b/dom/filesystem/OSFileSystem.h @@ -18,12 +18,12 @@ public: explicit OSFileSystem(const nsAString& aRootDir); void - Init(nsPIDOMWindowInner* aWindow); + Init(nsISupports* aParent); // Overrides FileSystemBase - virtual nsPIDOMWindowInner* - GetWindow() const override; + virtual nsISupports* + GetParentObject() const override; virtual void GetRootName(nsAString& aRetval) const override; @@ -44,7 +44,7 @@ public: private: virtual ~OSFileSystem() {} - nsCOMPtr mWindow; + nsCOMPtr mParent; }; } // namespace dom diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 8b806b72eee6..8bebeef1a2dc 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -36,7 +36,7 @@ RemoveTask::Create(FileSystemBase* aFileSystem, // aTargetPath can be null. In this case SetError will be called. nsCOMPtr globalObject = - do_QueryInterface(aFileSystem->GetWindow()); + do_QueryInterface(aFileSystem->GetParentObject()); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -148,7 +148,7 @@ RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath, param.recursive() = mRecursive; if (mTargetBlobImpl) { - RefPtr blob = Blob::Create(mFileSystem->GetWindow(), + RefPtr blob = Blob::Create(mFileSystem->GetParentObject(), mTargetBlobImpl); BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob); diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index e5d9ba798f97..712c69eb5ccb 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -11,8 +11,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/dom/Date.h" #include "mozilla/dom/Directory.h" -#include "mozilla/dom/FileSystemUtils.h" -#include "mozilla/dom/OSFileSystem.h" #include "nsAttrValueInlines.h" #include "nsCRTGlue.h" @@ -2525,10 +2523,8 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, uint32_t listLength; aFiles->GetLength(&listLength); for (uint32_t i = 0; i < listLength; i++) { - RefPtr file = files->Item(i); - OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); - element->SetAsFile() = file; + *element = files->UnsafeItem(i); } } @@ -2632,7 +2628,7 @@ HTMLInputElement::HandleNumberControlSpin(void* aData) } } -nsresult +void HTMLInputElement::UpdateFileList() { if (mFileList) { @@ -2642,16 +2638,17 @@ HTMLInputElement::UpdateFileList() GetFilesOrDirectoriesInternal(); for (uint32_t i = 0; i < array.Length(); ++i) { - if (array[i].IsFile() && !mFileList->Append(array[i].GetAsFile())) { - return NS_ERROR_FAILURE; + if (array[i].IsFile()) { + mFileList->Append(array[i].GetAsFile()); + } else { + MOZ_ASSERT(array[i].IsDirectory()); + mFileList->Append(array[i].GetAsDirectory()); } } } // Make sure we (lazily) create a new Promise for GetFilesAndDirectories: mFilesAndDirectoriesPromise = nullptr; - - return NS_OK; } nsresult diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 1988ce4b21d2..7ba264e4302c 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -924,7 +924,7 @@ protected: /** * Update mFileList with the currently selected file. */ - nsresult UpdateFileList(); + void UpdateFileList(); /** * Called after calling one of the SetFilesOrDirectories() functions. diff --git a/dom/webidl/FileList.webidl b/dom/webidl/FileList.webidl index 84a56be1bf90..0dc10401c390 100644 --- a/dom/webidl/FileList.webidl +++ b/dom/webidl/FileList.webidl @@ -11,6 +11,8 @@ */ interface FileList { - getter File? item(unsigned long index); + [Throws] + getter (File or Directory)? item(unsigned long index); + readonly attribute unsigned long length; }; diff --git a/embedding/browser/nsDocShellTreeOwner.cpp b/embedding/browser/nsDocShellTreeOwner.cpp index 43d214d2b958..44e11f05d96b 100644 --- a/embedding/browser/nsDocShellTreeOwner.cpp +++ b/embedding/browser/nsDocShellTreeOwner.cpp @@ -1186,7 +1186,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText, NS_ENSURE_SUCCESS(rv, rv); } else { FileList* fl = static_cast(fileList.get()); - fl->Item(0)->GetName(outText); + fl->UnsafeItem(0).GetAsFile()->GetName(outText); // For UX and performance (jank) reasons we cap the number of // files that we list in the tooltip to 20 plus a "and xxx more" @@ -1195,7 +1195,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText, uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT); for (uint32_t i = 1; i < count; ++i) { nsString fileName; - fl->Item(i)->GetName(fileName); + fl->UnsafeItem(i).GetAsFile()->GetName(fileName); outText.Append(NS_LITERAL_STRING("\n")); outText.Append(fileName); } From 69278a33e5b56776ce83046177ad53fd093c34f6 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 08/28] Bug 1173320 - patch 5/8 - Cleanup manual string path management, r=smaug --- dom/devicestorage/DeviceStorage.h | 6 +- dom/devicestorage/nsDeviceStorage.cpp | 94 ++++++++++++++----- dom/filesystem/CreateFileTask.cpp | 7 +- dom/filesystem/DeviceStorageFileSystem.cpp | 19 ++-- dom/filesystem/Directory.cpp | 10 +- dom/filesystem/FileSystemBase.cpp | 15 --- dom/filesystem/FileSystemBase.h | 6 -- .../FileSystemPermissionRequest.cpp | 6 +- dom/filesystem/FileSystemTaskBase.cpp | 2 +- dom/filesystem/FileSystemUtils.cpp | 59 +----------- dom/filesystem/FileSystemUtils.h | 22 +---- dom/filesystem/GetDirectoryListingTask.cpp | 18 +--- dom/filesystem/GetDirectoryListingTask.h | 4 - dom/filesystem/OSFileSystem.cpp | 2 - 14 files changed, 104 insertions(+), 166 deletions(-) diff --git a/dom/devicestorage/DeviceStorage.h b/dom/devicestorage/DeviceStorage.h index 4bc102e3c7e2..d36ad55d93b1 100644 --- a/dom/devicestorage/DeviceStorage.h +++ b/dom/devicestorage/DeviceStorage.h @@ -101,8 +101,9 @@ public: // we want to make sure that the names of file can't reach // outside of the type of storage the user asked for. - bool IsSafePath(); - bool IsSafePath(const nsAString& aPath); + bool IsSafePath() const; + bool ValidateAndSplitPath(const nsAString& aPath, + nsTArray* aParts = nullptr) const; void Dump(const char* label); @@ -137,7 +138,6 @@ public: private: ~DeviceStorageFile() {} void Init(); - void NormalizeFilePath(); void AppendRelativePath(const nsAString& aPath); void AccumDirectoryUsage(nsIFile* aFile, uint64_t* aPicturesSoFar, diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index f77eb806d40b..fdc432fef3a2 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -30,6 +30,7 @@ #include "nsArrayUtils.h" #include "nsAutoPtr.h" +#include "nsCharSeparatedTokenizer.h" #include "nsGlobalWindow.h" #include "nsServiceManagerUtils.h" #include "nsIFile.h" @@ -76,10 +77,35 @@ using namespace mozilla::dom; using namespace mozilla::dom::devicestorage; using namespace mozilla::ipc; -namespace mozilla { +namespace mozilla +{ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close); } // namespace mozilla +namespace { + +void +NormalizeFilePath(nsAString& aPath) +{ +#if defined(XP_WIN) + char16_t* cur = aPath.BeginWriting(); + char16_t* end = aPath.EndWriting(); + for (; cur < end; ++cur) { + if (char16_t('\\') == *cur) { + *cur = FILESYSTEM_DOM_PATH_SEPARATOR_CHAR; + } + } +#endif +} + +bool +TokenizerIgnoreNothing(char16_t /* aChar */) +{ + return false; +} + +} // anonymous namespace + StaticAutoPtr DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache; @@ -510,7 +536,8 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, if (!mPath.EqualsLiteral("")) { AppendRelativePath(mPath); } - NormalizeFilePath(); + + NormalizeFilePath(mPath); } DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, @@ -525,7 +552,7 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, { Init(); AppendRelativePath(aPath); - NormalizeFilePath(); + NormalizeFilePath(mPath); } DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, @@ -734,7 +761,7 @@ DeviceStorageFile::CreateUnique(const nsAString& aStorageType, void DeviceStorageFile::SetPath(const nsAString& aPath) { mPath.Assign(aPath); - NormalizeFilePath(); + NormalizeFilePath(mPath); } void @@ -745,13 +772,14 @@ DeviceStorageFile::SetEditable(bool aEditable) { // we want to make sure that the names of file can't reach // outside of the type of storage the user asked for. bool -DeviceStorageFile::IsSafePath() +DeviceStorageFile::IsSafePath() const { - return IsSafePath(mRootDir) && IsSafePath(mPath); + return ValidateAndSplitPath(mRootDir) && ValidateAndSplitPath(mPath); } bool -DeviceStorageFile::IsSafePath(const nsAString& aPath) +DeviceStorageFile::ValidateAndSplitPath(const nsAString& aPath, + nsTArray* aParts) const { nsAString::const_iterator start, end; aPath.BeginReading(start); @@ -764,33 +792,43 @@ DeviceStorageFile::IsSafePath(const nsAString& aPath) StringBeginsWith(aPath, tildeSlash)) { NS_WARNING("Path name starts with tilde!"); return false; - } - // split on /. if any token is "", ., or .., return false. - NS_ConvertUTF16toUTF8 cname(aPath); - char* buffer = cname.BeginWriting(); - const char* token; + } - while ((token = nsCRT::strtok(buffer, "/", &buffer))) { - if (PL_strcmp(token, "") == 0 || - PL_strcmp(token, ".") == 0 || - PL_strcmp(token, "..") == 0 ) { + NS_NAMED_LITERAL_STRING(kCurrentDir, "."); + NS_NAMED_LITERAL_STRING(kParentDir, ".."); + + // Split path and check each path component. + nsCharSeparatedTokenizerTemplate + tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR); + + while (tokenizer.hasMoreTokens()) { + nsDependentSubstring pathComponent = tokenizer.nextToken(); + // The path containing empty components, such as "foo//bar", is invalid. + // We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar", + // to walk up the directory. + if (pathComponent.IsEmpty() || + pathComponent.Equals(kCurrentDir) || + pathComponent.Equals(kParentDir)) { return false; } + + if (aParts) { + aParts->AppendElement(pathComponent); + } } return true; } void -DeviceStorageFile::NormalizeFilePath() { - FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath); -} - -void -DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { +DeviceStorageFile::AppendRelativePath(const nsAString& aPath) +{ if (!mFile) { return; } - if (!IsSafePath(aPath)) { + + nsTArray parts; + + if (!ValidateAndSplitPath(aPath, &parts)) { // All of the APIs (in the child) do checks to verify that the path is // valid and return PERMISSION_DENIED if a non-safe path is entered. // This check is done in the parent and prevents a compromised @@ -800,9 +838,13 @@ DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get()); return; } - nsString localPath; - FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath); - mFile->AppendRelativePath(localPath); + + for (uint32_t i = 0; i < parts.Length(); ++i) { + nsresult rv = mFile->AppendRelativePath(parts[i]); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + } } nsresult diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 3df2ac818bbc..42766305721c 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -168,8 +168,8 @@ CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath, param.replace() = mReplace; if (mBlobData) { - BlobChild* actor - = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData); + BlobChild* actor = + ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData); if (actor) { param.data() = actor; } @@ -205,7 +205,7 @@ CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue, nsresult CreateFileTask::Work() { - class AutoClose + class MOZ_RAII AutoClose final { public: explicit AutoClose(nsIOutputStream* aStream) @@ -218,6 +218,7 @@ CreateFileTask::Work() { mStream->Close(); } + private: nsCOMPtr mStream; }; diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 8f25b3eaa5c0..06437115e7ad 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -21,9 +21,8 @@ namespace mozilla { namespace dom { -DeviceStorageFileSystem::DeviceStorageFileSystem( - const nsAString& aStorageType, - const nsAString& aStorageName) +DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType, + const nsAString& aStorageName) : mWindowId(0) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); @@ -51,9 +50,6 @@ DeviceStorageFileSystem::DeviceStorageFileSystem( return; } - FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath, - mNormalizedLocalRootPath); - // DeviceStorageTypeChecker is a singleton object and must be initialized on // the main thread. We initialize it here so that we can use it on the worker // thread. @@ -105,12 +101,15 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const "Should be on parent process!"); MOZ_ASSERT(aFile); - // Check if this file belongs to this storage. - nsAutoString path; - if (NS_FAILED(aFile->GetPath(path))) { + nsCOMPtr rootPath; + nsresult rv = NS_NewLocalFile(GetLocalRootPath(), false, + getter_AddRefs(rootPath)); + if (NS_WARN_IF(NS_FAILED(rv))) { return false; } - if (!LocalPathToRealPath(path, path)) { + + // Check if this file belongs to this storage. + if (NS_WARN_IF(!FileSystemUtils::IsDescendantPath(rootPath, aFile))) { return false; } diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index aebcba1a0d2d..fd46ad15a3ef 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -37,6 +37,12 @@ namespace dom { namespace { +bool +TokenizerIgnoreNothing(char16_t /* aChar */) +{ + return false; +} + bool IsValidRelativeDOMPath(const nsString& aPath, nsTArray& aParts) { @@ -55,7 +61,9 @@ IsValidRelativeDOMPath(const nsString& aPath, nsTArray& aParts) NS_NAMED_LITERAL_STRING(kParentDir, ".."); // Split path and check each path component. - nsCharSeparatedTokenizer tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR); + nsCharSeparatedTokenizerTemplate + tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR); + while (tokenizer.hasMoreTokens()) { nsDependentSubstring pathComponent = tokenizer.nextToken(); // The path containing empty components, such as "foo//bar", is invalid. diff --git a/dom/filesystem/FileSystemBase.cpp b/dom/filesystem/FileSystemBase.cpp index b1147759acea..3d6e4529ae39 100644 --- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -99,20 +99,5 @@ FileSystemBase::IsSafeDirectory(Directory* aDir) const return false; } -bool -FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath, - nsAString& aRealPath) const -{ - nsAutoString path; - FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path); - if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) { - aRealPath.Truncate(); - return false; - } - - aRealPath = Substring(path, mNormalizedLocalRootPath.Length()); - return true; -} - } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index a01c84cdd11c..eb2c782baa92 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -87,18 +87,12 @@ public: protected: virtual ~FileSystemBase(); - bool - LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const; - // The local path of the root (i.e. the OS path, with OS path separators, of // the OS directory that acts as the root of this OSFileSystem). // Only available in the parent process. // In the child process, we don't use it and its value should be empty. nsString mLocalRootPath; - // The same, but with path separators normalized to "/". - nsString mNormalizedLocalRootPath; - bool mShutdown; // The permission name required to access the file system. diff --git a/dom/filesystem/FileSystemPermissionRequest.cpp b/dom/filesystem/FileSystemPermissionRequest.cpp index 488b0c14de2e..b2c8a9d82769 100644 --- a/dom/filesystem/FileSystemPermissionRequest.cpp +++ b/dom/filesystem/FileSystemPermissionRequest.cpp @@ -15,7 +15,8 @@ namespace mozilla { namespace dom { -NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest) +NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable, + nsIContentPermissionRequest) // static void @@ -28,8 +29,7 @@ FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask) NS_DispatchToCurrentThread(request); } -FileSystemPermissionRequest::FileSystemPermissionRequest( - FileSystemTaskBase* aTask) +FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTask) : mTask(aTask) { MOZ_ASSERT(mTask, "aTask should not be null!"); diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index df11f3512c01..46cae6d86c0e 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -23,7 +23,7 @@ namespace dom { namespace { -class FileSystemReleaseRunnable : public nsRunnable +class FileSystemReleaseRunnable final : public nsRunnable { public: explicit FileSystemReleaseRunnable(RefPtr& aDoomed) diff --git a/dom/filesystem/FileSystemUtils.cpp b/dom/filesystem/FileSystemUtils.cpp index c5a8817f7cd5..20d2198b2926 100644 --- a/dom/filesystem/FileSystemUtils.cpp +++ b/dom/filesystem/FileSystemUtils.cpp @@ -6,67 +6,10 @@ #include "mozilla/dom/FileSystemUtils.h" -#include "nsXULAppAPI.h" - namespace mozilla { namespace dom { -// static -void -FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal, - nsAString& aNorm) -{ - nsString result; - result = aLocal; -#if defined(XP_WIN) - char16_t* cur = result.BeginWriting(); - char16_t* end = result.EndWriting(); - for (; cur < end; ++cur) { - if (char16_t('\\') == *cur) - *cur = char16_t('/'); - } -#endif - aNorm = result; -} - -// static -void -FileSystemUtils::NormalizedPathToLocalPath(const nsAString& aNorm, - nsAString& aLocal) -{ - nsString result; - result = aNorm; -#if defined(XP_WIN) - char16_t* cur = result.BeginWriting(); - char16_t* end = result.EndWriting(); - for (; cur < end; ++cur) { - if (char16_t('/') == *cur) - *cur = char16_t('\\'); - } -#endif - aLocal = result; -} - -// static -bool -FileSystemUtils::IsDescendantPath(const nsAString& aPath, - const nsAString& aDescendantPath) -{ - // The descendant path should begin with its ancestor path. - nsAutoString prefix; - prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); - - // Check the sub-directory path to see if it has the parent path as prefix. - if (aDescendantPath.Length() < prefix.Length() || - !StringBeginsWith(aDescendantPath, prefix)) { - return false; - } - - return true; -} - -// static -bool +/* static */ bool FileSystemUtils::IsDescendantPath(nsIFile* aFile, nsIFile* aDescendantFile) { diff --git a/dom/filesystem/FileSystemUtils.h b/dom/filesystem/FileSystemUtils.h index a2572bbf1775..6c1fbc456def 100644 --- a/dom/filesystem/FileSystemUtils.h +++ b/dom/filesystem/FileSystemUtils.h @@ -7,7 +7,7 @@ #ifndef mozilla_dom_FileSystemUtils_h #define mozilla_dom_FileSystemUtils_h -#include "nsString.h" +class nsIFile; namespace mozilla { namespace dom { @@ -22,31 +22,11 @@ namespace dom { class FileSystemUtils { public: - /* - * Convert the path separator to "/". - */ - static void - LocalPathToNormalizedPath(const nsAString& aLocal, nsAString& aNorm); - - /* - * Convert the normalized path separator "/" to the system dependent path - * separator, which is "/" on Mac and Linux, and "\" on Windows. - */ - static void - NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal); - /* * Return true if aDescendantPath is a descendant of aPath. */ static bool IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath); - - /* - * Return true if aDescendantPath is a descendant of aPath. Both aPath and - * aDescendantPath are absolute DOM path. - */ - static bool - IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath); }; } // namespace dom diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 04406aedbf3f..dd7d566c2af3 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -128,15 +128,6 @@ GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath, mFilters); } -void -GetDirectoryListingTask::CreateNormalizedRelativePath(const nsAString& aPath, - nsAString& aRelativePath) const -{ - uint32_t rootPathLen = mFileSystem->GetLocalRootPath().Length(); - FileSystemUtils::LocalPathToNormalizedPath( - Substring(aPath, rootPathLen, aPath.Length() - rootPathLen), aRelativePath); -} - FileSystemResponseValue GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const { @@ -366,10 +357,11 @@ GetDirectoryListingTask::HandlerCallback() MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath)); #endif - RefPtr directory = Directory::Create(mFileSystem->GetParentObject(), - directoryPath, - Directory::eNotDOMRootDirectory, - mFileSystem); + RefPtr directory = + Directory::Create(mFileSystem->GetParentObject(), + directoryPath, + Directory::eNotDOMRootDirectory, + mFileSystem); MOZ_ASSERT(directory); // Propogate mFilter onto sub-Directory object: diff --git a/dom/filesystem/GetDirectoryListingTask.h b/dom/filesystem/GetDirectoryListingTask.h index ddf774e96350..f0a3ae7fd55f 100644 --- a/dom/filesystem/GetDirectoryListingTask.h +++ b/dom/filesystem/GetDirectoryListingTask.h @@ -70,10 +70,6 @@ private: virtual void HandlerCallback() override; - void - CreateNormalizedRelativePath(const nsAString& aPath, - nsAString& aRelativePath) const; - RefPtr mPromise; nsCOMPtr mTargetPath; nsString mFilters; diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index 1a7f947280f3..79743e731a1e 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -20,8 +20,6 @@ namespace dom { OSFileSystem::OSFileSystem(const nsAString& aRootDir) { mLocalRootPath = aRootDir; - FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath, - mNormalizedLocalRootPath); // Non-mobile devices don't have the concept of separate permissions to // access different parts of devices storage like Pictures, or Videos, etc. From 5f7cc5cffa26801e48245e6cc96dbc0cbcc3886c Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:10 +0100 Subject: [PATCH 09/28] Bug 1173320 - patch 6/8 - Make FileList clonable to workers if it doesn't contain Directories, r=smaug --- dom/base/FileList.cpp | 12 ++++++++++++ dom/base/FileList.h | 2 ++ dom/base/StructuredCloneHolder.cpp | 7 ++++--- dom/base/test/test_postMessages.html | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/dom/base/FileList.cpp b/dom/base/FileList.cpp index 854c13ebdacd..4018dc1e53a7 100644 --- a/dom/base/FileList.cpp +++ b/dom/base/FileList.cpp @@ -111,5 +111,17 @@ FileList::ToSequence(Sequence& aSequence, } } +bool +FileList::ClonableToDifferentThreadOrProcess() const +{ + for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) { + if (mFilesOrDirectories[i].IsDirectory()) { + return false; + } + } + + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/FileList.h b/dom/base/FileList.h index ca63f57a5fc0..b6aeeb7940a2 100644 --- a/dom/base/FileList.h +++ b/dom/base/FileList.h @@ -97,6 +97,8 @@ public: void ToSequence(Sequence& aSequence, ErrorResult& aRv) const; + bool ClonableToDifferentThreadOrProcess() const; + private: ~FileList() {} diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 9b734368edaa..1313e5325149 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -1008,7 +1008,6 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, } if (aTag == SCTAG_DOM_FILELIST) { - MOZ_ASSERT(mSupportedContext == SameProcessSameThread); return ReadFileList(aCx, aReader, aIndex, this); } @@ -1049,9 +1048,11 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx, } // See if this is a FileList object. - if (mSupportedContext == SameProcessSameThread) { + { FileList* fileList = nullptr; - if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) { + if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList)) && + (mSupportedContext == SameProcessSameThread || + fileList->ClonableToDifferentThreadOrProcess())) { return WriteFileList(aWriter, fileList, this); } } diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html index f90a80660d52..50d70464c6b3 100644 --- a/dom/base/test/test_postMessages.html +++ b/dom/base/test/test_postMessages.html @@ -84,7 +84,7 @@ function create_fileList() { var domFile = fileList.files[0]; is(domFile.name, "prefs.js", "fileName should be prefs.js"); - clonableObjects.push({ crossThreads: false, data: fileList.files }); + clonableObjects.push({ crossThreads: true, data: fileList.files }); script.destroy(); next(); } From 8d3c72ef6470f064ce299be21dfe9051491bfbe9 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:11 +0100 Subject: [PATCH 10/28] Bug 1173320 - patch 7/8 - Tests for FileList and Directories, r=smaug --- dom/base/test/script_postmessages_fileList.js | 22 +++- dom/base/test/test_postMessages.html | 26 ++++- dom/filesystem/GetDirectoryListingTask.cpp | 2 +- dom/filesystem/moz.build | 2 + dom/filesystem/tests/mochitest.ini | 5 + dom/filesystem/tests/moz.build | 7 ++ dom/filesystem/tests/script_fileList.js | 13 +++ dom/filesystem/tests/test_basic.html | 103 ++++++++++++++++++ dom/html/HTMLInputElement.cpp | 28 +++++ dom/html/HTMLInputElement.h | 1 + dom/webidl/HTMLInputElement.webidl | 4 + 11 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 dom/filesystem/tests/mochitest.ini create mode 100644 dom/filesystem/tests/moz.build create mode 100644 dom/filesystem/tests/script_fileList.js create mode 100644 dom/filesystem/tests/test_basic.html diff --git a/dom/base/test/script_postmessages_fileList.js b/dom/base/test/script_postmessages_fileList.js index e5a7d913263d..8382958f328f 100644 --- a/dom/base/test/script_postmessages_fileList.js +++ b/dom/base/test/script_postmessages_fileList.js @@ -1,15 +1,25 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.importGlobalProperties(["File"]); -var testFile = Cc["@mozilla.org/file/directory_service;1"] - .getService(Ci.nsIDirectoryService) - .QueryInterface(Ci.nsIProperties) - .get("ProfD", Ci.nsIFile); -testFile.append("prefs.js"); - addMessageListener("file.open", function () { + var testFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); + testFile.append("prefs.js"); + sendAsyncMessage("file.opened", { file: new File(testFile) }); }); +addMessageListener("dir.open", function () { + var testFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); + + sendAsyncMessage("dir.opened", { + dir: testFile.path + }); +}); diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html index 50d70464c6b3..4157ff0665d2 100644 --- a/dom/base/test/test_postMessages.html +++ b/dom/base/test/test_postMessages.html @@ -72,7 +72,7 @@ var clonableObjects = [ { crossThreads: true, data: new ImageData(2, 2) }, ]; -function create_fileList() { +function create_fileList_forFile() { var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); var script = SpecialPowers.loadChromeScript(url); @@ -93,6 +93,27 @@ function create_fileList() { script.sendAsyncMessage("file.open"); } +function create_fileList_forDir() { + var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js"); + var script = SpecialPowers.loadChromeScript(url); + + function onOpened(message) { + var fileList = document.getElementById('fileList'); + SpecialPowers.wrap(fileList).mozSetDirectory(message.dir); + + // Just a simple test + is(fileList.files.length, 1, "Filelist has 1 element"); + ok(fileList.files[0] instanceof Directory, "We have a directory."); + + clonableObjects.push({ crossThreads: false, data: fileList.files }); + script.destroy(); + next(); + } + + script.addMessageListener("dir.opened", onOpened); + script.sendAsyncMessage("dir.open"); +} + function runTests(obj) { ok(('clonableObjects' in obj) && ('transferableObjects' in obj) && @@ -513,7 +534,8 @@ function test_messagePort_inWorkers() { } var tests = [ - create_fileList, + create_fileList_forFile, + create_fileList_forDir, test_windowToWindow, test_windowToIframe, diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index dd7d566c2af3..f2672d1e2710 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -327,7 +327,7 @@ GetDirectoryListingTask::HandlerCallback() Sequence listing; if (!listing.SetLength(count, mozilla::fallible_t())) { - mPromise->MaybeReject(NS_ERROR_FAILURE); + mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); mPromise = nullptr; return; } diff --git a/dom/filesystem/moz.build b/dom/filesystem/moz.build index 06b916438ce1..adaf5794fefa 100644 --- a/dom/filesystem/moz.build +++ b/dom/filesystem/moz.build @@ -4,6 +4,8 @@ # 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/. +TEST_DIRS += ['tests'] + EXPORTS.mozilla.dom += [ 'DeviceStorageFileSystem.h', 'Directory.h', diff --git a/dom/filesystem/tests/mochitest.ini b/dom/filesystem/tests/mochitest.ini new file mode 100644 index 000000000000..15292f2eaff1 --- /dev/null +++ b/dom/filesystem/tests/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = + script_fileList.js + +[test_basic.html] diff --git a/dom/filesystem/tests/moz.build b/dom/filesystem/tests/moz.build new file mode 100644 index 000000000000..8421b15157a7 --- /dev/null +++ b/dom/filesystem/tests/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_MANIFESTS += ['mochitest.ini'] diff --git a/dom/filesystem/tests/script_fileList.js b/dom/filesystem/tests/script_fileList.js new file mode 100644 index 000000000000..81fd6b38d1ad --- /dev/null +++ b/dom/filesystem/tests/script_fileList.js @@ -0,0 +1,13 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +addMessageListener("dir.open", function () { + var testFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); + + sendAsyncMessage("dir.opened", { + dir: testFile.path + }); +}); diff --git a/dom/filesystem/tests/test_basic.html b/dom/filesystem/tests/test_basic.html new file mode 100644 index 000000000000..1b9f355b81a9 --- /dev/null +++ b/dom/filesystem/tests/test_basic.html @@ -0,0 +1,103 @@ + + + + Test for Directory API + + + + + + + + + diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 712c69eb5ccb..5e3a26eae6f0 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -2284,6 +2284,34 @@ HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, return rv.StealNSResult(); } +void +HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath, + ErrorResult& aRv) +{ + nsCOMPtr file; + NS_ConvertUTF16toUTF8 path(aDirectoryPath); + aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow(); + if (NS_WARN_IF(!window)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + RefPtr directory = Directory::Create(window, file, + Directory::eDOMRootDirectory); + MOZ_ASSERT(directory); + + nsTArray array; + OwningFileOrDirectory* element = array.AppendElement(); + element->SetAsDirectory() = directory; + + SetFilesOrDirectories(array, true); +} + bool HTMLInputElement::MozIsTextField(bool aExcludePassword) { diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 7ba264e4302c..b4f90adc3910 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -724,6 +724,7 @@ public: void MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv); void MozSetFileArray(const Sequence>& aFiles); + void MozSetDirectory(const nsAString& aDirectoryPath, ErrorResult& aRv); HTMLInputElement* GetOwnerNumberControl(); diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index 13f9f88d9b23..592953d49997 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -155,6 +155,10 @@ partial interface HTMLInputElement { [ChromeOnly] void mozSetFileArray(sequence files); + // This method is meant to use for testing only. + [ChromeOnly, Throws] + void mozSetDirectory(DOMString directoryPath); + // Number controls () have an anonymous text control // () in the anonymous shadow tree that they contain. On // such an anonymous text control this property provides access to the From 1e2dc533d618b8fc26f4af1d4d649ec0451a1e1f Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:56:11 +0100 Subject: [PATCH 11/28] Bug 1173320 - patch 8/8 - Fix e10s tests for DeviceStorage API, r=smaug --- dom/devicestorage/DeviceStorageStatics.cpp | 20 +++++++++---------- .../test/devicestorage_common.js | 8 ++++++++ dom/devicestorage/test/test_overrideDir.html | 3 +-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/dom/devicestorage/DeviceStorageStatics.cpp b/dom/devicestorage/DeviceStorageStatics.cpp index dbb9e8e65bd1..497240225474 100644 --- a/dom/devicestorage/DeviceStorageStatics.cpp +++ b/dom/devicestorage/DeviceStorageStatics.cpp @@ -582,7 +582,16 @@ DeviceStorageStatics::ResetOverrideRootDir() nsCOMPtr f; DS_LOG_INFO(""); - if (Preferences::GetBool(kPrefTesting, false)) { + // For users running on desktop, it's convenient to be able to override + // all of the directories to point to a single tree, much like what happens + // on a real device. + const nsAdoptingString& overrideRootDir = + mozilla::Preferences::GetString(kPrefOverrideRootDir); + if (overrideRootDir && !overrideRootDir.IsEmpty()) { + NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f)); + } + + if (!f && Preferences::GetBool(kPrefTesting, false)) { DS_LOG_INFO("temp"); nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); @@ -592,15 +601,6 @@ DeviceStorageStatics::ResetOverrideRootDir() f->AppendRelativeNativePath( NS_LITERAL_CSTRING("device-storage-testing")); } - } else { - // For users running on desktop, it's convenient to be able to override - // all of the directories to point to a single tree, much like what happens - // on a real device. - const nsAdoptingString& overrideRootDir = - mozilla::Preferences::GetString(kPrefOverrideRootDir); - if (overrideRootDir && !overrideRootDir.IsEmpty()) { - NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f)); - } } if (f) { diff --git a/dom/devicestorage/test/devicestorage_common.js b/dom/devicestorage/test/devicestorage_common.js index bbbf583b1324..7b4cf5dd614e 100644 --- a/dom/devicestorage/test/devicestorage_common.js +++ b/dom/devicestorage/test/devicestorage_common.js @@ -20,12 +20,20 @@ Object.defineProperty(Array.prototype, "remove", { function devicestorage_setup(callback) { SimpleTest.waitForExplicitFinish(); + const Cc = SpecialPowers.Cc; + const Ci = SpecialPowers.Ci; + var directoryService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + var f = directoryService.get("TmpD", Ci.nsIFile); + f.appendRelativePath("device-storage-testing"); + let script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('remove_testing_directory.js')); script.addMessageListener('directory-removed', function listener () { script.removeMessageListener('directory-removed', listener); var prefs = [["device.storage.enabled", true], ["device.storage.testing", true], + ["device.storage.overrideRootDir", f.path], ["device.storage.prompt.testing", true]]; SpecialPowers.pushPrefEnv({"set": prefs}, callback); }); diff --git a/dom/devicestorage/test/test_overrideDir.html b/dom/devicestorage/test/test_overrideDir.html index 2d9b214f397b..0a9d03c7e126 100644 --- a/dom/devicestorage/test/test_overrideDir.html +++ b/dom/devicestorage/test/test_overrideDir.html @@ -149,8 +149,7 @@ try { // The remove will fail if the directory doesn't exist, which is fine. f.remove(true); } catch (e) {} - SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path], - ["device.storage.testing", false]]}, + SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path]]}, function() { startTest(); }); From c24e6719051a8c5f29a90226dfbe2b4a42413e43 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 20 Mar 2016 11:58:01 +0100 Subject: [PATCH 12/28] Bug 1258137 - OSFileSystem should not be kept alive by more than 1 Directory, r=smaug --- dom/filesystem/DeviceStorageFileSystem.cpp | 11 +++++++++++ dom/filesystem/DeviceStorageFileSystem.h | 3 +++ dom/filesystem/Directory.cpp | 8 +++++++- dom/filesystem/FileSystemBase.h | 3 +++ dom/filesystem/OSFileSystem.cpp | 11 +++++++++++ dom/filesystem/OSFileSystem.h | 3 +++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 06437115e7ad..36db6f54ea58 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -62,6 +62,17 @@ DeviceStorageFileSystem::~DeviceStorageFileSystem() { } +already_AddRefed +DeviceStorageFileSystem::Clone() +{ + RefPtr fs = + new DeviceStorageFileSystem(mStorageType, mStorageName); + + fs->mWindowId = mWindowId; + + return fs.forget(); +} + void DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage) { diff --git a/dom/filesystem/DeviceStorageFileSystem.h b/dom/filesystem/DeviceStorageFileSystem.h index e14202ae0a8e..32168bc82553 100644 --- a/dom/filesystem/DeviceStorageFileSystem.h +++ b/dom/filesystem/DeviceStorageFileSystem.h @@ -27,6 +27,9 @@ public: // Overrides FileSystemBase + virtual already_AddRefed + Clone() override; + virtual void Shutdown() override; diff --git a/dom/filesystem/Directory.cpp b/dom/filesystem/Directory.cpp index fd46ad15a3ef..edab5908f54f 100644 --- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -158,7 +158,6 @@ Directory::Directory(nsISupports* aParent, DirectoryType aType, FileSystemBase* aFileSystem) : mParent(aParent) - , mFileSystem(aFileSystem) , mFile(aFile) , mType(aType) { @@ -166,6 +165,13 @@ Directory::Directory(nsISupports* aParent, MOZ_ASSERT(aFile); // aFileSystem can be null. In this case we create a OSFileSystem when needed. + if (aFileSystem) { + // More likely, this is a OSFileSystem. This object keeps a reference of + // mParent but it's not cycle collectable and to avoid manual + // addref/release, it's better to have 1 object per directory. For this + // reason we clone it here. + mFileSystem = aFileSystem->Clone(); + } } Directory::~Directory() diff --git a/dom/filesystem/FileSystemBase.h b/dom/filesystem/FileSystemBase.h index eb2c782baa92..6dc97247f4b5 100644 --- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -34,6 +34,9 @@ public: virtual void SerializeDOMPath(nsAString& aOutput) const = 0; + virtual already_AddRefed + Clone() = 0; + virtual nsISupports* GetParentObject() const; diff --git a/dom/filesystem/OSFileSystem.cpp b/dom/filesystem/OSFileSystem.cpp index 79743e731a1e..50335a982b4d 100644 --- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -30,6 +30,17 @@ OSFileSystem::OSFileSystem(const nsAString& aRootDir) #endif } +already_AddRefed +OSFileSystem::Clone() +{ + RefPtr fs = new OSFileSystem(mLocalRootPath); + if (mParent) { + fs->Init(mParent); + } + + return fs.forget(); +} + void OSFileSystem::Init(nsISupports* aParent) { diff --git a/dom/filesystem/OSFileSystem.h b/dom/filesystem/OSFileSystem.h index f3c329a22ea4..bf0708cf9bba 100644 --- a/dom/filesystem/OSFileSystem.h +++ b/dom/filesystem/OSFileSystem.h @@ -22,6 +22,9 @@ public: // Overrides FileSystemBase + virtual already_AddRefed + Clone() override; + virtual nsISupports* GetParentObject() const override; From 2a402eafbc21ce90660fbd4dbacbf761054f8ce0 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 11 Feb 2016 15:00:22 -0500 Subject: [PATCH 13/28] Bug 698882 - mozilla::net::PollableEvent r=dragana r=mayhemer --- netwerk/base/PollableEvent.cpp | 275 +++++++++++++++++++++ netwerk/base/PollableEvent.h | 38 +++ netwerk/base/moz.build | 1 + netwerk/base/nsSocketTransportService2.cpp | 144 +++++------ netwerk/base/nsSocketTransportService2.h | 20 +- netwerk/standalone/moz.build | 1 + 6 files changed, 394 insertions(+), 85 deletions(-) create mode 100644 netwerk/base/PollableEvent.cpp create mode 100644 netwerk/base/PollableEvent.h diff --git a/netwerk/base/PollableEvent.cpp b/netwerk/base/PollableEvent.cpp new file mode 100644 index 000000000000..10c4b7ea3137 --- /dev/null +++ b/netwerk/base/PollableEvent.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#include "nsSocketTransportService2.h" +#include "PollableEvent.h" +#include "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Logging.h" +#include "prerror.h" +#include "prio.h" +#include "private/pprio.h" +#include "prnetdb.h" + +#ifdef XP_WIN +#include "ShutdownLayer.h" +#else +#include +#define USEPIPE 1 +#endif + +namespace mozilla { +namespace net { + +#ifndef USEPIPE +static PRDescIdentity sPollableEventLayerIdentity; +static PRIOMethods sPollableEventLayerMethods; +static PRIOMethods *sPollableEventLayerMethodsPtr = nullptr; + +static void LazyInitSocket() +{ + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + if (sPollableEventLayerMethodsPtr) { + return; + } + sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer"); + sPollableEventLayerMethods = *PR_GetDefaultIOMethods(); + sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods; +} + +static bool NewTCPSocketPair(PRFileDesc *fd[]) +{ + // this is a replacement for PR_NewTCPSocketPair that manually + // sets the recv buffer to 64K. A windows bug (1248358) + // can result in using an incompatible rwin and window + // scale option on localhost pipes if not set before connect. + + PRFileDesc *listener = nullptr; + PRFileDesc *writer = nullptr; + PRFileDesc *reader = nullptr; + PRSocketOptionData recvBufferOpt; + recvBufferOpt.option = PR_SockOpt_RecvBufferSize; + recvBufferOpt.value.recv_buffer_size = 65535; + + PRSocketOptionData nodelayOpt; + nodelayOpt.option = PR_SockOpt_NoDelay; + nodelayOpt.value.no_delay = true; + + PRSocketOptionData noblockOpt; + noblockOpt.option = PR_SockOpt_Nonblocking; + noblockOpt.value.non_blocking = true; + + listener = PR_OpenTCPSocket(PR_AF_INET); + if (!listener) { + goto failed; + } + PR_SetSocketOption(listener, &recvBufferOpt); + PR_SetSocketOption(listener, &nodelayOpt); + PR_SetSocketOption(listener, &noblockOpt); + PRNetAddr listenAddr; + memset(&listenAddr, 0, sizeof(listenAddr)); + if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) || + (PR_Bind(listener, &listenAddr) == PR_FAILURE) || + (PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port + (PR_Listen(listener, 5) == PR_FAILURE)) { + goto failed; + } + + writer = PR_OpenTCPSocket(PR_AF_INET); + if (!writer) { + goto failed; + } + PR_SetSocketOption(writer, &recvBufferOpt); + PR_SetSocketOption(writer, &nodelayOpt); + PR_SetSocketOption(writer, &noblockOpt); + PRNetAddr writerAddr; + if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) { + goto failed; + } + + if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + if ((PR_GetError() != PR_IN_PROGRESS_ERROR) || + (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) { + goto failed; + } + } + + reader = PR_Accept(listener, &listenAddr, PR_INTERVAL_NO_TIMEOUT); + if (!reader) { + goto failed; + } + PR_SetSocketOption(reader, &recvBufferOpt); + PR_SetSocketOption(reader, &nodelayOpt); + PR_SetSocketOption(reader, &noblockOpt); + PR_Close(listener); + + fd[0] = reader; + fd[1] = writer; + return true; + +failed: + if (listener) { + PR_Close(listener); + } + if (reader) { + PR_Close(reader); + } + if (writer) { + PR_Close(writer); + } + return false; +} + +#endif + +PollableEvent::PollableEvent() + : mWriteFD(nullptr) + , mReadFD(nullptr) + , mSignaled(false) +{ + MOZ_COUNT_CTOR(PollableEvent); + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + // create pair of prfiledesc that can be used as a poll()ble + // signal. on windows use a localhost socket pair, and on + // unix use a pipe. +#ifdef USEPIPE + SOCKET_LOG(("PollableEvent() using pipe\n")); + if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) { + // make the pipe non blocking. NSPR asserts at + // trying to use SockOpt here + PROsfd fd = PR_FileDesc2NativeHandle(mReadFD); + int flags = fcntl(fd, F_GETFL, 0); + (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK); + fd = PR_FileDesc2NativeHandle(mWriteFD); + flags = fcntl(fd, F_GETFL, 0); + (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } else { + mReadFD = nullptr; + mWriteFD = nullptr; + SOCKET_LOG(("PollableEvent() pipe failed\n")); + } +#else + SOCKET_LOG(("PollableEvent() using socket pair\n")); + PRFileDesc *fd[2]; + LazyInitSocket(); + if (NewTCPSocketPair(fd)) { + mReadFD = fd[0]; + mWriteFD = fd[1]; + + // compatibility with LSPs such as McAfee that assume a NSPR + // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop. + PRFileDesc *topLayer = + PR_CreateIOLayerStub(sPollableEventLayerIdentity, + sPollableEventLayerMethodsPtr); + if (topLayer) { + if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) { + topLayer->dtor(topLayer); + } else { + SOCKET_LOG(("PollableEvent() nspr layer ok\n")); + mReadFD = topLayer; + } + } + + } else { + SOCKET_LOG(("PollableEvent() socketpair failed\n")); + } +#endif + + if (mReadFD && mWriteFD) { + // prime the system to deal with races invovled in [dc]tor cycle + SOCKET_LOG(("PollableEvent() ctor ok\n")); + mSignaled = true; + PR_Write(mWriteFD, "I", 1); + } +} + +PollableEvent::~PollableEvent() +{ + MOZ_COUNT_DTOR(PollableEvent); + if (mWriteFD) { +#if defined(XP_WIN) + AttachShutdownLayer(mWriteFD); +#endif + PR_Close(mWriteFD); + } + if (mReadFD) { +#if defined(XP_WIN) + AttachShutdownLayer(mReadFD); +#endif + PR_Close(mReadFD); + } +} + +// we do not record signals on the socket thread +// because the socket thread can reliably look at its +// own runnable queue before selecting a poll time +// this is the "service the network without blocking" comment in +// nsSocketTransportService2.cpp +bool +PollableEvent::Signal() +{ + SOCKET_LOG(("PollableEvent::Signal\n")); + + if (!mWriteFD) { + SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n")); + return false; + } + if (PR_GetCurrentThread() == gSocketThread) { + SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n")); + return true; + } + if (mSignaled) { + return true; + } + mSignaled = true; + int32_t status = PR_Write(mWriteFD, "M", 1); + SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status)); + if (status != 1) { + NS_WARNING("PollableEvent::Signal Failed\n"); + SOCKET_LOG(("PollableEvent::Signal Failed\n")); + } + return (status == 1); +} + +bool +PollableEvent::Clear() +{ + // necessary because of the "dont signal on socket thread" optimization + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + + SOCKET_LOG(("PollableEvent::Clear\n")); + mSignaled = false; + if (!mReadFD) { + SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n")); + return false; + } + char buf[2048]; + int32_t status = PR_Read(mReadFD, buf, 2048); + SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status)); + + if (status == 1) { + return true; + } + if (status == 0) { + SOCKET_LOG(("PollableEvent::Clear EOF!\n")); + return false; + } + if (status > 1) { + MOZ_ASSERT(false); + SOCKET_LOG(("PollableEvent::Clear Unexpected events\n")); + Clear(); + return true; + } + PRErrorCode code = PR_GetError(); + if (code == PR_WOULD_BLOCK_ERROR) { + return true; + } + SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code)); + return false; +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/base/PollableEvent.h b/netwerk/base/PollableEvent.h new file mode 100644 index 000000000000..80007993200d --- /dev/null +++ b/netwerk/base/PollableEvent.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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/. */ + +#ifndef PollableEvent_h__ +#define PollableEvent_h__ + +#include "mozilla/Mutex.h" + +namespace mozilla { +namespace net { + +// class must be called locked +class PollableEvent +{ +public: + PollableEvent(); + ~PollableEvent(); + + // Signal/Clear return false only if they fail + bool Signal(); + bool Clear(); + bool Valid() { return mWriteFD && mReadFD; } + + PRFileDesc *PollableFD() { return mReadFD; } + +private: + PRFileDesc *mWriteFD; + PRFileDesc *mReadFD; + bool mSignaled; +}; + +} // namespace net +} // namespace mozilla + +#endif diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 4736d2547215..a38ef8c26036 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -250,6 +250,7 @@ UNIFIED_SOURCES += [ 'nsURLHelper.cpp', 'nsURLParsers.cpp', 'OfflineObserver.cpp', + 'PollableEvent.cpp', 'Predictor.cpp', 'ProxyAutoConfig.cpp', 'RedirectChannelRegistrar.cpp', diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index adb50b25920c..e149e6c04c44 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -87,13 +87,13 @@ DebugMutexAutoLock::~DebugMutexAutoLock() nsSocketTransportService::nsSocketTransportService() : mThread(nullptr) - , mThreadEvent(nullptr) , mAutodialEnabled(false) , mLock("nsSocketTransportService::mLock") , mInitialized(false) , mShuttingDown(false) , mOffline(false) , mGoingOffline(false) + , mRawThread(nullptr) , mActiveListSize(SOCKET_LIMIT_MIN) , mIdleListSize(SOCKET_LIMIT_MIN) , mActiveCount(0) @@ -131,9 +131,6 @@ nsSocketTransportService::~nsSocketTransportService() { NS_ASSERTION(NS_IsMainThread(), "wrong thread"); NS_ASSERTION(!mInitialized, "not shutdown properly"); - - if (mThreadEvent) - PR_DestroyPollableEvent(mThreadEvent); free(mActiveList); free(mIdleList); @@ -435,7 +432,7 @@ nsSocketTransportService::PollTimeout() } int32_t -nsSocketTransportService::Poll(bool wait, uint32_t *interval, +nsSocketTransportService::Poll(uint32_t *interval, TimeDuration *pollDuration) { PRPollDesc *pollList; @@ -443,11 +440,16 @@ nsSocketTransportService::Poll(bool wait, uint32_t *interval, PRIntervalTime pollTimeout; *pollDuration = 0; + // If there are pending events for this thread then + // DoPollIteration() should service the network without blocking. + bool pendingEvents = false; + mRawThread->HasPendingEvents(&pendingEvents); + if (mPollList[0].fd) { mPollList[0].out_flags = 0; pollList = mPollList; pollCount = mActiveCount + 1; - pollTimeout = PollTimeout(); + pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(); } else { // no pollable event, so busy wait... @@ -456,12 +458,10 @@ nsSocketTransportService::Poll(bool wait, uint32_t *interval, pollList = &mPollList[1]; else pollList = nullptr; - pollTimeout = PR_MillisecondsToInterval(25); + pollTimeout = + pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25); } - if (!wait) - pollTimeout = PR_INTERVAL_NO_WAIT; - PRIntervalTime ts = PR_IntervalNow(); TimeStamp pollStart; @@ -513,24 +513,6 @@ nsSocketTransportService::Init() if (mShuttingDown) return NS_ERROR_UNEXPECTED; - if (!mThreadEvent) { - mThreadEvent = PR_NewPollableEvent(); - // - // NOTE: per bug 190000, this failure could be caused by Zone-Alarm - // or similar software. - // - // NOTE: per bug 191739, this failure could also be caused by lack - // of a loopback device on Windows and OS/2 platforms (NSPR creates - // a loopback socket pair on these platforms to implement a pollable - // event object). if we can't create a pollable event, then we'll - // have to "busy wait" to implement the socket event queue :-( - // - if (!mThreadEvent) { - NS_WARNING("running socket transport thread without a pollable event"); - SOCKET_LOG(("running socket transport thread without a pollable event")); - } - } - nsCOMPtr thread; nsresult rv = NS_NewThread(getter_AddRefs(thread), this); if (NS_FAILED(rv)) return rv; @@ -584,9 +566,9 @@ nsSocketTransportService::Shutdown() // signal the socket thread to shutdown mShuttingDown = true; - if (mThreadEvent) - PR_SetPollableEvent(mThreadEvent); - // else wait for Poll timeout + if (mPollableEvent) { + mPollableEvent->Signal(); + } } // join with thread @@ -635,8 +617,9 @@ nsSocketTransportService::SetOffline(bool offline) else if (mOffline && !offline) { mOffline = false; } - if (mThreadEvent) - PR_SetPollableEvent(mThreadEvent); + if (mPollableEvent) { + mPollableEvent->Signal(); + } return NS_OK; } @@ -749,9 +732,19 @@ nsSocketTransportService::SetAutodialEnabled(bool value) NS_IMETHODIMP nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread) { + if (PR_GetCurrentThread() == gSocketThread) { + // this check is redundant to one done inside ::Signal(), but + // we can do it here and skip obtaining the lock - given that + // this is a relatively common occurance its worth the + // redundant code + SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n")); + return NS_OK; + } + DebugMutexAutoLock lock(mLock); - if (mThreadEvent) - PR_SetPollableEvent(mThreadEvent); + if (mPollableEvent) { + mPollableEvent->Signal(); + } return NS_OK; } @@ -796,15 +789,31 @@ nsSocketTransportService::Run() gSocketThread = PR_GetCurrentThread(); - // add thread event to poll list (mThreadEvent may be nullptr) - mPollList[0].fd = mThreadEvent; - mPollList[0].in_flags = PR_POLL_READ; + mPollableEvent.reset(new PollableEvent()); + // + // NOTE: per bug 190000, this failure could be caused by Zone-Alarm + // or similar software. + // + // NOTE: per bug 191739, this failure could also be caused by lack + // of a loopback device on Windows and OS/2 platforms (it creates + // a loopback socket pair on these platforms to implement a pollable + // event object). if we can't create a pollable event, then we'll + // have to "busy wait" to implement the socket event queue :-( + // + if (!mPollableEvent->Valid()) { + mPollableEvent = nullptr; + NS_WARNING("running socket transport thread without a pollable event"); + SOCKET_LOG(("running socket transport thread without a pollable event")); + } + + mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; + mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; mPollList[0].out_flags = 0; - nsIThread *thread = NS_GetCurrentThread(); + mRawThread = NS_GetCurrentThread(); // hook ourselves up to observe event processing for this thread - nsCOMPtr threadInt = do_QueryInterface(thread); + nsCOMPtr threadInt = do_QueryInterface(mRawThread); threadInt->SetObserver(this); // make sure the pseudo random number generator is seeded on this thread @@ -833,7 +842,6 @@ nsSocketTransportService::Run() for (;;) { bool pendingEvents = false; - thread->HasPendingEvents(&pendingEvents); numberOfPendingEvents = 0; numberOfPendingEventsLastCycle = 0; @@ -848,9 +856,7 @@ nsSocketTransportService::Run() pollCycleStart = TimeStamp::NowLoRes(); } - // If there are pending events for this thread then - // DoPollIteration() should service the network without blocking. - DoPollIteration(!pendingEvents, &singlePollDuration); + DoPollIteration(&singlePollDuration); if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) { Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME, @@ -862,11 +868,7 @@ nsSocketTransportService::Run() pollDuration += singlePollDuration; } - // If nothing was pending before the poll, it might be now - if (!pendingEvents) { - thread->HasPendingEvents(&pendingEvents); - } - + mRawThread->HasPendingEvents(&pendingEvents); if (pendingEvents) { if (!mServingPendingQueue) { nsresult rv = Dispatch(NS_NewRunnableMethod(this, @@ -890,10 +892,10 @@ nsSocketTransportService::Run() } TimeStamp eventQueueStart = TimeStamp::NowLoRes(); do { - NS_ProcessNextEvent(thread); + NS_ProcessNextEvent(mRawThread); numberOfPendingEvents++; pendingEvents = false; - thread->HasPendingEvents(&pendingEvents); + mRawThread->HasPendingEvents(&pendingEvents); } while (pendingEvents && mServingPendingQueue && ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() < @@ -951,7 +953,7 @@ nsSocketTransportService::Run() // Final pass over the event queue. This makes sure that events posted by // socket detach handlers get processed. - NS_ProcessPendingEvents(thread); + NS_ProcessPendingEvents(mRawThread); gSocketThread = nullptr; @@ -990,12 +992,11 @@ nsSocketTransportService::Reset(bool aGuardLocals) } nsresult -nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration) +nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) { - SOCKET_LOG(("STS poll iter [%d]\n", wait)); + SOCKET_LOG(("STS poll iter\n")); int32_t i, count; - // // poll loop // @@ -1054,7 +1055,7 @@ nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration) *pollDuration = 0; if (!gIOService->IsNetTearingDown()) { // Let's not do polling during shutdown. - n = Poll(wait, &pollInterval, pollDuration); + n = Poll(&pollInterval, pollDuration); } if (n < 0) { @@ -1110,29 +1111,28 @@ nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration) DetachSocket(mActiveList, &mActiveList[i]); } - if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) { - // acknowledge pollable event (wait should not block) - if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) { + if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) { + DebugMutexAutoLock lock(mLock); + + // acknowledge pollable event (should not block) + if (mPollableEvent && + ((mPollList[0].out_flags & PR_POLL_EXCEPT) || + !mPollableEvent->Clear())) { // On Windows, the TCP loopback connection in the // pollable event may become broken when a laptop // switches between wired and wireless networks or // wakes up from hibernation. We try to create a // new pollable event. If that fails, we fall back // on "busy wait". - { - DebugMutexAutoLock lock(mLock); - PR_DestroyPollableEvent(mThreadEvent); - mThreadEvent = PR_NewPollableEvent(); + NS_WARNING("Trying to repair mPollableEvent"); + mPollableEvent.reset(new PollableEvent()); + if (!mPollableEvent->Valid()) { + mPollableEvent = nullptr; } - if (!mThreadEvent) { - NS_WARNING("running socket transport thread without " - "a pollable event"); - SOCKET_LOG(("running socket transport thread without " - "a pollable event")); - } - mPollList[0].fd = mThreadEvent; - // mPollList[0].in_flags was already set to PR_POLL_READ - // in Run(). + SOCKET_LOG(("running socket transport thread without " + "a pollable event now valid=%d", mPollableEvent->Valid())); + mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; + mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; mPollList[0].out_flags = 0; } } diff --git a/netwerk/base/nsSocketTransportService2.h b/netwerk/base/nsSocketTransportService2.h index 44f514740d50..d1c9ef722824 100644 --- a/netwerk/base/nsSocketTransportService2.h +++ b/netwerk/base/nsSocketTransportService2.h @@ -19,6 +19,8 @@ #include "mozilla/net/DashboardTypes.h" #include "mozilla/Atomics.h" #include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "PollableEvent.h" class nsASocketHandler; struct PRPollDesc; @@ -124,14 +126,7 @@ private: //------------------------------------------------------------------------- nsCOMPtr mThread; // protected by mLock - PRFileDesc *mThreadEvent; - // protected by mLock. mThreadEvent may change - // if the old pollable event is broken. only - // the socket thread may change mThreadEvent; - // it needs to lock mLock only when it changes - // mThreadEvent. other threads don't change - // mThreadEvent; they need to lock mLock - // whenever they access mThreadEvent. + mozilla::UniquePtr mPollableEvent; bool mAutodialEnabled; // pref to control autodial code @@ -173,6 +168,7 @@ private: SocketContext *mActiveList; /* mListSize entries */ SocketContext *mIdleList; /* mListSize entries */ + nsIThread *mRawThread; uint32_t mActiveListSize; uint32_t mIdleListSize; @@ -197,18 +193,16 @@ private: //------------------------------------------------------------------------- // poll list (socket thread only) // - // first element of the poll list is mThreadEvent (or null if the pollable + // first element of the poll list is mPollableEvent (or null if the pollable // event cannot be created). //------------------------------------------------------------------------- PRPollDesc *mPollList; /* mListSize + 1 entries */ PRIntervalTime PollTimeout(); // computes ideal poll timeout - nsresult DoPollIteration(bool wait, - mozilla::TimeDuration *pollDuration); + nsresult DoPollIteration(mozilla::TimeDuration *pollDuration); // perfoms a single poll iteration - int32_t Poll(bool wait, - uint32_t *interval, + int32_t Poll(uint32_t *interval, mozilla::TimeDuration *pollDuration); // calls PR_Poll. the out param // interval indicates the poll diff --git a/netwerk/standalone/moz.build b/netwerk/standalone/moz.build index dd3c9c71b1ab..794cec94d703 100644 --- a/netwerk/standalone/moz.build +++ b/netwerk/standalone/moz.build @@ -12,6 +12,7 @@ src_list = [ ] netwerk_base_src = [ + 'PollableEvent.cpp', 'nsDNSPrefetch.cpp', 'nsNetAddr.cpp', 'nsSocketTransportService2.cpp', From 752343237efbbb9d3843f4a0c85ffc78623062ab Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 19 Mar 2016 21:28:19 -0700 Subject: [PATCH 14/28] No bug - fix typo r=me DONTBUILD CLOSED TREE --HG-- extra : rebase_source : 25f0600425dec249f838ed221dde71d401571eb9 extra : amend_source : 49f7ccab9e47083df9e8b7776acc6de73a880473 --- dom/security/nsMixedContentBlocker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index d9ffe29e93e3..f6c0652c0f0f 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -434,7 +434,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // content websockets within the websockets implementation, so we don't need // to do any blocking here, nor do we need to provide a way to undo or // override the blocking. Websockets without TLS are very flaky anyway in the - // face of many HTTP-aware proxies. Compared to psasive content, there is + // face of many HTTP-aware proxies. Compared to passive content, there is // additional risk that the script using WebSockets will disclose sensitive // information from the HTTPS page and/or eval (directly or indirectly) // received data. From d171b7485c892ddb3c5b7337b166368c4956edea Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 20 Mar 2016 17:24:48 -0700 Subject: [PATCH 15/28] Back out changeset 06e41d2080a2 (bug 698882) for (at least) 10.6 leaks including PollableEvent CLOSED TREE --- netwerk/base/PollableEvent.cpp | 275 --------------------- netwerk/base/PollableEvent.h | 38 --- netwerk/base/moz.build | 1 - netwerk/base/nsSocketTransportService2.cpp | 144 +++++------ netwerk/base/nsSocketTransportService2.h | 20 +- netwerk/standalone/moz.build | 1 - 6 files changed, 85 insertions(+), 394 deletions(-) delete mode 100644 netwerk/base/PollableEvent.cpp delete mode 100644 netwerk/base/PollableEvent.h diff --git a/netwerk/base/PollableEvent.cpp b/netwerk/base/PollableEvent.cpp deleted file mode 100644 index 10c4b7ea3137..000000000000 --- a/netwerk/base/PollableEvent.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ - -#include "nsSocketTransportService2.h" -#include "PollableEvent.h" -#include "mozilla/Assertions.h" -#include "mozilla/DebugOnly.h" -#include "mozilla/Logging.h" -#include "prerror.h" -#include "prio.h" -#include "private/pprio.h" -#include "prnetdb.h" - -#ifdef XP_WIN -#include "ShutdownLayer.h" -#else -#include -#define USEPIPE 1 -#endif - -namespace mozilla { -namespace net { - -#ifndef USEPIPE -static PRDescIdentity sPollableEventLayerIdentity; -static PRIOMethods sPollableEventLayerMethods; -static PRIOMethods *sPollableEventLayerMethodsPtr = nullptr; - -static void LazyInitSocket() -{ - MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - if (sPollableEventLayerMethodsPtr) { - return; - } - sPollableEventLayerIdentity = PR_GetUniqueIdentity("PollableEvent Layer"); - sPollableEventLayerMethods = *PR_GetDefaultIOMethods(); - sPollableEventLayerMethodsPtr = &sPollableEventLayerMethods; -} - -static bool NewTCPSocketPair(PRFileDesc *fd[]) -{ - // this is a replacement for PR_NewTCPSocketPair that manually - // sets the recv buffer to 64K. A windows bug (1248358) - // can result in using an incompatible rwin and window - // scale option on localhost pipes if not set before connect. - - PRFileDesc *listener = nullptr; - PRFileDesc *writer = nullptr; - PRFileDesc *reader = nullptr; - PRSocketOptionData recvBufferOpt; - recvBufferOpt.option = PR_SockOpt_RecvBufferSize; - recvBufferOpt.value.recv_buffer_size = 65535; - - PRSocketOptionData nodelayOpt; - nodelayOpt.option = PR_SockOpt_NoDelay; - nodelayOpt.value.no_delay = true; - - PRSocketOptionData noblockOpt; - noblockOpt.option = PR_SockOpt_Nonblocking; - noblockOpt.value.non_blocking = true; - - listener = PR_OpenTCPSocket(PR_AF_INET); - if (!listener) { - goto failed; - } - PR_SetSocketOption(listener, &recvBufferOpt); - PR_SetSocketOption(listener, &nodelayOpt); - PR_SetSocketOption(listener, &noblockOpt); - PRNetAddr listenAddr; - memset(&listenAddr, 0, sizeof(listenAddr)); - if ((PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &listenAddr) == PR_FAILURE) || - (PR_Bind(listener, &listenAddr) == PR_FAILURE) || - (PR_GetSockName(listener, &listenAddr) == PR_FAILURE) || // learn the dynamic port - (PR_Listen(listener, 5) == PR_FAILURE)) { - goto failed; - } - - writer = PR_OpenTCPSocket(PR_AF_INET); - if (!writer) { - goto failed; - } - PR_SetSocketOption(writer, &recvBufferOpt); - PR_SetSocketOption(writer, &nodelayOpt); - PR_SetSocketOption(writer, &noblockOpt); - PRNetAddr writerAddr; - if (PR_InitializeNetAddr(PR_IpAddrLoopback, ntohs(listenAddr.inet.port), &writerAddr) == PR_FAILURE) { - goto failed; - } - - if (PR_Connect(writer, &writerAddr, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { - if ((PR_GetError() != PR_IN_PROGRESS_ERROR) || - (PR_ConnectContinue(writer, PR_POLL_WRITE) == PR_FAILURE)) { - goto failed; - } - } - - reader = PR_Accept(listener, &listenAddr, PR_INTERVAL_NO_TIMEOUT); - if (!reader) { - goto failed; - } - PR_SetSocketOption(reader, &recvBufferOpt); - PR_SetSocketOption(reader, &nodelayOpt); - PR_SetSocketOption(reader, &noblockOpt); - PR_Close(listener); - - fd[0] = reader; - fd[1] = writer; - return true; - -failed: - if (listener) { - PR_Close(listener); - } - if (reader) { - PR_Close(reader); - } - if (writer) { - PR_Close(writer); - } - return false; -} - -#endif - -PollableEvent::PollableEvent() - : mWriteFD(nullptr) - , mReadFD(nullptr) - , mSignaled(false) -{ - MOZ_COUNT_CTOR(PollableEvent); - MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - // create pair of prfiledesc that can be used as a poll()ble - // signal. on windows use a localhost socket pair, and on - // unix use a pipe. -#ifdef USEPIPE - SOCKET_LOG(("PollableEvent() using pipe\n")); - if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) { - // make the pipe non blocking. NSPR asserts at - // trying to use SockOpt here - PROsfd fd = PR_FileDesc2NativeHandle(mReadFD); - int flags = fcntl(fd, F_GETFL, 0); - (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK); - fd = PR_FileDesc2NativeHandle(mWriteFD); - flags = fcntl(fd, F_GETFL, 0); - (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK); - } else { - mReadFD = nullptr; - mWriteFD = nullptr; - SOCKET_LOG(("PollableEvent() pipe failed\n")); - } -#else - SOCKET_LOG(("PollableEvent() using socket pair\n")); - PRFileDesc *fd[2]; - LazyInitSocket(); - if (NewTCPSocketPair(fd)) { - mReadFD = fd[0]; - mWriteFD = fd[1]; - - // compatibility with LSPs such as McAfee that assume a NSPR - // layer for read ala the nspr Pollable Event - Bug 698882. This layer is a nop. - PRFileDesc *topLayer = - PR_CreateIOLayerStub(sPollableEventLayerIdentity, - sPollableEventLayerMethodsPtr); - if (topLayer) { - if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, topLayer) == PR_FAILURE) { - topLayer->dtor(topLayer); - } else { - SOCKET_LOG(("PollableEvent() nspr layer ok\n")); - mReadFD = topLayer; - } - } - - } else { - SOCKET_LOG(("PollableEvent() socketpair failed\n")); - } -#endif - - if (mReadFD && mWriteFD) { - // prime the system to deal with races invovled in [dc]tor cycle - SOCKET_LOG(("PollableEvent() ctor ok\n")); - mSignaled = true; - PR_Write(mWriteFD, "I", 1); - } -} - -PollableEvent::~PollableEvent() -{ - MOZ_COUNT_DTOR(PollableEvent); - if (mWriteFD) { -#if defined(XP_WIN) - AttachShutdownLayer(mWriteFD); -#endif - PR_Close(mWriteFD); - } - if (mReadFD) { -#if defined(XP_WIN) - AttachShutdownLayer(mReadFD); -#endif - PR_Close(mReadFD); - } -} - -// we do not record signals on the socket thread -// because the socket thread can reliably look at its -// own runnable queue before selecting a poll time -// this is the "service the network without blocking" comment in -// nsSocketTransportService2.cpp -bool -PollableEvent::Signal() -{ - SOCKET_LOG(("PollableEvent::Signal\n")); - - if (!mWriteFD) { - SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n")); - return false; - } - if (PR_GetCurrentThread() == gSocketThread) { - SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n")); - return true; - } - if (mSignaled) { - return true; - } - mSignaled = true; - int32_t status = PR_Write(mWriteFD, "M", 1); - SOCKET_LOG(("PollableEvent::Signal PR_Write %d\n", status)); - if (status != 1) { - NS_WARNING("PollableEvent::Signal Failed\n"); - SOCKET_LOG(("PollableEvent::Signal Failed\n")); - } - return (status == 1); -} - -bool -PollableEvent::Clear() -{ - // necessary because of the "dont signal on socket thread" optimization - MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - - SOCKET_LOG(("PollableEvent::Clear\n")); - mSignaled = false; - if (!mReadFD) { - SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n")); - return false; - } - char buf[2048]; - int32_t status = PR_Read(mReadFD, buf, 2048); - SOCKET_LOG(("PollableEvent::Signal PR_Read %d\n", status)); - - if (status == 1) { - return true; - } - if (status == 0) { - SOCKET_LOG(("PollableEvent::Clear EOF!\n")); - return false; - } - if (status > 1) { - MOZ_ASSERT(false); - SOCKET_LOG(("PollableEvent::Clear Unexpected events\n")); - Clear(); - return true; - } - PRErrorCode code = PR_GetError(); - if (code == PR_WOULD_BLOCK_ERROR) { - return true; - } - SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code)); - return false; -} - -} // namespace net -} // namespace mozilla diff --git a/netwerk/base/PollableEvent.h b/netwerk/base/PollableEvent.h deleted file mode 100644 index 80007993200d..000000000000 --- a/netwerk/base/PollableEvent.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* 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/. */ - -#ifndef PollableEvent_h__ -#define PollableEvent_h__ - -#include "mozilla/Mutex.h" - -namespace mozilla { -namespace net { - -// class must be called locked -class PollableEvent -{ -public: - PollableEvent(); - ~PollableEvent(); - - // Signal/Clear return false only if they fail - bool Signal(); - bool Clear(); - bool Valid() { return mWriteFD && mReadFD; } - - PRFileDesc *PollableFD() { return mReadFD; } - -private: - PRFileDesc *mWriteFD; - PRFileDesc *mReadFD; - bool mSignaled; -}; - -} // namespace net -} // namespace mozilla - -#endif diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index a38ef8c26036..4736d2547215 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -250,7 +250,6 @@ UNIFIED_SOURCES += [ 'nsURLHelper.cpp', 'nsURLParsers.cpp', 'OfflineObserver.cpp', - 'PollableEvent.cpp', 'Predictor.cpp', 'ProxyAutoConfig.cpp', 'RedirectChannelRegistrar.cpp', diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index e149e6c04c44..adb50b25920c 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -87,13 +87,13 @@ DebugMutexAutoLock::~DebugMutexAutoLock() nsSocketTransportService::nsSocketTransportService() : mThread(nullptr) + , mThreadEvent(nullptr) , mAutodialEnabled(false) , mLock("nsSocketTransportService::mLock") , mInitialized(false) , mShuttingDown(false) , mOffline(false) , mGoingOffline(false) - , mRawThread(nullptr) , mActiveListSize(SOCKET_LIMIT_MIN) , mIdleListSize(SOCKET_LIMIT_MIN) , mActiveCount(0) @@ -131,6 +131,9 @@ nsSocketTransportService::~nsSocketTransportService() { NS_ASSERTION(NS_IsMainThread(), "wrong thread"); NS_ASSERTION(!mInitialized, "not shutdown properly"); + + if (mThreadEvent) + PR_DestroyPollableEvent(mThreadEvent); free(mActiveList); free(mIdleList); @@ -432,7 +435,7 @@ nsSocketTransportService::PollTimeout() } int32_t -nsSocketTransportService::Poll(uint32_t *interval, +nsSocketTransportService::Poll(bool wait, uint32_t *interval, TimeDuration *pollDuration) { PRPollDesc *pollList; @@ -440,16 +443,11 @@ nsSocketTransportService::Poll(uint32_t *interval, PRIntervalTime pollTimeout; *pollDuration = 0; - // If there are pending events for this thread then - // DoPollIteration() should service the network without blocking. - bool pendingEvents = false; - mRawThread->HasPendingEvents(&pendingEvents); - if (mPollList[0].fd) { mPollList[0].out_flags = 0; pollList = mPollList; pollCount = mActiveCount + 1; - pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout(); + pollTimeout = PollTimeout(); } else { // no pollable event, so busy wait... @@ -458,10 +456,12 @@ nsSocketTransportService::Poll(uint32_t *interval, pollList = &mPollList[1]; else pollList = nullptr; - pollTimeout = - pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25); + pollTimeout = PR_MillisecondsToInterval(25); } + if (!wait) + pollTimeout = PR_INTERVAL_NO_WAIT; + PRIntervalTime ts = PR_IntervalNow(); TimeStamp pollStart; @@ -513,6 +513,24 @@ nsSocketTransportService::Init() if (mShuttingDown) return NS_ERROR_UNEXPECTED; + if (!mThreadEvent) { + mThreadEvent = PR_NewPollableEvent(); + // + // NOTE: per bug 190000, this failure could be caused by Zone-Alarm + // or similar software. + // + // NOTE: per bug 191739, this failure could also be caused by lack + // of a loopback device on Windows and OS/2 platforms (NSPR creates + // a loopback socket pair on these platforms to implement a pollable + // event object). if we can't create a pollable event, then we'll + // have to "busy wait" to implement the socket event queue :-( + // + if (!mThreadEvent) { + NS_WARNING("running socket transport thread without a pollable event"); + SOCKET_LOG(("running socket transport thread without a pollable event")); + } + } + nsCOMPtr thread; nsresult rv = NS_NewThread(getter_AddRefs(thread), this); if (NS_FAILED(rv)) return rv; @@ -566,9 +584,9 @@ nsSocketTransportService::Shutdown() // signal the socket thread to shutdown mShuttingDown = true; - if (mPollableEvent) { - mPollableEvent->Signal(); - } + if (mThreadEvent) + PR_SetPollableEvent(mThreadEvent); + // else wait for Poll timeout } // join with thread @@ -617,9 +635,8 @@ nsSocketTransportService::SetOffline(bool offline) else if (mOffline && !offline) { mOffline = false; } - if (mPollableEvent) { - mPollableEvent->Signal(); - } + if (mThreadEvent) + PR_SetPollableEvent(mThreadEvent); return NS_OK; } @@ -732,19 +749,9 @@ nsSocketTransportService::SetAutodialEnabled(bool value) NS_IMETHODIMP nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread) { - if (PR_GetCurrentThread() == gSocketThread) { - // this check is redundant to one done inside ::Signal(), but - // we can do it here and skip obtaining the lock - given that - // this is a relatively common occurance its worth the - // redundant code - SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n")); - return NS_OK; - } - DebugMutexAutoLock lock(mLock); - if (mPollableEvent) { - mPollableEvent->Signal(); - } + if (mThreadEvent) + PR_SetPollableEvent(mThreadEvent); return NS_OK; } @@ -789,31 +796,15 @@ nsSocketTransportService::Run() gSocketThread = PR_GetCurrentThread(); - mPollableEvent.reset(new PollableEvent()); - // - // NOTE: per bug 190000, this failure could be caused by Zone-Alarm - // or similar software. - // - // NOTE: per bug 191739, this failure could also be caused by lack - // of a loopback device on Windows and OS/2 platforms (it creates - // a loopback socket pair on these platforms to implement a pollable - // event object). if we can't create a pollable event, then we'll - // have to "busy wait" to implement the socket event queue :-( - // - if (!mPollableEvent->Valid()) { - mPollableEvent = nullptr; - NS_WARNING("running socket transport thread without a pollable event"); - SOCKET_LOG(("running socket transport thread without a pollable event")); - } - - mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; - mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; + // add thread event to poll list (mThreadEvent may be nullptr) + mPollList[0].fd = mThreadEvent; + mPollList[0].in_flags = PR_POLL_READ; mPollList[0].out_flags = 0; - mRawThread = NS_GetCurrentThread(); + nsIThread *thread = NS_GetCurrentThread(); // hook ourselves up to observe event processing for this thread - nsCOMPtr threadInt = do_QueryInterface(mRawThread); + nsCOMPtr threadInt = do_QueryInterface(thread); threadInt->SetObserver(this); // make sure the pseudo random number generator is seeded on this thread @@ -842,6 +833,7 @@ nsSocketTransportService::Run() for (;;) { bool pendingEvents = false; + thread->HasPendingEvents(&pendingEvents); numberOfPendingEvents = 0; numberOfPendingEventsLastCycle = 0; @@ -856,7 +848,9 @@ nsSocketTransportService::Run() pollCycleStart = TimeStamp::NowLoRes(); } - DoPollIteration(&singlePollDuration); + // If there are pending events for this thread then + // DoPollIteration() should service the network without blocking. + DoPollIteration(!pendingEvents, &singlePollDuration); if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) { Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME, @@ -868,7 +862,11 @@ nsSocketTransportService::Run() pollDuration += singlePollDuration; } - mRawThread->HasPendingEvents(&pendingEvents); + // If nothing was pending before the poll, it might be now + if (!pendingEvents) { + thread->HasPendingEvents(&pendingEvents); + } + if (pendingEvents) { if (!mServingPendingQueue) { nsresult rv = Dispatch(NS_NewRunnableMethod(this, @@ -892,10 +890,10 @@ nsSocketTransportService::Run() } TimeStamp eventQueueStart = TimeStamp::NowLoRes(); do { - NS_ProcessNextEvent(mRawThread); + NS_ProcessNextEvent(thread); numberOfPendingEvents++; pendingEvents = false; - mRawThread->HasPendingEvents(&pendingEvents); + thread->HasPendingEvents(&pendingEvents); } while (pendingEvents && mServingPendingQueue && ((TimeStamp::NowLoRes() - eventQueueStart).ToMilliseconds() < @@ -953,7 +951,7 @@ nsSocketTransportService::Run() // Final pass over the event queue. This makes sure that events posted by // socket detach handlers get processed. - NS_ProcessPendingEvents(mRawThread); + NS_ProcessPendingEvents(thread); gSocketThread = nullptr; @@ -992,11 +990,12 @@ nsSocketTransportService::Reset(bool aGuardLocals) } nsresult -nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) +nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration) { - SOCKET_LOG(("STS poll iter\n")); + SOCKET_LOG(("STS poll iter [%d]\n", wait)); int32_t i, count; + // // poll loop // @@ -1055,7 +1054,7 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) *pollDuration = 0; if (!gIOService->IsNetTearingDown()) { // Let's not do polling during shutdown. - n = Poll(&pollInterval, pollDuration); + n = Poll(wait, &pollInterval, pollDuration); } if (n < 0) { @@ -1111,28 +1110,29 @@ nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration) DetachSocket(mActiveList, &mActiveList[i]); } - if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) { - DebugMutexAutoLock lock(mLock); - - // acknowledge pollable event (should not block) - if (mPollableEvent && - ((mPollList[0].out_flags & PR_POLL_EXCEPT) || - !mPollableEvent->Clear())) { + if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) { + // acknowledge pollable event (wait should not block) + if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) { // On Windows, the TCP loopback connection in the // pollable event may become broken when a laptop // switches between wired and wireless networks or // wakes up from hibernation. We try to create a // new pollable event. If that fails, we fall back // on "busy wait". - NS_WARNING("Trying to repair mPollableEvent"); - mPollableEvent.reset(new PollableEvent()); - if (!mPollableEvent->Valid()) { - mPollableEvent = nullptr; + { + DebugMutexAutoLock lock(mLock); + PR_DestroyPollableEvent(mThreadEvent); + mThreadEvent = PR_NewPollableEvent(); } - SOCKET_LOG(("running socket transport thread without " - "a pollable event now valid=%d", mPollableEvent->Valid())); - mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; - mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; + if (!mThreadEvent) { + NS_WARNING("running socket transport thread without " + "a pollable event"); + SOCKET_LOG(("running socket transport thread without " + "a pollable event")); + } + mPollList[0].fd = mThreadEvent; + // mPollList[0].in_flags was already set to PR_POLL_READ + // in Run(). mPollList[0].out_flags = 0; } } diff --git a/netwerk/base/nsSocketTransportService2.h b/netwerk/base/nsSocketTransportService2.h index d1c9ef722824..44f514740d50 100644 --- a/netwerk/base/nsSocketTransportService2.h +++ b/netwerk/base/nsSocketTransportService2.h @@ -19,8 +19,6 @@ #include "mozilla/net/DashboardTypes.h" #include "mozilla/Atomics.h" #include "mozilla/TimeStamp.h" -#include "mozilla/UniquePtr.h" -#include "PollableEvent.h" class nsASocketHandler; struct PRPollDesc; @@ -126,7 +124,14 @@ private: //------------------------------------------------------------------------- nsCOMPtr mThread; // protected by mLock - mozilla::UniquePtr mPollableEvent; + PRFileDesc *mThreadEvent; + // protected by mLock. mThreadEvent may change + // if the old pollable event is broken. only + // the socket thread may change mThreadEvent; + // it needs to lock mLock only when it changes + // mThreadEvent. other threads don't change + // mThreadEvent; they need to lock mLock + // whenever they access mThreadEvent. bool mAutodialEnabled; // pref to control autodial code @@ -168,7 +173,6 @@ private: SocketContext *mActiveList; /* mListSize entries */ SocketContext *mIdleList; /* mListSize entries */ - nsIThread *mRawThread; uint32_t mActiveListSize; uint32_t mIdleListSize; @@ -193,16 +197,18 @@ private: //------------------------------------------------------------------------- // poll list (socket thread only) // - // first element of the poll list is mPollableEvent (or null if the pollable + // first element of the poll list is mThreadEvent (or null if the pollable // event cannot be created). //------------------------------------------------------------------------- PRPollDesc *mPollList; /* mListSize + 1 entries */ PRIntervalTime PollTimeout(); // computes ideal poll timeout - nsresult DoPollIteration(mozilla::TimeDuration *pollDuration); + nsresult DoPollIteration(bool wait, + mozilla::TimeDuration *pollDuration); // perfoms a single poll iteration - int32_t Poll(uint32_t *interval, + int32_t Poll(bool wait, + uint32_t *interval, mozilla::TimeDuration *pollDuration); // calls PR_Poll. the out param // interval indicates the poll diff --git a/netwerk/standalone/moz.build b/netwerk/standalone/moz.build index 794cec94d703..dd3c9c71b1ab 100644 --- a/netwerk/standalone/moz.build +++ b/netwerk/standalone/moz.build @@ -12,7 +12,6 @@ src_list = [ ] netwerk_base_src = [ - 'PollableEvent.cpp', 'nsDNSPrefetch.cpp', 'nsNetAddr.cpp', 'nsSocketTransportService2.cpp', From 865d078f807edfa560179be4ad18ccbc2e3566cd Mon Sep 17 00:00:00 2001 From: Ceridwen Date: Fri, 18 Mar 2016 18:32:37 -0400 Subject: [PATCH 16/28] Bug 1257983 - Fix MANIFEST.in in external-media-tests package; r=maja_zf This MANIFEST.in contained the wrong file name, external-media-tests-requirements.txt rather than requirements.txt. MozReview-Commit-ID: A0zJihjoxzf --HG-- extra : rebase_source : ddc462d754d234af04ed5a37885b89f9187a3942 --- dom/media/test/external/MANIFEST.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dom/media/test/external/MANIFEST.in b/dom/media/test/external/MANIFEST.in index 7b700277dca8..fe0b96e774b0 100644 --- a/dom/media/test/external/MANIFEST.in +++ b/dom/media/test/external/MANIFEST.in @@ -1,6 +1,4 @@ exclude MANIFEST.in -include external-media-tests-requirements.txt +include requirements.txt recursive-include external_media_harness * recursive-include external_media_tests * - - From 31a428dc424919f7d9ad6bbdc7fd2966ed2b9dae Mon Sep 17 00:00:00 2001 From: Gerald Squelart Date: Sun, 20 Mar 2016 16:52:34 +1100 Subject: [PATCH 17/28] Bug 1256533 - Use std::deque instead of nsDeque - r=cpearce The queue of pending block indexes was implemented using nsDeque where item pointers were perverted into pure 32-bit numbers, causing a size mismatch on 64-bit platforms, which was picked by VS2015. Note: We're using a deque because we need to implement a 'contains' method, which would be unreasonably-difficult with a pure queue. MozReview-Commit-ID: HpDBIwgSs9 --HG-- extra : rebase_source : d396e7677a51cac787202dd63653890f19626bf8 --- dom/media/FileBlockCache.cpp | 27 ++++++++++++++++++--------- dom/media/FileBlockCache.h | 36 ++---------------------------------- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/dom/media/FileBlockCache.cpp b/dom/media/FileBlockCache.cpp index 3a9e929144dd..8121b5e4d5c2 100644 --- a/dom/media/FileBlockCache.cpp +++ b/dom/media/FileBlockCache.cpp @@ -87,6 +87,14 @@ void FileBlockCache::Close() } } +template +bool +ContainerContains(const Container& aContainer, const Value& value) +{ + return std::find(aContainer.begin(), aContainer.end(), value) + != aContainer.end(); +} + nsresult FileBlockCache::WriteBlock(uint32_t aBlockIndex, const uint8_t* aData) { MonitorAutoLock mon(mDataMonitor); @@ -99,16 +107,16 @@ nsresult FileBlockCache::WriteBlock(uint32_t aBlockIndex, const uint8_t* aData) bool blockAlreadyHadPendingChange = mBlockChanges[aBlockIndex] != nullptr; mBlockChanges[aBlockIndex] = new BlockChange(aData); - if (!blockAlreadyHadPendingChange || !mChangeIndexList.Contains(aBlockIndex)) { + if (!blockAlreadyHadPendingChange || !ContainerContains(mChangeIndexList, aBlockIndex)) { // We either didn't already have a pending change for this block, or we // did but we didn't have an entry for it in mChangeIndexList (we're in the process // of writing it and have removed the block's index out of mChangeIndexList // in Run() but not finished writing the block to file yet). Add the blocks // index to the end of mChangeIndexList to ensure the block is written as // as soon as possible. - mChangeIndexList.PushBack(aBlockIndex); + mChangeIndexList.push_back(aBlockIndex); } - NS_ASSERTION(mChangeIndexList.Contains(aBlockIndex), "Must have entry for new block"); + NS_ASSERTION(ContainerContains(mChangeIndexList, aBlockIndex), "Must have entry for new block"); EnsureWriteScheduled(); @@ -196,10 +204,10 @@ nsresult FileBlockCache::Run() { MonitorAutoLock mon(mDataMonitor); NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); - NS_ASSERTION(!mChangeIndexList.IsEmpty(), "Only dispatch when there's work to do"); + NS_ASSERTION(!mChangeIndexList.empty(), "Only dispatch when there's work to do"); NS_ASSERTION(mIsWriteScheduled, "Should report write running or scheduled."); - while (!mChangeIndexList.IsEmpty()) { + while (!mChangeIndexList.empty()) { if (!mIsOpen) { // We've been closed, abort, discarding unwritten changes. mIsWriteScheduled = false; @@ -217,7 +225,8 @@ nsresult FileBlockCache::Run() // Hold a reference to the change, in case another change // overwrites the mBlockChanges entry for this block while we drop // mDataMonitor to take mFileMonitor. - int32_t blockIndex = mChangeIndexList.PopFront(); + int32_t blockIndex = mChangeIndexList.front(); + mChangeIndexList.pop_front(); RefPtr change = mBlockChanges[blockIndex]; MOZ_ASSERT(change, "Change index list should only contain entries for blocks " @@ -326,14 +335,14 @@ nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlock } if (mBlockChanges[aDestBlockIndex] == nullptr || - !mChangeIndexList.Contains(aDestBlockIndex)) { + !ContainerContains(mChangeIndexList, aDestBlockIndex)) { // Only add another entry to the change index list if we don't already // have one for this block. We won't have an entry when either there's // no pending change for this block, or if there is a pending change for // this block and we're in the process of writing it (we've popped the // block's index out of mChangeIndexList in Run() but not finished writing // the block to file yet. - mChangeIndexList.PushBack(aDestBlockIndex); + mChangeIndexList.push_back(aDestBlockIndex); } // If the source block hasn't yet been written to file then the dest block @@ -346,7 +355,7 @@ nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlock EnsureWriteScheduled(); - NS_ASSERTION(mChangeIndexList.Contains(aDestBlockIndex), + NS_ASSERTION(ContainerContains(mChangeIndexList, aDestBlockIndex), "Should have scheduled block for change"); return NS_OK; diff --git a/dom/media/FileBlockCache.h b/dom/media/FileBlockCache.h index 58ac39422776..f8ea573414ca 100644 --- a/dom/media/FileBlockCache.h +++ b/dom/media/FileBlockCache.h @@ -14,6 +14,7 @@ #include "MediaCache.h" #include "nsDeque.h" #include "nsThreadUtils.h" +#include struct PRFileDesc; @@ -124,38 +125,6 @@ public: } }; - class Int32Queue : private nsDeque { - public: - int32_t PopFront() { - int32_t front = ObjectAt(0); - nsDeque::PopFront(); - return front; - } - - void PushBack(int32_t aValue) { - nsDeque::Push(reinterpret_cast(aValue)); - } - - bool Contains(int32_t aValue) { - for (size_t i = 0; i < GetSize(); ++i) { - if (ObjectAt(i) == aValue) { - return true; - } - } - return false; - } - - bool IsEmpty() { - return nsDeque::GetSize() == 0; - } - - private: - int32_t ObjectAt(size_t aIndex) { - void* v = nsDeque::ObjectAt(aIndex); - return reinterpret_cast(v); - } - }; - private: int64_t BlockIndexToOffset(int32_t aBlockIndex) { return static_cast(aBlockIndex) * BLOCK_SIZE; @@ -202,8 +171,7 @@ private: // main thread). nsCOMPtr mThread; // Queue of pending block indexes that need to be written or moved. - //AutoTArray mChangeIndexList; - Int32Queue mChangeIndexList; + std::deque mChangeIndexList; // True if we've dispatched an event to commit all pending block changes // to file on mThread. bool mIsWriteScheduled; From dbccc09fbb1f6800fc71ff9777c8c2686168e323 Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Mon, 21 Mar 2016 10:22:44 +0800 Subject: [PATCH 18/28] Bug 907252 - B2G NFC: write xpcshell-test for NFC. r=yoshi --- dom/nfc/gonk/Nfc.js | 22 +- dom/nfc/tests/unit/header_helpers.js | 42 ++ ...entTransactionSystemMessageConfigurator.js | 6 - dom/nfc/tests/unit/test_Nfc.js | 479 ++++++++++++++++++ dom/nfc/tests/unit/xpcshell.ini | 3 +- 5 files changed, 537 insertions(+), 15 deletions(-) create mode 100644 dom/nfc/tests/unit/header_helpers.js create mode 100644 dom/nfc/tests/unit/test_Nfc.js diff --git a/dom/nfc/gonk/Nfc.js b/dom/nfc/gonk/Nfc.js index a8da3fb49456..e6de5d15bc9a 100644 --- a/dom/nfc/gonk/Nfc.js +++ b/dom/nfc/gonk/Nfc.js @@ -152,17 +152,16 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () { init: function init(nfc) { this.nfc = nfc; - - if (!NFC.DEBUG_NFC) { - let lock = gSettingsService.createLock(); - lock.get(NFC.SETTING_NFC_DEBUG, this.nfc); - Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false); - } - Services.obs.addObserver(this, NFC.TOPIC_XPCOM_SHUTDOWN, false); this._registerMessageListeners(); }, + listenDebugEvent: function listenDebugEvent() { + let lock = gSettingsService.createLock(); + lock.get(NFC.SETTING_NFC_DEBUG, this.nfc); + Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false); + }, + _shutdown: function _shutdown() { this.nfc.shutdown(); this.nfc = null; @@ -509,8 +508,15 @@ var SessionHelper = { } }; -function Nfc() { +function Nfc(isXPCShell) { + // TODO: Bug 1239954: xpcshell test timed out with + // SettingsSevice.createlock().get() + // gSettingsService.createLock will cause timeout while running xpshell-test, + // so we try to prevent to run gSettingsService under xpcshell-test here. gMessageManager.init(this); + if (!isXPCShell && !NFC.DEBUG_NFC) { + gMessageManager.listenDebugEvent(); + } this.targetsByRequestId = {}; } diff --git a/dom/nfc/tests/unit/header_helpers.js b/dom/nfc/tests/unit/header_helpers.js new file mode 100644 index 000000000000..4cfe281032cc --- /dev/null +++ b/dom/nfc/tests/unit/header_helpers.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +let NFC_CONSTS = {}; +let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + +Cu.import("resource://gre/modules/nfc_consts.js", NFC_CONSTS); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsISyncMessageSender"); + +function waitAsyncMessage(msg, callback) { + let handler = { + receiveMessage: function (message) { + if (message.name !== msg) { + return; + } + cpmm.removeMessageListener(msg, handler); + callback(message); + } + }; + + cpmm.addMessageListener(msg, handler); +} + +function sendAsyncMessage(msg, rsp, payload, callback) { + waitAsyncMessage(rsp, callback); + cpmm.sendAsyncMessage(msg, payload); +} + +function sendSyncMessage(msg, payload) { + return cpmm.sendSyncMessage(msg, payload); +} + diff --git a/dom/nfc/tests/unit/test_HCIEventTransactionSystemMessageConfigurator.js b/dom/nfc/tests/unit/test_HCIEventTransactionSystemMessageConfigurator.js index 0e618288d474..2f88b467ea5c 100644 --- a/dom/nfc/tests/unit/test_HCIEventTransactionSystemMessageConfigurator.js +++ b/dom/nfc/tests/unit/test_HCIEventTransactionSystemMessageConfigurator.js @@ -3,13 +3,7 @@ "use strict"; -/* globals run_next_test, add_test, ok, equal, Components, XPCOMUtils */ -/* exported run_test */ - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const MANIFEST_URL = "app://system.gaiamobile.org/manifest.webapp"; // secure element access rule format: / diff --git a/dom/nfc/tests/unit/test_Nfc.js b/dom/nfc/tests/unit/test_Nfc.js new file mode 100644 index 000000000000..12705e9d6e39 --- /dev/null +++ b/dom/nfc/tests/unit/test_Nfc.js @@ -0,0 +1,479 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let NFC = {}; +subscriptLoader.loadSubScript("resource://gre/components/Nfc.js", NFC); + +// Mock nfc service +// loadSubScript could not load const and let variables, type defined +// here should be consistent with type defined in Nfc.js +const MockReqType = { + CHANGE_RF_STATE: "changeRFState", + READ_NDEF: "readNDEF", + WRITE_NDEF: "writeNDEF", + MAKE_READ_ONLY: "makeReadOnly", + FORMAT: "format", + TRANSCEIVE: "transceive" +}; + +const MockRspType = {}; +MockRspType[MockReqType.CHANGE_RF_STATE] = "changeRFStateRsp"; +MockRspType[MockReqType.READ_NDEF] = "readNDEFRsp"; +MockRspType[MockReqType.WRITE_NDEF] = "writeNDEFRsp"; +MockRspType[MockReqType.MAKE_READ_ONLY] = "makeReadOnlyRsp"; +MockRspType[MockReqType.FORMAT] = "formatRsp"; +MockRspType[MockReqType.TRANSCEIVE] = "transceiveRsp"; + +const MockNtfType = { + INITIALIZED: "initialized", + TECH_DISCOVERED: "techDiscovered", + TECH_LOST: "techLost", +}; + +const MOCK_RECORDS = {"payload":{"0":4,"1":119,"2":119, + "3":119,"4":46,"5":109, + "6":111,"7":122,"8":105}, + "tnf":"well-known", + "type":{"0":85}}; + +let MockNfcService = { + listener: null, + + setListener: function (listener) { + this.listener = listener; + }, + + notifyEvent: function (event) { + if (this.listener && this.listener.onEvent) { + this.listener.onEvent(event); + } + }, + + start: function (eventListener) { + this.setListener(eventListener); + }, + + shutdown: function () { + this.listener = null; + }, + + sendCommand: function (message) { + switch (message.type) { + case MockReqType.CHANGE_RF_STATE: + this.notifyEvent({ errorMsg: "", + requestId: message.requestId, + rfState: message.rfState, + rspType: MockRspType[message.type] }); + break; + case MockReqType.READ_NDEF: + this.notifyEvent({ errorMsg: "", + requestId: message.requestId, + records: MOCK_RECORDS, + sessionId: message.sessionId, + rspType: MockRspType[message.type] }); + break; + case MockReqType.WRITE_NDEF: + case MockReqType.MAKE_READ_ONLY: + case MockReqType.TRANSCEIVE: + case MockReqType.FORMAT: + this.notifyEvent({ errorMsg: "", + requestId: message.requestId, + sessionId: message.sessionId, + rspType: MockRspType[message.type] }); + break; + default: + throw new Error("Don't know about this message type: " + message.type); + } + } +}; +// end of nfc service mock. + +let messageManager = NFC.gMessageManager; +let sessionHelper = NFC.SessionHelper; + +new NFC.Nfc(/* isXPCShell */ true); + +// It would better to use MockRegistrar but nsINfcService contains +// method marked with implicit_context which may not be implemented in js. +// Use a simple assignment to mock nfcService + +MockNfcService.start(messageManager.nfc); +messageManager.nfc.nfcService = MockNfcService; + +function run_test() { + run_next_test(); +} + +add_test(function test_setFocusTab() { + let tabId = 1; + let rsp = "NFC:DOMEvent"; + let focusOnCb = function (message) { + let result = message.data; + + deepEqual(result, + { tabId: tabId, + event: NFC_CONSTS.FOCUS_CHANGED, + focus: true }, + "Correct result SetFocusTab On"); + + let focusOffCb = function (msg) { + let result = msg.data; + + deepEqual(result, + { tabId: tabId, + event: NFC_CONSTS.FOCUS_CHANGED, + focus: false }, + "Correct result SetFocusTab Off"); + + equal(messageManager.focusId, NFC_CONSTS.SYSTEM_APP_ID); + + sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId }); + + run_next_test(); + }; + + sendAsyncMessage("NFC:SetFocusTab", rsp, { tabId: tabId, isFocus: false }, + focusOffCb); + }; + + sendSyncMessage("NFC:AddEventListener", { tabId: tabId }); + sendAsyncMessage("NFC:SetFocusTab", rsp, { tabId: tabId, isFocus: true }, + focusOnCb); +}); + +add_test(function test_checkP2PRegistrationSucceed() { + let appId = 1; + let requestId = 10; + let rsp = "NFC:CheckP2PRegistrationResponse"; + let sessionId = 1; + let isP2P = true; + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.errorMsg, undefined); + + sendSyncMessage("NFC:UnregisterPeerReadyTarget", { appId: appId }); + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sessionHelper.registerSession(sessionId, isP2P); + sendSyncMessage("NFC:RegisterPeerReadyTarget", { appId: appId }); + sendAsyncMessage("NFC:CheckP2PRegistration", + rsp, + { appId: appId, + requestId: requestId }, + callback); +}); + +add_test(function test_checkP2PRegistrationFailed() { + let appId = 1; + let requestId = 10; + let rsp = "NFC:CheckP2PRegistrationResponse"; + let errorMsg = + NFC_CONSTS.NFC_ERROR_MSG[NFC_CONSTS.NFC_GECKO_ERROR_P2P_REG_INVALID]; + let callback = function (message) { + let result = message.data; + + deepEqual(result, + { requestId: requestId, + errorMsg: errorMsg }, + "Correct result checkP2PRegistrationFailed"); + + run_next_test(); + }; + + sendAsyncMessage("NFC:CheckP2PRegistration", + rsp, + { appId: 10, + requestId: requestId }, + callback); +}); + +add_test(function test_notifyUserAcceptedP2P() { + let appId = 1; + let requestId = 10; + let rsp = "NFC:DOMEvent"; + let sessionId = 1; + let isP2P = true; + let callback = function (message) { + let result = message.data; + + ok(true, "received CheckP2PRegistrationResponse") + + sendSyncMessage("NFC:UnregisterPeerReadyTarget", { appId: appId }); + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sessionHelper.registerSession(sessionId, isP2P); + sendSyncMessage("NFC:RegisterPeerReadyTarget", { appId: appId }); + sendAsyncMessage("NFC:NotifyUserAcceptedP2P", rsp, { appId: appId }, + callback); +}); + +add_test(function test_changeRFState() { + let tabId = 1; + let requestId = 10; + let msg = "NFC:DOMEvent"; + let rfState = NFC_CONSTS.NFC_RF_STATE_LISTEN; + let callback = function (message) { + let result = message.data; + + deepEqual(result, + { tabId: tabId, + event: NFC_CONSTS.RF_EVENT_STATE_CHANGED, + rfState: rfState }, + "Correct result onRFStateChanged"); + + sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId }); + + run_next_test(); + }; + + sendSyncMessage("NFC:AddEventListener", { tabId: tabId }); + sendAsyncMessage("NFC:ChangeRFState", + msg, + { requestId: requestId, + rfState: rfState }, + callback); +}); + +add_test(function test_queryRFState() { + equal(sendSyncMessage("NFC:QueryInfo")[0].rfState, NFC_CONSTS.NFC_RF_STATE_LISTEN); + run_next_test(); +}); + +add_test(function test_onTagFound() { + let tabId = 1; + let msg = "NFC:DOMEvent"; + let sessionId = 10; + let setFocusCb = function (focusMsg) { + let callback = function (message) { + let result = message.data; + + equal(result.tabId, tabId); + equal(result.event, NFC_CONSTS.TAG_EVENT_FOUND); + + run_next_test(); + }; + + waitAsyncMessage(msg, callback); + MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_DISCOVERED, + sessionId: sessionId, + isP2P: false }); + }; + + sendSyncMessage("NFC:AddEventListener", { tabId: tabId }); + sendAsyncMessage("NFC:SetFocusTab", + msg, + { tabId: tabId, + isFocus: true }, + setFocusCb); +}); + +add_test(function test_onTagLost() { + let tabId = 1; + let msg = "NFC:DOMEvent"; + let sessionId = 10; + let callback = function (message) { + let result = message.data; + + equal(result.tabId, tabId); + equal(result.event, NFC_CONSTS.TAG_EVENT_LOST); + + sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId }); + sessionHelper.tokenMap = {}; + sendSyncMessage("NFC:SetFocusTab", + { tabId: tabId, + isFocus: false }); + + run_next_test(); + }; + + waitAsyncMessage(msg, callback); + MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_LOST, + sessionId: sessionId, + isP2P: false }); +}); + +add_test(function test_onPeerFound() { + let tabId = 1; + let msg = "NFC:DOMEvent"; + let sessionId = 10; + let setFocusCb = function (focusMsg) { + let callback = function (message) { + let result = message.data; + + equal(result.tabId, tabId); + equal(result.event, NFC_CONSTS.PEER_EVENT_FOUND); + + run_next_test(); + }; + + waitAsyncMessage(msg, callback); + MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_DISCOVERED, + sessionId: sessionId, + isP2P: true }); + }; + + sendSyncMessage("NFC:AddEventListener", { tabId: tabId }); + sendAsyncMessage("NFC:SetFocusTab", + msg, + { tabId: tabId, + isFocus: true }, + setFocusCb); + +}); + +add_test(function test_onPeerLost() { + let tabId = 1; + let msg = "NFC:DOMEvent"; + let sessionId = 10; + let callback = function (message) { + let result = message.data; + + equal(result.tabId, tabId); + equal(result.event, NFC_CONSTS.PEER_EVENT_LOST); + + sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId }); + sessionHelper.tokenMap = {}; + sendSyncMessage("NFC:SetFocusTab", + { tabId: tabId, + isFocus: false }); + + run_next_test(); + }; + + waitAsyncMessage(msg, callback); + MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_LOST, + sessionId: sessionId, + isP2P: true }); + +}); + +add_test(function test_readNDEF() { + let requestId = 10; + let sessionId = 15; + let msg = "NFC:ReadNDEFResponse"; + let sessionToken = sessionHelper.registerSession(sessionId, false); + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.sessionId, sessionId); + deepEqual(result.records, MOCK_RECORDS); + + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sendAsyncMessage("NFC:ReadNDEF", + msg, + { requestId: requestId, + sessionToken: sessionToken }, + callback); +}); + +add_test(function test_writeNDEF() { + let requestId = 10; + let sessionId = 15; + let msg = "NFC:WriteNDEFResponse"; + let sessionToken = sessionHelper.registerSession(sessionId, false); + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.sessionId, sessionId); + + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sendAsyncMessage("NFC:WriteNDEF", + msg, + { requestId: requestId, + sessionToken: sessionToken, + records: MOCK_RECORDS, + isP2P: true }, + callback); +}); + +add_test(function test_format() { + let requestId = 10; + let sessionId = 15; + let msg = "NFC:FormatResponse"; + let sessionToken = sessionHelper.registerSession(sessionId, false); + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.sessionId, sessionId); + + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sendAsyncMessage("NFC:Format", + msg, + { requestId: requestId, + sessionToken: sessionToken }, + callback); +}); + +add_test(function test_makeReadOnly() { + let requestId = 10; + let sessionId = 15; + let msg = "NFC:MakeReadOnlyResponse"; + let sessionToken = sessionHelper.registerSession(sessionId, false); + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.sessionId, sessionId); + + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sendAsyncMessage("NFC:MakeReadOnly", + msg, + { requestId: requestId, + sessionToken: sessionToken }, + callback); +}); + + +add_test(function test_transceive() { + let requestId = 10; + let sessionId = 15; + let msg = "NFC:TransceiveResponse"; + let sessionToken = sessionHelper.registerSession(sessionId, false); + let callback = function (message) { + let result = message.data; + + equal(result.requestId, requestId); + equal(result.sessionId, sessionId); + + sessionHelper.tokenMap = {}; + + run_next_test(); + }; + + sendAsyncMessage("NFC:Transceive", + msg, + { requestId: requestId, + sessionToken: sessionToken, + technology: "NFCA", + command: "0x50" }, + callback); +}); diff --git a/dom/nfc/tests/unit/xpcshell.ini b/dom/nfc/tests/unit/xpcshell.ini index 5e222f26cc79..cb36c7e3e716 100644 --- a/dom/nfc/tests/unit/xpcshell.ini +++ b/dom/nfc/tests/unit/xpcshell.ini @@ -1,5 +1,6 @@ [DEFAULT] -head = +head = header_helpers.js tail = [test_HCIEventTransactionSystemMessageConfigurator.js] +[test_Nfc.js] From e13588537e21dcd41263c6a88cf99a44d41b9508 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Sat, 19 Mar 2016 18:29:34 +0800 Subject: [PATCH 19/28] Bug 1242783. Part 1 - per comment 11, increase mAmpleAudioThresholdUsecs to 2s to avoid audio underrun when BT is connected. r=kinetik. MozReview-Commit-ID: DBQwrDxnjjh --- dom/media/MediaDecoderStateMachine.cpp | 2 +- dom/media/MediaDecoderStateMachine.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index e00d7fe7c4db..803835783893 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -93,7 +93,7 @@ static const uint32_t LOW_AUDIO_USECS = 300000; // decoding more audio. If we increase the low audio threshold (see // LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not // less than the low audio threshold. -const int64_t AMPLE_AUDIO_USECS = 1000000; +static const int64_t AMPLE_AUDIO_USECS = 2000000; } // namespace detail diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index b2b95a7beb28..baeda0967964 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -952,7 +952,7 @@ private: uint32_t AudioPrerollUsecs() const { MOZ_ASSERT(OnTaskQueue()); - return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs; + return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs / 2; } uint32_t VideoPrerollFrames() const From 1f50efdd99d3beef2b2c21b80665196a29188bd8 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Sat, 19 Mar 2016 18:29:44 +0800 Subject: [PATCH 20/28] Bug 1242783. Part 2 - per comment 12, buffer only 1s when audio is captured. r=kinetik. MozReview-Commit-ID: 2SUSvQzUSgR --- dom/media/MediaDecoderStateMachine.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 803835783893..37158f612f7a 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -2931,6 +2931,15 @@ MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) mAudioCaptured = aCaptured; ScheduleStateMachine(); + + // Don't buffer as much when audio is captured because we don't need to worry + // about high latency audio devices. + mAmpleAudioThresholdUsecs = mAudioCaptured ? + detail::AMPLE_AUDIO_USECS / 2 : + detail::AMPLE_AUDIO_USECS; + if (mIsAudioPrerolling && DonePrerollingAudio()) { + StopPrerollingAudio(); + } } uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const From 2011d3b3285c6505896ac3f93d5e74a19ff3503e Mon Sep 17 00:00:00 2001 From: JerryShih Date: Thu, 17 Mar 2016 01:45:00 -0400 Subject: [PATCH 21/28] Bug 1252414 - Handle lost_context for webgl ClearBuffer*. r=jgilbert --- dom/canvas/WebGL2ContextMRTs.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/dom/canvas/WebGL2ContextMRTs.cpp b/dom/canvas/WebGL2ContextMRTs.cpp index 107cf303f1be..d4803bc09667 100644 --- a/dom/canvas/WebGL2ContextMRTs.cpp +++ b/dom/canvas/WebGL2ContextMRTs.cpp @@ -94,6 +94,10 @@ WebGL2Context::ClearBufferfv_base(GLenum buffer, GLint drawbuffer, const GLfloat void WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Int32Array& value) { + if (IsContextLost()) { + return; + } + value.ComputeLengthAndData(); if (!ValidateClearBuffer("clearBufferiv", buffer, drawbuffer, value.Length())) { return; @@ -105,6 +109,10 @@ WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Int32Ar void WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Sequence& value) { + if (IsContextLost()) { + return; + } + if (!ValidateClearBuffer("clearBufferiv", buffer, drawbuffer, value.Length())) { return; } @@ -115,6 +123,10 @@ WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Sequenc void WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Uint32Array& value) { + if (IsContextLost()) { + return; + } + value.ComputeLengthAndData(); if (!ValidateClearBuffer("clearBufferuiv", buffer, drawbuffer, value.Length())) { return; @@ -126,6 +138,10 @@ WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Uint32 void WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Sequence& value) { + if (IsContextLost()) { + return; + } + if (!ValidateClearBuffer("clearBufferuiv", buffer, drawbuffer, value.Length())) { return; } @@ -136,6 +152,10 @@ WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Sequen void WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Float32Array& value) { + if (IsContextLost()) { + return; + } + value.ComputeLengthAndData(); if (!ValidateClearBuffer("clearBufferfv", buffer, drawbuffer, value.Length())) { return; @@ -147,6 +167,10 @@ WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Float32 void WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Sequence& value) { + if (IsContextLost()) { + return; + } + if (!ValidateClearBuffer("clearBufferfv", buffer, drawbuffer, value.Length())) { return; } @@ -157,6 +181,10 @@ WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Sequenc void WebGL2Context::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) { + if (IsContextLost()) { + return; + } + if (buffer != LOCAL_GL_DEPTH_STENCIL) { return ErrorInvalidEnumInfo("clearBufferfi: buffer", buffer); } From 8de9fb7cf28fcaf6e1ba5588a0ee003344e2f4e0 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 17 Mar 2016 14:53:00 -0400 Subject: [PATCH 22/28] Bug 1257019 - Add move construction to js::Mutex. r=terrence --- js/src/jsapi-tests/testThreadingMutex.cpp | 14 ++++++++ js/src/threading/Mutex.h | 40 +++++++++++++---------- js/src/threading/posix/Mutex.cpp | 21 ++++++------ js/src/threading/windows/Mutex.cpp | 20 +++++++----- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/js/src/jsapi-tests/testThreadingMutex.cpp b/js/src/jsapi-tests/testThreadingMutex.cpp index ed5e8833fa32..f0d3d408e418 100644 --- a/js/src/jsapi-tests/testThreadingMutex.cpp +++ b/js/src/jsapi-tests/testThreadingMutex.cpp @@ -34,3 +34,17 @@ BEGIN_TEST(testThreadingUnlockGuard) return true; } END_TEST(testThreadingUnlockGuard) + +BEGIN_TEST(testThreadingMoveMutex) +{ + js::Mutex mutex; + mutex.lock(); + mutex.unlock(); + + js::Mutex another(mozilla::Move(mutex)); + another.lock(); + another.unlock(); + + return true; +} +END_TEST(testThreadingMoveMutex) diff --git a/js/src/threading/Mutex.h b/js/src/threading/Mutex.h index 2640bfc05492..fae91c3145bf 100644 --- a/js/src/threading/Mutex.h +++ b/js/src/threading/Mutex.h @@ -7,7 +7,12 @@ #ifndef threading_Mutex_h #define threading_Mutex_h +#include +#include + +#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/Move.h" namespace js { @@ -22,28 +27,29 @@ public: void lock(); void unlock(); + Mutex(Mutex&& rhs) + : platformData_(rhs.platformData_) + { + MOZ_ASSERT(this != &rhs, "self move disallowed!"); + rhs.platformData_ = nullptr; + } + + Mutex& operator=(Mutex&& rhs) { + this->~Mutex(); + new (this) Mutex(mozilla::Move(rhs)); + return *this; + } + private: Mutex(const Mutex&) = delete; void operator=(const Mutex&) = delete; - Mutex(Mutex&&) = delete; - void operator=(Mutex&&) = delete; - PlatformData* platformData(); + PlatformData* platformData() { + MOZ_ASSERT(platformData_); + return platformData_; + }; -// Linux and maybe other platforms define the storage size of pthread_mutex_t in -// bytes. However, we must define it as an array of void pointers to ensure -// proper alignment. -#if defined(__APPLE__) && defined(__MACH__) && defined(__i386__) - void* platformData_[11]; -#elif defined(__APPLE__) && defined(__MACH__) && defined(__amd64__) - void* platformData_[8]; -#elif defined(__linux__) - void* platformData_[40 / sizeof(void*)]; -#elif defined(_WIN32) - void* platformData_[6]; -#else - void* platformData_[64 / sizeof(void*)]; -#endif + PlatformData* platformData_; }; } // namespace js diff --git a/js/src/threading/posix/Mutex.cpp b/js/src/threading/posix/Mutex.cpp index 3e26178edc19..bef5b2102aab 100644 --- a/js/src/threading/posix/Mutex.cpp +++ b/js/src/threading/posix/Mutex.cpp @@ -4,23 +4,32 @@ * 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 "mozilla/Assertions.h" - #include +#include "js/Utility.h" + #include "threading/Mutex.h" #include "threading/posix/MutexPlatformData.h" js::Mutex::Mutex() { + AutoEnterOOMUnsafeRegion oom; + platformData_ = js_new(); + if (!platformData_) + oom.crash("js::Mutex::Mutex"); + int r = pthread_mutex_init(&platformData()->ptMutex, NULL); MOZ_RELEASE_ASSERT(r == 0); } js::Mutex::~Mutex() { + if (!platformData_) + return; + int r = pthread_mutex_destroy(&platformData()->ptMutex); MOZ_RELEASE_ASSERT(r == 0); + js_delete(platformData()); } void @@ -36,11 +45,3 @@ js::Mutex::unlock() int r = pthread_mutex_unlock(&platformData()->ptMutex); MOZ_RELEASE_ASSERT(r == 0); } - -js::Mutex::PlatformData* -js::Mutex::platformData() -{ - static_assert(sizeof(platformData_) >= sizeof(PlatformData), - "platformData_ is too small"); - return reinterpret_cast(platformData_); -} diff --git a/js/src/threading/windows/Mutex.cpp b/js/src/threading/windows/Mutex.cpp index ec638100e29c..e3277c314930 100644 --- a/js/src/threading/windows/Mutex.cpp +++ b/js/src/threading/windows/Mutex.cpp @@ -4,11 +4,12 @@ * 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 "mozilla/Assertions.h" #include "mozilla/DebugOnly.h" #include "jswin.h" +#include "js/Utility.h" + #include "threading/Mutex.h" #include "threading/windows/MutexPlatformData.h" @@ -43,6 +44,11 @@ static MutexNativeImports NativeImports; js::Mutex::Mutex() { + AutoEnterOOMUnsafeRegion oom; + platformData_ = js_new(); + if (!platformData_) + oom.crash("js::Mutex::Mutex"); + // This number was adopted from NSPR. const static DWORD LockSpinCount = 1500; BOOL r; @@ -59,7 +65,11 @@ js::Mutex::Mutex() js::Mutex::~Mutex() { + if (!platformData_) + return; + DeleteCriticalSection(&platformData()->criticalSection); + js_delete(platformData()); } void @@ -73,11 +83,3 @@ js::Mutex::unlock() { LeaveCriticalSection(&platformData()->criticalSection); } - -js::Mutex::PlatformData* -js::Mutex::platformData() -{ - static_assert(sizeof(platformData_) >= sizeof(PlatformData), - "platformData_ is too small"); - return reinterpret_cast(platformData_); -} From dc73cc267cacb36def16d6628c7780ed68c2b03a Mon Sep 17 00:00:00 2001 From: JerryShih Date: Sun, 20 Mar 2016 01:07:00 -0400 Subject: [PATCH 23/28] Bug 1257593 - Handle webgl FramebufferTexture2D() with an unbound texture. r=jgilbert --- dom/canvas/WebGLFramebuffer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index 333ed95090e8..448f1e58f981 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -624,6 +624,12 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum attachment, TexImageTarget texImag return; if (tex) { + if (!tex->HasEveryBeenBound()) { + mContext->ErrorInvalidOperation("framebufferTexture2D: the texture" + " is not the name of a texture."); + return; + } + bool isTexture2D = tex->Target() == LOCAL_GL_TEXTURE_2D; bool isTexTarget2D = texImageTarget == LOCAL_GL_TEXTURE_2D; if (isTexture2D != isTexTarget2D) { From 5001f89b5f82705c91d8f30d988424cd24d1e651 Mon Sep 17 00:00:00 2001 From: Greg Weng Date: Fri, 18 Mar 2016 00:43:00 -0400 Subject: [PATCH 24/28] Bug 1198701 - ArrayIterator gets length property after iteration has finished. r=till --- js/src/builtin/Array.js | 32 +++++++++++++++++----- js/src/tests/ecma_6/Array/iterator_next.js | 23 ++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 js/src/tests/ecma_6/Array/iterator_next.js diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 18f4b7ec6221..20634deead86 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -715,43 +715,61 @@ function CreateArrayIterator(obj, kind) { return CreateArrayIteratorAt(obj, kind, 0); } - +// ES6, 22.1.5.2.1 +// https://tc39.github.io/ecma262/#sec-%arrayiteratorprototype%.next function ArrayIteratorNext() { + // Step 1-3. if (!IsObject(this) || !IsArrayIterator(this)) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); } - var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET); + + // Step 4. + var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET); + var result = { value: undefined, done: false }; + + // Step 5. + if (!a) { + result.done = true; + return result; + } + + // Step 6. // The index might not be an integer, so we have to do a generic get here. var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + + // Step 7. var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); - var result = { value: undefined, done: false }; + + // Step 8-9. var len = IsPossiblyWrappedTypedArray(a) ? PossiblyWrappedTypedArrayLength(a) : TO_UINT32(a.length); - // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058. + // Step 10. if (index >= len) { - // When the above is changed to ToLength, use +1/0 here instead - // of MAX_UINT32. - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); + UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null); result.done = true; return result; } + // Step 11. UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); + // Step 16. if (itemKind === ITEM_KIND_VALUE) { result.value = a[index]; return result; } + // Step 13. if (itemKind === ITEM_KIND_KEY_AND_VALUE) { var pair = [index, a[index]]; result.value = pair; return result; } + // Step 12. assert(itemKind === ITEM_KIND_KEY, itemKind); result.value = index; return result; diff --git a/js/src/tests/ecma_6/Array/iterator_next.js b/js/src/tests/ecma_6/Array/iterator_next.js new file mode 100644 index 000000000000..050cd70008b1 --- /dev/null +++ b/js/src/tests/ecma_6/Array/iterator_next.js @@ -0,0 +1,23 @@ +function testIteratorNextGetLength() { + var lengthCalledTimes = 0; + var array = { + __proto__: Array.prototype, + get length() { + lengthCalledTimes += 1; + return { + valueOf() { + return 0; + } + }; + } + }; + var it = array[Symbol.iterator](); + it.next(); + it.next(); + if (typeof reportCompare === 'function') { + reportCompare(1, lengthCalledTimes, + "when an iterator get length zero, it shouldn't access it again"); + } +} +testIteratorNextGetLength(); + From b0bc1ab435252051fe9b06a34c08f6beed8d0e47 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 20 Mar 2016 21:19:14 -0700 Subject: [PATCH 25/28] Bug 1257593 followup, fix bustage from believing a reviewer and then sending a different patch to try CLOSED TREE --- dom/canvas/WebGLFramebuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index 448f1e58f981..3d4b71b11036 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -624,7 +624,7 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum attachment, TexImageTarget texImag return; if (tex) { - if (!tex->HasEveryBeenBound()) { + if (!tex->HasEverBeenBound()) { mContext->ErrorInvalidOperation("framebufferTexture2D: the texture" " is not the name of a texture."); return; From 67054259812de2e09dbf1d4cd16d8758638bac66 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 20 Mar 2016 21:29:29 -0700 Subject: [PATCH 26/28] Back out changeset 1364b664788a (bug 1257019) for check_spidermonkey_style.py failures CLOSED TREE --- js/src/jsapi-tests/testThreadingMutex.cpp | 14 -------- js/src/threading/Mutex.h | 40 ++++++++++------------- js/src/threading/posix/Mutex.cpp | 21 ++++++------ js/src/threading/windows/Mutex.cpp | 20 +++++------- 4 files changed, 36 insertions(+), 59 deletions(-) diff --git a/js/src/jsapi-tests/testThreadingMutex.cpp b/js/src/jsapi-tests/testThreadingMutex.cpp index f0d3d408e418..ed5e8833fa32 100644 --- a/js/src/jsapi-tests/testThreadingMutex.cpp +++ b/js/src/jsapi-tests/testThreadingMutex.cpp @@ -34,17 +34,3 @@ BEGIN_TEST(testThreadingUnlockGuard) return true; } END_TEST(testThreadingUnlockGuard) - -BEGIN_TEST(testThreadingMoveMutex) -{ - js::Mutex mutex; - mutex.lock(); - mutex.unlock(); - - js::Mutex another(mozilla::Move(mutex)); - another.lock(); - another.unlock(); - - return true; -} -END_TEST(testThreadingMoveMutex) diff --git a/js/src/threading/Mutex.h b/js/src/threading/Mutex.h index fae91c3145bf..2640bfc05492 100644 --- a/js/src/threading/Mutex.h +++ b/js/src/threading/Mutex.h @@ -7,12 +7,7 @@ #ifndef threading_Mutex_h #define threading_Mutex_h -#include -#include - -#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -#include "mozilla/Move.h" namespace js { @@ -27,29 +22,28 @@ public: void lock(); void unlock(); - Mutex(Mutex&& rhs) - : platformData_(rhs.platformData_) - { - MOZ_ASSERT(this != &rhs, "self move disallowed!"); - rhs.platformData_ = nullptr; - } - - Mutex& operator=(Mutex&& rhs) { - this->~Mutex(); - new (this) Mutex(mozilla::Move(rhs)); - return *this; - } - private: Mutex(const Mutex&) = delete; void operator=(const Mutex&) = delete; + Mutex(Mutex&&) = delete; + void operator=(Mutex&&) = delete; - PlatformData* platformData() { - MOZ_ASSERT(platformData_); - return platformData_; - }; + PlatformData* platformData(); - PlatformData* platformData_; +// Linux and maybe other platforms define the storage size of pthread_mutex_t in +// bytes. However, we must define it as an array of void pointers to ensure +// proper alignment. +#if defined(__APPLE__) && defined(__MACH__) && defined(__i386__) + void* platformData_[11]; +#elif defined(__APPLE__) && defined(__MACH__) && defined(__amd64__) + void* platformData_[8]; +#elif defined(__linux__) + void* platformData_[40 / sizeof(void*)]; +#elif defined(_WIN32) + void* platformData_[6]; +#else + void* platformData_[64 / sizeof(void*)]; +#endif }; } // namespace js diff --git a/js/src/threading/posix/Mutex.cpp b/js/src/threading/posix/Mutex.cpp index bef5b2102aab..3e26178edc19 100644 --- a/js/src/threading/posix/Mutex.cpp +++ b/js/src/threading/posix/Mutex.cpp @@ -4,32 +4,23 @@ * 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 +#include "mozilla/Assertions.h" -#include "js/Utility.h" +#include #include "threading/Mutex.h" #include "threading/posix/MutexPlatformData.h" js::Mutex::Mutex() { - AutoEnterOOMUnsafeRegion oom; - platformData_ = js_new(); - if (!platformData_) - oom.crash("js::Mutex::Mutex"); - int r = pthread_mutex_init(&platformData()->ptMutex, NULL); MOZ_RELEASE_ASSERT(r == 0); } js::Mutex::~Mutex() { - if (!platformData_) - return; - int r = pthread_mutex_destroy(&platformData()->ptMutex); MOZ_RELEASE_ASSERT(r == 0); - js_delete(platformData()); } void @@ -45,3 +36,11 @@ js::Mutex::unlock() int r = pthread_mutex_unlock(&platformData()->ptMutex); MOZ_RELEASE_ASSERT(r == 0); } + +js::Mutex::PlatformData* +js::Mutex::platformData() +{ + static_assert(sizeof(platformData_) >= sizeof(PlatformData), + "platformData_ is too small"); + return reinterpret_cast(platformData_); +} diff --git a/js/src/threading/windows/Mutex.cpp b/js/src/threading/windows/Mutex.cpp index e3277c314930..ec638100e29c 100644 --- a/js/src/threading/windows/Mutex.cpp +++ b/js/src/threading/windows/Mutex.cpp @@ -4,12 +4,11 @@ * 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 "mozilla/Assertions.h" #include "mozilla/DebugOnly.h" #include "jswin.h" -#include "js/Utility.h" - #include "threading/Mutex.h" #include "threading/windows/MutexPlatformData.h" @@ -44,11 +43,6 @@ static MutexNativeImports NativeImports; js::Mutex::Mutex() { - AutoEnterOOMUnsafeRegion oom; - platformData_ = js_new(); - if (!platformData_) - oom.crash("js::Mutex::Mutex"); - // This number was adopted from NSPR. const static DWORD LockSpinCount = 1500; BOOL r; @@ -65,11 +59,7 @@ js::Mutex::Mutex() js::Mutex::~Mutex() { - if (!platformData_) - return; - DeleteCriticalSection(&platformData()->criticalSection); - js_delete(platformData()); } void @@ -83,3 +73,11 @@ js::Mutex::unlock() { LeaveCriticalSection(&platformData()->criticalSection); } + +js::Mutex::PlatformData* +js::Mutex::platformData() +{ + static_assert(sizeof(platformData_) >= sizeof(PlatformData), + "platformData_ is too small"); + return reinterpret_cast(platformData_); +} From bc647795619d0a72b92798bc6eb41f6b8f8b4e57 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 18 Mar 2016 22:49:46 +1300 Subject: [PATCH 27/28] bug 1211892 read -unico-border-gradient of early Unico versions for ThreeDHighlight and ThreeDShadow r=acomminos MozReview-Commit-ID: KWfQqT5MIjT --HG-- extra : rebase_source : d480fe8538530aafd0b68da9b947f215af119bd1 --- config/system-headers | 1 + widget/gtk/mozgtk/mozgtk.c | 3 + widget/gtk/nsLookAndFeel.cpp | 125 +++++++++++++++++++++++++++++++++-- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/config/system-headers b/config/system-headers index 835da777752f..f8e2b70cbfcc 100644 --- a/config/system-headers +++ b/config/system-headers @@ -245,6 +245,7 @@ cairo-atsui.h cairo-beos.h cairo-ft.h cairo-glitz.h +cairo-gobject.h cairo-pdf.h cairo-ps.h cairo-tee.h diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c index 137fa644471d..82fae8c1b071 100644 --- a/widget/gtk/mozgtk/mozgtk.c +++ b/widget/gtk/mozgtk/mozgtk.c @@ -551,6 +551,8 @@ STUB(gtk_style_context_get_border_color) STUB(gtk_style_context_get_color) STUB(gtk_style_context_get_margin) STUB(gtk_style_context_get_padding) +STUB(gtk_style_context_get_property) +STUB(gtk_style_context_get_state) STUB(gtk_style_context_has_class) STUB(gtk_style_context_new) STUB(gtk_style_context_remove_class) @@ -559,6 +561,7 @@ STUB(gtk_style_context_restore) STUB(gtk_style_context_save) STUB(gtk_style_context_set_path) STUB(gtk_style_context_set_state) +STUB(gtk_style_properties_lookup_property) STUB(gtk_tree_view_column_get_button) STUB(gtk_widget_get_preferred_size) STUB(gtk_widget_get_state_flags) diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index a88340605913..97674c912a08 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -29,6 +29,10 @@ #include "mozilla/gfx/2D.h" +#if MOZ_WIDGET_GTK != 2 +#include +#endif + using mozilla::LookAndFeel; #define GDK_COLOR_TO_NS_RGB(c) \ @@ -61,6 +65,121 @@ nsLookAndFeel::~nsLookAndFeel() #endif } +#if MOZ_WIDGET_GTK != 2 +static void +GetLightAndDarkness(const GdkRGBA& aColor, + double* aLightness, double* aDarkness) +{ + double sum = aColor.red + aColor.green + aColor.blue; + *aLightness = sum * aColor.alpha; + *aDarkness = (3.0 - sum) * aColor.alpha; +} + +static bool +GetGradientColors(const GValue* aValue, + GdkRGBA* aLightColor, GdkRGBA* aDarkColor) +{ + if (!G_TYPE_CHECK_VALUE_TYPE(aValue, CAIRO_GOBJECT_TYPE_PATTERN)) + return false; + + auto pattern = static_cast(g_value_get_boxed(aValue)); + + // Just picking the lightest and darkest colors as simple samples rather + // than trying to blend, which could get messy if there are many stops. + if (CAIRO_STATUS_SUCCESS != + cairo_pattern_get_color_stop_rgba(pattern, 0, nullptr, &aDarkColor->red, + &aDarkColor->green, &aDarkColor->blue, + &aDarkColor->alpha)) + return false; + + double maxLightness, maxDarkness; + GetLightAndDarkness(*aDarkColor, &maxLightness, &maxDarkness); + *aLightColor = *aDarkColor; + + GdkRGBA stop; + for (int index = 1; + CAIRO_STATUS_SUCCESS == + cairo_pattern_get_color_stop_rgba(pattern, index, nullptr, + &stop.red, &stop.green, + &stop.blue, &stop.alpha); + ++index) { + double lightness, darkness; + GetLightAndDarkness(stop, &lightness, &darkness); + if (lightness > maxLightness) { + maxLightness = lightness; + *aLightColor = stop; + } + if (darkness > maxDarkness) { + maxDarkness = darkness; + *aDarkColor = stop; + } + } + + return true; +} + +static bool +GetUnicoBorderGradientColors(GtkStyleContext* aContext, + GdkRGBA* aLightColor, GdkRGBA* aDarkColor) +{ + // Ubuntu 12.04 has GTK engine Unico-1.0.2, which overrides render_frame, + // providing its own border code. Ubuntu 14.04 has + // Unico-1.0.3+14.04.20140109, which does not override render_frame, and + // so does not need special attention. The earlier Unico can be detected + // by the -unico-border-gradient style property it registers. + // gtk_style_properties_lookup_property() is checked first to avoid the + // warning from gtk_style_context_get_property() when the property does + // not exist. (gtk_render_frame() of GTK+ 3.16 no longer uses the + // engine.) + const char* propertyName = "-unico-border-gradient"; + if (!gtk_style_properties_lookup_property(propertyName, nullptr, nullptr)) + return false; + + // -unico-border-gradient is used only when the CSS node's engine is Unico. + GtkThemingEngine* engine; + GtkStateFlags state = gtk_style_context_get_state(aContext); + gtk_style_context_get(aContext, state, "engine", &engine, nullptr); + if (strcmp(g_type_name(G_TYPE_FROM_INSTANCE(engine)), "UnicoEngine") != 0) + return false; + + // draw_border() of Unico engine uses -unico-border-gradient + // in preference to border-color. + GValue value = G_VALUE_INIT; + gtk_style_context_get_property(aContext, propertyName, state, &value); + + bool result = GetGradientColors(&value, aLightColor, aDarkColor); + + g_value_unset(&value); + return result; +} + + +static void +GetBorderColors(GtkStyleContext* aContext, + GdkRGBA* aLightColor, GdkRGBA* aDarkColor) +{ + if (GetUnicoBorderGradientColors(aContext, aLightColor, aDarkColor)) + return; + + GtkStateFlags state = gtk_style_context_get_state(aContext); + gtk_style_context_get_border_color(aContext, state, aDarkColor); + // TODO GTK3 - update aLightColor + // for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles. + // https://bugzilla.mozilla.org/show_bug.cgi?id=978172#c25 + *aLightColor = *aDarkColor; +} + +static void +GetBorderColors(GtkStyleContext* aContext, + nscolor* aLightColor, nscolor* aDarkColor) +{ + GdkRGBA lightColor, darkColor; + GetBorderColors(aContext, &lightColor, &darkColor); + *aLightColor = GDK_RGBA_TO_NS_RGBA(lightColor); + *aDarkColor = GDK_RGBA_TO_NS_RGBA(darkColor); +} +#endif + nsresult nsLookAndFeel::NativeGetColor(ColorID aID, nscolor& aColor) { @@ -1201,12 +1320,8 @@ nsLookAndFeel::Init() GtkWidget *frame = gtk_frame_new(nullptr); gtk_container_add(GTK_CONTAINER(parent), frame); - - // TODO GTK3 - update sFrameOuterLightBorder - // for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles (Bug 978172). style = gtk_widget_get_style_context(frame); - gtk_style_context_get_border_color(style, GTK_STATE_FLAG_NORMAL, &color); - sFrameInnerDarkBorder = sFrameOuterLightBorder = GDK_RGBA_TO_NS_RGBA(color); + GetBorderColors(style, &sFrameOuterLightBorder, &sFrameInnerDarkBorder); gtk_widget_path_free(path); From 522eeca20cfd5c0c893e39ef844602f78baffbb2 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 20 Mar 2016 22:35:54 -0700 Subject: [PATCH 28/28] Back out dbf70d71fe29 (bug 1198701) for breaking test_document.all_iteration.html --- js/src/builtin/Array.js | 32 +++++----------------- js/src/tests/ecma_6/Array/iterator_next.js | 23 ---------------- 2 files changed, 7 insertions(+), 48 deletions(-) delete mode 100644 js/src/tests/ecma_6/Array/iterator_next.js diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 20634deead86..18f4b7ec6221 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -715,61 +715,43 @@ function CreateArrayIterator(obj, kind) { return CreateArrayIteratorAt(obj, kind, 0); } -// ES6, 22.1.5.2.1 -// https://tc39.github.io/ecma262/#sec-%arrayiteratorprototype%.next + function ArrayIteratorNext() { - // Step 1-3. if (!IsObject(this) || !IsArrayIterator(this)) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); } - - // Step 4. - var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET); - var result = { value: undefined, done: false }; - - // Step 5. - if (!a) { - result.done = true; - return result; - } - - // Step 6. + var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET); // The index might not be an integer, so we have to do a generic get here. var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); - - // Step 7. var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); - - // Step 8-9. + var result = { value: undefined, done: false }; var len = IsPossiblyWrappedTypedArray(a) ? PossiblyWrappedTypedArrayLength(a) : TO_UINT32(a.length); - // Step 10. + // FIXME: This should be ToLength, which clamps at 2**53. Bug 924058. if (index >= len) { - UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null); + // When the above is changed to ToLength, use +1/0 here instead + // of MAX_UINT32. + UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff); result.done = true; return result; } - // Step 11. UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); - // Step 16. if (itemKind === ITEM_KIND_VALUE) { result.value = a[index]; return result; } - // Step 13. if (itemKind === ITEM_KIND_KEY_AND_VALUE) { var pair = [index, a[index]]; result.value = pair; return result; } - // Step 12. assert(itemKind === ITEM_KIND_KEY, itemKind); result.value = index; return result; diff --git a/js/src/tests/ecma_6/Array/iterator_next.js b/js/src/tests/ecma_6/Array/iterator_next.js deleted file mode 100644 index 050cd70008b1..000000000000 --- a/js/src/tests/ecma_6/Array/iterator_next.js +++ /dev/null @@ -1,23 +0,0 @@ -function testIteratorNextGetLength() { - var lengthCalledTimes = 0; - var array = { - __proto__: Array.prototype, - get length() { - lengthCalledTimes += 1; - return { - valueOf() { - return 0; - } - }; - } - }; - var it = array[Symbol.iterator](); - it.next(); - it.next(); - if (typeof reportCompare === 'function') { - reportCompare(1, lengthCalledTimes, - "when an iterator get length zero, it shouldn't access it again"); - } -} -testIteratorNextGetLength(); -