mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1212114 - Stop using dom::Promise::MaybeRejectBrokenly() in various FileSystemTaskBase subclasses. r=baku (9f6ea5db27) - Bug 1253534 - Suspicious code with probably reversed parms in call to IsSingleLineTextControl, r=mounir (1a5ee1fd1e) - Bug 769117 - Mochitests for youtube flash -> html5 rewriting; r=bz r=hsivonen (6324471dd9) - Bug 1250148 - FormData should treat empty input type=file as empty string in FormData and as unnamed Blob in HTML submission, r=smaug (7043113247) - Bug 1173320 - patch 1/8 - Implement Directory object as string and not as BlobImpl, r=smaug (eca4bec6ea) - Bug 1173320 - patch 2/8 - Proper naming for the FileSystem path serialization, r=smaug (e1604ff2b1) - Bug 1173320 - patch 3/8 - Improve the Windows path management, r=smaug (f8da8541b5) - Bug 1252347 - Provide missing implementations of nsIBaseWindow::SetPositionDesktopPix. r=emk (97f9b92465) - Bug 1235066 - SVG elements should not display title attributes as tooltips. r=enndeakin (a77e809688) - Bug 1251809 - Add input[type=file] tooltip support for e10s. r=ehsan (9f72cf3cd2) - Bug 1173320 - patch 4/8 - Directory in FileList, r=smaug (c09d445043) - Bug 1246244 - Regression test. r=jaws,Margaret (8de084f9db) - Bug 1224105 - Allow windowless chrome docshells containing content docshells (r=smaug) (3343a2a966) - Bug 1230267 - Inconsistent display of SVG title as tooltip when multi-process is enabled. r=jst (b3fc298b7f) - Bug 1173320 - patch 5/8 - Cleanup manual string path management, r=smaug (8f6f0c4e44) - Bug 1250403 - Part 1. Define ARCH_CPU_ARM64 instead of ARCH_CPU_AARCH64. r=billm (d93a0b54da) - Bug 1250403 - Part 2. Import crbug #354405 for aarch64. r=billm (9dca949bcc) - Bug 1246501 - Add ppc specific atomic operations to ipc/chromium. r=Waldo (8054b612be) - Bug 1257305 - Avoid VS2015 about casting int to void*. r=dvander (6234acf6fc) - Bug 1207401 - Send B2G sandbox logging to both stderr and logcat. r=kang (ae404aa5ca) - Bug 1173320 - patch 6/8 - Make FileList clonable to workers if it doesn't contain Directories, r=smaug (ea6ba42f31) - Bug 1173320 - patch 7/8 - Tests for FileList and Directories, r=smaug (3f11503300) - Bug 1222522, part 1 - Make most dom/devicestorage/ tests work with e10s. r=dhylands (3cc7e339a5) - Bug 1173320 - patch 8/8 - Fix e10s tests for DeviceStorage API, r=smaug (949454bae7) - Bug 1222522, part 2 - Inline devicestorage_cleanup() in dom/devicestorage/ tests. r=dhylands (bcdee11385) - Bug 1258137 - OSFileSystem should not be kept alive by more than 1 Directory, r=smaug (225775f48d) - Bug 1255867. Remove some unnecessary AutoJSAPI uses. r=bholley (618cf018e8) - Bug 1237173 - Part2: Change type of duration to Maybe<StickyTimeDuration>. r=birtles (6b83473e05) - Bug 1238469 - Part 1: Refactor b2g emulator tests to remove the usage of custom mozharness configs; r=ahal (a128c8a8bb) - Bug 1238469 - Part 2: Refactor b2g mulet tests to remove the usage of custom mozharness configs; r=ahal (6c7ced3419) - Bug 1238469 - Part 3: Use b2g_emulator_unittest.py for b2g marionette tests; r=ahal (5fdb88b0e1) - Bug 1237173 - Part3: Throw TypeError if duration is NaN, negative value or not 'auto' string. r=birtles, r=smaug (d60b0318d2) - Bug 1136567 - Marionette test for selection carets' positions after changing orientation of device. r=Automatedtester (e7b25b1e4f) - Bug 1138839 - Part1 - Marionette test for selecting text inside an iframe. r=automatedtester (45db397a94) - Bug 1138839 - Part2 - Fix naming issue in marionette test for selection carets. r=automatedtester (c54850d9d3) - Bug 1198542: Update Marionette element IDs to be valid UUID. r=ato This allows WebDriver compatibility to create valid URI. (1b3de245de) - Bug 1204496: When searching by link text start from the startNode and not the rootNode; r=ato (0dfa3b8830) - Bug 1157725 - Rewrite test_mouse_action.py to be more robust. r=ato (40273abca2) - Bug 1141519: added test that puts marionette into a position that can cause hangs when in content scope; r=jgriffin (c99d8cf325) - Bug 1246407 - Rename parent directories for Marionette client and test harness; r=automatedtester (79d4e521fd) - Bug 1253989 Part 3 - Use @parameterized to rewrite selection mode tests. r=mtseng (baf189f1d5) - Bug 1253989 Part 4 - Rename AccessibleCaret test files. r=mtseng (d562edc8f4) - Bug 1251519 Part 3 - Add regression tests for caret dragging. r=mats (048dd5f103) - Bug 1253989 Part 5 - Remove touch caret and selection carets naming. r=mtseng (9929425b6d) - Bug 1253989 Part 6 - Refactor open_test_html(). r=mtseng (25a2424ca1) - Bug 1251519 Part 4 - Add tests for dragging caret to content boundary. r=mats (489b47269b) - Bug 1251519 Part 5 - Use union rect of child frames for clamping. r=mats (5520416749) - Bug 1216924 - Don't align to tiles (either real or virtual) if displayport suppression is enabled. r=BenWa (d470d188e4) - Bug 1254273 - Align the displayport to a max of 256 pixels even if the layer is larger. r=BenWa (0cf8cc0b6c) - Bug 1257938 part 3: Remove support for the "layout.css.sticky.enabled" pref (so we'll unconditionally support "position: sticky"). r=corey (fe12efd18f) - Bug 1257938 part 1: Adjust automated tests to assume position:sticky is unconditionally supported. r=corey (e749ac579e) - Bug 1257938 part 2: Remove separation between test_position_sticky.html & its helper-file, now that it doesn't need to tweak a pref. r=corey (631edab31a) - Bug 1257491 - Ensure that if the peek-messages code modifies the displayport, we schedule a repaint. r=BenWa (d6fb6ff96c) - Bug 1255006 - Ensure the displayport rect takes priority over a suppressed-margins displayport. r=kats (aa6cbc0250) - Bug 1259235 - Add IsScrollFrameWithSnapping to speed up event regions. r=mstange (2a744c311e) - Bug 1185140 - [css-grid][flexbox] Make grid/flex item blockification happen before creating table pseudos, per the latest specs. r=dholbert (dddb8b17ef) - Bug 1224424 - Replace mask-mode:auto keyword by mask-mode:match-source; r=dbaron (5a51b3b301) - Bug 1252039 - corrected MOZ_ASSERT expression in SeparatorRequiredBetweenTokens. r=dbaron (08790aa514) - Bug 1243734 - Part 1. Use MOZ_ENABLE_MASK_AS_SHORTHAND to define the type of mask property; r=dbaron (d8cd3a1c4a) - Bug 1243734 - Part 2. Set up gCSSProperties depends on mask-as-shorth and; r=dbaron (aa6b0259d8) - Bug 1243734 - Part 3. Set mask-mode reftest as failure before enable mask-as-shorthand; r=dbaron (6a326fbaf2) - Bug 1243734 - Part 5. Add MOZ_ENABLE_MASK_AS_SHORTHAND compile flag; r=ted r=dbaron (fcc1344ac8) - Bug 1142531: Check more bits in nsStyleContext::MoveTo assertion. r=heycam (8b62b139df) - Bug 1258147 - Pierce through display:contents style context ancestors when looking for CB context to compare our writing-mode to. r=jfkthame (956d8c25e5) - Bug 823483 patch 1 - Check for percentage max-width in addition to percentage width when deciding to ignore intrinsic min-width of replaced elements. r=dholbert (f88cb5f6a8) - Bug 823483 patch 3 - Limit effect of percentage width and max-width on intrinsic size to elements with replaced element sizing. r=dholbert (2573c3cfff) - Bug 823483 patch 4 - Make a percentage max-width override a fixed width for replaced element intrinsic size computation. r=dholbert (dda859f06c) - Bug 823483 patch 5 - Make (again) percentage width on text inputs make intrinsic minimum width be 0. r=dholbert (d46ada73ef) - Bug 1247929 patch 2 - Hard-code the Web-compatible set of form controls whose intrinsic minimum inline-size shrinks to 0 when inline-size (width) is specified as a percentage. r=dholbert (e4f0c80fcb) - Bug 1254968 - Add support for running JS builtins' constructors over Xray wrappers without unwrapping the newTarget. r=bholley,f=bz (56213ae395) - Bug 1249123 - Add telemetry for __defineGetter__/__defineSetter__ |this| values. data-review=bsmedberg r=till (52c5fd3488) - Bug 1232639 - Implement Object.{values,entries} in C++ to avoid native call overhead in tight loop. r=jorendorff (7262497283) - Bug 1254966 - Disambiguate JS Telemetry macro names. r=evilpie (781d0916c4) - Bug 1254384: Use generic shell switch syntax in js/src/jit-test tests. r=nbp (6f5975cc55) - Bug 1253016 - Remove legacy __defineGetter__/__defineSetter__ this behavior. r=till (cf1b7ad28c) - Bug 1253016 - Implement and test the new spec for legacy functions. r=till (1ff7762e3e)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<svg width="640px" height="480px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>This is a root SVG element's title</title>
|
||||
<foreignObject>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg1">
|
||||
<title>This is a non-root SVG element title</title>
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
||||
</foreignObject>
|
||||
<text id="text1" x="10px" y="32px" font-size="24px">
|
||||
This contains only <title>
|
||||
<title>
|
||||
|
||||
|
||||
This is a title
|
||||
|
||||
</title>
|
||||
</text>
|
||||
<text id="text2" x="10px" y="96px" font-size="24px">
|
||||
This contains only <desc>
|
||||
<desc>This is a desc</desc>
|
||||
</text>
|
||||
<text id="text3" x="10px" y="128px" font-size="24px" title="ignored for SVG">
|
||||
This contains nothing.
|
||||
</text>
|
||||
<a id="link1" xlink:href="#">
|
||||
This link contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
<text id="text4" x="10px" y="192px" font-size="24px">
|
||||
</text>
|
||||
</a>
|
||||
<a id="link2" xlink:href="#">
|
||||
<text x="10px" y="192px" font-size="24px">
|
||||
This text contains <title>
|
||||
<title>
|
||||
This is a title
|
||||
</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link3" xlink:href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="224px" font-size="24px">
|
||||
This link contains <title> & xlink:title attr.
|
||||
<title>This is a title</title>
|
||||
</text>
|
||||
</a>
|
||||
<a id="link4" xlink:href="#" xlink:title="This is an xlink:title attribute">
|
||||
<text x="10px" y="256px" font-size="24px">
|
||||
This link contains xlink:title attr.
|
||||
</text>
|
||||
</a>
|
||||
<text id="text5" x="10px" y="160px" font-size="24px"
|
||||
xlink:title="This is an xlink:title attribute but it isn't on a link" >
|
||||
This contains nothing.
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -50,9 +50,9 @@ SEARCH_PATHS = [
|
||||
'testing/firefox-ui/harness',
|
||||
'testing/firefox-ui/tests',
|
||||
'testing/luciddream',
|
||||
'testing/marionette/harness',
|
||||
'testing/marionette/harness/marionette/runner/mixins/browsermob-proxy-py',
|
||||
'testing/marionette/client',
|
||||
'testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py',
|
||||
'testing/marionette/driver',
|
||||
'testing/mozbase/mozcrash',
|
||||
'testing/mozbase/mozdebug',
|
||||
'testing/mozbase/mozdevice',
|
||||
|
||||
@@ -258,3 +258,6 @@ https://sha256ee.example.com:443 privileged,cer
|
||||
https://ssl3.example.com:443 privileged,ssl3
|
||||
https://rc4.example.com:443 privileged,rc4
|
||||
https://ssl3rc4.example.com:443 privileged,ssl3,rc4
|
||||
|
||||
# Hosts for youtube rewrite tests
|
||||
https://mochitest.youtube.com:443
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
marionette_driver.pth:testing/marionette/driver
|
||||
browsermobproxy.pth:testing/marionette/client/marionette/runner/mixins/browsermob-proxy-py
|
||||
marionette_driver.pth:testing/marionette/client
|
||||
browsermobproxy.pth:testing/marionette/harness/marionette/runner/mixins/browsermob-proxy-py
|
||||
wptserve.pth:testing/web-platform/tests/tools/wptserve
|
||||
marionette.pth:testing/marionette/client
|
||||
marionette.pth:testing/marionette/harness
|
||||
blessings.pth:python/blessings
|
||||
configobj.pth:python/configobj
|
||||
jsmin.pth:python/jsmin
|
||||
|
||||
@@ -8839,6 +8839,16 @@ fi
|
||||
|
||||
MOZ_CONFIG_ICU()
|
||||
|
||||
dnl ========================================================
|
||||
dnl mask as short hand property
|
||||
dnl ========================================================
|
||||
dnl Uncommenting the next line would enable mask-as-shorthand feature.
|
||||
dnl MOZ_ENABLE_MASK_AS_SHORTHAND=1
|
||||
if test "$MOZ_ENABLE_MASK_AS_SHORTHAND"; then
|
||||
AC_DEFINE(MOZ_ENABLE_MASK_AS_SHORTHAND)
|
||||
fi
|
||||
AC_SUBST(MOZ_ENABLE_MASK_AS_SHORTHAND)
|
||||
|
||||
if test -z "$JS_SHARED_LIBRARY"; then
|
||||
AC_DEFINE(MOZ_STATIC_JS)
|
||||
fi
|
||||
|
||||
@@ -5783,10 +5783,14 @@ nsDocShell::SetPosition(int32_t aX, int32_t aY)
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY)
|
||||
{
|
||||
// Added to nsIBaseWindow in bug 1247335;
|
||||
// implement if a use-case is found.
|
||||
NS_ASSERTION(false, "implement me!");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
|
||||
if (ownerWindow) {
|
||||
return ownerWindow->SetPositionDesktopPix(aX, aY);
|
||||
}
|
||||
|
||||
double scale = 1.0;
|
||||
GetDevicePixelsPerDesktopPixel(&scale);
|
||||
return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/dom/AnimatableBinding.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingBinding.h"
|
||||
#include "mozilla/TimingParams.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -39,26 +40,20 @@ AnimationEffectTiming::SetEndDelay(double aEndDelay)
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectTiming::SetDuration(const UnrestrictedDoubleOrString& aDuration)
|
||||
AnimationEffectTiming::SetDuration(const UnrestrictedDoubleOrString& aDuration,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mTiming.mDuration.IsUnrestrictedDouble() &&
|
||||
aDuration.IsUnrestrictedDouble() &&
|
||||
mTiming.mDuration.GetAsUnrestrictedDouble() ==
|
||||
aDuration.GetAsUnrestrictedDouble()) {
|
||||
Maybe<StickyTimeDuration> newDuration =
|
||||
TimingParams::ParseDuration(aDuration, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTiming.mDuration.IsString() && aDuration.IsString() &&
|
||||
mTiming.mDuration.GetAsString() == aDuration.GetAsString()) {
|
||||
if (mTiming.mDuration == newDuration) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aDuration.IsUnrestrictedDouble()) {
|
||||
mTiming.mDuration.SetAsUnrestrictedDouble() =
|
||||
aDuration.GetAsUnrestrictedDouble();
|
||||
} else {
|
||||
mTiming.mDuration.SetAsString() = aDuration.GetAsString();
|
||||
}
|
||||
mTiming.mDuration = newDuration;
|
||||
|
||||
NotifyTimingUpdate();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ public:
|
||||
void Unlink() override { mEffect = nullptr; }
|
||||
|
||||
void SetEndDelay(double aEndDelay);
|
||||
void SetDuration(const UnrestrictedDoubleOrString& aDuration);
|
||||
void SetDuration(const UnrestrictedDoubleOrString& aDuration,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
void NotifyTimingUpdate();
|
||||
|
||||
@@ -26,6 +26,17 @@ AnimationEffectTimingReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*>
|
||||
return AnimationEffectTimingReadOnlyBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectTimingReadOnly::GetDuration(
|
||||
OwningUnrestrictedDoubleOrString& aRetVal) const
|
||||
{
|
||||
if (mTiming.mDuration) {
|
||||
aRetVal.SetAsUnrestrictedDouble() = mTiming.mDuration->ToMilliseconds();
|
||||
} else {
|
||||
aRetVal.SetAsString().AssignLiteral("auto");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectTimingReadOnly::GetEasing(nsString& aRetVal) const
|
||||
{
|
||||
|
||||
@@ -41,10 +41,7 @@ public:
|
||||
FillMode Fill() const { return mTiming.mFill; }
|
||||
double IterationStart() const { return mTiming.mIterationStart; }
|
||||
double Iterations() const { return mTiming.mIterations; }
|
||||
void GetDuration(OwningUnrestrictedDoubleOrString& aRetVal) const
|
||||
{
|
||||
aRetVal = mTiming.mDuration;
|
||||
}
|
||||
void GetDuration(OwningUnrestrictedDoubleOrString& aRetVal) const;
|
||||
PlaybackDirection Direction() const { return mTiming.mDirection; }
|
||||
void GetEasing(nsString& aRetVal) const;
|
||||
|
||||
|
||||
@@ -238,12 +238,12 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
ComputedTiming result;
|
||||
|
||||
if (aTiming.mDuration.IsUnrestrictedDouble()) {
|
||||
double durationMs = aTiming.mDuration.GetAsUnrestrictedDouble();
|
||||
if (!IsNaN(durationMs) && durationMs >= 0.0f) {
|
||||
result.mDuration = StickyTimeDuration::FromMilliseconds(durationMs);
|
||||
}
|
||||
if (aTiming.mDuration) {
|
||||
MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
|
||||
"Iteration duration should be positive");
|
||||
result.mDuration = aTiming.mDuration.ref();
|
||||
}
|
||||
|
||||
result.mIterations = IsNaN(aTiming.mIterations) || aTiming.mIterations < 0.0f ?
|
||||
1.0f :
|
||||
aTiming.mIterations;
|
||||
|
||||
@@ -204,9 +204,13 @@ public:
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
TimingParams timingParams =
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
|
||||
aGlobal, aTarget, aFrames,
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
|
||||
aGlobal, aTarget, aFrames, timingParams, aRv);
|
||||
}
|
||||
|
||||
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
|
||||
@@ -431,9 +435,13 @@ public:
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
TimingParams timingParams =
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return ConstructKeyframeEffect<KeyframeEffect>(
|
||||
aGlobal, aTarget, aFrames,
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
|
||||
aGlobal, aTarget, aFrames, timingParams, aRv);
|
||||
}
|
||||
|
||||
// More generalized version for Animatable.animate.
|
||||
|
||||
@@ -8,24 +8,6 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
TimingParams::TimingParams(const dom::AnimationEffectTimingProperties& aRhs,
|
||||
const dom::Element* aTarget)
|
||||
: mDuration(aRhs.mDuration)
|
||||
, mDelay(TimeDuration::FromMilliseconds(aRhs.mDelay))
|
||||
, mEndDelay(TimeDuration::FromMilliseconds(aRhs.mEndDelay))
|
||||
, mIterations(aRhs.mIterations)
|
||||
, mIterationStart(aRhs.mIterationStart)
|
||||
, mDirection(aRhs.mDirection)
|
||||
, mFill(aRhs.mFill)
|
||||
{
|
||||
mFunction = AnimationUtils::ParseEasing(aTarget, aRhs.mEasing);
|
||||
}
|
||||
|
||||
TimingParams::TimingParams(double aDuration)
|
||||
{
|
||||
mDuration.SetAsUnrestrictedDouble() = aDuration;
|
||||
}
|
||||
|
||||
template <class OptionsType>
|
||||
static const dom::AnimationEffectTimingProperties&
|
||||
GetTimingProperties(const OptionsType& aOptions);
|
||||
@@ -52,10 +34,18 @@ template <class OptionsType>
|
||||
static TimingParams
|
||||
TimingParamsFromOptionsUnion(
|
||||
const OptionsType& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
TimingParams result;
|
||||
if (aOptions.IsUnrestrictedDouble()) {
|
||||
return TimingParams(aOptions.GetAsUnrestrictedDouble());
|
||||
double durationInMs = aOptions.GetAsUnrestrictedDouble();
|
||||
if (durationInMs >= 0) {
|
||||
result.mDuration.emplace(
|
||||
StickyTimeDuration::FromMilliseconds(durationInMs));
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
}
|
||||
} else {
|
||||
// If aTarget is a pseudo element, we pass its parent element because
|
||||
// TimingParams only needs its owner doc to parse easing and both pseudo
|
||||
@@ -72,41 +62,48 @@ TimingParamsFromOptionsUnion(
|
||||
targetElement = target.GetAsCSSPseudoElement().ParentElement();
|
||||
}
|
||||
}
|
||||
return TimingParams(GetTimingProperties(aOptions), targetElement);
|
||||
const dom::AnimationEffectTimingProperties& timing =
|
||||
GetTimingProperties(aOptions);
|
||||
Maybe<StickyTimeDuration> duration =
|
||||
TimingParams::ParseDuration(timing.mDuration, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return result;
|
||||
}
|
||||
result.mDuration = duration;
|
||||
result.mDelay = TimeDuration::FromMilliseconds(timing.mDelay);
|
||||
result.mEndDelay = TimeDuration::FromMilliseconds(timing.mEndDelay);
|
||||
result.mIterations = timing.mIterations;
|
||||
result.mIterationStart = timing.mIterationStart;
|
||||
result.mDirection = timing.mDirection;
|
||||
result.mFill = timing.mFill;
|
||||
result.mFunction =
|
||||
AnimationUtils::ParseEasing(targetElement, timing.mEasing);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ TimingParams
|
||||
TimingParams::FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget);
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
|
||||
}
|
||||
|
||||
/* static */ TimingParams
|
||||
TimingParams::FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget);
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
|
||||
}
|
||||
|
||||
bool
|
||||
TimingParams::operator==(const TimingParams& aOther) const
|
||||
{
|
||||
bool durationEqual;
|
||||
if (mDuration.IsUnrestrictedDouble()) {
|
||||
durationEqual = aOther.mDuration.IsUnrestrictedDouble() &&
|
||||
(mDuration.GetAsUnrestrictedDouble() ==
|
||||
aOther.mDuration.GetAsUnrestrictedDouble());
|
||||
} else {
|
||||
// We consider all string values and uninitialized values as meaning "auto".
|
||||
// Since mDuration is either a string or uninitialized, we consider it equal
|
||||
// if aOther.mDuration is also either a string or uninitialized.
|
||||
durationEqual = !aOther.mDuration.IsUnrestrictedDouble();
|
||||
}
|
||||
return durationEqual &&
|
||||
return mDuration == aOther.mDuration &&
|
||||
mDelay == aOther.mDelay &&
|
||||
mIterations == aOther.mIterations &&
|
||||
mIterationStart == aOther.mIterationStart &&
|
||||
|
||||
@@ -33,20 +33,39 @@ class ElementOrCSSPseudoElement;
|
||||
struct TimingParams
|
||||
{
|
||||
TimingParams() = default;
|
||||
TimingParams(const dom::AnimationEffectTimingProperties& aTimingProperties,
|
||||
const dom::Element* aTarget);
|
||||
explicit TimingParams(double aDuration);
|
||||
|
||||
static TimingParams FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
|
||||
ErrorResult& aRv);
|
||||
static TimingParams FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// The unitialized state of mDuration represents "auto".
|
||||
// Bug 1237173: We will replace this with Maybe<TimeDuration>.
|
||||
dom::OwningUnrestrictedDoubleOrString mDuration;
|
||||
// Range-checks and validates an UnrestrictedDoubleOrString or
|
||||
// OwningUnrestrictedDoubleOrString object and converts to a
|
||||
// StickyTimeDuration value or Nothing() if aDuration is "auto".
|
||||
// Caller must check aRv.Failed().
|
||||
template <class DoubleOrString>
|
||||
static Maybe<StickyTimeDuration> ParseDuration(DoubleOrString& aDuration,
|
||||
ErrorResult& aRv) {
|
||||
Maybe<StickyTimeDuration> result;
|
||||
if (aDuration.IsUnrestrictedDouble()) {
|
||||
double durationInMs = aDuration.GetAsUnrestrictedDouble();
|
||||
if (durationInMs >= 0) {
|
||||
result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
|
||||
return result;
|
||||
}
|
||||
} else if (aDuration.GetAsString().EqualsLiteral("auto")) {
|
||||
return result;
|
||||
}
|
||||
aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
|
||||
return result;
|
||||
}
|
||||
|
||||
// mDuration.isNothing() represents the "auto" value
|
||||
Maybe<StickyTimeDuration> mDuration;
|
||||
TimeDuration mDelay; // Initializes to zero
|
||||
TimeDuration mEndDelay;
|
||||
double mIterations = 1.0; // Can be NaN, negative, +/-Infinity
|
||||
|
||||
@@ -3425,9 +3425,14 @@ Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
}
|
||||
}
|
||||
|
||||
TimingParams timingParams =
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<KeyframeEffect> effect =
|
||||
KeyframeEffect::Constructor(global, aTarget, frames,
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget), aError);
|
||||
KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+2
-20
@@ -227,12 +227,6 @@ Blob::IsFile() const
|
||||
return mImpl->IsFile();
|
||||
}
|
||||
|
||||
bool
|
||||
Blob::IsDirectory() const
|
||||
{
|
||||
return mImpl->IsDirectory();
|
||||
}
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>*
|
||||
Blob::GetSubBlobImpls() const
|
||||
{
|
||||
@@ -420,11 +414,10 @@ File::Create(nsISupports* aParent, BlobImpl* aImpl)
|
||||
/* static */ already_AddRefed<File>
|
||||
File::Create(nsISupports* aParent, const nsAString& aName,
|
||||
const nsAString& aContentType, uint64_t aLength,
|
||||
int64_t aLastModifiedDate, BlobDirState aDirState)
|
||||
int64_t aLastModifiedDate)
|
||||
{
|
||||
RefPtr<File> 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
|
||||
|
||||
|
||||
+3
-85
@@ -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<RefPtr<BlobImpl>>* 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<File> 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<File> ToFile(const nsAString& aName,
|
||||
@@ -194,7 +173,7 @@ public:
|
||||
static already_AddRefed<File>
|
||||
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 moz_free so it must be allocated by moz_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; }
|
||||
|
||||
+83
-4
@@ -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<JSObject*> 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,76 @@ FileList::GetLength(uint32_t* aLength)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileList::Item(uint32_t aIndex, nsISupports** aFile)
|
||||
FileList::Item(uint32_t aIndex, nsISupports** aValue)
|
||||
{
|
||||
nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
|
||||
file.forget(aFile);
|
||||
if (aIndex >= mFilesOrDirectories.Length()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mFilesOrDirectories[aIndex].IsFile()) {
|
||||
nsCOMPtr<nsIDOMBlob> file = mFilesOrDirectories[aIndex].GetAsFile();
|
||||
file.forget(aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mFilesOrDirectories[aIndex].IsDirectory());
|
||||
RefPtr<Directory> directory = mFilesOrDirectories[aIndex].GetAsDirectory();
|
||||
directory.forget(aValue);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FileList::Item(uint32_t aIndex, Nullable<OwningFileOrDirectory>& aValue,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
if (aIndex >= mFilesOrDirectories.Length()) {
|
||||
aValue.SetNull();
|
||||
return;
|
||||
}
|
||||
|
||||
aValue.SetValue(mFilesOrDirectories[aIndex]);
|
||||
}
|
||||
|
||||
void
|
||||
FileList::IndexedGetter(uint32_t aIndex, bool& aFound,
|
||||
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
aFound = aIndex < mFilesOrDirectories.Length();
|
||||
Item(aIndex, aFileOrDirectory, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
FileList::ToSequence(Sequence<OwningFileOrDirectory>& 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];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
+24
-20
@@ -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,34 @@ public:
|
||||
return static_cast<FileList*>(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<OwningFileOrDirectory>& aFileOrDirectory,
|
||||
ErrorResult& aRv) const;
|
||||
|
||||
void IndexedGetter(uint32_t aIndex, bool& aFound,
|
||||
Nullable<OwningFileOrDirectory>& 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<OwningFileOrDirectory>& aSequence,
|
||||
ErrorResult& aRv) const;
|
||||
|
||||
bool ClonableToDifferentThreadOrProcess() const;
|
||||
|
||||
private:
|
||||
~FileList() {}
|
||||
|
||||
nsTArray<RefPtr<File>> mFiles;
|
||||
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
};
|
||||
|
||||
|
||||
+20
-8
@@ -125,7 +125,7 @@ FormData::Append(const nsAString& aName, Blob& aBlob,
|
||||
return;
|
||||
}
|
||||
|
||||
AddNameBlobPair(aName, file);
|
||||
AddNameBlobOrNullPair(aName, file);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -179,12 +179,18 @@ FormData::Has(const nsAString& aName)
|
||||
}
|
||||
|
||||
nsresult
|
||||
FormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob)
|
||||
FormData::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
|
||||
{
|
||||
MOZ_ASSERT(aBlob);
|
||||
RefPtr<File> file;
|
||||
|
||||
if (!aBlob) {
|
||||
FormDataTuple* data = mFormData.AppendElement();
|
||||
SetNameValuePair(data, aName, EmptyString(), true /* aWasNullBlob */);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<File> file = GetOrCreateFileCalledBlob(*aBlob, rv);
|
||||
file = GetOrCreateFileCalledBlob(*aBlob, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@@ -269,10 +275,12 @@ FormData::GetValueAtIndex(uint32_t aIndex) const
|
||||
void
|
||||
FormData::SetNameValuePair(FormDataTuple* aData,
|
||||
const nsAString& aName,
|
||||
const nsAString& aValue)
|
||||
const nsAString& aValue,
|
||||
bool aWasNullBlob)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
aData->name = aName;
|
||||
aData->wasNullBlob = aWasNullBlob;
|
||||
aData->value.SetAsUSVString() = aValue;
|
||||
}
|
||||
|
||||
@@ -285,6 +293,7 @@ FormData::SetNameFilePair(FormDataTuple* aData,
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
aData->name = aName;
|
||||
aData->wasNullBlob = false;
|
||||
aData->value.SetAsBlob() = aFile;
|
||||
}
|
||||
|
||||
@@ -366,13 +375,16 @@ FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
|
||||
nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
|
||||
|
||||
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
||||
if (mFormData[i].value.IsBlob()) {
|
||||
fs.AddNameBlobPair(mFormData[i].name, mFormData[i].value.GetAsBlob());
|
||||
if (mFormData[i].wasNullBlob) {
|
||||
MOZ_ASSERT(mFormData[i].value.IsUSVString());
|
||||
fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
|
||||
} else if (mFormData[i].value.IsUSVString()) {
|
||||
fs.AddNameValuePair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsUSVString());
|
||||
} else {
|
||||
MOZ_CRASH("This should no be possible.");
|
||||
MOZ_ASSERT(mFormData[i].value.IsBlob());
|
||||
fs.AddNameBlobOrNullPair(mFormData[i].name,
|
||||
mFormData[i].value.GetAsBlob());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -35,6 +35,7 @@ private:
|
||||
struct FormDataTuple
|
||||
{
|
||||
nsString name;
|
||||
bool wasNullBlob;
|
||||
OwningBlobOrUSVString value;
|
||||
};
|
||||
|
||||
@@ -45,7 +46,8 @@ private:
|
||||
|
||||
void SetNameValuePair(FormDataTuple* aData,
|
||||
const nsAString& aName,
|
||||
const nsAString& aValue);
|
||||
const nsAString& aValue,
|
||||
bool aWasNullBlob = false);
|
||||
|
||||
void SetNameFilePair(FormDataTuple* aData,
|
||||
const nsAString& aName,
|
||||
@@ -114,8 +116,8 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual nsresult AddNameBlobPair(const nsAString& aName,
|
||||
Blob* aBlob) override;
|
||||
virtual nsresult AddNameBlobOrNullPair(const nsAString& aName,
|
||||
Blob* aBlob) override;
|
||||
|
||||
typedef bool (*FormDataEntryCallback)(const nsString& aName,
|
||||
const OwningBlobOrUSVString& aValue,
|
||||
|
||||
@@ -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> 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> 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 = File::Create(aHolder->ParentDuringRead(), blobImpl);
|
||||
if (!fileList->Append(file)) {
|
||||
if (tagOrDirectoryType == SCTAG_DOM_BLOB) {
|
||||
MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
|
||||
|
||||
RefPtr<BlobImpl> 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 =
|
||||
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<nsIFile> file;
|
||||
nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
|
||||
getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Directory> 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<RefPtr<BlobImpl>> blobImpls;
|
||||
|
||||
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
|
||||
RefPtr<BlobImpl> 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> 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;
|
||||
}
|
||||
|
||||
@@ -1004,7 +1050,9 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
|
||||
// See if this is a FileList object.
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7576,7 +7576,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.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[uuid(57128a85-34de-42db-a252-84dd57724a59)]
|
||||
[builtinclass, uuid(57128a85-34de-42db-a252-84dd57724a59)]
|
||||
interface nsIDOMFileList : nsISupports
|
||||
{
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
function utf8decode(s) {
|
||||
return decodeURIComponent(escape(s));
|
||||
}
|
||||
|
||||
function utf8encode(s) {
|
||||
return unescape(encodeURIComponent(s));
|
||||
}
|
||||
|
||||
function handleRequest(request, response) {
|
||||
var bodyStream = new BinaryInputStream(request.bodyInputStream);
|
||||
|
||||
var requestBody = "";
|
||||
while ((bodyAvail = bodyStream.available()) > 0) {
|
||||
requestBody += bodyStream.readBytes(bodyAvail);
|
||||
}
|
||||
|
||||
var result = [];
|
||||
|
||||
if (request.method == "POST") {
|
||||
var contentTypeParams = {};
|
||||
request.getHeader("Content-Type").split(/\s*\;\s*/).forEach(function(s) {
|
||||
if (s.indexOf('=') >= 0) {
|
||||
let [name, value] = s.split('=');
|
||||
contentTypeParams[name] = value;
|
||||
}
|
||||
else {
|
||||
contentTypeParams[''] = s;
|
||||
}
|
||||
});
|
||||
|
||||
if (contentTypeParams[''] == "multipart/form-data" &&
|
||||
request.queryString == "") {
|
||||
requestBody.split("--" + contentTypeParams.boundary).slice(1, -1).forEach(function (s) {
|
||||
|
||||
let headers = {};
|
||||
let headerEnd = s.indexOf("\r\n\r\n");
|
||||
s.substr(2, headerEnd-2).split("\r\n").forEach(function(s) {
|
||||
// We're assuming UTF8 for now
|
||||
let [name, value] = s.split(': ');
|
||||
headers[name] = utf8decode(value);
|
||||
});
|
||||
|
||||
let body = s.substring(headerEnd + 4, s.length - 2);
|
||||
if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") {
|
||||
// We're assuming UTF8 for now
|
||||
body = utf8decode(body);
|
||||
}
|
||||
result.push({ headers: headers, body: body});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
|
||||
response.write(utf8encode(JSON.stringify(result)));
|
||||
}
|
||||
@@ -257,6 +257,7 @@ support-files =
|
||||
referrer_change_server.sjs
|
||||
file_change_policy_redirect.html
|
||||
file_bug1198095.js
|
||||
file_bug1250148.sjs
|
||||
|
||||
[test_anonymousContent_api.html]
|
||||
[test_anonymousContent_append_after_reflow.html]
|
||||
@@ -848,3 +849,5 @@ skip-if = buildapp == 'b2g' #no ssl support
|
||||
[test_document.all_iteration.html]
|
||||
[test_bug1198095.html]
|
||||
[test_bug1187157.html]
|
||||
[test_bug769117.html]
|
||||
[test_bug1250148.html]
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
|
||||
let 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
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,14 +15,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=789315
|
||||
|
||||
<script type="text/javascript">
|
||||
var obj = new FormData(document.getElementById('a')).get('b');
|
||||
is(obj, "", "We want an empty string.");
|
||||
|
||||
ok(obj instanceof Blob, "This should return an empty Blob");
|
||||
is(obj.size, 0, "Size should be 0");
|
||||
is(obj.type, "application/octet-stream", "Type should be application/octet-stream");
|
||||
|
||||
var o = obj.slice(10, 100, "foo/bar");
|
||||
is(o.size, 0, "The new blob has size 0");
|
||||
is(o.type, "foo/bar", "The new blob has type foo/bar");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1250148
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1250148 - FormData and HTML submission compatibility</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<form id="form" enctype="multipart/form-data"><input type="file" name="test" /></form>
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var f = document.getElementById('form');
|
||||
var fd = new FormData(f);
|
||||
is(fd.get("test"), "", "We want an empty string.");
|
||||
|
||||
var getAll = fd.getAll("test");
|
||||
ok(Array.isArray(getAll), "We want an array with 1 empty string.");
|
||||
is(getAll.length, 1, "We want an array with 1 empty string.");
|
||||
is(getAll[0], "", "We want an array with 1 empty string.");
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "file_bug1250148.sjs", true);
|
||||
xhr.onload = function() {
|
||||
var obj = JSON.parse(xhr.responseText);
|
||||
|
||||
ok(Array.isArray(obj), "XHR response is an array.");
|
||||
is(obj.length, 1, "XHR response array contains 1 result.");
|
||||
|
||||
ok("headers" in obj[0], "XHR response has an header value");
|
||||
|
||||
ok("Content-Disposition" in obj[0].headers, "XHR response.headers array has a Content-Desposition value");
|
||||
is(obj[0].headers["Content-Disposition"], "form-data; name=\"test\"; filename=\"\"", "Correct Content-Disposition");
|
||||
|
||||
ok("Content-Type" in obj[0].headers, "XHR response.headers array has a Content-Type value");
|
||||
is(obj[0].headers["Content-Type"], "application/octet-stream", "Correct Content-Type");
|
||||
|
||||
ok("body" in obj[0], "XHR response has a body value");
|
||||
is(obj[0].body, "", "Correct body value");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
xhr.send(fd);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=769117
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 769117</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
/** Test for Bug 769117 **/
|
||||
"use strict";
|
||||
function onLoad () {
|
||||
let youtube_url = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
|
||||
let youtube_changed_url = "https://mochitest.youtube.com/embed/Xm5i5kbIXzc";
|
||||
|
||||
function testEmbed(embed) {
|
||||
ok (embed, "Embed node exists");
|
||||
embed = SpecialPowers.wrap(embed);
|
||||
is (embed.srcURI.spec, youtube_changed_url, "Should have src uri of " + youtube_changed_url);
|
||||
}
|
||||
info("Running static embed youtube rewrite test");
|
||||
testEmbed(document.getElementById("testembed"));
|
||||
testEmbed(document.getElementById("testobject"));
|
||||
|
||||
info("Running dynamic embed youtube rewrite test");
|
||||
let embed_dynamic = document.createElement("embed");
|
||||
embed_dynamic.src = "https://mochitest.youtube.com/v/Xm5i5kbIXzc";
|
||||
embed_dynamic.type = "application/x-shockwave-flash";
|
||||
|
||||
document.body.appendChild(embed_dynamic);
|
||||
|
||||
SimpleTest.executeSoon(() =>
|
||||
{
|
||||
testEmbed(embed_dynamic);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="onLoad()">
|
||||
<embed id="testembed"
|
||||
src="https://mochitest.youtube.com/v/Xm5i5kbIXzc"
|
||||
type="application/x-shockwave-flash"
|
||||
allowscriptaccess="always"></embed>
|
||||
<object id="testobject"
|
||||
data="https://mochitest.youtube.com/v/Xm5i5kbIXzc"></embed>
|
||||
</body>
|
||||
</html>
|
||||
@@ -61,18 +61,18 @@ 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() {
|
||||
function create_fileList_forFile() {
|
||||
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
|
||||
var script = SpecialPowers.loadChromeScript(url);
|
||||
|
||||
@@ -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: true, data: fileList.files });
|
||||
script.destroy();
|
||||
next();
|
||||
}
|
||||
@@ -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) &&
|
||||
@@ -113,8 +134,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 +232,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 +286,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 +334,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 +378,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 +424,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 +466,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 +512,7 @@ function test_messagePort_inWorkers() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@@ -498,7 +534,8 @@ function test_messagePort_inWorkers() {
|
||||
}
|
||||
|
||||
var tests = [
|
||||
create_fileList,
|
||||
create_fileList_forFile,
|
||||
create_fileList_forDir,
|
||||
|
||||
test_windowToWindow,
|
||||
test_windowToIframe,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -520,12 +521,7 @@ BrowserElementAudioChannel::NotifyChannel(const nsAString& aEvent,
|
||||
do_GetService("@mozilla.org/system-message-internal;1");
|
||||
MOZ_ASSERT(systemMessenger);
|
||||
|
||||
AutoJSAPI jsAPI;
|
||||
if (!jsAPI.Init(GetOwner())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(jsAPI.cx());
|
||||
JS::Rooted<JS::Value> value(nsContentUtils::RootingCxForThread());
|
||||
value.setInt32((uint32_t)mAudioChannel);
|
||||
|
||||
nsCOMPtr<nsIURI> manifestURI;
|
||||
|
||||
@@ -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<nsString>* 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,
|
||||
|
||||
@@ -573,7 +573,16 @@ DeviceStorageStatics::ResetOverrideRootDir()
|
||||
nsCOMPtr<nsIFile> 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<nsIProperties> dirService
|
||||
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
@@ -583,15 +592,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) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"runtests":{
|
||||
},
|
||||
"excludetests":{
|
||||
"dom/devicestorage/test/test_dirs.html":"excluded",
|
||||
"dom/devicestorage/test/test_storageAreaListener.html":"excluded"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
skip-if = toolkit == 'android' || e10s #bug 781789 & bug 782275
|
||||
support-files =
|
||||
../test/devicestorage_common.js
|
||||
ipc.json
|
||||
|
||||
[test_ipc.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(nested ipc not working) b2g-debug(nested ipc not working) b2g-desktop(nested ipc not working)
|
||||
@@ -1,173 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for OOP DeviceStorage</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
// The crash observer registration functions are stubbed out here to
|
||||
// prevent the iframe test runner from breaking later crash-related tests.
|
||||
function iframeScriptFirst() {
|
||||
SpecialPowers.prototype.registerProcessCrashObservers = () => {};
|
||||
SpecialPowers.prototype.unregisterProcessCrashObservers = () => {};
|
||||
|
||||
content.wrappedJSObject.RunSet.reloadAndRunAll({
|
||||
preventDefault: function() { },
|
||||
__exposedProps__: { preventDefault: 'r' }
|
||||
});
|
||||
}
|
||||
|
||||
function iframeScriptSecond() {
|
||||
let TestRunner = content.wrappedJSObject.TestRunner;
|
||||
|
||||
let oldComplete = TestRunner.onComplete;
|
||||
|
||||
TestRunner.onComplete = function() {
|
||||
TestRunner.onComplete = oldComplete;
|
||||
|
||||
sendAsyncMessage("test:DeviceStorage:ipcTestComplete", {
|
||||
result: JSON.stringify(TestRunner._failedTests)
|
||||
});
|
||||
|
||||
if (oldComplete) {
|
||||
oldComplete();
|
||||
}
|
||||
};
|
||||
|
||||
TestRunner.structuredLogger._dumpMessage = function(msg) {
|
||||
sendAsyncMessage("test:DeviceStorage:ipcTestMessage", { msg: msg });
|
||||
}
|
||||
}
|
||||
|
||||
let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
|
||||
function validStructuredMessage(message) {
|
||||
return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
|
||||
}
|
||||
function onTestMessage(data) {
|
||||
let message = SpecialPowers.wrap(data).data.msg;
|
||||
|
||||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
case "test_end":
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
break;
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onTestComplete() {
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
let spObserver = comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.removeMessageListener("SPPrefService", spObserver);
|
||||
mm.removeMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.removeMessageListener("SPPingService", spObserver);
|
||||
mm.removeMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.removeMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.removeMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage);
|
||||
mm.removeMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete);
|
||||
|
||||
SimpleTest.executeSoon(function () { SimpleTest.finish(); });
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
let iframe = document.createElement("iframe");
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
iframe.id = "iframe";
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "1000px";
|
||||
|
||||
function iframeLoadSecond() {
|
||||
ok(true, "Got second iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
mm.loadFrameScript("data:,(" + iframeScriptSecond.toString() + ")();",
|
||||
false);
|
||||
}
|
||||
|
||||
function iframeLoadFirst() {
|
||||
ok(true, "Got first iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
|
||||
let spObserver =
|
||||
comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.addMessageListener("SPPrefService", spObserver);
|
||||
mm.addMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.addMessageListener("SPPingService", spObserver);
|
||||
mm.addMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.addMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.addMessageListener("test:DeviceStorage:ipcTestMessage", onTestMessage);
|
||||
mm.addMessageListener("test:DeviceStorage:ipcTestComplete", onTestComplete);
|
||||
|
||||
let specialPowersBase = "chrome://specialpowers/content/";
|
||||
mm.loadFrameScript(specialPowersBase + "MozillaLogger.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowersAPI.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowers.js", false);
|
||||
|
||||
mm.loadFrameScript("data:,(" + iframeScriptFirst.toString() + ")();", false);
|
||||
}
|
||||
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
|
||||
// Strip this filename and one directory level and then add "/test".
|
||||
let href = window.location.href;
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
let manifest = "tests/dom/devicestorage/ipc/ipc.json";
|
||||
iframe.src = href + "/test?consoleLevel=INFO&testManifest=" + manifest;
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener("load", function() {
|
||||
|
||||
SpecialPowers.addPermission("browser", true, document);
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
|
||||
["device.storage.enabled", true],
|
||||
["device.storage.testing", true],
|
||||
["device.storage.prompt.testing", true],
|
||||
|
||||
// TODO: remove this as part of bug 820712
|
||||
["network.disable.ipc.security", true],
|
||||
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["browser.pagethumbnails.capturing_disabled", true]
|
||||
]
|
||||
}, runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -41,7 +41,6 @@ LOCAL_INCLUDES += [
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'ipc/mochitest.ini',
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
|
||||
@@ -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>
|
||||
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<nsString>* 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<TokenizerIgnoreNothing>
|
||||
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<nsString> 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
|
||||
@@ -3932,14 +3974,8 @@ DeviceStorageRequestManager::Resolve(uint32_t aId, uint64_t aValue,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIGlobalObject* global = mPending[i].mRequest->GetOwnerGlobal();
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(global))) {
|
||||
return RejectInternal(i, NS_LITERAL_STRING(POST_ERROR_EVENT_UNKNOWN));
|
||||
}
|
||||
|
||||
JS::RootedValue value(jsapi.cx(), JS_NumberValue((double)aValue));
|
||||
JS::RootedValue value(nsContentUtils::RootingCxForThread(),
|
||||
JS_NumberValue((double)aValue));
|
||||
return ResolveInternal(i, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
var oldVal = false;
|
||||
|
||||
|
||||
Object.defineProperty(Array.prototype, "remove", {
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
@@ -17,36 +17,26 @@ Object.defineProperty(Array.prototype, "remove", {
|
||||
}
|
||||
});
|
||||
|
||||
function devicestorage_setup() {
|
||||
|
||||
// ensure that the directory we are writing into is empty
|
||||
try {
|
||||
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");
|
||||
f.remove(true);
|
||||
} catch(e) {}
|
||||
|
||||
function devicestorage_setup(callback) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
try {
|
||||
oldVal = SpecialPowers.getBoolPref("device.storage.enabled");
|
||||
} catch(e) {}
|
||||
SpecialPowers.setBoolPref("device.storage.enabled", true);
|
||||
SpecialPowers.setBoolPref("device.storage.testing", true);
|
||||
SpecialPowers.setBoolPref("device.storage.prompt.testing", true);
|
||||
}
|
||||
}
|
||||
|
||||
function devicestorage_cleanup() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
SpecialPowers.setBoolPref("device.storage.enabled", oldVal);
|
||||
SpecialPowers.setBoolPref("device.storage.testing", false);
|
||||
SpecialPowers.setBoolPref("device.storage.prompt.testing", false);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
function getRandomBuffer() {
|
||||
@@ -75,7 +65,7 @@ function randomFilename(l) {
|
||||
|
||||
function reportErrorAndQuit(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function createTestFiles(storage, paths) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s || (toolkit == 'android' && processor == 'x86') # bug 781789 & bug 782275
|
||||
skip-if = (toolkit == 'android' && processor == 'x86') # Android: bug 781789 & bug 782275
|
||||
support-files = devicestorage_common.js
|
||||
remove_testing_directory.js
|
||||
|
||||
[test_823965.html]
|
||||
# [test_add.html]
|
||||
@@ -9,6 +10,7 @@ support-files = devicestorage_common.js
|
||||
[test_available.html]
|
||||
[test_basic.html]
|
||||
[test_dirs.html]
|
||||
skip-if = e10s # Bug 1063569.
|
||||
# [test_diskSpace.html]
|
||||
# Possible race between the time we write a file, and the
|
||||
# time it takes to be reflected by statfs(). Bug # 791287
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// ensure that the directory we are writing into is empty
|
||||
try {
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var directoryService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var f = directoryService.get("TmpD", Ci.nsIFile);
|
||||
f.appendRelativePath("device-storage-testing");
|
||||
f.remove(true);
|
||||
} catch(e) {}
|
||||
|
||||
sendAsyncMessage('directory-removed', {});
|
||||
@@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=823965
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner (?!?). My IRC nick is DougT. I like Maple cookies."
|
||||
@@ -43,11 +43,11 @@ function getSuccess(e) {
|
||||
var dreq = storage2.delete(mreq.result.name);
|
||||
dreq.onerror = function () {
|
||||
ok(true, "The bug has been fixed");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
dreq.onsuccess = function () {
|
||||
ok(false, "The bug has been fixed");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ function getSuccess(e) {
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -85,7 +85,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
@@ -99,8 +99,9 @@ ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=786922
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function add(storage, mime) {
|
||||
dump("adding: " + mime + "\n");
|
||||
@@ -40,18 +40,18 @@ var tests = [
|
||||
|
||||
function fail(e) {
|
||||
ok(false, "onerror was called");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined)
|
||||
ok(true, "addError was called");
|
||||
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,9 @@ function next(e) {
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=786922
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786922">Mozilla Bug 786922</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function addNamed(storage, mime, fileExtension) {
|
||||
dump("adding: " + mime + " " + fileExtension + "\n");
|
||||
@@ -45,18 +45,18 @@ function fail(e) {
|
||||
ok(false, "addSuccess was called");
|
||||
ok(e.target.error.name == "TypeMismatchError", "Error must be TypeMismatchError");
|
||||
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined)
|
||||
ok(true, "addError was called");
|
||||
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,9 @@ function next(e) {
|
||||
|
||||
next();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -22,16 +22,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=834595
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function availableSuccess(e) {
|
||||
isnot(e.target.result, null, "result should not be null");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function availableError(e) {
|
||||
ok(false, "availableError was called");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
@@ -42,6 +42,8 @@ ok(request, "Should have a non-null request");
|
||||
request.onsuccess = availableSuccess;
|
||||
request.onerror = availableError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
@@ -17,12 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function() {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies."
|
||||
@@ -31,12 +31,12 @@ var gFileReader = new FileReader();
|
||||
|
||||
function getAfterDeleteSuccess(e) {
|
||||
ok(false, "file was deleted not successfully");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getAfterDeleteError(e) {
|
||||
ok(true, "file was deleted successfully");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteSuccess(e) {
|
||||
@@ -53,7 +53,7 @@ function deleteSuccess(e) {
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getSuccess(e) {
|
||||
@@ -88,7 +88,7 @@ function readerCallback(e) {
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -119,7 +119,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
@@ -133,8 +133,9 @@ ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,13 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var freeBytes = -1;
|
||||
var stats = 0;
|
||||
@@ -39,7 +38,7 @@ function stat(s, file_list_length) {
|
||||
stats = stats + 1;
|
||||
|
||||
if (stats == 2) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(true, "hi");
|
||||
@@ -94,8 +93,9 @@ for (var i=0; i < music_files.length; i++) {
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function testingStorage() {
|
||||
return navigator.getDeviceStorage("pictures");
|
||||
@@ -40,20 +40,20 @@ var gFileName = "../owned.png";
|
||||
function fail(e) {
|
||||
ok(false, "addSuccess was called");
|
||||
dump(request);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function next(e) {
|
||||
|
||||
if (e != undefined) {
|
||||
ok(true, "addError was called");
|
||||
ok(true, "addError was called");
|
||||
ok(e.target.error.name == "SecurityError", "Error must be SecurityError");
|
||||
}
|
||||
|
||||
var f = tests.pop();
|
||||
|
||||
if (f == undefined) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -64,10 +64,9 @@ function next(e) {
|
||||
|
||||
next();
|
||||
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,22 +17,22 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function enumerateSuccess(e) {
|
||||
|
||||
if (e.target.result == null) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
dump("We still have length = " + files.length + "\n");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var filename = e.target.result.name;
|
||||
if (filename[0] == "/") {
|
||||
// We got /storageName/prefix/filename
|
||||
@@ -61,7 +61,7 @@ function enumerateSuccess(e) {
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -76,7 +76,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
@@ -96,8 +96,9 @@ for (var i=0; i<files.length; i++) {
|
||||
request.onerror = addError;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function enumerateSuccess(e) {
|
||||
}
|
||||
@@ -39,12 +39,12 @@ try {
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "Calling continue before enumerateSuccess fires should throw");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ function enumerateSuccess(e) {
|
||||
|
||||
if (e.target.result == null) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ function enumerateSuccess(e) {
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -82,7 +82,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
@@ -18,12 +18,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup()
|
||||
devicestorage_setup(function () {
|
||||
|
||||
storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
@@ -70,12 +70,11 @@ var cursor = storage.enumerate({"path": "a", "since": new Date(0) });
|
||||
} catch(e) {throws = true}
|
||||
ok(!throws, "enumerate object parameter with path");
|
||||
|
||||
SimpleTest.finish()
|
||||
|
||||
});
|
||||
|
||||
|
||||
devicestorage_cleanup()
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -22,23 +22,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function freeSpaceSuccess(e) {
|
||||
ok(e.target.result > 0, "free bytes should exist and be greater than zero");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function freeSpaceError(e) {
|
||||
ok(false, "freeSpaceError was called");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -54,8 +54,9 @@ request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -26,15 +26,15 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=855952
|
||||
var file = new Blob(["This is a text file."], {type: "text/plain"});
|
||||
var appendFile = new Blob([" Another text file."], {type: "text/plain"});
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function deleteSuccess(e) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function appendSuccess(e) {
|
||||
@@ -47,7 +47,7 @@ function appendSuccess(e) {
|
||||
|
||||
function appendError(e) {
|
||||
ok(false, "appendError was called.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -83,8 +83,9 @@ ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
ok(gStorage, "Should get storage from sdcard");
|
||||
runtest();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = randomFilename(12);
|
||||
|
||||
@@ -46,12 +46,12 @@ function createDirectorySuccess(d) {
|
||||
|
||||
function getSuccess(d) {
|
||||
ok(d.name === gFileName, "Should get directory - " + gFileName + ".");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive here! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
@@ -63,8 +63,10 @@ var promise = storage.getRoot();
|
||||
ok(promise, "Should have a non-null promise");
|
||||
|
||||
promise.then(getRootSuccess, cbError);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
// The root directory object.
|
||||
var gRoot;
|
||||
@@ -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;
|
||||
@@ -59,7 +59,7 @@ function getSuccess(d) {
|
||||
// Create directory with an existing path.
|
||||
gRoot.createDirectory(gPath).then(function(what) {
|
||||
ok(false, "Should not overwrite an existing directory.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}, function(e) {
|
||||
ok(true, "Creating directory should fail if it already exists.");
|
||||
|
||||
@@ -73,10 +73,10 @@ function getSuccess(d) {
|
||||
// Create the parent directory.
|
||||
d.createDirectory('..').then(function(what) {
|
||||
ok(false, "Should not overwrite an existing directory.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}, function(e) {
|
||||
ok(true, "Accessing parent directory with '..' is not allowed.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function getSuccess(d) {
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, e.name + " error should not arrive here!");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
@@ -98,8 +98,10 @@ ok(promise, "Should have a non-null promise for getRoot.");
|
||||
|
||||
gName = storage.storageName;
|
||||
promise.then(getSuccess, cbError);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
let gTestCount = 0;
|
||||
let gFileReader = new FileReader();
|
||||
@@ -84,7 +84,7 @@ let gTestCases = [
|
||||
|
||||
function next() {
|
||||
if (gTestCount >= gTestCases.length) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
let c = gTestCases[gTestCount++];
|
||||
@@ -122,11 +122,12 @@ storage.getRoot().then(function(dir) {
|
||||
next();
|
||||
}, function(e) {
|
||||
ok(false, e.name + " error should not arrive here!");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=910412
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
// The root directory object.
|
||||
@@ -77,7 +78,7 @@ function getSuccess(r) {
|
||||
break;
|
||||
default:
|
||||
ok(false, "Should not arrive at getSuccess!");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
@@ -137,11 +138,11 @@ function getFailure(e) {
|
||||
testGetFailure(gRoot, "sub1//sub2");
|
||||
break;
|
||||
case 17:
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
default:
|
||||
ok(false, "Should not arrive here!");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
}
|
||||
gTestCount++;
|
||||
@@ -149,12 +150,12 @@ function getFailure(e) {
|
||||
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbSuccess(e) {
|
||||
ok(false, "Should not arrive at cbSuccess!");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
@@ -173,7 +174,7 @@ function createTestFile(path, callback) {
|
||||
|
||||
req.onerror = function(e) {
|
||||
ok(false, "Failed to create " + path + ": " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -197,8 +198,9 @@ createTestFile("sub1/sub2/test.png", function() {
|
||||
promise.then(getSuccess, cbError);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=XXX
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
// The root directory object.
|
||||
@@ -62,12 +62,12 @@ function checkContents1(contents) {
|
||||
|
||||
function checkContents2(contents) {
|
||||
is(contents[0].name, "c.png", "'sub2' should contain 'c.png'");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function handleError(e) {
|
||||
ok(false, "Should not arrive at handleError! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var gStorage = navigator.getDeviceStorage("pictures");
|
||||
@@ -87,11 +87,12 @@ createTestFiles(gStorage, ["sub/a.png", "sub/b.png", "sub/sub2/c.png", "sub/sub3
|
||||
runTests();
|
||||
}, function() {
|
||||
ok(false, "Failed to created test files.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=934368
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
let gStorage = null;
|
||||
let gTestCount = 0;
|
||||
@@ -93,7 +93,7 @@ function runNextTests() {
|
||||
function runTests() {
|
||||
function cbError(e) {
|
||||
ok(false, "Should not arrive at cbError! Error: " + e.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbSuccess(r) {
|
||||
@@ -116,7 +116,7 @@ function runNextTests() {
|
||||
testNextRemove();
|
||||
}, function() {
|
||||
ok(false, "Failed to get test files.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}, cbError);
|
||||
};
|
||||
@@ -124,7 +124,7 @@ function runNextTests() {
|
||||
runTests();
|
||||
}, function() {
|
||||
ok(false, "Failed to created test files.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ function testNextRemove() {
|
||||
return;
|
||||
}
|
||||
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage.");
|
||||
@@ -168,8 +168,9 @@ ok(gStorage, "Should have gotten a storage.");
|
||||
gRemoveDeep = true;
|
||||
runNextTests();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,13 +17,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
// We put the old files in 2 levels deep. When you add a file to a directory
|
||||
// it will modify the parents last modification time, but not the parents
|
||||
// parents. So we want to make sure that even though x's timestamp is earlier
|
||||
@@ -44,7 +45,7 @@ function verifyAndDelete(prefix, files, e) {
|
||||
ok(files.length == 0, "when the enumeration is done, we shouldn't have any files in this array")
|
||||
dump("We still have length = " + files.length + "\n");
|
||||
dump(files + "\n");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,12 +87,12 @@ function addFile(filename, callback) {
|
||||
}
|
||||
getReq.onerror = function(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
addReq.onerror = function(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +116,7 @@ function delFile(filename, callback) {
|
||||
};
|
||||
req.onerror = function(e) {
|
||||
ok(false, "delError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ function afterNewFiles() {
|
||||
};
|
||||
cursor.onerror = function (e) {
|
||||
ok(false, "handleError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,8 +168,9 @@ function addOldFiles() {
|
||||
|
||||
addOldFiles();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = "devicestorage/" + randomFilename(12) + "/hi.png";
|
||||
var gData = "My name is Doug Turner. My IRC nick is DougT. I like Maple cookies."
|
||||
@@ -31,12 +31,12 @@ var gFileReader = new FileReader();
|
||||
|
||||
function getAfterDeleteSuccess(e) {
|
||||
ok(false, "file was deleted not successfully");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getAfterDeleteError(e) {
|
||||
ok(true, "file was deleted successfully");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteSuccess(e) {
|
||||
@@ -54,7 +54,7 @@ function deleteSuccess(e) {
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getSuccess(e) {
|
||||
@@ -92,7 +92,7 @@ function readerCallback(e) {
|
||||
|
||||
function getError(e) {
|
||||
ok(false, "getError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -123,7 +123,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
@@ -149,15 +149,15 @@ 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();
|
||||
});
|
||||
} catch(e) {}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,28 +18,28 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var filename = "devicestorage/aaaa.png"
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
|
||||
function deleteSuccess(e) {
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function deleteError(e) {
|
||||
ok(false, "deleteError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addOverwritingSuccess(e) {
|
||||
ok(false, "addOverwritingSuccess was called.");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addOverwritingError(e) {
|
||||
@@ -85,8 +85,9 @@ function runtest() {
|
||||
|
||||
runtest();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717103">Mozilla Bug 717103</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup()
|
||||
devicestorage_setup(function () {
|
||||
|
||||
ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
|
||||
|
||||
@@ -50,10 +50,11 @@ ok(storage, "videos - Should have getDeviceStorage");
|
||||
var cursor = storage.enumerate();
|
||||
ok(cursor, "Should have a non-null cursor");
|
||||
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1126694
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup()
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var XPCOMUtils = SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
|
||||
var Ci = SpecialPowers.Ci;
|
||||
@@ -49,7 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1126694
|
||||
}
|
||||
else if (e.operation == "removed") {
|
||||
ok (true, "got removal event");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,6 +58,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1126694
|
||||
|
||||
volumeService.createFakeVolume(volName, mountPoint);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
@@ -22,23 +22,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
function usedSpaceSuccess(e) {
|
||||
ok(e.target.result > 0, "total bytes should exist and be greater than zero");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function usedSpaceError(e) {
|
||||
ok(false, "usedSpaceError was called");
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var storage = navigator.getDeviceStorage("pictures");
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function addSuccess(e) {
|
||||
@@ -54,6 +54,8 @@ request = storage.addNamed(createRandomBlob('image/png'), prefix + "/a/b.png");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
@@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = randomFilename(20) + ".png"
|
||||
|
||||
@@ -31,7 +31,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
@@ -51,7 +51,7 @@ function onChange(e) {
|
||||
if (filename == gFileName) {
|
||||
ok(true, "we saw the file get created");
|
||||
storage.removeEventListener("change", onChange);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
else {
|
||||
// we may see other file changes during the test, and
|
||||
@@ -69,6 +69,8 @@ ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
@@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=717103
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
devicestorage_setup();
|
||||
devicestorage_setup(function () {
|
||||
|
||||
var gFileName = randomFilename(20) + ".png"
|
||||
|
||||
@@ -31,7 +31,7 @@ function addSuccess(e) {
|
||||
|
||||
function addError(e) {
|
||||
ok(false, "addError was called : " + e.target.error.name);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function onChange(e) {
|
||||
@@ -51,7 +51,7 @@ function onChange(e) {
|
||||
if (filename == gFileName) {
|
||||
ok(true, "we saw the file get created");
|
||||
storage.removeEventListener("change", onChange);
|
||||
devicestorage_cleanup();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
else {
|
||||
// we may see other file changes during the test, and
|
||||
@@ -78,6 +78,8 @@ ok(request, "Should have a non-null request");
|
||||
request.onsuccess = addSuccess;
|
||||
request.onerror = addError;
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
+16
-48
@@ -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<nsIDOMDataTransfer*>(this));
|
||||
if (!mFileList) {
|
||||
mFileList = new FileList(static_cast<nsIDOMDataTransfer*>(this));
|
||||
|
||||
uint32_t count = mItems.Length();
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
nsCOMPtr<nsIVariant> 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,18 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal
|
||||
MOZ_ASSERT(domFile);
|
||||
}
|
||||
|
||||
if (!mFiles->Append(domFile)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mFileList->Append(domFile);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -874,7 +873,7 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mFiles) {
|
||||
if (!mFileList) {
|
||||
GetFiles(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@@ -882,40 +881,9 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
|
||||
|
||||
if (mFiles && mFiles->Length()) {
|
||||
if (!filesAndDirsSeq.SetLength(mFiles->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<OSFileSystem> fs = new OSFileSystem(dirname);
|
||||
fs->Init(parentNode->OwnerDoc()->GetInnerWindow());
|
||||
|
||||
RefPtr<Directory> directory = new Directory(fs, basename);
|
||||
directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
|
||||
filesAndDirsSeq[i].SetAsDirectory() = directory;
|
||||
} else {
|
||||
filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
|
||||
}
|
||||
}
|
||||
mFileList->ToSequence(filesAndDirsSeq, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p->MaybeResolve(filesAndDirsSeq);
|
||||
|
||||
@@ -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<nsTArray<TransferItem> > mItems;
|
||||
|
||||
// array of files, containing only the files present in the dataTransfer
|
||||
RefPtr<FileList> mFiles;
|
||||
// array of files and directories, containing only the files present in the
|
||||
// dataTransfer
|
||||
RefPtr<FileList> mFileList;
|
||||
|
||||
// the target of the drag. The drag and dragend events will fire at this.
|
||||
nsCOMPtr<mozilla::dom::Element> mDragTarget;
|
||||
|
||||
@@ -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> blobImpl = actor->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "CreateDirectoryTask.h"
|
||||
|
||||
#include "DOMError.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
@@ -17,33 +16,73 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aPath)
|
||||
/* static */ already_AddRefed<CreateDirectoryTask>
|
||||
CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
RefPtr<CreateDirectoryTask> task =
|
||||
new CreateDirectoryTask(aFileSystem, aTargetPath);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
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>
|
||||
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<CreateDirectoryTask> 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()
|
||||
@@ -60,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
|
||||
@@ -92,13 +150,8 @@ CreateDirectoryTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> 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;
|
||||
}
|
||||
@@ -107,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
|
||||
@@ -121,13 +178,16 @@ CreateDirectoryTask::HandlerCallback()
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
RefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeRejectBrokenly(domError);
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
|
||||
mTargetPath,
|
||||
Directory::eNotDOMRootDirectory,
|
||||
mFileSystem);
|
||||
MOZ_ASSERT(dir);
|
||||
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
@@ -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<CreateDirectoryTask>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<CreateDirectoryTask>
|
||||
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<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "DOMError.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
@@ -26,34 +25,104 @@ namespace dom {
|
||||
|
||||
uint32_t CreateFileTask::sOutputBufferSize = 0;
|
||||
|
||||
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aPath)
|
||||
, mReplace(replace)
|
||||
/* static */ already_AddRefed<CreateFileTask>
|
||||
CreateFileTask::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool aReplace,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
GetOutputBufferSize();
|
||||
|
||||
RefPtr<CreateFileTask> 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<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
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>
|
||||
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<CreateFileTask> 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<BlobParent*>(static_cast<PBlobParent*>(data));
|
||||
RefPtr<BlobImpl> 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,
|
||||
@@ -62,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<BlobParent*>(static_cast<PBlobParent*>(data));
|
||||
RefPtr<BlobImpl> 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()
|
||||
@@ -108,16 +154,22 @@ 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
|
||||
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
|
||||
BlobChild* actor =
|
||||
ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
|
||||
if (actor) {
|
||||
param.data() = actor;
|
||||
}
|
||||
@@ -128,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);
|
||||
@@ -141,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;
|
||||
@@ -152,7 +205,7 @@ CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
nsresult
|
||||
CreateFileTask::Work()
|
||||
{
|
||||
class AutoClose
|
||||
class MOZ_RAII AutoClose final
|
||||
{
|
||||
public:
|
||||
explicit AutoClose(nsIOutputStream* aStream)
|
||||
@@ -165,6 +218,7 @@ CreateFileTask::Work()
|
||||
{
|
||||
mStream->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIOutputStream> mStream;
|
||||
};
|
||||
@@ -177,24 +231,19 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> 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;
|
||||
}
|
||||
@@ -208,19 +257,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<nsIOutputStream> outputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@@ -263,7 +312,7 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mTargetBlobImpl = new BlobImplFile(file);
|
||||
mTargetBlobImpl = new BlobImplFile(mTargetPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -282,7 +331,7 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
mTargetBlobImpl = new BlobImplFile(file);
|
||||
mTargetBlobImpl = new BlobImplFile(mTargetPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -297,15 +346,14 @@ CreateFileTask::HandlerCallback()
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
RefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeRejectBrokenly(domError);
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
mBlobData = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
mTargetBlobImpl);
|
||||
mPromise->MaybeResolve(blob);
|
||||
mPromise = nullptr;
|
||||
mBlobData = nullptr;
|
||||
|
||||
@@ -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<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv);
|
||||
CreateFileTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
static already_AddRefed<CreateFileTask>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aFile,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<CreateFileTask>
|
||||
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<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
|
||||
// Not thread-safe and should be released on main thread.
|
||||
RefPtr<Blob> mBlobData;
|
||||
|
||||
@@ -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!");
|
||||
@@ -31,12 +30,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);
|
||||
|
||||
@@ -46,19 +39,16 @@ 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<nsIFile> rootFile;
|
||||
DeviceStorageFile::GetRootDirectoryForType(aStorageType,
|
||||
aStorageName,
|
||||
getter_AddRefs(rootFile));
|
||||
|
||||
NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
|
||||
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
|
||||
mNormalizedLocalRootPath);
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -72,6 +62,17 @@ DeviceStorageFileSystem::~DeviceStorageFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<FileSystemBase>
|
||||
DeviceStorageFileSystem::Clone()
|
||||
{
|
||||
RefPtr<DeviceStorageFileSystem> fs =
|
||||
new DeviceStorageFileSystem(mStorageType, mStorageName);
|
||||
|
||||
fs->mWindowId = mWindowId;
|
||||
|
||||
return fs.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
|
||||
{
|
||||
@@ -89,13 +90,13 @@ DeviceStorageFileSystem::Shutdown()
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
DeviceStorageFileSystem::GetWindow() const
|
||||
nsISupports*
|
||||
DeviceStorageFileSystem::GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
MOZ_ASSERT_IF(!mShutdown, window);
|
||||
return window;
|
||||
return ToSupports(window);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -111,12 +112,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<nsIFile> 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;
|
||||
}
|
||||
|
||||
@@ -132,10 +136,32 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aDir);
|
||||
RefPtr<FileSystemBase> fs = aDir->GetFileSystem();
|
||||
MOZ_ASSERT(fs);
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
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
|
||||
|
||||
@@ -27,11 +27,14 @@ public:
|
||||
|
||||
// Overrides FileSystemBase
|
||||
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() override;
|
||||
|
||||
virtual void
|
||||
Shutdown() override;
|
||||
|
||||
virtual nsPIDOMWindow*
|
||||
GetWindow() const override;
|
||||
virtual nsISupports*
|
||||
GetParentObject() const override;
|
||||
|
||||
virtual void
|
||||
GetRootName(nsAString& aRetval) const override;
|
||||
@@ -41,6 +44,10 @@ public:
|
||||
|
||||
virtual bool
|
||||
IsSafeDirectory(Directory* aDir) const override;
|
||||
|
||||
virtual void
|
||||
SerializeDOMPath(nsAString& aSerializedString) const override;
|
||||
|
||||
private:
|
||||
virtual
|
||||
~DeviceStorageFileSystem();
|
||||
|
||||
+233
-103
@@ -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
|
||||
@@ -34,15 +35,70 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
TokenizerIgnoreNothing(char16_t /* aChar */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
IsValidRelativeDOMPath(const nsString& aPath, nsTArray<nsString>& 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.
|
||||
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
|
||||
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)
|
||||
tmp->mFileSystem->Unlink();
|
||||
if (tmp->mFileSystem) {
|
||||
tmp->mFileSystem->Unlink();
|
||||
tmp->mFileSystem = nullptr;
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
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(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@@ -59,33 +115,73 @@ NS_INTERFACE_MAP_END
|
||||
already_AddRefed<Promise>
|
||||
Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
|
||||
aFileSystem, EmptyString(), true, aRv);
|
||||
if (aRv.Failed()) {
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
nsCOMPtr<nsIFile> path;
|
||||
aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()),
|
||||
true, getter_AddRefs(path));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetFileOrDirectoryTask> 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>
|
||||
Directory::Create(nsISupports* aParent, 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(aParent);
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
#ifdef DEBUG
|
||||
bool isDir;
|
||||
nsresult rv = aFile->IsDirectory(&isDir);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir);
|
||||
#endif
|
||||
|
||||
RefPtr<Directory> directory =
|
||||
new Directory(aParent, aFile, aType, aFileSystem);
|
||||
return directory.forget();
|
||||
}
|
||||
|
||||
Directory::Directory(nsISupports* aParent,
|
||||
nsIFile* aFile,
|
||||
DirectoryType aType,
|
||||
FileSystemBase* aFileSystem)
|
||||
: mParent(aParent)
|
||||
, mFile(aFile)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
nsISupports*
|
||||
Directory::GetParentObject() const
|
||||
{
|
||||
return mFileSystem->GetWindow();
|
||||
return mParent;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -95,25 +191,28 @@ Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> 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<FileSystemBase> 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<Promise>
|
||||
Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsAutoString realPath;
|
||||
RefPtr<Blob> blobData;
|
||||
InfallibleTArray<uint8_t> arrayData;
|
||||
bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
|
||||
@@ -138,15 +237,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
}
|
||||
}
|
||||
|
||||
if (!DOMPathToRealPath(aPath, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CreateFileTask> 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 +259,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
already_AddRefed<Promise>
|
||||
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<CreateDirectoryTask> task = new CreateDirectoryTask(
|
||||
mFileSystem, realPath, aRv);
|
||||
if (aRv.Failed()) {
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CreateDirectoryTask> 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 +281,21 @@ Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
|
||||
already_AddRefed<Promise>
|
||||
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<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
|
||||
mFileSystem, realPath, false, aRv);
|
||||
if (aRv.Failed()) {
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetFileOrDirectoryTask> 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 +318,33 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsAutoString realPath;
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
RefPtr<BlobImpl> blob;
|
||||
|
||||
// Check and get the target path.
|
||||
|
||||
RefPtr<FileSystemBase> 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<RemoveTask> task = new RemoveTask(mFileSystem, mPath, blob, realPath,
|
||||
aRecursive, aRv);
|
||||
if (aRv.Failed()) {
|
||||
RefPtr<RemoveTask> task =
|
||||
RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
task->SetError(error);
|
||||
@@ -237,23 +353,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.
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
|
||||
if (mType == eDOMRootDirectory) {
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
|
||||
} else {
|
||||
aRetval = mPath;
|
||||
// TODO: this should be a bit different...
|
||||
GetName(aRetval, aRv);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
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<Promise>
|
||||
Directory::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetDirectoryListingTask> 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 +399,38 @@ Directory::SetContentFilters(const nsAString& aFilters)
|
||||
}
|
||||
|
||||
FileSystemBase*
|
||||
Directory::GetFileSystem() const
|
||||
Directory::GetFileSystem(ErrorResult& aRv)
|
||||
{
|
||||
return mFileSystem.get();
|
||||
if (!mFileSystem) {
|
||||
nsCOMPtr<nsIFile> 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<OSFileSystem> fs = new OSFileSystem(path);
|
||||
fs->Init(mParent);
|
||||
|
||||
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;
|
||||
|
||||
@@ -285,49 +438,26 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
|
||||
static const char kWhitespace[] = "\b\t\r\n ";
|
||||
relativePath.Trim(kWhitespace);
|
||||
|
||||
if (!IsValidRelativePath(relativePath)) {
|
||||
return false;
|
||||
nsTArray<nsString> parts;
|
||||
if (!IsValidRelativeDOMPath(relativePath, parts)) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
|
||||
relativePath;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
Directory::IsValidRelativePath(const nsString& aPath)
|
||||
{
|
||||
// We don't allow empty relative path to access the root.
|
||||
if (aPath.IsEmpty()) {
|
||||
return false;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = mFile->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 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;
|
||||
for (uint32_t i = 0; i < parts.Length(); ++i) {
|
||||
rv = file->AppendRelativePath(parts[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
file.forget(aFile);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
+49
-16
@@ -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
|
||||
@@ -41,25 +40,48 @@ class Directory final
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
struct BlobImplOrDirectoryPath
|
||||
{
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
nsString mDirectoryPath;
|
||||
|
||||
enum {
|
||||
eBlobImpl,
|
||||
eDirectoryPath
|
||||
} mType;
|
||||
};
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
|
||||
|
||||
public:
|
||||
static already_AddRefed<Promise>
|
||||
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<Directory>
|
||||
Create(nsISupports* aParent, nsIFile* aDirectory,
|
||||
DirectoryType aType, FileSystemBase* aFileSystem = 0);
|
||||
|
||||
// ========= Begin WebIDL bindings. ===========
|
||||
|
||||
nsPIDOMWindow*
|
||||
nsISupports*
|
||||
GetParentObject() const;
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetName(nsAString& aRetval) const;
|
||||
GetName(nsAString& aRetval, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
@@ -80,10 +102,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<Promise>
|
||||
GetFilesAndDirectories();
|
||||
GetFilesAndDirectories(ErrorResult& aRv);
|
||||
|
||||
// =========== End WebIDL bindings.============
|
||||
|
||||
@@ -113,26 +138,34 @@ public:
|
||||
SetContentFilters(const nsAString& aFilters);
|
||||
|
||||
FileSystemBase*
|
||||
GetFileSystem() const;
|
||||
private:
|
||||
~Directory();
|
||||
GetFileSystem(ErrorResult& aRv);
|
||||
|
||||
static bool
|
||||
IsValidRelativePath(const nsString& aPath);
|
||||
DirectoryType Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
private:
|
||||
Directory(nsISupports* aParent,
|
||||
nsIFile* aFile, DirectoryType aType,
|
||||
FileSystemBase* aFileSystem = nullptr);
|
||||
~Directory();
|
||||
|
||||
/*
|
||||
* 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<Promise>
|
||||
RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<FileSystemBase> mFileSystem;
|
||||
nsString mPath;
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
DirectoryType mType;
|
||||
|
||||
nsString mFilters;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
// static
|
||||
already_AddRefed<FileSystemBase>
|
||||
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<OSFileSystem>(new OSFileSystem(aString)).forget();
|
||||
}
|
||||
|
||||
@@ -57,37 +58,19 @@ FileSystemBase::Shutdown()
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
FileSystemBase::GetWindow() const
|
||||
nsISupports*
|
||||
FileSystemBase::GetParentObject() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileSystemBase::GetLocalFile(const nsAString& aRealPath) const
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"Should be on parent process!");
|
||||
nsAutoString localPath;
|
||||
FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
|
||||
localPath = mLocalRootPath + localPath;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = NS_NewLocalFile(localPath, 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 +78,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
|
||||
@@ -110,19 +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
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@@ -25,28 +23,22 @@ public:
|
||||
|
||||
// Create file system object from its string representation.
|
||||
static already_AddRefed<FileSystemBase>
|
||||
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 nsPIDOMWindow*
|
||||
GetWindow() const;
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() = 0;
|
||||
|
||||
/**
|
||||
* Create nsIFile object from the given real path (absolute DOM path).
|
||||
*/
|
||||
already_AddRefed<nsIFile>
|
||||
GetLocalFile(const nsAString& aRealPath) const;
|
||||
virtual nsISupports*
|
||||
GetParentObject() const;
|
||||
|
||||
/*
|
||||
* Get the virtual name of the root directory. This name will be exposed to
|
||||
@@ -73,13 +65,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.
|
||||
@@ -103,21 +90,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;
|
||||
|
||||
// The string representation of the file system.
|
||||
nsString mString;
|
||||
|
||||
bool mShutdown;
|
||||
|
||||
// The permission name required to access the file system.
|
||||
|
||||
@@ -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!");
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@ 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()); \
|
||||
task = new name##Task(mFileSystem, p, this); \
|
||||
mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \
|
||||
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<FileSystemTaskBase> task;
|
||||
ErrorResult rv;
|
||||
|
||||
switch (aParams.type()) {
|
||||
|
||||
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
class FileSystemReleaseRunnable : public nsRunnable
|
||||
class FileSystemReleaseRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
|
||||
@@ -106,12 +106,22 @@ FileSystemTaskBase::Start()
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString serialization;
|
||||
mFileSystem->SerializeDOMPath(serialization);
|
||||
|
||||
ErrorResult rv;
|
||||
FileSystemParams params = GetRequestParams(serialization, 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 +164,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 +187,9 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
|
||||
FileSystemErrorResponse r = aValue;
|
||||
mErrorValue = r.error();
|
||||
} else {
|
||||
SetSuccessRequestResult(aValue);
|
||||
ErrorResult rv;
|
||||
SetSuccessRequestResult(aValue, rv);
|
||||
mErrorValue = rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -6,59 +6,28 @@
|
||||
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
void
|
||||
FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal,
|
||||
nsAString& aNorm)
|
||||
/* static */ bool
|
||||
FileSystemUtils::IsDescendantPath(nsIFile* aFile,
|
||||
nsIFile* aDescendantFile)
|
||||
{
|
||||
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('/');
|
||||
nsAutoString path;
|
||||
nsresult rv = aFile->GetPath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
#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('\\');
|
||||
nsAutoString descendantPath;
|
||||
rv = aDescendantFile->GetPath(descendantPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
#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);
|
||||
|
||||
// Check the sub-directory path to see if it has the parent path as prefix.
|
||||
if (aDescendantPath.Length() < prefix.Length() ||
|
||||
!StringBeginsWith(aDescendantPath, prefix)) {
|
||||
if (descendantPath.Length() <= path.Length() ||
|
||||
!StringBeginsWith(descendantPath, path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
#ifndef mozilla_dom_FileSystemUtils_h
|
||||
#define mozilla_dom_FileSystemUtils_h
|
||||
|
||||
#include "nsString.h"
|
||||
class nsIFile;
|
||||
|
||||
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.
|
||||
@@ -22,26 +23,10 @@ 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. Both aPath and
|
||||
* aDescendantPath are absolute DOM path.
|
||||
* Return true if aDescendantPath is a descendant of aPath.
|
||||
*/
|
||||
static bool
|
||||
IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
|
||||
|
||||
static const char16_t kSeparatorChar = char16_t('/');
|
||||
IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -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>
|
||||
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<GetDirectoryListingTask> task =
|
||||
new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
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>
|
||||
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<GetDirectoryListingTask> 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::eNotDOMRootDirectory;
|
||||
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,79 @@ 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);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
GetDirectoryListingTask::GetSuccessRequestResult() const
|
||||
GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
InfallibleTArray<PBlobParent*> blobs;
|
||||
|
||||
for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
|
||||
BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
|
||||
if (blobParent) {
|
||||
blobs.AppendElement(blobParent);
|
||||
nsTArray<FileSystemDirectoryListingResponseData> 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<PBlobChild*>& 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<BlobChild*>(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<BlobChild*>(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 +199,19 @@ GetDirectoryListingTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Whether we want to get the root directory.
|
||||
bool getRoot = mTargetRealPath.IsEmpty();
|
||||
|
||||
nsCOMPtr<nsIFile> 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::eNotDOMRootDirectory) {
|
||||
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 +219,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 +229,7 @@ GetDirectoryListingTask::Work()
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@@ -186,7 +260,7 @@ GetDirectoryListingTask::Work()
|
||||
|
||||
nsCOMPtr<nsIFile> 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 +287,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,37 +322,55 @@ GetDirectoryListingTask::HandlerCallback()
|
||||
return;
|
||||
}
|
||||
|
||||
size_t count = mTargetBlobImpls.Length();
|
||||
size_t count = mTargetData.Length();
|
||||
|
||||
Sequence<OwningFileOrDirectory> listing;
|
||||
|
||||
if (!listing.SetLength(count, mozilla::fallible_t())) {
|
||||
mPromise->MaybeReject(NS_ERROR_FAILURE);
|
||||
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
#ifdef DEBUG
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
|
||||
bool exist;
|
||||
file->Exists(&exist);
|
||||
MOZ_ASSERT(exist);
|
||||
if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) {
|
||||
nsCOMPtr<nsIFile> 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;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIFile> 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 = new Directory(mFileSystem, path);
|
||||
|
||||
RefPtr<Directory> directory =
|
||||
Directory::Create(mFileSystem->GetParentObject(),
|
||||
directoryPath,
|
||||
Directory::eNotDOMRootDirectory,
|
||||
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->GetParentObject(), mTargetData[i].mBlobImpl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<GetDirectoryListingTask>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
Directory::DirectoryType aType,
|
||||
const nsAString& aFilters,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<GetDirectoryListingTask>
|
||||
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,14 @@ protected:
|
||||
virtual void
|
||||
HandlerCallback() override;
|
||||
|
||||
private:
|
||||
RefPtr<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> 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<RefPtr<BlobImpl>> mTargetBlobImpls;
|
||||
nsTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "mozilla/dom/Directory.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
@@ -21,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>
|
||||
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<GetFileOrDirectoryTask> task =
|
||||
new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
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>
|
||||
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<GetFileOrDirectoryTask> 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::eNotDOMRootDirectory;
|
||||
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()
|
||||
@@ -68,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);
|
||||
@@ -92,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()) {
|
||||
@@ -105,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;
|
||||
}
|
||||
@@ -128,33 +193,26 @@ GetFileOrDirectoryTask::Work()
|
||||
}
|
||||
|
||||
// Whether we want to get the root directory.
|
||||
bool getRoot = mTargetRealPath.IsEmpty();
|
||||
|
||||
nsCOMPtr<nsIFile> 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::eNotDOMRootDirectory) {
|
||||
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;
|
||||
}
|
||||
@@ -164,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;
|
||||
}
|
||||
@@ -180,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;
|
||||
}
|
||||
@@ -199,21 +257,25 @@ GetFileOrDirectoryTask::HandlerCallback()
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
RefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeRejectBrokenly(domError);
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsDirectory) {
|
||||
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
|
||||
mTargetPath,
|
||||
mType,
|
||||
mFileSystem);
|
||||
MOZ_ASSERT(dir);
|
||||
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
mTargetBlobImpl);
|
||||
mPromise->MaybeResolve(blob);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
@@ -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<GetFileOrDirectoryTask>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
Directory::DirectoryType aType,
|
||||
bool aDirectoryOnly,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<GetFileOrDirectoryTask>
|
||||
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<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> 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.
|
||||
|
||||
@@ -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 {
|
||||
@@ -20,40 +20,52 @@ 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.
|
||||
mRequiresPermissionChecks = false;
|
||||
|
||||
mString = mLocalRootPath;
|
||||
|
||||
#ifdef DEBUG
|
||||
mPermission.AssignLiteral("never-used");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
OSFileSystem::Init(nsPIDOMWindow* aWindow)
|
||||
already_AddRefed<FileSystemBase>
|
||||
OSFileSystem::Clone()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(!mWindow, "No duple Init() calls");
|
||||
MOZ_ASSERT(aWindow);
|
||||
mWindow = aWindow;
|
||||
RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalRootPath);
|
||||
if (mParent) {
|
||||
fs->Init(mParent);
|
||||
}
|
||||
|
||||
return fs.forget();
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
OSFileSystem::GetWindow() const
|
||||
void
|
||||
OSFileSystem::Init(nsISupports* aParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return mWindow;
|
||||
MOZ_ASSERT(!mParent, "No duple Init() calls");
|
||||
MOZ_ASSERT(aParent);
|
||||
mParent = aParent;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
|
||||
MOZ_ASSERT(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
OSFileSystem::GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
OSFileSystem::GetRootName(nsAString& aRetval) const
|
||||
{
|
||||
return aRetval.AssignLiteral("/");
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -79,14 +91,20 @@ 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
|
||||
OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
|
||||
{
|
||||
aOutput = mLocalRootPath;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -18,12 +18,15 @@ public:
|
||||
explicit OSFileSystem(const nsAString& aRootDir);
|
||||
|
||||
void
|
||||
Init(nsPIDOMWindow* aWindow);
|
||||
Init(nsISupports* aParent);
|
||||
|
||||
// Overrides FileSystemBase
|
||||
|
||||
virtual nsPIDOMWindow*
|
||||
GetWindow() const override;
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() override;
|
||||
|
||||
virtual nsISupports*
|
||||
GetParentObject() const override;
|
||||
|
||||
virtual void
|
||||
GetRootName(nsAString& aRetval) const override;
|
||||
@@ -34,6 +37,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;
|
||||
@@ -41,7 +47,7 @@ public:
|
||||
private:
|
||||
virtual ~OSFileSystem() {}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> mWindow;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -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
|
||||
|
||||
+110
-53
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "RemoveTask.h"
|
||||
|
||||
#include "DOMError.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
@@ -19,27 +18,94 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */ already_AddRefed<RemoveTask>
|
||||
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<RemoveTask> task =
|
||||
new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
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>
|
||||
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<RemoveTask> 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<BlobParent*>(static_cast<PBlobParent*>(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<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
}
|
||||
mPromise = Promise::Create(globalObject, aRv);
|
||||
MOZ_ASSERT(aDirPath);
|
||||
}
|
||||
|
||||
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
|
||||
@@ -49,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<BlobParent*>(static_cast<PBlobParent*>(target));
|
||||
mTargetBlobImpl = bp->GetBlobImpl();
|
||||
MOZ_ASSERT(mTargetBlobImpl);
|
||||
}
|
||||
|
||||
RemoveTask::~RemoveTask()
|
||||
@@ -84,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 = Blob::Create(mFileSystem->GetWindow(),
|
||||
mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
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;
|
||||
@@ -131,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<nsIFile> 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;
|
||||
}
|
||||
@@ -158,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;
|
||||
}
|
||||
@@ -187,9 +246,7 @@ RemoveTask::HandlerCallback()
|
||||
}
|
||||
|
||||
if (HasError()) {
|
||||
RefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
|
||||
mErrorValue);
|
||||
mPromise->MaybeRejectBrokenly(domError);
|
||||
mPromise->MaybeReject(mErrorValue);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
+32
-16
@@ -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<RemoveTask>
|
||||
Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aDirPath,
|
||||
BlobImpl* aTargetBlob,
|
||||
nsIFile* aTargetPath,
|
||||
bool aRecursive,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<RemoveTask>
|
||||
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<Promise> mPromise;
|
||||
nsString mDirRealPath;
|
||||
nsCOMPtr<nsIFile> 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<BlobImpl> mTargetBlobImpl;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
bool mRecursive;
|
||||
bool mReturnValue;
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
script_fileList.js
|
||||
|
||||
[test_basic.html]
|
||||
@@ -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']
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user