From 4039f06695940b7ae76fdd42a612f965062714e1 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:34 -0700 Subject: [PATCH 01/33] Bug 1066911 - Allow eRestyle_Force{,Descendants} in RuleNodeWithReplacement. r=heycam This is fine since the assertion is just checking that we're not calling RuleNodeWithReplacement in cases when stronger restyling (i.e., restyling that does more work) is needed; the new eRestyle_Force and eRestyle_ForceDescendants flags control whether we restyle at all. This probably should have been done in bug 931668 (since bug 996796 landed before bug 931668, although not before the patches were written), but it doesn't actually show up in our test suite until bug 977991 (or maybe slightly earlier in bug 1057231 or bug 1058346). --- layout/style/nsStyleSet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index c1591accf818..fe0ab9ed519a 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1335,8 +1335,10 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, nsRestyleHint aReplacements) { NS_ABORT_IF_FALSE(!(aReplacements & ~(eRestyle_CSSTransitions | - eRestyle_CSSAnimations)), - // FIXME: Once bug 931668 lands we'll have a better + eRestyle_CSSAnimations | + eRestyle_Force | + eRestyle_ForceDescendants)), + // FIXME: Once bug 979133 lands we'll have a better // way to print these. nsPrintfCString("unexpected replacement bits 0x%lX", uint32_t(aReplacements)).get()); From 02e1286d9aa710f525c241d02db84094987b111b Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:34 -0700 Subject: [PATCH 02/33] Bug 1057231 patch 1 - Add a rule processor class for rules from SMIL Animation of SVG attributes. r=dholbert This patch series (to move SVG Animation rules to their own level of the cascade) is needed so that they can participate in the mechanism for updating only animation styles to the present time without touching any other styles. In theory, this should have been done as part of 996796 or even as part of prior work on off main thread animations, except that it doesn't seem possible to have SVG Animations and CSS Transitions interacting on the same element and have that element's style out of date, which means it wasn't necessary for the existing code to update only animation styles. However, bug 960465 will use this similar mechanism as the new mechanism for making sure transitions start in a reasonable way (replacing the mechanism of phases, going from style without animation to style with animation via nsPresContext::IsProcessingAnimationStyleChange). This will require that, for SVG Animations to continue interacting correctly with CSS Transitions, that they have their own cascade level so that they can participate in RestyleManager::UpdateOnlyAnimationStyles. Additionally, this is needed even sooner, for bug 977991, which needs (temporarily, until bug 960465 lands) a mechanism for updating the style attribute style *and* all animation-phase-dependent styles. Yes, it's a little bit annoying to have to have another class, and another object per-document for this, but that's currently what comes with being a level of the cascade. But it's one class and one object per document, and I believe there will be substantial benefits to having all rule processor levels be only-animation-related or not-animation-related (except for the style attribute level, which is simple enough that it isn't needed yet). --- .../style/SVGAttrAnimationRuleProcessor.cpp | 97 +++++++++++++++++++ layout/style/SVGAttrAnimationRuleProcessor.h | 59 +++++++++++ layout/style/moz.build | 1 + 3 files changed, 157 insertions(+) create mode 100644 layout/style/SVGAttrAnimationRuleProcessor.cpp create mode 100644 layout/style/SVGAttrAnimationRuleProcessor.h diff --git a/layout/style/SVGAttrAnimationRuleProcessor.cpp b/layout/style/SVGAttrAnimationRuleProcessor.cpp new file mode 100644 index 000000000000..cd5b22a0fccc --- /dev/null +++ b/layout/style/SVGAttrAnimationRuleProcessor.cpp @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * style rule processor for rules from SMIL Animation of SVG mapped + * attributes (attributes whose values are mapped into style) + */ + +#include "SVGAttrAnimationRuleProcessor.h" + +using namespace mozilla; + +SVGAttrAnimationRuleProcessor::SVGAttrAnimationRuleProcessor() +{ +} + +SVGAttrAnimationRuleProcessor::~SVGAttrAnimationRuleProcessor() +{ +} + +NS_IMPL_ISUPPORTS(SVGAttrAnimationRuleProcessor, nsIStyleRuleProcessor) + +/* virtual */ void +SVGAttrAnimationRuleProcessor::RulesMatching(ElementRuleProcessorData* aData) +{ +} + +/* virtual */ nsRestyleHint +SVGAttrAnimationRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData) +{ + return nsRestyleHint(0); +} + +/* virtual */ nsRestyleHint +SVGAttrAnimationRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) +{ + return nsRestyleHint(0); +} + +/* virtual */ bool +SVGAttrAnimationRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData) +{ + return false; +} + +/* virtual */ nsRestyleHint +SVGAttrAnimationRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +{ + return nsRestyleHint(0); +} + +/* virtual */ bool +SVGAttrAnimationRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext) +{ + return false; +} + +/* virtual */ void +SVGAttrAnimationRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData) +{ +} + +/* virtual */ void +SVGAttrAnimationRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData) +{ +} + +#ifdef MOZ_XUL +/* virtual */ void +SVGAttrAnimationRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData) +{ +} +#endif + +/* virtual */ size_t +SVGAttrAnimationRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + return 0; // SVGAttrAnimationRuleProcessors are charged to the DOM, not layout +} + +/* virtual */ size_t +SVGAttrAnimationRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return 0; // SVGAttrAnimationRuleProcessors are charged to the DOM, not layout +} + +size_t +SVGAttrAnimationRuleProcessor::DOMSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + return n; +} diff --git a/layout/style/SVGAttrAnimationRuleProcessor.h b/layout/style/SVGAttrAnimationRuleProcessor.h new file mode 100644 index 000000000000..c61ae9a6fe77 --- /dev/null +++ b/layout/style/SVGAttrAnimationRuleProcessor.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * style rule processor for rules from SMIL Animation of SVG mapped + * attributes (attributes whose values are mapped into style) + */ + +#ifndef mozilla_SVGAttrAnimationRuleProcessor_h_ +#define mozilla_SVGAttrAnimationRuleProcessor_h_ + +#include "nsIStyleRuleProcessor.h" + +class nsIDocument; + +namespace mozilla { + +class SVGAttrAnimationRuleProcessor MOZ_FINAL : public nsIStyleRuleProcessor +{ +public: + SVGAttrAnimationRuleProcessor(); + +private: + ~SVGAttrAnimationRuleProcessor(); + +public: + NS_DECL_ISUPPORTS + + // nsIStyleRuleProcessor API + virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE; + virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE; + virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE; +#ifdef MOZ_XUL + virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE; +#endif + virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE; + virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) MOZ_OVERRIDE; + virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE; + virtual nsRestyleHint + HasAttributeDependentStyle(AttributeRuleProcessorData* aData) MOZ_OVERRIDE; + virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) MOZ_OVERRIDE; + virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) + const MOZ_MUST_OVERRIDE MOZ_OVERRIDE; + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) + const MOZ_MUST_OVERRIDE MOZ_OVERRIDE; + + size_t DOMSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + SVGAttrAnimationRuleProcessor(const SVGAttrAnimationRuleProcessor& aCopy) MOZ_DELETE; + SVGAttrAnimationRuleProcessor& operator=(const SVGAttrAnimationRuleProcessor& aCopy) MOZ_DELETE; +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_SVGAttrAnimationRuleProcessor_h_) */ diff --git a/layout/style/moz.build b/layout/style/moz.build index 8c47847adb56..cd1a435e236b 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -131,6 +131,7 @@ UNIFIED_SOURCES += [ 'nsTransitionManager.cpp', 'StyleAnimationValue.cpp', 'StyleRule.cpp', + 'SVGAttrAnimationRuleProcessor.cpp', ] # nsCSSRuleProcessor.cpp needs to be built separately because it uses plarena.h. From 0901b41cd7a0cba96dc18735a50c372b5f74286d Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:35 -0700 Subject: [PATCH 03/33] Bug 1057231 patch 2 - Add SVG Attribute Animation rule processor to the document. r=dholbert --- content/base/public/nsIDocument.h | 12 ++++++++++++ content/base/src/nsDocument.cpp | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index bafadecdd610..4d04291b1169 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -85,6 +85,7 @@ namespace mozilla { class CSSStyleSheet; class ErrorResult; class EventStates; +class SVGAttrAnimationRuleProcessor; namespace css { class Loader; @@ -925,6 +926,16 @@ public: return mStyleAttrStyleSheet; } + /** + * Get this document's SVG Animation rule processor. May return null + * if there isn't one. + */ + mozilla::SVGAttrAnimationRuleProcessor* + GetSVGAttrAnimationRuleProcessor() const + { + return mSVGAttrAnimationRuleProcessor; + } + virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject) = 0; /** @@ -2443,6 +2454,7 @@ protected: nsRefPtr mStyleImageLoader; nsRefPtr mAttrStyleSheet; nsRefPtr mStyleAttrStyleSheet; + nsRefPtr mSVGAttrAnimationRuleProcessor; // The set of all object, embed, applet, video/audio elements or // nsIObjectLoadingContent or nsIDocumentActivity for which this is the diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 77edf9a85692..2e6e813553f9 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -168,6 +168,7 @@ #include "nsCSPService.h" #include "nsHTMLStyleSheet.h" #include "nsHTMLCSSStyleSheet.h" +#include "SVGAttrAnimationRuleProcessor.h" #include "mozilla/dom/DOMImplementation.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/Comment.h" @@ -2449,6 +2450,11 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI) mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(); } + if (!mSVGAttrAnimationRuleProcessor) { + mSVGAttrAnimationRuleProcessor = + new mozilla::SVGAttrAnimationRuleProcessor(); + } + // Now set up our style sets nsCOMPtr shell = GetShell(); if (shell) { @@ -12169,6 +12175,12 @@ nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) : 0; + aWindowSizes->mDOMOtherSize += + mSVGAttrAnimationRuleProcessor ? + mSVGAttrAnimationRuleProcessor->DOMSizeOfIncludingThis( + aWindowSizes->mMallocSizeOf) : + 0; + aWindowSizes->mDOMOtherSize += mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf); From 5c924eff08e1536c9b1295c2ce6c82a5ba492c9a Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:35 -0700 Subject: [PATCH 04/33] Bug 1057231 patch 3 - Add new cascade level for rules from SMIL Animation of SVG attributes to the style set. r=dholbert I will fix the indentation of gCascadeLevels in bug 977991 patch 2, which adds a member to each element of the array. Note that this bumps the maximum sheetType from 8 to 9 (and number of them from 9 to 10), which does not require updating NS_RULE_NODE_LEVEL_MASK, since NS_RULE_NODE_LEVEL_MASK currently has 4 bits and allows a maximum of 15. --- layout/style/nsStyleSet.cpp | 42 ++++++++++++++++++++++++++++--------- layout/style/nsStyleSet.h | 3 ++- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index fe0ab9ed519a..d772688b30a5 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -33,6 +33,7 @@ #include "GeckoProfiler.h" #include "nsHTMLCSSStyleSheet.h" #include "nsHTMLStyleSheet.h" +#include "SVGAttrAnimationRuleProcessor.h" #include "nsCSSRules.h" #include "nsPrintfCString.h" #include "nsIFrame.h" @@ -187,8 +188,14 @@ nsStyleSet::Init(nsPresContext *aPresContext) mRuleTree = nsRuleNode::CreateRootNode(aPresContext); + // Make an explicit GatherRuleProcessors call for the levels that + // don't have style sheets. The other levels will have their calls + // triggered by DirtyRuleProcessors. (We should probably convert the + // ePresHintSheet and eStyleAttrSheet levels to work like this as + // well, and not implement nsIStyleSheet.) GatherRuleProcessors(eAnimationSheet); GatherRuleProcessors(eTransitionSheet); + GatherRuleProcessors(eSVGAttrAnimationSheet); } nsresult @@ -360,20 +367,26 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) // handle the types for which have a rule processor that does not // implement the style sheet interface. case eAnimationSheet: - MOZ_ASSERT(mSheets[aType].Count() == 0); + MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->AnimationManager(); return NS_OK; case eTransitionSheet: - MOZ_ASSERT(mSheets[aType].Count() == 0); + MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->TransitionManager(); return NS_OK; case eStyleAttrSheet: - MOZ_ASSERT(mSheets[aType].Count() == 0); + MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet(); return NS_OK; case ePresHintSheet: - MOZ_ASSERT(mSheets[aType].Count() == 0); - mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet(); + MOZ_ASSERT(mSheets[aType].IsEmpty()); + mRuleProcessors[aType] = + PresContext()->Document()->GetAttributeStyleSheet(); + return NS_OK; + case eSVGAttrAnimationSheet: + MOZ_ASSERT(mSheets[aType].IsEmpty()); + mRuleProcessors[aType] = + PresContext()->Document()->GetSVGAttrAnimationRuleProcessor(); return NS_OK; default: // keep going @@ -960,6 +973,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, // - UA normal rules = Agent normal // - User normal rules = User normal // - Presentation hints = PresHint normal + // - SVG Animation (highest pres hint) = SVGAttrAnimation normal // - Author normal rules = Document normal // - Override normal rules = Override normal // - animation rules = Animation normal @@ -991,8 +1005,12 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, aRuleWalker->SetLevel(ePresHintSheet, false, false); if (mRuleProcessors[ePresHintSheet]) (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData); - nsRuleNode* lastPresHintRN = aRuleWalker->CurrentNode(); - + + aRuleWalker->SetLevel(eSVGAttrAnimationSheet, false, false); + if (mRuleProcessors[eSVGAttrAnimationSheet]) + (*aCollectorFunc)(mRuleProcessors[eSVGAttrAnimationSheet], aData); + nsRuleNode* lastSVGAttrAnimationRN = aRuleWalker->CurrentNode(); + aRuleWalker->SetLevel(eDocSheet, false, true); bool cutOffInheritance = false; if (mBindingManager && aElement) { @@ -1065,11 +1083,11 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, if (haveImportantDocRules) { aRuleWalker->SetLevel(eDocSheet, true, false); - AddImportantRules(lastDocRN, lastPresHintRN, aRuleWalker); // doc + AddImportantRules(lastDocRN, lastSVGAttrAnimationRN, aRuleWalker); // doc } #ifdef DEBUG else { - AssertNoImportantRules(lastDocRN, lastPresHintRN); + AssertNoImportantRules(lastDocRN, lastSVGAttrAnimationRN); } #endif @@ -1094,7 +1112,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif #ifdef DEBUG - AssertNoCSSRules(lastPresHintRN, lastUserRN); + AssertNoCSSRules(lastSVGAttrAnimationRN, lastUserRN); #endif if (haveImportantUserRules) { @@ -1149,6 +1167,9 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, if (mRuleProcessors[ePresHintSheet]) (*aFunc)(mRuleProcessors[ePresHintSheet], aData); + if (mRuleProcessors[eSVGAttrAnimationSheet]) + (*aFunc)(mRuleProcessors[eSVGAttrAnimationSheet], aData); + bool cutOffInheritance = false; if (mBindingManager) { // We can supply additional document-level sheets that should be walked. @@ -1314,6 +1335,7 @@ static const CascadeLevel gCascadeLevels[] = { { nsStyleSet::eAgentSheet, false, nsRestyleHint(0) }, { nsStyleSet::eUserSheet, false, nsRestyleHint(0) }, { nsStyleSet::ePresHintSheet, false, nsRestyleHint(0) }, + { nsStyleSet::eSVGAttrAnimationSheet, false, nsRestyleHint(0) }, { nsStyleSet::eDocSheet, false, nsRestyleHint(0) }, { nsStyleSet::eScopedDocSheet, false, nsRestyleHint(0) }, { nsStyleSet::eStyleAttrSheet, false, nsRestyleHint(0) }, diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index 2929aef82c1c..610db9d09bc4 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -275,6 +275,7 @@ class nsStyleSet eAgentSheet, // CSS eUserSheet, // CSS ePresHintSheet, + eSVGAttrAnimationSheet, eDocSheet, // CSS eScopedDocSheet, eStyleAttrSheet, @@ -472,7 +473,7 @@ class nsStyleSet unsigned mAuthorStyleDisabled: 1; unsigned mInReconstruct : 1; unsigned mInitFontFeatureValuesLookup : 1; - unsigned mDirty : 9; // one dirty bit is used per sheet type + unsigned mDirty : 10; // one dirty bit is used per sheet type uint32_t mUnusedRuleNodeCount; // used to batch rule node GC nsTArray mRoots; // style contexts with no parent From 1f9790d467091f47281a3bfc348a63bf2a934f5f Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:35 -0700 Subject: [PATCH 05/33] Bug 1057231 patch 4 - Move SVG Attribute Animation rules from the pres hint level to their own cascade level. r=dholbert This is only a very slight reordering of their position in the cascade, since they were previously walked at the end of nsSVGElement::WalkContentStyleRules, which was called near the end of nsHTMLStyleSheet::RulesMatching. So the only change should be that they now take priority over the xml:lang rule added by nsHTMLStyleSheet, a rule with which they do not interact. --- content/svg/content/src/nsSVGElement.cpp | 8 ++++++-- content/svg/content/src/nsSVGElement.h | 1 + layout/style/SVGAttrAnimationRuleProcessor.cpp | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index cf143a104bae..dac9b24bd4a9 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -905,6 +905,12 @@ nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) aRuleWalker->Forward(mContentStyleRule); } + return NS_OK; +} + +void +nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker) +{ // Update & walk the animated content style rule, to include style from // animated mapped attributes. But first, get nsPresContext to check // whether this is a "no-animation restyle". (This should match the check @@ -933,8 +939,6 @@ nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) aRuleWalker->Forward(animContentStyleRule); } } - - return NS_OK; } NS_IMETHODIMP_(bool) diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 3fc467f9f36b..9745325dbf66 100644 --- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -113,6 +113,7 @@ public: virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE; NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE; + void WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker); NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const MOZ_OVERRIDE; diff --git a/layout/style/SVGAttrAnimationRuleProcessor.cpp b/layout/style/SVGAttrAnimationRuleProcessor.cpp index cd5b22a0fccc..23a627478534 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.cpp +++ b/layout/style/SVGAttrAnimationRuleProcessor.cpp @@ -11,8 +11,11 @@ */ #include "SVGAttrAnimationRuleProcessor.h" +#include "nsRuleProcessorData.h" +#include "nsSVGElement.h" using namespace mozilla; +using namespace mozilla::dom; SVGAttrAnimationRuleProcessor::SVGAttrAnimationRuleProcessor() { @@ -27,6 +30,11 @@ NS_IMPL_ISUPPORTS(SVGAttrAnimationRuleProcessor, nsIStyleRuleProcessor) /* virtual */ void SVGAttrAnimationRuleProcessor::RulesMatching(ElementRuleProcessorData* aData) { + Element* element = aData->mElement; + if (element->IsSVG()) { + static_cast(element)-> + WalkAnimatedContentStyleRules(aData->mRuleWalker); + } } /* virtual */ nsRestyleHint From 87487f1ab094882a02543d69085ecf8ae5e38454 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:35 -0700 Subject: [PATCH 06/33] Bug 1058346 patch 1 - Expose a simpler variant of RulesMatching from SVGAttrAnimationRuleProcessor. r=birtles This allows nsStyleSet::RuleNodeWithReplacement to call it without constructing an entire (and unnecessary) ElementRuleProcessorData, which will happen in patch 2. --- layout/style/SVGAttrAnimationRuleProcessor.cpp | 14 ++++++++++---- layout/style/SVGAttrAnimationRuleProcessor.h | 9 +++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/layout/style/SVGAttrAnimationRuleProcessor.cpp b/layout/style/SVGAttrAnimationRuleProcessor.cpp index 23a627478534..c1526362aa70 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.cpp +++ b/layout/style/SVGAttrAnimationRuleProcessor.cpp @@ -30,10 +30,16 @@ NS_IMPL_ISUPPORTS(SVGAttrAnimationRuleProcessor, nsIStyleRuleProcessor) /* virtual */ void SVGAttrAnimationRuleProcessor::RulesMatching(ElementRuleProcessorData* aData) { - Element* element = aData->mElement; - if (element->IsSVG()) { - static_cast(element)-> - WalkAnimatedContentStyleRules(aData->mRuleWalker); + ElementRulesMatching(aData->mElement, aData->mRuleWalker); +} + +void +SVGAttrAnimationRuleProcessor::ElementRulesMatching(Element* aElement, + nsRuleWalker* aRuleWalker) +{ + if (aElement->IsSVG()) { + static_cast(aElement)-> + WalkAnimatedContentStyleRules(aRuleWalker); } } diff --git a/layout/style/SVGAttrAnimationRuleProcessor.h b/layout/style/SVGAttrAnimationRuleProcessor.h index c61ae9a6fe77..2f3899082497 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.h +++ b/layout/style/SVGAttrAnimationRuleProcessor.h @@ -15,9 +15,14 @@ #include "nsIStyleRuleProcessor.h" class nsIDocument; +class nsRuleWalker; namespace mozilla { +namespace dom { +class Element; +} + class SVGAttrAnimationRuleProcessor MOZ_FINAL : public nsIStyleRuleProcessor { public: @@ -49,6 +54,10 @@ public: size_t DOMSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + // A shortcut for nsStyleSet to call RulesMatching with less setup. + void ElementRulesMatching(mozilla::dom::Element* aElement, + nsRuleWalker* aRuleWalker); + private: SVGAttrAnimationRuleProcessor(const SVGAttrAnimationRuleProcessor& aCopy) MOZ_DELETE; SVGAttrAnimationRuleProcessor& operator=(const SVGAttrAnimationRuleProcessor& aCopy) MOZ_DELETE; From 80f5a01d0c202a0d55a858b630c3666865e5b717 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:35 -0700 Subject: [PATCH 07/33] Bug 1058346 patch 2 - Add eRestyle_SVGAttrAnimations and support it in nsStyleSet::RuleNodeWithReplacement. r=birtles This allows posting a restyle that says that only the rule(s) from the SVGAttrAnimationSheet cascade level will be replaced, which avoids running selector matching. This is needed to land bug 977991 prior to landing bug 960465, since that requires replacing all levels that contain animations. (I'll rename this to match the name determined in bug 1057231.) --- layout/base/nsChangeHint.h | 11 +++++++++-- layout/style/SVGAttrAnimationRuleProcessor.cpp | 9 +++++++++ layout/style/nsStyleSet.cpp | 13 ++++++++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 1b195cd1e7cf..72f3405019f3 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -305,14 +305,21 @@ enum nsRestyleHint { // work.) eRestyle_CSSAnimations = (1<<4), + // Replace the style data coming from SVG animations (SMIL Animations) + // without updating any other style data. If a new style context + // results, update style contexts on the descendants. (Irrelevant if + // eRestyle_Self or eRestyle_Subtree is also set, since those imply a + // superset of the work.) + eRestyle_SVGAttrAnimations = (1<<5), + // Continue the restyling process to the current frame's children even // if this frame's restyling resulted in no style changes. - eRestyle_Force = (1<<5), + eRestyle_Force = (1<<8), // Continue the restyling process to all of the current frame's // descendants, even if any frame's restyling resulted in no style // changes. (Implies eRestyle_Force.) - eRestyle_ForceDescendants = (1<<6), + eRestyle_ForceDescendants = (1<<9), }; diff --git a/layout/style/SVGAttrAnimationRuleProcessor.cpp b/layout/style/SVGAttrAnimationRuleProcessor.cpp index c1526362aa70..c1323e2a74b4 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.cpp +++ b/layout/style/SVGAttrAnimationRuleProcessor.cpp @@ -76,17 +76,26 @@ SVGAttrAnimationRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext /* virtual */ void SVGAttrAnimationRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData) { + // If SMIL Animation of SVG attributes can ever target + // pseudo-elements, we need to adjust either + // nsStyleSet::RuleNodeWithReplacement or the test in + // ElementRestyler::RestyleSelf (added in bug 977991 patch 4) to + // handle such styles. } /* virtual */ void SVGAttrAnimationRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData) { + // If SMIL Animation of SVG attributes can ever target anonymous boxes, + // see comment in RulesMatching(PseudoElementRuleProcessorData*). } #ifdef MOZ_XUL /* virtual */ void SVGAttrAnimationRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData) { + // If SMIL Animation of SVG attributes can ever target XUL tree pseudos, + // see comment in RulesMatching(PseudoElementRuleProcessorData*). } #endif diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index d772688b30a5..99f5bd3994b1 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1335,7 +1335,7 @@ static const CascadeLevel gCascadeLevels[] = { { nsStyleSet::eAgentSheet, false, nsRestyleHint(0) }, { nsStyleSet::eUserSheet, false, nsRestyleHint(0) }, { nsStyleSet::ePresHintSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eSVGAttrAnimationSheet, false, nsRestyleHint(0) }, + { nsStyleSet::eSVGAttrAnimationSheet, false, eRestyle_SVGAttrAnimations }, { nsStyleSet::eDocSheet, false, nsRestyleHint(0) }, { nsStyleSet::eScopedDocSheet, false, nsRestyleHint(0) }, { nsStyleSet::eStyleAttrSheet, false, nsRestyleHint(0) }, @@ -1358,6 +1358,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, { NS_ABORT_IF_FALSE(!(aReplacements & ~(eRestyle_CSSTransitions | eRestyle_CSSAnimations | + eRestyle_SVGAttrAnimations | eRestyle_Force | eRestyle_ForceDescendants)), // FIXME: Once bug 979133 lands we'll have a better @@ -1431,6 +1432,16 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, } break; } + case eRestyle_SVGAttrAnimations: { + SVGAttrAnimationRuleProcessor* ruleProcessor = + static_cast( + mRuleProcessors[eSVGAttrAnimationSheet].get()); + if (ruleProcessor && + aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { + ruleProcessor->ElementRulesMatching(aElement, &ruleWalker); + } + break; + } default: break; } From a4fd946d3e51009c561479c26d39463a3088c855 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:36 -0700 Subject: [PATCH 08/33] Bug 977991 patch 0 - Add bitwise operators to nsRestyleHint. r=birtles This avoids having to cast back to nsRestyleHint after using bitwise operators, and allows |= (etc.). (In the future we should consider converting nsRestyleHint, and probably also nsChangeHint, to use MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS.) --- layout/base/nsChangeHint.h | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 72f3405019f3..6a8443b5b1de 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -9,6 +9,7 @@ #define nsChangeHint_h___ #include "nsDebug.h" +#include "mozilla/Types.h" // Defines for various style related constants @@ -322,5 +323,46 @@ enum nsRestyleHint { eRestyle_ForceDescendants = (1<<9), }; +// The functions below need an integral type to cast to to avoid +// infinite recursion. +typedef decltype(nsRestyleHint(0) + nsRestyleHint(0)) nsRestyleHint_size_t; + +inline nsRestyleHint operator|(nsRestyleHint aLeft, nsRestyleHint aRight) +{ + return nsRestyleHint(nsRestyleHint_size_t(aLeft) | + nsRestyleHint_size_t(aRight)); +} + +inline nsRestyleHint operator&(nsRestyleHint aLeft, nsRestyleHint aRight) +{ + return nsRestyleHint(nsRestyleHint_size_t(aLeft) & + nsRestyleHint_size_t(aRight)); +} + +inline nsRestyleHint& operator|=(nsRestyleHint& aLeft, nsRestyleHint aRight) +{ + return aLeft = aLeft | aRight; +} + +inline nsRestyleHint& operator&=(nsRestyleHint& aLeft, nsRestyleHint aRight) +{ + return aLeft = aLeft & aRight; +} + +inline nsRestyleHint operator~(nsRestyleHint aArg) +{ + return nsRestyleHint(~nsRestyleHint_size_t(aArg)); +} + +inline nsRestyleHint operator^(nsRestyleHint aLeft, nsRestyleHint aRight) +{ + return nsRestyleHint(nsRestyleHint_size_t(aLeft) ^ + nsRestyleHint_size_t(aRight)); +} + +inline nsRestyleHint operator^=(nsRestyleHint& aLeft, nsRestyleHint aRight) +{ + return aLeft = aLeft ^ aRight; +} #endif /* nsChangeHint_h___ */ From 9fc7885297f62e491d99f82b7d5b9faf0c13b076 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:36 -0700 Subject: [PATCH 09/33] Bug 977991 patch 1 - Expose variant of RulesMatching on nsHTMLCSSStyleSheet that is less work to call. r=birtles This allows nsStyleSet::RuleNodeWithReplacement to call it without constructing an entire (and unnecessary) ElementRuleProcessorData, which will happen in patch 3. --- layout/style/nsHTMLCSSStyleSheet.cpp | 25 ++++++++++++++++--------- layout/style/nsHTMLCSSStyleSheet.h | 13 +++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 96f60749605d..aa6841d12e99 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -54,29 +54,36 @@ NS_IMPL_ISUPPORTS(nsHTMLCSSStyleSheet, nsIStyleRuleProcessor) /* virtual */ void nsHTMLCSSStyleSheet::RulesMatching(ElementRuleProcessorData* aData) { - Element* element = aData->mElement; + ElementRulesMatching(aData->mPresContext, aData->mElement, + aData->mRuleWalker); +} +void +nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, + Element* aElement, + nsRuleWalker* aRuleWalker) +{ // just get the one and only style rule from the content's STYLE attribute - css::StyleRule* rule = element->GetInlineStyleRule(); + css::StyleRule* rule = aElement->GetInlineStyleRule(); if (rule) { rule->RuleMatched(); - aData->mRuleWalker->Forward(rule); + aRuleWalker->Forward(rule); } - rule = element->GetSMILOverrideStyleRule(); + rule = aElement->GetSMILOverrideStyleRule(); if (rule) { - if (aData->mPresContext->IsProcessingRestyles() && - !aData->mPresContext->IsProcessingAnimationStyleChange()) { + if (aPresContext->IsProcessingRestyles() && + !aPresContext->IsProcessingAnimationStyleChange()) { // Non-animation restyle -- don't process SMIL override style, because we // don't want SMIL animation to trigger new CSS transitions. Instead, // request an Animation restyle, so we still get noticed. - aData->mPresContext->PresShell()->RestyleForAnimation(element, - eRestyle_Self); + aPresContext->PresShell()->RestyleForAnimation(aElement, + eRestyle_Self); } else { // Animation restyle (or non-restyle traversal of rules) // Now we can walk SMIL overrride style, without triggering transitions. rule->RuleMatched(); - aData->mRuleWalker->Forward(rule); + aRuleWalker->Forward(rule); } } } diff --git a/layout/style/nsHTMLCSSStyleSheet.h b/layout/style/nsHTMLCSSStyleSheet.h index a7768ee9300f..d38979a07d9f 100644 --- a/layout/style/nsHTMLCSSStyleSheet.h +++ b/layout/style/nsHTMLCSSStyleSheet.h @@ -16,8 +16,15 @@ #include "nsDataHashtable.h" #include "nsIStyleRuleProcessor.h" +class nsRuleWalker; struct MiscContainer; +namespace mozilla { +namespace dom { +class Element; +} +} + class nsHTMLCSSStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor { public: @@ -43,6 +50,12 @@ public: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE MOZ_OVERRIDE; + // Variant of RulesMatching method above that is specific to this + // rule processor. + void ElementRulesMatching(nsPresContext* aPresContext, + mozilla::dom::Element* aElement, + nsRuleWalker* aRuleWalker); + void CacheStyleAttr(const nsAString& aSerialized, MiscContainer* aValue); void EvictStyleAttr(const nsAString& aSerialized, MiscContainer* aValue); MiscContainer* LookupStyleAttr(const nsAString& aSerialized); From 771a48f9ed27cd812bdf27cad68882ff5c6edb10 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:36 -0700 Subject: [PATCH 10/33] Bug 977991 patch 2 - Add mCheckForImportantRules to the information nsStyleSet::RuleNodeWithReplacement keeps about cascade levels. r=birtles This is needed to support doing replacements of levels that can contain important rules, which will happen in patch 3. --- layout/style/nsStyleSet.cpp | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 99f5bd3994b1..804c2a6739d6 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1328,26 +1328,27 @@ struct RuleNodeInfo { struct CascadeLevel { uint8_t mLevel; bool mIsImportant; + bool mCheckForImportantRules; nsRestyleHint mLevelReplacementHint; }; static const CascadeLevel gCascadeLevels[] = { - { nsStyleSet::eAgentSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eUserSheet, false, nsRestyleHint(0) }, - { nsStyleSet::ePresHintSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eSVGAttrAnimationSheet, false, eRestyle_SVGAttrAnimations }, - { nsStyleSet::eDocSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eScopedDocSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eOverrideSheet, false, nsRestyleHint(0) }, - { nsStyleSet::eAnimationSheet, false, eRestyle_CSSAnimations }, - { nsStyleSet::eScopedDocSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eDocSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eOverrideSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eUserSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eAgentSheet, true, nsRestyleHint(0) }, - { nsStyleSet::eTransitionSheet, false, eRestyle_CSSTransitions }, + { nsStyleSet::eAgentSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eUserSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::ePresHintSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eSVGAttrAnimationSheet, false, false, eRestyle_SVGAttrAnimations }, + { nsStyleSet::eDocSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eScopedDocSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eStyleAttrSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eOverrideSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eAnimationSheet, false, false, eRestyle_CSSAnimations }, + { nsStyleSet::eScopedDocSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eDocSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eStyleAttrSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eOverrideSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eUserSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eAgentSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eTransitionSheet, false, false, eRestyle_CSSTransitions }, }; nsRuleNode* @@ -1389,9 +1390,12 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, for (const CascadeLevel *level = gCascadeLevels, *levelEnd = ArrayEnd(gCascadeLevels); level != levelEnd; ++level) { - ruleWalker.SetLevel(level->mLevel, level->mIsImportant, false); bool doReplace = level->mLevelReplacementHint & aReplacements; + + ruleWalker.SetLevel(level->mLevel, level->mIsImportant, + level->mCheckForImportantRules && doReplace); + if (doReplace) { switch (level->mLevelReplacementHint) { case eRestyle_CSSAnimations: { From cf461aae314bfa2ef24a70cd870544fdb3654b27 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:36 -0700 Subject: [PATCH 11/33] Bug 977991 patch 3 - Add ability for RuleNodeWithReplacement to replace the style attribute rule and its important rule. r=birtles This allows posting a restyle that says that only the rule(s) from the StyleAttrSheet cascade level will be replaced, which avoids running selector matching. Part 4 will ensure that we only invoke this code for element styles (and not pseudo-element or anonymous box styles). Despite that, I prefer having the runtime check here as well given that it's a very simple way to ensure we don't do something silly that might have security implications. --- layout/base/nsChangeHint.h | 9 ++++++++ layout/style/nsStyleSet.cpp | 41 +++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 6a8443b5b1de..fda1515414a3 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -313,6 +313,15 @@ enum nsRestyleHint { // superset of the work.) eRestyle_SVGAttrAnimations = (1<<5), + // Replace the style data coming from inline style without updating + // any other style data. If a new style context results, update style + // contexts on the descendants. (Irrelevant if eRestyle_Self or + // eRestyle_Subtree is also set, since those imply a superset of the + // work.) Supported only for element style contexts and not for + // pseudo-elements or anonymous boxes, on which it converts to + // eRestyle_Self. + eRestyle_StyleAttribute = (1<<6), + // Continue the restyling process to the current frame's children even // if this frame's restyling resulted in no style changes. eRestyle_Force = (1<<8), diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 804c2a6739d6..2bd8f02392e6 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1339,12 +1339,12 @@ static const CascadeLevel gCascadeLevels[] = { { nsStyleSet::eSVGAttrAnimationSheet, false, false, eRestyle_SVGAttrAnimations }, { nsStyleSet::eDocSheet, false, false, nsRestyleHint(0) }, { nsStyleSet::eScopedDocSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, false, false, nsRestyleHint(0) }, + { nsStyleSet::eStyleAttrSheet, false, true, eRestyle_StyleAttribute }, { nsStyleSet::eOverrideSheet, false, false, nsRestyleHint(0) }, { nsStyleSet::eAnimationSheet, false, false, eRestyle_CSSAnimations }, { nsStyleSet::eScopedDocSheet, true, false, nsRestyleHint(0) }, { nsStyleSet::eDocSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, true, false, nsRestyleHint(0) }, + { nsStyleSet::eStyleAttrSheet, true, false, eRestyle_StyleAttribute }, { nsStyleSet::eOverrideSheet, true, false, nsRestyleHint(0) }, { nsStyleSet::eUserSheet, true, false, nsRestyleHint(0) }, { nsStyleSet::eAgentSheet, true, false, nsRestyleHint(0) }, @@ -1360,6 +1360,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, NS_ABORT_IF_FALSE(!(aReplacements & ~(eRestyle_CSSTransitions | eRestyle_CSSAnimations | eRestyle_SVGAttrAnimations | + eRestyle_StyleAttribute | eRestyle_Force | eRestyle_ForceDescendants)), // FIXME: Once bug 979133 lands we'll have a better @@ -1387,6 +1388,12 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); auto rulesIndex = rules.Length(); + // We need to transfer this information between the non-!important and + // !important phases for the style attribute level. + nsRuleNode* lastScopedRN = nullptr; + nsRuleNode* lastStyleAttrRN = nullptr; + bool haveImportantStyleAttrRules = false; + for (const CascadeLevel *level = gCascadeLevels, *levelEnd = ArrayEnd(gCascadeLevels); level != levelEnd; ++level) { @@ -1446,7 +1453,37 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, } break; } + case eRestyle_StyleAttribute: { + if (!level->mIsImportant) { + // First time through, we handle the non-!important rule. + MOZ_ASSERT(aPseudoType == + nsCSSPseudoElements::ePseudo_NotPseudoElement, + "this code doesn't know how to replace " + "pseudo-element rules"); + nsHTMLCSSStyleSheet* ruleProcessor = + static_cast( + mRuleProcessors[eStyleAttrSheet].get()); + if (ruleProcessor && + // check condition we asserted above (belt & braces security) + aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { + lastScopedRN = ruleWalker.CurrentNode(); + ruleProcessor->ElementRulesMatching(PresContext(), + aElement, + &ruleWalker); + lastStyleAttrRN = ruleWalker.CurrentNode(); + haveImportantStyleAttrRules = + !ruleWalker.GetCheckForImportantRules(); + } + } else { + // Second time through, we handle the !important rule(s). + if (haveImportantStyleAttrRules) { + AddImportantRules(lastStyleAttrRN, lastScopedRN, &ruleWalker); + } + } + break; + } default: + MOZ_ASSERT(false, "unexpected result from gCascadeLevels lookup"); break; } } From 0b5209fe1d494c1d87495542daaf55677e2a547f Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:36 -0700 Subject: [PATCH 12/33] Bug 977991 patch 4 - Ensure we don't try to use the style attribute optimization on pseudo-elements. r=birtles This is needed because patch 1 and patch 3 did not add a mechanism that allows restyling pseudo-elements, which would be substantially more work and very little use (since the only case in which they have style attributes is for our internal use on the ::-moz-color-swatch pseudo-element). RestyleUndisplayedChildren does not need the same fix because it's only used on elements, and never on pseudo-elements or anonymous boxes. --- layout/base/RestyleManager.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 119e1d3a3151..5a324b697949 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2747,6 +2747,15 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsChangeHint_Hints_NotHandledForDescendants; } + // We don't support using eRestyle_StyleAttribute when pseudo-elements + // are involved. This is mostly irrelevant since style attribute + // changes on pseudo-elements are very rare, though it does mean we + // don't get the optimization for table elements. + if (pseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement && + (aRestyleHint & eRestyle_StyleAttribute)) { + aRestyleHint = (aRestyleHint & ~eRestyle_StyleAttribute) | eRestyle_Self; + } + // do primary context nsRefPtr newContext; nsIFrame *prevContinuation = From 4f6e5fae3bef52487f1b90a0695ad5f0bc165a9d Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:37 -0700 Subject: [PATCH 13/33] Bug 977991 patch 5 - Add eRestyle_ChangeAnimationPhase to switch between the with-animation and without-animation styles. r=birtles This is an additional bit on nsRestyleHint that says that the restyling operation should also perform all the work needed to switch between style-without-animation and style-with-animation (based on nsPresContext::IsProcessingAnimationStyleChange). These concepts will go away in bug 960465. Note that we don't want this behavior for the animation-only style update code (bug 996796, etc.), and I wanted to make this explicit so that it was clear when it was happening, and so that it was clear what code should be removed when we git rid of it. This is the workaround needed to land bug 977991 prior to bug 960465. (I think there's also a minor dependency in the other direction, so we need a workaround one way or the other.) Note that this depends on bug 1057231. --- layout/base/nsChangeHint.h | 10 +++++++ layout/style/nsStyleSet.cpp | 53 +++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index fda1515414a3..98d5c3da4413 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -322,6 +322,16 @@ enum nsRestyleHint { // eRestyle_Self. eRestyle_StyleAttribute = (1<<6), + // Additional restyle hint to be used along with CSSTransitions, + // CSSAnimations, SVGAttrAnimations, or StyleAttribute. This + // indicates that along with the replacement given, appropriate + // switching between the style with animation and style without + // animation should be performed by adding or removing rules that + // should be present only in the style with animation. + // This is implied by eRestyle_Self or eRestyle_Subtree. + // FIXME: Remove this as part of bug 960465. + eRestyle_ChangeAnimationPhase = (1 << 7), + // Continue the restyling process to the current frame's children even // if this frame's restyling resulted in no style changes. eRestyle_Force = (1<<8), diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 2bd8f02392e6..b3403162a014 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1361,6 +1361,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, eRestyle_CSSAnimations | eRestyle_SVGAttrAnimations | eRestyle_StyleAttribute | + eRestyle_ChangeAnimationPhase | eRestyle_Force | eRestyle_ForceDescendants)), // FIXME: Once bug 979133 lands we'll have a better @@ -1368,6 +1369,21 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, nsPrintfCString("unexpected replacement bits 0x%lX", uint32_t(aReplacements)).get()); + bool skipAnimationRules = false; + + // If we're changing animation phase, we have to reconsider what rules + // are in these four levels. + if (aReplacements & eRestyle_ChangeAnimationPhase) { + aReplacements |= eRestyle_CSSTransitions | + eRestyle_CSSAnimations | + eRestyle_SVGAttrAnimations | + eRestyle_StyleAttribute; + + nsPresContext* presContext = PresContext(); + skipAnimationRules = presContext->IsProcessingRestyles() && + !presContext->IsProcessingAnimationStyleChange(); + } + // FIXME (perf): This should probably not rebuild the whole path, but // only the path from the last change in the rule tree, like // ReplaceAnimationRule in nsStyleSet.cpp does. (That could then @@ -1409,17 +1425,22 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, // FIXME: This should probably be more similar to what // FileRules does; this feels like too much poking into the // internals of nsAnimationManager. + nsPresContext* presContext = PresContext(); nsAnimationManager* animationManager = - PresContext()->AnimationManager(); + presContext->AnimationManager(); AnimationPlayerCollection* collection = animationManager->GetAnimationPlayers(aElement, aPseudoType, false); if (collection) { - animationManager->UpdateStyleAndEvents( - collection, PresContext()->RefreshDriver()->MostRecentRefresh(), - EnsureStyleRule_IsNotThrottled); - if (collection->mStyleRule) { - ruleWalker.ForwardOnPossiblyCSSRule(collection->mStyleRule); + if (skipAnimationRules) { + collection->PostRestyleForAnimation(presContext); + } else { + animationManager->UpdateStyleAndEvents( + collection, PresContext()->RefreshDriver()->MostRecentRefresh(), + EnsureStyleRule_IsNotThrottled); + if (collection->mStyleRule) { + ruleWalker.ForwardOnPossiblyCSSRule(collection->mStyleRule); + } } } break; @@ -1434,16 +1455,23 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, aElement, aPseudoType, false); if (collection) { - collection->EnsureStyleRuleFor( - presContext->RefreshDriver()->MostRecentRefresh(), - EnsureStyleRule_IsNotThrottled); - if (collection->mStyleRule) { - ruleWalker.ForwardOnPossiblyCSSRule(collection->mStyleRule); + if (skipAnimationRules) { + collection->PostRestyleForAnimation(presContext); + } else { + collection->EnsureStyleRuleFor( + presContext->RefreshDriver()->MostRecentRefresh(), + EnsureStyleRule_IsNotThrottled); + if (collection->mStyleRule) { + ruleWalker.ForwardOnPossiblyCSSRule(collection->mStyleRule); + } } } break; } case eRestyle_SVGAttrAnimations: { + MOZ_ASSERT(aReplacements & eRestyle_ChangeAnimationPhase, + "don't know how to do this level without phase change"); + SVGAttrAnimationRuleProcessor* ruleProcessor = static_cast( mRuleProcessors[eSVGAttrAnimationSheet].get()); @@ -1454,6 +1482,9 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, break; } case eRestyle_StyleAttribute: { + MOZ_ASSERT(aReplacements & eRestyle_ChangeAnimationPhase, + "don't know how to do this level without phase change"); + if (!level->mIsImportant) { // First time through, we handle the non-!important rule. MOZ_ASSERT(aPseudoType == From 916454e5cdd299a0514dc9066af87941137b7e5c Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:37 -0700 Subject: [PATCH 14/33] Bug 977991 patch 6 - Use the faster eRestyle_StyleAttribute path for style attribute changes. r=birtles --- content/base/src/Element.cpp | 3 ++- layout/style/nsHTMLCSSStyleSheet.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/content/base/src/Element.cpp b/content/base/src/Element.cpp index a05507987acb..c0a482434fef 100644 --- a/content/base/src/Element.cpp +++ b/content/base/src/Element.cpp @@ -1576,7 +1576,8 @@ Element::SetSMILOverrideStyleRule(css::StyleRule* aStyleRule, if (doc) { nsCOMPtr shell = doc->GetShell(); if (shell) { - shell->RestyleForAnimation(this, eRestyle_Self); + shell->RestyleForAnimation(this, + eRestyle_StyleAttribute | eRestyle_ChangeAnimationPhase); } } } diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index aa6841d12e99..bcb60f73e249 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -78,7 +78,7 @@ nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, // don't want SMIL animation to trigger new CSS transitions. Instead, // request an Animation restyle, so we still get noticed. aPresContext->PresShell()->RestyleForAnimation(aElement, - eRestyle_Self); + eRestyle_StyleAttribute | eRestyle_ChangeAnimationPhase); } else { // Animation restyle (or non-restyle traversal of rules) // Now we can walk SMIL overrride style, without triggering transitions. @@ -143,7 +143,7 @@ nsHTMLCSSStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aDat // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but // it doesn't really matter. if (aData->mAttrHasChanged && aData->mAttribute == nsGkAtoms::style) { - return eRestyle_Self; + return eRestyle_StyleAttribute | eRestyle_ChangeAnimationPhase; } return nsRestyleHint(0); From 1d4505fd18afefdf1a0d1cd2b7d6ed89c30ccefb Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:37 -0700 Subject: [PATCH 15/33] Bug 977991 patch 7 - Use the faster eRestyle_SVGAttrAnimations hint from bug 1058346 for SMIL-animated SVG attribute changes. r=birtles This couldn't be done in bug 1058346 because it depends on patch 5 in this bug. --- content/svg/content/src/nsSVGElement.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index dac9b24bd4a9..bce1c4cbd5d8 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -925,7 +925,8 @@ nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker) // want that to happen from SMIL-animated value of mapped attrs, so // ignore animated value for now, and request an animation restyle to // get our animated value noticed. - shell->RestyleForAnimation(this, eRestyle_Self); + shell->RestyleForAnimation(this, + eRestyle_SVGAttrAnimations | eRestyle_ChangeAnimationPhase); } else { // Ok, this is an animation restyle -- go ahead and update/walk the // animated content style rule. From 657fde68cc1f0a543e63679c6e5530e862e51824 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sat, 13 Sep 2014 06:17:37 -0700 Subject: [PATCH 16/33] Bug 575675 - Remove assertion about aRelevantLinkVisited that isn't needed, and assumes that all link elements are styled with a style sheet that has :visited rules. r=bzbarsky --- layout/style/nsStyleContext.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 279f755e4483..11e2e495b33e 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -274,8 +274,6 @@ nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, nsRuleNode* aRulesIfVisited, bool aRelevantLinkVisited) { - NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited, - "aRelevantLinkVisited should only be set when we have a separate style"); uint32_t threshold = 10; // The # of siblings we're willing to examine // before just giving this whole thing up. From 1a91d40956cb94a058be763b6b781784450d3fa0 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Sat, 13 Sep 2014 12:12:19 -0400 Subject: [PATCH 17/33] Bug 994190 - 'Modify main-thread IndexedDB to use PBackground', r=khuey. --HG-- rename : dom/indexedDB/ipc/SerializationHelpers.h => dom/indexedDB/SerializationHelpers.h rename : dom/indexedDB/ipc/unit/head.js => dom/indexedDB/test/unit/xpcshell-head-child-process.js rename : dom/indexedDB/test/unit/head.js => dom/indexedDB/test/unit/xpcshell-head-parent-process.js rename : dom/ipc/Blob.h => dom/ipc/BlobParent.h rename : dom/ipc/FileDescriptorSetChild.cpp => ipc/glue/FileDescriptorSetChild.cpp rename : dom/ipc/FileDescriptorSetChild.h => ipc/glue/FileDescriptorSetChild.h rename : dom/ipc/FileDescriptorSetParent.cpp => ipc/glue/FileDescriptorSetParent.cpp rename : dom/ipc/FileDescriptorSetParent.h => ipc/glue/FileDescriptorSetParent.h rename : dom/ipc/PFileDescriptorSet.ipdl => ipc/glue/PFileDescriptorSet.ipdl --- content/base/public/nsDOMFile.h | 21 +- content/base/src/nsDOMBlobBuilder.cpp | 23 +- content/base/src/nsDOMBlobBuilder.h | 2 +- content/base/src/nsDOMFile.cpp | 64 +- content/base/src/nsDocument.cpp | 8 - content/base/src/nsFrameMessageManager.cpp | 4 +- dom/archivereader/ArchiveZipFile.cpp | 11 +- dom/archivereader/ArchiveZipFile.h | 6 +- dom/base/nsDOMWindowUtils.cpp | 74 +- dom/base/nsGlobalWindow.cpp | 14 +- dom/bluetooth/BluetoothService.cpp | 2 + dom/bluetooth/BluetoothService.h | 7 +- .../bluedroid/BluetoothOppManager.cpp | 1 + dom/bluetooth/bluedroid/BluetoothOppManager.h | 8 +- dom/bluetooth/bluez/BluetoothDBusService.cpp | 1 + dom/bluetooth/bluez/BluetoothOppManager.cpp | 1 + dom/bluetooth/bluez/BluetoothOppManager.h | 8 +- .../ipc/BluetoothServiceChildProcess.cpp | 1 + dom/bluetooth2/BluetoothService.cpp | 2 + dom/bluetooth2/BluetoothService.h | 7 +- .../bluedroid/BluetoothOppManager.cpp | 1 + .../bluedroid/BluetoothOppManager.h | 8 +- dom/bluetooth2/bluez/BluetoothOppManager.cpp | 1 + dom/bluetooth2/bluez/BluetoothOppManager.h | 8 +- dom/datastore/DataStoreDB.cpp | 48 +- dom/datastore/DataStoreRevision.cpp | 3 +- dom/datastore/DataStoreService.cpp | 2 + dom/datastore/tests/test_oop_events.html | 4 +- .../DeviceStorageRequestChild.cpp | 2 +- .../DeviceStorageRequestParent.cpp | 2 +- dom/devicestorage/nsDeviceStorage.cpp | 2 +- dom/filehandle/FileStreamWrappers.cpp | 29 +- dom/filehandle/FileStreamWrappers.h | 11 +- dom/filehandle/moz.build | 2 + dom/filesystem/CreateFileTask.cpp | 2 + dom/filesystem/FileSystemTaskBase.cpp | 1 + dom/filesystem/FileSystemTaskBase.h | 2 +- dom/filesystem/GetFileOrDirectoryTask.cpp | 2 + dom/filesystem/RemoveTask.cpp | 2 + dom/indexedDB/ActorsChild.cpp | 2351 +++ dom/indexedDB/ActorsChild.h | 627 + dom/indexedDB/ActorsParent.cpp | 17020 ++++++++++++++++ dom/indexedDB/ActorsParent.h | 67 + dom/indexedDB/AsyncConnectionHelper.cpp | 710 - dom/indexedDB/AsyncConnectionHelper.h | 257 - dom/indexedDB/CheckPermissionsHelper.cpp | 220 - dom/indexedDB/CheckPermissionsHelper.h | 59 - dom/indexedDB/Client.cpp | 369 - dom/indexedDB/Client.h | 97 - dom/indexedDB/DatabaseInfo.cpp | 269 - dom/indexedDB/DatabaseInfo.h | 203 - dom/indexedDB/FileInfo.cpp | 176 +- dom/indexedDB/FileInfo.h | 162 +- dom/indexedDB/FileManager.cpp | 433 - dom/indexedDB/FileManager.h | 173 +- dom/indexedDB/FileSnapshot.cpp | 118 +- dom/indexedDB/FileSnapshot.h | 81 +- dom/indexedDB/IDBCursor.cpp | 1353 +- dom/indexedDB/IDBCursor.h | 314 +- dom/indexedDB/IDBDatabase.cpp | 1761 +- dom/indexedDB/IDBDatabase.h | 500 +- dom/indexedDB/IDBEvents.cpp | 114 +- dom/indexedDB/IDBEvents.h | 173 +- dom/indexedDB/IDBFactory.cpp | 1291 +- dom/indexedDB/IDBFactory.h | 350 +- dom/indexedDB/IDBFileHandle.cpp | 12 +- dom/indexedDB/IDBIndex.cpp | 2826 +-- dom/indexedDB/IDBIndex.h | 312 +- dom/indexedDB/IDBKeyRange.cpp | 188 +- dom/indexedDB/IDBKeyRange.h | 226 +- dom/indexedDB/IDBMutableFile.cpp | 293 +- dom/indexedDB/IDBMutableFile.h | 82 +- dom/indexedDB/IDBObjectStore.cpp | 5843 ++---- dom/indexedDB/IDBObjectStore.h | 484 +- dom/indexedDB/IDBRequest.cpp | 421 +- dom/indexedDB/IDBRequest.h | 236 +- dom/indexedDB/IDBTransaction.cpp | 1763 +- dom/indexedDB/IDBTransaction.h | 608 +- dom/indexedDB/IDBWrapperCache.cpp | 28 +- dom/indexedDB/IDBWrapperCache.h | 46 +- dom/indexedDB/IndexedDatabase.h | 156 +- dom/indexedDB/IndexedDatabaseInlines.h | 103 +- dom/indexedDB/IndexedDatabaseManager.cpp | 91 +- dom/indexedDB/IndexedDatabaseManager.h | 33 +- dom/indexedDB/Key.cpp | 138 +- dom/indexedDB/Key.h | 285 +- dom/indexedDB/KeyPath.cpp | 13 +- dom/indexedDB/KeyPath.h | 24 +- dom/indexedDB/OpenDatabaseHelper.cpp | 2851 --- dom/indexedDB/OpenDatabaseHelper.h | 175 - dom/indexedDB/PBackgroundIDBCursor.ipdl | 90 + dom/indexedDB/PBackgroundIDBDatabase.ipdl | 72 + ...t.ipdl => PBackgroundIDBDatabaseFile.ipdl} | 12 +- dom/indexedDB/PBackgroundIDBFactory.ipdl | 63 + .../PBackgroundIDBFactoryRequest.ipdl | 48 + dom/indexedDB/PBackgroundIDBRequest.ipdl | 112 + dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh | 259 + dom/indexedDB/PBackgroundIDBTransaction.ipdl | 41 + ...BackgroundIDBVersionChangeTransaction.ipdl | 49 + .../PIndexedDBPermissionRequest.ipdl | 27 + dom/indexedDB/PermissionRequestBase.cpp | 267 + dom/indexedDB/PermissionRequestBase.h | 77 + dom/indexedDB/ProfilerHelpers.h | 8 +- dom/indexedDB/ReportInternalError.cpp | 8 +- dom/indexedDB/ReportInternalError.h | 8 +- dom/indexedDB/SerializationHelpers.h | 103 + dom/indexedDB/TransactionThreadPool.cpp | 845 +- dom/indexedDB/TransactionThreadPool.h | 238 +- dom/indexedDB/ipc/IndexedDBChild.cpp | 1401 -- dom/indexedDB/ipc/IndexedDBChild.h | 457 - dom/indexedDB/ipc/IndexedDBParams.ipdlh | 76 - dom/indexedDB/ipc/IndexedDBParent.cpp | 2262 -- dom/indexedDB/ipc/IndexedDBParent.h | 880 - dom/indexedDB/ipc/Makefile.in | 14 - dom/indexedDB/ipc/PIndexedDB.ipdl | 37 - dom/indexedDB/ipc/PIndexedDBCursor.ipdl | 49 - dom/indexedDB/ipc/PIndexedDBDatabase.ipdl | 69 - dom/indexedDB/ipc/PIndexedDBIndex.ipdl | 64 - dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl | 113 - dom/indexedDB/ipc/PIndexedDBRequest.ipdl | 113 - dom/indexedDB/ipc/PIndexedDBTransaction.ipdl | 73 - dom/indexedDB/ipc/SerializationHelpers.h | 309 - dom/indexedDB/ipc/mochitest.ini | 4 - dom/indexedDB/ipc/moz.build | 41 - dom/indexedDB/ipc/unit/head.js | 18 - dom/indexedDB/ipc/unit/xpcshell.ini | 68 - dom/indexedDB/moz.build | 58 +- dom/indexedDB/test/browser.ini | 52 +- dom/indexedDB/test/file.js | 39 +- dom/indexedDB/test/helpers.js | 62 +- dom/indexedDB/test/mochitest.ini | 543 +- dom/indexedDB/test/test_blob_simple.html | 17 + dom/indexedDB/test/test_blocked_order.html | 18 + .../test/test_disabled_quota_prompt.html | 118 + .../test_file_cross_database_copying.html | 6 +- dom/indexedDB/test/test_file_os_delete.html | 7 + dom/indexedDB/test/test_file_sharing.html | 6 +- .../test/test_filehandle_compat.html | 2 +- .../test/test_filehandle_getFile.html | 2 +- .../test/test_filehandle_lifetimes.html | 2 +- .../test_filehandle_lifetimes_nested.html | 2 +- .../test/test_filehandle_location.html | 2 +- .../test/test_filehandle_ordering.html | 2 +- .../test/test_filehandle_overlapping.html | 2 +- .../test_filehandle_readonly_exceptions.html | 2 +- .../test_filehandle_request_readyState.html | 2 +- ...filehandle_success_events_after_abort.html | 2 +- dom/indexedDB/test/test_invalidate.html | 18 + dom/indexedDB/test/test_persistenceType.html | 13 +- dom/indexedDB/test/unit/test_blocked_order.js | 150 + dom/indexedDB/test/unit/test_indexes.js | 2 +- .../test/unit/test_indexes_funny_things.js | 2 +- dom/indexedDB/test/unit/test_invalidate.js | 82 + .../test/unit/test_setVersion_events.js | 4 +- .../test/unit/test_temporary_storage.js | 32 +- .../test/unit/xpcshell-child-process.ini | 18 + .../test/unit/xpcshell-head-child-process.js | 27 + ...ead.js => xpcshell-head-parent-process.js} | 85 +- .../test/unit/xpcshell-parent-process.ini | 26 + .../{mochitest.ini => xpcshell-shared.ini} | 11 +- dom/indexedDB/test/unit/xpcshell.ini | 90 - dom/interfaces/base/nsIDOMWindowUtils.idl | 9 +- dom/ipc/Blob.cpp | 1815 +- dom/ipc/Blob.h | 200 - dom/ipc/BlobChild.h | 184 + dom/ipc/BlobParent.h | 216 + dom/ipc/ContentBridgeChild.cpp | 2 +- dom/ipc/ContentChild.cpp | 18 +- dom/ipc/ContentChild.h | 12 +- dom/ipc/ContentParent.cpp | 113 +- dom/ipc/ContentParent.h | 25 +- dom/ipc/FilePickerParent.cpp | 2 +- dom/ipc/PBlob.ipdl | 14 +- dom/ipc/PBrowser.ipdl | 20 +- dom/ipc/PContent.ipdl | 15 +- dom/ipc/TabChild.cpp | 43 +- dom/ipc/TabChild.h | 15 +- dom/ipc/TabParent.cpp | 158 +- dom/ipc/TabParent.h | 28 +- dom/ipc/moz.build | 10 +- dom/ipc/nsIContentChild.cpp | 11 +- dom/ipc/nsIContentChild.h | 22 +- dom/ipc/nsIContentParent.cpp | 13 +- dom/ipc/nsIContentParent.h | 18 +- dom/ipc/nsIRemoteBlob.h | 23 +- dom/mobilemessage/MmsMessage.cpp | 2 + dom/mobilemessage/ipc/SmsIPCService.cpp | 1 + dom/mobilemessage/ipc/SmsParent.cpp | 2 +- dom/quota/QuotaManager.cpp | 156 +- dom/quota/QuotaManager.h | 28 +- dom/quota/StoragePrivilege.h | 5 +- dom/quota/nsIOfflineStorage.h | 19 +- dom/webidl/IDBDatabase.webidl | 1 - dom/webidl/IDBIndex.webidl | 8 +- dom/webidl/IDBObjectStore.webidl | 1 - dom/webidl/IDBTransaction.webidl | 1 - ipc/glue/BackgroundChild.h | 7 + ipc/glue/BackgroundChildImpl.cpp | 64 +- ipc/glue/BackgroundChildImpl.h | 39 +- ipc/glue/BackgroundImpl.cpp | 184 +- ipc/glue/BackgroundParent.h | 8 + ipc/glue/BackgroundParentImpl.cpp | 99 +- ipc/glue/BackgroundParentImpl.h | 28 + .../glue}/FileDescriptorSetChild.cpp | 8 +- .../ipc => ipc/glue}/FileDescriptorSetChild.h | 35 +- .../glue}/FileDescriptorSetParent.cpp | 8 +- .../glue}/FileDescriptorSetParent.h | 35 +- ipc/glue/InputStreamParams.ipdlh | 9 + ipc/glue/InputStreamUtils.cpp | 48 +- ipc/glue/PBackground.ipdl | 32 +- {dom/ipc => ipc/glue}/PFileDescriptorSet.ipdl | 7 +- ipc/glue/moz.build | 5 + netwerk/base/public/nsIFileStreams.idl | 8 + netwerk/base/src/nsFileStreams.cpp | 26 +- netwerk/protocol/http/HttpChannelChild.cpp | 2 +- netwerk/protocol/http/HttpChannelParent.cpp | 2 +- testing/mochitest/b2g_start_script.js | 2 + .../components/SpecialPowersObserver.js | 1 + .../content/SpecialPowersObserverAPI.js | 96 +- .../specialpowers/content/specialpowersAPI.js | 60 +- .../idbcursor_continue_invalid.htm.ini | 5 - .../idbfactory_deleteDatabase3.htm.ini | 5 - .../IndexedDB/idbobjectstore_clear.htm.ini | 8 - .../IndexedDB/idbobjectstore_clear2.htm.ini | 8 - .../IndexedDB/idbversionchangeevent.htm.ini | 5 - widget/android/NativeJSContainer.cpp | 1 + widget/xpwidgets/nsFilePickerProxy.cpp | 2 +- xpcom/io/nsILocalFileWin.idl | 15 +- xpcom/io/nsLocalFileWin.cpp | 49 +- xpcom/io/nsLocalFileWin.h | 5 + xpcom/threads/LazyIdleThread.cpp | 4 + 231 files changed, 33838 insertions(+), 27055 deletions(-) create mode 100644 dom/indexedDB/ActorsChild.cpp create mode 100644 dom/indexedDB/ActorsChild.h create mode 100644 dom/indexedDB/ActorsParent.cpp create mode 100644 dom/indexedDB/ActorsParent.h delete mode 100644 dom/indexedDB/AsyncConnectionHelper.cpp delete mode 100644 dom/indexedDB/AsyncConnectionHelper.h delete mode 100644 dom/indexedDB/CheckPermissionsHelper.cpp delete mode 100644 dom/indexedDB/CheckPermissionsHelper.h delete mode 100644 dom/indexedDB/Client.cpp delete mode 100644 dom/indexedDB/Client.h delete mode 100644 dom/indexedDB/DatabaseInfo.cpp delete mode 100644 dom/indexedDB/DatabaseInfo.h delete mode 100644 dom/indexedDB/FileManager.cpp delete mode 100644 dom/indexedDB/OpenDatabaseHelper.cpp delete mode 100644 dom/indexedDB/OpenDatabaseHelper.h create mode 100644 dom/indexedDB/PBackgroundIDBCursor.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBDatabase.ipdl rename dom/indexedDB/{ipc/PIndexedDBDeleteDatabaseRequest.ipdl => PBackgroundIDBDatabaseFile.ipdl} (67%) create mode 100644 dom/indexedDB/PBackgroundIDBFactory.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBRequest.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh create mode 100644 dom/indexedDB/PBackgroundIDBTransaction.ipdl create mode 100644 dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl create mode 100644 dom/indexedDB/PIndexedDBPermissionRequest.ipdl create mode 100644 dom/indexedDB/PermissionRequestBase.cpp create mode 100644 dom/indexedDB/PermissionRequestBase.h create mode 100644 dom/indexedDB/SerializationHelpers.h delete mode 100644 dom/indexedDB/ipc/IndexedDBChild.cpp delete mode 100644 dom/indexedDB/ipc/IndexedDBChild.h delete mode 100644 dom/indexedDB/ipc/IndexedDBParams.ipdlh delete mode 100644 dom/indexedDB/ipc/IndexedDBParent.cpp delete mode 100644 dom/indexedDB/ipc/IndexedDBParent.h delete mode 100644 dom/indexedDB/ipc/Makefile.in delete mode 100644 dom/indexedDB/ipc/PIndexedDB.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBCursor.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBDatabase.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBIndex.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBRequest.ipdl delete mode 100644 dom/indexedDB/ipc/PIndexedDBTransaction.ipdl delete mode 100644 dom/indexedDB/ipc/SerializationHelpers.h delete mode 100644 dom/indexedDB/ipc/mochitest.ini delete mode 100644 dom/indexedDB/ipc/moz.build delete mode 100644 dom/indexedDB/ipc/unit/head.js delete mode 100644 dom/indexedDB/ipc/unit/xpcshell.ini create mode 100644 dom/indexedDB/test/test_blocked_order.html create mode 100644 dom/indexedDB/test/test_disabled_quota_prompt.html create mode 100644 dom/indexedDB/test/test_invalidate.html create mode 100644 dom/indexedDB/test/unit/test_blocked_order.js create mode 100644 dom/indexedDB/test/unit/test_invalidate.js create mode 100644 dom/indexedDB/test/unit/xpcshell-child-process.ini create mode 100644 dom/indexedDB/test/unit/xpcshell-head-child-process.js rename dom/indexedDB/test/unit/{head.js => xpcshell-head-parent-process.js} (74%) create mode 100644 dom/indexedDB/test/unit/xpcshell-parent-process.ini rename dom/indexedDB/test/unit/{mochitest.ini => xpcshell-shared.ini} (88%) delete mode 100644 dom/indexedDB/test/unit/xpcshell.ini delete mode 100644 dom/ipc/Blob.h create mode 100644 dom/ipc/BlobChild.h create mode 100644 dom/ipc/BlobParent.h rename {dom/ipc => ipc/glue}/FileDescriptorSetChild.cpp (90%) rename {dom/ipc => ipc/glue}/FileDescriptorSetChild.h (68%) rename {dom/ipc => ipc/glue}/FileDescriptorSetParent.cpp (91%) rename {dom/ipc => ipc/glue}/FileDescriptorSetParent.h (69%) rename {dom/ipc => ipc/glue}/PFileDescriptorSet.ipdl (78%) delete mode 100644 testing/web-platform/meta/IndexedDB/idbcursor_continue_invalid.htm.ini delete mode 100644 testing/web-platform/meta/IndexedDB/idbfactory_deleteDatabase3.htm.ini delete mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore_clear.htm.ini delete mode 100644 testing/web-platform/meta/IndexedDB/idbobjectstore_clear2.htm.ini delete mode 100644 testing/web-platform/meta/IndexedDB/idbversionchangeevent.htm.ini diff --git a/content/base/public/nsDOMFile.h b/content/base/public/nsDOMFile.h index f4c91a827f02..c6e3416629c9 100644 --- a/content/base/public/nsDOMFile.h +++ b/content/base/public/nsDOMFile.h @@ -33,6 +33,7 @@ #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "nsWrapperCache.h" #include "nsCycleCollectionParticipant.h" +#include "nsWeakReference.h" class nsDOMMultipartFile; class nsIFile; @@ -64,6 +65,7 @@ class DOMFile MOZ_FINAL : public nsIDOMFile , public nsIXHRSendable , public nsIMutable , public nsIJSNativeInitializer + , public nsSupportsWeakReference { public: NS_DECL_NSIDOMBLOB @@ -190,9 +192,9 @@ public: virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) = 0; nsresult Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType, - uint8_t aArgc, nsIDOMBlob **aBlob); + uint8_t aArgc, DOMFileImpl** aBlobImpl); - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) = 0; @@ -321,7 +323,7 @@ public: virtual nsresult GetMozLastModifiedDate(uint64_t* aDate) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -464,7 +466,7 @@ public: virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -551,7 +553,7 @@ public: virtual nsresult GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE; - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; @@ -573,7 +575,7 @@ private: nsString mContentType; }; -class DOMFileImplFile MOZ_FINAL : public DOMFileImplBase +class DOMFileImplFile : public DOMFileImplBase { public: NS_DECL_ISUPPORTS_INHERITED @@ -690,6 +692,9 @@ public: void SetPath(const nsAString& aFullPath); +protected: + virtual ~DOMFileImplFile() {} + private: // Create slice DOMFileImplFile(const DOMFileImplFile* aOther, uint64_t aStart, @@ -719,9 +724,7 @@ private: } } - ~DOMFileImplFile() {} - - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index 33f5fc0733e3..c8adb79a51b1 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -55,7 +55,7 @@ DOMMultipartFileImpl::GetInternalStream(nsIInputStream** aStream) return CallQueryInterface(stream, aStream); } -already_AddRefed +already_AddRefed DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { @@ -77,18 +77,17 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, if (skipStart < l) { uint64_t upperBound = std::min(l - skipStart, length); - nsCOMPtr firstBlob; - rv = blobImpl->Slice(skipStart, skipStart + upperBound, - aContentType, 3, - getter_AddRefs(firstBlob)); + nsRefPtr firstImpl; + rv = blobImpl->Slice(skipStart, skipStart + upperBound, aContentType, 3, + getter_AddRefs(firstImpl)); NS_ENSURE_SUCCESS(rv, nullptr); // Avoid wrapping a single blob inside an DOMMultipartFileImpl if (length == upperBound) { - return firstBlob.forget(); + return firstImpl.forget(); } - blobImpls.AppendElement(static_cast(firstBlob.get())->Impl()); + blobImpls.AppendElement(firstImpl); length -= upperBound; i++; break; @@ -105,12 +104,12 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, NS_ENSURE_SUCCESS(rv, nullptr); if (length < l) { - nsCOMPtr lastBlob; + nsRefPtr lastBlob; rv = blobImpl->Slice(0, length, aContentType, 3, getter_AddRefs(lastBlob)); NS_ENSURE_SUCCESS(rv, nullptr); - blobImpls.AppendElement(static_cast(lastBlob.get())->Impl()); + blobImpls.AppendElement(lastBlob); } else { blobImpls.AppendElement(blobImpl); } @@ -118,9 +117,9 @@ DOMMultipartFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, } // we can create our blob now - nsCOMPtr blob = - new DOMFile(new DOMMultipartFileImpl(blobImpls, aContentType)); - return blob.forget(); + nsRefPtr impl = + new DOMMultipartFileImpl(blobImpls, aContentType); + return impl.forget(); } /* static */ nsresult diff --git a/content/base/src/nsDOMBlobBuilder.h b/content/base/src/nsDOMBlobBuilder.h index 7f238e442194..45bcf232e16b 100644 --- a/content/base/src/nsDOMBlobBuilder.h +++ b/content/base/src/nsDOMBlobBuilder.h @@ -78,7 +78,7 @@ public: uint32_t aArgc, JS::Value* aArgv); - virtual already_AddRefed + virtual already_AddRefed CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 1ab34fe4a9ca..132d9ddf9ee9 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -147,6 +147,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMFile) NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) NS_INTERFACE_MAP_ENTRY(nsIMutable) NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, IsFile()) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !(IsFile())) NS_INTERFACE_MAP_END @@ -298,7 +299,10 @@ already_AddRefed DOMFile::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - return mImpl->CreateSlice(aStart, aLength, aContentType); + nsRefPtr impl = + mImpl->CreateSlice(aStart, aLength, aContentType); + nsRefPtr slice = new DOMFile(impl); + return slice.forget(); } NS_IMETHODIMP @@ -400,7 +404,17 @@ DOMFile::Slice(int64_t aStart, int64_t aEnd, nsIDOMBlob **aBlob) { MOZ_ASSERT(mImpl); - return mImpl->Slice(aStart, aEnd, aContentType, aArgc, aBlob); + nsRefPtr impl; + nsresult rv = mImpl->Slice(aStart, aEnd, aContentType, aArgc, + getter_AddRefs(impl)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsRefPtr blob = new DOMFile(impl); + blob.forget(aBlob); + + return NS_OK; } NS_IMETHODIMP @@ -460,9 +474,9 @@ DOMFile::IsMemoryFile() nsresult DOMFileImpl::Slice(int64_t aStart, int64_t aEnd, const nsAString& aContentType, uint8_t aArgc, - nsIDOMBlob **aBlob) + DOMFileImpl** aBlobImpl) { - *aBlob = nullptr; + *aBlobImpl = nullptr; // Truncate aStart and aEnd so that we stay within this file. uint64_t thisLength; @@ -475,12 +489,15 @@ DOMFileImpl::Slice(int64_t aStart, int64_t aEnd, ParseSize((int64_t)thisLength, aStart, aEnd); - // Create the new file - nsCOMPtr blob = + nsRefPtr impl = CreateSlice((uint64_t)aStart, (uint64_t)(aEnd - aStart), aContentType); - blob.forget(aBlob); - return *aBlob ? NS_OK : NS_ERROR_UNEXPECTED; + if (!impl) { + return NS_ERROR_UNEXPECTED; + } + + impl.forget(aBlobImpl); + return NS_OK; } //////////////////////////////////////////////////////////////////////////// @@ -575,7 +592,7 @@ DOMFileImplBase::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) return NS_OK; } -already_AddRefed +already_AddRefed DOMFileImplBase::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { @@ -724,13 +741,13 @@ DOMFileImplBase::SetMutable(bool aMutable) //////////////////////////////////////////////////////////////////////////// // DOMFileImplFile implementation -already_AddRefed +already_AddRefed DOMFileImplFile::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsCOMPtr blob = - new DOMFile(new DOMFileImplFile(this, aStart, aLength, aContentType)); - return blob.forget(); + nsRefPtr impl = + new DOMFileImplFile(this, aStart, aLength, aContentType); + return impl.forget(); } nsresult @@ -832,7 +849,8 @@ DOMFileImplFile::GetMozLastModifiedDate(uint64_t* aLastModifiedDate) const uint32_t sFileStreamFlags = nsIFileInputStream::CLOSE_ON_EOF | nsIFileInputStream::REOPEN_ON_REWIND | - nsIFileInputStream::DEFER_OPEN; + nsIFileInputStream::DEFER_OPEN | + nsIFileInputStream::SHARE_DELETE; nsresult DOMFileImplFile::GetInternalStream(nsIInputStream** aStream) @@ -857,13 +875,13 @@ DOMFileImplFile::SetPath(const nsAString& aPath) NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplMemory, DOMFileImpl) -already_AddRefed +already_AddRefed DOMFileImplMemory::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsCOMPtr blob = - new DOMFile(new DOMFileImplMemory(this, aStart, aLength, aContentType)); - return blob.forget(); + nsRefPtr impl = + new DOMFileImplMemory(this, aStart, aLength, aContentType); + return impl.forget(); } nsresult @@ -982,17 +1000,17 @@ DOMFileImplMemory::DataOwner::EnsureMemoryReporterRegistered() NS_IMPL_ISUPPORTS_INHERITED0(DOMFileImplTemporaryFileBlob, DOMFileImpl) -already_AddRefed +already_AddRefed DOMFileImplTemporaryFileBlob::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { if (aStart + aLength > mLength) return nullptr; - nsCOMPtr blob = - new DOMFile(new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, - aLength, aContentType)); - return blob.forget(); + nsRefPtr impl = + new DOMFileImplTemporaryFileBlob(this, aStart + mStartPos, aLength, + aContentType); + return impl.forget(); } nsresult diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 2e6e813553f9..1d34d868b08c 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -153,7 +153,6 @@ #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" #include "mozilla/dom/EncodingUtils.h" -#include "mozilla/dom/quota/QuotaManager.h" #include "nsDOMNavigationTiming.h" #include "nsSMILAnimationController.h" @@ -8517,13 +8516,6 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest) } } - // Check if we have running offline storage transactions - quota::QuotaManager* quotaManager = - win ? quota::QuotaManager::Get() : nullptr; - if (quotaManager && quotaManager->HasOpenTransactions(win)) { - return false; - } - #ifdef MOZ_MEDIA_NAVIGATOR // Check if we have active GetUserMedia use if (MediaManager::Exists() && win && diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index a221b65f30a6..0b97e31f0469 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -34,8 +34,8 @@ #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/StructuredCloneUtils.h" -#include "mozilla/dom/PBlobChild.h" -#include "mozilla/dom/PBlobParent.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "JavaScriptChild.h" #include "JavaScriptParent.h" #include "mozilla/dom/DOMStringList.h" diff --git a/dom/archivereader/ArchiveZipFile.cpp b/dom/archivereader/ArchiveZipFile.cpp index c7973729fae6..816bdbf30f0c 100644 --- a/dom/archivereader/ArchiveZipFile.cpp +++ b/dom/archivereader/ArchiveZipFile.cpp @@ -396,16 +396,15 @@ ArchiveZipFileImpl::Traverse(nsCycleCollectionTraversalCallback &cb) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArchiveReader); } -already_AddRefed +already_AddRefed ArchiveZipFileImpl::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType) { - nsCOMPtr t = - new DOMFile(new ArchiveZipFileImpl(mFilename, mContentType, - aStart, mLength, mCentral, - mArchiveReader)); - return t.forget(); + nsRefPtr impl = + new ArchiveZipFileImpl(mFilename, mContentType, aStart, mLength, mCentral, + mArchiveReader); + return impl.forget(); } NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipFileImpl, DOMFileImpl) diff --git a/dom/archivereader/ArchiveZipFile.h b/dom/archivereader/ArchiveZipFile.h index dfede39fca85..dcf74323268a 100644 --- a/dom/archivereader/ArchiveZipFile.h +++ b/dom/archivereader/ArchiveZipFile.h @@ -71,9 +71,9 @@ protected: MOZ_COUNT_DTOR(ArchiveZipFileImpl); } - virtual already_AddRefed CreateSlice(uint64_t aStart, - uint64_t aLength, - const nsAString& aContentType) MOZ_OVERRIDE; + virtual already_AddRefed CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType) MOZ_OVERRIDE; private: // Data ZipCentral mCentral; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 9eeab72b6198..ec4f0ba05455 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -100,6 +100,7 @@ using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::ipc; using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla::gfx; @@ -3004,30 +3005,65 @@ nsDOMWindowUtils::AreDialogsEnabled(bool* aResult) NS_IMETHODIMP nsDOMWindowUtils::GetFileId(JS::Handle aFile, JSContext* aCx, - int64_t* aResult) + int64_t* _retval) { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - if (!aFile.isPrimitive()) { - JSObject* obj = aFile.toObjectOrNull(); - - indexedDB::IDBMutableFile* mutableFile = nullptr; - if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) { - *aResult = mutableFile->GetFileId(); - return NS_OK; - } - - nsISupports* nativeObj = - nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); - - nsCOMPtr blob = do_QueryInterface(nativeObj); - if (blob) { - *aResult = blob->GetFileId(); - return NS_OK; - } + if (aFile.isPrimitive()) { + *_retval = -1; + return NS_OK; } - *aResult = -1; + JSObject* obj = aFile.toObjectOrNull(); + + indexedDB::IDBMutableFile* mutableFile = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, obj, mutableFile))) { + *_retval = mutableFile->GetFileId(); + return NS_OK; + } + + nsISupports* nativeObj = + nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); + + nsCOMPtr blob = do_QueryInterface(nativeObj); + if (blob) { + *_retval = blob->GetFileId(); + return NS_OK; + } + + *_retval = -1; + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWindowUtils::GetFilePath(JS::HandleValue aFile, JSContext* aCx, + nsAString& _retval) +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + + if (aFile.isPrimitive()) { + _retval.Truncate(); + return NS_OK; + } + + JSObject* obj = aFile.toObjectOrNull(); + + nsISupports* nativeObj = + nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj); + + nsCOMPtr file = do_QueryInterface(nativeObj); + if (file) { + nsString filePath; + nsresult rv = file->GetMozFullPathInternal(filePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + _retval = filePath; + return NS_OK; + } + + _retval.Truncate(); return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index df5f51713338..b4dbb24c18da 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -187,7 +187,6 @@ #include "mozilla/dom/MessagePort.h" #include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/indexedDB/IDBFactory.h" -#include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/StructuredCloneTags.h" @@ -1562,12 +1561,6 @@ nsGlobalWindow::FreeInnerObjects() // Kill all of the workers for this window. mozilla::dom::workers::CancelWorkersForWindow(this); - // Close all offline storages for this window. - quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); - if (quotaManager) { - quotaManager->AbortCloseStoragesForWindow(this); - } - ClearAllTimeouts(); if (mIdleTimer) { @@ -10581,9 +10574,11 @@ GetIndexedDBEnabledForAboutURI(nsIURI *aURI) return flags & nsIAboutModule::ENABLE_INDEXED_DB; } -indexedDB::IDBFactory* +mozilla::dom::indexedDB::IDBFactory* nsGlobalWindow::GetIndexedDB(ErrorResult& aError) { + using mozilla::dom::indexedDB::IDBFactory; + if (!mIndexedDB) { // If the document has the sandboxed origin flag set // don't allow access to indexedDB. @@ -10630,8 +10625,7 @@ nsGlobalWindow::GetIndexedDB(ErrorResult& aError) } // This may be null if being created from a file. - aError = indexedDB::IDBFactory::Create(this, nullptr, - getter_AddRefs(mIndexedDB)); + aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB)); } return mIndexedDB; diff --git a/dom/bluetooth/BluetoothService.cpp b/dom/bluetooth/BluetoothService.cpp index 4784ae3608b1..ccb2b1b45fa6 100644 --- a/dom/bluetooth/BluetoothService.cpp +++ b/dom/bluetooth/BluetoothService.cpp @@ -26,6 +26,8 @@ #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/ipc/UnixSocket.h" #include "nsContentUtils.h" #include "nsIObserverService.h" diff --git a/dom/bluetooth/BluetoothService.h b/dom/bluetooth/BluetoothService.h index 8a3f006feb9b..d2df15a326bd 100644 --- a/dom/bluetooth/BluetoothService.h +++ b/dom/bluetooth/BluetoothService.h @@ -9,7 +9,6 @@ #include "BluetoothCommon.h" #include "BluetoothProfileManagerBase.h" -#include "mozilla/dom/ipc/Blob.h" #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsIDOMFile.h" @@ -17,7 +16,13 @@ #include "nsTObserverArray.h" #include "nsThreadUtils.h" +class nsIDOMBlob; + namespace mozilla { +namespace dom { +class BlobChild; +class BlobParent; +} namespace ipc { class UnixSocketConsumer; } diff --git a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp index 91a9c6d0abf6..09ff842f8940 100644 --- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp +++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp @@ -14,6 +14,7 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth/bluedroid/BluetoothOppManager.h b/dom/bluetooth/bluedroid/BluetoothOppManager.h index a0805ac67c17..fbfda2f294c6 100644 --- a/dom/bluetooth/bluedroid/BluetoothOppManager.h +++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h @@ -11,14 +11,20 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" -#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" +class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; +namespace mozilla { +namespace dom { +class BlobParent; +} +} + BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth/bluez/BluetoothDBusService.cpp b/dom/bluetooth/bluez/BluetoothDBusService.cpp index 51d07d102bda..838e62941e32 100644 --- a/dom/bluetooth/bluez/BluetoothDBusService.cpp +++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp @@ -44,6 +44,7 @@ #include "mozilla/ipc/DBusUtils.h" #include "mozilla/ipc/RawDBusConnection.h" #include "mozilla/LazyIdleThread.h" +#include "mozilla/Monitor.h" #include "mozilla/Mutex.h" #include "mozilla/NullPtr.h" #include "mozilla/StaticMutex.h" diff --git a/dom/bluetooth/bluez/BluetoothOppManager.cpp b/dom/bluetooth/bluez/BluetoothOppManager.cpp index 19bccd4423eb..26351aaea420 100644 --- a/dom/bluetooth/bluez/BluetoothOppManager.cpp +++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp @@ -14,6 +14,7 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth/bluez/BluetoothOppManager.h b/dom/bluetooth/bluez/BluetoothOppManager.h index d7de3917883a..d6e7bf6daa5c 100644 --- a/dom/bluetooth/bluez/BluetoothOppManager.h +++ b/dom/bluetooth/bluez/BluetoothOppManager.h @@ -11,14 +11,20 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" -#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" +class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; +namespace mozilla { +namespace dom { +class BlobParent; +} +} + BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp index 5c6038191dff..9f5ac93e929b 100644 --- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp @@ -10,6 +10,7 @@ #include "mozilla/Assertions.h" #include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ipc/BlobChild.h" #include "BluetoothChild.h" #include "MainThreadUtils.h" diff --git a/dom/bluetooth2/BluetoothService.cpp b/dom/bluetooth2/BluetoothService.cpp index 428c7c1f25c2..efa78cb74e19 100644 --- a/dom/bluetooth2/BluetoothService.cpp +++ b/dom/bluetooth2/BluetoothService.cpp @@ -26,6 +26,8 @@ #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "nsContentUtils.h" #include "nsIObserverService.h" #include "nsISettingsService.h" diff --git a/dom/bluetooth2/BluetoothService.h b/dom/bluetooth2/BluetoothService.h index 0588d15e76c3..8d05f49fcda5 100644 --- a/dom/bluetooth2/BluetoothService.h +++ b/dom/bluetooth2/BluetoothService.h @@ -9,7 +9,6 @@ #include "BluetoothCommon.h" #include "BluetoothProfileManagerBase.h" -#include "mozilla/dom/ipc/Blob.h" #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsIDOMFile.h" @@ -17,7 +16,13 @@ #include "nsTObserverArray.h" #include "nsThreadUtils.h" +class nsIDOMBlob; + namespace mozilla { +namespace dom { +class BlobChild; +class BlobParent; +} namespace ipc { class UnixSocketConsumer; } diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp index 91a9c6d0abf6..09ff842f8940 100644 --- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp +++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp @@ -14,6 +14,7 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth2/bluedroid/BluetoothOppManager.h b/dom/bluetooth2/bluedroid/BluetoothOppManager.h index a0805ac67c17..fbfda2f294c6 100644 --- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h +++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h @@ -11,14 +11,20 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" -#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" +class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; +namespace mozilla { +namespace dom { +class BlobParent; +} +} + BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/bluetooth2/bluez/BluetoothOppManager.cpp b/dom/bluetooth2/bluez/BluetoothOppManager.cpp index 19bccd4423eb..26351aaea420 100644 --- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp +++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp @@ -14,6 +14,7 @@ #include "ObexBase.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" diff --git a/dom/bluetooth2/bluez/BluetoothOppManager.h b/dom/bluetooth2/bluez/BluetoothOppManager.h index d7de3917883a..d6e7bf6daa5c 100644 --- a/dom/bluetooth2/bluez/BluetoothOppManager.h +++ b/dom/bluetooth2/bluez/BluetoothOppManager.h @@ -11,14 +11,20 @@ #include "BluetoothProfileManagerBase.h" #include "BluetoothSocketObserver.h" #include "DeviceStorage.h" -#include "mozilla/dom/ipc/Blob.h" #include "mozilla/ipc/UnixSocket.h" #include "nsCOMArray.h" +class nsIDOMBlob; class nsIOutputStream; class nsIInputStream; class nsIVolumeMountLock; +namespace mozilla { +namespace dom { +class BlobParent; +} +} + BEGIN_BLUETOOTH_NAMESPACE class BluetoothSocket; diff --git a/dom/datastore/DataStoreDB.cpp b/dom/datastore/DataStoreDB.cpp index 56b9135c4ca7..ee7c7b233348 100644 --- a/dom/datastore/DataStoreDB.cpp +++ b/dom/datastore/DataStoreDB.cpp @@ -7,15 +7,22 @@ #include "DataStoreDB.h" #include "DataStoreCallbacks.h" +#include "jsapi.h" #include "mozilla/dom/IDBDatabaseBinding.h" #include "mozilla/dom/IDBFactoryBinding.h" +#include "mozilla/dom/IDBObjectStoreBinding.h" #include "mozilla/dom/indexedDB/IDBDatabase.h" #include "mozilla/dom/indexedDB/IDBEvents.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IDBIndex.h" #include "mozilla/dom/indexedDB/IDBObjectStore.h" #include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" #include "nsIDOMEvent.h" +#include "nsIPrincipal.h" +#include "nsIXPConnect.h" #define DATASTOREDB_VERSION 1 #define DATASTOREDB_NAME "DataStoreDB" @@ -63,7 +70,9 @@ public: MOZ_ASSERT(version.IsNull()); #endif - return mDatabase->Close(); + mDatabase->Close(); + + return NS_OK; } private: @@ -93,7 +102,36 @@ nsresult DataStoreDB::CreateFactoryIfNeeded() { if (!mFactory) { - nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory)); + nsresult rv; + nsCOMPtr principal = + do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + MOZ_ASSERT(xpc); + + AutoSafeJSContext cx; + + nsCOMPtr globalHolder; + rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + JS::Rooted global(cx, globalHolder->GetJSObject()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_UNEXPECTED; + } + + // The CreateSandbox call returns a proxy to the actual sandbox object. We + // don't need a proxy here. + global = js::UncheckedUnwrap(global); + + JSAutoCompartment ac(cx, global); + + rv = IDBFactory::CreateForDatastore(cx, global, getter_AddRefs(mFactory)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -301,11 +339,7 @@ DataStoreDB::Delete() mTransaction = nullptr; if (mDatabase) { - rv = mDatabase->Close(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - + mDatabase->Close(); mDatabase = nullptr; } diff --git a/dom/datastore/DataStoreRevision.cpp b/dom/datastore/DataStoreRevision.cpp index c12dc7ef0aeb..1078df678b89 100644 --- a/dom/datastore/DataStoreRevision.cpp +++ b/dom/datastore/DataStoreRevision.cpp @@ -9,8 +9,9 @@ #include "DataStoreCallbacks.h" #include "DataStoreService.h" #include "mozilla/dom/DataStoreBinding.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" #include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/indexedDB/IDBObjectStore.h" +#include "mozilla/dom/indexedDB/IDBRequest.h" #include "nsIDOMEvent.h" namespace mozilla { diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index 580246bc54be..eef48ade2935 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -22,6 +22,8 @@ #include "mozilla/dom/DOMError.h" #include "mozilla/dom/indexedDB/IDBCursor.h" #include "mozilla/dom/indexedDB/IDBObjectStore.h" +#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/unused.h" diff --git a/dom/datastore/tests/test_oop_events.html b/dom/datastore/tests/test_oop_events.html index 349c4ceee2f3..07132590485b 100644 --- a/dom/datastore/tests/test_oop_events.html +++ b/dom/datastore/tests/test_oop_events.html @@ -1,4 +1,4 @@ - + @@ -137,7 +137,7 @@ // Uninstall the apps function() { uninstallApp(gApps[0]); }, - function() { uninstallApp(gApps[1]); }, + function() { uninstallApp(gApps[1]); } ]; function runTest() { diff --git a/dom/devicestorage/DeviceStorageRequestChild.cpp b/dom/devicestorage/DeviceStorageRequestChild.cpp index 5075f4ae8f7c..3b426a4c309b 100644 --- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -8,7 +8,7 @@ #include "DeviceStorageFileDescriptor.h" #include "nsDeviceStorage.h" #include "nsDOMFile.h" -#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/ipc/BlobChild.h" namespace mozilla { namespace dom { diff --git a/dom/devicestorage/DeviceStorageRequestParent.cpp b/dom/devicestorage/DeviceStorageRequestParent.cpp index 7ca5e776543c..8ccc281d19b9 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -8,7 +8,7 @@ #include "nsIMIMEService.h" #include "nsCExternalHandlerService.h" #include "mozilla/unused.h" -#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "ContentParent.h" #include "nsProxyRelease.h" #include "AppProcessChecker.h" diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index a3d7fe1bfd65..aa0947a22f12 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -16,7 +16,7 @@ #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/FileSystemUtils.h" -#include "mozilla/dom/ipc/Blob.h" +#include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/Promise.h" diff --git a/dom/filehandle/FileStreamWrappers.cpp b/dom/filehandle/FileStreamWrappers.cpp index 124da638ca90..688c315416ec 100644 --- a/dom/filehandle/FileStreamWrappers.cpp +++ b/dom/filehandle/FileStreamWrappers.cpp @@ -8,7 +8,9 @@ #include "FileHelper.h" #include "MainThreadUtils.h" +#include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/ipc/InputStreamParams.h" #include "MutableFile.h" #include "nsDebug.h" #include "nsError.h" @@ -16,6 +18,10 @@ #include "nsISeekableStream.h" #include "nsThreadUtils.h" +#ifdef DEBUG +#include "nsXULAppAPI.h" +#endif + namespace mozilla { namespace dom { @@ -127,7 +133,8 @@ FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream, NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper, FileStreamWrapper, - nsIInputStream) + nsIInputStream, + nsIIPCSerializableInputStream) NS_IMETHODIMP FileInputStreamWrapper::Close() @@ -230,6 +237,26 @@ FileInputStreamWrapper::IsNonBlocking(bool* _retval) return NS_OK; } +void +FileInputStreamWrapper::Serialize(InputStreamParams& aParams, + FileDescriptorArray& /* aFDs */) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr thisStream = do_QueryObject(this); + + aParams = mozilla::ipc::SameProcessInputStreamParams( + reinterpret_cast(thisStream.forget().take())); +} + +bool +FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */, + const FileDescriptorArray& /* aFDs */) +{ + MOZ_CRASH("Should never get here!"); +} + FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream, FileHelper* aFileHelper, uint64_t aOffset, diff --git a/dom/filehandle/FileStreamWrappers.h b/dom/filehandle/FileStreamWrappers.h index ff97c069cead..de847ec8c2f8 100644 --- a/dom/filehandle/FileStreamWrappers.h +++ b/dom/filehandle/FileStreamWrappers.h @@ -11,8 +11,13 @@ #include "nsCOMPtr.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" +#include "nsIIPCSerializableInputStream.h" namespace mozilla { +namespace ipc { +class InputStreamParams; +} // namespace ipc + namespace dom { class FileHelper; @@ -46,11 +51,15 @@ protected: }; class FileInputStreamWrapper : public FileStreamWrapper, - public nsIInputStream + public nsIInputStream, + public nsIIPCSerializableInputStream { + typedef mozilla::ipc::InputStreamParams InputStreamParams; + public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIINPUTSTREAM + NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM FileInputStreamWrapper(nsISupports* aFileStream, FileHelper* aFileHelper, diff --git a/dom/filehandle/moz.build b/dom/filehandle/moz.build index a42e8a200dca..ed76866e9b6f 100644 --- a/dom/filehandle/moz.build +++ b/dom/filehandle/moz.build @@ -28,6 +28,8 @@ UNIFIED_SOURCES += [ FAIL_ON_WARNINGS = True +include('/ipc/chromium/chromium-config.mozbuild') + LOCAL_INCLUDES += [ '../base', ] diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 518f024e4d60..f6e1102f4086 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -13,6 +13,8 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsNetUtil.h" diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index a70b6be3aebf..d64b53b85de9 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -13,6 +13,7 @@ #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PContent.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/unused.h" #include "nsDOMFile.h" diff --git a/dom/filesystem/FileSystemTaskBase.h b/dom/filesystem/FileSystemTaskBase.h index 50068bfe5d02..d57231226399 100644 --- a/dom/filesystem/FileSystemTaskBase.h +++ b/dom/filesystem/FileSystemTaskBase.h @@ -10,13 +10,13 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/PFileSystemRequestChild.h" -#include "mozilla/dom/ipc/Blob.h" class nsIDOMFile; namespace mozilla { namespace dom { +class BlobParent; class FileSystemBase; class FileSystemParams; class Promise; diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 993a37913d25..9e63d7834879 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -11,6 +11,8 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsStringGlue.h" diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index fc0c3cf0e1ec..94d1207f84e4 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -10,6 +10,8 @@ #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" #include "nsDOMFile.h" #include "nsIFile.h" #include "nsStringGlue.h" diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp new file mode 100644 index 000000000000..f896fdd80b3c --- /dev/null +++ b/dom/indexedDB/ActorsChild.cpp @@ -0,0 +1,2351 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ActorsChild.h" + +#include "BackgroundChildImpl.h" +#include "FileManager.h" +#include "IDBDatabase.h" +#include "IDBEvents.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "IDBMutableFile.h" +#include "IDBRequest.h" +#include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/Maybe.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIBFCacheEntry.h" +#include "nsIDocument.h" +#include "nsIDOMEvent.h" +#include "nsIEventTarget.h" +#include "nsPIDOMWindow.h" +#include "nsThreadUtils.h" +#include "nsTraceRefcnt.h" +#include "PermissionRequestBase.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" + +#ifdef DEBUG +#include "IndexedDatabaseManager.h" +#endif + +#define GC_ON_IPC_MESSAGES 0 + +#if defined(DEBUG) || GC_ON_IPC_MESSAGES + +#include "js/GCAPI.h" +#include "nsJSEnvironment.h" + +#define BUILD_GC_ON_IPC_MESSAGES + +#endif // DEBUG || GC_ON_IPC_MESSAGES + +namespace mozilla { +namespace dom { +namespace indexedDB { + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +namespace { + +void +MaybeCollectGarbageOnIPCMessage() +{ +#ifdef BUILD_GC_ON_IPC_MESSAGES + static const bool kCollectGarbageOnIPCMessages = +#if GC_ON_IPC_MESSAGES + true; +#else + false; +#endif // GC_ON_IPC_MESSAGES + + if (!kCollectGarbageOnIPCMessages) { + return; + } + + static bool haveWarnedAboutGC = false; + static bool haveWarnedAboutNonMainThread = false; + + if (!haveWarnedAboutGC) { + haveWarnedAboutGC = true; + NS_WARNING("IndexedDB child actor GC debugging enabled!"); + } + + if (!NS_IsMainThread()) { + if (!haveWarnedAboutNonMainThread) { + haveWarnedAboutNonMainThread = true; + NS_WARNING("Don't know how to GC on a non-main thread yet."); + } + return; + } + + nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC); + nsJSContext::CycleCollectNow(); +#endif // BUILD_GC_ON_IPC_MESSAGES +} + +class MOZ_STACK_CLASS AutoSetCurrentTransaction MOZ_FINAL +{ + typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; + + IDBTransaction* const mTransaction; + IDBTransaction* mPreviousTransaction; + IDBTransaction** mThreadLocalSlot; + +public: + AutoSetCurrentTransaction(IDBTransaction* aTransaction) + : mTransaction(aTransaction) + , mPreviousTransaction(nullptr) + , mThreadLocalSlot(nullptr) + { + if (aTransaction) { + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + // Hang onto this location for resetting later. + mThreadLocalSlot = &threadLocal->mCurrentTransaction; + + // Save the current value. + mPreviousTransaction = *mThreadLocalSlot; + + // Set the new value. + *mThreadLocalSlot = aTransaction; + } + } + + ~AutoSetCurrentTransaction() + { + MOZ_ASSERT_IF(mThreadLocalSlot, mTransaction); + + if (mThreadLocalSlot) { + MOZ_ASSERT(*mThreadLocalSlot == mTransaction); + + // Reset old value. + *mThreadLocalSlot = mPreviousTransaction; + } + } + + IDBTransaction* + Transaction() const + { + return mTransaction; + } +}; + +class MOZ_STACK_CLASS ResultHelper MOZ_FINAL + : public IDBRequest::ResultCallback +{ + IDBRequest* mRequest; + AutoSetCurrentTransaction mAutoTransaction; + + union + { + nsISupports* mISupports; + StructuredCloneReadInfo* mStructuredClone; + const nsTArray* mStructuredCloneArray; + const Key* mKey; + const nsTArray* mKeyArray; + const JS::Value* mJSVal; + const JS::Handle* mJSValHandle; + } mResult; + + enum + { + ResultTypeISupports, + ResultTypeStructuredClone, + ResultTypeStructuredCloneArray, + ResultTypeKey, + ResultTypeKeyArray, + ResultTypeJSVal, + ResultTypeJSValHandle, + } mResultType; + +public: + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + nsISupports* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeISupports) + { + MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!"); + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mISupports = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + StructuredCloneReadInfo* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeStructuredClone) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mStructuredClone = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const nsTArray* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeStructuredCloneArray) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mStructuredCloneArray = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const Key* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeKey) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mKey = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const nsTArray* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeKeyArray) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aResult); + + mResult.mKeyArray = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const JS::Value* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeJSVal) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(!aResult->isGCThing()); + + mResult.mJSVal = aResult; + } + + ResultHelper(IDBRequest* aRequest, + IDBTransaction* aTransaction, + const JS::Handle* aResult) + : mRequest(aRequest) + , mAutoTransaction(aTransaction) + , mResultType(ResultTypeJSValHandle) + { + MOZ_ASSERT(aRequest); + + mResult.mJSValHandle = aResult; + } + + IDBRequest* + Request() const + { + return mRequest; + } + + IDBTransaction* + Transaction() const + { + return mAutoTransaction.Transaction(); + } + + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle aResult) MOZ_OVERRIDE + { + MOZ_ASSERT(aCx); + MOZ_ASSERT(mRequest); + + switch (mResultType) { + case ResultTypeISupports: + return GetResult(aCx, mResult.mISupports, aResult); + + case ResultTypeStructuredClone: + return GetResult(aCx, mResult.mStructuredClone, aResult); + + case ResultTypeStructuredCloneArray: + return GetResult(aCx, mResult.mStructuredCloneArray, aResult); + + case ResultTypeKey: + return GetResult(aCx, mResult.mKey, aResult); + + case ResultTypeKeyArray: + return GetResult(aCx, mResult.mKeyArray, aResult); + + case ResultTypeJSVal: + aResult.set(*mResult.mJSVal); + return NS_OK; + + case ResultTypeJSValHandle: + aResult.set(*mResult.mJSValHandle); + return NS_OK; + + default: + MOZ_CRASH("Unknown result type!"); + } + + MOZ_CRASH("Should never get here!"); + } + +private: + nsresult + GetResult(JSContext* aCx, + nsISupports* aSupports, + JS::MutableHandle aResult) + { + MOZ_ASSERT(NS_IsMainThread(), "This won't work off the main thread!"); + + if (!aSupports) { + aResult.setNull(); + return NS_OK; + } + + nsresult rv = nsContentUtils::WrapNative(aCx, aSupports, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + StructuredCloneReadInfo* aCloneInfo, + JS::MutableHandle aResult) + { + bool ok = IDBObjectStore::DeserializeValue(aCx, *aCloneInfo, aResult); + + aCloneInfo->mCloneBuffer.clear(); + + if (NS_WARN_IF(!ok)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsTArray* aCloneInfos, + JS::MutableHandle aResult) + { + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!array)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aCloneInfos->IsEmpty()) { + const uint32_t count = aCloneInfos->Length(); + + if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0; index < count; index++) { + auto& cloneInfo = + const_cast(aCloneInfos->ElementAt(index)); + + JS::Rooted value(aCx); + + nsresult rv = GetResult(aCx, &cloneInfo, &value); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aResult.setObject(*array); + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const Key* aKey, + JS::MutableHandle aResult) + { + nsresult rv = aKey->ToJSVal(aCx, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsTArray* aKeys, + JS::MutableHandle aResult) + { + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (NS_WARN_IF(!array)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (!aKeys->IsEmpty()) { + const uint32_t count = aKeys->Length(); + + if (NS_WARN_IF(!JS_SetArrayLength(aCx, array, count))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t index = 0; index < count; index++) { + const Key& key = aKeys->ElementAt(index); + MOZ_ASSERT(!key.IsUnset()); + + JS::Rooted value(aCx); + + nsresult rv = GetResult(aCx, &key, &value); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!JS_SetElement(aCx, array, index, value))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + } + + aResult.setObject(*array); + return NS_OK; + } +}; + +class PermissionRequestMainProcessHelper MOZ_FINAL + : public PermissionRequestBase +{ + BackgroundFactoryRequestChild* mActor; + nsRefPtr mFactory; + +public: + PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor, + IDBFactory* aFactory, + nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal) + : PermissionRequestBase(aWindow, aPrincipal) + , mActor(aActor) + , mFactory(aFactory) + { + MOZ_ASSERT(aActor); + MOZ_ASSERT(aFactory); + aActor->AssertIsOnOwningThread(); + } + +protected: + ~PermissionRequestMainProcessHelper() + { } + +private: + virtual void + OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE; +}; + +class PermissionRequestChildProcessActor MOZ_FINAL + : public PIndexedDBPermissionRequestChild +{ + BackgroundFactoryRequestChild* mActor; + nsRefPtr mFactory; + +public: + PermissionRequestChildProcessActor(BackgroundFactoryRequestChild* aActor, + IDBFactory* aFactory) + : mActor(aActor) + , mFactory(aFactory) + { + MOZ_ASSERT(aActor); + MOZ_ASSERT(aFactory); + aActor->AssertIsOnOwningThread(); + } + +protected: + ~PermissionRequestChildProcessActor() + { } + + virtual bool + Recv__delete__(const uint32_t& aPermission) MOZ_OVERRIDE; +}; + +void +ConvertActorsToBlobs(IDBDatabase* aDatabase, + const SerializedStructuredCloneReadInfo& aCloneReadInfo, + nsTArray& aFiles) +{ + MOZ_ASSERT(aFiles.IsEmpty()); + + const nsTArray& blobs = aCloneReadInfo.blobsChild(); + const nsTArray& fileInfos = aCloneReadInfo.fileInfos(); + + MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(), + blobs.Length() == fileInfos.Length()); + MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty()); + + if (!blobs.IsEmpty()) { + const uint32_t count = blobs.Length(); + aFiles.SetCapacity(count); + + for (uint32_t index = 0; index < count; index++) { + BlobChild* actor = static_cast(blobs[index]); + + nsCOMPtr blob = actor->GetBlob(); + MOZ_ASSERT(blob); + + nsRefPtr fileInfo; + if (!fileInfos.IsEmpty()) { + fileInfo = dont_AddRef(reinterpret_cast(fileInfos[index])); + + MOZ_ASSERT(fileInfo); + MOZ_ASSERT(fileInfo->Id() > 0); + + blob->AddFileInfo(fileInfo); + } + + aDatabase->NoteReceivedBlob(blob); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mFile.swap(blob); + file->mFileInfo.swap(fileInfo); + } + } +} + +void +DispatchSuccessEvent(ResultHelper* aResultHelper, + nsIDOMEvent* aEvent = nullptr) +{ + MOZ_ASSERT(aResultHelper); + + PROFILER_LABEL("IndexedDB", + "DispatchSuccessEvent", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr request = aResultHelper->Request(); + MOZ_ASSERT(request); + request->AssertIsOnOwningThread(); + + nsRefPtr transaction = aResultHelper->Transaction(); + + nsCOMPtr successEvent; + if (!aEvent) { + successEvent = CreateGenericEvent(request, + nsDependentString(kSuccessEventType), + eDoesNotBubble, + eNotCancelable); + if (NS_WARN_IF(!successEvent)) { + return; + } + + aEvent = successEvent; + } + + request->SetResultCallback(aResultHelper); + + MOZ_ASSERT(aEvent); + MOZ_ASSERT_IF(transaction, transaction->IsOpen()); + + bool dummy; + nsresult rv = request->DispatchEvent(aEvent, &dummy); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT_IF(transaction, + transaction->IsOpen() || transaction->IsAborted()); + + WidgetEvent* internalEvent = aEvent->GetInternalNSEvent(); + MOZ_ASSERT(internalEvent); + + if (transaction && + transaction->IsOpen() && + internalEvent->mFlags.mExceptionHasBeenRisen) { + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } +} + +void +DispatchErrorEvent(IDBRequest* aRequest, + nsresult aErrorCode, + IDBTransaction* aTransaction = nullptr, + nsIDOMEvent* aEvent = nullptr) +{ + MOZ_ASSERT(aRequest); + aRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + PROFILER_LABEL("IndexedDB", + "DispatchErrorEvent", + js::ProfileEntry::Category::STORAGE); + + nsRefPtr request = aRequest; + nsRefPtr transaction = aTransaction; + + request->SetError(aErrorCode); + + nsCOMPtr errorEvent; + if (!aEvent) { + // Make an error event and fire it at the target. + errorEvent = CreateGenericEvent(request, + nsDependentString(kErrorEventType), + eDoesBubble, + eCancelable); + if (NS_WARN_IF(!errorEvent)) { + return; + } + + aEvent = errorEvent; + } + + Maybe asct; + if (aTransaction) { + asct.emplace(aTransaction); + } + + bool doDefault; + nsresult rv = request->DispatchEvent(aEvent, &doDefault); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted()); + + if (transaction && transaction->IsOpen()) { + WidgetEvent* internalEvent = aEvent->GetInternalNSEvent(); + MOZ_ASSERT(internalEvent); + + if (internalEvent->mFlags.mExceptionHasBeenRisen) { + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } else if (doDefault) { + transaction->Abort(request); + } + } +} + +} // anonymous namespace + +/******************************************************************************* + * Local class implementations + ******************************************************************************/ + +void +PermissionRequestMainProcessHelper::OnPromptComplete( + PermissionValue aPermissionValue) +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + mActor->SendPermissionRetry(); + + mActor = nullptr; + mFactory = nullptr; +} + +bool +PermissionRequestChildProcessActor::Recv__delete__( + const uint32_t& /* aPermission */) +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mFactory); + + MaybeCollectGarbageOnIPCMessage(); + + nsRefPtr factory; + mFactory.swap(factory); + + mActor->SendPermissionRetry(); + mActor = nullptr; + + return true; +} + +/******************************************************************************* + * BackgroundRequestChildBase + ******************************************************************************/ + +BackgroundRequestChildBase::BackgroundRequestChildBase(IDBRequest* aRequest) + : mRequest(aRequest) + , mActorDestroyed(false) +{ + MOZ_ASSERT(aRequest); + aRequest->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase); +} + +BackgroundRequestChildBase::~BackgroundRequestChildBase() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase); +} + +#ifdef DEBUG + +void +BackgroundRequestChildBase::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mRequest); + mRequest->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundRequestChildBase::NoteActorDestroyed() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; +} + +/******************************************************************************* + * BackgroundFactoryChild + ******************************************************************************/ + +BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory* aFactory) + : mFactory(aFactory) +#ifdef DEBUG + , mOwningThread(NS_GetCurrentThread()) +#endif +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild); +} + +BackgroundFactoryChild::~BackgroundFactoryChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild); +} + +#ifdef DEBUG + +void +BackgroundFactoryChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +#endif // DEBUG + +void +BackgroundFactoryChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mFactory) { + mFactory->ClearBackgroundActor(); + mFactory = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe()); + } +} + +void +BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mFactory) { + mFactory->ClearBackgroundActor(); +#ifdef DEBUG + mFactory = nullptr; +#endif + } +} + +PBackgroundIDBFactoryRequestChild* +BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild( + const FactoryRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBFactoryRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild( + PBackgroundIDBFactoryRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBDatabaseChild* +BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild( + const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestChild* aRequest) +{ + AssertIsOnOwningThread(); + + auto request = static_cast(aRequest); + MOZ_ASSERT(request); + + return new BackgroundDatabaseChild(aSpec, request); +} + +bool +BackgroundFactoryChild::DeallocPBackgroundIDBDatabaseChild( + PBackgroundIDBDatabaseChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFactoryRequestChild + ******************************************************************************/ + +BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( + IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + bool aIsDeleteOp, + uint64_t aRequestedVersion, + PersistenceType aPersistenceType) + : BackgroundRequestChildBase(aOpenRequest) + , mFactory(aFactory) + , mRequestedVersion(aRequestedVersion) + , mPersistenceType(aPersistenceType) + , mIsDeleteOp(aIsDeleteOp) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aOpenRequest); + + MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild); +} + +BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild); +} + +IDBOpenDBRequest* +BackgroundFactoryRequestChild::GetOpenDBRequest() const +{ + AssertIsOnOwningThread(); + + IDBRequest* baseRequest = BackgroundRequestChildBase::GetDOMObject(); + return static_cast(baseRequest); +} + +bool +BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + mRequest->Reset(); + + DispatchErrorEvent(mRequest, aResponse); + + return true; +} + +bool +BackgroundFactoryRequestChild::HandleResponse( + const OpenDatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + mRequest->Reset(); + + auto databaseActor = + static_cast(aResponse.databaseChild()); + MOZ_ASSERT(databaseActor); + + databaseActor->EnsureDOMObject(); + + IDBDatabase* database = databaseActor->GetDOMObject(); + MOZ_ASSERT(database); + + ResultHelper helper(mRequest, nullptr, + static_cast(database)); + + DispatchSuccessEvent(&helper); + + databaseActor->ReleaseDOMObject(); + + return true; +} + +bool +BackgroundFactoryRequestChild::HandleResponse( + const DeleteDatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, nullptr, &JS::UndefinedHandleValue); + + nsCOMPtr successEvent = + IDBVersionChangeEvent::Create(mRequest, + nsDependentString(kSuccessEventType), + aResponse.previousVersion()); + if (NS_WARN_IF(!successEvent)) { + return false; + } + + DispatchSuccessEvent(&helper, successEvent); + + return true; +} + +void +BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + NoteActorDestroyed(); +} + +bool +BackgroundFactoryRequestChild::Recv__delete__( + const FactoryRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + MaybeCollectGarbageOnIPCMessage(); + + switch (aResponse.type()) { + case FactoryRequestResponse::Tnsresult: + return HandleResponse(aResponse.get_nsresult()); + + case FactoryRequestResponse::TOpenDatabaseRequestResponse: + return HandleResponse(aResponse.get_OpenDatabaseRequestResponse()); + + case FactoryRequestResponse::TDeleteDatabaseRequestResponse: + return HandleResponse(aResponse.get_DeleteDatabaseRequestResponse()); + + default: + MOZ_CRASH("Unknown response type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +bool +BackgroundFactoryRequestChild::RecvPermissionChallenge( + const PrincipalInfo& aPrincipalInfo) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!NS_IsMainThread()) { + MOZ_CRASH("Implement me for workers!"); + } + + nsresult rv; + nsCOMPtr principal = + mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (XRE_GetProcessType() == GeckoProcessType_Default) { + nsCOMPtr window = mFactory->GetParentObject(); + MOZ_ASSERT(window); + + nsRefPtr helper = + new PermissionRequestMainProcessHelper(this, mFactory, window, principal); + + PermissionRequestBase::PermissionValue permission; + if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { + return false; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || + permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt); + + if (permission != PermissionRequestBase::kPermissionPrompt) { + SendPermissionRetry(); + } + return true; + } + + nsRefPtr tabChild = mFactory->GetTabChild(); + MOZ_ASSERT(tabChild); + + IPC::Principal ipcPrincipal(principal); + + auto* actor = new PermissionRequestChildProcessActor(this, mFactory); + + tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal); + + return true; +} + +bool +BackgroundFactoryRequestChild::RecvBlocked(const uint64_t& aCurrentVersion) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + MaybeCollectGarbageOnIPCMessage(); + + const nsDependentString type(kBlockedEventType); + + nsCOMPtr blockedEvent; + if (mIsDeleteOp) { + blockedEvent = + IDBVersionChangeEvent::Create(mRequest, type, aCurrentVersion); + } else { + blockedEvent = + IDBVersionChangeEvent::Create(mRequest, + type, + aCurrentVersion, + mRequestedVersion); + } + + if (NS_WARN_IF(!blockedEvent)) { + return false; + } + + nsRefPtr kungFuDeathGrip = mRequest; + + bool dummy; + NS_WARN_IF(NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))); + + return true; +} + +/******************************************************************************* + * BackgroundDatabaseChild + ******************************************************************************/ + +BackgroundDatabaseChild::BackgroundDatabaseChild( + const DatabaseSpec& aSpec, + BackgroundFactoryRequestChild* aOpenRequestActor) + : mSpec(new DatabaseSpec(aSpec)) + , mOpenRequestActor(aOpenRequestActor) + , mDatabase(nullptr) + , mPersistenceType(aOpenRequestActor->mPersistenceType) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aOpenRequestActor); + + MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild); +} + +BackgroundDatabaseChild::~BackgroundDatabaseChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild); +} + +void +BackgroundDatabaseChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongDatabase); + MOZ_ASSERT(!mOpenRequestActor); + + if (mDatabase) { + mDatabase->ClearBackgroundActor(); + mDatabase = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe()); + } +} + +void +BackgroundDatabaseChild::EnsureDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenRequestActor); + + if (mTemporaryStrongDatabase) { + MOZ_ASSERT(!mSpec); + return; + } + + MOZ_ASSERT(mSpec); + + auto request = mOpenRequestActor->GetDOMObject(); + MOZ_ASSERT(request); + + auto factory = + static_cast(Manager())->GetDOMObject(); + MOZ_ASSERT(factory); + + mTemporaryStrongDatabase = + IDBDatabase::Create(request, factory, this, mSpec); + + MOZ_ASSERT(mTemporaryStrongDatabase); + mTemporaryStrongDatabase->AssertIsOnOwningThread(); + + mDatabase = mTemporaryStrongDatabase; + mSpec.forget(); +} + +void +BackgroundDatabaseChild::ReleaseDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTemporaryStrongDatabase); + mTemporaryStrongDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenRequestActor); + MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); + + mOpenRequestActor = nullptr; + + // This may be the final reference to the IDBDatabase object so we may end up + // calling SendDeleteMeInternal() here. Make sure everything is cleaned up + // properly before proceeding. + mTemporaryStrongDatabase = nullptr; +} + +void +BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mDatabase) { + mDatabase->ClearBackgroundActor(); +#ifdef DEBUG + mDatabase = nullptr; +#endif + } +} + +PBackgroundIDBDatabaseFileChild* +BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild( + const BlobOrInputStream& aBlobOrInputStream) +{ + MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild( + PBackgroundIDBDatabaseFileChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete aActor; + return true; +} + +PBackgroundIDBTransactionChild* +BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild( + const nsTArray& aObjectStoreNames, + const Mode& aMode) +{ + MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually " + "constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBTransactionChild( + PBackgroundIDBTransactionChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBVersionChangeTransactionChild* +BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) +{ + AssertIsOnOwningThread(); + + IDBOpenDBRequest* request = mOpenRequestActor->GetOpenDBRequest(); + MOZ_ASSERT(request); + + return new BackgroundVersionChangeTransactionChild(request); +} + +bool +BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor( + PBackgroundIDBVersionChangeTransactionChild* aActor, + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(mOpenRequestActor); + + MaybeCollectGarbageOnIPCMessage(); + + EnsureDOMObject(); + + auto actor = static_cast(aActor); + + nsRefPtr transaction = + IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId, + aNextIndexId); + if (NS_WARN_IF(!transaction)) { + return false; + } + + transaction->AssertIsOnOwningThread(); + + actor->SetDOMTransaction(transaction); + + mDatabase->EnterSetVersionTransaction(aRequestedVersion); + + nsRefPtr request = mOpenRequestActor->GetOpenDBRequest(); + MOZ_ASSERT(request); + + request->SetTransaction(transaction); + + nsCOMPtr upgradeNeededEvent = + IDBVersionChangeEvent::Create(request, + nsDependentString(kUpgradeNeededEventType), + aCurrentVersion, + aRequestedVersion); + if (NS_WARN_IF(!upgradeNeededEvent)) { + return false; + } + + ResultHelper helper(request, transaction, + static_cast(mDatabase)); + + DispatchSuccessEvent(&helper, upgradeNeededEvent); + + return true; +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBVersionChangeTransactionChild( + PBackgroundIDBVersionChangeTransactionChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +bool +BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, + const NullableVersion& aNewVersion) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!mDatabase || mDatabase->IsClosed()) { + return true; + } + + nsRefPtr kungFuDeathGrip = mDatabase; + + // Handle bfcache'd windows. + if (nsPIDOMWindow* owner = mDatabase->GetOwner()) { + // The database must be closed if the window is already frozen. + bool shouldAbortAndClose = owner->IsFrozen(); + + // Anything in the bfcache has to be evicted and then we have to close the + // database also. + if (nsCOMPtr doc = owner->GetExtantDoc()) { + if (nsCOMPtr bfCacheEntry = doc->GetBFCacheEntry()) { + bfCacheEntry->RemoveFromBFCacheSync(); + shouldAbortAndClose = true; + } + } + + if (shouldAbortAndClose) { + // Invalidate() doesn't close the database in the parent, so we have + // to call Close() and AbortTransactions() manually. + mDatabase->AbortTransactions(); + mDatabase->Close(); + return true; + } + } + + // Otherwise fire a versionchange event. + const nsDependentString type(kVersionChangeEventType); + + nsCOMPtr versionChangeEvent; + + switch (aNewVersion.type()) { + case NullableVersion::Tnull_t: + versionChangeEvent = + IDBVersionChangeEvent::Create(mDatabase, type, aOldVersion); + break; + + case NullableVersion::Tuint64_t: + versionChangeEvent = + IDBVersionChangeEvent::Create(mDatabase, + type, + aOldVersion, + aNewVersion.get_uint64_t()); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + if (NS_WARN_IF(!versionChangeEvent)) { + return false; + } + + bool dummy; + NS_WARN_IF(NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))); + + if (!mDatabase->IsClosed()) { + SendBlocked(); + } + + return true; +} + +bool +BackgroundDatabaseChild::RecvInvalidate() +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (mDatabase) { + mDatabase->Invalidate(); + } + + return true; +} + +/******************************************************************************* + * BackgroundTransactionBase + ******************************************************************************/ + +BackgroundTransactionBase::BackgroundTransactionBase() +: mTransaction(nullptr) +{ + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); +} + +BackgroundTransactionBase::BackgroundTransactionBase( + IDBTransaction* aTransaction) + : mTemporaryStrongTransaction(aTransaction) + , mTransaction(aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); +} + +BackgroundTransactionBase::~BackgroundTransactionBase() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionBase); +} + +#ifdef DEBUG + +void +BackgroundTransactionBase::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundTransactionBase::NoteActorDestroyed() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction); + + if (mTransaction) { + mTransaction->ClearBackgroundActor(); + + // Normally this would be DEBUG-only but NoteActorDestroyed is also called + // from SendDeleteMeInternal. In that case we're going to receive an actual + // ActorDestroy call later and we don't want to touch a dead object. + mTemporaryStrongTransaction = nullptr; + mTransaction = nullptr; + } +} + +void +BackgroundTransactionBase::SetDOMTransaction(IDBTransaction* aTransaction) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongTransaction); + MOZ_ASSERT(!mTransaction); + + mTemporaryStrongTransaction = aTransaction; + mTransaction = aTransaction; +} + +void +BackgroundTransactionBase::NoteComplete() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction); + + mTemporaryStrongTransaction = nullptr; +} + +/******************************************************************************* + * BackgroundTransactionChild + ******************************************************************************/ + +BackgroundTransactionChild::BackgroundTransactionChild( + IDBTransaction* aTransaction) + : BackgroundTransactionBase(aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild); +} + +BackgroundTransactionChild::~BackgroundTransactionChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild); +} + +#ifdef DEBUG + +void +BackgroundTransactionChild::AssertIsOnOwningThread() const +{ + static_cast(Manager())->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundTransactionChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mTransaction) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe()); + } +} + +void +BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + NoteActorDestroyed(); +} + +bool +BackgroundTransactionChild::RecvComplete(const nsresult& aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MaybeCollectGarbageOnIPCMessage(); + + mTransaction->FireCompleteOrAbortEvents(aResult); + + NoteComplete(); + return true; +} + +PBackgroundIDBRequestChild* +BackgroundTransactionChild::AllocPBackgroundIDBRequestChild( + const RequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild( + PBackgroundIDBRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBCursorChild* +BackgroundTransactionChild::AllocPBackgroundIDBCursorChild( + const OpenCursorParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); +} + +bool +BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild( + PBackgroundIDBCursorChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundVersionChangeTransactionChild + ******************************************************************************/ + +BackgroundVersionChangeTransactionChild:: +BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest) + : mOpenDBRequest(aOpenDBRequest) +{ + MOZ_ASSERT(aOpenDBRequest); + aOpenDBRequest->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild); +} + +BackgroundVersionChangeTransactionChild:: +~BackgroundVersionChangeTransactionChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild); +} + +#ifdef DEBUG + +void +BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const +{ + static_cast(Manager())->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +BackgroundVersionChangeTransactionChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mTransaction) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBVersionChangeTransactionChild:: + SendDeleteMe()); + } +} + +void +BackgroundVersionChangeTransactionChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + mOpenDBRequest = nullptr; + + NoteActorDestroyed(); +} + +bool +BackgroundVersionChangeTransactionChild::RecvComplete(const nsresult& aResult) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + if (!mTransaction) { + return true; + } + + MOZ_ASSERT(mOpenDBRequest); + + IDBDatabase* database = mTransaction->Database(); + MOZ_ASSERT(database); + + database->ExitSetVersionTransaction(); + + if (NS_FAILED(aResult)) { + database->Close(); + } + + mTransaction->FireCompleteOrAbortEvents(aResult); + + mOpenDBRequest->SetTransaction(nullptr); + mOpenDBRequest = nullptr; + + NoteComplete(); + return true; +} + +PBackgroundIDBRequestChild* +BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild( + const RequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild( + PBackgroundIDBRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +PBackgroundIDBCursorChild* +BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild( + const OpenCursorParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); +} + +bool +BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild( + PBackgroundIDBCursorChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast(aActor); + return true; +} + +/******************************************************************************* + * BackgroundRequestChild + ******************************************************************************/ + +BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest) + : BackgroundRequestChildBase(aRequest) + , mTransaction(aRequest->GetTransaction()) +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild); + + mTransaction->OnNewRequest(); +} + +BackgroundRequestChild::~BackgroundRequestChild() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(!IsActorDestroyed(), mTransaction); + + MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild); + + MaybeFinishTransactionEarly(); +} + +void +BackgroundRequestChild::HoldFileInfosUntilComplete( + nsTArray>& aFileInfos) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileInfos.IsEmpty()); + + mFileInfos.SwapElements(aFileInfos); +} + +void +BackgroundRequestChild::MaybeFinishTransactionEarly() +{ + AssertIsOnOwningThread(); + + if (mTransaction) { + mTransaction->AssertIsOnOwningThread(); + + mTransaction->OnRequestFinished(); + mTransaction = nullptr; + } +} + +bool +BackgroundRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT(mTransaction); + + DispatchErrorEvent(mRequest, aResponse, mTransaction); + return true; +} + +bool +BackgroundRequestChild::HandleResponse(const Key& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); + return true; +} + +bool +BackgroundRequestChild::HandleResponse(const nsTArray& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); + return true; +} + +bool +BackgroundRequestChild::HandleResponse( + const SerializedStructuredCloneReadInfo& aResponse) +{ + AssertIsOnOwningThread(); + + // XXX Fix this somehow... + auto& serializedCloneInfo = + const_cast(aResponse); + + StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo)); + cloneReadInfo.mDatabase = mTransaction->Database(); + + ConvertActorsToBlobs(mTransaction->Database(), + aResponse, + cloneReadInfo.mFiles); + + ResultHelper helper(mRequest, mTransaction, &cloneReadInfo); + + DispatchSuccessEvent(&helper); + return true; +} + +bool +BackgroundRequestChild::HandleResponse( + const nsTArray& aResponse) +{ + AssertIsOnOwningThread(); + + nsTArray cloneReadInfos; + + if (!aResponse.IsEmpty()) { + const uint32_t count = aResponse.Length(); + + cloneReadInfos.SetCapacity(count); + + IDBDatabase* database = mTransaction->Database(); + + for (uint32_t index = 0; index < count; index++) { + // XXX Fix this somehow... + auto& serializedCloneInfo = + const_cast(aResponse[index]); + + StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement(); + + *cloneReadInfo = Move(serializedCloneInfo); + + cloneReadInfo->mDatabase = mTransaction->Database(); + + ConvertActorsToBlobs(database, + serializedCloneInfo, + cloneReadInfo->mFiles); + } + } + + ResultHelper helper(mRequest, mTransaction, &cloneReadInfos); + + DispatchSuccessEvent(&helper); + return true; +} + +bool +BackgroundRequestChild::HandleResponse(JS::Handle aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mRequest, mTransaction, &aResponse); + + DispatchSuccessEvent(&helper); + return true; +} + +bool +BackgroundRequestChild::HandleResponse(uint64_t aResponse) +{ + AssertIsOnOwningThread(); + + JS::Value response(JS::NumberValue(aResponse)); + + ResultHelper helper(mRequest, mTransaction, &response); + + DispatchSuccessEvent(&helper); + return true; +} + +void +BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MaybeCollectGarbageOnIPCMessage(); + + MaybeFinishTransactionEarly(); + + NoteActorDestroyed(); +} + +bool +BackgroundRequestChild::Recv__delete__(const RequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + + MaybeCollectGarbageOnIPCMessage(); + + // Always fire an "error" event with ABORT_ERR if the transaction was aborted, + // even if the request succeeded or failed with another error. + if (mTransaction->IsAborted()) { + return HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); + } + + switch (aResponse.type()) { + case RequestResponse::Tnsresult: + return HandleResponse(aResponse.get_nsresult()); + + case RequestResponse::TObjectStoreAddResponse: + return HandleResponse(aResponse.get_ObjectStoreAddResponse().key()); + + case RequestResponse::TObjectStorePutResponse: + return HandleResponse(aResponse.get_ObjectStorePutResponse().key()); + + case RequestResponse::TObjectStoreGetResponse: + return HandleResponse(aResponse.get_ObjectStoreGetResponse().cloneInfo()); + + case RequestResponse::TObjectStoreGetAllResponse: + return HandleResponse(aResponse.get_ObjectStoreGetAllResponse() + .cloneInfos()); + + case RequestResponse::TObjectStoreGetAllKeysResponse: + return HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse() + .keys()); + + case RequestResponse::TObjectStoreDeleteResponse: + return HandleResponse(JS::UndefinedHandleValue); + + case RequestResponse::TObjectStoreClearResponse: + return HandleResponse(JS::UndefinedHandleValue); + + case RequestResponse::TObjectStoreCountResponse: + return HandleResponse(aResponse.get_ObjectStoreCountResponse().count()); + + case RequestResponse::TIndexGetResponse: + return HandleResponse(aResponse.get_IndexGetResponse().cloneInfo()); + + case RequestResponse::TIndexGetKeyResponse: + return HandleResponse(aResponse.get_IndexGetKeyResponse().key()); + + case RequestResponse::TIndexGetAllResponse: + return HandleResponse(aResponse.get_IndexGetAllResponse().cloneInfos()); + + case RequestResponse::TIndexGetAllKeysResponse: + return HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys()); + + case RequestResponse::TIndexCountResponse: + return HandleResponse(aResponse.get_IndexCountResponse().count()); + + default: + MOZ_CRASH("Unknown response type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +/******************************************************************************* + * BackgroundCursorChild + ******************************************************************************/ + +class BackgroundCursorChild::DelayedDeleteRunnable MOZ_FINAL + : public nsIRunnable +{ + BackgroundCursorChild* mActor; + nsRefPtr mRequest; + +public: + DelayedDeleteRunnable(BackgroundCursorChild* aActor) + : mActor(aActor) + , mRequest(aActor->mRequest) + { + MOZ_ASSERT(aActor); + aActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + } + + // Does not need to be threadsafe since this only runs on one thread. + NS_DECL_ISUPPORTS + +private: + ~DelayedDeleteRunnable() + { } + + NS_DECL_NSIRUNNABLE +}; + +BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + Direction aDirection) + : mRequest(aRequest) + , mTransaction(aRequest->GetTransaction()) + , mObjectStore(aObjectStore) + , mIndex(nullptr) + , mCursor(nullptr) + , mStrongRequest(aRequest) + , mDirection(aDirection) +{ + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); + +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); + MOZ_ASSERT(mOwningThread); +#endif +} + +BackgroundCursorChild::BackgroundCursorChild(IDBRequest* aRequest, + IDBIndex* aIndex, + Direction aDirection) + : mRequest(aRequest) + , mTransaction(aRequest->GetTransaction()) + , mObjectStore(nullptr) + , mIndex(aIndex) + , mCursor(nullptr) + , mStrongRequest(aRequest) + , mDirection(aDirection) +{ + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild); + +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); + MOZ_ASSERT(mOwningThread); +#endif +} + +BackgroundCursorChild::~BackgroundCursorChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild); +} + +#ifdef DEBUG + +void +BackgroundCursorChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread == PR_GetCurrentThread()); +} + +#endif // DEBUG + +void +BackgroundCursorChild::SendContinueInternal(const CursorRequestParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // Make sure all our DOM objects stay alive. + mStrongRequest = mRequest; + mStrongCursor = mCursor; + + MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); + mRequest->Reset(); + + mTransaction->OnNewRequest(); + + MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(aParams)); +} + +void +BackgroundCursorChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + mRequest = nullptr; + mTransaction = nullptr; + mObjectStore = nullptr; + mIndex = nullptr; + + if (mCursor) { + mCursor->ClearBackgroundActor(); + mCursor = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe()); + } +} + +void +BackgroundCursorChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + DispatchErrorEvent(mRequest, aResponse, mTransaction); +} + +void +BackgroundCursorChild::HandleResponse(const void_t& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + if (mCursor) { + mCursor->Reset(); + } + + ResultHelper helper(mRequest, mTransaction, &JS::NullHandleValue); + DispatchSuccessEvent(&helper); + + if (!mCursor) { + nsCOMPtr deleteRunnable = new DelayedDeleteRunnable(this); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable))); + } +} + +void +BackgroundCursorChild::HandleResponse( + const ObjectStoreCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); + + ConvertActorsToBlobs(mTransaction->Database(), + response.cloneInfo(), + cloneReadInfo.mFiles); + + nsRefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), Move(cloneReadInfo)); + } else { + newCursor = IDBCursor::Create(mObjectStore, + this, + mDirection, + Move(response.key()), + Move(cloneReadInfo)); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse( + const ObjectStoreKeyCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mObjectStore); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + nsRefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key())); + } else { + newCursor = IDBCursor::Create(mObjectStore, + this, + mDirection, + Move(response.key())); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse(const IndexCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mIndex); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + StructuredCloneReadInfo cloneReadInfo(Move(response.cloneInfo())); + + ConvertActorsToBlobs(mTransaction->Database(), + aResponse.cloneInfo(), + cloneReadInfo.mFiles); + + nsRefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), + Move(response.objectKey()), + Move(cloneReadInfo)); + } else { + newCursor = IDBCursor::Create(mIndex, + this, + mDirection, + Move(response.key()), + Move(response.objectKey()), + Move(cloneReadInfo)); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::HandleResponse(const IndexKeyCursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mIndex); + MOZ_ASSERT(!mStrongRequest); + MOZ_ASSERT(!mStrongCursor); + + // XXX Fix this somehow... + auto& response = const_cast(aResponse); + + nsRefPtr newCursor; + + if (mCursor) { + mCursor->Reset(Move(response.key()), Move(response.objectKey())); + } else { + newCursor = IDBCursor::Create(mIndex, + this, + mDirection, + Move(response.key()), + Move(response.objectKey())); + mCursor = newCursor; + } + + ResultHelper helper(mRequest, mTransaction, mCursor); + DispatchSuccessEvent(&helper); +} + +void +BackgroundCursorChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest); + MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor); + + MaybeCollectGarbageOnIPCMessage(); + + if (mStrongRequest && !mStrongCursor && mTransaction) { + mTransaction->OnRequestFinished(); + } + + if (mCursor) { + mCursor->ClearBackgroundActor(); +#ifdef DEBUG + mCursor = nullptr; +#endif + } + +#ifdef DEBUG + mRequest = nullptr; + mTransaction = nullptr; + mObjectStore = nullptr; + mIndex = nullptr; +#endif +} + +bool +BackgroundCursorChild::RecvResponse(const CursorResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); + MOZ_ASSERT(mRequest); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(mStrongRequest); + MOZ_ASSERT_IF(mCursor, mStrongCursor); + + MaybeCollectGarbageOnIPCMessage(); + + nsRefPtr request; + mStrongRequest.swap(request); + + nsRefPtr cursor; + mStrongCursor.swap(cursor); + + switch (aResponse.type()) { + case CursorResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case CursorResponse::Tvoid_t: + HandleResponse(aResponse.get_void_t()); + break; + + case CursorResponse::TObjectStoreCursorResponse: + HandleResponse(aResponse.get_ObjectStoreCursorResponse()); + break; + + case CursorResponse::TObjectStoreKeyCursorResponse: + HandleResponse(aResponse.get_ObjectStoreKeyCursorResponse()); + break; + + case CursorResponse::TIndexCursorResponse: + HandleResponse(aResponse.get_IndexCursorResponse()); + break; + + case CursorResponse::TIndexKeyCursorResponse: + HandleResponse(aResponse.get_IndexKeyCursorResponse()); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + mTransaction->OnRequestFinished(); + + return true; +} + +// XXX This doesn't belong here. However, we're not yet porting MutableFile +// stuff to PBackground so this is necessary for the time being. +void +DispatchMutableFileResult(IDBRequest* aRequest, + nsresult aResultCode, + IDBMutableFile* aMutableFile) +{ + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRequest); + MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile); + + if (NS_SUCCEEDED(aResultCode)) { + ResultHelper helper(aRequest, nullptr, aMutableFile); + DispatchSuccessEvent(&helper); + } else { + DispatchErrorEvent(aRequest, aResultCode); + } +} + +NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedDeleteRunnable, + nsIRunnable) + +NS_IMETHODIMP +BackgroundCursorChild:: +DelayedDeleteRunnable::Run() +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + mActor->SendDeleteMeInternal(); + + mActor = nullptr; + mRequest = nullptr; + + return NS_OK; +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h new file mode 100644 index 000000000000..6114f9d569ae --- /dev/null +++ b/dom/indexedDB/ActorsChild.h @@ -0,0 +1,627 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_actorschild_h__ +#define mozilla_dom_indexeddb_actorschild_h__ + +#include "js/RootingAPI.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" + +class nsIEventTarget; +struct PRThread; + +namespace mozilla { +namespace ipc { + +class BackgroundChildImpl; + +} // namespace ipc + +namespace dom { +namespace indexedDB { + +class FileInfo; +class IDBCursor; +class IDBDatabase; +class IDBFactory; +class IDBMutableFile; +class IDBOpenDBRequest; +class IDBRequest; +class IDBTransaction; +class Key; +class PBackgroundIDBFileChild; +class PermissionRequestChild; +class PermissionRequestParent; +class SerializedStructuredCloneReadInfo; + +class BackgroundFactoryChild MOZ_FINAL + : public PBackgroundIDBFactoryChild +{ + friend class mozilla::ipc::BackgroundChildImpl; + friend class IDBFactory; + + IDBFactory* mFactory; + +#ifdef DEBUG + nsCOMPtr mOwningThread; +#endif + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + IDBFactory* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mFactory; + } + +private: + // Only created by IDBFactory. + BackgroundFactoryChild(IDBFactory* aFactory); + + // Only destroyed by mozilla::ipc::BackgroundChildImpl. + ~BackgroundFactoryChild(); + + void + SendDeleteMeInternal(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual PBackgroundIDBFactoryRequestChild* + AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBFactoryRequestChild( + PBackgroundIDBFactoryRequestChild* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBDatabaseChild* + AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestChild* aRequest) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor) + MOZ_OVERRIDE; + + bool + SendDeleteMe() MOZ_DELETE; +}; + +class BackgroundDatabaseChild; + +class BackgroundRequestChildBase +{ +protected: + nsRefPtr mRequest; + +private: + bool mActorDestroyed; + +public: + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + IDBRequest* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mRequest; + } + + bool + IsActorDestroyed() const + { + AssertIsOnOwningThread(); + return mActorDestroyed; + } + +protected: + BackgroundRequestChildBase(IDBRequest* aRequest); + + virtual + ~BackgroundRequestChildBase(); + + void + NoteActorDestroyed(); +}; + +class BackgroundFactoryRequestChild MOZ_FINAL + : public BackgroundRequestChildBase + , public PBackgroundIDBFactoryRequestChild +{ + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + friend class IDBFactory; + friend class BackgroundFactoryChild; + friend class BackgroundDatabaseChild; + friend class PermissionRequestChild; + friend class PermissionRequestParent; + + nsRefPtr mFactory; + const uint64_t mRequestedVersion; + const PersistenceType mPersistenceType; + const bool mIsDeleteOp; + +public: + IDBOpenDBRequest* + GetOpenDBRequest() const; + +private: + // Only created by IDBFactory. + BackgroundFactoryRequestChild(IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + bool aIsDeleteOp, + uint64_t aRequestedVersion, + PersistenceType aPersistenceType); + + // Only destroyed by BackgroundFactoryChild. + ~BackgroundFactoryRequestChild(); + + bool + HandleResponse(nsresult aResponse); + + bool + HandleResponse(const OpenDatabaseRequestResponse& aResponse); + + bool + HandleResponse(const DeleteDatabaseRequestResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + Recv__delete__(const FactoryRequestResponse& aResponse) MOZ_OVERRIDE; + + virtual bool + RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE; + + virtual bool + RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE; +}; + +class BackgroundDatabaseChild MOZ_FINAL + : public PBackgroundIDBDatabaseChild +{ + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + friend class BackgroundFactoryChild; + friend class BackgroundFactoryRequestChild; + friend class IDBDatabase; + + nsAutoPtr mSpec; + nsRefPtr mTemporaryStrongDatabase; + BackgroundFactoryRequestChild* mOpenRequestActor; + IDBDatabase* mDatabase; + + PersistenceType mPersistenceType; + +public: + void + AssertIsOnOwningThread() const + { + static_cast(Manager())->AssertIsOnOwningThread(); + } + + const DatabaseSpec* + Spec() const + { + AssertIsOnOwningThread(); + return mSpec; + } + + IDBDatabase* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mDatabase; + } + +private: + // Only constructed by BackgroundFactoryChild. + BackgroundDatabaseChild(const DatabaseSpec& aSpec, + BackgroundFactoryRequestChild* aOpenRequest); + + // Only destroyed by BackgroundFactoryChild. + ~BackgroundDatabaseChild(); + + void + SendDeleteMeInternal(); + + void + EnsureDOMObject(); + + void + ReleaseDOMObject(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual PBackgroundIDBDatabaseFileChild* + AllocPBackgroundIDBDatabaseFileChild( + const BlobOrInputStream& aBlobOrInputStream) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBDatabaseFileChild( + PBackgroundIDBDatabaseFileChild* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBTransactionChild* + AllocPBackgroundIDBTransactionChild( + const nsTArray& aObjectStoreNames, + const Mode& aMode) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBVersionChangeTransactionChild* + AllocPBackgroundIDBVersionChangeTransactionChild( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) + MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBVersionChangeTransactionConstructor( + PBackgroundIDBVersionChangeTransactionChild* aActor, + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBVersionChangeTransactionChild( + PBackgroundIDBVersionChangeTransactionChild* aActor) + MOZ_OVERRIDE; + + virtual bool + RecvVersionChange(const uint64_t& aOldVersion, + const NullableVersion& aNewVersion) + MOZ_OVERRIDE; + + virtual bool + RecvInvalidate() MOZ_OVERRIDE; + + bool + SendDeleteMe() MOZ_DELETE; +}; + +class BackgroundVersionChangeTransactionChild; + +class BackgroundTransactionBase +{ + friend class BackgroundVersionChangeTransactionChild; + + // mTemporaryStrongTransaction is strong and is only valid until the end of + // NoteComplete() member function or until the NoteActorDestroyed() member + // function is called. + nsRefPtr mTemporaryStrongTransaction; + +protected: + // mTransaction is weak and is valid until the NoteActorDestroyed() member + // function is called. + IDBTransaction* mTransaction; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const = 0; +#else + void + AssertIsOnOwningThread() const + { } +#endif + + IDBTransaction* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mTransaction; + } + +protected: + BackgroundTransactionBase(); + BackgroundTransactionBase(IDBTransaction* aTransaction); + + virtual + ~BackgroundTransactionBase(); + + void + NoteActorDestroyed(); + + void + NoteComplete(); + +private: + // Only called by BackgroundVersionChangeTransactionChild. + void + SetDOMTransaction(IDBTransaction* aDOMObject); +}; + +class BackgroundTransactionChild MOZ_FINAL + : public BackgroundTransactionBase + , public PBackgroundIDBTransactionChild +{ + friend class BackgroundDatabaseChild; + friend class IDBDatabase; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const MOZ_OVERRIDE; +#endif + + void + SendDeleteMeInternal(); + +private: + // Only created by IDBDatabase. + BackgroundTransactionChild(IDBTransaction* aTransaction); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundTransactionChild(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + bool + RecvComplete(const nsresult& aResult) MOZ_OVERRIDE; + + virtual PBackgroundIDBRequestChild* + AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBCursorChild* + AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) + MOZ_OVERRIDE; + + bool + SendDeleteMe() MOZ_DELETE; +}; + +class BackgroundVersionChangeTransactionChild MOZ_FINAL + : public BackgroundTransactionBase + , public PBackgroundIDBVersionChangeTransactionChild +{ + friend class BackgroundDatabaseChild; + + IDBOpenDBRequest* mOpenDBRequest; + +public: +#ifdef DEBUG + virtual void + AssertIsOnOwningThread() const MOZ_OVERRIDE; +#endif + + void + SendDeleteMeInternal(); + +private: + // Only created by BackgroundDatabaseChild. + BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundVersionChangeTransactionChild(); + + // Only called by BackgroundDatabaseChild. + void + SetDOMTransaction(IDBTransaction* aDOMObject) + { + BackgroundTransactionBase::SetDOMTransaction(aDOMObject); + } + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + bool + RecvComplete(const nsresult& aResult) MOZ_OVERRIDE; + + virtual PBackgroundIDBRequestChild* + AllocPBackgroundIDBRequestChild(const RequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBCursorChild* + AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) + MOZ_OVERRIDE; + + bool + SendDeleteMe() MOZ_DELETE; +}; + +class BackgroundRequestChild MOZ_FINAL + : public BackgroundRequestChildBase + , public PBackgroundIDBRequestChild +{ + friend class BackgroundTransactionChild; + friend class BackgroundVersionChangeTransactionChild; + + nsRefPtr mTransaction; + nsTArray> mFileInfos; + +public: + BackgroundRequestChild(IDBRequest* aRequest); + + void + HoldFileInfosUntilComplete(nsTArray>& aFileInfos); + +private: + // Only destroyed by BackgroundTransactionChild or + // BackgroundVersionChangeTransactionChild. + ~BackgroundRequestChild(); + + void + MaybeFinishTransactionEarly(); + + bool + HandleResponse(nsresult aResponse); + + bool + HandleResponse(const Key& aResponse); + + bool + HandleResponse(const nsTArray& aResponse); + + bool + HandleResponse(const SerializedStructuredCloneReadInfo& aResponse); + + bool + HandleResponse(const nsTArray& aResponse); + + bool + HandleResponse(JS::Handle aResponse); + + bool + HandleResponse(uint64_t aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + Recv__delete__(const RequestResponse& aResponse) MOZ_OVERRIDE; +}; + +class BackgroundCursorChild MOZ_FINAL + : public PBackgroundIDBCursorChild +{ + friend class BackgroundTransactionChild; + friend class BackgroundVersionChangeTransactionChild; + + class DelayedDeleteRunnable; + + IDBRequest* mRequest; + IDBTransaction* mTransaction; + IDBObjectStore* mObjectStore; + IDBIndex* mIndex; + IDBCursor* mCursor; + + // These are only set while a request is in progress. + nsRefPtr mStrongRequest; + nsRefPtr mStrongCursor; + + Direction mDirection; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + +public: + BackgroundCursorChild(IDBRequest* aRequest, + IDBObjectStore* aObjectStore, + Direction aDirection); + + BackgroundCursorChild(IDBRequest* aRequest, + IDBIndex* aIndex, + Direction aDirection); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + SendContinueInternal(const CursorRequestParams& aParams); + + void + SendDeleteMeInternal(); + +private: + // Only destroyed by BackgroundTransactionChild or + // BackgroundVersionChangeTransactionChild. + ~BackgroundCursorChild(); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(const void_t& aResponse); + + void + HandleResponse(const ObjectStoreCursorResponse& aResponse); + + void + HandleResponse(const ObjectStoreKeyCursorResponse& aResponse); + + void + HandleResponse(const IndexCursorResponse& aResponse); + + void + HandleResponse(const IndexKeyCursorResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvResponse(const CursorResponse& aResponse) MOZ_OVERRIDE; + + // Force callers to use SendContinueInternal. + bool + SendContinue(const CursorRequestParams& aParams) MOZ_DELETE; + + bool + SendDeleteMe() MOZ_DELETE; +}; + +// XXX This doesn't belong here. However, we're not yet porting MutableFile +// stuff to PBackground so this is necessary for the time being. +void +DispatchMutableFileResult(IDBRequest* aRequest, + nsresult aResultCode, + IDBMutableFile* aMutableFile); + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_indexeddb_actorschild_h__ diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp new file mode 100644 index 000000000000..bad8ec36d0b9 --- /dev/null +++ b/dom/indexedDB/ActorsParent.cpp @@ -0,0 +1,17020 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ActorsParent.h" + +#include +#include "FileInfo.h" +#include "FileManager.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "IndexedDatabaseManager.h" +#include "js/StructuredClone.h" +#include "js/Value.h" +#include "jsapi.h" +#include "KeyPath.h" +#include "mozilla/Attributes.h" +#include "mozilla/AppProcessChecker.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/Endian.h" +#include "mozilla/LazyIdleThread.h" +#include "mozilla/Maybe.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/storage.h" +#include "mozilla/unused.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/FileStreams.h" +#include "mozilla/dom/quota/OriginOrPatternString.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/dom/quota/StoragePrivilege.h" +#include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/InputStreamParams.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/PBackground.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsClassHashtable.h" +#include "nsCOMPtr.h" +#include "nsDataHashtable.h" +#include "nsDOMFile.h" +#include "nsEscape.h" +#include "nsHashKeys.h" +#include "nsNetUtil.h" +#include "nsIAppsService.h" +#include "nsIDOMFile.h" +#include "nsIEventTarget.h" +#include "nsIFile.h" +#include "nsIFileURL.h" +#include "nsIInputStream.h" +#include "nsIInterfaceRequestor.h" +#include "nsInterfaceHashtable.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsIOfflineStorage.h" +#include "nsIOutputStream.h" +#include "nsIPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsISupports.h" +#include "nsISupportsImpl.h" +#include "nsISupportsPriority.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "nsRefPtrHashtable.h" +#include "nsString.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCID.h" +#include "PermissionRequestBase.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" +#include "snappy/snappy.h" +#include "TransactionThreadPool.h" + +namespace mozilla { +namespace dom { +namespace indexedDB { + +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +namespace { + +class Cursor; +class Database; +struct DatabaseActorInfo; +class DatabaseFile; +class DatabaseOfflineStorage; +class Factory; +class OpenDatabaseOp; +class TransactionBase; +class VersionChangeTransaction; + +/******************************************************************************* + * Constants + ******************************************************************************/ + +// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major +// schema version. +static_assert(JS_STRUCTURED_CLONE_VERSION == 4, + "Need to update the major schema version."); + +// Major schema version. Bump for almost everything. +const uint32_t kMajorSchemaVersion = 16; + +// Minor schema version. Should almost always be 0 (maybe bump on release +// branches if we have to). +const uint32_t kMinorSchemaVersion = 0; + +// The schema version we store in the SQLite database is a (signed) 32-bit +// integer. The major version is left-shifted 4 bits so the max value is +// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF. +static_assert(kMajorSchemaVersion <= 0xFFFFFFF, + "Major version needs to fit in 28 bits."); +static_assert(kMinorSchemaVersion <= 0xF, + "Minor version needs to fit in 4 bits."); + +const int32_t kSQLiteSchemaVersion = + int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion); + +const int32_t kStorageProgressGranularity = 1000; + +const char kSavepointClause[] = "SAVEPOINT sp;"; + +const fallible_t fallible = fallible_t(); + +const uint32_t kFileCopyBufferSize = 32768; + +const char kJournalDirectoryName[] = "journals"; + +const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled"; + +#define IDB_PREFIX "indexedDB" +#define CHROME_PREFIX "chrome" + +const char kPermissionString[] = IDB_PREFIX; + +const char kChromeOrigin[] = CHROME_PREFIX; + +const char kPermissionStringChromeBase[] = IDB_PREFIX "-" CHROME_PREFIX "-"; +const char kPermissionStringChromeReadSuffix[] = "-read"; +const char kPermissionStringChromeWriteSuffix[] = "-write"; + +#undef CHROME_PREFIX +#undef IDB_PREFIX + +enum AppId { + kNoAppId = nsIScriptSecurityManager::NO_APP_ID, + kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID +}; + +#ifdef DEBUG + +const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; +const uint32_t kDEBUGThreadSleepMS = 0; + +#endif + +/******************************************************************************* + * Metadata classes + ******************************************************************************/ + +struct FullIndexMetadata +{ + IndexMetadata mCommonMetadata; + + bool mDeleted; + +public: + FullIndexMetadata() + : mCommonMetadata(0, nsString(), KeyPath(0), false, false) + , mDeleted(false) + { + // This can happen either on the QuotaManager IO thread or on a + // versionchange transaction thread. These threads can never race so this is + // totally safe. + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata) + +private: + ~FullIndexMetadata() + { } +}; + +typedef nsRefPtrHashtable IndexTable; + +struct FullObjectStoreMetadata +{ + ObjectStoreMetadata mCommonMetadata; + IndexTable mIndexes; + + // These two members are only ever touched on a transaction thread! + int64_t mNextAutoIncrementId; + int64_t mComittedAutoIncrementId; + + bool mDeleted; + +public: + FullObjectStoreMetadata() + : mCommonMetadata(0, nsString(), KeyPath(0), false) + , mNextAutoIncrementId(0) + , mComittedAutoIncrementId(0) + , mDeleted(false) + { + // This can happen either on the QuotaManager IO thread or on a + // versionchange transaction thread. These threads can never race so this is + // totally safe. + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata); + +private: + ~FullObjectStoreMetadata() + { } +}; + +typedef nsRefPtrHashtable + ObjectStoreTable; + +struct FullDatabaseMetadata +{ + DatabaseMetadata mCommonMetadata; + nsCString mDatabaseId; + nsString mFilePath; + ObjectStoreTable mObjectStores; + + int64_t mNextObjectStoreId; + int64_t mNextIndexId; + +public: + FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata) + : mCommonMetadata(aCommonMetadata) + , mNextObjectStoreId(0) + , mNextIndexId(0) + { + AssertIsOnBackgroundThread(); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata) + + already_AddRefed + Duplicate() const; + +private: + ~FullDatabaseMetadata() + { } +}; + +template +class MOZ_STACK_CLASS MetadataNameOrIdMatcher MOZ_FINAL +{ + typedef MetadataNameOrIdMatcher SelfType; + + const int64_t mId; + const nsString mName; + nsRefPtr mMetadata; + bool mCheckName; + +public: + template + static MetadataType* + Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + + SelfType closure(aId, aName); + aEnumerable.EnumerateRead(Enumerate, &closure); + + return closure.mMetadata; + } + + template + static MetadataType* + Match(const Enumerable& aEnumerable, uint64_t aId) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + + SelfType closure(aId); + aEnumerable.EnumerateRead(Enumerate, &closure); + + return closure.mMetadata; + } + +private: + MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName) + : mId(aId) + , mName(PromiseFlatString(aName)) + , mMetadata(nullptr) + , mCheckName(true) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + } + + MetadataNameOrIdMatcher(const int64_t& aId) + : mId(aId) + , mMetadata(nullptr) + , mCheckName(false) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aId); + } + + static PLDHashOperator + Enumerate(const uint64_t& aKey, MetadataType* aValue, void* aClosure) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* closure = static_cast(aClosure); + + if (!aValue->mDeleted && + (closure->mId == aValue->mCommonMetadata.id() || + (closure->mCheckName && + closure->mName == aValue->mCommonMetadata.name()))) { + closure->mMetadata = aValue; + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } +}; + +/******************************************************************************* + * SQLite functions + ******************************************************************************/ + +int32_t +MakeSchemaVersion(uint32_t aMajorSchemaVersion, + uint32_t aMinorSchemaVersion) +{ + return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion); +} + +uint32_t +HashName(const nsAString& aName) +{ + struct Helper + { + static uint32_t + RotateBitsLeft32(uint32_t aValue, uint8_t aBits) + { + MOZ_ASSERT(aBits < 32); + return (aValue << aBits) | (aValue >> (32 - aBits)); + } + }; + + static const uint32_t kGoldenRatioU32 = 0x9e3779b9u; + + const char16_t* str = aName.BeginReading(); + size_t length = aName.Length(); + + uint32_t hash = 0; + for (size_t i = 0; i < length; i++) { + hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]); + } + + return hash; +} + +nsresult +ClampResultCode(nsresult aResultCode) +{ + if (NS_SUCCEEDED(aResultCode) || + NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) { + return aResultCode; + } + + switch (aResultCode) { + case NS_ERROR_FILE_NO_DEVICE_SPACE: + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + case NS_ERROR_STORAGE_CONSTRAINT: + return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; + default: +#ifdef DEBUG + nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to " + "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR", + aResultCode); + NS_WARNING(message.get()); +#else + ; +#endif + } + + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; +} + +void +GetDatabaseFilename(const nsAString& aName, + nsAutoString& aDatabaseFilename) +{ + MOZ_ASSERT(aDatabaseFilename.IsEmpty()); + + aDatabaseFilename.AppendInt(HashName(aName)); + + nsCString escapedName; + if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { + MOZ_CRASH("Can't escape database name!"); + } + + const char* forwardIter = escapedName.BeginReading(); + const char* backwardIter = escapedName.EndReading() - 1; + + nsAutoCString substring; + while (forwardIter <= backwardIter && substring.Length() < 21) { + if (substring.Length() % 2) { + substring.Append(*backwardIter--); + } else { + substring.Append(*forwardIter++); + } + } + + aDatabaseFilename.AppendASCII(substring.get(), substring.Length()); +} + +nsresult +CreateFileTables(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "CreateFileTables", + js::ProfileEntry::Category::STORAGE); + + // Table `file` + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE file (" + "id INTEGER PRIMARY KEY, " + "refcount INTEGER NOT NULL" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER file_update_trigger " + "AFTER UPDATE ON file " + "FOR EACH ROW WHEN NEW.refcount = 0 " + "BEGIN " + "DELETE FROM file WHERE id = OLD.id; " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +CreateTables(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "CreateTables", + js::ProfileEntry::Category::STORAGE); + + // Table `database` + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `object_store` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER PRIMARY KEY, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "name TEXT NOT NULL, " + "key_path TEXT, " + "UNIQUE (name)" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `object_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value BLOB DEFAULT NULL, " + "file_ids TEXT, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `index` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `index_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Need this to make cascading deletes from object_data and object_store fast. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Table `unique_index_data` + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Need this to make cascading deletes from object_data and object_store fast. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CreateFileTables(aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom4To5", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + // All we changed is the type of the version column, so lets try to + // convert that to an integer, and if we fail, set it to 0. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, version, dataVersion " + "FROM database" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsString name; + int32_t intVersion; + int64_t dataVersion; + + { + mozStorageStatementScoper scoper(stmt); + + bool hasResults; + rv = stmt->ExecuteStep(&hasResults); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!hasResults)) { + return NS_ERROR_FAILURE; + } + + nsString version; + rv = stmt->GetString(1, version); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + intVersion = version.ToInteger(&rv); + if (NS_FAILED(rv)) { + intVersion = 0; + } + + rv = stmt->GetString(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->GetInt64(2, &dataVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE database" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE database (" + "name TEXT NOT NULL, " + "version INTEGER NOT NULL DEFAULT 0, " + "dataVersion INTEGER NOT NULL" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name, version, dataVersion) " + "VALUES (:name, :version, :dataVersion)" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + { + mozStorageStatementScoper scoper(stmt); + + rv = stmt->BindStringParameter(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt32Parameter(1, intVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64Parameter(2, dataVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aConnection->SetSchemaVersion(5); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom5To6", + js::ProfileEntry::Category::STORAGE); + + // First, drop all the indexes we're no longer going to use. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX key_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_key_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX value_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP INDEX ai_value_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now, reorder the columns of object_data to put the blob data last. We do + // this by copying into a temporary table, dropping the original, then copying + // back into a newly created table. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, key_value, data " + "FROM object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value DEFAULT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // We need to add a unique constraint to our ai_object_data table. Copy all + // the data out of it using a temporary table as before. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "data " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, data " + "FROM ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_object_data (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "object_store_id INTEGER NOT NULL, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, id), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_object_data " + "SELECT id, object_store_id, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the unique_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "object_data_key NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the ai_index_data table. We're reordering the columns as well as + // changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT OR IGNORE INTO ai_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_index_data_ai_object_data_id_index " + "ON ai_index_data (ai_object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Fix up the ai_unique_index_data table. We're reordering the columns as well + // as changing the primary key from being a simple id to being a composite. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "ai_object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, value, ai_object_data_id " + "FROM ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE ai_unique_index_data (" + "index_id INTEGER NOT NULL, " + "value NOT NULL, " + "ai_object_data_id INTEGER NOT NULL, " + "UNIQUE (index_id, value), " + "PRIMARY KEY (index_id, value, ai_object_data_id), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO ai_unique_index_data " + "SELECT index_id, value, ai_object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " + "ON ai_unique_index_data (ai_object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(6); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom6To7", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "name, " + "key_path, " + "auto_increment" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, name, key_path, auto_increment " + "FROM object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store (" + "id INTEGER PRIMARY KEY, " + "auto_increment INTEGER NOT NULL DEFAULT 0, " + "name TEXT NOT NULL, " + "key_path TEXT, " + "UNIQUE (name)" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store " + "SELECT id, auto_increment, name, nullif(key_path, '') " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(7); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom7To8", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "object_store_autoincrement" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, object_store_autoincrement " + "FROM object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "object_store_autoincrement INTERGER NOT NULL, " + "PRIMARY KEY (id), " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, 0, object_store_autoincrement " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(8); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +class CompressDataBlobsFunction MOZ_FINAL + : public mozIStorageFunction +{ +public: + NS_DECL_ISUPPORTS + +private: + ~CompressDataBlobsFunction() + { } + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) MOZ_OVERRIDE + { + MOZ_ASSERT(aArguments); + MOZ_ASSERT(aResult); + + PROFILER_LABEL("IndexedDB", + "CompressDataBlobsFunction::OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const uint8_t* uncompressed; + uint32_t uncompressedLength; + rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + nsAutoArrayPtr compressed(new (fallible) char[compressedLength]); + if (NS_WARN_IF(!compressed)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + snappy::RawCompress(reinterpret_cast(uncompressed), + uncompressedLength, compressed.get(), + &compressedLength); + + std::pair data(static_cast(compressed.get()), + int(compressedLength)); + + // XXX This copies the buffer again... There doesn't appear to be any way to + // preallocate space and write directly to a BlobVariant at the moment. + nsCOMPtr result = new mozilla::storage::BlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +nsresult +UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom8To9_0", + js::ProfileEntry::Category::STORAGE); + + // We no longer use the dataVersion column. + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE database SET dataVersion = 0;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr compressor = new CompressDataBlobsFunction(); + + NS_NAMED_LITERAL_CSTRING(compressorName, "compress"); + + rv = aConnection->CreateFunction(compressorName, 1, compressor); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Turn off foreign key constraints before we do anything here. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_data SET data = compress(data);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE ai_object_data SET data = compress(data);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(compressorName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom9_0To10_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE object_data ADD COLUMN file_ids TEXT;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CreateFileTables(aConnection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom10_0To11_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id, " + "object_store_id, " + "name, " + "key_path, " + "unique_index, " + "multientry" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_store_index;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_store_index (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "name TEXT NOT NULL, " + "key_path TEXT NOT NULL, " + "unique_index INTEGER NOT NULL, " + "multientry INTEGER NOT NULL, " + "UNIQUE (object_store_id, name), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_store_index " + "SELECT id, object_store_id, name, key_path, " + "unique_index, multientry " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TRIGGER object_data_insert_trigger;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "SELECT object_store_id, id, data, file_ids " + "FROM ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " + "FROM ai_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_index_data.ai_object_data_id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " + "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " + "FROM ai_unique_index_data " + "INNER JOIN object_store_index ON " + "object_store_index.id = ai_unique_index_data.index_id " + "INNER JOIN object_data ON " + "object_data.object_store_id = object_store_index.object_store_id AND " + "object_data.key_value = ai_unique_index_data.ai_object_data_id;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "UPDATE object_store " + "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " + "WHERE auto_increment;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE ai_object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +class EncodeKeysFunction MOZ_FINAL + : public mozIStorageFunction +{ +public: + NS_DECL_ISUPPORTS + +private: + ~EncodeKeysFunction() + { } + + NS_IMETHOD + OnFunctionCall(mozIStorageValueArray* aArguments, + nsIVariant** aResult) MOZ_OVERRIDE + { + MOZ_ASSERT(aArguments); + MOZ_ASSERT(aResult); + + PROFILER_LABEL("IndexedDB", + "EncodeKeysFunction::OnFunctionCall", + js::ProfileEntry::Category::STORAGE); + + uint32_t argc; + nsresult rv = aArguments->GetNumEntries(&argc); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (argc != 1) { + NS_WARNING("Don't call me with the wrong number of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + int32_t type; + rv = aArguments->GetTypeOfIndex(0, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + Key key; + if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) { + int64_t intKey; + aArguments->GetInt64(0, &intKey); + key.SetFromInteger(intKey); + } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) { + nsString stringKey; + aArguments->GetString(0, stringKey); + key.SetFromString(stringKey); + } else { + NS_WARNING("Don't call me with the wrong type of arguments!"); + return NS_ERROR_UNEXPECTED; + } + + const nsCString& buffer = key.GetBuffer(); + + std::pair data(static_cast(buffer.get()), + int(buffer.Length())); + + nsCOMPtr result = new mozilla::storage::BlobVariant(data); + + result.forget(aResult); + return NS_OK; + } +}; + +nsresult +UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom11_0To12_0", + js::ProfileEntry::Category::STORAGE); + + NS_NAMED_LITERAL_CSTRING(encoderName, "encode"); + + nsCOMPtr encoder = new EncodeKeysFunction(); + + nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "id INTEGER PRIMARY KEY, " + "object_store_id, " + "key_value, " + "data, " + "file_ids " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT id, object_store_id, encode(key_value), data, file_ids " + "FROM object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE object_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE object_data (" + "id INTEGER PRIMARY KEY, " + "object_store_id INTEGER NOT NULL, " + "key_value BLOB DEFAULT NULL, " + "file_ids TEXT, " + "data BLOB NOT NULL, " + "UNIQUE (object_store_id, key_value), " + "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO object_data " + "SELECT id, object_store_id, key_value, file_ids, data " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_insert_trigger " + "AFTER INSERT ON object_data " + "FOR EACH ROW " + "WHEN NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(NULL, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_update_trigger " + "AFTER UPDATE OF file_ids ON object_data " + "FOR EACH ROW " + "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TRIGGER object_data_delete_trigger " + "AFTER DELETE ON object_data " + "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " + "BEGIN " + "SELECT update_refcount(OLD.file_ids, NULL); " + "END;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE, " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX index_data_object_data_id_index " + "ON index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TEMPORARY TABLE temp_upgrade (" + "index_id, " + "value, " + "object_data_key, " + "object_data_id " + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO temp_upgrade " + "SELECT index_id, encode(value), encode(object_data_key), object_data_id " + "FROM unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE unique_index_data;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE TABLE unique_index_data (" + "index_id INTEGER NOT NULL, " + "value BLOB NOT NULL, " + "object_data_key BLOB NOT NULL, " + "object_data_id INTEGER NOT NULL, " + "PRIMARY KEY (index_id, value, object_data_key), " + "UNIQUE (index_id, value), " + "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " + "CASCADE " + "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " + "CASCADE" + ");" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "INSERT INTO unique_index_data " + "SELECT index_id, value, object_data_key, object_data_id " + "FROM temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE temp_upgrade;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE INDEX unique_index_data_object_data_id_index " + "ON unique_index_data (object_data_id);" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->RemoveFunction(encoderName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection, + bool* aVacuumNeeded) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "UpgradeSchemaFrom12_0To13_0", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + int32_t defaultPageSize; + rv = aConnection->GetDefaultPageSize(&defaultPageSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Enable auto_vacuum mode and update the page size to the platform default. + nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = "); + upgradeQuery.AppendInt(defaultPageSize); + + rv = aConnection->ExecuteSimpleSQL(upgradeQuery); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aVacuumNeeded = true; +#endif + + rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + + // The only change between 13 and 14 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection) +{ + // The only change between 14 and 15 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection) +{ + // The only change between 15 and 16 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +GetDatabaseFileURL(nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + nsIFileURL** aResult) +{ + MOZ_ASSERT(aDatabaseFile); + MOZ_ASSERT(aResult); + + nsCOMPtr uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr fileUrl = do_QueryInterface(uri); + MOZ_ASSERT(fileUrl); + + nsAutoCString type; + PersistenceTypeToText(aPersistenceType, type); + + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + + NS_LITERAL_CSTRING("&group=") + aGroup + + NS_LITERAL_CSTRING("&origin=") + aOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + fileUrl.forget(aResult); + return NS_OK; +} + +nsresult +SetDefaultPragmas(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(aConnection); + + static const char query[] = +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + // Switch the journaling mode to TRUNCATE to avoid changing the directory + // structure at the conclusion of every transaction for devices with slower + // file systems. + "PRAGMA journal_mode = TRUNCATE; " +#endif + // We use foreign keys in lots of places. + "PRAGMA foreign_keys = ON; " + // The "INSERT OR REPLACE" statement doesn't fire the update trigger, + // instead it fires only the insert trigger. This confuses the update + // refcount function. This behavior changes with enabled recursive triggers, + // so the statement fires the delete trigger first and then the insert + // trigger. + "PRAGMA recursive_triggers = ON;"; + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +CreateDatabaseConnection(nsIFile* aDBFile, + nsIFile* aFMDirectory, + const nsAString& aName, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + mozIStorageConnection** aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDBFile); + MOZ_ASSERT(aFMDirectory); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "CreateDatabaseConnection", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + bool exists; + + if (IndexedDatabaseManager::InLowDiskSpaceMode()) { + rv = aDBFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!exists) { + NS_WARNING("Refusing to create database because disk space is low!"); + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + } + + nsCOMPtr dbFileUrl; + rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin, + getter_AddRefs(dbFileUrl)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr connection; + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + if (rv == NS_ERROR_FILE_CORRUPTED) { + // If we're just opening the database during origin initialization, then + // we don't want to erase any files. The failure here will fail origin + // initialization too. + if (aName.IsVoid()) { + return rv; + } + + // Nuke the database file. + rv = aDBFile->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aFMDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + bool isDirectory; + rv = aFMDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (NS_WARN_IF(!isDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + rv = aFMDirectory->Remove(true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = SetDefaultPragmas(connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Check to make sure that the database schema is correct. + int32_t schemaVersion; + rv = connection->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Unknown schema will fail origin initialization too. + if (!schemaVersion && aName.IsVoid()) { + IDB_WARNING("Unable to open IndexedDB database, schema is not set!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (schemaVersion > kSQLiteSchemaVersion) { + IDB_WARNING("Unable to open IndexedDB database, schema is too high!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool vacuumNeeded = false; + + if (schemaVersion != kSQLiteSchemaVersion) { +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) + if (!schemaVersion) { + // Have to do this before opening a transaction. + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + // Turn on auto_vacuum mode to reclaim disk space on mobile devices. + "PRAGMA auto_vacuum = FULL; " + )); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } +#endif + + mozStorageTransaction transaction(connection, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + if (!schemaVersion) { + // Brand new file, initialize our tables. + rv = CreateTables(connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion))); + MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion); + + nsCOMPtr stmt; + nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO database (name) " + "VALUES (:name)" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + // This logic needs to change next time we change the schema! + static_assert(kSQLiteSchemaVersion == int32_t((16 << 4) + 0), + "Upgrade function needed due to schema version increase."); + + while (schemaVersion != kSQLiteSchemaVersion) { + if (schemaVersion == 4) { + rv = UpgradeSchemaFrom4To5(connection); + } else if (schemaVersion == 5) { + rv = UpgradeSchemaFrom5To6(connection); + } else if (schemaVersion == 6) { + rv = UpgradeSchemaFrom6To7(connection); + } else if (schemaVersion == 7) { + rv = UpgradeSchemaFrom7To8(connection); + } else if (schemaVersion == 8) { + rv = UpgradeSchemaFrom8To9_0(connection); + vacuumNeeded = true; + } else if (schemaVersion == MakeSchemaVersion(9, 0)) { + rv = UpgradeSchemaFrom9_0To10_0(connection); + } else if (schemaVersion == MakeSchemaVersion(10, 0)) { + rv = UpgradeSchemaFrom10_0To11_0(connection); + } else if (schemaVersion == MakeSchemaVersion(11, 0)) { + rv = UpgradeSchemaFrom11_0To12_0(connection); + } else if (schemaVersion == MakeSchemaVersion(12, 0)) { + rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded); + } else if (schemaVersion == MakeSchemaVersion(13, 0)) { + rv = UpgradeSchemaFrom13_0To14_0(connection); + } else if (schemaVersion == MakeSchemaVersion(14, 0)) { + rv = UpgradeSchemaFrom14_0To15_0(connection); + } else if (schemaVersion == MakeSchemaVersion(15, 0)) { + rv = UpgradeSchemaFrom15_0To16_0(connection); + } else { + IDB_WARNING("Unable to open IndexedDB database, no upgrade path is " + "available!"); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = connection->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion); + } + + rv = transaction.Commit(); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, + // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (vacuumNeeded) { + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + connection.forget(aConnection); + return NS_OK; +} + +already_AddRefed +GetFileForPath(const nsAString& aPath) +{ + MOZ_ASSERT(!aPath.IsEmpty()); + + nsCOMPtr file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + if (NS_WARN_IF(!file)) { + return nullptr; + } + + if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) { + return nullptr; + } + + return file.forget(); +} + +nsresult +GetDatabaseConnection(const nsAString& aDatabaseFilePath, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + mozIStorageConnection** aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(!aDatabaseFilePath.IsEmpty()); + MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite"))); + MOZ_ASSERT(aConnection); + + PROFILER_LABEL("IndexedDB", + "GetDatabaseConnection", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr dbFile = GetFileForPath(aDatabaseFilePath); + if (NS_WARN_IF(!dbFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool exists; + nsresult rv = dbFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!exists)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr dbFileUrl; + rv = GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin, + getter_AddRefs(dbFileUrl)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr connection; + rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = SetDefaultPragmas(connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + connection.forget(aConnection); + return NS_OK; +} + +/******************************************************************************* + * Actor class declarations + ******************************************************************************/ + +class DatabaseOperationBase + : public nsRunnable + , public mozIStorageProgressHandler +{ + // Uniquely tracks each operation for logging purposes. Only modified on the + // PBackground thread. + static uint64_t sNextSerialNumber; + +protected: + class AutoSetProgressHandler; + + typedef nsDataHashtable UniqueIndexTable; + + nsCOMPtr mOwningThread; + const uint64_t mSerialNumber; + nsresult mResultCode; + +private: + Atomic mOperationMayProceed; + bool mActorDestroyed; + +public: + NS_DECL_ISUPPORTS_INHERITED + + void + AssertIsOnOwningThread() const + { + AssertIsOnBackgroundThread(); + +#ifdef DEBUG + MOZ_ASSERT(mOwningThread); + bool current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +#endif + } + + void + NoteActorDestroyed() + { + AssertIsOnOwningThread(); + + mActorDestroyed = true; + mOperationMayProceed = false; + } + + bool + IsActorDestroyed() const + { + AssertIsOnOwningThread(); + + return mActorDestroyed; + } + + // May be called on any thread. + bool + OperationMayProceed() const + { + return mOperationMayProceed; + } + + uint64_t + SerialNumber() const + { + return mSerialNumber; + } + + nsresult + ResultCode() const + { + return mResultCode; + } + + void + SetFailureCode(nsresult aErrorCode) + { + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + + mResultCode = aErrorCode; + } + +protected: + DatabaseOperationBase() + : mOwningThread(NS_GetCurrentThread()) + , mSerialNumber(++sNextSerialNumber) + , mResultCode(NS_OK) + , mOperationMayProceed(true) + , mActorDestroyed(false) + { + AssertIsOnOwningThread(); + } + + virtual + ~DatabaseOperationBase() + { + MOZ_ASSERT(mActorDestroyed); + } + + static void + GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange, + const nsACString& aKeyColumnName, + nsAutoCString& aBindingClause); + + static uint64_t + ReinterpretDoubleAsUInt64(double aDouble); + + static nsresult + GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement, + uint32_t aDataIndex, + uint32_t aFileIdsIndex, + FileManager* aFileManager, + StructuredCloneReadInfo* aInfo); + + static nsresult + BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange, + mozIStorageStatement* aStatement); + + static void + AppendConditionClause(const nsACString& aColumnName, + const nsACString& aArgName, + bool aLessThan, + bool aEquals, + nsAutoCString& aResult); + + static nsresult + UpdateIndexes(TransactionBase* aTransaction, + const UniqueIndexTable& aUniqueIndexTable, + const Key& aObjectStoreKey, + bool aOverwrite, + int64_t aObjectDataId, + const nsTArray& aUpdateInfoArray); + +private: + // Not to be overridden by subclasses. + NS_DECL_MOZISTORAGEPROGRESSHANDLER +}; + +class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler MOZ_FINAL +{ + mozIStorageConnection* mConnection; + DebugOnly mDEBUGDatabaseOp; + +public: + AutoSetProgressHandler() + : mConnection(nullptr) + , mDEBUGDatabaseOp(nullptr) + { } + + ~AutoSetProgressHandler(); + + nsresult + Register(DatabaseOperationBase* aDatabaseOp, + const nsCOMPtr& aConnection); +}; + +class TransactionDatabaseOperationBase + : public DatabaseOperationBase +{ + nsRefPtr mTransaction; + const bool mTransactionIsAborted; + +public: + void + AssertIsOnTransactionThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + DispatchToTransactionThreadPool(); + + // May be overridden by subclasses if they need to perform work on the + // background thread before being dispatched. Returning false will kill the + // child actors and prevent dispatch. + virtual bool + Init(TransactionBase* aTransaction); + + // This callback will be called on the background thread before releasing the + // final reference to this request object. Subclasses may perform any + // additional cleanup here but must always call the base class implementation. + virtual void + Cleanup(); + +protected: + TransactionDatabaseOperationBase(TransactionBase* aTransaction); + + virtual + ~TransactionDatabaseOperationBase(); + + // Must be overridden in subclasses. Called on the target thread to allow the + // subclass to perform necessary database or file operations. A successful + // return value will trigger a SendSuccessResult callback on the background + // thread while a failure value will trigger a SendFailureResult callback. + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) = 0; + + // Must be overridden in subclasses. Called on the background thread to allow + // the subclass to serialize its results and send them to the child actor. A + // failed return value will trigger a SendFailureResult callback. + virtual nsresult + SendSuccessResult() = 0; + + // Must be overridden in subclasses. Called on the background thread to allow + // the subclass to send its failure code. Returning false will cause the + // transaction to be aborted with aResultCode. Returning true will not cause + // the transaction to be aborted. + virtual bool + SendFailureResult(nsresult aResultCode) = 0; + +private: + void + RunOnTransactionThread(); + + void + RunOnOwningThread(); + + // Not to be overridden by subclasses. + NS_DECL_NSIRUNNABLE +}; + +class Factory MOZ_FINAL + : public PBackgroundIDBFactoryParent +{ + // Counts the number of "live" Factory instances that have not yet had + // ActorDestroy called. + static uint64_t sFactoryInstanceCount; + + nsRefPtr mTransactionThreadPool; + + const OptionalWindowId mOptionalWindowId; + + DebugOnly mActorDestroyed; + +public: + static already_AddRefed + Create(const OptionalWindowId& aOptionalWindowId); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory) + + TransactionThreadPool* + GetTransactionThreadPool() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionThreadPool); + + return mTransactionThreadPool; + } + +private: + // Only constructed in Create(). + Factory(const OptionalWindowId& aOptionalWindowId); + + // Reference counted. + ~Factory(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvDeleteMe() MOZ_OVERRIDE; + + virtual PBackgroundIDBFactoryRequestParent* + AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBFactoryRequestConstructor( + PBackgroundIDBFactoryRequestParent* aActor, + const FactoryRequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBFactoryRequestParent( + PBackgroundIDBFactoryRequestParent* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBDatabaseParent* + AllocPBackgroundIDBDatabaseParent( + const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestParent* aRequest) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBDatabaseParent(PBackgroundIDBDatabaseParent* aActor) + MOZ_OVERRIDE; +}; + +class Database MOZ_FINAL + : public PBackgroundIDBDatabaseParent +{ + friend class VersionChangeTransaction; + + nsRefPtr mFactory; + nsRefPtr mTransactionThreadPool; + nsRefPtr mMetadata; + nsRefPtr mFileManager; + nsRefPtr mOfflineStorage; + nsTHashtable> mTransactions; + const PrincipalInfo mPrincipalInfo; + const nsCString mGroup; + const nsCString mOrigin; + const nsCString mId; + const nsString mFilePath; + Atomic mInvalidatedOnAnyThread; + const PersistenceType mPersistenceType; + const bool mChromeWriteAccessAllowed; + bool mClosed; + bool mInvalidated; + bool mActorWasAlive; + bool mActorDestroyed; + bool mMetadataCleanedUp; + +public: + // Created by OpenDatabaseOp. + Database(Factory* aFactory, + const PrincipalInfo& aPrincipalInfo, + const nsACString& aGroup, + const nsACString& aOrigin, + FullDatabaseMetadata* aMetadata, + FileManager* aFileManager, + already_AddRefed aOfflineStorage, + bool aChromeWriteAccessAllowed); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Database) + + void + Invalidate(); + + const PrincipalInfo& + GetPrincipalInfo() const + { + return mPrincipalInfo; + } + + const nsCString& + Group() const + { + return mGroup; + } + + const nsCString& + Origin() const + { + return mOrigin; + } + + const nsCString& + Id() const + { + return mId; + } + + PersistenceType + Type() const + { + return mPersistenceType; + } + + const nsString& + FilePath() const + { + return mFilePath; + } + + FileManager* + GetFileManager() const + { + return mFileManager; + } + + FullDatabaseMetadata* + Metadata() const + { + MOZ_ASSERT(mMetadata); + return mMetadata; + } + + PBackgroundParent* + GetBackgroundParent() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return Manager()->Manager(); + } + + TransactionThreadPool* + GetTransactionThreadPool() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionThreadPool); + + return mTransactionThreadPool; + } + + bool + RegisterTransaction(TransactionBase* aTransaction); + + void + UnregisterTransaction(TransactionBase* aTransaction); + + void + SetActorAlive(); + + bool + IsActorAlive() const + { + AssertIsOnBackgroundThread(); + + return mActorWasAlive && !mActorDestroyed; + } + + bool + IsActorDestroyed() const + { + AssertIsOnBackgroundThread(); + + return mActorWasAlive && mActorDestroyed; + } + + bool + IsClosed() const + { + AssertIsOnBackgroundThread(); + + return mClosed; + } + + bool + IsInvalidated() const + { + AssertIsOnBackgroundThread(); + + return mInvalidated; + } + +private: + // Reference counted. + ~Database() + { + MOZ_ASSERT(mClosed); + MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); + } + + bool + CloseInternal(); + + void + CleanupMetadata(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual PBackgroundIDBDatabaseFileParent* + AllocPBackgroundIDBDatabaseFileParent( + const BlobOrInputStream& aBlobOrInputStream) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBDatabaseFileParent( + PBackgroundIDBDatabaseFileParent* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBTransactionParent* + AllocPBackgroundIDBTransactionParent( + const nsTArray& aObjectStoreNames, + const Mode& aMode) + MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBTransactionConstructor( + PBackgroundIDBTransactionParent* aActor, + const nsTArray& aObjectStoreNames, + const Mode& aMode) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBTransactionParent( + PBackgroundIDBTransactionParent* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBVersionChangeTransactionParent* + AllocPBackgroundIDBVersionChangeTransactionParent( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBVersionChangeTransactionParent( + PBackgroundIDBVersionChangeTransactionParent* aActor) + MOZ_OVERRIDE; + + virtual bool + RecvDeleteMe() MOZ_OVERRIDE; + + virtual bool + RecvBlocked() MOZ_OVERRIDE; + + virtual bool + RecvClose() MOZ_OVERRIDE; +}; + +class DatabaseFile MOZ_FINAL + : public PBackgroundIDBDatabaseFileParent +{ + friend class Database; + + InputStreamParams mInputStreamParams; + nsRefPtr mFileInfo; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile); + + FileInfo* + GetFileInfo() const + { + AssertIsOnBackgroundThread(); + + return mFileInfo; + } + + already_AddRefed + GetInputStream() const; + + void + ClearInputStreamParams(); + +private: + // Called when sending to the child. + DatabaseFile(FileInfo* aFileInfo) + : mFileInfo(aFileInfo) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileInfo); + } + + // Called when receiving from the child. + DatabaseFile(const InputStreamParams& aInputStreamParams, FileInfo* aFileInfo) + : mInputStreamParams(aInputStreamParams) + , mFileInfo(aFileInfo) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aInputStreamParams.type() != InputStreamParams::T__None); + MOZ_ASSERT(aFileInfo); + } + + ~DatabaseFile() + { } + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; +}; + +class TransactionBase +{ + friend class Cursor; + + class CommitOp; + class UpdateRefcountFunction; + +public: + class AutoSavepoint; + class CachedStatement; + +protected: + typedef IDBTransaction::Mode Mode; + +private: + nsRefPtr mDatabase; + nsRefPtr mTransactionThreadPool; + nsCOMPtr mConnection; + nsRefPtr mUpdateFileRefcountFunction; + nsInterfaceHashtable + mCachedStatements; + nsTArray> + mModifiedAutoIncrementObjectStoreMetadataArray; + const uint64_t mTransactionId; + const nsCString mDatabaseId; + uint64_t mActiveRequestCount; + Atomic mInvalidatedOnAnyThread; + Mode mMode; + bool mHasBeenActive; + bool mActorDestroyed; + bool mInvalidated; + +protected: + nsresult mResultCode; + bool mCommitOrAbortReceived; + bool mCommittedOrAborted; + bool mForceAborted; + +private: + DebugOnly mTransactionThread; + DebugOnly mSavepointCount; + +public: + void + AssertIsOnTransactionThread() const + { + MOZ_ASSERT(mTransactionThread); + MOZ_ASSERT(PR_GetCurrentThread() == mTransactionThread); + } + + bool + IsActorDestroyed() const + { + AssertIsOnBackgroundThread(); + + return mActorDestroyed; + } + + // Must be called on the background thread. + bool + IsInvalidated() const + { + MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()"); + MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode)); + + return mInvalidated; + } + + // May be called on any thread, but is more expensive than IsInvalidated(). + bool + IsInvalidatedOnAnyThread() const + { + return mInvalidatedOnAnyThread; + } + + void + SetActive() + { + AssertIsOnBackgroundThread(); + + mHasBeenActive = true; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING( + mozilla::dom::indexedDB::TransactionBase) + + nsresult + GetCachedStatement(const nsACString& aQuery, + CachedStatement* aCachedStatement); + + template + nsresult + GetCachedStatement(const char (&aQuery)[N], + CachedStatement* aCachedStatement) + { + AssertIsOnTransactionThread(); + MOZ_ASSERT(aCachedStatement); + + return GetCachedStatement(NS_LITERAL_CSTRING(aQuery), aCachedStatement); + } + + nsresult + EnsureConnection(); + + void + Abort(nsresult aResultCode, bool aForce); + + mozIStorageConnection* + Connection() const + { + AssertIsOnTransactionThread(); + MOZ_ASSERT(mConnection); + + return mConnection; + } + + uint64_t + TransactionId() const + { + return mTransactionId; + } + + const nsCString& + DatabaseId() const + { + return mDatabaseId; + } + + Mode + GetMode() const + { + return mMode; + } + + Database* + GetDatabase() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mDatabase); + + return mDatabase; + } + + bool + IsAborted() const + { + AssertIsOnBackgroundThread(); + + return NS_FAILED(mResultCode); + } + + already_AddRefed + GetMetadataForObjectStoreId(int64_t aObjectStoreId) const; + + already_AddRefed + GetMetadataForIndexId(FullObjectStoreMetadata* const aObjectStoreMetadata, + int64_t aIndexId) const; + + PBackgroundParent* + GetBackgroundParent() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return GetDatabase()->GetBackgroundParent(); + } + + TransactionThreadPool* + GetTransactionThreadPool() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionThreadPool); + + return mTransactionThreadPool; + } + + void + NoteModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata); + + void + ForgetModifiedAutoIncrementObjectStore(FullObjectStoreMetadata* aMetadata); + + nsresult + StartSavepoint(); + + nsresult + ReleaseSavepoint(); + + nsresult + RollbackSavepoint(); + + void + NoteActiveRequest(); + + void + NoteFinishedRequest(); + + void + Invalidate(); + +protected: + TransactionBase(Database* aDatabase, + Mode aMode); + + virtual + ~TransactionBase(); + + void + NoteActorDestroyed() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + } + +#ifdef DEBUG + // Only called by VersionChangeTransaction. + void + FakeActorDestroyed() + { + mActorDestroyed = true; + } +#endif + + bool + RecvCommit(); + + bool + RecvAbort(nsresult aResultCode); + + void + MaybeCommitOrAbort() + { + AssertIsOnBackgroundThread(); + + // If we've already committed or aborted then there's nothing else to do. + if (mCommittedOrAborted) { + return; + } + + // If there are active requests then we have to wait for those requests to + // complete (see NoteFinishedRequest). + if (mActiveRequestCount) { + return; + } + + // If we haven't yet received a commit or abort message then there could be + // additional requests coming so we should wait unless we're being forced to + // abort. + if (!mCommitOrAbortReceived && !mForceAborted) { + return; + } + + CommitOrAbort(); + } + + PBackgroundIDBRequestParent* + AllocRequest(const RequestParams& aParams, bool aTrustParams); + + bool + StartRequest(PBackgroundIDBRequestParent* aActor); + + bool + DeallocRequest(PBackgroundIDBRequestParent* aActor); + + PBackgroundIDBCursorParent* + AllocCursor(const OpenCursorParams& aParams, bool aTrustParams); + + bool + StartCursor(PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams); + + bool + DeallocCursor(PBackgroundIDBCursorParent* aActor); + + virtual void + UpdateMetadata(nsresult aResult) + { } + + virtual bool + SendCompleteNotification(nsresult aResult) = 0; + +private: + // Only called by CommitOp. + void + ReleaseTransactionThreadObjects(); + + // Only called by CommitOp. + void + ReleaseBackgroundThreadObjects(); + + bool + VerifyRequestParams(const RequestParams& aParams) const; + + bool + VerifyRequestParams(const OpenCursorParams& aParams) const; + + bool + VerifyRequestParams(const CursorRequestParams& aParams) const; + + bool + VerifyRequestParams(const SerializedKeyRange& aKeyRange) const; + + bool + VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const; + + bool + VerifyRequestParams(const OptionalKeyRange& aKeyRange) const; + + void + CommitOrAbort(); +}; + +class TransactionBase::CommitOp MOZ_FINAL + : public DatabaseOperationBase + , public TransactionThreadPool::FinishCallback +{ + friend class TransactionBase; + + nsRefPtr mTransaction; + nsresult mResultCode; + +private: + CommitOp(TransactionBase* aTransaction, + nsresult aResultCode) + : mTransaction(aTransaction) + , mResultCode(aResultCode) + { + MOZ_ASSERT(aTransaction); + } + + ~CommitOp() + { } + + // Writes new autoIncrement counts to database. + nsresult + WriteAutoIncrementCounts(); + + // Updates counts after a database activity has finished. + void + CommitOrRollbackAutoIncrementCounts(); + + NS_DECL_NSIRUNNABLE + + virtual void + TransactionFinishedBeforeUnblock() MOZ_OVERRIDE; + + virtual void + TransactionFinishedAfterUnblock() MOZ_OVERRIDE; + +public: + void + AssertIsOnTransactionThread() const + { + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnTransactionThread(); + } + + NS_DECL_ISUPPORTS_INHERITED +}; + +class TransactionBase::UpdateRefcountFunction MOZ_FINAL + : public mozIStorageFunction +{ + class FileInfoEntry + { + friend class UpdateRefcountFunction; + + nsRefPtr mFileInfo; + int32_t mDelta; + int32_t mSavepointDelta; + + public: + FileInfoEntry(FileInfo* aFileInfo) + : mFileInfo(aFileInfo) + , mDelta(0) + , mSavepointDelta(0) + { } + }; + + enum UpdateType + { + eIncrement, + eDecrement + }; + + class DatabaseUpdateFunction + { + nsCOMPtr mConnection; + nsCOMPtr mUpdateStatement; + nsCOMPtr mSelectStatement; + nsCOMPtr mInsertStatement; + + UpdateRefcountFunction* mFunction; + + nsresult mErrorCode; + + public: + DatabaseUpdateFunction(mozIStorageConnection* aConnection, + UpdateRefcountFunction* aFunction) + : mConnection(aConnection) + , mFunction(aFunction) + , mErrorCode(NS_OK) + { } + + bool + Update(int64_t aId, int32_t aDelta); + + nsresult + ErrorCode() const + { + return mErrorCode; + } + + private: + nsresult + UpdateInternal(int64_t aId, int32_t aDelta); + }; + + FileManager* mFileManager; + nsClassHashtable mFileInfoEntries; + nsDataHashtable mSavepointEntriesIndex; + + nsTArray mJournalsToCreateBeforeCommit; + nsTArray mJournalsToRemoveAfterCommit; + nsTArray mJournalsToRemoveAfterAbort; + + bool mInSavepoint; + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION + + UpdateRefcountFunction(FileManager* aFileManager) + : mFileManager(aFileManager) + , mInSavepoint(false) + { } + + void + ClearFileInfoEntries() + { + mFileInfoEntries.Clear(); + } + + nsresult + WillCommit(mozIStorageConnection* aConnection); + + void + DidCommit(); + + void + DidAbort(); + + void + StartSavepoint(); + + void + ReleaseSavepoint(); + + void + RollbackSavepoint(); + +private: + ~UpdateRefcountFunction() + { } + + nsresult + ProcessValue(mozIStorageValueArray* aValues, + int32_t aIndex, + UpdateType aUpdateType); + + nsresult + CreateJournals(); + + nsresult + RemoveJournals(const nsTArray& aJournals); + + static PLDHashOperator + DatabaseUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg); + + static PLDHashOperator + FileInfoUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg); +}; + +class MOZ_STACK_CLASS TransactionBase::AutoSavepoint MOZ_FINAL +{ + TransactionBase* mTransaction; + +public: + AutoSavepoint() + : mTransaction(nullptr) + { } + + ~AutoSavepoint(); + + nsresult + Start(TransactionBase* aTransaction); + + nsresult + Commit(); +}; + +class TransactionBase::CachedStatement MOZ_FINAL +{ + friend class TransactionBase; + + nsCOMPtr mStatement; + Maybe mScoper; + +public: + CachedStatement() + { } + + ~CachedStatement() + { } + + operator mozIStorageStatement*() + { + return mStatement; + } + + mozIStorageStatement* + operator->() + { + MOZ_ASSERT(mStatement); + return mStatement; + } + + void + Reset() + { + MOZ_ASSERT_IF(mStatement, mScoper); + + if (mStatement) { + mScoper.reset(); + mScoper.emplace(mStatement); + } + } + +private: + // Only called by TransactionBase. + void + Assign(already_AddRefed aStatement) + { + mScoper.reset(); + + mStatement = aStatement; + + if (mStatement) { + mScoper.emplace(mStatement); + } + } + + // No funny business allowed. + CachedStatement(const CachedStatement&) MOZ_DELETE; + CachedStatement& operator=(const CachedStatement&) MOZ_DELETE; +}; + +class NormalTransaction MOZ_FINAL + : public TransactionBase + , public PBackgroundIDBTransactionParent +{ + friend class Database; + + nsTArray> mObjectStores; + +private: + // This constructor is only called by Database. + NormalTransaction(Database* aDatabase, + nsTArray>& aObjectStores, + TransactionBase::Mode aMode); + + // Reference counted. + ~NormalTransaction() + { } + + bool + IsSameProcessActor(); + + // Only called by TransactionBase. + virtual bool + SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE; + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvDeleteMe() MOZ_OVERRIDE; + + virtual bool + RecvCommit() MOZ_OVERRIDE; + + virtual bool + RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE; + + virtual PBackgroundIDBRequestParent* + AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor, + const RequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBCursorParent* + AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor) + MOZ_OVERRIDE; +}; + +class VersionChangeTransaction MOZ_FINAL + : public TransactionBase + , public PBackgroundIDBVersionChangeTransactionParent +{ + friend class OpenDatabaseOp; + + nsRefPtr mOpenDatabaseOp; + nsRefPtr mOldMetadata; + + bool mActorWasAlive; + +private: + // Only called by OpenDatabaseOp. + VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp); + + // Reference counted. + ~VersionChangeTransaction(); + + bool + IsSameProcessActor(); + + // Only called by OpenDatabaseOp. + bool + CopyDatabaseMetadata(); + + void + SetActorAlive(); + + // Only called by TransactionBase. + virtual void + UpdateMetadata(nsresult aResult) MOZ_OVERRIDE; + + // Only called by TransactionBase. + virtual bool + SendCompleteNotification(nsresult aResult) MOZ_OVERRIDE; + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvDeleteMe() MOZ_OVERRIDE; + + virtual bool + RecvCommit() MOZ_OVERRIDE; + + virtual bool + RecvAbort(const nsresult& aResultCode) MOZ_OVERRIDE; + + virtual bool + RecvCreateObjectStore(const ObjectStoreMetadata& aMetadata) MOZ_OVERRIDE; + + virtual bool + RecvDeleteObjectStore(const int64_t& aObjectStoreId) MOZ_OVERRIDE; + + virtual bool + RecvCreateIndex(const int64_t& aObjectStoreId, + const IndexMetadata& aMetadata) MOZ_OVERRIDE; + + virtual bool + RecvDeleteIndex(const int64_t& aObjectStoreId, + const int64_t& aIndexId) MOZ_OVERRIDE; + + virtual PBackgroundIDBRequestParent* + AllocPBackgroundIDBRequestParent(const RequestParams& aParams) MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBRequestConstructor(PBackgroundIDBRequestParent* aActor, + const RequestParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBRequestParent(PBackgroundIDBRequestParent* aActor) + MOZ_OVERRIDE; + + virtual PBackgroundIDBCursorParent* + AllocPBackgroundIDBCursorParent(const OpenCursorParams& aParams) MOZ_OVERRIDE; + + virtual bool + RecvPBackgroundIDBCursorConstructor(PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams) + MOZ_OVERRIDE; + + virtual bool + DeallocPBackgroundIDBCursorParent(PBackgroundIDBCursorParent* aActor) + MOZ_OVERRIDE; +}; + +class FactoryOp + : public DatabaseOperationBase + , public PBackgroundIDBFactoryRequestParent +{ +public: + struct MaybeBlockedDatabaseInfo; + +protected: + enum State + { + // Just created on the PBackground thread, dispatched to the main thread. + // Next step is State_OpenPending. + State_Initial, + + // Waiting for open allowed on the main thread. The next step is either + // State_SendingResults if permission is denied, + // State_PermissionChallenge if the permission is unknown, or + // State_DatabaseWorkOpen if permission is granted. + State_OpenPending, + + // Sending a permission challenge message to the child on the PBackground + // thread. Next step is State_PermissionRetryReady. + State_PermissionChallenge, + + // Retrying permission check after a challenge on the main thread. Next step + // is either State_SendingResults if permission is denied or + // State_DatabaseWorkOpen if permission is granted. + State_PermissionRetry, + + // Waiting to do/doing work on the QuotaManager IO thread. Its next step is + // either State_BeginVersionChange if the requested version doesn't match + // the existing database version or State_SendingResults if the versions + // match. + State_DatabaseWorkOpen, + + // Starting a version change transaction or deleting a database on the + // PBackground thread. We need to notify other databases that a version + // change is about to happen, and maybe tell the request that a version + // change has been blocked. If databases are notified then the next step is + // State_WaitingForOtherDatabasesToClose. Otherwise the next step is + // State_DispatchToWorkThread. + State_BeginVersionChange, + + // Waiting for other databases to close on the PBackground thread. This + // state may persist until all databases are closed. The next state is + // State_WaitingForTransactionsToComplete. + State_WaitingForOtherDatabasesToClose, + + // Waiting for all transactions that could interfere with this operation to + // complete on the PBackground thread. Next state is + // State_DatabaseWorkVersionChange. + State_WaitingForTransactionsToComplete, + + // Waiting to do/doing work on the "work thread". This involves waiting for + // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a + // different implementation) to do its work. Eventually the state will + // transition to State_SendingResults. + State_DatabaseWorkVersionChange, + + // Waiting to send/sending results on the PBackground thread. Next step is + // UnblockingQuotaManager. + State_SendingResults, + + // Notifying the QuotaManager that it can proceed to the next operation on + // the main thread. Next step is Completed. + State_UnblockingQuotaManager, + + // All done. + State_Completed + }; + + // Must be released on the background thread! + nsRefPtr mFactory; + + // Must be released on the main thread! + nsRefPtr mContentParent; + + nsTArray mMaybeBlockedDatabases; + + const CommonFactoryRequestParams mCommonParams; + nsCString mGroup; + nsCString mOrigin; + nsCString mDatabaseId; + State mState; + StoragePrivilege mStoragePrivilege; + const bool mDeleting; + bool mBlockedQuotaManager; + bool mChromeWriteAccessAllowed; + +public: + void + NoteDatabaseBlocked(Database* aDatabase); + + virtual void + NoteDatabaseClosed(Database* aDatabase) = 0; + +#ifdef DEBUG + bool + HasBlockedDatabases() const + { + return !mMaybeBlockedDatabases.IsEmpty(); + } +#endif + +protected: + FactoryOp(Factory* aFactory, + already_AddRefed aContentParent, + const CommonFactoryRequestParams& aCommonParams, + bool aDeleting) + : mFactory(aFactory) + , mContentParent(Move(aContentParent)) + , mCommonParams(aCommonParams) + , mState(State_Initial) + , mStoragePrivilege(mozilla::dom::quota::Content) + , mDeleting(aDeleting) + , mBlockedQuotaManager(false) + , mChromeWriteAccessAllowed(false) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFactory); + } + + virtual + ~FactoryOp() + { + // Normally this would be out-of-line since it is a virtual function but + // MSVC 2010 fails to link for some reason if it is not inlined here... + MOZ_ASSERT_IF(OperationMayProceed(), + mState == State_Initial || mState == State_Completed); + } + + nsresult + Open(); + + nsresult + ChallengePermission(); + + nsresult + RetryCheckPermission(); + + nsresult + SendToIOThread(); + + void + WaitForTransactions(); + + void + FinishSendResults(); + + void + UnblockQuotaManager(); + + nsresult + SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, + Database* aOpeningDatabase, + uint64_t aOldVersion, + const NullableVersion& aNewVersion); + + // Methods that subclasses must implement. + virtual nsresult + QuotaManagerOpen() = 0; + + virtual nsresult + DoDatabaseWork() = 0; + + virtual nsresult + BeginVersionChange() = 0; + + virtual nsresult + DispatchToWorkThread() = 0; + + virtual void + SendResults() = 0; + + // Common nsIRunnable implementation that subclasses may not override. + NS_IMETHOD + Run() MOZ_FINAL; + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvPermissionRetry() MOZ_OVERRIDE; + + virtual void + SendBlockedNotification() = 0; + +private: + nsresult + CheckPermission(ContentParent* aContentParent, + PermissionRequestBase::PermissionValue* aPermission); + + static bool + CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, + const nsACString& aPermissionString); + + nsresult + FinishOpen(); +}; + +struct FactoryOp::MaybeBlockedDatabaseInfo MOZ_FINAL +{ + nsRefPtr mDatabase; + bool mBlocked; + + MaybeBlockedDatabaseInfo(Database* aDatabase) + : mDatabase(aDatabase) + , mBlocked(false) + { + MOZ_ASSERT(aDatabase); + + MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo); + } + + ~MaybeBlockedDatabaseInfo() + { + MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo); + } + + bool + operator==(const MaybeBlockedDatabaseInfo& aOther) const + { + return mDatabase == aOther.mDatabase; + } + + bool + operator<(const MaybeBlockedDatabaseInfo& aOther) const + { + return mDatabase < aOther.mDatabase; + } + + Database* + operator->() + { + return mDatabase; + } +}; + +class OpenDatabaseOp MOZ_FINAL + : public FactoryOp +{ + friend class Database; + friend class VersionChangeTransaction; + + class VersionChangeOp; + + const OptionalWindowId mOptionalWindowId; + const OptionalWindowId mOptionalContentParentId; + + nsRefPtr mMetadata; + + uint64_t mRequestedVersion; + nsString mDatabaseFilePath; + nsRefPtr mFileManager; + + nsRefPtr mDatabase; + nsRefPtr mVersionChangeTransaction; + + nsRefPtr mOfflineStorage; + +public: + OpenDatabaseOp(Factory* aFactory, + already_AddRefed aContentParent, + const OptionalWindowId& aOptionalWindowId, + const CommonFactoryRequestParams& aParams); + + bool + IsOtherProcessActor() const + { + MOZ_ASSERT(mOptionalContentParentId.type() != OptionalWindowId::T__None); + + return mOptionalContentParentId.type() == OptionalWindowId::Tuint64_t; + } + +private: + ~OpenDatabaseOp() + { } + + nsresult + LoadDatabaseInformation(mozIStorageConnection* aConnection); + + nsresult + SendUpgradeNeeded(); + + void + EnsureDatabaseActor(); + + nsresult + EnsureDatabaseActorIsAlive(); + + void + MetadataToSpec(DatabaseSpec& aSpec); + + void + AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata) +#ifdef DEBUG + ; +#else + { } +#endif + + virtual nsresult + QuotaManagerOpen() MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork() MOZ_OVERRIDE; + + virtual nsresult + BeginVersionChange() MOZ_OVERRIDE; + + virtual void + NoteDatabaseClosed(Database* aDatabase) MOZ_OVERRIDE; + + virtual void + SendBlockedNotification() MOZ_OVERRIDE; + + virtual nsresult + DispatchToWorkThread() MOZ_OVERRIDE; + + virtual void + SendResults() MOZ_OVERRIDE; +}; + +class OpenDatabaseOp::VersionChangeOp MOZ_FINAL + : public TransactionDatabaseOperationBase +{ + friend class OpenDatabaseOp; + + nsRefPtr mOpenDatabaseOp; + const uint64_t mRequestedVersion; + uint64_t mPreviousVersion; + +private: + VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp) + : TransactionDatabaseOperationBase( + aOpenDatabaseOp->mVersionChangeTransaction) + , mOpenDatabaseOp(aOpenDatabaseOp) + , mRequestedVersion(aOpenDatabaseOp->mRequestedVersion) + , mPreviousVersion(aOpenDatabaseOp->mMetadata->mCommonMetadata.version()) + { + MOZ_ASSERT(aOpenDatabaseOp); + MOZ_ASSERT(mRequestedVersion); + } + + ~VersionChangeOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual nsresult + SendSuccessResult() MOZ_OVERRIDE; + + virtual bool + SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; + + virtual void + Cleanup() MOZ_OVERRIDE; +}; + +class DeleteDatabaseOp MOZ_FINAL + : public FactoryOp +{ + class VersionChangeOp; + + nsString mDatabaseDirectoryPath; + nsString mDatabaseFilenameBase; + uint64_t mPreviousVersion; + +public: + DeleteDatabaseOp(Factory* aFactory, + already_AddRefed aContentParent, + const CommonFactoryRequestParams& aParams) + : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ true) + , mPreviousVersion(0) + { } + +private: + ~DeleteDatabaseOp() + { } + + void + LoadPreviousVersion(nsIFile* aDatabaseFile); + + virtual nsresult + QuotaManagerOpen() MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork() MOZ_OVERRIDE; + + virtual nsresult + BeginVersionChange() MOZ_OVERRIDE; + + virtual void + NoteDatabaseClosed(Database* aDatabase) MOZ_OVERRIDE; + + virtual void + SendBlockedNotification() MOZ_OVERRIDE; + + virtual nsresult + DispatchToWorkThread() MOZ_OVERRIDE; + + virtual void + SendResults() MOZ_OVERRIDE; +}; + +class DeleteDatabaseOp::VersionChangeOp MOZ_FINAL + : public DatabaseOperationBase +{ + friend class DeleteDatabaseOp; + + nsRefPtr mDeleteDatabaseOp; + +private: + VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp) + : mDeleteDatabaseOp(aDeleteDatabaseOp) + { + MOZ_ASSERT(aDeleteDatabaseOp); + MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty()); + } + + ~VersionChangeOp() + { } + + // XXX This should be much simpler when the QuotaManager lives on the + // PBackground thread. + nsresult + RunOnMainThread(); + + nsresult + RunOnIOThread(); + + void + RunOnOwningThread(); + + NS_DECL_NSIRUNNABLE +}; + +class VersionChangeTransactionOp + : public TransactionDatabaseOperationBase +{ +public: + virtual void + Cleanup() MOZ_OVERRIDE; + +protected: + VersionChangeTransactionOp(VersionChangeTransaction* aTransaction) + : TransactionDatabaseOperationBase(aTransaction) + { } + + virtual + ~VersionChangeTransactionOp() + { } + +private: + virtual nsresult + SendSuccessResult() MOZ_OVERRIDE; + + virtual bool + SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; +}; + +class CreateObjectStoreOp MOZ_FINAL + : public VersionChangeTransactionOp +{ + friend class VersionChangeTransaction; + + const ObjectStoreMetadata mMetadata; + +private: + // Only created by VersionChangeTransaction. + CreateObjectStoreOp(VersionChangeTransaction* aTransaction, + const ObjectStoreMetadata& aMetadata) + : VersionChangeTransactionOp(aTransaction) + , mMetadata(aMetadata) + { + MOZ_ASSERT(aMetadata.id()); + } + + ~CreateObjectStoreOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; +}; + +class DeleteObjectStoreOp MOZ_FINAL + : public VersionChangeTransactionOp +{ + friend class VersionChangeTransaction; + + const nsRefPtr mMetadata; + +private: + // Only created by VersionChangeTransaction. + DeleteObjectStoreOp(VersionChangeTransaction* aTransaction, + FullObjectStoreMetadata* const aMetadata) + : VersionChangeTransactionOp(aTransaction) + , mMetadata(aMetadata) + { + MOZ_ASSERT(aMetadata->mCommonMetadata.id()); + } + + ~DeleteObjectStoreOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; +}; + +class CreateIndexOp MOZ_FINAL + : public VersionChangeTransactionOp +{ + friend class VersionChangeTransaction; + + class ThreadLocalJSRuntime; + friend class ThreadLocalJSRuntime; + + static const unsigned int kBadThreadLocalIndex = + static_cast(-1); + + static unsigned int sThreadLocalIndex; + + const IndexMetadata mMetadata; + Maybe mMaybeUniqueIndexTable; + nsRefPtr mFileManager; + const nsCString mDatabaseId; + const uint64_t mObjectStoreId; + +private: + // Only created by VersionChangeTransaction. + CreateIndexOp(VersionChangeTransaction* aTransaction, + const int64_t aObjectStoreId, + const IndexMetadata& aMetadata); + + ~CreateIndexOp() + { } + + static void + InitThreadLocals(); + + nsresult + InsertDataFromObjectStore(TransactionBase* aTransaction); + + virtual bool + Init(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; +}; + +class CreateIndexOp::ThreadLocalJSRuntime MOZ_FINAL +{ + friend class CreateIndexOp; + friend class nsAutoPtr; + + static const JSClass kGlobalClass; + static const uint32_t kRuntimeHeapSize = 768 * 1024; + + JSRuntime* mRuntime; + JSContext* mContext; + JSObject* mGlobal; + +public: + static ThreadLocalJSRuntime* + GetOrCreate(); + + JSContext* + Context() const + { + return mContext; + } + + JSObject* + Global() const + { + return mGlobal; + } + +private: + ThreadLocalJSRuntime() + : mRuntime(nullptr) + , mContext(nullptr) + , mGlobal(nullptr) + { + MOZ_COUNT_CTOR(CreateIndexOp::ThreadLocalJSRuntime); + } + + ~ThreadLocalJSRuntime() + { + MOZ_COUNT_DTOR(CreateIndexOp::ThreadLocalJSRuntime); + + if (mContext) { + JS_DestroyContext(mContext); + } + + if (mRuntime) { + JS_DestroyRuntime(mRuntime); + } + } + + bool + Init(); +}; + +class DeleteIndexOp MOZ_FINAL + : public VersionChangeTransactionOp +{ + friend class VersionChangeTransaction; + + const int64_t mIndexId; + +private: + // Only created by VersionChangeTransaction. + DeleteIndexOp(VersionChangeTransaction* aTransaction, + const int64_t aIndexId) + : VersionChangeTransactionOp(aTransaction) + , mIndexId(aIndexId) + { + MOZ_ASSERT(aIndexId); + } + + ~DeleteIndexOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; +}; + +class NormalTransactionOp + : public TransactionDatabaseOperationBase + , public PBackgroundIDBRequestParent +{ + DebugOnly mResponseSent; + +public: + virtual void + Cleanup() MOZ_OVERRIDE; + +protected: + NormalTransactionOp(TransactionBase* aTransaction) + : TransactionDatabaseOperationBase(aTransaction) + , mResponseSent(false) + { } + + virtual + ~NormalTransactionOp() + { } + + // Subclasses use this override to set the IPDL response value. + virtual void + GetResponse(RequestResponse& aResponse) = 0; + +private: + virtual nsresult + SendSuccessResult() MOZ_OVERRIDE; + + virtual bool + SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; +}; + +class ObjectStoreAddOrPutRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + struct StoredFileInfo; + + const ObjectStoreAddPutParams mParams; + Maybe mUniqueIndexTable; + + // This must be non-const so that we can update the mNextAutoIncrementId field + // if we are modifying an autoIncrement objectStore. + nsRefPtr mMetadata; + + FallibleTArray mStoredFileInfos; + + nsRefPtr mFileManager; + + Key mResponse; + const nsCString mGroup; + const nsCString mOrigin; + const PersistenceType mPersistenceType; + const bool mOverwrite; + +private: + // Only created by TransactionBase. + ObjectStoreAddOrPutRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams); + + ~ObjectStoreAddOrPutRequestOp() + { } + + nsresult + CopyFileData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream); + + virtual bool + Init(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; + + virtual void + Cleanup() MOZ_OVERRIDE; +}; + +struct ObjectStoreAddOrPutRequestOp::StoredFileInfo MOZ_FINAL +{ + nsRefPtr mFileActor; + nsRefPtr mFileInfo; + nsCOMPtr mInputStream; + bool mCopiedSuccessfully; + + StoredFileInfo() + : mCopiedSuccessfully(false) + { + AssertIsOnBackgroundThread(); + + MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); + } + + ~StoredFileInfo() + { + AssertIsOnBackgroundThread(); + + MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo); + } +}; + +class ObjectStoreGetRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + const uint32_t mObjectStoreId; + nsRefPtr mFileManager; + const OptionalKeyRange mOptionalKeyRange; + AutoFallibleTArray mResponse; + PBackgroundParent* mBackgroundParent; + const uint32_t mLimit; + const bool mGetAll; + +private: + // Only created by TransactionBase. + ObjectStoreGetRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll); + + ~ObjectStoreGetRequestOp() + { } + + nsresult + ConvertResponse(uint32_t aIndex, + SerializedStructuredCloneReadInfo& aSerializedInfo); + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; +}; + +class ObjectStoreGetAllKeysRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + const ObjectStoreGetAllKeysParams mParams; + FallibleTArray mResponse; + +private: + // Only created by TransactionBase. + ObjectStoreGetAllKeysRequestOp(TransactionBase* aTransaction, + const ObjectStoreGetAllKeysParams& aParams) + : NormalTransactionOp(aTransaction) + , mParams(aParams) + { } + + ~ObjectStoreGetAllKeysRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; +}; + +class ObjectStoreDeleteRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + const ObjectStoreDeleteParams mParams; + ObjectStoreDeleteResponse mResponse; + +private: + ObjectStoreDeleteRequestOp(TransactionBase* aTransaction, + const ObjectStoreDeleteParams& aParams) + : NormalTransactionOp(aTransaction) + , mParams(aParams) + { } + + ~ObjectStoreDeleteRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE + { + aResponse = Move(mResponse); + } +}; + +class ObjectStoreClearRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + const ObjectStoreClearParams mParams; + ObjectStoreClearResponse mResponse; + +private: + ObjectStoreClearRequestOp(TransactionBase* aTransaction, + const ObjectStoreClearParams& aParams) + : NormalTransactionOp(aTransaction) + , mParams(aParams) + { } + + ~ObjectStoreClearRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE + { + aResponse = Move(mResponse); + } +}; + +class ObjectStoreCountRequestOp MOZ_FINAL + : public NormalTransactionOp +{ + friend class TransactionBase; + + const ObjectStoreCountParams mParams; + ObjectStoreCountResponse mResponse; + +private: + ObjectStoreCountRequestOp(TransactionBase* aTransaction, + const ObjectStoreCountParams& aParams) + : NormalTransactionOp(aTransaction) + , mParams(aParams) + { } + + ~ObjectStoreCountRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE + { + aResponse = Move(mResponse); + } +}; + +class IndexRequestOpBase + : public NormalTransactionOp +{ +protected: + const nsRefPtr mMetadata; + +protected: + IndexRequestOpBase(TransactionBase* aTransaction, + const RequestParams& aParams) + : NormalTransactionOp(aTransaction) + , mMetadata(IndexMetadataForParams(aTransaction, aParams)) + { } + + virtual + ~IndexRequestOpBase() + { } + +private: + static already_AddRefed + IndexMetadataForParams(TransactionBase* aTransaction, + const RequestParams& aParams); +}; + +class IndexGetRequestOp MOZ_FINAL + : public IndexRequestOpBase +{ + friend class TransactionBase; + + nsRefPtr mFileManager; + const OptionalKeyRange mOptionalKeyRange; + AutoFallibleTArray mResponse; + PBackgroundParent* mBackgroundParent; + const uint32_t mLimit; + const bool mGetAll; + +private: + // Only created by TransactionBase. + IndexGetRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll); + + ~IndexGetRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; +}; + +class IndexGetKeyRequestOp MOZ_FINAL + : public IndexRequestOpBase +{ + friend class TransactionBase; + + const OptionalKeyRange mOptionalKeyRange; + AutoFallibleTArray mResponse; + const uint32_t mLimit; + const bool mGetAll; + +private: + // Only created by TransactionBase. + IndexGetKeyRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll); + + ~IndexGetKeyRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE; +}; + +class IndexCountRequestOp MOZ_FINAL + : public IndexRequestOpBase +{ + friend class TransactionBase; + + const IndexCountParams mParams; + IndexCountResponse mResponse; + +private: + // Only created by TransactionBase. + IndexCountRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams) + : IndexRequestOpBase(aTransaction, aParams) + , mParams(aParams.get_IndexCountParams()) + { } + + ~IndexCountRequestOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual void + GetResponse(RequestResponse& aResponse) MOZ_OVERRIDE + { + aResponse = Move(mResponse); + } +}; + +class Cursor MOZ_FINAL : + public PBackgroundIDBCursorParent +{ + friend class TransactionBase; + + class ContinueOp; + class CursorOpBase; + class OpenOp; + +public: + typedef OpenCursorParams::Type Type; + +private: + nsRefPtr mTransaction; + nsRefPtr mFileManager; + PBackgroundParent* mBackgroundParent; + + const int64_t mObjectStoreId; + const int64_t mIndexId; + + nsCString mContinueQuery; + nsCString mContinueToQuery; + + Key mKey; + Key mObjectKey; + Key mRangeKey; + + CursorOpBase* mCurrentlyRunningOp; + + const Type mType; + const Direction mDirection; + + bool mUniqueIndex; + bool mActorDestroyed; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Cursor) + +private: + // Only created by TransactionBase. + Cursor(TransactionBase* aTransaction, + Type aType, + int64_t aObjectStoreId, + int64_t aIndexId, + Direction aDirection); + + // Reference counted. + ~Cursor() + { + MOZ_ASSERT(mActorDestroyed); + } + + // Only called by TransactionBase. + bool + Start(const OpenCursorParams& aParams); + + void + SendResponseInternal(CursorResponse& aResponse, + const nsTArray& aFiles); + + // Must call SendResponseInternal! + bool + SendResponse(const CursorResponse& aResponse) MOZ_DELETE; + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool + RecvDeleteMe() MOZ_OVERRIDE; + + virtual bool + RecvContinue(const CursorRequestParams& aParams) MOZ_OVERRIDE; +}; + +class Cursor::CursorOpBase + : public TransactionDatabaseOperationBase +{ +protected: + nsRefPtr mCursor; + FallibleTArray mFiles; + + CursorResponse mResponse; + + DebugOnly mResponseSent; + +protected: + CursorOpBase(Cursor* aCursor) + : TransactionDatabaseOperationBase(aCursor->mTransaction) + , mCursor(aCursor) + , mResponseSent(false) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aCursor); + } + + virtual + ~CursorOpBase() + { } + + virtual bool + SendFailureResult(nsresult aResultCode) MOZ_OVERRIDE; + + virtual void + Cleanup() MOZ_OVERRIDE; +}; + +class Cursor::OpenOp MOZ_FINAL + : public Cursor::CursorOpBase +{ + friend class Cursor; + + const OptionalKeyRange mOptionalKeyRange; + +private: + // Only created by Cursor. + OpenOp(Cursor* aCursor, + const OptionalKeyRange& aOptionalKeyRange) + : CursorOpBase(aCursor) + , mOptionalKeyRange(aOptionalKeyRange) + { } + + // Reference counted. + ~OpenOp() + { } + + void + GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen); + + nsresult + DoObjectStoreDatabaseWork(TransactionBase* aTransaction); + + nsresult + DoObjectStoreKeyDatabaseWork(TransactionBase* aTransaction); + + nsresult + DoIndexDatabaseWork(TransactionBase* aTransaction); + + nsresult + DoIndexKeyDatabaseWork(TransactionBase* aTransaction); + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual nsresult + SendSuccessResult() MOZ_OVERRIDE; +}; + +class Cursor::ContinueOp MOZ_FINAL + : public Cursor::CursorOpBase +{ + friend class Cursor; + + const CursorRequestParams mParams; + +private: + // Only created by Cursor. + ContinueOp(Cursor* aCursor, const CursorRequestParams& aParams) + : CursorOpBase(aCursor) + , mParams(aParams) + { + MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); + } + + // Reference counted. + ~ContinueOp() + { } + + virtual nsresult + DoDatabaseWork(TransactionBase* aTransaction) MOZ_OVERRIDE; + + virtual nsresult + SendSuccessResult() MOZ_OVERRIDE; +}; + +class PermissionRequestHelper MOZ_FINAL + : public PermissionRequestBase + , public PIndexedDBPermissionRequestParent +{ + bool mActorDestroyed; + +public: + PermissionRequestHelper(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal) + : PermissionRequestBase(aWindow, aPrincipal) + , mActorDestroyed(false) + { } + +protected: + ~PermissionRequestHelper() + { } + +private: + virtual void + OnPromptComplete(PermissionValue aPermissionValue) MOZ_OVERRIDE; + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; +}; + +/******************************************************************************* + * Other class declarations + ******************************************************************************/ + +struct DatabaseActorInfo +{ + friend class nsAutoPtr; + + nsRefPtr mMetadata; + nsTArray mLiveDatabases; + nsRefPtr mWaitingFactoryOp; + + DatabaseActorInfo(FullDatabaseMetadata* aMetadata, + Database* aDatabase) + : mMetadata(aMetadata) + { + MOZ_ASSERT(aDatabase); + + MOZ_COUNT_CTOR(DatabaseActorInfo); + + mLiveDatabases.AppendElement(aDatabase); + } + +private: + ~DatabaseActorInfo() + { + MOZ_ASSERT(mLiveDatabases.IsEmpty()); + MOZ_ASSERT(!mWaitingFactoryOp || + !mWaitingFactoryOp->HasBlockedDatabases()); + + MOZ_COUNT_DTOR(DatabaseActorInfo); + } +}; + +class NonMainThreadHackBlob MOZ_FINAL + : public DOMFileImplFile +{ +public: + NonMainThreadHackBlob(nsIFile* aFile, FileInfo* aFileInfo) + : DOMFileImplFile(aFile, aFileInfo) + { + // Getting the content type is not currently supported off the main thread. + // This isn't a problem here because: + // + // 1. The real content type is stored in the structured clone data and + // that's all that the DOM will see. This blob's data will be updated + // during RecvSetMysteryBlobInfo(). + // 2. The nsExternalHelperAppService guesses the content type based only + // on the file extension. Our stored files have no extension so the + // current code path fails and sets the content type to the empty + // string. + // + // So, this is a hack to keep the nsExternalHelperAppService out of the + // picture entirely. Eventually we should probably fix this some other way. + mContentType.Truncate(); + } +}; + +class QuotaClient MOZ_FINAL + : public mozilla::dom::quota::Client +{ + class ShutdownTransactionThreadPoolRunnable; + friend class ShutdownTransactionThreadPoolRunnable; + + class WaitForTransactionsRunnable; + friend class WaitForTransactionsRunnable; + + static QuotaClient* sInstance; + + nsCOMPtr mBackgroundThread; + nsRefPtr mShutdownRunnable; + + // This is only touched on the background thread! + nsTArray> mDyingTransactionThreadPools; + + bool mShutdownRequested; + +public: + QuotaClient(); + + static QuotaClient* + GetInstance() + { + return sInstance; + } + + void + NoteBackgroundThread(nsIEventTarget* aBackgroundThread); + + void + NoteNewTransactionThreadPool(); + + void + NoteDyingTransactionThreadPool(); + + bool + HasShutDown() const + { + MOZ_ASSERT(NS_IsMainThread()); + + return mShutdownRequested; + } + + NS_INLINE_DECL_REFCOUNTING(QuotaClient) + + virtual mozilla::dom::quota::Client::Type + GetType() MOZ_OVERRIDE; + + virtual nsresult + InitOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) MOZ_OVERRIDE; + + virtual nsresult + GetUsageForOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) MOZ_OVERRIDE; + + virtual void + OnOriginClearCompleted(PersistenceType aPersistenceType, + const OriginOrPatternString& aOriginOrPattern) + MOZ_OVERRIDE; + + virtual void + ReleaseIOThreadObjects() MOZ_OVERRIDE; + + virtual bool + IsFileServiceUtilized() MOZ_OVERRIDE; + + virtual bool + IsTransactionServiceActivated() MOZ_OVERRIDE; + + virtual void + WaitForStoragesToComplete(nsTArray& aStorages, + nsIRunnable* aCallback) MOZ_OVERRIDE; + + virtual void + AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; + + virtual bool + HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; + + virtual void + ShutdownTransactionService() MOZ_OVERRIDE; + +private: + ~QuotaClient(); + + nsresult + GetDirectory(PersistenceType aPersistenceType, + const nsACString& aOrigin, + nsIFile** aDirectory); + + nsresult + GetUsageForDirectoryInternal(nsIFile* aDirectory, + UsageInfo* aUsageInfo, + bool aDatabaseFiles); +}; + +class QuotaClient::ShutdownTransactionThreadPoolRunnable MOZ_FINAL + : public nsRunnable +{ + nsRefPtr mQuotaClient; + bool mHasRequestedShutDown; + +public: + + ShutdownTransactionThreadPoolRunnable(QuotaClient* aQuotaClient) + : mQuotaClient(aQuotaClient) + , mHasRequestedShutDown(false) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQuotaClient); + MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); + MOZ_ASSERT(aQuotaClient->mShutdownRequested); + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~ShutdownTransactionThreadPoolRunnable() + { + MOZ_ASSERT(!mQuotaClient); + } + + NS_DECL_NSIRUNNABLE +}; + +class QuotaClient::WaitForTransactionsRunnable MOZ_FINAL + : public nsRunnable +{ + nsRefPtr mQuotaClient; + nsTArray mDatabaseIds; + nsCOMPtr mCallback; + + enum + { + State_Initial = 0, + State_WaitingForTransactions, + State_CallingCallback, + State_Complete + } mState; + +public: + WaitForTransactionsRunnable(QuotaClient* aQuotaClient, + nsTArray& aDatabaseIds, + nsIRunnable* aCallback) + : mQuotaClient(aQuotaClient) + , mCallback(aCallback) + , mState(State_Initial) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQuotaClient); + MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); + MOZ_ASSERT(!aDatabaseIds.IsEmpty()); + MOZ_ASSERT(aCallback); + + mDatabaseIds.SwapElements(aDatabaseIds); + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~WaitForTransactionsRunnable() + { + MOZ_ASSERT(!mQuotaClient); + MOZ_ASSERT(!mCallback); + MOZ_ASSERT(mState = State_Complete); + } + + void + MaybeWait(); + + void + SendToMainThread(); + + void + CallCallback(); + + NS_DECL_NSIRUNNABLE +}; + +class DatabaseOfflineStorage MOZ_FINAL + : public nsIOfflineStorage +{ + // Must be released on the main thread! + nsRefPtr mStrongQuotaClient; + + // Only used on the main thread. + QuotaClient* mWeakQuotaClient; + + // Only used on the background thread. + Database* mDatabase; + + const OptionalWindowId mOptionalWindowId; + const OptionalWindowId mOptionalContentParentId; + const nsCString mOrigin; + const nsCString mId; + nsCOMPtr mOwningThread; + Atomic mTransactionCount; + + bool mClosedOnMainThread; + bool mClosedOnOwningThread; + bool mInvalidatedOnMainThread; + bool mInvalidatedOnOwningThread; + + DebugOnly mRegisteredWithQuotaManager; + +public: + DatabaseOfflineStorage(QuotaClient* aQuotaClient, + const OptionalWindowId& aOptionalWindowId, + const OptionalWindowId& aOptionalContentParentId, + const nsACString& aGroup, + const nsACString& aOrigin, + const nsACString& aId, + PersistenceType aPersistenceType, + nsIEventTarget* aOwningThread); + + static void + UnregisterOnOwningThread( + already_AddRefed aOfflineStorage); + + void + SetDatabase(Database* aDatabase) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aDatabase); + MOZ_ASSERT(!mDatabase); + + mDatabase = aDatabase; + } + + void + NoteNewTransaction() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionCount < UINT32_MAX); + + mTransactionCount++; + } + + void + NoteFinishedTransaction() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionCount); + + mTransactionCount--; + } + + bool + HasOpenTransactions() const + { + MOZ_ASSERT(NS_IsMainThread()); + + // XXX This is racy, is this correct? + return !!mTransactionCount; + } + + nsIEventTarget* + OwningThread() const + { + return mOwningThread; + } + + void + NoteRegisteredWithQuotaManager() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mRegisteredWithQuotaManager); + + mRegisteredWithQuotaManager = true; + } + + void + CloseOnOwningThread(); + + NS_DECL_THREADSAFE_ISUPPORTS + +private: + ~DatabaseOfflineStorage() + { + MOZ_ASSERT(!mDatabase); + MOZ_ASSERT(!mRegisteredWithQuotaManager); + } + + void + CloseOnMainThread(); + + void + InvalidateOnMainThread(); + + void + InvalidateOnOwningThread(); + + void + UnregisterOnMainThread(); + + NS_DECL_NSIOFFLINESTORAGE +}; + +#ifdef DEBUG + +class DEBUGThreadSlower MOZ_FINAL + : public nsIThreadObserver +{ +public: + DEBUGThreadSlower() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(kDEBUGThreadSleepMS); + } + + NS_DECL_ISUPPORTS + +private: + ~DEBUGThreadSlower() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(kDEBUGThreadSleepMS); + } + + NS_DECL_NSITHREADOBSERVER +}; + +#endif // DEBUG + +/******************************************************************************* + * Helper Functions + ******************************************************************************/ + +bool +TokenizerIgnoreNothing(char16_t /* aChar */) +{ + return false; +} + +nsresult +ConvertFileIdsToArray(const nsAString& aFileIds, + nsTArray& aResult) +{ + nsCharSeparatedTokenizerTemplate + tokenizer(aFileIds, ' '); + + nsAutoString token; + nsresult rv; + + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + MOZ_ASSERT(!token.IsEmpty()); + + int32_t id = token.ToInteger(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + aResult.AppendElement(id); + } + + return NS_OK; +} + +bool +GetDatabaseBaseFilename(const nsAString& aFilename, + nsAString& aDatabaseBaseFilename) +{ + MOZ_ASSERT(!aFilename.IsEmpty()); + + NS_NAMED_LITERAL_STRING(sqlite, ".sqlite"); + + if (!StringEndsWith(aFilename, sqlite)) { + return false; + } + + aDatabaseBaseFilename = + Substring(aFilename, 0, aFilename.Length() - sqlite.Length()); + + return true; +} + +nsresult +ConvertBlobsToActors(PBackgroundParent* aBackgroundActor, + FileManager* aFileManager, + const nsTArray& aFiles, + FallibleTArray& aActors, + FallibleTArray& aFileInfos) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aActors.IsEmpty()); + MOZ_ASSERT(aFileInfos.IsEmpty()); + + if (aFiles.IsEmpty()) { + return NS_OK; + } + + nsCOMPtr directory = aFileManager->GetDirectory(); + if (NS_WARN_IF(!directory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + DebugOnly exists; + MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists))); + MOZ_ASSERT(exists); + + DebugOnly isDirectory; + MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory))); + MOZ_ASSERT(isDirectory); + + const uint32_t count = aFiles.Length(); + + if (NS_WARN_IF(!aActors.SetCapacity(count))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + const bool collectFileInfos = + !BackgroundParent::IsOtherProcessActor(aBackgroundActor); + + if (collectFileInfos && NS_WARN_IF(!aFileInfos.SetCapacity(count))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t index = 0; index < count; index++) { + const StructuredCloneFile& file = aFiles[index]; + + const int64_t fileId = file.mFileInfo->Id(); + MOZ_ASSERT(fileId > 0); + + nsCOMPtr nativeFile = + aFileManager->GetFileForId(directory, fileId); + if (NS_WARN_IF(!nativeFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + MOZ_ASSERT(NS_SUCCEEDED(nativeFile->Exists(&exists))); + MOZ_ASSERT(exists); + + DebugOnly isFile; + MOZ_ASSERT(NS_SUCCEEDED(nativeFile->IsFile(&isFile))); + MOZ_ASSERT(isFile); + + nsRefPtr impl = + new NonMainThreadHackBlob(nativeFile, file.mFileInfo); + + PBlobParent* actor = + BackgroundParent::GetOrCreateActorForBlobImpl(aBackgroundActor, impl); + if (!actor) { + // This can only fail if the child has crashed. + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + MOZ_ALWAYS_TRUE(aActors.AppendElement(actor)); + + if (collectFileInfos) { + nsRefPtr fileInfo = file.mFileInfo; + + // Transfer a reference to the receiver. + auto transferedFileInfo = + reinterpret_cast(fileInfo.forget().take()); + MOZ_ALWAYS_TRUE(aFileInfos.AppendElement(transferedFileInfo)); + } + } + + return NS_OK; +} + +/******************************************************************************* + * Globals + ******************************************************************************/ + +// Maps a database id to information about live database actors. +typedef nsClassHashtable + DatabaseActorHashtable; + +StaticAutoPtr gLiveDatabaseHashtable; + +StaticRefPtr gStartTransactionRunnable; + +TransactionThreadPool* gCurrentTransactionThreadPool = nullptr; + +#ifdef DEBUG + +StaticRefPtr gDEBUGThreadSlower; + +#endif // DEBUG + +} // anonymous namespace + +/******************************************************************************* + * Exported functions + ******************************************************************************/ + +PBackgroundIDBFactoryParent* +AllocPBackgroundIDBFactoryParent(PBackgroundParent* aManager, + const OptionalWindowId& aOptionalWindowId) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aOptionalWindowId.type() != OptionalWindowId::T__None); + + if (BackgroundParent::IsOtherProcessActor(aManager)) { + if (NS_WARN_IF(aOptionalWindowId.type() != OptionalWindowId::Tvoid_t)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + } + + nsRefPtr actor = Factory::Create(aOptionalWindowId); + return actor.forget().take(); +} + +bool +RecvPBackgroundIDBFactoryConstructor(PBackgroundParent* /* aManager */, + PBackgroundIDBFactoryParent* aActor, + const OptionalWindowId& aOptionalWindowId) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return true; +} + +bool +DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr actor = dont_AddRef(static_cast(aActor)); + return true; +} + +PIndexedDBPermissionRequestParent* +AllocPIndexedDBPermissionRequestParent(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr actor = + new PermissionRequestHelper(aWindow, aPrincipal); + return actor.forget().take(); +} + +bool +RecvPIndexedDBPermissionRequestConstructor( + PIndexedDBPermissionRequestParent* aActor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + auto* actor = static_cast(aActor); + + PermissionRequestBase::PermissionValue permission; + nsresult rv = actor->PromptIfNeeded(&permission); + if (NS_FAILED(rv)) { + return false; + } + + if (permission != PermissionRequestBase::kPermissionPrompt) { + unused << + PIndexedDBPermissionRequestParent::Send__delete__(actor, permission); + } + + return true; +} + +bool +DeallocPIndexedDBPermissionRequestParent( + PIndexedDBPermissionRequestParent* aActor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + nsRefPtr actor = + dont_AddRef(static_cast(aActor)); + return true; +} + +already_AddRefed +CreateQuotaClient() +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr client = new QuotaClient(); + return client.forget(); +} + +/******************************************************************************* + * Metadata classes + ******************************************************************************/ + +already_AddRefed +FullDatabaseMetadata::Duplicate() const +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS IndexClosure MOZ_FINAL + { + FullObjectStoreMetadata& mNew; + + public: + IndexClosure(FullObjectStoreMetadata& aNew) + : mNew(aNew) + { } + + static PLDHashOperator + Copy(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* closure = static_cast(aClosure); + + nsRefPtr newMetadata = new FullIndexMetadata(); + + newMetadata->mCommonMetadata = aValue->mCommonMetadata; + + if (NS_WARN_IF(!closure->mNew.mIndexes.Put(aKey, newMetadata, + fallible))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } + }; + + class MOZ_STACK_CLASS ObjectStoreClosure MOZ_FINAL + { + FullDatabaseMetadata& mNew; + + public: + ObjectStoreClosure(FullDatabaseMetadata& aNew) + : mNew(aNew) + { } + + static PLDHashOperator + Copy(const uint64_t& aKey, FullObjectStoreMetadata* aValue, void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* objClosure = static_cast(aClosure); + + nsRefPtr newMetadata = + new FullObjectStoreMetadata(); + + newMetadata->mCommonMetadata = aValue->mCommonMetadata; + newMetadata->mNextAutoIncrementId = aValue->mNextAutoIncrementId; + newMetadata->mComittedAutoIncrementId = aValue->mComittedAutoIncrementId; + + IndexClosure idxClosure(*newMetadata); + aValue->mIndexes.EnumerateRead(IndexClosure::Copy, &idxClosure); + + if (NS_WARN_IF(aValue->mIndexes.Count() != + newMetadata->mIndexes.Count())) { + return PL_DHASH_STOP; + } + + if (NS_WARN_IF(!objClosure->mNew.mObjectStores.Put(aKey, newMetadata, + fallible))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } + }; + + // FullDatabaseMetadata contains two hash tables of pointers that we need to + // duplicate so we can't just use the copy constructor. + nsRefPtr newMetadata = + new FullDatabaseMetadata(mCommonMetadata); + + newMetadata->mDatabaseId = mDatabaseId; + newMetadata->mFilePath = mFilePath; + newMetadata->mNextObjectStoreId = mNextObjectStoreId; + newMetadata->mNextIndexId = mNextIndexId; + + ObjectStoreClosure closure(*newMetadata); + mObjectStores.EnumerateRead(ObjectStoreClosure::Copy, &closure); + + if (NS_WARN_IF(mObjectStores.Count() != + newMetadata->mObjectStores.Count())) { + return nullptr; + } + + return newMetadata.forget(); +} + +/******************************************************************************* + * Factory + ******************************************************************************/ + +uint64_t Factory::sFactoryInstanceCount = 0; + +Factory::Factory(const OptionalWindowId& aOptionalWindowId) + : mTransactionThreadPool(gCurrentTransactionThreadPool) + , mOptionalWindowId(aOptionalWindowId) + , mActorDestroyed(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransactionThreadPool); +} + +Factory::~Factory() +{ + MOZ_ASSERT(mActorDestroyed); + MOZ_ASSERT(!mTransactionThreadPool); +} + +// static +already_AddRefed +Factory::Create(const OptionalWindowId& aOptionalWindowId) +{ + AssertIsOnBackgroundThread(); + + nsRefPtr kungFuDeathGrip; + + // If this is the first instance then we need to do some initialization. + if (!sFactoryInstanceCount) { + MOZ_ASSERT(!gCurrentTransactionThreadPool); + + nsRefPtr threadPool = + TransactionThreadPool::Create(); + if (NS_WARN_IF(!threadPool)) { + return nullptr; + } + + gCurrentTransactionThreadPool = threadPool; + + // Hold this alive until the Factory constructor runs. + kungFuDeathGrip.swap(threadPool); + + MOZ_ASSERT(!gLiveDatabaseHashtable); + gLiveDatabaseHashtable = new DatabaseActorHashtable(); + + MOZ_ASSERT(!gStartTransactionRunnable); + gStartTransactionRunnable = new nsRunnable(); + +#ifdef DEBUG + if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { + NS_WARNING("PBackground thread debugging enabled, priority has been " + "modified!"); + nsCOMPtr thread = + do_QueryInterface(NS_GetCurrentThread()); + MOZ_ASSERT(thread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->SetPriority(kDEBUGThreadPriority))); + } + + if (kDEBUGThreadSleepMS) { + NS_WARNING("PBackground thread debugging enabled, sleeping after every " + "event!"); + nsCOMPtr thread = + do_QueryInterface(NS_GetCurrentThread()); + MOZ_ASSERT(thread); + + gDEBUGThreadSlower = new DEBUGThreadSlower(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AddObserver(gDEBUGThreadSlower))); + } +#endif // DEBUG + } + + nsRefPtr actor = new Factory(aOptionalWindowId); + + sFactoryInstanceCount++; + + return actor.forget(); +} + +void +Factory::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + // Make sure this is released on this thread, but keep it alive past the call + // to TransactionThreadPool::ShutdownAsync() below. + nsRefPtr kungFuDeathGrip; + mTransactionThreadPool.swap(kungFuDeathGrip); + + // Clean up if there are no more instances. + if (!(--sFactoryInstanceCount)) { + if (gCurrentTransactionThreadPool) { + MOZ_ASSERT(gCurrentTransactionThreadPool == kungFuDeathGrip); + + gCurrentTransactionThreadPool->ShutdownAsync(); + + QuotaClient::GetInstance()->NoteDyingTransactionThreadPool(); + MOZ_ASSERT(!gCurrentTransactionThreadPool); + } + + MOZ_ASSERT(gStartTransactionRunnable); + gStartTransactionRunnable = nullptr; + + MOZ_ASSERT(gLiveDatabaseHashtable); + MOZ_ASSERT(!gLiveDatabaseHashtable->Count()); + gLiveDatabaseHashtable = nullptr; + +#ifdef DEBUG + if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { + nsCOMPtr thread = + do_QueryInterface(NS_GetCurrentThread()); + MOZ_ASSERT(thread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))); + } + + if (kDEBUGThreadSleepMS) { + MOZ_ASSERT(gDEBUGThreadSlower); + + nsCOMPtr thread = + do_QueryInterface(NS_GetCurrentThread()); + MOZ_ASSERT(thread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->RemoveObserver(gDEBUGThreadSlower))); + + gDEBUGThreadSlower = nullptr; + } +#endif // DEBUG + } +} + +bool +Factory::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + return PBackgroundIDBFactoryParent::Send__delete__(this); +} + +PBackgroundIDBFactoryRequestParent* +Factory::AllocPBackgroundIDBFactoryRequestParent( + const FactoryRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); + + const CommonFactoryRequestParams* commonParams; + + switch (aParams.type()) { + case FactoryRequestParams::TOpenDatabaseRequestParams: { + const OpenDatabaseRequestParams& params = + aParams.get_OpenDatabaseRequestParams(); + commonParams = ¶ms.commonParams(); + break; + } + + case FactoryRequestParams::TDeleteDatabaseRequestParams: { + const DeleteDatabaseRequestParams& params = + aParams.get_DeleteDatabaseRequestParams(); + commonParams = ¶ms.commonParams(); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(commonParams); + + const DatabaseMetadata& metadata = commonParams->metadata(); + if (NS_WARN_IF(metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT && + metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + const PrincipalInfo& principalInfo = commonParams->principalInfo(); + if (NS_WARN_IF(principalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsRefPtr contentParent = + BackgroundParent::GetContentParent(Manager()); + + nsRefPtr actor; + if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) { + actor = new OpenDatabaseOp(this, + contentParent.forget(), + mOptionalWindowId, + *commonParams); + } else { + actor = new DeleteDatabaseOp(this, contentParent.forget(), *commonParams); + } + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +Factory::RecvPBackgroundIDBFactoryRequestConstructor( + PBackgroundIDBFactoryRequestParent* aActor, + const FactoryRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); + + auto* op = static_cast(aActor); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(op))); + return true; +} + +bool +Factory::DeallocPBackgroundIDBFactoryRequestParent( + PBackgroundIDBFactoryRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + nsRefPtr op = dont_AddRef(static_cast(aActor)); + return true; +} + +PBackgroundIDBDatabaseParent* +Factory::AllocPBackgroundIDBDatabaseParent( + const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestParent* aRequest) +{ + MOZ_CRASH("PBackgroundIDBDatabaseParent actors should be constructed " + "manually!"); +} + +bool +Factory::DeallocPBackgroundIDBDatabaseParent( + PBackgroundIDBDatabaseParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr database = dont_AddRef(static_cast(aActor)); + return true; +} + +/******************************************************************************* + * Database + ******************************************************************************/ + +Database::Database(Factory* aFactory, + const PrincipalInfo& aPrincipalInfo, + const nsACString& aGroup, + const nsACString& aOrigin, + FullDatabaseMetadata* aMetadata, + FileManager* aFileManager, + already_AddRefed aOfflineStorage, + bool aChromeWriteAccessAllowed) + : mFactory(aFactory) + , mTransactionThreadPool(aFactory->GetTransactionThreadPool()) + , mMetadata(aMetadata) + , mFileManager(aFileManager) + , mOfflineStorage(Move(aOfflineStorage)) + , mPrincipalInfo(aPrincipalInfo) + , mGroup(aGroup) + , mOrigin(aOrigin) + , mId(aMetadata->mDatabaseId) + , mFilePath(aMetadata->mFilePath) + , mPersistenceType(aMetadata->mCommonMetadata.persistenceType()) + , mChromeWriteAccessAllowed(aChromeWriteAccessAllowed) + , mClosed(false) + , mInvalidated(false) + , mActorWasAlive(false) + , mActorDestroyed(false) + , mMetadataCleanedUp(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFactory); + MOZ_ASSERT(mTransactionThreadPool); + MOZ_ASSERT(aMetadata); + MOZ_ASSERT(aFileManager); + MOZ_ASSERT_IF(aChromeWriteAccessAllowed, + aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo); + + mOfflineStorage->SetDatabase(this); +} + +void +Database::Invalidate() +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static bool + InvalidateTransactions(nsTHashtable>& aTable) + { + AssertIsOnBackgroundThread(); + + const uint32_t count = aTable.Count(); + if (!count) { + return true; + } + + FallibleTArray> transactions; + if (NS_WARN_IF(!transactions.SetCapacity(count))) { + return false; + } + + aTable.EnumerateEntries(Collect, &transactions); + + if (NS_WARN_IF(transactions.Length() != count)) { + return false; + } + + if (count) { + IDB_REPORT_INTERNAL_ERR(); + + for (uint32_t index = 0; index < count; index++) { + nsRefPtr transaction = transactions[index].forget(); + MOZ_ASSERT(transaction); + + transaction->Invalidate(); + } + } + + return true; + } + + private: + static PLDHashOperator + Collect(nsPtrHashKey* aEntry, void* aUserData) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aUserData); + + auto* array = + static_cast>*>(aUserData); + + if (NS_WARN_IF(!array->AppendElement(aEntry->GetKey()))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } + }; + + if (mInvalidated) { + return; + } + + mInvalidated = true; + + if (!mActorDestroyed) { + unused << SendInvalidate(); + } + + if (!Helper::InvalidateTransactions(mTransactions)) { + NS_WARNING("Failed to abort all transactions!"); + } + + MOZ_ALWAYS_TRUE(CloseInternal()); + + CleanupMetadata(); +} + +bool +Database::RegisterTransaction(TransactionBase* aTransaction) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(!mTransactions.GetEntry(aTransaction)); + MOZ_ASSERT(mOfflineStorage); + + if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) { + return false; + } + + mOfflineStorage->NoteNewTransaction(); + return true; +} + +void +Database::UnregisterTransaction(TransactionBase* aTransaction) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(mTransactions.GetEntry(aTransaction)); + + mTransactions.RemoveEntry(aTransaction); + + if (mOfflineStorage) { + mOfflineStorage->NoteFinishedTransaction(); + + if (!mTransactions.Count() && IsClosed()) { + DatabaseOfflineStorage::UnregisterOnOwningThread( + mOfflineStorage.forget()); + CleanupMetadata(); + } + } +} + +void +Database::SetActorAlive() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorWasAlive); + MOZ_ASSERT(!mActorDestroyed); + + mActorWasAlive = true; + + // This reference will be absorbed by IPDL and released when the actor is + // destroyed. + AddRef(); +} + +bool +Database::CloseInternal() +{ + AssertIsOnBackgroundThread(); + + if (mClosed) { + if (NS_WARN_IF(!IsInvalidated())) { + // Kill misbehaving child for sending the close message twice. + return false; + } + + // Ignore harmless race when we just invalidated the database. + return true; + } + + mClosed = true; + + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); + + MOZ_ASSERT(info->mLiveDatabases.Contains(this)); + + if (info->mWaitingFactoryOp) { + info->mWaitingFactoryOp->NoteDatabaseClosed(this); + } + + if (mOfflineStorage) { + mOfflineStorage->CloseOnOwningThread(); + + if (!mTransactions.Count()) { + DatabaseOfflineStorage::UnregisterOnOwningThread( + mOfflineStorage.forget()); + CleanupMetadata(); + } + } + + return true; +} + +void +Database::CleanupMetadata() +{ + AssertIsOnBackgroundThread(); + + if (!mMetadataCleanedUp) { + mMetadataCleanedUp = true; + + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); + MOZ_ALWAYS_TRUE(info->mLiveDatabases.RemoveElement(this)); + + if (info->mLiveDatabases.IsEmpty()) { + MOZ_ASSERT(!info->mWaitingFactoryOp || + !info->mWaitingFactoryOp->HasBlockedDatabases()); + gLiveDatabaseHashtable->Remove(Id()); + } + } +} + +void +Database::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + // Make sure this is released on this thread. + mTransactionThreadPool = nullptr; + + if (!IsInvalidated()) { + Invalidate(); + } +} + +PBackgroundIDBDatabaseFileParent* +Database::AllocPBackgroundIDBDatabaseFileParent( + const BlobOrInputStream& aBlobOrInputStream) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aBlobOrInputStream.type() != BlobOrInputStream::T__None); + + nsRefPtr actor; + + switch (aBlobOrInputStream.type()) { + case BlobOrInputStream::TInputStreamParams: { + const InputStreamParams& inputStreamParams = + aBlobOrInputStream.get_InputStreamParams(); + MOZ_ASSERT(inputStreamParams.type() != InputStreamParams::T__None); + + nsRefPtr fileInfo = mFileManager->GetNewFileInfo(); + MOZ_ASSERT(fileInfo); + + actor = new DatabaseFile(inputStreamParams, fileInfo); + break; + } + + case BlobOrInputStream::TPBlobParent: { + auto* blobParent = + static_cast(aBlobOrInputStream.get_PBlobParent()); + if (NS_WARN_IF(!blobParent)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsRefPtr blobImpl = blobParent->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsRefPtr fileInfo = blobImpl->GetFileInfo(mFileManager); + MOZ_ASSERT(fileInfo); + + actor = new DatabaseFile(fileInfo); + break; + } + + case BlobOrInputStream::TPBlobChild: { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(actor); + + return actor.forget().take(); +} + +bool +Database::DeallocPBackgroundIDBDatabaseFileParent( + PBackgroundIDBDatabaseFileParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr actor = + dont_AddRef(static_cast(aActor)); + return true; +} + +PBackgroundIDBTransactionParent* +Database::AllocPBackgroundIDBTransactionParent( + const nsTArray& aObjectStoreNames, + const Mode& aMode) +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS Closure MOZ_FINAL + { + const nsString& mName; + FallibleTArray>& mObjectStores; + + public: + Closure(const nsString& aName, + FallibleTArray>& aObjectStores) + : mName(aName) + , mObjectStores(aObjectStores) + { } + + static PLDHashOperator + Find(const uint64_t& aKey, + FullObjectStoreMetadata* aValue, + void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* closure = static_cast(aClosure); + + if (closure->mName == aValue->mCommonMetadata.name() && + !aValue->mDeleted) { + MOZ_ALWAYS_TRUE(closure->mObjectStores.AppendElement(aValue)); + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } + }; + + // Once a database is closed it must not try to open new transactions. + if (NS_WARN_IF(mClosed)) { + if (!mInvalidated) { + ASSERT_UNLESS_FUZZING(); + } + return nullptr; + } + + if (NS_WARN_IF(aObjectStoreNames.IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(aMode != IDBTransaction::READ_ONLY && + aMode != IDBTransaction::READ_WRITE)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + // If this is a readwrite transaction to a chrome database make sure the child + // has write access. + if (NS_WARN_IF(aMode == IDBTransaction::READ_WRITE && + mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo && + !mChromeWriteAccessAllowed)) { + return nullptr; + } + + const ObjectStoreTable& objectStores = mMetadata->mObjectStores; + const uint32_t nameCount = aObjectStoreNames.Length(); + + if (NS_WARN_IF(nameCount > objectStores.Count())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + FallibleTArray> fallibleObjectStores; + if (NS_WARN_IF(!fallibleObjectStores.SetCapacity(nameCount))) { + return nullptr; + } + + for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) { + const nsString& name = aObjectStoreNames[nameIndex]; + const uint32_t oldLength = fallibleObjectStores.Length(); + + Closure closure(name, fallibleObjectStores); + objectStores.EnumerateRead(Closure::Find, &closure); + + if (NS_WARN_IF((oldLength + 1) != fallibleObjectStores.Length())) { + return nullptr; + } + } + + nsTArray> infallibleObjectStores; + infallibleObjectStores.SwapElements(fallibleObjectStores); + + nsRefPtr transaction = + new NormalTransaction(this, infallibleObjectStores, aMode); + + MOZ_ASSERT(infallibleObjectStores.IsEmpty()); + + return transaction.forget().take(); +} + +bool +Database::RecvPBackgroundIDBTransactionConstructor( + PBackgroundIDBTransactionParent* aActor, + const nsTArray& aObjectStoreNames, + const Mode& aMode) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(!aObjectStoreNames.IsEmpty()); + MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY || + aMode == IDBTransaction::READ_WRITE); + MOZ_ASSERT(!mClosed); + + if (IsInvalidated()) { + // This is an expected race. We don't want the child to die here, just don't + // actually do any work. + return true; + } + + auto* transaction = static_cast(aActor); + + TransactionThreadPool* threadPool = GetTransactionThreadPool(); + MOZ_ASSERT(threadPool); + + // Add a placeholder for this transaction immediately. + threadPool->Dispatch(transaction->TransactionId(), + mMetadata->mDatabaseId, + aObjectStoreNames, + aMode, + gStartTransactionRunnable, + /* aFinish */ false, + /* aFinishCallback */ nullptr); + + transaction->SetActive(); + + if (NS_WARN_IF(!RegisterTransaction(transaction))) { + IDB_REPORT_INTERNAL_ERR(); + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false); + return true; + } + + return true; +} + +bool +Database::DeallocPBackgroundIDBTransactionParent( + PBackgroundIDBTransactionParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr transaction = + dont_AddRef(static_cast(aActor)); + return true; +} + +PBackgroundIDBVersionChangeTransactionParent* +Database::AllocPBackgroundIDBVersionChangeTransactionParent( + const uint64_t& aCurrentVersion, + const uint64_t& aRequestedVersion, + const int64_t& aNextObjectStoreId, + const int64_t& aNextIndexId) +{ + MOZ_CRASH("PBackgroundIDBVersionChangeTransactionParent actors should be " + "constructed manually!"); +} + +bool +Database::DeallocPBackgroundIDBVersionChangeTransactionParent( + PBackgroundIDBVersionChangeTransactionParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr transaction = + dont_AddRef(static_cast(aActor)); + return true; +} + +bool +Database::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + return PBackgroundIDBDatabaseParent::Send__delete__(this); +} + +bool +Database::RecvBlocked() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mClosed)) { + return false; + } + + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info)); + + MOZ_ASSERT(info->mLiveDatabases.Contains(this)); + MOZ_ASSERT(info->mWaitingFactoryOp); + + info->mWaitingFactoryOp->NoteDatabaseBlocked(this); + + return true; +} + +bool +Database::RecvClose() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!CloseInternal())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + return true; +} + +/******************************************************************************* + * DatabaseFile + ******************************************************************************/ + +already_AddRefed +DatabaseFile::GetInputStream() const +{ + if (mInputStreamParams.type() == InputStreamParams::T__None) { + return nullptr; + } + + nsTArray fileDescriptors; + nsCOMPtr inputStream = + DeserializeInputStream(mInputStreamParams, fileDescriptors); + + if (NS_WARN_IF(!inputStream)) { + return nullptr; + } + + MOZ_ASSERT(fileDescriptors.IsEmpty()); + + return inputStream.forget(); +} + +void +DatabaseFile::ClearInputStreamParams() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mInputStreamParams.type() != InputStreamParams::T__None); + + mInputStreamParams = InputStreamParams(); +} + +void +DatabaseFile::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + + mFileInfo = nullptr; + mInputStreamParams = InputStreamParams(); +} + +/******************************************************************************* + * TransactionBase + ******************************************************************************/ + +TransactionBase::TransactionBase(Database* aDatabase, + Mode aMode) + : mDatabase(aDatabase) + , mTransactionThreadPool(aDatabase->GetTransactionThreadPool()) + , mTransactionId(mTransactionThreadPool->NextTransactionId()) + , mDatabaseId(aDatabase->Id()) + , mActiveRequestCount(0) + , mInvalidatedOnAnyThread(false) + , mMode(aMode) + , mHasBeenActive(false) + , mActorDestroyed(false) + , mInvalidated(false) + , mResultCode(NS_OK) + , mCommitOrAbortReceived(false) + , mCommittedOrAborted(false) + , mForceAborted(false) + , mTransactionThread(nullptr) + , mSavepointCount(0) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aDatabase); + MOZ_ASSERT(mTransactionThreadPool); +} + +TransactionBase::~TransactionBase() +{ + MOZ_ASSERT(!mSavepointCount); + MOZ_ASSERT(!mActiveRequestCount); + MOZ_ASSERT(mActorDestroyed); + MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted); +} + +nsresult +TransactionBase::EnsureConnection() +{ +#ifdef DEBUG + MOZ_ASSERT(!IsOnBackgroundThread()); + if (!mTransactionThread) { + mTransactionThread = PR_GetCurrentThread(); + } +#endif + + AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "TransactionBase::EnsureConnection", + js::ProfileEntry::Category::STORAGE); + + if (!mConnection) { + nsCOMPtr connection; + nsresult rv = + GetDatabaseConnection(mDatabase->FilePath(), mDatabase->Type(), + mDatabase->Group(), mDatabase->Origin(), + getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsRefPtr function; + nsCString beginTransaction; + + if (mMode == IDBTransaction::READ_ONLY) { + beginTransaction.AssignLiteral("BEGIN TRANSACTION;"); + } else { + function = new UpdateRefcountFunction(mDatabase->GetFileManager()); + + rv = connection->CreateFunction(NS_LITERAL_CSTRING("update_refcount"), 2, + function); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;"); + } + + nsCOMPtr stmt; + rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + function.swap(mUpdateFileRefcountFunction); + connection.swap(mConnection); + } + + return NS_OK; +} + +void +TransactionBase::Abort(nsresult aResultCode, bool aForce) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(NS_FAILED(aResultCode)); + + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = aResultCode; + } + + if (aForce) { + mForceAborted = true; + } + + MaybeCommitOrAbort(); +} + +bool +TransactionBase::RecvCommit() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mCommitOrAbortReceived = true; + + MaybeCommitOrAbort(); + return true; +} + +bool +TransactionBase::RecvAbort(nsresult aResultCode) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(NS_SUCCEEDED(aResultCode))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode) != + NS_ERROR_MODULE_DOM_INDEXEDDB)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mCommitOrAbortReceived = true; + + Abort(aResultCode, /* aForce */ false); + return true; +} + +void +TransactionBase::CommitOrAbort() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mCommittedOrAborted); + + mCommittedOrAborted = true; + + if (!mHasBeenActive) { + return; + } + + nsRefPtr commitOp = + new CommitOp(this, ClampResultCode(mResultCode)); + + TransactionThreadPool* threadPool = GetTransactionThreadPool(); + MOZ_ASSERT(threadPool); + + threadPool->Dispatch(TransactionId(), + DatabaseId(), + commitOp, + /* aFinish */ true, + /* aFinishCallback */ commitOp); +} + +already_AddRefed +TransactionBase::GetMetadataForObjectStoreId(int64_t aObjectStoreId) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aObjectStoreId); + + if (!aObjectStoreId) { + return nullptr; + } + + nsRefPtr metadata; + if (!mDatabase->Metadata()->mObjectStores.Get(aObjectStoreId, + getter_AddRefs(metadata))) { + return nullptr; + } + + MOZ_ASSERT(metadata->mCommonMetadata.id() == aObjectStoreId); + MOZ_ASSERT(!metadata->mDeleted); + + return metadata.forget(); +} + +already_AddRefed +TransactionBase::GetMetadataForIndexId( + FullObjectStoreMetadata* const aObjectStoreMetadata, + int64_t aIndexId) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aIndexId); + + if (!aIndexId) { + return nullptr; + } + + nsRefPtr metadata; + if (!aObjectStoreMetadata->mIndexes.Get(aIndexId, getter_AddRefs(metadata))) { + return nullptr; + } + + MOZ_ASSERT(metadata->mCommonMetadata.id() == aIndexId); + MOZ_ASSERT(!metadata->mDeleted); + + return metadata.forget(); +} + +void +TransactionBase::NoteModifiedAutoIncrementObjectStore( + FullObjectStoreMetadata* aMetadata) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aMetadata); + + if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) { + mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(aMetadata); + } +} + +void +TransactionBase::ForgetModifiedAutoIncrementObjectStore( + FullObjectStoreMetadata* aMetadata) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aMetadata); + + mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(aMetadata); +} + +bool +TransactionBase::VerifyRequestParams(const RequestParams& aParams) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + switch (aParams.type()) { + case RequestParams::TObjectStoreAddParams: { + const ObjectStoreAddPutParams& params = + aParams.get_ObjectStoreAddParams().commonParams(); + if (NS_WARN_IF(!VerifyRequestParams(params))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStorePutParams: { + const ObjectStoreAddPutParams& params = + aParams.get_ObjectStorePutParams().commonParams(); + if (NS_WARN_IF(!VerifyRequestParams(params))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreGetParams: { + const ObjectStoreGetParams& params = aParams.get_ObjectStoreGetParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreGetAllParams: { + const ObjectStoreGetAllParams& params = + aParams.get_ObjectStoreGetAllParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreGetAllKeysParams: { + const ObjectStoreGetAllKeysParams& params = + aParams.get_ObjectStoreGetAllKeysParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreDeleteParams: { + const ObjectStoreDeleteParams& params = + aParams.get_ObjectStoreDeleteParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreClearParams: { + const ObjectStoreClearParams& params = + aParams.get_ObjectStoreClearParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TObjectStoreCountParams: { + const ObjectStoreCountParams& params = + aParams.get_ObjectStoreCountParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + + case RequestParams::TIndexGetParams: { + const IndexGetParams& params = aParams.get_IndexGetParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + const nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TIndexGetKeyParams: { + const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + const nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.keyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TIndexGetAllParams: { + const IndexGetAllParams& params = aParams.get_IndexGetAllParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + const nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TIndexGetAllKeysParams: { + const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + const nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case RequestParams::TIndexCountParams: { + const IndexCountParams& params = aParams.get_IndexCountParams(); + const nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + const nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +bool +TransactionBase::VerifyRequestParams(const OpenCursorParams& aParams) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); + + switch (aParams.type()) { + case OpenCursorParams::TObjectStoreOpenCursorParams: { + const ObjectStoreOpenCursorParams& params = + aParams.get_ObjectStoreOpenCursorParams(); + nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { + const ObjectStoreOpenKeyCursorParams& params = + aParams.get_ObjectStoreOpenKeyCursorParams(); + nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case OpenCursorParams::TIndexOpenCursorParams: { + const IndexOpenCursorParams& params = aParams.get_IndexOpenCursorParams(); + nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + case OpenCursorParams::TIndexOpenKeyCursorParams: { + const IndexOpenKeyCursorParams& params = + aParams.get_IndexOpenKeyCursorParams(); + nsRefPtr objectStoreMetadata = + GetMetadataForObjectStoreId(params.objectStoreId()); + if (NS_WARN_IF(!objectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + nsRefPtr indexMetadata = + GetMetadataForIndexId(objectStoreMetadata, params.indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +bool +TransactionBase::VerifyRequestParams(const CursorRequestParams& aParams) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); + + switch (aParams.type()) { + case CursorRequestParams::TContinueParams: + break; + + case CursorRequestParams::TAdvanceParams: + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +bool +TransactionBase::VerifyRequestParams(const SerializedKeyRange& aParams) const +{ + AssertIsOnBackgroundThread(); + + // XXX Check more here? + + if (aParams.isOnly()) { + if (NS_WARN_IF(aParams.lower().IsUnset())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(!aParams.upper().IsUnset())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(aParams.lowerOpen())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + if (NS_WARN_IF(aParams.upperOpen())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + } else if (NS_WARN_IF(aParams.lower().IsUnset() && + aParams.upper().IsUnset())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + return true; +} + +bool +TransactionBase::VerifyRequestParams(const ObjectStoreAddPutParams& aParams) + const +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mMode != IDBTransaction::READ_WRITE && + mMode != IDBTransaction::VERSION_CHANGE)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr objMetadata = + GetMetadataForObjectStoreId(aParams.objectStoreId()); + if (NS_WARN_IF(!objMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aParams.cloneInfo().data().IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (objMetadata->mCommonMetadata.autoIncrement() && + objMetadata->mCommonMetadata.keyPath().IsValid() && + aParams.key().IsUnset()) { + const SerializedStructuredCloneWriteInfo cloneInfo = aParams.cloneInfo(); + + if (NS_WARN_IF(!cloneInfo.offsetToKeyProp())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(cloneInfo.data().Length() < sizeof(uint64_t))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(cloneInfo.offsetToKeyProp() > + (cloneInfo.data().Length() - sizeof(uint64_t)))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + } else if (NS_WARN_IF(aParams.cloneInfo().offsetToKeyProp())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const nsTArray& updates = aParams.indexUpdateInfos(); + + for (uint32_t index = 0; index < updates.Length(); index++) { + nsRefPtr indexMetadata = + GetMetadataForIndexId(objMetadata, updates[index].indexId()); + if (NS_WARN_IF(!indexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(updates[index].value().IsUnset())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + } + + const nsTArray& files = aParams.files(); + + for (uint32_t index = 0; index < files.Length(); index++) { + const DatabaseFileOrMutableFileId& fileOrFileId = files[index]; + + MOZ_ASSERT(fileOrFileId.type() != DatabaseFileOrMutableFileId::T__None); + + switch (fileOrFileId.type()) { + case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileChild: + ASSERT_UNLESS_FUZZING(); + return false; + + case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileParent: + if (NS_WARN_IF(!fileOrFileId.get_PBackgroundIDBDatabaseFileParent())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + + case DatabaseFileOrMutableFileId::Tint64_t: + if (NS_WARN_IF(fileOrFileId.get_int64_t() <= 0)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + + default: + MOZ_CRASH("Should never get here!"); + } + } + + return true; +} + +bool +TransactionBase::VerifyRequestParams(const OptionalKeyRange& aParams) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != OptionalKeyRange::T__None); + + switch (aParams.type()) { + case OptionalKeyRange::TSerializedKeyRange: + if (NS_WARN_IF(!VerifyRequestParams(aParams.get_SerializedKeyRange()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + + case OptionalKeyRange::Tvoid_t: + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +nsresult +TransactionBase::StartSavepoint() +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(mConnection); + MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || + IDBTransaction::VERSION_CHANGE == mMode); + + CachedStatement stmt; + nsresult rv = GetCachedStatement(kSavepointClause, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mUpdateFileRefcountFunction->StartSavepoint(); + + mSavepointCount++; + + return NS_OK; +} + +nsresult +TransactionBase::ReleaseSavepoint() +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(mConnection); + MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || + IDBTransaction::VERSION_CHANGE == mMode); + MOZ_ASSERT(mSavepointCount); + + mSavepointCount--; + + CachedStatement stmt; + nsresult rv = GetCachedStatement( + NS_LITERAL_CSTRING("RELEASE ") + NS_LITERAL_CSTRING(kSavepointClause), + &stmt); + if (NS_SUCCEEDED(rv)) { + rv = stmt->Execute(); + if (NS_SUCCEEDED(rv)) { + mUpdateFileRefcountFunction->ReleaseSavepoint(); + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + mUpdateFileRefcountFunction->RollbackSavepoint(); + } + + return rv; +} + +nsresult +TransactionBase::RollbackSavepoint() +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(mConnection); + MOZ_ASSERT(IDBTransaction::READ_WRITE == mMode || + IDBTransaction::VERSION_CHANGE == mMode); + MOZ_ASSERT(mSavepointCount); + + mSavepointCount--; + + mUpdateFileRefcountFunction->RollbackSavepoint(); + + CachedStatement stmt; + nsresult rv = GetCachedStatement( + NS_LITERAL_CSTRING("ROLLBACK TO ") + NS_LITERAL_CSTRING(kSavepointClause), + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // This may fail if SQLite already rolled back the savepoint so ignore any + // errors. + unused << stmt->Execute(); + + return NS_OK; +} + +void +TransactionBase::NoteActiveRequest() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mActiveRequestCount < UINT64_MAX); + + mActiveRequestCount++; +} + +void +TransactionBase::NoteFinishedRequest() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mActiveRequestCount); + + mActiveRequestCount--; + + MaybeCommitOrAbort(); +} + +void +TransactionBase::Invalidate() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread); + + if (!mInvalidated) { + mInvalidated = true; + mInvalidatedOnAnyThread = true; + + Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ true); + } +} + +PBackgroundIDBRequestParent* +TransactionBase::AllocRequest(const RequestParams& aParams, bool aTrustParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + +#ifdef DEBUG + // Always verify parameters in DEBUG builds! + aTrustParams = false; +#endif + + if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsRefPtr actor; + + switch (aParams.type()) { + case RequestParams::TObjectStoreAddParams: + case RequestParams::TObjectStorePutParams: + actor = new ObjectStoreAddOrPutRequestOp(this, aParams); + break; + + case RequestParams::TObjectStoreGetParams: + actor = + new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ false); + break; + + case RequestParams::TObjectStoreGetAllParams: + actor = + new ObjectStoreGetRequestOp(this, aParams, /* aGetAll */ true); + break; + + case RequestParams::TObjectStoreGetAllKeysParams: + actor = + new ObjectStoreGetAllKeysRequestOp(this, + aParams.get_ObjectStoreGetAllKeysParams()); + break; + + case RequestParams::TObjectStoreDeleteParams: + actor = + new ObjectStoreDeleteRequestOp(this, + aParams.get_ObjectStoreDeleteParams()); + break; + + case RequestParams::TObjectStoreClearParams: + actor = + new ObjectStoreClearRequestOp(this, + aParams.get_ObjectStoreClearParams()); + break; + + case RequestParams::TObjectStoreCountParams: + actor = + new ObjectStoreCountRequestOp(this, + aParams.get_ObjectStoreCountParams()); + break; + + case RequestParams::TIndexGetParams: + actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ false); + break; + + case RequestParams::TIndexGetKeyParams: + actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ false); + break; + + case RequestParams::TIndexGetAllParams: + actor = new IndexGetRequestOp(this, aParams, /* aGetAll */ true); + break; + + case RequestParams::TIndexGetAllKeysParams: + actor = new IndexGetKeyRequestOp(this, aParams, /* aGetAll */ true); + break; + + case RequestParams::TIndexCountParams: + actor = new IndexCountRequestOp(this, aParams); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(actor); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +TransactionBase::StartRequest(PBackgroundIDBRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + auto* op = static_cast(aActor); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->DispatchToTransactionThreadPool(); + return true; +} + +bool +TransactionBase::DeallocRequest(PBackgroundIDBRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + nsRefPtr actor = + dont_AddRef(static_cast(aActor)); + return true; +} + +PBackgroundIDBCursorParent* +TransactionBase::AllocCursor(const OpenCursorParams& aParams, bool aTrustParams) +{ + AssertIsOnBackgroundThread(); + +#ifdef DEBUG + // Always verify parameters in DEBUG builds! + aTrustParams = false; +#endif + + if (!aTrustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + OpenCursorParams::Type type = aParams.type(); + MOZ_ASSERT(type != OpenCursorParams::T__None); + + int64_t objectStoreId; + int64_t indexId; + Cursor::Direction direction; + + switch(type) { + case OpenCursorParams::TObjectStoreOpenCursorParams: { + const auto& params = aParams.get_ObjectStoreOpenCursorParams(); + objectStoreId = params.objectStoreId(); + indexId = 0; + direction = params.direction(); + break; + } + + case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { + const auto& params = aParams.get_ObjectStoreOpenKeyCursorParams(); + objectStoreId = params.objectStoreId(); + indexId = 0; + direction = params.direction(); + break; + } + + case OpenCursorParams::TIndexOpenCursorParams: { + const auto& params = aParams.get_IndexOpenCursorParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + direction = params.direction(); + break; + } + + case OpenCursorParams::TIndexOpenKeyCursorParams: { + const auto& params = aParams.get_IndexOpenKeyCursorParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + direction = params.direction(); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + nsRefPtr actor = + new Cursor(this, type, objectStoreId, indexId, direction); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +TransactionBase::StartCursor(PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); + + auto* op = static_cast(aActor); + + if (NS_WARN_IF(!op->Start(aParams))) { + return false; + } + + return true; +} + +bool +TransactionBase::DeallocCursor(PBackgroundIDBCursorParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + nsRefPtr actor = dont_AddRef(static_cast(aActor)); + return true; +} + +nsresult +TransactionBase::GetCachedStatement(const nsACString& aQuery, + CachedStatement* aCachedStatement) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(!aQuery.IsEmpty()); + MOZ_ASSERT(aCachedStatement); + MOZ_ASSERT(mConnection); + + nsCOMPtr stmt; + + if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) { + nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt)); + if (NS_FAILED(rv)) { +#ifdef DEBUG + nsCString msg; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->GetLastErrorString(msg))); + + nsAutoCString error = + NS_LITERAL_CSTRING("The statement '") + aQuery + + NS_LITERAL_CSTRING("' failed to compile with the error message '") + + msg + NS_LITERAL_CSTRING("'."); + + NS_WARNING(error.get()); +#endif + return rv; + } + + mCachedStatements.Put(aQuery, stmt); + } + + aCachedStatement->Assign(stmt.forget()); + return NS_OK; +} + +void +TransactionBase::ReleaseTransactionThreadObjects() +{ + AssertIsOnTransactionThread(); + + mCachedStatements.Clear(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->Close())); + mConnection = nullptr; +} + +void +TransactionBase::ReleaseBackgroundThreadObjects() +{ + AssertIsOnBackgroundThread(); + + if (mUpdateFileRefcountFunction) { + mUpdateFileRefcountFunction->ClearFileInfoEntries(); + mUpdateFileRefcountFunction = nullptr; + } + + mTransactionThreadPool = nullptr; +} + +/******************************************************************************* + * NormalTransaction + ******************************************************************************/ + +NormalTransaction::NormalTransaction( + Database* aDatabase, + nsTArray>& aObjectStores, + TransactionBase::Mode aMode) + : TransactionBase(aDatabase, aMode) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!aObjectStores.IsEmpty()); + + mObjectStores.SwapElements(aObjectStores); +} + +bool +NormalTransaction::IsSameProcessActor() +{ + AssertIsOnBackgroundThread(); + + PBackgroundParent* actor = Manager()->Manager()->Manager(); + MOZ_ASSERT(actor); + + return !BackgroundParent::IsOtherProcessActor(actor); +} + +bool +NormalTransaction::SendCompleteNotification(nsresult aResult) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + if (NS_WARN_IF(!SendComplete(aResult))) { + return false; + } + + return true; +} + +void +NormalTransaction::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + + if (!mCommittedOrAborted) { + if (NS_SUCCEEDED(mResultCode)) { + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mForceAborted = true; + + MaybeCommitOrAbort(); + } + + NoteActorDestroyed(); +} + +bool +NormalTransaction::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return PBackgroundIDBTransactionParent::Send__delete__(this); +} + +bool +NormalTransaction::RecvCommit() +{ + AssertIsOnBackgroundThread(); + + return TransactionBase::RecvCommit(); +} + +bool +NormalTransaction::RecvAbort(const nsresult& aResultCode) +{ + AssertIsOnBackgroundThread(); + + return TransactionBase::RecvAbort(aResultCode); +} + +PBackgroundIDBRequestParent* +NormalTransaction::AllocPBackgroundIDBRequestParent( + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + return AllocRequest(aParams, IsSameProcessActor()); +} + +bool +NormalTransaction::RecvPBackgroundIDBRequestConstructor( + PBackgroundIDBRequestParent* aActor, + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + return StartRequest(aActor); +} + +bool +NormalTransaction::DeallocPBackgroundIDBRequestParent( + PBackgroundIDBRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return DeallocRequest(aActor); +} + +PBackgroundIDBCursorParent* +NormalTransaction::AllocPBackgroundIDBCursorParent( + const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + + return AllocCursor(aParams, IsSameProcessActor()); +} + +bool +NormalTransaction::RecvPBackgroundIDBCursorConstructor( + PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); + + return StartCursor(aActor, aParams); +} + +bool +NormalTransaction::DeallocPBackgroundIDBCursorParent( + PBackgroundIDBCursorParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return DeallocCursor(aActor); +} + +/******************************************************************************* + * VersionChangeTransaction + ******************************************************************************/ + +VersionChangeTransaction::VersionChangeTransaction( + OpenDatabaseOp* aOpenDatabaseOp) + : TransactionBase(aOpenDatabaseOp->mDatabase, IDBTransaction::VERSION_CHANGE) + , mOpenDatabaseOp(aOpenDatabaseOp) + , mActorWasAlive(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aOpenDatabaseOp); +} + +VersionChangeTransaction::~VersionChangeTransaction() +{ +#ifdef DEBUG + // Silence the base class' destructor assertion if we never made this actor + // live. + FakeActorDestroyed(); +#endif +} + +bool +VersionChangeTransaction::IsSameProcessActor() +{ + AssertIsOnBackgroundThread(); + + PBackgroundParent* actor = Manager()->Manager()->Manager(); + MOZ_ASSERT(actor); + + return !BackgroundParent::IsOtherProcessActor(actor); +} + +void +VersionChangeTransaction::SetActorAlive() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorWasAlive); + MOZ_ASSERT(!IsActorDestroyed()); + + mActorWasAlive = true; + + // This reference will be absorbed by IPDL and released when the actor is + // destroyed. + AddRef(); +} + +bool +VersionChangeTransaction::CopyDatabaseMetadata() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mOldMetadata); + + const nsRefPtr origMetadata = + GetDatabase()->Metadata(); + MOZ_ASSERT(origMetadata); + + nsRefPtr newMetadata = origMetadata->Duplicate(); + if (NS_WARN_IF(!newMetadata)) { + return false; + } + + // Replace the live metadata with the new mutable copy. + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(origMetadata->mDatabaseId, + &info)); + MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); + MOZ_ASSERT(info->mMetadata == origMetadata); + + mOldMetadata = info->mMetadata.forget(); + info->mMetadata.swap(newMetadata); + + // Replace metadata pointers for all live databases. + for (uint32_t count = info->mLiveDatabases.Length(), index = 0; + index < count; + index++) { + info->mLiveDatabases[index]->mMetadata = info->mMetadata; + } + + return true; +} + +void +VersionChangeTransaction::UpdateMetadata(nsresult aResult) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(GetDatabase()); + MOZ_ASSERT(mOpenDatabaseOp); + MOZ_ASSERT(mOpenDatabaseOp->mDatabase); + MOZ_ASSERT(!mOpenDatabaseOp->mDatabaseId.IsEmpty()); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static PLDHashOperator + Enumerate(const uint64_t& aKey, + nsRefPtr& aValue, + void* /* aClosure */) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + + if (aValue->mDeleted) { + return PL_DHASH_REMOVE; + } + + aValue->mIndexes.Enumerate(Enumerate, nullptr); +#ifdef DEBUG + aValue->mIndexes.MarkImmutable(); +#endif + + return PL_DHASH_NEXT; + } + + private: + static PLDHashOperator + Enumerate(const uint64_t& aKey, + nsRefPtr& aValue, + void* /* aClosure */) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + + if (aValue->mDeleted) { + return PL_DHASH_REMOVE; + } + + return PL_DHASH_NEXT; + } + }; + + if (IsActorDestroyed()) { + return; + } + + nsRefPtr oldMetadata; + mOldMetadata.swap(oldMetadata); + + DatabaseActorInfo* info; + if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) { + return; + } + + MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); + + if (NS_SUCCEEDED(aResult)) { + // Remove all deleted objectStores and indexes, then mark immutable. + info->mMetadata->mObjectStores.Enumerate(Helper::Enumerate, nullptr); +#ifdef DEBUG + info->mMetadata->mObjectStores.MarkImmutable(); +#endif + } else { + // Replace metadata pointers for all live databases. + info->mMetadata = oldMetadata.forget(); + + for (uint32_t count = info->mLiveDatabases.Length(), index = 0; + index < count; + index++) { + info->mLiveDatabases[index]->mMetadata = info->mMetadata; + } + } +} + +bool +VersionChangeTransaction::SendCompleteNotification(nsresult aResult) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mOpenDatabaseOp); + MOZ_ASSERT(!IsActorDestroyed()); + + nsRefPtr openDatabaseOp; + mOpenDatabaseOp.swap(openDatabaseOp); + + if (NS_FAILED(aResult) && NS_SUCCEEDED(openDatabaseOp->mResultCode)) { + openDatabaseOp->mResultCode = aResult; + } + + openDatabaseOp->mState = OpenDatabaseOp::State_SendingResults; + + bool result = SendComplete(aResult); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(openDatabaseOp->Run())); + + return result; +} + +void +VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + + if (!mCommittedOrAborted) { + if (NS_SUCCEEDED(mResultCode)) { + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mForceAborted = true; + + MaybeCommitOrAbort(); + } + + NoteActorDestroyed(); +} + +bool +VersionChangeTransaction::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this); +} + +bool +VersionChangeTransaction::RecvCommit() +{ + AssertIsOnBackgroundThread(); + + return TransactionBase::RecvCommit(); +} + +bool +VersionChangeTransaction::RecvAbort(const nsresult& aResultCode) +{ + AssertIsOnBackgroundThread(); + + return TransactionBase::RecvAbort(aResultCode); +} + +bool +VersionChangeTransaction::RecvCreateObjectStore( + const ObjectStoreMetadata& aMetadata) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!aMetadata.id())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const nsRefPtr dbMetadata = GetDatabase()->Metadata(); + MOZ_ASSERT(dbMetadata); + + if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + auto* foundMetadata = + MetadataNameOrIdMatcher::Match( + dbMetadata->mObjectStores, aMetadata.id(), aMetadata.name()); + + if (NS_WARN_IF(foundMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr newMetadata = new FullObjectStoreMetadata(); + newMetadata->mCommonMetadata = aMetadata; + newMetadata->mNextAutoIncrementId = aMetadata.autoIncrement() ? 1 : 0; + newMetadata->mComittedAutoIncrementId = newMetadata->mNextAutoIncrementId; + + if (NS_WARN_IF(!dbMetadata->mObjectStores.Put(aMetadata.id(), newMetadata, + fallible))) { + return false; + } + + dbMetadata->mNextObjectStoreId++; + + nsRefPtr op = new CreateObjectStoreOp(this, aMetadata); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->DispatchToTransactionThreadPool(); + + return true; +} + +bool +VersionChangeTransaction::RecvDeleteObjectStore(const int64_t& aObjectStoreId) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!aObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const nsRefPtr dbMetadata = GetDatabase()->Metadata(); + MOZ_ASSERT(dbMetadata); + MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0); + + if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr foundMetadata = + GetMetadataForObjectStoreId(aObjectStoreId); + + if (NS_WARN_IF(!foundMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + foundMetadata->mDeleted = true; + + nsRefPtr op = + new DeleteObjectStoreOp(this, foundMetadata); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->DispatchToTransactionThreadPool(); + + return true; +} + +bool +VersionChangeTransaction::RecvCreateIndex(const int64_t& aObjectStoreId, + const IndexMetadata& aMetadata) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!aObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!aMetadata.id())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const nsRefPtr dbMetadata = GetDatabase()->Metadata(); + MOZ_ASSERT(dbMetadata); + + if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextIndexId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr foundObjectStoreMetadata = + GetMetadataForObjectStoreId(aObjectStoreId); + + if (NS_WARN_IF(!foundObjectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr foundIndexMetadata = + MetadataNameOrIdMatcher::Match( + foundObjectStoreMetadata->mIndexes, aMetadata.id(), aMetadata.name()); + + if (NS_WARN_IF(foundIndexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr newMetadata = new FullIndexMetadata(); + newMetadata->mCommonMetadata = aMetadata; + + if (NS_WARN_IF(!foundObjectStoreMetadata->mIndexes.Put(aMetadata.id(), + newMetadata, + fallible))) { + return false; + } + + dbMetadata->mNextIndexId++; + + nsRefPtr op = + new CreateIndexOp(this, aObjectStoreId, aMetadata); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->DispatchToTransactionThreadPool(); + + return true; +} + +bool +VersionChangeTransaction::RecvDeleteIndex(const int64_t& aObjectStoreId, + const int64_t& aIndexId) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(!aObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!aIndexId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const nsRefPtr dbMetadata = GetDatabase()->Metadata(); + MOZ_ASSERT(dbMetadata); + MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0); + MOZ_ASSERT(dbMetadata->mNextIndexId > 0); + + if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr foundObjectStoreMetadata = + GetMetadataForObjectStoreId(aObjectStoreId); + + if (NS_WARN_IF(!foundObjectStoreMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + nsRefPtr foundIndexMetadata = + GetMetadataForIndexId(foundObjectStoreMetadata, aIndexId); + + if (NS_WARN_IF(!foundIndexMetadata)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + foundIndexMetadata->mDeleted = true; + + nsRefPtr op = new DeleteIndexOp(this, aIndexId); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->DispatchToTransactionThreadPool(); + + return true; +} + +PBackgroundIDBRequestParent* +VersionChangeTransaction::AllocPBackgroundIDBRequestParent( + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + return AllocRequest(aParams, IsSameProcessActor()); +} + +bool +VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor( + PBackgroundIDBRequestParent* aActor, + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + return StartRequest(aActor); +} + +bool +VersionChangeTransaction::DeallocPBackgroundIDBRequestParent( + PBackgroundIDBRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return DeallocRequest(aActor); +} + +PBackgroundIDBCursorParent* +VersionChangeTransaction::AllocPBackgroundIDBCursorParent( + const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + + return AllocCursor(aParams, IsSameProcessActor()); +} + +bool +VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor( + PBackgroundIDBCursorParent* aActor, + const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); + + return StartCursor(aActor, aParams); +} + +bool +VersionChangeTransaction::DeallocPBackgroundIDBCursorParent( + PBackgroundIDBCursorParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + return DeallocCursor(aActor); +} + +/******************************************************************************* + * Cursor + ******************************************************************************/ + +Cursor::Cursor(TransactionBase* aTransaction, + Type aType, + int64_t aObjectStoreId, + int64_t aIndexId, + Direction aDirection) + : mTransaction(aTransaction) + , mBackgroundParent(nullptr) + , mObjectStoreId(aObjectStoreId) + , mIndexId(aIndexId) + , mCurrentlyRunningOp(nullptr) + , mType(aType) + , mDirection(aDirection) + , mUniqueIndex(false) + , mActorDestroyed(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(aType != OpenCursorParams::T__None); + MOZ_ASSERT(aObjectStoreId); + MOZ_ASSERT_IF(aType == OpenCursorParams::TIndexOpenCursorParams || + aType == OpenCursorParams::TIndexOpenKeyCursorParams, + aIndexId); + + if (mType == OpenCursorParams::TObjectStoreOpenCursorParams || + mType == OpenCursorParams::TIndexOpenCursorParams) { + mFileManager = aTransaction->GetDatabase()->GetFileManager(); + MOZ_ASSERT(mFileManager); + + mBackgroundParent = aTransaction->GetBackgroundParent(); + MOZ_ASSERT(mBackgroundParent); + } + + if (aIndexId) { + MOZ_ASSERT(aType == OpenCursorParams::TIndexOpenCursorParams || + aType == OpenCursorParams::TIndexOpenKeyCursorParams); + + nsRefPtr objectStoreMetadata = + aTransaction->GetMetadataForObjectStoreId(aObjectStoreId); + MOZ_ASSERT(objectStoreMetadata); + + nsRefPtr indexMetadata = + aTransaction->GetMetadataForIndexId(objectStoreMetadata, aIndexId); + MOZ_ASSERT(indexMetadata); + + mUniqueIndex = indexMetadata->mCommonMetadata.unique(); + } + + static_assert(OpenCursorParams::T__None == 0 && + OpenCursorParams::T__Last == 4, + "Lots of code here assumes only four types of cursors!"); +} + +bool +Cursor::Start(const OpenCursorParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() == mType); + MOZ_ASSERT(!mActorDestroyed); + + if (NS_WARN_IF(mCurrentlyRunningOp)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const OptionalKeyRange& optionalKeyRange = + mType == OpenCursorParams::TObjectStoreOpenCursorParams ? + aParams.get_ObjectStoreOpenCursorParams().optionalKeyRange() : + mType == OpenCursorParams::TObjectStoreOpenKeyCursorParams ? + aParams.get_ObjectStoreOpenKeyCursorParams().optionalKeyRange() : + mType == OpenCursorParams::TIndexOpenCursorParams ? + aParams.get_IndexOpenCursorParams().optionalKeyRange() : + aParams.get_IndexOpenKeyCursorParams().optionalKeyRange(); + + if (mTransaction->IsInvalidated()) { + return true; + } + + nsRefPtr openOp = new OpenOp(this, optionalKeyRange); + + if (NS_WARN_IF(!openOp->Init(mTransaction))) { + openOp->Cleanup(); + return false; + } + + openOp->DispatchToTransactionThreadPool(); + mCurrentlyRunningOp = openOp; + + return true; +} + +void +Cursor::SendResponseInternal(CursorResponse& aResponse, + const nsTArray& aFiles) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult, + NS_FAILED(aResponse.get_nsresult())); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult, + NS_ERROR_GET_MODULE(aResponse.get_nsresult()) == + NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, mKey.IsUnset()); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, + mRangeKey.IsUnset()); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tvoid_t, + mObjectKey.IsUnset()); + MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult || + aResponse.type() == CursorResponse::Tvoid_t || + aResponse.type() == + CursorResponse::TObjectStoreKeyCursorResponse || + aResponse.type() == CursorResponse::TIndexKeyCursorResponse, + aFiles.IsEmpty()); + MOZ_ASSERT(!mActorDestroyed); + MOZ_ASSERT(mCurrentlyRunningOp); + + if (!aFiles.IsEmpty()) { + MOZ_ASSERT(aResponse.type() == CursorResponse::TObjectStoreCursorResponse || + aResponse.type() == CursorResponse::TIndexCursorResponse); + MOZ_ASSERT(mFileManager); + MOZ_ASSERT(mBackgroundParent); + + FallibleTArray actors; + FallibleTArray fileInfos; + nsresult rv = ConvertBlobsToActors(mBackgroundParent, + mFileManager, + aFiles, + actors, + fileInfos); + if (NS_WARN_IF(NS_FAILED(rv))) { + aResponse = ClampResultCode(rv); + } else { + SerializedStructuredCloneReadInfo* serializedInfo = nullptr; + switch (aResponse.type()) { + case CursorResponse::TObjectStoreCursorResponse: + serializedInfo = + &aResponse.get_ObjectStoreCursorResponse().cloneInfo(); + break; + + case CursorResponse::TIndexCursorResponse: + serializedInfo = &aResponse.get_IndexCursorResponse().cloneInfo(); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(serializedInfo); + MOZ_ASSERT(serializedInfo->blobsParent().IsEmpty()); + MOZ_ASSERT(serializedInfo->fileInfos().IsEmpty()); + + serializedInfo->blobsParent().SwapElements(actors); + serializedInfo->fileInfos().SwapElements(fileInfos); + } + } + + // Work around the deleted function by casting to the base class. + auto* base = static_cast(this); + if (!base->SendResponse(aResponse)) { + NS_WARNING("Failed to send response!"); + } + + mCurrentlyRunningOp = nullptr; +} + +void +Cursor::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + if (mCurrentlyRunningOp) { + mCurrentlyRunningOp->NoteActorDestroyed(); + } + + mBackgroundParent = nullptr; +} + +bool +Cursor::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + if (NS_WARN_IF(mCurrentlyRunningOp)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + return PBackgroundIDBCursorParent::Send__delete__(this); +} + +bool +Cursor::RecvContinue(const CursorRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None); + MOZ_ASSERT(!mActorDestroyed); + + if (NS_WARN_IF(mCurrentlyRunningOp)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (aParams.type() == CursorRequestParams::TContinueParams) { + const Key& key = aParams.get_ContinueParams().key(); + if (!key.IsUnset()) { + switch (mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + if (NS_WARN_IF(key <= mKey)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + if (NS_WARN_IF(key >= mKey)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + break; + + default: + MOZ_CRASH("Should never get here!"); + } + } + } + + if (mTransaction->IsInvalidated()) { + return true; + } + + nsRefPtr continueOp = new ContinueOp(this, aParams); + if (NS_WARN_IF(!continueOp->Init(mTransaction))) { + continueOp->Cleanup(); + return false; + } + + continueOp->DispatchToTransactionThreadPool(); + mCurrentlyRunningOp = continueOp; + + return true; +} + +/******************************************************************************* + * FileManager + ******************************************************************************/ + +FileManager::FileManager(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + StoragePrivilege aPrivilege, + const nsAString& aDatabaseName) + : mPersistenceType(aPersistenceType) + , mGroup(aGroup) + , mOrigin(aOrigin) + , mPrivilege(aPrivilege) + , mDatabaseName(aDatabaseName) + , mLastFileId(0) + , mInvalidated(false) +{ } + +FileManager::~FileManager() +{ } + +nsresult +FileManager::Init(nsIFile* aDirectory, + mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aConnection); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!isDirectory)) { + return NS_ERROR_FAILURE; + } + } else { + rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = aDirectory->GetPath(mDirectoryPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + NS_ConvertASCIItoUTF16 dirName(NS_LITERAL_CSTRING(kJournalDirectoryName)); + rv = journalDirectory->Append(dirName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = journalDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!isDirectory)) { + return NS_ERROR_FAILURE; + } + } + + rv = journalDirectory->GetPath(mJournalDirectoryPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, refcount " + "FROM file" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + int64_t id; + rv = stmt->GetInt64(0, &id); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t refcount; + rv = stmt->GetInt32(1, &refcount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(refcount > 0); + + nsRefPtr fileInfo = FileInfo::Create(this, id); + fileInfo->mDBRefCnt = static_cast(refcount); + + mFileInfos.Put(id, fileInfo); + + mLastFileId = std::max(id, mLastFileId); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +FileManager::Invalidate() +{ + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static PLDHashOperator + CopyToTArray(const uint64_t& aKey, FileInfo* aValue, void* aUserArg) + { + MOZ_ASSERT(aValue); + + auto* array = static_cast*>(aUserArg); + MOZ_ASSERT(array); + + MOZ_ALWAYS_TRUE(array->AppendElement(aValue)); + + return PL_DHASH_NEXT; + } + }; + + if (IndexedDatabaseManager::IsClosed()) { + MOZ_ASSERT(false, "Shouldn't be called after shutdown!"); + return NS_ERROR_UNEXPECTED; + } + + FallibleTArray fileInfos; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + MOZ_ASSERT(!mInvalidated); + mInvalidated = true; + + if (NS_WARN_IF(!fileInfos.SetCapacity(mFileInfos.Count()))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + mFileInfos.EnumerateRead(Helper::CopyToTArray, &fileInfos); + } + + for (uint32_t count = fileInfos.Length(), index = 0; index < count; index++) { + FileInfo* fileInfo = fileInfos[index]; + MOZ_ASSERT(fileInfo); + + fileInfo->ClearDBRefs(); + } + + return NS_OK; +} + +already_AddRefed +FileManager::GetDirectory() +{ + return GetFileForPath(mDirectoryPath); +} + +already_AddRefed +FileManager::GetJournalDirectory() +{ + return GetFileForPath(mJournalDirectoryPath); +} + +already_AddRefed +FileManager::EnsureJournalDirectory() +{ + // This can happen on the IO or on a transaction thread. + MOZ_ASSERT(!NS_IsMainThread()); + + nsCOMPtr journalDirectory = GetFileForPath(mJournalDirectoryPath); + if (NS_WARN_IF(!journalDirectory)) { + return nullptr; + } + + bool exists; + nsresult rv = journalDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + if (exists) { + bool isDirectory; + rv = journalDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + if (NS_WARN_IF(!isDirectory)) { + return nullptr; + } + } else { + rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + } + + return journalDirectory.forget(); +} + +already_AddRefed +FileManager::GetFileInfo(int64_t aId) +{ + if (IndexedDatabaseManager::IsClosed()) { + MOZ_ASSERT(false, "Shouldn't be called after shutdown!"); + return nullptr; + } + + FileInfo* fileInfo; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + fileInfo = mFileInfos.Get(aId); + } + + nsRefPtr result = fileInfo; + return result.forget(); +} + +already_AddRefed +FileManager::GetNewFileInfo() +{ + MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); + + FileInfo* fileInfo; + { + MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); + + int64_t id = mLastFileId + 1; + + fileInfo = FileInfo::Create(this, id); + + mFileInfos.Put(id, fileInfo); + + mLastFileId = id; + } + + nsRefPtr result = fileInfo; + return result.forget(); +} + +// static +already_AddRefed +FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) +{ + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aId > 0); + + nsAutoString id; + id.AppendInt(aId); + + nsCOMPtr file; + nsresult rv = aDirectory->Clone(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + rv = file->Append(id); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + return file.forget(); +} + +// static +nsresult +FileManager::InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aDatabaseFile); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!exists) { + return NS_OK; + } + + bool isDirectory; + rv = aDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!isDirectory)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr journalDirectory; + rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + NS_ConvertASCIItoUTF16 dirName(NS_LITERAL_CSTRING(kJournalDirectoryName)); + rv = journalDirectory->Append(dirName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = journalDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + rv = journalDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!isDirectory)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr entries; + rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasElements; + rv = entries->HasMoreElements(&hasElements); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasElements) { + nsCOMPtr connection; + rv = CreateDatabaseConnection(aDatabaseFile, + aDirectory, + NullString(), + aPersistenceType, + aGroup, + aOrigin, + getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mozStorageTransaction transaction(connection, false); + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE VIRTUAL TABLE fs USING filesystem;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, (name IN (SELECT id FROM file)) " + "FROM fs " + "WHERE path = :path" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsString path; + rv = journalDirectory->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + nsString name; + rv = stmt->GetString(0, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t flag = stmt->AsInt32(1); + + if (!flag) { + nsCOMPtr file; + rv = aDirectory->Clone(getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = file->Append(name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_FAILED(file->Remove(false))) { + NS_WARNING("Failed to remove orphaned file!"); + } + } + + nsCOMPtr journalFile; + rv = journalDirectory->Clone(getter_AddRefs(journalFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = journalFile->Append(name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_FAILED(journalFile->Remove(false))) { + NS_WARNING("Failed to remove journal file!"); + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "DROP TABLE fs;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + transaction.Commit(); + } + } + + return NS_OK; +} + +// static +nsresult +FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aUsage); + + bool exists; + nsresult rv = aDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!exists) { + *aUsage = 0; + return NS_OK; + } + + nsCOMPtr entries; + rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + uint64_t usage = 0; + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr file = do_QueryInterface(entry); + MOZ_ASSERT(file); + + nsString leafName; + rv = file->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (leafName.EqualsLiteral(kJournalDirectoryName)) { + continue; + } + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + quota::IncrementUsage(&usage, uint64_t(fileSize)); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aUsage = usage; + return NS_OK; +} + +/******************************************************************************* + * QuotaClient + ******************************************************************************/ + +QuotaClient* QuotaClient::sInstance = nullptr; + +QuotaClient::QuotaClient() + : mShutdownRequested(false) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sInstance, "We expect this to be a singleton!"); + + sInstance = this; +} + +QuotaClient::~QuotaClient() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!"); + + sInstance = nullptr; +} + +void +QuotaClient::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBackgroundThread); + MOZ_ASSERT(!mShutdownRequested); + + mBackgroundThread = aBackgroundThread; +} + +void +QuotaClient::NoteNewTransactionThreadPool() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(gCurrentTransactionThreadPool); + MOZ_ASSERT(!mDyingTransactionThreadPools.Contains( + gCurrentTransactionThreadPool)); + + if (!mDyingTransactionThreadPools.IsEmpty()) { + for (uint32_t index = mDyingTransactionThreadPools.Length(); + index > 0; + index--) { + const uint32_t thisIndex = index - 1; + + if (mDyingTransactionThreadPools[thisIndex]->HasCompletedShutdown()) { + mDyingTransactionThreadPools.RemoveElementAt(thisIndex); + } + } + } +} + +void +QuotaClient::NoteDyingTransactionThreadPool() +{ + AssertIsOnBackgroundThread(); + + if (gCurrentTransactionThreadPool) { + if (!gCurrentTransactionThreadPool->HasCompletedShutdown() && + !mDyingTransactionThreadPools.Contains(gCurrentTransactionThreadPool)) { + mDyingTransactionThreadPools.AppendElement(gCurrentTransactionThreadPool); + } + + gCurrentTransactionThreadPool = nullptr; + } +} + +mozilla::dom::quota::Client::Type +QuotaClient::GetType() +{ + return QuotaClient::IDB; +} + +nsresult +QuotaClient::InitOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) +{ + AssertIsOnIOThread(); + + nsCOMPtr directory; + nsresult rv = + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // We need to see if there are any files in the directory already. If they + // are database files then we need to cleanup stored files (if it's needed) + // and also get the usage. + + nsAutoTArray subdirsToProcess; + nsAutoTArray , 20> unknownFiles; + nsTHashtable validSubdirs(20); + + nsCOMPtr entries; + rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && + hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr file = do_QueryInterface(entry); + MOZ_ASSERT(file); + + nsString leafName; + rv = file->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { + continue; + } + + if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + continue; + } + + bool isDirectory; + rv = file->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (isDirectory) { + if (!validSubdirs.GetEntry(leafName)) { + subdirsToProcess.AppendElement(leafName); + } + continue; + } + + nsString dbBaseFilename; + if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { + unknownFiles.AppendElement(file); + continue; + } + + nsCOMPtr fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = fmDirectory->Append(dbBaseFilename); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup, + aOrigin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aUsageInfo) { + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(fileSize >= 0); + + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); + + uint64_t usage; + rv = FileManager::GetUsage(fmDirectory, &usage); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + aUsageInfo->AppendToFileUsage(usage); + } + + validSubdirs.PutEntry(dbBaseFilename); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { + const nsString& subdir = subdirsToProcess[i]; + if (NS_WARN_IF(!validSubdirs.GetEntry(subdir))) { + return NS_ERROR_UNEXPECTED; + } + } + + for (uint32_t i = 0; i < unknownFiles.Length(); i++) { + nsCOMPtr& unknownFile = unknownFiles[i]; + + // Some temporary SQLite files could disappear, so we have to check if the + // unknown file still exists. + bool exists; + rv = unknownFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + nsString leafName; + unknownFile->GetLeafName(leafName); + + // The journal file may exists even after db has been correctly opened. + if (NS_WARN_IF(!StringEndsWith(leafName, + NS_LITERAL_STRING(".sqlite-journal")))) { + return NS_ERROR_UNEXPECTED; + } + } + } + + return NS_OK; +} + +nsresult +QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + UsageInfo* aUsageInfo) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aUsageInfo); + + nsCOMPtr directory; + nsresult rv = + GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +QuotaClient::OnOriginClearCompleted( + PersistenceType aPersistenceType, + const OriginOrPatternString& aOriginOrPattern) +{ + AssertIsOnIOThread(); + + if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) { + mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern); + } +} + +void +QuotaClient::ReleaseIOThreadObjects() +{ + AssertIsOnIOThread(); + + if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) { + mgr->InvalidateAllFileManagers(); + } +} + +bool +QuotaClient::IsFileServiceUtilized() +{ + MOZ_ASSERT(NS_IsMainThread()); + + return true; +} + +bool +QuotaClient::IsTransactionServiceActivated() +{ + MOZ_ASSERT(NS_IsMainThread()); + + return true; +} + +void +QuotaClient::WaitForStoragesToComplete(nsTArray& aStorages, + nsIRunnable* aCallback) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aStorages.IsEmpty()); + MOZ_ASSERT(aCallback); + + nsCOMPtr backgroundThread; + nsTArray databaseIds; + + for (uint32_t count = aStorages.Length(), index = 0; index < count; index++) { + nsIOfflineStorage* storage = aStorages[index]; + MOZ_ASSERT(storage); + MOZ_ASSERT(storage->GetClient() == this); + + const nsACString& databaseId = storage->Id(); + + if (!databaseIds.Contains(databaseId)) { + databaseIds.AppendElement(databaseId); + + if (!backgroundThread) { + backgroundThread = + static_cast(storage)->OwningThread(); + MOZ_ASSERT(backgroundThread); + } +#ifdef DEBUG + else { + MOZ_ASSERT(backgroundThread == + static_cast(storage)-> + OwningThread()); + } +#endif + } + } + + if (databaseIds.IsEmpty()) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(aCallback))); + return; + } + + MOZ_ASSERT(backgroundThread); + + nsCOMPtr runnable = + new WaitForTransactionsRunnable(this, databaseIds, aCallback); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + backgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); +} + +void +QuotaClient::AbortTransactionsForStorage(nsIOfflineStorage* aStorage) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aStorage); + MOZ_ASSERT(aStorage->GetClient() == this); + MOZ_ASSERT(aStorage->IsClosed()); + + // Nothing to do here, calling DatabaseOfflineStorage::Close() should have + // aborted any transactions already. +} + +bool +QuotaClient::HasTransactionsForStorage(nsIOfflineStorage* aStorage) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aStorage); + MOZ_ASSERT(aStorage->GetClient() == this); + + return static_cast(aStorage)->HasOpenTransactions(); +} + +void +QuotaClient::ShutdownTransactionService() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mShutdownRunnable); + MOZ_ASSERT(!mShutdownRequested); + + mShutdownRequested = true; + + if (mBackgroundThread) { + nsRefPtr runnable = + new ShutdownTransactionThreadPoolRunnable(this); + + if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { + // This can happen if the thread has shut down already. + return; + } + + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + mShutdownRunnable.swap(runnable); + + while (mShutdownRunnable) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } + } +} + +nsresult +QuotaClient::GetDirectory(PersistenceType aPersistenceType, + const nsACString& aOrigin, nsIFile** aDirectory) +{ + QuotaManager* quotaManager = QuotaManager::Get(); + NS_ASSERTION(quotaManager, "This should never fail!"); + + nsCOMPtr directory; + nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin, + getter_AddRefs(directory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(directory); + + rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + directory.forget(aDirectory); + return NS_OK; +} + +nsresult +QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory, + UsageInfo* aUsageInfo, + bool aDatabaseFiles) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aUsageInfo); + + nsCOMPtr entries; + nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!entries) { + return NS_OK; + } + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && + hasMore && + !aUsageInfo->Canceled()) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr file = do_QueryInterface(entry); + MOZ_ASSERT(file); + + bool isDirectory; + rv = file->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (isDirectory) { + if (aDatabaseFiles) { + rv = GetUsageForDirectoryInternal(file, aUsageInfo, false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + nsString leafName; + rv = file->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!leafName.EqualsLiteral(kJournalDirectoryName)) { + NS_WARNING("Unknown directory found!"); + } + } + + continue; + } + + int64_t fileSize; + rv = file->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(fileSize >= 0); + + if (aDatabaseFiles) { + aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); + } else { + aUsageInfo->AppendToFileUsage(uint64_t(fileSize)); + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + + +void +QuotaClient:: +WaitForTransactionsRunnable::MaybeWait() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mState == State_Initial); + MOZ_ASSERT(mQuotaClient); + + nsRefPtr threadPool = gCurrentTransactionThreadPool; + + if (threadPool) { + mState = State_WaitingForTransactions; + + threadPool->WaitForDatabasesToComplete(mDatabaseIds, this); + + MOZ_ASSERT(mDatabaseIds.IsEmpty()); + return; + } + + mDatabaseIds.Clear(); + + SendToMainThread(); +} + +void +QuotaClient:: +WaitForTransactionsRunnable::SendToMainThread() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mState == State_Initial || mState == State_WaitingForTransactions); + MOZ_ASSERT(mDatabaseIds.IsEmpty()); + + mState = State_CallingCallback; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); +} + +void +QuotaClient:: +WaitForTransactionsRunnable::CallCallback() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_CallingCallback); + MOZ_ASSERT(mDatabaseIds.IsEmpty()); + + nsRefPtr quotaClient; + mQuotaClient.swap(quotaClient); + + nsCOMPtr callback; + mCallback.swap(callback); + + callback->Run(); + + mState = State_Complete; +} + +NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::WaitForTransactionsRunnable, + nsRunnable) + +NS_IMETHODIMP +QuotaClient:: +WaitForTransactionsRunnable::Run() +{ + MOZ_ASSERT(mState != State_Complete); + MOZ_ASSERT(mCallback); + + switch (mState) { + case State_Initial: + MaybeWait(); + break; + + case State_WaitingForTransactions: + SendToMainThread(); + break; + + case State_CallingCallback: + CallCallback(); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::ShutdownTransactionThreadPoolRunnable, + nsRunnable) + +NS_IMETHODIMP +QuotaClient:: +ShutdownTransactionThreadPoolRunnable::Run() +{ + if (NS_IsMainThread()) { + MOZ_ASSERT(mHasRequestedShutDown); + MOZ_ASSERT(QuotaClient::GetInstance() == mQuotaClient); + MOZ_ASSERT(mQuotaClient->mShutdownRunnable == this); + + mQuotaClient->mShutdownRunnable = nullptr; + mQuotaClient = nullptr; + + return NS_OK; + } + + AssertIsOnBackgroundThread(); + + if (!mHasRequestedShutDown) { + mHasRequestedShutDown = true; + + mQuotaClient->NoteDyingTransactionThreadPool(); + MOZ_ASSERT(!gCurrentTransactionThreadPool); + + while (!mQuotaClient->mDyingTransactionThreadPools.IsEmpty()) { + nsRefPtr threadPool; + mQuotaClient->mDyingTransactionThreadPools[0].swap(threadPool); + mQuotaClient->mDyingTransactionThreadPools.RemoveElementAt(0); + + if (!threadPool->HasCompletedShutdown()) { + threadPool->ShutdownAndSpin(); + + MOZ_ASSERT(threadPool->HasCompletedShutdown()); + } + } + + MOZ_ASSERT(!gCurrentTransactionThreadPool); + MOZ_ASSERT(mQuotaClient->mDyingTransactionThreadPools.IsEmpty()); + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + + return NS_OK; +} + +/******************************************************************************* + * DatabaseOfflineStorage + ******************************************************************************/ + +DatabaseOfflineStorage::DatabaseOfflineStorage( + QuotaClient* aQuotaClient, + const OptionalWindowId& aOptionalWindowId, + const OptionalWindowId& aOptionalContentParentId, + const nsACString& aGroup, + const nsACString& aOrigin, + const nsACString& aId, + PersistenceType aPersistenceType, + nsIEventTarget* aOwningThread) + : mStrongQuotaClient(aQuotaClient) + , mWeakQuotaClient(aQuotaClient) + , mDatabase(nullptr) + , mOptionalWindowId(aOptionalWindowId) + , mOptionalContentParentId(aOptionalContentParentId) + , mOrigin(aOrigin) + , mId(aId) + , mOwningThread(aOwningThread) + , mTransactionCount(0) + , mClosedOnMainThread(false) + , mClosedOnOwningThread(false) + , mInvalidatedOnMainThread(false) + , mInvalidatedOnOwningThread(false) + , mRegisteredWithQuotaManager(false) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQuotaClient); + MOZ_ASSERT(aOptionalWindowId.type() != OptionalWindowId::T__None); + MOZ_ASSERT_IF(aOptionalWindowId.type() == OptionalWindowId::Tuint64_t, + aOptionalContentParentId.type() == OptionalWindowId::Tvoid_t); + MOZ_ASSERT(aOptionalContentParentId.type() != OptionalWindowId::T__None); + MOZ_ASSERT_IF(aOptionalContentParentId.type() == OptionalWindowId::Tuint64_t, + aOptionalWindowId.type() == OptionalWindowId::Tvoid_t); + MOZ_ASSERT(aOwningThread); + + DebugOnly current; + MOZ_ASSERT(NS_SUCCEEDED(aOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(!current); + + mGroup = aGroup; + mPersistenceType = aPersistenceType; +} + +// static +void +DatabaseOfflineStorage::UnregisterOnOwningThread( + already_AddRefed aOfflineStorage) +{ + AssertIsOnBackgroundThread(); + + nsRefPtr offlineStorage = Move(aOfflineStorage); + MOZ_ASSERT(offlineStorage); + MOZ_ASSERT(offlineStorage->mClosedOnOwningThread); + + offlineStorage->mDatabase = nullptr; + + nsCOMPtr runnable = + NS_NewRunnableMethod(offlineStorage.get(), + &DatabaseOfflineStorage::UnregisterOnMainThread); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); +} + +void +DatabaseOfflineStorage::CloseOnOwningThread() +{ + AssertIsOnBackgroundThread(); + + if (mClosedOnOwningThread) { + return; + } + + mClosedOnOwningThread = true; + + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &DatabaseOfflineStorage::CloseOnMainThread); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); +} + +void +DatabaseOfflineStorage::CloseOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mClosedOnMainThread) { + return; + } + + mClosedOnMainThread = true; + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + quotaManager->OnStorageClosed(this); +} + +void +DatabaseOfflineStorage::InvalidateOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mInvalidatedOnMainThread) { + return; + } + + mInvalidatedOnMainThread = true; + + nsCOMPtr runnable = + NS_NewRunnableMethod(this, + &DatabaseOfflineStorage::InvalidateOnOwningThread); + MOZ_ASSERT(runnable); + + nsCOMPtr owningThread = mOwningThread; + MOZ_ASSERT(owningThread); + + CloseOnMainThread(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(owningThread->Dispatch(runnable, + NS_DISPATCH_NORMAL))); +} + +void +DatabaseOfflineStorage::InvalidateOnOwningThread() +{ + AssertIsOnBackgroundThread(); + + if (mInvalidatedOnOwningThread) { + return; + } + + mInvalidatedOnOwningThread = true; + + if (nsRefPtr database = mDatabase) { + mDatabase = nullptr; + + database->Invalidate(); + } +} + +void +DatabaseOfflineStorage::UnregisterOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(mRegisteredWithQuotaManager); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + quotaManager->UnregisterStorage(this); + mRegisteredWithQuotaManager = false; + + mStrongQuotaClient = nullptr; + + mOwningThread = nullptr; +} + +NS_IMPL_ISUPPORTS(DatabaseOfflineStorage, nsIOfflineStorage) + +NS_IMETHODIMP_(const nsACString&) +DatabaseOfflineStorage::Id() +{ + return mId; +} + +NS_IMETHODIMP_(Client*) +DatabaseOfflineStorage::GetClient() +{ + MOZ_ASSERT(NS_IsMainThread()); + + return mWeakQuotaClient; +} + +NS_IMETHODIMP_(bool) +DatabaseOfflineStorage::IsOwnedByWindow(nsPIDOMWindow* aOwner) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aOwner); + MOZ_ASSERT(aOwner->IsInnerWindow()); + + return mOptionalWindowId.type() == OptionalWindowId::Tuint64_t && + mOptionalWindowId.get_uint64_t() == aOwner->WindowID(); +} + +NS_IMETHODIMP_(bool) +DatabaseOfflineStorage::IsOwnedByProcess(ContentParent* aOwner) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aOwner); + + return mOptionalContentParentId.type() == OptionalWindowId::Tuint64_t && + mOptionalContentParentId.get_uint64_t() == aOwner->ChildID(); +} + +NS_IMETHODIMP_(const nsACString&) +DatabaseOfflineStorage::Origin() +{ + return mOrigin; +} + +NS_IMETHODIMP_(nsresult) +DatabaseOfflineStorage::Close() +{ + MOZ_ASSERT(NS_IsMainThread()); + + InvalidateOnMainThread(); + return NS_OK; +} + +NS_IMETHODIMP_(bool) +DatabaseOfflineStorage::IsClosed() +{ + MOZ_ASSERT(NS_IsMainThread()); + + return mClosedOnMainThread; +} + +NS_IMETHODIMP_(void) +DatabaseOfflineStorage::Invalidate() +{ + MOZ_ASSERT(NS_IsMainThread()); + + InvalidateOnMainThread(); +} + +/******************************************************************************* + * Local class implementations + ******************************************************************************/ + +NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction) +NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction) + +uint64_t DatabaseOperationBase::sNextSerialNumber = 0; + +// static +void +DatabaseOperationBase::GetBindingClauseForKeyRange( + const SerializedKeyRange& aKeyRange, + const nsACString& aKeyColumnName, + nsAutoCString& aBindingClause) +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(!aKeyColumnName.IsEmpty()); + + NS_NAMED_LITERAL_CSTRING(andStr, " AND "); + NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + + if (aKeyRange.isOnly()) { + // Both keys equal. + aBindingClause = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + + spacecolon + lowerKey; + return; + } + + aBindingClause.Truncate(); + + if (!aKeyRange.lower().IsUnset()) { + // Lower key is set. + aBindingClause.Append(andStr + aKeyColumnName); + aBindingClause.AppendLiteral(" >"); + if (!aKeyRange.lowerOpen()) { + aBindingClause.AppendLiteral("="); + } + aBindingClause.Append(spacecolon + lowerKey); + } + + if (!aKeyRange.upper().IsUnset()) { + // Upper key is set. + aBindingClause.Append(andStr + aKeyColumnName); + aBindingClause.AppendLiteral(" <"); + if (!aKeyRange.upperOpen()) { + aBindingClause.AppendLiteral("="); + } + aBindingClause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); + } + + MOZ_ASSERT(!aBindingClause.IsEmpty()); +} + +// static +uint64_t +DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble) +{ + // This is a duplicate of the js engine's byte munging in StructuredClone.cpp + union { + double d; + uint64_t u; + } pun; + pun.d = aDouble; + return pun.u; +} + +// static +nsresult +DatabaseOperationBase::GetStructuredCloneReadInfoFromStatement( + mozIStorageStatement* aStatement, + uint32_t aDataIndex, + uint32_t aFileIdsIndex, + FileManager* aFileManager, + StructuredCloneReadInfo* aInfo) +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aStatement); + MOZ_ASSERT(aFileManager); + + PROFILER_LABEL("IndexedDB", + "DatabaseOperationBase::" + "GetStructuredCloneReadInfoFromStatement", + js::ProfileEntry::Category::STORAGE); + +#ifdef DEBUG + { + int32_t type; + MOZ_ASSERT(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type))); + MOZ_ASSERT(type == mozIStorageStatement::VALUE_TYPE_BLOB); + } +#endif + + const uint8_t* blobData; + uint32_t blobDataLength; + nsresult rv = + aStatement->GetSharedBlob(aDataIndex, &blobDataLength, &blobData); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + const char* compressed = reinterpret_cast(blobData); + size_t compressedLength = size_t(blobDataLength); + + size_t uncompressedLength; + if (NS_WARN_IF(!snappy::GetUncompressedLength(compressed, compressedLength, + &uncompressedLength))) { + return NS_ERROR_FILE_CORRUPTED; + } + + FallibleTArray uncompressed; + if (NS_WARN_IF(!uncompressed.SetLength(uncompressedLength))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + char* uncompressedBuffer = reinterpret_cast(uncompressed.Elements()); + + if (NS_WARN_IF(!snappy::RawUncompress(compressed, compressedLength, + uncompressedBuffer))) { + return NS_ERROR_FILE_CORRUPTED; + } + + aInfo->mData.SwapElements(uncompressed); + + bool isNull; + rv = aStatement->GetIsNull(aFileIdsIndex, &isNull); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!isNull) { + nsString ids; + rv = aStatement->GetString(aFileIdsIndex, ids); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoTArray array; + rv = ConvertFileIdsToArray(ids, array); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (uint32_t count = array.Length(), index = 0; index < count; index++) { + MOZ_ASSERT(array[index] > 0); + + nsRefPtr fileInfo = aFileManager->GetFileInfo(array[index]); + MOZ_ASSERT(fileInfo); + + StructuredCloneFile* file = aInfo->mFiles.AppendElement(); + file->mFileInfo.swap(fileInfo); + } + } + + return NS_OK; +} + +// static +nsresult +DatabaseOperationBase::BindKeyRangeToStatement( + const SerializedKeyRange& aKeyRange, + mozIStorageStatement* aStatement) +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aStatement); + + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + + if (aKeyRange.isOnly()) { + return aKeyRange.lower().BindToStatement(aStatement, lowerKey); + } + + nsresult rv; + + if (!aKeyRange.lower().IsUnset()) { + rv = aKeyRange.lower().BindToStatement(aStatement, lowerKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (!aKeyRange.upper().IsUnset()) { + rv = aKeyRange.upper().BindToStatement(aStatement, + NS_LITERAL_CSTRING("upper_key")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +// static +void +DatabaseOperationBase::AppendConditionClause(const nsACString& aColumnName, + const nsACString& aArgName, + bool aLessThan, + bool aEquals, + nsAutoCString& aResult) +{ + aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName + + NS_LITERAL_CSTRING(" "); + + if (aLessThan) { + aResult.Append('<'); + } + else { + aResult.Append('>'); + } + + if (aEquals) { + aResult.Append('='); + } + + aResult += NS_LITERAL_CSTRING(" :") + aArgName; +} + +// static +nsresult +DatabaseOperationBase::UpdateIndexes( + TransactionBase* aTransaction, + const UniqueIndexTable& aUniqueIndexTable, + const Key& aObjectStoreKey, + bool aOverwrite, + int64_t aObjectDataId, + const nsTArray& aUpdateInfoArray) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(!aObjectStoreKey.IsUnset()); + + PROFILER_LABEL("IndexedDB", + "DatabaseOperationBase::UpdateIndexes", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); + + if (aOverwrite) { + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "DELETE FROM unique_index_data " + "WHERE object_data_id = :object_data_id; " + "DELETE FROM index_data " + "WHERE object_data_id = :object_data_id", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + // Avoid lots of hash lookups for objectStores with lots of indexes by lazily + // holding the necessary statements on the stack outside the loop. + TransactionBase::CachedStatement insertUniqueStmt; + TransactionBase::CachedStatement insertStmt; + + for (uint32_t idxCount = aUpdateInfoArray.Length(), idxIndex = 0; + idxIndex < idxCount; + idxIndex++) { + const IndexUpdateInfo& updateInfo = aUpdateInfoArray[idxIndex]; + + bool unique; + MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(updateInfo.indexId(), &unique)); + + TransactionBase::CachedStatement& stmt = + unique ? insertUniqueStmt : insertStmt; + + if (stmt) { + stmt.Reset(); + } else if (unique) { + rv = aTransaction->GetCachedStatement( + "INSERT INTO unique_index_data " + "(index_id, object_data_id, object_data_key, value) " + "VALUES (:index_id, :object_data_id, :object_data_key, :value)", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + rv = aTransaction->GetCachedStatement( + "INSERT OR IGNORE INTO index_data (" + "index_id, object_data_id, object_data_key, value) " + "VALUES (:index_id, :object_data_id, :object_data_key, :value)", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + updateInfo.indexId()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = aObjectStoreKey.BindToStatement(stmt, + NS_LITERAL_CSTRING("object_data_key")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = updateInfo.value().BindToStatement(stmt, NS_LITERAL_CSTRING("value")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (rv == NS_ERROR_STORAGE_CONSTRAINT && unique) { + // If we're inserting multiple entries for the same unique index, then + // we might have failed to insert due to colliding with another entry for + // the same index in which case we should ignore it. + for (int32_t index = int32_t(idxIndex) - 1; + index >= 0 && + aUpdateInfoArray[index].indexId() == updateInfo.indexId(); + --index) { + if (updateInfo.value() == aUpdateInfoArray[index].value()) { + // We found a key with the same value for the same index. So we + // must have had a collision with a value we just inserted. + rv = NS_OK; + break; + } + } + } + + if (NS_FAILED(rv)) { + return rv; + } + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase, + nsRunnable, + mozIStorageProgressHandler) + +NS_IMETHODIMP +DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection, + bool* _retval) +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(_retval); + + // This is intentionally racy. + *_retval = !OperationMayProceed(); + return NS_OK; +} + +DatabaseOperationBase:: +AutoSetProgressHandler::~AutoSetProgressHandler() +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT_IF(mConnection, mDEBUGDatabaseOp); + + if (mConnection) { + nsCOMPtr oldHandler; + nsresult rv = + mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)); + if (NS_SUCCEEDED(rv)) { + MOZ_ASSERT(SameCOMIdentity(oldHandler, + static_cast(mDEBUGDatabaseOp))); + } else { + NS_WARNING("Failed to remove progress handler!"); + } + } +} + +nsresult +DatabaseOperationBase:: +AutoSetProgressHandler::Register( + DatabaseOperationBase* aDatabaseOp, + const nsCOMPtr& aConnection) +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aDatabaseOp); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(!mConnection); + MOZ_ASSERT(!mDEBUGDatabaseOp); + + nsCOMPtr oldProgressHandler; + + nsresult rv = + aConnection->SetProgressHandler(kStorageProgressGranularity, aDatabaseOp, + getter_AddRefs(oldProgressHandler)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!oldProgressHandler); + + mConnection = aConnection; + mDEBUGDatabaseOp = aDatabaseOp; + + return NS_OK; +} + +nsresult +FactoryOp::Open() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial); + + // Swap this to the stack now to ensure that we release it on this thread. + nsRefPtr contentParent; + mContentParent.swap(contentParent); + + if (!OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + PermissionRequestBase::PermissionValue permission; + nsresult rv = CheckPermission(contentParent, &permission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || + permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt); + + if (permission == PermissionRequestBase::kPermissionDenied) { + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + + // This has to be started on the main thread currently. + if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + const DatabaseMetadata& metadata = mCommonParams.metadata(); + + QuotaManager::GetStorageId(metadata.persistenceType(), + mOrigin, + Client::IDB, + metadata.name(), + mDatabaseId); + + if (permission == PermissionRequestBase::kPermissionPrompt) { + mState = State_PermissionChallenge; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); + return NS_OK; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); + + rv = FinishOpen(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +FactoryOp::ChallengePermission() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_PermissionChallenge); + + const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); + MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); + + if (NS_WARN_IF(!SendPermissionChallenge(principalInfo))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +FactoryOp::RetryCheckPermission() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_PermissionRetry); + MOZ_ASSERT(mCommonParams.principalInfo().type() == + PrincipalInfo::TContentPrincipalInfo); + + // Swap this to the stack now to ensure that we release it on this thread. + nsRefPtr contentParent; + mContentParent.swap(contentParent); + + if (!OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + PermissionRequestBase::PermissionValue permission; + nsresult rv = CheckPermission(contentParent, &permission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed || + permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt); + + if (permission == PermissionRequestBase::kPermissionDenied || + permission == PermissionRequestBase::kPermissionPrompt) { + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + + MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed); + + rv = FinishOpen(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +FactoryOp::SendToIOThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_OpenPending); + + if (!OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (NS_WARN_IF(!quotaManager)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + // Must set this before dispatching otherwise we will race with the IO thread. + mState = State_DatabaseWorkOpen; + + nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; +} + +void +FactoryOp::WaitForTransactions() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_BeginVersionChange || + mState == State_WaitingForOtherDatabasesToClose); + MOZ_ASSERT(!mDatabaseId.IsEmpty()); + MOZ_ASSERT(!IsActorDestroyed()); + + nsTArray databaseIds; + databaseIds.AppendElement(mDatabaseId); + + TransactionThreadPool* threadPool = mFactory->GetTransactionThreadPool(); + MOZ_ASSERT(threadPool); + + // WaitForDatabasesToComplete() will run this op immediately if there are no + // transactions blocking it, so be sure to set the next state here before + // calling it. + mState = State_WaitingForTransactionsToComplete; + + threadPool->WaitForDatabasesToComplete(databaseIds, this); + return; +} + +void +FactoryOp::FinishSendResults() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_SendingResults); + MOZ_ASSERT(mFactory); + + // Make sure to release the factory on this thread. + nsRefPtr factory; + mFactory.swap(factory); + + if (mBlockedQuotaManager) { + // Must set mState before dispatching otherwise we will race with the main + // thread. + mState = State_UnblockingQuotaManager; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + } else { + mState = State_Completed; + } +} + +void +FactoryOp::UnblockQuotaManager() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_UnblockingQuotaManager); + + Nullable persistenceType( + const_cast(mCommonParams.metadata().persistenceType())); + + if (QuotaManager* quotaManager = QuotaManager::Get()) { + quotaManager-> + AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mOrigin), + persistenceType, + mDatabaseId); + } else { + NS_WARNING("QuotaManager went away before we could unblock it!"); + } + + mState = State_Completed; +} + +nsresult +FactoryOp::CheckPermission(ContentParent* aContentParent, + PermissionRequestBase::PermissionValue* aPermission) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial || mState == State_PermissionRetry); + + if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { + if (aContentParent) { + // The DOM in the other process should have kept us from receiving any + // indexedDB messages so assume that the child is misbehaving. + aContentParent->KillHard(); + } + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + + if (NS_WARN_IF(mCommonParams.privateBrowsingMode())) { + // XXX This is only temporary. + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + + const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); + MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo); + + if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { + MOZ_ASSERT(mState == State_Initial); + + if (aContentParent) { + // Check to make sure that the child process has access to the database it + // is accessing. + NS_NAMED_LITERAL_CSTRING(permissionStringBase, + kPermissionStringChromeBase); + NS_ConvertUTF16toUTF8 databaseName(mCommonParams.metadata().name()); + NS_NAMED_LITERAL_CSTRING(readSuffix, kPermissionStringChromeReadSuffix); + NS_NAMED_LITERAL_CSTRING(writeSuffix, kPermissionStringChromeWriteSuffix); + + const nsAutoCString permissionStringWrite = + permissionStringBase + databaseName + writeSuffix; + const nsAutoCString permissionStringRead = + permissionStringBase + databaseName + readSuffix; + + bool canWrite = + CheckAtLeastOneAppHasPermission(aContentParent, permissionStringWrite); + + bool canRead; + if (canWrite) { + MOZ_ASSERT(CheckAtLeastOneAppHasPermission(aContentParent, + permissionStringRead)); + canRead = true; + } else { + canRead = + CheckAtLeastOneAppHasPermission(aContentParent, permissionStringRead); + } + + // Deleting a database requires write permissions. + if (mDeleting && !canWrite) { + aContentParent->KillHard(); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + // Opening or deleting requires read permissions. + if (!canRead) { + aContentParent->KillHard(); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mChromeWriteAccessAllowed = canWrite; + } else { + mChromeWriteAccessAllowed = true; + } + + if (State_Initial == mState) { + QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, &mStoragePrivilege, + nullptr); + } + + *aPermission = PermissionRequestBase::kPermissionAllowed; + return NS_OK; + } + + MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); + + nsresult rv; + nsCOMPtr principal = + PrincipalInfoToPrincipal(principalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + PermissionRequestBase::PermissionValue permission; + + if (mCommonParams.metadata().persistenceType() == + PERSISTENCE_TYPE_TEMPORARY) { + // Temporary storage doesn't need to check the permission. + permission = PermissionRequestBase::kPermissionAllowed; + } else { + MOZ_ASSERT(mCommonParams.metadata().persistenceType() == + PERSISTENCE_TYPE_PERSISTENT); + +#ifdef MOZ_CHILD_PERMISSIONS + if (aContentParent) { + if (NS_WARN_IF(!AssertAppPrincipal(aContentParent, principal))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + uint32_t intPermission = + mozilla::CheckPermission(aContentParent, principal, kPermissionString); + + permission = + PermissionRequestBase::PermissionValueForIntPermission(intPermission); + } else +#endif // MOZ_CHILD_PERMISSIONS + { + rv = PermissionRequestBase::GetCurrentPermission(principal, &permission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + + if (permission != PermissionRequestBase::kPermissionDenied && + State_Initial == mState) { + rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &mOrigin, + &mStoragePrivilege, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + *aPermission = permission; + return NS_OK; +} + +nsresult +FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, + Database* aOpeningDatabase, + uint64_t aOldVersion, + const NullableVersion& aNewVersion) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aDatabaseActorInfo); + MOZ_ASSERT(mState == State_BeginVersionChange); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + MOZ_ASSERT(!IsActorDestroyed()); + + const uint32_t expectedCount = mDeleting ? 0 : 1; + const uint32_t liveCount = aDatabaseActorInfo->mLiveDatabases.Length(); + if (liveCount > expectedCount) { + FallibleTArray maybeBlockedDatabases; + for (uint32_t index = 0; index < liveCount; index++) { + Database* database = aDatabaseActorInfo->mLiveDatabases[index]; + if ((!aOpeningDatabase || database != aOpeningDatabase) && + !database->IsClosed() && + NS_WARN_IF(!maybeBlockedDatabases.AppendElement(database))) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + if (!maybeBlockedDatabases.IsEmpty()) { + mMaybeBlockedDatabases.SwapElements(maybeBlockedDatabases); + } + } + + if (!mMaybeBlockedDatabases.IsEmpty()) { + for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0; + index < count; + /* incremented conditionally */) { + if (mMaybeBlockedDatabases[index]->SendVersionChange(aOldVersion, + aNewVersion)) { + index++; + } else { + // We don't want to wait forever if we were not able to send the + // message. + mMaybeBlockedDatabases.RemoveElementAt(index); + count--; + } + } + } + + return NS_OK; +} + +// static +bool +FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent, + const nsACString& aPermissionString) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContentParent); + MOZ_ASSERT(!aPermissionString.IsEmpty()); + +#ifdef MOZ_CHILD_PERMISSIONS + const nsTArray& browsers = + aContentParent->ManagedPBrowserParent(); + + if (!browsers.IsEmpty()) { + nsCOMPtr appsService = + do_GetService(APPS_SERVICE_CONTRACTID); + if (NS_WARN_IF(!appsService)) { + return false; + } + + nsCOMPtr ioService = do_GetIOService(); + if (NS_WARN_IF(!ioService)) { + return false; + } + + nsCOMPtr secMan = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + if (NS_WARN_IF(!secMan)) { + return false; + } + + nsCOMPtr permMan = + mozilla::services::GetPermissionManager(); + if (NS_WARN_IF(!permMan)) { + return false; + } + + const nsPromiseFlatCString permissionString = + PromiseFlatCString(aPermissionString); + + for (uint32_t index = 0, count = browsers.Length(); + index < count; + index++) { + uint32_t appId = + static_cast(browsers[index])->OwnOrContainingAppId(); + MOZ_ASSERT(kUnknownAppId != appId && kNoAppId != appId); + + nsCOMPtr app; + nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(app)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsString origin; + rv = app->GetOrigin(origin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), origin, nullptr, nullptr, ioService); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsCOMPtr principal; + rv = secMan->GetAppCodebasePrincipal(uri, appId, false, + getter_AddRefs(principal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + uint32_t permission; + rv = permMan->TestExactPermissionFromPrincipal(principal, + permissionString.get(), + &permission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (permission == nsIPermissionManager::ALLOW_ACTION) { + return true; + } + } + } + + return false; +#else + return true; +#endif // MOZ_CHILD_PERMISSIONS +} + +nsresult +FactoryOp::FinishOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial || mState == State_PermissionRetry); + MOZ_ASSERT(!mOrigin.IsEmpty()); + MOZ_ASSERT(!mDatabaseId.IsEmpty()); + MOZ_ASSERT(!mBlockedQuotaManager); + MOZ_ASSERT(!mContentParent); + + PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); + + // XXX This is temporary, but we don't currently support the explicit + // 'persistent' storage type. + if (persistenceType == PERSISTENCE_TYPE_PERSISTENT && + mCommonParams.metadata().persistenceTypeIsExplicit()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + QuotaManager* quotaManager; + + if (QuotaManager::IsShuttingDown() || + !(quotaManager = QuotaManager::GetOrCreate())) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsresult rv = + quotaManager-> + WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mOrigin), + Nullable(persistenceType), + mDatabaseId, + this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mBlockedQuotaManager = true; + + mState = State_OpenPending; + return NS_OK; +} + +void +FactoryOp::NoteDatabaseBlocked(Database* aDatabase) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); + MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); + MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase)); + + // Only send the blocked event if all databases have reported back. If the + // database was closed then it will have been removed from the array. + // Otherwise if it was blocked its |mBlocked| flag will be true. + bool sendBlockedEvent = true; + + for (uint32_t count = mMaybeBlockedDatabases.Length(), index = 0; + index < count; + index++) { + MaybeBlockedDatabaseInfo& info = mMaybeBlockedDatabases[index]; + if (info == aDatabase) { + // This database was blocked, mark accordingly. + info.mBlocked = true; + } else if (!info.mBlocked) { + // A database has not yet reported back yet, don't send the event yet. + sendBlockedEvent = false; + } + } + + if (sendBlockedEvent) { + SendBlockedNotification(); + } +} + +NS_IMETHODIMP +FactoryOp::Run() +{ + nsresult rv; + + switch (mState) { + case State_Initial: + rv = Open(); + break; + + case State_OpenPending: + rv = QuotaManagerOpen(); + break; + + case State_PermissionChallenge: + rv = ChallengePermission(); + break; + + case State_PermissionRetry: + rv = RetryCheckPermission(); + break; + + case State_DatabaseWorkOpen: + rv = DoDatabaseWork(); + break; + + case State_BeginVersionChange: + rv = BeginVersionChange(); + break; + + case State_WaitingForTransactionsToComplete: + rv = DispatchToWorkThread(); + break; + + case State_SendingResults: + SendResults(); + return NS_OK; + + case State_UnblockingQuotaManager: + UnblockQuotaManager(); + return NS_OK; + + default: + MOZ_CRASH("Bad state!"); + } + + if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_SendingResults) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + // Must set mState before dispatching otherwise we will race with the owning + // thread. + mState = State_SendingResults; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); + } + + return NS_OK; +} + +void +FactoryOp::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + + NoteActorDestroyed(); +} + +bool +FactoryOp::RecvPermissionRetry() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!IsActorDestroyed()); + MOZ_ASSERT(mState == State_PermissionChallenge); + + mContentParent = BackgroundParent::GetContentParent(Manager()->Manager()); + + mState = State_PermissionRetry; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + + return true; +} + +OpenDatabaseOp::OpenDatabaseOp(Factory* aFactory, + already_AddRefed aContentParent, + const OptionalWindowId& aOptionalWindowId, + const CommonFactoryRequestParams& aParams) + : FactoryOp(aFactory, Move(aContentParent), aParams, /* aDeleting */ false) + , mOptionalWindowId(aOptionalWindowId) + , mMetadata(new FullDatabaseMetadata(aParams.metadata())) + , mRequestedVersion(aParams.metadata().version()) +{ + MOZ_ASSERT_IF(mContentParent, + mOptionalWindowId.type() == OptionalWindowId::Tvoid_t); + + auto& optionalContentParentId = + const_cast(mOptionalContentParentId); + + if (mContentParent) { + // This is a little scary but it looks safe to call this off the main thread + // for now. + optionalContentParentId = mContentParent->ChildID(); + } else { + optionalContentParentId = void_t(); + } +} + +nsresult +OpenDatabaseOp::QuotaManagerOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_OpenPending); + MOZ_ASSERT(!mOfflineStorage); + + QuotaClient* quotaClient = QuotaClient::GetInstance(); + MOZ_ASSERT(quotaClient); + + if (NS_WARN_IF(quotaClient->HasShutDown())) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsRefPtr offlineStorage = + new DatabaseOfflineStorage(quotaClient, + mOptionalWindowId, + mOptionalContentParentId, + mGroup, + mOrigin, + mDatabaseId, + mCommonParams.metadata().persistenceType(), + mOwningThread); + + if (NS_WARN_IF(!quotaManager->RegisterStorage(offlineStorage))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + offlineStorage->NoteRegisteredWithQuotaManager(); + + quotaClient->NoteBackgroundThread(mOwningThread); + + mOfflineStorage.swap(offlineStorage); + + nsresult rv = SendToIOThread(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +OpenDatabaseOp::DoDatabaseWork() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mState == State_DatabaseWorkOpen); + + PROFILER_LABEL("IndexedDB", + "OpenDatabaseHelper::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (NS_WARN_IF(QuotaManager::IsShuttingDown()) || !OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + const nsString& databaseName = mCommonParams.metadata().name(); + PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsCOMPtr dbDirectory; + + nsresult rv = + quotaManager->EnsureOriginIsInitialized(persistenceType, + mGroup, + mOrigin, + mStoragePrivilege != Chrome, + getter_AddRefs(dbDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool exists; + rv = dbDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!exists) { + rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } +#ifdef DEBUG + else { + bool isDirectory; + MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory))); + MOZ_ASSERT(isDirectory); + } +#endif + + nsAutoString filename; + GetDatabaseFilename(databaseName, filename); + + nsCOMPtr dbFile; + rv = dbDirectory->Clone(getter_AddRefs(dbFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbFile->GetPath(mDatabaseFilePath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr fmDirectory; + rv = dbDirectory->Clone(getter_AddRefs(fmDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = fmDirectory->Append(filename); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr connection; + rv = CreateDatabaseConnection(dbFile, + fmDirectory, + databaseName, + persistenceType, + mGroup, + mOrigin, + getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AutoSetProgressHandler asph; + rv = asph.Register(this, connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = LoadDatabaseInformation(connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(mMetadata->mNextObjectStoreId > mMetadata->mObjectStores.Count()); + MOZ_ASSERT(mMetadata->mNextIndexId > 0); + + // See if we need to do a versionchange transaction + + // Optional version semantics. + if (!mRequestedVersion) { + // If the requested version was not specified and the database was created, + // treat it as if version 1 were requested. + if (mMetadata->mCommonMetadata.version() == 0) { + mRequestedVersion = 1; + } else { + // Otherwise, treat it as if the current version were requested. + mRequestedVersion = mMetadata->mCommonMetadata.version(); + } + } + + if (NS_WARN_IF(mMetadata->mCommonMetadata.version() > mRequestedVersion)) { + return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + MOZ_ASSERT(mgr); + + nsRefPtr fileManager = + mgr->GetFileManager(persistenceType, mOrigin, databaseName); + if (!fileManager) { + fileManager = new FileManager(persistenceType, + mGroup, + mOrigin, + mStoragePrivilege, + databaseName); + + rv = fileManager->Init(fmDirectory, connection); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mgr->AddFileManager(fileManager); + } + + mFileManager = fileManager.forget(); + + // Must set mState before dispatching otherwise we will race with the owning + // thread. + mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion) ? + State_SendingResults : + State_BeginVersionChange; + + rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +OpenDatabaseOp::LoadDatabaseInformation(mozIStorageConnection* aConnection) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(mMetadata); + + // Load version information. + nsCOMPtr stmt; + nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name, version " + "FROM database" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!hasResult)) { + return NS_ERROR_FILE_CORRUPTED; + } + + nsString databaseName; + rv = stmt->GetString(0, databaseName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(mCommonParams.metadata().name() != databaseName)) { + return NS_ERROR_FILE_CORRUPTED; + } + + int64_t version; + rv = stmt->GetInt64(1, &version); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mMetadata->mCommonMetadata.version() = uint64_t(version); + + ObjectStoreTable& objectStores = mMetadata->mObjectStores; + + // Load object store names and ids. + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, auto_increment, name, key_path " + "FROM object_store" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + Maybe> usedIds; + Maybe> usedNames; + + int64_t lastObjectStoreId = 0; + + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + int64_t objectStoreId; + rv = stmt->GetInt64(0, &objectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!usedIds) { + usedIds.emplace(); + } + + if (NS_WARN_IF(objectStoreId <= 0) || + NS_WARN_IF(usedIds.ref().Contains(objectStoreId))) { + return NS_ERROR_FILE_CORRUPTED; + } + + if (NS_WARN_IF(!usedIds.ref().PutEntry(objectStoreId, fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsString name; + rv = stmt->GetString(2, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!usedNames) { + usedNames.emplace(); + } + + if (NS_WARN_IF(usedNames.ref().Contains(name))) { + return NS_ERROR_FILE_CORRUPTED; + } + + if (NS_WARN_IF(!usedNames.ref().PutEntry(name, fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsRefPtr metadata = new FullObjectStoreMetadata(); + metadata->mCommonMetadata.id() = objectStoreId; + metadata->mCommonMetadata.name() = name; + + int32_t columnType; + rv = stmt->GetTypeOfIndex(3, &columnType); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) { + metadata->mCommonMetadata.keyPath() = KeyPath(0); + } else { + MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_TEXT); + + nsString keyPathSerialization; + rv = stmt->GetString(3, keyPathSerialization); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + metadata->mCommonMetadata.keyPath() = + KeyPath::DeserializeFromString(keyPathSerialization); + if (NS_WARN_IF(!metadata->mCommonMetadata.keyPath().IsValid())) { + return NS_ERROR_FILE_CORRUPTED; + } + } + + int64_t nextAutoIncrementId; + rv = stmt->GetInt64(1, &nextAutoIncrementId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + metadata->mCommonMetadata.autoIncrement() = !!nextAutoIncrementId; + metadata->mNextAutoIncrementId = nextAutoIncrementId; + metadata->mComittedAutoIncrementId = nextAutoIncrementId; + + if (NS_WARN_IF(!objectStores.Put(objectStoreId, metadata, fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + lastObjectStoreId = std::max(lastObjectStoreId, objectStoreId); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + usedIds.reset(); + usedNames.reset(); + + // Load index information + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id, object_store_id, name, key_path, unique_index, multientry " + "FROM object_store_index" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t lastIndexId = 0; + + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + int64_t objectStoreId; + rv = stmt->GetInt64(1, &objectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsRefPtr objectStoreMetadata; + if (NS_WARN_IF(!objectStores.Get(objectStoreId, + getter_AddRefs(objectStoreMetadata)))) { + return NS_ERROR_FILE_CORRUPTED; + } + + MOZ_ASSERT(objectStoreMetadata->mCommonMetadata.id() == objectStoreId); + + int64_t indexId; + rv = stmt->GetInt64(0, &indexId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!usedIds) { + usedIds.emplace(); + } + + if (NS_WARN_IF(indexId <= 0) || + NS_WARN_IF(usedIds.ref().Contains(indexId))) { + return NS_ERROR_FILE_CORRUPTED; + } + + if (NS_WARN_IF(!usedIds.ref().PutEntry(indexId, fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsString name; + rv = stmt->GetString(2, name); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoString hashName; + hashName.AppendInt(indexId); + hashName.Append(':'); + hashName.Append(name); + + if (!usedNames) { + usedNames.emplace(); + } + + if (NS_WARN_IF(usedNames.ref().Contains(hashName))) { + return NS_ERROR_FILE_CORRUPTED; + } + + if (NS_WARN_IF(!usedNames.ref().PutEntry(hashName, fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsRefPtr indexMetadata = new FullIndexMetadata(); + indexMetadata->mCommonMetadata.id() = indexId; + indexMetadata->mCommonMetadata.name() = name; + +#ifdef DEBUG + { + int32_t columnType; + rv = stmt->GetTypeOfIndex(3, &columnType); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + MOZ_ASSERT(columnType != mozIStorageStatement::VALUE_TYPE_NULL); + } +#endif + + nsString keyPathSerialization; + rv = stmt->GetString(3, keyPathSerialization); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + indexMetadata->mCommonMetadata.keyPath() = + KeyPath::DeserializeFromString(keyPathSerialization); + if (NS_WARN_IF(!indexMetadata->mCommonMetadata.keyPath().IsValid())) { + return NS_ERROR_FILE_CORRUPTED; + } + + int32_t scratch; + rv = stmt->GetInt32(4, &scratch); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + indexMetadata->mCommonMetadata.unique() = !!scratch; + + rv = stmt->GetInt32(5, &scratch); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + indexMetadata->mCommonMetadata.multiEntry() = !!scratch; + + if (NS_WARN_IF(!objectStoreMetadata->mIndexes.Put(indexId, indexMetadata, + fallible))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + lastIndexId = std::max(lastIndexId, indexId); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(lastObjectStoreId == INT64_MAX) || + NS_WARN_IF(lastIndexId == INT64_MAX)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mMetadata->mNextObjectStoreId = lastObjectStoreId + 1; + mMetadata->mNextIndexId = lastIndexId + 1; + + return NS_OK; +} + +nsresult +OpenDatabaseOp::BeginVersionChange() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_BeginVersionChange); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion); + MOZ_ASSERT(!mDatabase); + MOZ_ASSERT(!mVersionChangeTransaction); + + if (IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + EnsureDatabaseActor(); + + if (mDatabase->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + MOZ_ASSERT(!mDatabase->IsClosed()); + + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); + + MOZ_ASSERT(info->mLiveDatabases.Contains(mDatabase)); + MOZ_ASSERT(!info->mWaitingFactoryOp); + MOZ_ASSERT(info->mMetadata == mMetadata); + + nsRefPtr transaction = + new VersionChangeTransaction(this); + + if (NS_WARN_IF(!transaction->CopyDatabaseMetadata())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + MOZ_ASSERT(info->mMetadata != mMetadata); + mMetadata = info->mMetadata; + + NullableVersion newVersion = mRequestedVersion; + + nsresult rv = + SendVersionChangeMessages(info, + mDatabase, + mMetadata->mCommonMetadata.version(), + newVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mVersionChangeTransaction.swap(transaction); + + if (mMaybeBlockedDatabases.IsEmpty()) { + // We don't need to wait on any databases, just jump to the transaction + // pool. + WaitForTransactions(); + return NS_OK; + } + + info->mWaitingFactoryOp = this; + + mState = State_WaitingForOtherDatabasesToClose; + return NS_OK; +} + +void +OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); + MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); + + bool actorDestroyed = IsActorDestroyed() || mDatabase->IsActorDestroyed(); + + nsresult rv; + if (actorDestroyed) { + IDB_REPORT_INTERNAL_ERR(); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } else { + rv = NS_OK; + } + + if (mMaybeBlockedDatabases.RemoveElement(aDatabase) && + mMaybeBlockedDatabases.IsEmpty()) { + if (actorDestroyed) { + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); + MOZ_ASSERT(info->mWaitingFactoryOp == this); + info->mWaitingFactoryOp = nullptr; + } else { + WaitForTransactions(); + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + mState = State_SendingResults; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run())); + } +} + +void +OpenDatabaseOp::SendBlockedNotification() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); + + if (!IsActorDestroyed()) { + unused << SendBlocked(mMetadata->mCommonMetadata.version()); + } +} + +nsresult +OpenDatabaseOp::DispatchToWorkThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete); + MOZ_ASSERT(mVersionChangeTransaction); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + + if (IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mState = State_DatabaseWorkVersionChange; + + TransactionThreadPool* threadPool = mFactory->GetTransactionThreadPool(); + MOZ_ASSERT(threadPool); + + // Intentionally empty. + nsTArray objectStoreNames; + + nsRefPtr versionChangeOp = new VersionChangeOp(this); + + threadPool->Dispatch(mVersionChangeTransaction->TransactionId(), + mVersionChangeTransaction->DatabaseId(), + objectStoreNames, + mVersionChangeTransaction->GetMode(), + versionChangeOp, + /* aFinish */ false, + /* aFinishCallback */ nullptr); + + mVersionChangeTransaction->SetActive(); + + mVersionChangeTransaction->NoteActiveRequest(); + + if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +nsresult +OpenDatabaseOp::SendUpgradeNeeded() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_DatabaseWorkVersionChange); + MOZ_ASSERT(mVersionChangeTransaction); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase); + + if (IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsRefPtr transaction; + mVersionChangeTransaction.swap(transaction); + + nsresult rv = EnsureDatabaseActorIsAlive(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Transfer ownership to IPDL. + transaction->SetActorAlive(); + + if (!mDatabase->SendPBackgroundIDBVersionChangeTransactionConstructor( + transaction, + mMetadata->mCommonMetadata.version(), + mRequestedVersion, + mMetadata->mNextObjectStoreId, + mMetadata->mNextIndexId)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; +} + +void +OpenDatabaseOp::SendResults() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_SendingResults); + MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), mMaybeBlockedDatabases.IsEmpty()); + MOZ_ASSERT_IF(NS_SUCCEEDED(mResultCode), !mVersionChangeTransaction); + + mMaybeBlockedDatabases.Clear(); + + // Only needed if we're being called from within NoteDatabaseDone() since this + // OpenDatabaseOp is only held alive by the gLiveDatabaseHashtable. + nsRefPtr kungFuDeathGrip; + + DatabaseActorInfo* info; + if (gLiveDatabaseHashtable->Get(mDatabaseId, &info) && + info->mWaitingFactoryOp) { + MOZ_ASSERT(info->mWaitingFactoryOp == this); + kungFuDeathGrip = + static_cast(info->mWaitingFactoryOp.get()); + info->mWaitingFactoryOp = nullptr; + } + + if (mVersionChangeTransaction) { + MOZ_ASSERT(NS_FAILED(mResultCode)); + + mVersionChangeTransaction->Abort(mResultCode, /* aForce */ true); + mVersionChangeTransaction = nullptr; + } + + if (!IsActorDestroyed() && + (!mDatabase || !mDatabase->IsInvalidated())) { + FactoryRequestResponse response; + + if (NS_SUCCEEDED(mResultCode)) { + // If we just successfully completed a versionchange operation then we + // need to update the version in our metadata. + mMetadata->mCommonMetadata.version() = mRequestedVersion; + + nsresult rv = EnsureDatabaseActorIsAlive(); + if (NS_SUCCEEDED(rv)) { + // We successfully opened a database so use its actor as the success + // result for this request. + OpenDatabaseRequestResponse openResponse; + openResponse.databaseParent() = mDatabase; + response = openResponse; + } else { + response = ClampResultCode(rv); +#ifdef DEBUG + mResultCode = response.get_nsresult(); +#endif + } + } else { +#ifdef DEBUG + // If something failed then our metadata pointer is now bad. No one should + // ever touch it again though so just null it out in DEBUG builds to make + // sure we find such cases. + mMetadata = nullptr; +#endif + response = ClampResultCode(mResultCode); + } + + NS_WARN_IF(!PBackgroundIDBFactoryRequestParent::Send__delete__(this, + response)); + } + + if (NS_FAILED(mResultCode) && mOfflineStorage) { + mOfflineStorage->CloseOnOwningThread(); + DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget()); + } + + FinishSendResults(); +} + +void +OpenDatabaseOp::EnsureDatabaseActor() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_BeginVersionChange || + mState == State_DatabaseWorkVersionChange || + mState == State_SendingResults); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + MOZ_ASSERT(!mDatabaseFilePath.IsEmpty()); + MOZ_ASSERT(!IsActorDestroyed()); + + if (mDatabase) { + return; + } + + MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty()); + mMetadata->mDatabaseId = mDatabaseId; + + MOZ_ASSERT(mMetadata->mFilePath.IsEmpty()); + mMetadata->mFilePath = mDatabaseFilePath; + + DatabaseActorInfo* info; + if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) { + AssertMetadataConsistency(info->mMetadata); + mMetadata = info->mMetadata; + } + + auto factory = static_cast(Manager()); + + mDatabase = new Database(factory, + mCommonParams.principalInfo(), + mGroup, + mOrigin, + mMetadata, + mFileManager, + mOfflineStorage.forget(), + mChromeWriteAccessAllowed); + + if (info) { + info->mLiveDatabases.AppendElement(mDatabase); + } else { + info = new DatabaseActorInfo(mMetadata, mDatabase); + gLiveDatabaseHashtable->Put(mDatabaseId, info); + } + + QuotaClient::GetInstance()->NoteNewTransactionThreadPool(); +} + +nsresult +OpenDatabaseOp::EnsureDatabaseActorIsAlive() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_DatabaseWorkVersionChange || + mState == State_SendingResults); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + MOZ_ASSERT(!IsActorDestroyed()); + + EnsureDatabaseActor(); + + if (mDatabase->IsActorAlive()) { + return NS_OK; + } + + auto factory = static_cast(Manager()); + + DatabaseSpec spec; + MetadataToSpec(spec); + + // Transfer ownership to IPDL. + mDatabase->SetActorAlive(); + + if (!factory->SendPBackgroundIDBDatabaseConstructor(mDatabase, spec, this)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; +} + +void +OpenDatabaseOp::MetadataToSpec(DatabaseSpec& aSpec) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + DatabaseSpec& mSpec; + ObjectStoreSpec* mCurrentObjectStoreSpec; + + public: + static void + CopyToSpec(const FullDatabaseMetadata* aMetadata, DatabaseSpec& aSpec) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aMetadata); + + aSpec.metadata() = aMetadata->mCommonMetadata; + + Helper helper(aSpec); + aMetadata->mObjectStores.EnumerateRead(Enumerate, &helper); + } + + private: + Helper(DatabaseSpec& aSpec) + : mSpec(aSpec) + , mCurrentObjectStoreSpec(nullptr) + { } + + static PLDHashOperator + Enumerate(const uint64_t& aKey, + FullObjectStoreMetadata* aValue, + void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* helper = static_cast(aClosure); + + MOZ_ASSERT(!helper->mCurrentObjectStoreSpec); + + // XXX This should really be fallible... + ObjectStoreSpec* objectStoreSpec = + helper->mSpec.objectStores().AppendElement(); + objectStoreSpec->metadata() = aValue->mCommonMetadata; + + AutoRestore ar(helper->mCurrentObjectStoreSpec); + helper->mCurrentObjectStoreSpec = objectStoreSpec; + + aValue->mIndexes.EnumerateRead(Enumerate, helper); + + return PL_DHASH_NEXT; + } + + static PLDHashOperator + Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* helper = static_cast(aClosure); + + MOZ_ASSERT(helper->mCurrentObjectStoreSpec); + + // XXX This should really be fallible... + IndexMetadata* metadata = + helper->mCurrentObjectStoreSpec->indexes().AppendElement(); + *metadata = aValue->mCommonMetadata; + + return PL_DHASH_NEXT; + } + }; + + Helper::CopyToSpec(mMetadata, aSpec); +} + + +#ifdef DEBUG + +void +OpenDatabaseOp::AssertMetadataConsistency(const FullDatabaseMetadata* aMetadata) +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + const ObjectStoreTable& mOtherObjectStores; + IndexTable* mCurrentOtherIndexTable; + + public: + static void + AssertConsistent(const ObjectStoreTable& aThisObjectStores, + const ObjectStoreTable& aOtherObjectStores) + { + Helper helper(aOtherObjectStores); + aThisObjectStores.EnumerateRead(Enumerate, &helper); + } + + private: + Helper(const ObjectStoreTable& aOtherObjectStores) + : mOtherObjectStores(aOtherObjectStores) + , mCurrentOtherIndexTable(nullptr) + { } + + static PLDHashOperator + Enumerate(const uint64_t& /* aKey */, + FullObjectStoreMetadata* aThisObjectStore, + void* aClosure) + { + MOZ_ASSERT(aThisObjectStore); + MOZ_ASSERT(!aThisObjectStore->mDeleted); + MOZ_ASSERT(aClosure); + + auto* helper = static_cast(aClosure); + + MOZ_ASSERT(!helper->mCurrentOtherIndexTable); + + auto* otherObjectStore = + MetadataNameOrIdMatcher::Match( + helper->mOtherObjectStores, aThisObjectStore->mCommonMetadata.id()); + MOZ_ASSERT(otherObjectStore); + + MOZ_ASSERT(aThisObjectStore != otherObjectStore); + + MOZ_ASSERT(aThisObjectStore->mCommonMetadata.id() == + otherObjectStore->mCommonMetadata.id()); + MOZ_ASSERT(aThisObjectStore->mCommonMetadata.name() == + otherObjectStore->mCommonMetadata.name()); + MOZ_ASSERT(aThisObjectStore->mCommonMetadata.autoIncrement() == + otherObjectStore->mCommonMetadata.autoIncrement()); + MOZ_ASSERT(aThisObjectStore->mCommonMetadata.keyPath() == + otherObjectStore->mCommonMetadata.keyPath()); + // mNextAutoIncrementId and mComittedAutoIncrementId may be modified + // concurrently with this OpenOp, so it is not possible to assert equality + // here. + MOZ_ASSERT(aThisObjectStore->mNextAutoIncrementId <= + otherObjectStore->mNextAutoIncrementId); + MOZ_ASSERT(aThisObjectStore->mComittedAutoIncrementId <= + otherObjectStore->mComittedAutoIncrementId); + MOZ_ASSERT(!otherObjectStore->mDeleted); + + MOZ_ASSERT(aThisObjectStore->mIndexes.Count() == + otherObjectStore->mIndexes.Count()); + + AutoRestore ar(helper->mCurrentOtherIndexTable); + helper->mCurrentOtherIndexTable = &otherObjectStore->mIndexes; + + aThisObjectStore->mIndexes.EnumerateRead(Enumerate, helper); + + return PL_DHASH_NEXT; + } + + static PLDHashOperator + Enumerate(const uint64_t& /* aKey */, + FullIndexMetadata* aThisIndex, + void* aClosure) + { + MOZ_ASSERT(aThisIndex); + MOZ_ASSERT(!aThisIndex->mDeleted); + MOZ_ASSERT(aClosure); + + auto* helper = static_cast(aClosure); + + MOZ_ASSERT(helper->mCurrentOtherIndexTable); + + auto* otherIndex = + MetadataNameOrIdMatcher::Match( + *helper->mCurrentOtherIndexTable, aThisIndex->mCommonMetadata.id()); + MOZ_ASSERT(otherIndex); + + MOZ_ASSERT(aThisIndex != otherIndex); + + MOZ_ASSERT(aThisIndex->mCommonMetadata.id() == + otherIndex->mCommonMetadata.id()); + MOZ_ASSERT(aThisIndex->mCommonMetadata.name() == + otherIndex->mCommonMetadata.name()); + MOZ_ASSERT(aThisIndex->mCommonMetadata.keyPath() == + otherIndex->mCommonMetadata.keyPath()); + MOZ_ASSERT(aThisIndex->mCommonMetadata.unique() == + otherIndex->mCommonMetadata.unique()); + MOZ_ASSERT(aThisIndex->mCommonMetadata.multiEntry() == + otherIndex->mCommonMetadata.multiEntry()); + MOZ_ASSERT(!otherIndex->mDeleted); + + return PL_DHASH_NEXT; + } + }; + + const FullDatabaseMetadata* thisDB = mMetadata; + const FullDatabaseMetadata* otherDB = aMetadata; + + MOZ_ASSERT(thisDB); + MOZ_ASSERT(otherDB); + MOZ_ASSERT(thisDB != otherDB); + + MOZ_ASSERT(thisDB->mCommonMetadata.name() == otherDB->mCommonMetadata.name()); + MOZ_ASSERT(thisDB->mCommonMetadata.version() == + otherDB->mCommonMetadata.version()); + MOZ_ASSERT(thisDB->mCommonMetadata.persistenceType() == + otherDB->mCommonMetadata.persistenceType()); + MOZ_ASSERT(thisDB->mDatabaseId == otherDB->mDatabaseId); + MOZ_ASSERT(thisDB->mFilePath == otherDB->mFilePath); + + // The newer database metadata (db2) reflects the latest objectStore and index + // ids that have committed to disk. The in-memory metadata (db1) keeps track + // of objectStores and indexes that were created and then removed as well, so + // the next ids for db1 may be higher than for db2. + MOZ_ASSERT(thisDB->mNextObjectStoreId >= otherDB->mNextObjectStoreId); + MOZ_ASSERT(thisDB->mNextIndexId >= otherDB->mNextIndexId); + + MOZ_ASSERT(thisDB->mObjectStores.Count() == otherDB->mObjectStores.Count()); + + Helper::AssertConsistent(thisDB->mObjectStores, otherDB->mObjectStores); +} + +#endif // DEBUG + +nsresult +OpenDatabaseOp:: +VersionChangeOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); + + PROFILER_LABEL("IndexedDB", + "VersionChangeOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + mozIStorageConnection* connection = aTransaction->Connection(); + MOZ_ASSERT(connection); + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr stmt; + rv = connection->CreateStatement( + NS_LITERAL_CSTRING("UPDATE database " + "SET version = :version"), + getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"), + int64_t(mRequestedVersion)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +OpenDatabaseOp:: +VersionChangeOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenDatabaseOp); + MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); + + nsresult rv = mOpenDatabaseOp->SendUpgradeNeeded(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +bool +OpenDatabaseOp:: +VersionChangeOp::SendFailureResult(nsresult aResultCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mOpenDatabaseOp); + MOZ_ASSERT(mOpenDatabaseOp->mState == State_DatabaseWorkVersionChange); + + mOpenDatabaseOp->SetFailureCode(aResultCode); + mOpenDatabaseOp->mState = State_SendingResults; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOpenDatabaseOp->Run())); + + return false; +} + +void +OpenDatabaseOp:: +VersionChangeOp::Cleanup() +{ + AssertIsOnOwningThread(); + + mOpenDatabaseOp = nullptr; + +#ifdef DEBUG + // A bit hacky but the VersionChangeOp is not generated in response to a + // child request like most other database operations. Do this to make our + // assertions happy. + NoteActorDestroyed(); +#endif + + TransactionDatabaseOperationBase::Cleanup(); +} + +void +DeleteDatabaseOp::LoadPreviousVersion(nsIFile* aDatabaseFile) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(aDatabaseFile); + MOZ_ASSERT(mState == State_DatabaseWorkOpen); + MOZ_ASSERT(!mPreviousVersion); + + PROFILER_LABEL("IndexedDB", + "DeleteDatabaseOp::LoadPreviousVersion", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + nsCOMPtr ss = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsCOMPtr connection; + rv = ss->OpenDatabase(aDatabaseFile, getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + +#ifdef DEBUG + { + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT name " + "FROM database" + ), getter_AddRefs(stmt)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "CreateStatement failed!"); + + if (NS_SUCCEEDED(rv)) { + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "ExecuteStep failed!"); + + if (NS_SUCCEEDED(rv)) { + nsString databaseName; + rv = stmt->GetString(0, databaseName); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "GetString failed!"); + + if (NS_SUCCEEDED(rv)) { + NS_WARN_IF_FALSE(mCommonParams.metadata().name() == databaseName, + "Database names don't match!"); + } + } + } + } +#endif + + nsCOMPtr stmt; + rv = connection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT version " + "FROM database" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (NS_WARN_IF(!hasResult)) { + return; + } + + int64_t version; + rv = stmt->GetInt64(0, &version); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + mPreviousVersion = uint64_t(version); +} + +nsresult +DeleteDatabaseOp::QuotaManagerOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_OpenPending); + + // Swap this to the stack now to ensure that we release it on this thread. + nsRefPtr contentParent; + mContentParent.swap(contentParent); + + nsresult rv = SendToIOThread(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +DeleteDatabaseOp::DoDatabaseWork() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mState == State_DatabaseWorkOpen); + + PROFILER_LABEL("IndexedDB", + "DeleteDatabaseOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (!OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + const nsString& databaseName = mCommonParams.metadata().name(); + PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsCOMPtr directory; + nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType, + mOrigin, + getter_AddRefs(directory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = directory->GetPath(mDatabaseDirectoryPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoString filename; + GetDatabaseFilename(databaseName, filename); + + mDatabaseFilenameBase = filename; + + nsCOMPtr dbFile; + rv = directory->Clone(getter_AddRefs(dbFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool exists; + rv = dbFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + // Parts of this function may fail but that shouldn't prevent us from + // deleting the file eventually. + LoadPreviousVersion(dbFile); + + mState = State_BeginVersionChange; + } else { + mState = State_SendingResults; + } + + rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +DeleteDatabaseOp::BeginVersionChange() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_BeginVersionChange); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + + if (IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + DatabaseActorInfo* info; + if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) { + MOZ_ASSERT(!info->mWaitingFactoryOp); + + NullableVersion newVersion = null_t(); + + nsresult rv = + SendVersionChangeMessages(info, nullptr, mPreviousVersion, newVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!mMaybeBlockedDatabases.IsEmpty()) { + info->mWaitingFactoryOp = this; + + mState = State_WaitingForOtherDatabasesToClose; + return NS_OK; + } + } + + // No other databases need to be notified, just make sure that all + // transactions are complete. + WaitForTransactions(); + return NS_OK; +} + +nsresult +DeleteDatabaseOp::DispatchToWorkThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete); + MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); + + if (IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mState = State_DatabaseWorkVersionChange; + + nsRefPtr versionChangeOp = new VersionChangeOp(this); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(versionChangeOp))); + + return NS_OK; +} + +void +DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); + MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty()); + + bool actorDestroyed = IsActorDestroyed(); + + nsresult rv; + if (actorDestroyed) { + IDB_REPORT_INTERNAL_ERR(); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } else { + rv = NS_OK; + } + + if (mMaybeBlockedDatabases.RemoveElement(aDatabase) && + mMaybeBlockedDatabases.IsEmpty()) { + if (actorDestroyed) { + DatabaseActorInfo* info; + MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info)); + MOZ_ASSERT(info->mWaitingFactoryOp == this); + info->mWaitingFactoryOp = nullptr; + } else { + WaitForTransactions(); + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + mState = State_SendingResults; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run())); + } +} + +void +DeleteDatabaseOp::SendBlockedNotification() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_WaitingForOtherDatabasesToClose); + + if (!IsActorDestroyed()) { + unused << SendBlocked(0); + } +} + +void +DeleteDatabaseOp::SendResults() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_SendingResults); + + if (!IsActorDestroyed()) { + FactoryRequestResponse response; + + if (NS_SUCCEEDED(mResultCode)) { + response = DeleteDatabaseRequestResponse(mPreviousVersion); + } else { + response = ClampResultCode(mResultCode); + } + + NS_WARN_IF(!PBackgroundIDBFactoryRequestParent::Send__delete__(this, + response)); + } + + FinishSendResults(); +} + +nsresult +DeleteDatabaseOp:: +VersionChangeOp::RunOnMainThread() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return NS_OK; +} + +nsresult +DeleteDatabaseOp:: +VersionChangeOp::RunOnIOThread() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); + + PROFILER_LABEL("IndexedDB", + "DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", + js::ProfileEntry::Category::STORAGE); + + if (!OperationMayProceed()) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr directory = + GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath); + if (NS_WARN_IF(!directory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr dbFile; + nsresult rv = directory->Clone(getter_AddRefs(dbFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase + + NS_LITERAL_STRING(".sqlite")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool exists; + rv = dbFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + const nsString& databaseName = + mDeleteDatabaseOp->mCommonParams.metadata().name(); + PersistenceType persistenceType = + mDeleteDatabaseOp->mCommonParams.metadata().persistenceType(); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + if (exists) { + int64_t fileSize; + + if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { + rv = dbFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = dbFile->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { + quotaManager->DecreaseUsageForOrigin(persistenceType, + mDeleteDatabaseOp->mGroup, + mDeleteDatabaseOp->mOrigin, + fileSize); + } + } + + nsCOMPtr dbJournalFile; + rv = directory->Clone(getter_AddRefs(dbJournalFile)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbJournalFile->Append(mDeleteDatabaseOp->mDatabaseFilenameBase + + NS_LITERAL_STRING(".sqlite-journal")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = dbJournalFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + rv = dbJournalFile->Remove(false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + nsCOMPtr fmDirectory; + rv = directory->Clone(getter_AddRefs(fmDirectory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = fmDirectory->Append(mDeleteDatabaseOp->mDatabaseFilenameBase); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = fmDirectory->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (exists) { + bool isDirectory; + rv = fmDirectory->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!isDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + uint64_t usage = 0; + + if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { + rv = FileManager::GetUsage(fmDirectory, &usage); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = fmDirectory->Remove(true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mDeleteDatabaseOp->mStoragePrivilege != Chrome) { + quotaManager->DecreaseUsageForOrigin(persistenceType, + mDeleteDatabaseOp->mGroup, + mDeleteDatabaseOp->mOrigin, + usage); + } + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); + MOZ_ASSERT(mgr); + + mgr->InvalidateFileManager(persistenceType, + mDeleteDatabaseOp->mOrigin, + databaseName); + + rv = mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +DeleteDatabaseOp:: +VersionChangeOp::RunOnOwningThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mDeleteDatabaseOp->mState == State_DatabaseWorkVersionChange); + + nsRefPtr deleteOp; + mDeleteDatabaseOp.swap(deleteOp); + + if (deleteOp->IsActorDestroyed()) { + IDB_REPORT_INTERNAL_ERR(); + deleteOp->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } else { + DatabaseActorInfo* info; + if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId, &info) && + info->mWaitingFactoryOp) { + MOZ_ASSERT(info->mWaitingFactoryOp == deleteOp); + info->mWaitingFactoryOp = nullptr; + } + + if (NS_FAILED(mResultCode)) { + if (NS_SUCCEEDED(deleteOp->ResultCode())) { + deleteOp->SetFailureCode(mResultCode); + } + } else { + // Inform all the other databases that they are now invalidated. That + // should remove the previous metadata from our table. + if (info) { + MOZ_ASSERT(!info->mLiveDatabases.IsEmpty()); + + FallibleTArray liveDatabases; + if (NS_WARN_IF(!liveDatabases.AppendElements(info->mLiveDatabases))) { + deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY); + } else { +#ifdef DEBUG + // The code below should result in the deletion of |info|. Set to null + // here to make sure we find invalid uses later. + info = nullptr; +#endif + for (uint32_t count = liveDatabases.Length(), index = 0; + index < count; + index++) { + nsRefPtr database = liveDatabases[index]; + database->Invalidate(); + } + + MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId)); + } + } + } + } + + deleteOp->mState = State_SendingResults; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(deleteOp->Run())); + +#ifdef DEBUG + // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a + // normal database operation that is tied to an actor. Do this to make our + // assertions happy. + NoteActorDestroyed(); +#endif +} + +nsresult +DeleteDatabaseOp:: +VersionChangeOp::Run() +{ + nsresult rv; + + if (NS_IsMainThread()) { + rv = RunOnMainThread(); + } else if (!IsOnBackgroundThread()) { + rv = RunOnIOThread(); + } else { + RunOnOwningThread(); + rv = NS_OK; + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = rv; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); + } + + return NS_OK; +} + +TransactionDatabaseOperationBase::TransactionDatabaseOperationBase( + TransactionBase* aTransaction) + : mTransaction(aTransaction) + , mTransactionIsAborted(aTransaction->IsAborted()) +{ + MOZ_ASSERT(aTransaction); +} + +TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase() +{ + MOZ_ASSERT(!mTransaction, + "TransactionDatabaseOperationBase::Cleanup() was not called by a " + "subclass!"); +} + +#ifdef DEBUG + +void +TransactionDatabaseOperationBase::AssertIsOnTransactionThread() const +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnTransactionThread(); +} + +#endif // DEBUG + +void +TransactionDatabaseOperationBase::DispatchToTransactionThreadPool() +{ + AssertIsOnOwningThread(); + + TransactionThreadPool* threadPool = mTransaction->GetTransactionThreadPool(); + MOZ_ASSERT(threadPool); + + threadPool->Dispatch(mTransaction->TransactionId(), + mTransaction->DatabaseId(), + this, + /* aFinish */ false, + /* aFinishCallback */ nullptr); + + mTransaction->NoteActiveRequest(); +} + +void +TransactionDatabaseOperationBase::RunOnTransactionThread() +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mTransaction); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + + // There are several cases where we don't actually have to to any work here. + + if (mTransactionIsAborted) { + // This transaction is already set to be aborted. + mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } else if (mTransaction->IsInvalidatedOnAnyThread()) { + // This transaction is being invalidated. + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } else if (!OperationMayProceed()) { + // The operation was canceled in some way, likely because the child process + // has crashed. + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } else { + // Here we're actually going to perform the database operation. + nsresult rv = mTransaction->EnsureConnection(); + if (NS_WARN_IF(NS_FAILED(rv))) { + mResultCode = rv; + } else { + mTransaction->AssertIsOnTransactionThread(); + + AutoSetProgressHandler autoProgress; + rv = autoProgress.Register(this, mTransaction->Connection()); + if (NS_WARN_IF(NS_FAILED(rv))) { + mResultCode = rv; + } else { + rv = DoDatabaseWork(mTransaction); + if (NS_FAILED(rv)) { + mResultCode = rv; + } + } + } + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); +} + +void +TransactionDatabaseOperationBase::RunOnOwningThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + if (NS_WARN_IF(IsActorDestroyed())) { + // Don't send any notifications if the actor was destroyed already. + if (NS_SUCCEEDED(mResultCode)) { + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } else { + if (mTransaction->IsInvalidated()) { + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } else if (mTransaction->IsAborted()) { + // Aborted transactions always see their requests fail with ABORT_ERR, + // even if the request succeeded or failed with another error. + mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } else if (NS_SUCCEEDED(mResultCode)) { + // This may release the IPDL reference. + mResultCode = SendSuccessResult(); + } + + if (NS_FAILED(mResultCode)) { + // This should definitely release the IPDL reference. + if (!SendFailureResult(mResultCode)) { + // Abort the transaction. + mTransaction->Abort(mResultCode, /* aForce */ false); + } + } + } + + mTransaction->NoteFinishedRequest(); + + Cleanup(); +} + +bool +TransactionDatabaseOperationBase::Init(TransactionBase* aTransaction) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + + return true; +} + +void +TransactionDatabaseOperationBase::Cleanup() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); + + mTransaction = nullptr; +} + +NS_IMETHODIMP +TransactionDatabaseOperationBase::Run() +{ + MOZ_ASSERT(mTransaction); + + if (IsOnBackgroundThread()) { + RunOnOwningThread(); + } else { + RunOnTransactionThread(); + } + + return NS_OK; +} + +nsresult +TransactionBase:: +CommitOp::WriteAutoIncrementCounts() +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(mTransaction); + + const nsTArray>& metadataArray = + mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray; + + nsCOMPtr stmt; + nsresult rv; + + if (!metadataArray.IsEmpty()) { + NS_NAMED_LITERAL_CSTRING(osid, "osid"); + NS_NAMED_LITERAL_CSTRING(ai, "ai"); + + for (uint32_t count = metadataArray.Length(), index = 0; + index < count; + index++) { + const nsRefPtr& metadata = metadataArray[index]; + MOZ_ASSERT(!metadata->mDeleted); + MOZ_ASSERT(metadata->mNextAutoIncrementId > 1); + + if (stmt) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(stmt->Reset())); + } else { + rv = mTransaction->mConnection->CreateStatement( + NS_LITERAL_CSTRING("UPDATE object_store " + "SET auto_increment = :") + ai + + NS_LITERAL_CSTRING(" WHERE id = :") + osid + + NS_LITERAL_CSTRING(";"), + getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = stmt->BindInt64ByName(osid, metadata->mCommonMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(ai, metadata->mNextAutoIncrementId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + + return NS_OK; +} + +void +TransactionBase:: +CommitOp::CommitOrRollbackAutoIncrementCounts() +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(mTransaction); + + nsTArray>& metadataArray = + mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray; + + if (!metadataArray.IsEmpty()) { + bool committed = NS_SUCCEEDED(mResultCode); + + for (uint32_t count = metadataArray.Length(), index = 0; + index < count; + index++) { + nsRefPtr& metadata = metadataArray[index]; + + if (committed) { + metadata->mComittedAutoIncrementId = metadata->mNextAutoIncrementId; + } else { + metadata->mNextAutoIncrementId = metadata->mComittedAutoIncrementId; + } + } + } +} + +NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp, nsRunnable) + +NS_IMETHODIMP +TransactionBase:: +CommitOp::Run() +{ + MOZ_ASSERT(mTransaction); + + PROFILER_LABEL("IndexedDB", + "CommitOp::Run", + js::ProfileEntry::Category::STORAGE); + + nsCOMPtr& connection = mTransaction->mConnection; + + if (!connection) { + return NS_OK; + } + + AssertIsOnTransactionThread(); + + if (NS_SUCCEEDED(mResultCode) && mTransaction->mUpdateFileRefcountFunction) { + mResultCode = mTransaction-> + mUpdateFileRefcountFunction->WillCommit(connection); + } + + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = WriteAutoIncrementCounts(); + } + + if (NS_SUCCEEDED(mResultCode)) { + NS_NAMED_LITERAL_CSTRING(commit, "COMMIT TRANSACTION"); + mResultCode = connection->ExecuteSimpleSQL(commit); + + if (NS_SUCCEEDED(mResultCode)) { + if (mTransaction->mUpdateFileRefcountFunction) { + mTransaction->mUpdateFileRefcountFunction->DidCommit(); + } + } + } + + if (NS_FAILED(mResultCode)) { + if (mTransaction->mUpdateFileRefcountFunction) { + mTransaction->mUpdateFileRefcountFunction->DidAbort(); + } + + // This may fail if SQLite already rolled back the transaction so ignore any + // errors. + unused << + connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION")); + } + + CommitOrRollbackAutoIncrementCounts(); + + if (mTransaction->mUpdateFileRefcountFunction) { + NS_NAMED_LITERAL_CSTRING(functionName, "update_refcount"); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(connection->RemoveFunction(functionName))); + } + + mTransaction->ReleaseTransactionThreadObjects(); + + return NS_OK; +} + +void +TransactionBase:: +CommitOp::TransactionFinishedBeforeUnblock() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransaction); + + PROFILER_LABEL("IndexedDB", + "CommitOp::TransactionFinishedBeforeUnblock", + js::ProfileEntry::Category::STORAGE); + + if (!IsActorDestroyed()) { + mTransaction->UpdateMetadata(mResultCode); + } +} + +void +TransactionBase:: +CommitOp::TransactionFinishedAfterUnblock() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mTransaction); + + PROFILER_LABEL("IndexedDB", + "CommitOp::TransactionFinishedAfterUnblock", + js::ProfileEntry::Category::STORAGE); + + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", + "IDBTransaction[%llu] MT Complete", + mTransaction->TransactionId(), mResultCode); + + mTransaction->ReleaseBackgroundThreadObjects(); + + if (!mTransaction->IsActorDestroyed()) { + mTransaction->SendCompleteNotification(ClampResultCode(mResultCode)); + } + + mTransaction->GetDatabase()->UnregisterTransaction(mTransaction); + + mTransaction = nullptr; + +#ifdef DEBUG + // A bit hacky but the CommitOp is not really a normal database operation + // that is tied to an actor. Do this to make our assertions happy. + NoteActorDestroyed(); +#endif +} + +NS_IMPL_ISUPPORTS(TransactionBase::UpdateRefcountFunction, mozIStorageFunction) + +NS_IMETHODIMP +TransactionBase:: +UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues, + nsIVariant** _retval) +{ + MOZ_ASSERT(aValues); + MOZ_ASSERT(_retval); + + uint32_t numEntries; + nsresult rv = aValues->GetNumEntries(&numEntries); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(numEntries == 2); + +#ifdef DEBUG + { + int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL; + MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(0, &type1))); + + int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL; + MOZ_ASSERT(NS_SUCCEEDED(aValues->GetTypeOfIndex(1, &type2))); + + MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL && + type2 == mozIStorageValueArray::VALUE_TYPE_NULL)); + } +#endif + + rv = ProcessValue(aValues, 0, eDecrement); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = ProcessValue(aValues, 1, eIncrement); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +TransactionBase:: +UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(aConnection); + + DatabaseUpdateFunction function(aConnection, this); + + mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function); + + nsresult rv = function.ErrorCode(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = CreateJournals(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +TransactionBase:: +UpdateRefcountFunction::DidCommit() +{ + mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr); + + if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterCommit))) { + NS_WARNING("RemoveJournals failed!"); + } +} + +void +TransactionBase:: +UpdateRefcountFunction::DidAbort() +{ + if (NS_FAILED(RemoveJournals(mJournalsToRemoveAfterAbort))) { + NS_WARNING("RemoveJournals failed!"); + } +} + +void +TransactionBase:: +UpdateRefcountFunction::StartSavepoint() +{ + MOZ_ASSERT(!mInSavepoint); + MOZ_ASSERT(!mSavepointEntriesIndex.Count()); + + mInSavepoint = true; +} + +void +TransactionBase:: +UpdateRefcountFunction::ReleaseSavepoint() +{ + MOZ_ASSERT(mInSavepoint); + + mSavepointEntriesIndex.Clear(); + mInSavepoint = false; +} + +void +TransactionBase:: +UpdateRefcountFunction::RollbackSavepoint() +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mInSavepoint); + + struct Helper + { + static PLDHashOperator + Rollback(const uint64_t& aKey, FileInfoEntry* aValue, void* /* aUserArg */) + { + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aValue); + + aValue->mDelta -= aValue->mSavepointDelta; + return PL_DHASH_NEXT; + } + }; + + mSavepointEntriesIndex.EnumerateRead(Helper::Rollback, nullptr); + + mInSavepoint = false; + mSavepointEntriesIndex.Clear(); +} + +nsresult +TransactionBase:: +UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues, + int32_t aIndex, + UpdateType aUpdateType) +{ + MOZ_ASSERT(aValues); + + int32_t type; + nsresult rv = aValues->GetTypeOfIndex(aIndex, &type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (type == mozIStorageValueArray::VALUE_TYPE_NULL) { + return NS_OK; + } + + nsString ids; + rv = aValues->GetString(aIndex, ids); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsTArray fileIds; + rv = ConvertFileIdsToArray(ids, fileIds); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (uint32_t i = 0; i < fileIds.Length(); i++) { + int64_t id = fileIds.ElementAt(i); + + FileInfoEntry* entry; + if (!mFileInfoEntries.Get(id, &entry)) { + nsRefPtr fileInfo = mFileManager->GetFileInfo(id); + MOZ_ASSERT(fileInfo); + + entry = new FileInfoEntry(fileInfo); + mFileInfoEntries.Put(id, entry); + } + + if (mInSavepoint) { + mSavepointEntriesIndex.Put(id, entry); + } + + switch (aUpdateType) { + case eIncrement: + entry->mDelta++; + if (mInSavepoint) { + entry->mSavepointDelta++; + } + break; + case eDecrement: + entry->mDelta--; + if (mInSavepoint) { + entry->mSavepointDelta--; + } + break; + default: + MOZ_CRASH("Unknown update type!"); + } + } + + return NS_OK; +} + +nsresult +TransactionBase:: +UpdateRefcountFunction::CreateJournals() +{ + nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); + if (NS_WARN_IF(!journalDirectory)) { + return NS_ERROR_FAILURE; + } + + for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) { + int64_t id = mJournalsToCreateBeforeCommit[i]; + + nsCOMPtr file = + mFileManager->GetFileForId(journalDirectory, id); + if (NS_WARN_IF(!file)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mJournalsToRemoveAfterAbort.AppendElement(id); + } + + return NS_OK; +} + +nsresult +TransactionBase:: +UpdateRefcountFunction::RemoveJournals(const nsTArray& aJournals) +{ + nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); + if (NS_WARN_IF(!journalDirectory)) { + return NS_ERROR_FAILURE; + } + + for (uint32_t index = 0; index < aJournals.Length(); index++) { + nsCOMPtr file = + mFileManager->GetFileForId(journalDirectory, aJournals[index]); + if (NS_WARN_IF(!file)) { + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(file->Remove(false))) { + NS_WARNING("Failed to removed journal!"); + } + } + + return NS_OK; +} + +PLDHashOperator +TransactionBase:: +UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg) +{ + MOZ_ASSERT(aValue); + MOZ_ASSERT(aUserArg); + + if (!aValue->mDelta) { + return PL_DHASH_NEXT; + } + + auto function = static_cast(aUserArg); + + if (!function->Update(aKey, aValue->mDelta)) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + +PLDHashOperator +TransactionBase:: +UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey, + FileInfoEntry* aValue, + void* aUserArg) +{ + MOZ_ASSERT(aValue); + + if (aValue->mDelta) { + aValue->mFileInfo->UpdateDBRefs(aValue->mDelta); + } + + return PL_DHASH_NEXT; +} + +bool +TransactionBase::UpdateRefcountFunction:: +DatabaseUpdateFunction::Update(int64_t aId, + int32_t aDelta) +{ + nsresult rv = UpdateInternal(aId, aDelta); + if (NS_FAILED(rv)) { + mErrorCode = rv; + return false; + } + + return true; +} + +nsresult +TransactionBase::UpdateRefcountFunction:: +DatabaseUpdateFunction::UpdateInternal(int64_t aId, + int32_t aDelta) +{ + nsresult rv; + + if (!mUpdateStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE file " + "SET refcount = refcount + :delta " + "WHERE id = :id" + ), getter_AddRefs(mUpdateStatement)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mozStorageStatementScoper updateScoper(mUpdateStatement); + + rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mUpdateStatement->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t rows; + rv = mConnection->GetAffectedRows(&rows); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (rows > 0) { + if (!mSelectStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id " + "FROM file " + "WHERE id = :id" + ), getter_AddRefs(mSelectStatement)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mozStorageStatementScoper selectScoper(mSelectStatement); + + rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = mSelectStatement->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + // Don't have to create the journal here, we can create all at once, + // just before commit + mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId); + } + + return NS_OK; + } + + if (!mInsertStatement) { + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( + "INSERT INTO file (id, refcount) " + "VALUES(:id, :delta)" + ), getter_AddRefs(mInsertStatement)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mozStorageStatementScoper insertScoper(mInsertStatement); + + rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mInsertStatement->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId); + return NS_OK; +} + +TransactionBase:: +AutoSavepoint::~AutoSavepoint() +{ + if (mTransaction) { + mTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::READ_WRITE || + mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); + + NS_WARN_IF(NS_FAILED(mTransaction->RollbackSavepoint())); + } +} + +nsresult +TransactionBase:: +AutoSavepoint::Start(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(aTransaction->GetMode() == IDBTransaction::READ_WRITE || + aTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); + MOZ_ASSERT(!mTransaction); + + nsresult rv = aTransaction->StartSavepoint(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mTransaction = aTransaction; + + return NS_OK; +} + +nsresult +TransactionBase:: +AutoSavepoint::Commit() +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnTransactionThread(); + + nsresult rv = mTransaction->ReleaseSavepoint(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mTransaction = nullptr; + + return NS_OK; +} + +nsresult +VersionChangeTransactionOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + + // Nothing to send here, the API assumes that this request always succeeds. + return NS_OK; +} + +bool +VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode) +{ + AssertIsOnOwningThread(); + + // The only option here is to cause the transaction to abort. + return false; +} + +void +VersionChangeTransactionOp::Cleanup() +{ + AssertIsOnOwningThread(); + +#ifdef DEBUG + // A bit hacky but the VersionChangeTransactionOp is not generated in response + // to a child request like most other database operations. Do this to make our + // assertions happy. + NoteActorDestroyed(); +#endif + + TransactionDatabaseOperationBase::Cleanup(); +} + +nsresult +CreateObjectStoreOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "CreateObjectStoreOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "INSERT INTO object_store (id, auto_increment, name, key_path) " + "VALUES (:id, :auto_increment, :name, :key_path)", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), + mMetadata.autoIncrement() ? 1 : 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + NS_NAMED_LITERAL_CSTRING(keyPath, "key_path"); + + if (mMetadata.keyPath().IsValid()) { + nsAutoString keyPathSerialization; + mMetadata.keyPath().SerializeToString(keyPathSerialization); + + rv = stmt->BindStringByName(keyPath, keyPathSerialization); + } else { + rv = stmt->BindNullByName(keyPath); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#ifdef DEBUG + { + int64_t id; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + aTransaction->Connection()->GetLastInsertRowID(&id))); + MOZ_ASSERT(mMetadata.id() == id); + } +#endif + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +DeleteObjectStoreOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "DeleteObjectStoreOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "DELETE FROM object_store " + "WHERE id = :id", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), + mMetadata->mCommonMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (mMetadata->mCommonMetadata.autoIncrement()) { + aTransaction->ForgetModifiedAutoIncrementObjectStore(mMetadata); + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +CreateIndexOp::CreateIndexOp(VersionChangeTransaction* aTransaction, + const int64_t aObjectStoreId, + const IndexMetadata& aMetadata) + : VersionChangeTransactionOp(aTransaction) + , mMetadata(aMetadata) + , mFileManager(aTransaction->GetDatabase()->GetFileManager()) + , mDatabaseId(aTransaction->DatabaseId()) + , mObjectStoreId(aObjectStoreId) +{ + MOZ_ASSERT(aObjectStoreId); + MOZ_ASSERT(aMetadata.id()); + MOZ_ASSERT(mFileManager); + MOZ_ASSERT(!mDatabaseId.IsEmpty()); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static void + CopyUniqueValues(const IndexTable& aIndexes, + Maybe& aMaybeUniqueIndexTable) + { + aMaybeUniqueIndexTable.emplace(); + + const uint32_t indexCount = aIndexes.Count(); + MOZ_ASSERT(indexCount); + + aIndexes.EnumerateRead(Enumerate, aMaybeUniqueIndexTable.ptr()); + + if (NS_WARN_IF(aMaybeUniqueIndexTable.ref().Count() != indexCount)) { + aMaybeUniqueIndexTable.reset(); + return; + } + +#ifdef DEBUG + aMaybeUniqueIndexTable.ref().MarkImmutable(); +#endif + } + + private: + static PLDHashOperator + Enumerate(const uint64_t& aKey, FullIndexMetadata* aValue, void* aClosure) + { + auto* uniqueIndexTable = static_cast(aClosure); + MOZ_ASSERT(uniqueIndexTable); + MOZ_ASSERT(!uniqueIndexTable->Get(aValue->mCommonMetadata.id())); + + if (NS_WARN_IF(!uniqueIndexTable->Put(aValue->mCommonMetadata.id(), + aValue->mCommonMetadata.unique(), + fallible))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; + } + }; + + InitThreadLocals(); + + const nsRefPtr objectStoreMetadata = + aTransaction->GetMetadataForObjectStoreId(aObjectStoreId); + MOZ_ASSERT(objectStoreMetadata); + + Helper::CopyUniqueValues(objectStoreMetadata->mIndexes, + mMaybeUniqueIndexTable); +} + +unsigned int CreateIndexOp::sThreadLocalIndex = kBadThreadLocalIndex; + +// static +void +CreateIndexOp::InitThreadLocals() +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static void + Destroy(void* aThreadLocal) + { + delete static_cast(aThreadLocal); + } + }; + + if (sThreadLocalIndex == kBadThreadLocalIndex) { + if (NS_WARN_IF(PR_SUCCESS != + PR_NewThreadPrivateIndex(&sThreadLocalIndex, + &Helper::Destroy))) { + return; + } + } + + MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex); +} + +nsresult +CreateIndexOp::InsertDataFromObjectStore(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode()); + MOZ_ASSERT(mMaybeUniqueIndexTable); + + PROFILER_LABEL("IndexedDB", + "CreateIndexOp::InsertDataFromObjectStore", + js::ProfileEntry::Category::STORAGE); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement( + "SELECT id, data, file_ids, key_value " + "FROM object_data " + "WHERE object_store_id = :osid", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + // Bail early if we have no data to avoid creating the runtime below. + return NS_OK; + } + + ThreadLocalJSRuntime* runtime = ThreadLocalJSRuntime::GetOrCreate(); + if (NS_WARN_IF(!runtime)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + JSContext* cx = runtime->Context(); + JSAutoRequest ar(cx); + JSAutoCompartment ac(cx, runtime->Global()); + + do { + StructuredCloneReadInfo cloneInfo; + rv = GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, mFileManager, + &cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + JS::Rooted clone(cx); + if (NS_WARN_IF(!IDBObjectStore::DeserializeIndexValue(cx, cloneInfo, + &clone))) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + + nsTArray updateInfo; + rv = IDBObjectStore::AppendIndexUpdateInfo(mMetadata.id(), + mMetadata.keyPath(), + mMetadata.unique(), + mMetadata.multiEntry(), + cx, + clone, + updateInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t objectDataId = stmt->AsInt64(0); + + Key key; + rv = key.SetFromStatement(stmt, 3); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = UpdateIndexes(aTransaction, + mMaybeUniqueIndexTable.ref(), + key, + false, + objectDataId, + updateInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +bool +CreateIndexOp::Init(TransactionBase* aTransaction) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + + if (NS_WARN_IF(!mMaybeUniqueIndexTable) || + NS_WARN_IF(sThreadLocalIndex == kBadThreadLocalIndex)) { + return false; + } + + return true; +} + +nsresult +CreateIndexOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "CreateIndexOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "INSERT INTO object_store_index (id, name, key_path, unique_index, " + "multientry, object_store_id) " + "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mMetadata.name()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoString keyPathSerialization; + mMetadata.keyPath().SerializeToString(keyPathSerialization); + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), + keyPathSerialization); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"), + mMetadata.unique() ? 1 : 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), + mMetadata.multiEntry() ? 1 : 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + +#ifdef DEBUG + { + int64_t id; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + aTransaction->Connection()->GetLastInsertRowID(&id))); + MOZ_ASSERT(mMetadata.id() == id); + } +#endif + + rv = InsertDataFromObjectStore(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +const JSClass CreateIndexOp::ThreadLocalJSRuntime::kGlobalClass = { + "IndexedDBTransactionThreadGlobal", + JSCLASS_GLOBAL_FLAGS, + /* addProperty*/ JS_PropertyStub, + /* delProperty */ JS_DeletePropertyStub, + /* getProperty */ JS_PropertyStub, + /* setProperty */ JS_StrictPropertyStub, + /* enumerate */ JS_EnumerateStub, + /* resolve */ JS_ResolveStub, + /* convert */ JS_ConvertStub, + /* finalize */ nullptr, + /* call */ nullptr, + /* hasInstance */ nullptr, + /* construct */ nullptr, + /* trace */ JS_GlobalObjectTraceHook +}; + +// static +auto +CreateIndexOp:: +ThreadLocalJSRuntime::GetOrCreate() -> ThreadLocalJSRuntime* +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(CreateIndexOp::kBadThreadLocalIndex != + CreateIndexOp::sThreadLocalIndex); + + auto* runtime = static_cast( + PR_GetThreadPrivate(CreateIndexOp::sThreadLocalIndex)); + if (runtime) { + return runtime; + } + + nsAutoPtr newRuntime(new ThreadLocalJSRuntime()); + + if (NS_WARN_IF(!newRuntime->Init())) { + return nullptr; + } + + DebugOnly status = + PR_SetThreadPrivate(CreateIndexOp::sThreadLocalIndex, newRuntime); + MOZ_ASSERT(status == PR_SUCCESS); + + return newRuntime.forget(); +} + +bool +CreateIndexOp:: +ThreadLocalJSRuntime::Init() +{ + MOZ_ASSERT(!IsOnBackgroundThread()); + + mRuntime = JS_NewRuntime(kRuntimeHeapSize); + if (NS_WARN_IF(!mRuntime)) { + return false; + } + + // Not setting this will cause JS_CHECK_RECURSION to report false positives. + JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); + + mContext = JS_NewContext(mRuntime, 0); + if (NS_WARN_IF(!mContext)) { + return false; + } + + JSAutoRequest ar(mContext); + + mGlobal = JS_NewGlobalObject(mContext, &kGlobalClass, nullptr, + JS::FireOnNewGlobalHook); + if (NS_WARN_IF(!mGlobal)) { + return false; + } + + return true; +} + +nsresult +DeleteIndexOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "DeleteIndexOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "DELETE FROM object_store_index " + "WHERE id = :id ", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndexId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +NormalTransactionOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + + if (!IsActorDestroyed()) { + RequestResponse response; + GetResponse(response); + + MOZ_ASSERT(response.type() != RequestResponse::T__None); + + if (response.type() == RequestResponse::Tnsresult) { + MOZ_ASSERT(NS_FAILED(response.get_nsresult())); + + return response.get_nsresult(); + } + + if (NS_WARN_IF(!PBackgroundIDBRequestParent::Send__delete__(this, + response))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } + + mResponseSent = true; + + return NS_OK; +} + +bool +NormalTransactionOp::SendFailureResult(nsresult aResultCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResultCode)); + + bool result = false; + + if (!IsActorDestroyed()) { + result = + PBackgroundIDBRequestParent::Send__delete__(this, + ClampResultCode(aResultCode)); + } + + mResponseSent = true; + + return result; +} + +void +NormalTransactionOp::Cleanup() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent); + + TransactionDatabaseOperationBase::Cleanup(); +} + +void +NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteActorDestroyed(); +} + +ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp( + TransactionBase* aTransaction, + const RequestParams& aParams) + : NormalTransactionOp(aTransaction) + , mParams(aParams.type() == RequestParams::TObjectStoreAddParams ? + aParams.get_ObjectStoreAddParams().commonParams() : + aParams.get_ObjectStorePutParams().commonParams()) + , mGroup(aTransaction->GetDatabase()->Group()) + , mOrigin(aTransaction->GetDatabase()->Origin()) + , mPersistenceType(aTransaction->GetDatabase()->Type()) + , mOverwrite(aParams.type() == RequestParams::TObjectStorePutParams) +{ + MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreAddParams || + aParams.type() == RequestParams::TObjectStorePutParams); + + mMetadata = + aTransaction->GetMetadataForObjectStoreId(mParams.objectStoreId()); + MOZ_ASSERT(mMetadata); +} + +nsresult +ObjectStoreAddOrPutRequestOp::CopyFileData(nsIInputStream* aInputStream, + nsIOutputStream* aOutputStream) +{ + AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreAddOrPutRequestOp::CopyFileData", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + do { + char copyBuffer[kFileCopyBufferSize]; + + uint32_t numRead; + rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); + if (NS_WARN_IF(NS_FAILED(rv))) { + break; + } + + if (!numRead) { + break; + } + + uint32_t numWrite; + rv = aOutputStream->Write(copyBuffer, numRead, &numWrite); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + break; + } + + if (NS_WARN_IF(numWrite != numRead)) { + rv = NS_ERROR_FAILURE; + break; + } + } while (true); + + nsresult rv2 = aOutputStream->Flush(); + if (NS_WARN_IF(NS_FAILED(rv2))) { + return NS_SUCCEEDED(rv) ? rv2 : rv; + } + + rv2 = aOutputStream->Close(); + if (NS_WARN_IF(NS_FAILED(rv2))) { + return NS_SUCCEEDED(rv) ? rv2 : rv; + } + + return rv; +} + +bool +ObjectStoreAddOrPutRequestOp::Init(TransactionBase* aTransaction) +{ + AssertIsOnOwningThread(); + + const nsTArray& indexUpdateInfos = + mParams.indexUpdateInfos(); + + if (!indexUpdateInfos.IsEmpty()) { + const uint32_t count = indexUpdateInfos.Length(); + + mUniqueIndexTable.emplace(); + + for (uint32_t index = 0; index < count; index++) { + const IndexUpdateInfo& updateInfo = indexUpdateInfos[index]; + + nsRefPtr indexMetadata; + MOZ_ALWAYS_TRUE(mMetadata->mIndexes.Get(updateInfo.indexId(), + getter_AddRefs(indexMetadata))); + + MOZ_ASSERT(!indexMetadata->mDeleted); + + const int64_t& indexId = indexMetadata->mCommonMetadata.id(); + const bool& unique = indexMetadata->mCommonMetadata.unique(); + + MOZ_ASSERT(indexId == updateInfo.indexId()); + MOZ_ASSERT_IF(!indexMetadata->mCommonMetadata.multiEntry(), + !mUniqueIndexTable.ref().Get(indexId)); + + if (NS_WARN_IF(!mUniqueIndexTable.ref().Put(indexId, unique, fallible))) { + return false; + } + } + } else if (mOverwrite) { + // Kinda lame... + mUniqueIndexTable.emplace(); + } + +#ifdef DEBUG + if (mUniqueIndexTable) { + mUniqueIndexTable.ref().MarkImmutable(); + } +#endif + + const nsTArray& files = mParams.files(); + + if (!files.IsEmpty()) { + const uint32_t count = files.Length(); + + if (NS_WARN_IF(!mStoredFileInfos.SetCapacity(count))) { + return false; + } + + nsRefPtr fileManager = + aTransaction->GetDatabase()->GetFileManager(); + MOZ_ASSERT(fileManager); + + for (uint32_t index = 0; index < count; index++) { + const DatabaseFileOrMutableFileId& fileOrFileId = files[index]; + MOZ_ASSERT(fileOrFileId.type() == + DatabaseFileOrMutableFileId:: + TPBackgroundIDBDatabaseFileParent || + fileOrFileId.type() == DatabaseFileOrMutableFileId::Tint64_t); + + StoredFileInfo* storedFileInfo = mStoredFileInfos.AppendElement(); + MOZ_ASSERT(storedFileInfo); + + switch (fileOrFileId.type()) { + case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileParent: { + storedFileInfo->mFileActor = + static_cast( + fileOrFileId.get_PBackgroundIDBDatabaseFileParent()); + MOZ_ASSERT(storedFileInfo->mFileActor); + + storedFileInfo->mFileInfo = storedFileInfo->mFileActor->GetFileInfo(); + MOZ_ASSERT(storedFileInfo->mFileInfo); + + storedFileInfo->mInputStream = + storedFileInfo->mFileActor->GetInputStream(); + if (storedFileInfo->mInputStream && !mFileManager) { + mFileManager = fileManager; + } + break; + } + + case DatabaseFileOrMutableFileId::Tint64_t: + storedFileInfo->mFileInfo = + fileManager->GetFileInfo(fileOrFileId.get_int64_t()); + MOZ_ASSERT(storedFileInfo->mFileInfo); + break; + + case DatabaseFileOrMutableFileId::TPBackgroundIDBDatabaseFileChild: + default: + MOZ_CRASH("Should never get here!"); + } + } + } + + return true; +} + +nsresult +ObjectStoreAddOrPutRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT_IF(mFileManager, !mStoredFileInfos.IsEmpty()); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreAddOrPutRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + if (NS_WARN_IF(IndexedDatabaseManager::InLowDiskSpaceMode())) { + return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + } + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // This will be the final key we use. + Key& key = mResponse; + key = mParams.key(); + + const bool keyUnset = key.IsUnset(); + const int64_t osid = mParams.objectStoreId(); + const KeyPath& keyPath = mMetadata->mCommonMetadata.keyPath(); + + // The "|| keyUnset" here is mostly a debugging tool. If a key isn't + // specified we should never have a collision and so it shouldn't matter + // if we allow overwrite or not. By not allowing overwrite we raise + // detectable errors rather than corrupting data. + TransactionBase::CachedStatement stmt; + if (!mOverwrite || keyUnset) { + rv = aTransaction->GetCachedStatement( + "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)", + &stmt); + } else { + rv = aTransaction->GetCachedStatement( + "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, " + "file_ids) " + "VALUES (:osid, :key_value, :data, :file_ids)", + &stmt); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(), + "Should have key unless autoIncrement"); + + int64_t autoIncrementNum = 0; + + if (mMetadata->mCommonMetadata.autoIncrement()) { + if (keyUnset) { + autoIncrementNum = mMetadata->mNextAutoIncrementId; + + MOZ_ASSERT(autoIncrementNum > 0); + + if (autoIncrementNum > (1LL << 53)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + key.SetFromInteger(autoIncrementNum); + } else if (key.IsFloat() && + key.ToFloat() >= mMetadata->mNextAutoIncrementId) { + autoIncrementNum = floor(key.ToFloat()); + } + + if (keyUnset && keyPath.IsValid()) { + const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo(); + MOZ_ASSERT(cloneInfo.offsetToKeyProp()); + MOZ_ASSERT(cloneInfo.data().Length() > sizeof(uint64_t)); + MOZ_ASSERT(cloneInfo.offsetToKeyProp() <= + (cloneInfo.data().Length() - sizeof(uint64_t))); + + // Special case where someone put an object into an autoIncrement'ing + // objectStore with no key in its keyPath set. We needed to figure out + // which row id we would get above before we could set that properly. + uint8_t* keyPropPointer = + const_cast(cloneInfo.data().Elements() + + cloneInfo.offsetToKeyProp()); + uint64_t keyPropValue = + ReinterpretDoubleAsUInt64(static_cast(autoIncrementNum)); + + LittleEndian::writeUint64(keyPropPointer, keyPropValue); + } + } + + key.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); + + // Compress the bytes before adding into the database. + const char* uncompressed = + reinterpret_cast(mParams.cloneInfo().data().Elements()); + size_t uncompressedLength = mParams.cloneInfo().data().Length(); + + // We don't have a smart pointer class that calls moz_free, so we need to + // manage | compressed | manually. + { + size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); + + // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage + // expects to be able to free the adopted pointer with NS_Free. + char* compressed = static_cast(moz_malloc(compressedLength)); + if (NS_WARN_IF(!compressed)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + snappy::RawCompress(uncompressed, uncompressedLength, compressed, + &compressedLength); + + uint8_t* dataBuffer = reinterpret_cast(compressed); + size_t dataBufferLength = compressedLength; + + // If this call succeeds, | compressed | is now owned by the statement, and + // we are no longer responsible for it. + rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, + dataBufferLength); + if (NS_WARN_IF(NS_FAILED(rv))) { + moz_free(compressed); + return rv; + } + } + + nsCOMPtr fileDirectory; + nsCOMPtr journalDirectory; + + if (mFileManager) { + fileDirectory = mFileManager->GetDirectory(); + if (NS_WARN_IF(!fileDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + journalDirectory = mFileManager->EnsureJournalDirectory(); + if (NS_WARN_IF(!journalDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + DebugOnly exists; + MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->Exists(&exists))); + MOZ_ASSERT(exists); + + DebugOnly isDirectory; + MOZ_ASSERT(NS_SUCCEEDED(fileDirectory->IsDirectory(&isDirectory))); + MOZ_ASSERT(isDirectory); + + MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists))); + MOZ_ASSERT(exists); + + MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory))); + MOZ_ASSERT(isDirectory); + } + + if (!mStoredFileInfos.IsEmpty()) { + nsAutoString fileIds; + + for (uint32_t count = mStoredFileInfos.Length(), index = 0; + index < count; + index++) { + StoredFileInfo& storedFileInfo = mStoredFileInfos[index]; + MOZ_ASSERT(storedFileInfo.mFileInfo); + + const int64_t id = storedFileInfo.mFileInfo->Id(); + + nsCOMPtr inputStream; + storedFileInfo.mInputStream.swap(inputStream); + + if (inputStream) { + MOZ_ASSERT(fileDirectory); + MOZ_ASSERT(journalDirectory); + + nsCOMPtr diskFile = + mFileManager->GetFileForId(fileDirectory, id); + if (NS_WARN_IF(!diskFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + bool exists; + rv = diskFile->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (exists) { + bool isFile; + rv = diskFile->IsFile(&isFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_WARN_IF(!isFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + uint64_t inputStreamSize; + rv = inputStream->Available(&inputStreamSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + int64_t fileSize; + rv = diskFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_WARN_IF(fileSize < 0)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + if (NS_WARN_IF(uint64_t(fileSize) != inputStreamSize)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } else { + // Create a journal file first. + nsCOMPtr journalFile = + mFileManager->GetFileForId(journalDirectory, id); + if (NS_WARN_IF(!journalFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + // Now try to copy the stream. + nsRefPtr outputStream = + FileOutputStream::Create(mPersistenceType, + mGroup, + mOrigin, + diskFile); + if (NS_WARN_IF(!outputStream)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + rv = CopyFileData(inputStream, outputStream); + if (NS_FAILED(rv) && + NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { + IDB_REPORT_INTERNAL_ERR(); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + // Try to remove the file if the copy failed. + if (NS_FAILED(diskFile->Remove(false))) { + NS_WARNING("Failed to remove file after copying failed!"); + } + return rv; + } + + storedFileInfo.mCopiedSuccessfully = true; + } + } + + if (index) { + fileIds.Append(' '); + } + fileIds.AppendInt(id); + } + + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + rv = stmt->Execute(); + if (rv == NS_ERROR_STORAGE_CONSTRAINT) { + MOZ_ASSERT(!keyUnset, "Generated key had a collision!"); + return rv; + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t objectDataId; + rv = aTransaction->Connection()->GetLastInsertRowID(&objectDataId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Update our indexes if needed. + if (mOverwrite || !mParams.indexUpdateInfos().IsEmpty()) { + MOZ_ASSERT(mUniqueIndexTable); + + rv = UpdateIndexes(aTransaction, + mUniqueIndexTable.ref(), + key, + mOverwrite, + objectDataId, + mParams.indexUpdateInfos()); + if (NS_FAILED(rv)) { + return rv; + } + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (autoIncrementNum) { + mMetadata->mNextAutoIncrementId = autoIncrementNum + 1; + aTransaction->NoteModifiedAutoIncrementObjectStore(mMetadata); + } + + return NS_OK; +} + +void +ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + if (mOverwrite) { + aResponse = ObjectStorePutResponse(mResponse); + } else { + aResponse = ObjectStoreAddResponse(mResponse); + } +} + +void +ObjectStoreAddOrPutRequestOp::Cleanup() +{ + AssertIsOnOwningThread(); + + if (!mStoredFileInfos.IsEmpty()) { + for (uint32_t count = mStoredFileInfos.Length(), index = 0; + index < count; + index++) { + StoredFileInfo& storedFileInfo = mStoredFileInfos[index]; + nsRefPtr& fileActor = storedFileInfo.mFileActor; + + MOZ_ASSERT_IF(!fileActor, !storedFileInfo.mCopiedSuccessfully); + + if (fileActor && storedFileInfo.mCopiedSuccessfully) { + fileActor->ClearInputStreamParams(); + } + } + + mStoredFileInfos.Clear(); + } + + NormalTransactionOp::Cleanup(); +} + +ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll) + : NormalTransactionOp(aTransaction) + , mObjectStoreId(aGetAll ? + aParams.get_ObjectStoreGetAllParams().objectStoreId() : + aParams.get_ObjectStoreGetParams().objectStoreId()) + , mFileManager(aTransaction->GetDatabase()->GetFileManager()) + , mOptionalKeyRange(aGetAll ? + aParams.get_ObjectStoreGetAllParams() + .optionalKeyRange() : + OptionalKeyRange(aParams.get_ObjectStoreGetParams() + .keyRange())) + , mBackgroundParent(aTransaction->GetBackgroundParent()) + , mLimit(aGetAll ? aParams.get_ObjectStoreGetAllParams().limit() : 1) + , mGetAll(aGetAll) +{ + MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetParams || + aParams.type() == RequestParams::TObjectStoreGetAllParams); + MOZ_ASSERT(mObjectStoreId); + MOZ_ASSERT(mFileManager); + MOZ_ASSERT_IF(!aGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); + MOZ_ASSERT(mBackgroundParent); +} + +nsresult +ObjectStoreGetRequestOp::ConvertResponse( + uint32_t aIndex, + SerializedStructuredCloneReadInfo& aSerializedInfo) +{ + MOZ_ASSERT(aIndex < mResponse.Length()); + + StructuredCloneReadInfo& info = mResponse[aIndex]; + + info.mData.SwapElements(aSerializedInfo.data()); + + FallibleTArray blobs; + FallibleTArray fileInfos; + nsresult rv = ConvertBlobsToActors(mBackgroundParent, + mFileManager, + info.mFiles, + blobs, + fileInfos); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(aSerializedInfo.blobsParent().IsEmpty()); + MOZ_ASSERT(aSerializedInfo.fileInfos().IsEmpty()); + + aSerializedInfo.blobsParent().SwapElements(blobs); + aSerializedInfo.fileInfos().SwapElements(fileInfos); + + return NS_OK; +} + +nsresult +ObjectStoreGetRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT_IF(!mGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); + MOZ_ASSERT_IF(!mGetAll, mLimit == 1); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreGetRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + NS_LITERAL_CSTRING("key_value"), + keyRangeClause); + } + + nsCString limitClause; + if (mLimit) { + limitClause.AssignLiteral(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT data, file_ids " + "FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause + + NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + + limitClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(); + if (NS_WARN_IF(!cloneInfo)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, mFileManager, + cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); + + return NS_OK; +} + +void +ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse) +{ + MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit); + + if (mGetAll) { + aResponse = ObjectStoreGetAllResponse(); + + if (!mResponse.IsEmpty()) { + FallibleTArray fallibleCloneInfos; + if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length()))) { + aResponse = NS_ERROR_OUT_OF_MEMORY; + return; + } + + for (uint32_t count = mResponse.Length(), index = 0; + index < count; + index++) { + nsresult rv = ConvertResponse(index, fallibleCloneInfos[index]); + if (NS_WARN_IF(NS_FAILED(rv))) { + aResponse = rv; + return; + } + } + + nsTArray& cloneInfos = + aResponse.get_ObjectStoreGetAllResponse().cloneInfos(); + + fallibleCloneInfos.SwapElements(cloneInfos); + } + + return; + } + + aResponse = ObjectStoreGetResponse(); + + if (!mResponse.IsEmpty()) { + SerializedStructuredCloneReadInfo& serializedInfo = + aResponse.get_ObjectStoreGetResponse().cloneInfo(); + + nsresult rv = ConvertResponse(0, serializedInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + aResponse = rv; + } + } +} + +nsresult +ObjectStoreGetAllKeysRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreGetAllKeysRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange( + mParams.optionalKeyRange().get_SerializedKeyRange(), + NS_LITERAL_CSTRING("key_value"), + keyRangeClause); + } + + nsAutoCString limitClause; + if (uint32_t limit = mParams.limit()) { + limitClause.AssignLiteral(" LIMIT "); + limitClause.AppendInt(limit); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT key_value " + "FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause + + NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + + limitClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mParams.objectStoreId()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement( + mParams.optionalKeyRange().get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + Key* key = mResponse.AppendElement(); + if (NS_WARN_IF(!key)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = key->SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +ObjectStoreGetAllKeysRequestOp::GetResponse(RequestResponse& aResponse) +{ + aResponse = ObjectStoreGetAllKeysResponse(); + + if (!mResponse.IsEmpty()) { + nsTArray& response = + aResponse.get_ObjectStoreGetAllKeysResponse().keys(); + mResponse.SwapElements(response); + } +} + +nsresult +ObjectStoreDeleteRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreDeleteRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoCString keyRangeClause; + GetBindingClauseForKeyRange(mParams.keyRange(), + NS_LITERAL_CSTRING("key_value"), + keyRangeClause); + + nsCString query = + NS_LITERAL_CSTRING("DELETE FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause; + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mParams.objectStoreId()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = BindKeyRangeToStatement(mParams.keyRange(), stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +ObjectStoreClearRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreClearRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + TransactionBase::AutoSavepoint autoSave; + nsresult rv = autoSave.Start(aTransaction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + TransactionBase::CachedStatement stmt; + rv = aTransaction->GetCachedStatement( + "DELETE FROM object_data " + "WHERE object_store_id = :osid", + &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mParams.objectStoreId()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = autoSave.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +ObjectStoreCountRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "ObjectStoreCountRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange( + mParams.optionalKeyRange().get_SerializedKeyRange(), + NS_LITERAL_CSTRING("key_value"), + keyRangeClause); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT count(*) " + "FROM object_data " + "WHERE object_store_id = :osid") + + keyRangeClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), + mParams.objectStoreId()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement( + mParams.optionalKeyRange().get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!hasResult)) { + MOZ_ASSERT(false, "This should never be possible!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + int64_t count = stmt->AsInt64(0); + if (NS_WARN_IF(count < 0)) { + MOZ_ASSERT(false, "This should never be possible!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mResponse.count() = count; + + return NS_OK; +} + +// static +already_AddRefed +IndexRequestOpBase::IndexMetadataForParams(TransactionBase* aTransaction, + const RequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aTransaction); + MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams || + aParams.type() == RequestParams::TIndexGetKeyParams || + aParams.type() == RequestParams::TIndexGetAllParams || + aParams.type() == RequestParams::TIndexGetAllKeysParams || + aParams.type() == RequestParams::TIndexCountParams); + + uint64_t objectStoreId; + uint64_t indexId; + + switch (aParams.type()) { + case RequestParams::TIndexGetParams: { + const IndexGetParams& params = aParams.get_IndexGetParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + break; + } + + case RequestParams::TIndexGetKeyParams: { + const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + break; + } + + case RequestParams::TIndexGetAllParams: { + const IndexGetAllParams& params = aParams.get_IndexGetAllParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + break; + } + + case RequestParams::TIndexGetAllKeysParams: { + const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + break; + } + + case RequestParams::TIndexCountParams: { + const IndexCountParams& params = aParams.get_IndexCountParams(); + objectStoreId = params.objectStoreId(); + indexId = params.indexId(); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + const nsRefPtr objectStoreMetadata = + aTransaction->GetMetadataForObjectStoreId(objectStoreId); + MOZ_ASSERT(objectStoreMetadata); + + nsRefPtr indexMetadata = + aTransaction->GetMetadataForIndexId(objectStoreMetadata, indexId); + MOZ_ASSERT(indexMetadata); + + return indexMetadata.forget(); +} + +IndexGetRequestOp::IndexGetRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll) + : IndexRequestOpBase(aTransaction, aParams) + , mFileManager(aTransaction->GetDatabase()->GetFileManager()) + , mOptionalKeyRange(aGetAll ? + aParams.get_IndexGetAllParams().optionalKeyRange() : + OptionalKeyRange(aParams.get_IndexGetParams() + .keyRange())) + , mBackgroundParent(aTransaction->GetBackgroundParent()) + , mLimit(aGetAll ? aParams.get_IndexGetAllParams().limit() : 1) + , mGetAll(aGetAll) +{ + MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams || + aParams.type() == RequestParams::TIndexGetAllParams); + MOZ_ASSERT(mFileManager); + MOZ_ASSERT_IF(!aGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); + MOZ_ASSERT(mBackgroundParent); +} + +nsresult +IndexGetRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT_IF(!mGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); + MOZ_ASSERT_IF(!mGetAll, mLimit == 1); + + PROFILER_LABEL("IndexedDB", + "IndexGetRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + nsCString indexTable; + if (mMetadata->mCommonMetadata.unique()) { + indexTable.AssignLiteral("unique_index_data "); + } + else { + indexTable.AssignLiteral("index_data "); + } + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + NS_LITERAL_CSTRING("value"), + keyRangeClause); + } + + nsCString limitClause; + if (mLimit) { + limitClause.AssignLiteral(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT data, file_ids " + "FROM object_data " + "INNER JOIN ") + + indexTable + + NS_LITERAL_CSTRING("AS index_table " + "ON object_data.id = index_table.object_data_id " + "WHERE index_id = :index_id") + + keyRangeClause + + limitClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mMetadata->mCommonMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + StructuredCloneReadInfo* cloneInfo = mResponse.AppendElement(); + if (NS_WARN_IF(!cloneInfo)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, mFileManager, + cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); + + return NS_OK; +} + +void +IndexGetRequestOp::GetResponse(RequestResponse& aResponse) +{ + MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); + + if (mGetAll) { + aResponse = IndexGetAllResponse(); + + if (!mResponse.IsEmpty()) { + FallibleTArray fallibleCloneInfos; + if (NS_WARN_IF(!fallibleCloneInfos.SetLength(mResponse.Length()))) { + aResponse = NS_ERROR_OUT_OF_MEMORY; + return; + } + + for (uint32_t count = mResponse.Length(), index = 0; + index < count; + index++) { + StructuredCloneReadInfo& info = mResponse[index]; + + SerializedStructuredCloneReadInfo& serializedInfo = + fallibleCloneInfos[index]; + + info.mData.SwapElements(serializedInfo.data()); + + FallibleTArray blobs; + FallibleTArray fileInfos; + nsresult rv = ConvertBlobsToActors(mBackgroundParent, + mFileManager, + info.mFiles, + blobs, + fileInfos); + if (NS_WARN_IF(NS_FAILED(rv))) { + aResponse = rv; + return; + } + + MOZ_ASSERT(serializedInfo.blobsParent().IsEmpty()); + MOZ_ASSERT(serializedInfo.fileInfos().IsEmpty()); + + serializedInfo.blobsParent().SwapElements(blobs); + serializedInfo.fileInfos().SwapElements(fileInfos); + } + + nsTArray& cloneInfos = + aResponse.get_IndexGetAllResponse().cloneInfos(); + + fallibleCloneInfos.SwapElements(cloneInfos); + } + + return; + } + + aResponse = IndexGetResponse(); + + if (!mResponse.IsEmpty()) { + StructuredCloneReadInfo& info = mResponse[0]; + + SerializedStructuredCloneReadInfo& serializedInfo = + aResponse.get_IndexGetResponse().cloneInfo(); + + info.mData.SwapElements(serializedInfo.data()); + + FallibleTArray blobs; + FallibleTArray fileInfos; + nsresult rv = + ConvertBlobsToActors(mBackgroundParent, + mFileManager, + info.mFiles, + blobs, + fileInfos); + if (NS_WARN_IF(NS_FAILED(rv))) { + aResponse = rv; + return; + } + + MOZ_ASSERT(serializedInfo.blobsParent().IsEmpty()); + MOZ_ASSERT(serializedInfo.fileInfos().IsEmpty()); + + serializedInfo.blobsParent().SwapElements(blobs); + serializedInfo.fileInfos().SwapElements(fileInfos); + } +} + +IndexGetKeyRequestOp::IndexGetKeyRequestOp(TransactionBase* aTransaction, + const RequestParams& aParams, + bool aGetAll) + : IndexRequestOpBase(aTransaction, aParams) + , mOptionalKeyRange(aGetAll ? + aParams.get_IndexGetAllKeysParams().optionalKeyRange() : + OptionalKeyRange(aParams.get_IndexGetKeyParams() + .keyRange())) + , mLimit(aGetAll ? aParams.get_IndexGetAllKeysParams().limit() : 1) + , mGetAll(aGetAll) +{ + MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetKeyParams || + aParams.type() == RequestParams::TIndexGetAllKeysParams); + MOZ_ASSERT_IF(!aGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); +} + +nsresult +IndexGetKeyRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT_IF(!mGetAll, + mOptionalKeyRange.type() == + OptionalKeyRange::TSerializedKeyRange); + MOZ_ASSERT_IF(!mGetAll, mLimit == 1); + + PROFILER_LABEL("IndexedDB", + "IndexGetKeyRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + nsCString indexTable; + if (mMetadata->mCommonMetadata.unique()) { + indexTable.AssignLiteral("unique_index_data "); + } + else { + indexTable.AssignLiteral("index_data "); + } + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + NS_LITERAL_CSTRING("value"), + keyRangeClause); + } + + nsCString limitClause; + if (mLimit) { + limitClause.AssignLiteral(" LIMIT "); + limitClause.AppendInt(mLimit); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT object_data_key " + "FROM ") + + indexTable + + NS_LITERAL_CSTRING("WHERE index_id = :index_id") + + keyRangeClause + + limitClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mMetadata->mCommonMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { + Key* key = mResponse.AppendElement(); + if (NS_WARN_IF(!key)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = key->SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); + + return NS_OK; +} + +void +IndexGetKeyRequestOp::GetResponse(RequestResponse& aResponse) +{ + MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1); + + if (mGetAll) { + aResponse = IndexGetAllKeysResponse(); + + if (!mResponse.IsEmpty()) { + mResponse.SwapElements(aResponse.get_IndexGetAllKeysResponse().keys()); + } + + return; + } + + aResponse = IndexGetKeyResponse(); + + if (!mResponse.IsEmpty()) { + aResponse.get_IndexGetKeyResponse().key() = Move(mResponse[0]); + } +} + +nsresult +IndexCountRequestOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + + PROFILER_LABEL("IndexedDB", + "IndexCountRequestOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool hasKeyRange = + mParams.optionalKeyRange().type() == OptionalKeyRange::TSerializedKeyRange; + + nsCString indexTable; + if (mMetadata->mCommonMetadata.unique()) { + indexTable.AssignLiteral("unique_index_data "); + } + else { + indexTable.AssignLiteral("index_data "); + } + + nsAutoCString keyRangeClause; + if (hasKeyRange) { + GetBindingClauseForKeyRange( + mParams.optionalKeyRange().get_SerializedKeyRange(), + NS_LITERAL_CSTRING("value"), + keyRangeClause); + } + + nsCString query = + NS_LITERAL_CSTRING("SELECT count(*) " + "FROM ") + + indexTable + + NS_LITERAL_CSTRING("WHERE index_id = :index_id") + + keyRangeClause; + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), + mMetadata->mCommonMetadata.id()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasKeyRange) { + rv = BindKeyRangeToStatement( + mParams.optionalKeyRange().get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!hasResult)) { + MOZ_ASSERT(false, "This should never be possible!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + int64_t count = stmt->AsInt64(0); + if (NS_WARN_IF(count < 0)) { + MOZ_ASSERT(false, "This should never be possible!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mResponse.count() = count; + + return NS_OK; +} + +bool +Cursor:: +CursorOpBase::SendFailureResult(nsresult aResultCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResultCode)); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); + MOZ_ASSERT(!mResponseSent); + + if (!IsActorDestroyed()) { + mResponse = ClampResultCode(aResultCode); + + mCursor->SendResponseInternal(mResponse, mFiles); + } + + mResponseSent = true; + return false; +} + +void +Cursor:: +CursorOpBase::Cleanup() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent); + + mCursor = nullptr; + +#ifdef DEBUG + // A bit hacky but the CursorOp request is not generated in response to a + // child request like most other database operations. Do this to make our + // assertions happy. + NoteActorDestroyed(); +#endif + + TransactionDatabaseOperationBase::Cleanup(); +} + +void +Cursor:: +OpenOp::GetRangeKeyInfo(bool aLowerBound, Key* aKey, bool* aOpen) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aKey); + MOZ_ASSERT(aKey->IsUnset()); + MOZ_ASSERT(aOpen); + if (mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange) { + const SerializedKeyRange& range = + mOptionalKeyRange.get_SerializedKeyRange(); + if (range.isOnly()) { + *aKey = range.lower(); + *aOpen = false; + } else { + *aKey = aLowerBound ? range.lower() : range.upper(); + *aOpen = aLowerBound ? range.lowerOpen() : range.upperOpen(); + } + } else { + *aOpen = false; + } +} + +nsresult +Cursor:: +OpenOp::DoObjectStoreDatabaseWork(TransactionBase* aTransaction) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mType == OpenCursorParams::TObjectStoreOpenCursorParams); + MOZ_ASSERT(mCursor->mObjectStoreId); + MOZ_ASSERT(mCursor->mFileManager); + + PROFILER_LABEL("IndexedDB", + "Cursor::OpenOp::DoObjectStoreDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool usingKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); + + nsCString queryStart = + NS_LITERAL_CSTRING("SELECT ") + + keyValue + + NS_LITERAL_CSTRING(", data, file_ids " + "FROM object_data " + "WHERE object_store_id = :") + + id; + + nsAutoCString keyRangeClause; + if (usingKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + keyValue, + keyRangeClause); + } + + nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AppendLiteral(" ASC"); + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + directionClause.AppendLiteral(" DESC"); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + nsCString firstQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit + + NS_LITERAL_CSTRING("1"); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (usingKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + mResponse = void_t(); + return NS_OK; + } + + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + StructuredCloneReadInfo cloneInfo; + rv = GetStructuredCloneReadInfoFromStatement(stmt, + 1, + 2, + mCursor->mFileManager, + &cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now we need to make the query to get the next match. + keyRangeClause.Truncate(); + nsAutoCString continueToKeyRangeClause; + + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + AppendConditionClause(keyValue, currentKey, false, false, + keyRangeClause); + AppendConditionClause(keyValue, currentKey, false, true, + continueToKeyRangeClause); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(keyValue, rangeKey, true, !open, keyRangeClause); + AppendConditionClause(keyValue, rangeKey, true, !open, + continueToKeyRangeClause); + mCursor->mRangeKey = upper; + } + break; + } + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); + AppendConditionClause(keyValue, currentKey, true, true, + continueToKeyRangeClause); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(keyValue, rangeKey, false, !open, keyRangeClause); + AppendConditionClause(keyValue, rangeKey, false, !open, + continueToKeyRangeClause); + mCursor->mRangeKey = lower; + } + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + mCursor->mContinueQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit; + + mCursor->mContinueToQuery = + queryStart + + continueToKeyRangeClause + + directionClause + + openLimit; + + mResponse = ObjectStoreCursorResponse(); + + auto& response = mResponse.get_ObjectStoreCursorResponse(); + response.cloneInfo().data().SwapElements(cloneInfo.mData); + response.key() = mCursor->mKey; + + mFiles.SwapElements(cloneInfo.mFiles); + + return NS_OK; +} + +nsresult +Cursor:: +OpenOp::DoObjectStoreKeyDatabaseWork(TransactionBase* aTransaction) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mType == + OpenCursorParams::TObjectStoreOpenKeyCursorParams); + MOZ_ASSERT(mCursor->mObjectStoreId); + + PROFILER_LABEL("IndexedDB", + "Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool usingKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); + + nsCString queryStart = + NS_LITERAL_CSTRING("SELECT ") + + keyValue + + NS_LITERAL_CSTRING(" FROM object_data " + "WHERE object_store_id = :") + + id; + + nsAutoCString keyRangeClause; + if (usingKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + keyValue, + keyRangeClause); + } + + nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AppendLiteral(" ASC"); + break; + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: + directionClause.AppendLiteral(" DESC"); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + nsCString firstQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit + + NS_LITERAL_CSTRING("1"); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(id, mCursor->mObjectStoreId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (usingKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + mResponse = void_t(); + return NS_OK; + } + + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now we need to make the query to get the next match. + keyRangeClause.Truncate(); + nsAutoCString continueToKeyRangeClause; + + NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + AppendConditionClause(keyValue, currentKey, false, false, + keyRangeClause); + AppendConditionClause(keyValue, currentKey, false, true, + continueToKeyRangeClause); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(keyValue, rangeKey, true, !open, keyRangeClause); + AppendConditionClause(keyValue, rangeKey, true, !open, + continueToKeyRangeClause); + mCursor->mRangeKey = upper; + } + break; + } + + case IDBCursor::PREV: + case IDBCursor::PREV_UNIQUE: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); + AppendConditionClause(keyValue, currentKey, true, true, + continueToKeyRangeClause); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(keyValue, rangeKey, false, !open, keyRangeClause); + AppendConditionClause(keyValue, rangeKey, false, !open, + continueToKeyRangeClause); + mCursor->mRangeKey = lower; + } + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + mCursor->mContinueQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + continueToKeyRangeClause + + directionClause + + openLimit; + + mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey); + + return NS_OK; +} + +nsresult +Cursor:: +OpenOp::DoIndexDatabaseWork(TransactionBase* aTransaction) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenCursorParams); + MOZ_ASSERT(mCursor->mObjectStoreId); + MOZ_ASSERT(mCursor->mIndexId); + + PROFILER_LABEL("IndexedDB", + "Cursor::OpenOp::DoIndexDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool usingKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + nsCString indexTable = mCursor->mUniqueIndex ? + NS_LITERAL_CSTRING("unique_index_data") : + NS_LITERAL_CSTRING("index_data"); + + NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); + + nsAutoCString keyRangeClause; + if (usingKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + value, + keyRangeClause); + } + + nsAutoCString directionClause = + NS_LITERAL_CSTRING(" ORDER BY ") + + value; + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AppendLiteral(" ASC, index_table.object_data_key ASC"); + break; + + case IDBCursor::PREV: + directionClause.AppendLiteral(" DESC, index_table.object_data_key DESC"); + break; + + case IDBCursor::PREV_UNIQUE: + directionClause.AppendLiteral(" DESC, index_table.object_data_key ASC"); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + nsAutoCString queryStart = + NS_LITERAL_CSTRING("SELECT index_table.value, " + "index_table.object_data_key, " + "object_data.data, " + "object_data.file_ids " + "FROM ") + + indexTable + + NS_LITERAL_CSTRING(" AS index_table " + "JOIN object_data " + "ON index_table.object_data_id = object_data.id " + "WHERE index_table.index_id = :") + + id; + + nsCString firstQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit + + NS_LITERAL_CSTRING("1"); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(id, mCursor->mIndexId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (usingKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + mResponse = void_t(); + return NS_OK; + } + + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + StructuredCloneReadInfo cloneInfo; + rv = GetStructuredCloneReadInfoFromStatement(stmt, + 2, + 3, + mCursor->mFileManager, + &cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now we need to make the query to get the next match. + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(value, rangeKey, true, !open, queryStart); + mCursor->mRangeKey = upper; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key " + "AND ( index_table.value > :current_key OR " + "index_table.object_data_key > :object_key ) " + ) + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + + directionClause + + openLimit; + break; + } + + case IDBCursor::NEXT_UNIQUE: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(value, rangeKey, true, !open, queryStart); + mCursor->mRangeKey = upper; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + + directionClause + + openLimit; + break; + } + + case IDBCursor::PREV: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(value, rangeKey, false, !open, queryStart); + mCursor->mRangeKey = lower; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key " + "AND ( index_table.value < :current_key OR " + "index_table.object_data_key < :object_key ) " + ) + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + + directionClause + + openLimit; + break; + } + + case IDBCursor::PREV_UNIQUE: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(value, rangeKey, false, !open, queryStart); + mCursor->mRangeKey = lower; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + + directionClause + + openLimit; + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + mResponse = IndexCursorResponse(); + + auto& response = mResponse.get_IndexCursorResponse(); + response.cloneInfo().data().SwapElements(cloneInfo.mData); + response.key() = mCursor->mKey; + response.objectKey() = mCursor->mObjectKey; + + mFiles.SwapElements(cloneInfo.mFiles); + + return NS_OK; +} + +nsresult +Cursor:: +OpenOp::DoIndexKeyDatabaseWork(TransactionBase* aTransaction) +{ + AssertIsOnTransactionThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams); + MOZ_ASSERT(mCursor->mObjectStoreId); + MOZ_ASSERT(mCursor->mIndexId); + + PROFILER_LABEL("IndexedDB", + "Cursor::OpenOp::DoIndexKeyDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + const bool usingKeyRange = + mOptionalKeyRange.type() == OptionalKeyRange::TSerializedKeyRange; + + nsCString table = mCursor->mUniqueIndex ? + NS_LITERAL_CSTRING("unique_index_data") : + NS_LITERAL_CSTRING("index_data"); + + NS_NAMED_LITERAL_CSTRING(value, "value"); + NS_NAMED_LITERAL_CSTRING(id, "id"); + NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); + + nsAutoCString keyRangeClause; + if (usingKeyRange) { + GetBindingClauseForKeyRange(mOptionalKeyRange.get_SerializedKeyRange(), + value, + keyRangeClause); + } + + nsAutoCString directionClause = + NS_LITERAL_CSTRING(" ORDER BY ") + + value; + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: + case IDBCursor::NEXT_UNIQUE: + directionClause.AppendLiteral(" ASC, object_data_key ASC"); + break; + + case IDBCursor::PREV: + directionClause.AppendLiteral(" DESC, object_data_key DESC"); + break; + + case IDBCursor::PREV_UNIQUE: + directionClause.AppendLiteral(" DESC, object_data_key ASC"); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + nsAutoCString queryStart = + NS_LITERAL_CSTRING("SELECT value, object_data_key " + "FROM ") + + table + + NS_LITERAL_CSTRING(" WHERE index_id = :") + + id; + + nsCString firstQuery = + queryStart + + keyRangeClause + + directionClause + + openLimit + + NS_LITERAL_CSTRING("1"); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(firstQuery, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->BindInt64ByName(id, mCursor->mIndexId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (usingKeyRange) { + rv = BindKeyRangeToStatement(mOptionalKeyRange.get_SerializedKeyRange(), + stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + mResponse = void_t(); + return NS_OK; + } + + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Now we need to make the query to get the next match. + NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); + + switch (mCursor->mDirection) { + case IDBCursor::NEXT: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(value, rangeKey, true, !open, queryStart); + mCursor->mRangeKey = upper; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value >= :current_key " + "AND ( value > :current_key OR " + "object_data_key > :object_key )") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value >= :current_key ") + + directionClause + + openLimit; + break; + } + + case IDBCursor::NEXT_UNIQUE: { + Key upper; + bool open; + GetRangeKeyInfo(false, &upper, &open); + if (usingKeyRange && !upper.IsUnset()) { + AppendConditionClause(value, rangeKey, true, !open, queryStart); + mCursor->mRangeKey = upper; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value > :current_key") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value >= :current_key") + + directionClause + + openLimit; + break; + } + + case IDBCursor::PREV: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(value, rangeKey, false, !open, queryStart); + mCursor->mRangeKey = lower; + } + + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key " + "AND ( value < :current_key OR " + "object_data_key < :object_key )") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key ") + + directionClause + + openLimit; + break; + } + + case IDBCursor::PREV_UNIQUE: { + Key lower; + bool open; + GetRangeKeyInfo(true, &lower, &open); + if (usingKeyRange && !lower.IsUnset()) { + AppendConditionClause(value, rangeKey, false, !open, queryStart); + mCursor->mRangeKey = lower; + } + mCursor->mContinueQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value < :current_key") + + directionClause + + openLimit; + mCursor->mContinueToQuery = + queryStart + + NS_LITERAL_CSTRING(" AND value <= :current_key") + + directionClause + + openLimit; + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + mResponse = IndexKeyCursorResponse(); + + auto& response = mResponse.get_IndexKeyCursorResponse(); + response.key() = mCursor->mKey; + response.objectKey() = mCursor->mObjectKey; + + return NS_OK; +} + +nsresult +Cursor:: +OpenOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty()); + MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty()); + MOZ_ASSERT(mCursor->mKey.IsUnset()); + MOZ_ASSERT(mCursor->mRangeKey.IsUnset()); + + nsresult rv; + + switch (mCursor->mType) { + case OpenCursorParams::TObjectStoreOpenCursorParams: + rv = DoObjectStoreDatabaseWork(aTransaction); + break; + + case OpenCursorParams::TObjectStoreOpenKeyCursorParams: + rv = DoObjectStoreKeyDatabaseWork(aTransaction); + break; + + case OpenCursorParams::TIndexOpenCursorParams: + rv = DoIndexDatabaseWork(aTransaction); + break; + + case OpenCursorParams::TIndexOpenKeyCursorParams: + rv = DoIndexKeyDatabaseWork(aTransaction); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +Cursor:: +OpenOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); + MOZ_ASSERT(mResponse.type() != CursorResponse::T__None); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mKey.IsUnset()); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mRangeKey.IsUnset()); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mObjectKey.IsUnset()); + + if (IsActorDestroyed()) { + return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } + + mCursor->SendResponseInternal(mResponse, mFiles); + + mResponseSent = true; + return NS_OK; +} + +nsresult +Cursor:: +ContinueOp::DoDatabaseWork(TransactionBase* aTransaction) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnTransactionThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mObjectStoreId); + MOZ_ASSERT(!mCursor->mContinueQuery.IsEmpty()); + MOZ_ASSERT(!mCursor->mContinueToQuery.IsEmpty()); + MOZ_ASSERT(!mCursor->mKey.IsUnset()); + + const bool isIndex = + mCursor->mType == OpenCursorParams::TIndexOpenCursorParams || + mCursor->mType == OpenCursorParams::TIndexOpenKeyCursorParams; + + MOZ_ASSERT_IF(isIndex, mCursor->mIndexId); + MOZ_ASSERT_IF(isIndex, !mCursor->mObjectKey.IsUnset()); + + PROFILER_LABEL("IndexedDB", + "Cursor::ContinueOp::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); + + // We need to pick a query based on whether or not a key was passed to the + // continue function. If not we'll grab the the next item in the database that + // is greater than (or less than, if we're running a PREV cursor) the current + // key. If a key was passed we'll grab the next item in the database that is + // greater than (or less than, if we're running a PREV cursor) or equal to the + // key that was specified. + + nsAutoCString countString; + nsCString query; + + bool hasContinueKey = false; + uint32_t advanceCount; + + if (mParams.type() == CursorRequestParams::TContinueParams) { + // Always go to the next result. + advanceCount = 1; + countString.AppendLiteral("1"); + + if (mParams.get_ContinueParams().key().IsUnset()) { + query = mCursor->mContinueQuery + countString; + hasContinueKey = false; + } else { + query = mCursor->mContinueToQuery + countString; + hasContinueKey = true; + } + } else { + advanceCount = mParams.get_AdvanceParams().count(); + countString.AppendInt(advanceCount); + + query = mCursor->mContinueQuery + countString; + hasContinueKey = false; + } + + NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); + NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); + NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key"); + + const Key& currentKey = + hasContinueKey ? mParams.get_ContinueParams().key() : mCursor->mKey; + + const bool usingRangeKey = !mCursor->mRangeKey.IsUnset(); + + TransactionBase::CachedStatement stmt; + nsresult rv = aTransaction->GetCachedStatement(query, &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t id = isIndex ? mCursor->mIndexId : mCursor->mObjectStoreId; + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Bind current key. + rv = currentKey.BindToStatement(stmt, currentKeyName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Bind range key if it is specified. + if (usingRangeKey) { + rv = mCursor->mRangeKey.BindToStatement(stmt, rangeKeyName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + // Bind object key if duplicates are allowed and we're not continuing to a + // specific key. + if (isIndex && + !hasContinueKey && + (mCursor->mDirection == IDBCursor::NEXT || + mCursor->mDirection == IDBCursor::PREV)) { + rv = mCursor->mObjectKey.BindToStatement(stmt, objectKeyName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + for (uint32_t index = 0; index < advanceCount; index++) { + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!hasResult) { + break; + } + } + + if (!hasResult) { + mCursor->mKey.Unset(); + mCursor->mRangeKey.Unset(); + mCursor->mObjectKey.Unset(); + mResponse = void_t(); + return NS_OK; + } + + switch (mCursor->mType) { + case OpenCursorParams::TObjectStoreOpenCursorParams: { + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + StructuredCloneReadInfo cloneInfo; + rv = GetStructuredCloneReadInfoFromStatement(stmt, + 1, + 2, + mCursor->mFileManager, + &cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mResponse = ObjectStoreCursorResponse(); + + auto& response = mResponse.get_ObjectStoreCursorResponse(); + response.cloneInfo().data().SwapElements(cloneInfo.mData); + response.key() = mCursor->mKey; + + mFiles.SwapElements(cloneInfo.mFiles); + + break; + } + + case OpenCursorParams::TObjectStoreOpenKeyCursorParams: { + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mResponse = ObjectStoreKeyCursorResponse(mCursor->mKey); + + break; + } + + case OpenCursorParams::TIndexOpenCursorParams: { + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + StructuredCloneReadInfo cloneInfo; + rv = GetStructuredCloneReadInfoFromStatement(stmt, + 2, + 3, + mCursor->mFileManager, + &cloneInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mResponse = IndexCursorResponse(); + + auto& response = mResponse.get_IndexCursorResponse(); + response.cloneInfo().data().SwapElements(cloneInfo.mData); + response.key() = mCursor->mKey; + response.objectKey() = mCursor->mObjectKey; + + mFiles.SwapElements(cloneInfo.mFiles); + + break; + } + + case OpenCursorParams::TIndexOpenKeyCursorParams: { + rv = mCursor->mKey.SetFromStatement(stmt, 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mCursor->mObjectKey.SetFromStatement(stmt, 1); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mResponse = IndexKeyCursorResponse(mCursor->mKey, mCursor->mObjectKey); + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + return NS_OK; +} + +nsresult +Cursor:: +ContinueOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mCursor); + MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mKey.IsUnset()); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mRangeKey.IsUnset()); + MOZ_ASSERT_IF(mResponse.type() == CursorResponse::Tvoid_t, + mCursor->mObjectKey.IsUnset()); + + if (IsActorDestroyed()) { + return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; + } + + mCursor->SendResponseInternal(mResponse, mFiles); + + mResponseSent = true; + return NS_OK; +} + +void +PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mActorDestroyed) { + unused << + PIndexedDBPermissionRequestParent::Send__delete__(this, aPermissionValue); + } +} + +void +PermissionRequestHelper::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; +} + +#ifdef DEBUG + +NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver) + +NS_IMETHODIMP +DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */) +{ + MOZ_CRASH("Should never be called!"); +} + +NS_IMETHODIMP +DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */, + bool /* aMayWait */, + uint32_t /* aRecursionDepth */) +{ + return NS_OK; +} + +NS_IMETHODIMP +DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, + uint32_t /* aRecursionDepth */, + bool /* aEventWasProcessed */) +{ + MOZ_ASSERT(kDEBUGThreadSleepMS); + + MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == + PR_SUCCESS); + return NS_OK; +} + +#endif // DEBUG + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ActorsParent.h b/dom/indexedDB/ActorsParent.h new file mode 100644 index 000000000000..162f6fa09a97 --- /dev/null +++ b/dom/indexedDB/ActorsParent.h @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_actorsparent_h__ +#define mozilla_dom_indexeddb_actorsparent_h__ + +template struct already_AddRefed; +class nsCString; +class nsIPrincipal; +class nsPIDOMWindow; + +namespace mozilla { +namespace ipc { + +class PBackgroundParent; + +} // namespace ipc + +namespace dom { + +class TabParent; + +namespace quota { + +class Client; + +} // namespace quota + +namespace indexedDB { + +class OptionalWindowId; +class PBackgroundIDBFactoryParent; +class PIndexedDBPermissionRequestParent; + +PBackgroundIDBFactoryParent* +AllocPBackgroundIDBFactoryParent(mozilla::ipc::PBackgroundParent* aManager, + const OptionalWindowId& aOptionalWindowId); + +bool +RecvPBackgroundIDBFactoryConstructor(mozilla::ipc::PBackgroundParent* aManager, + PBackgroundIDBFactoryParent* aActor, + const OptionalWindowId& aOptionalWindowId); + +bool +DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor); + +PIndexedDBPermissionRequestParent* +AllocPIndexedDBPermissionRequestParent(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal); + +bool +RecvPIndexedDBPermissionRequestConstructor( + PIndexedDBPermissionRequestParent* aActor); + +bool +DeallocPIndexedDBPermissionRequestParent( + PIndexedDBPermissionRequestParent* aActor); + +already_AddRefed +CreateQuotaClient(); + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_indexeddb_actorsparent_h__ diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp deleted file mode 100644 index 5ee6b272d1e6..000000000000 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ /dev/null @@ -1,710 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "base/basictypes.h" - -#include "AsyncConnectionHelper.h" - -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/storage.h" -#include "nsComponentManagerUtils.h" -#include "nsContentUtils.h" -#include "nsProxyRelease.h" -#include "nsThreadUtils.h" -#include "nsWrapperCacheInlines.h" - -#include "IDBEvents.h" -#include "IDBTransaction.h" -#include "IndexedDatabaseManager.h" -#include "ProfilerHelpers.h" -#include "ReportInternalError.h" -#include "TransactionThreadPool.h" - -#include "ipc/IndexedDBChild.h" -#include "ipc/IndexedDBParent.h" - -using namespace mozilla; -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::quota::QuotaManager; - -namespace { - -IDBTransaction* gCurrentTransaction = nullptr; - -const uint32_t kProgressHandlerGranularity = 1000; - -class MOZ_STACK_CLASS TransactionPoolEventTarget : public StackBasedEventTarget -{ -public: - NS_DECL_NSIEVENTTARGET - - explicit TransactionPoolEventTarget(IDBTransaction* aTransaction) - : mTransaction(aTransaction) - { } - -private: - IDBTransaction* mTransaction; -}; - -// This inline is just so that we always clear aBuffers appropriately even if -// something fails. -inline -nsresult -ConvertCloneReadInfosToArrayInternal( - JSContext* aCx, - nsTArray& aReadInfos, - JS::MutableHandle aResult) -{ - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (!array) { - IDB_WARNING("Failed to make array!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!aReadInfos.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) { - IDB_WARNING("Failed to set array length!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t index = 0, count = aReadInfos.Length(); index < count; - index++) { - StructuredCloneReadInfo& readInfo = aReadInfos[index]; - - JS::Rooted val(aCx); - if (!IDBObjectStore::DeserializeValue(aCx, readInfo, &val)) { - NS_WARNING("Failed to decode!"); - return NS_ERROR_DOM_DATA_CLONE_ERR; - } - - if (!JS_SetElement(aCx, array, index, val)) { - IDB_WARNING("Failed to set array element!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - } - - aResult.setObject(*array); - return NS_OK; -} - -} // anonymous namespace - -HelperBase::~HelperBase() -{ - if (!NS_IsMainThread()) { - IDBRequest* request; - mRequest.forget(&request); - - if (request) { - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); - - if (mainThread) { - NS_ProxyRelease(mainThread, static_cast(request)); - } - } - } -} - -nsresult -HelperBase::WrapNative(JSContext* aCx, - nsISupports* aNative, - JS::MutableHandle aResult) -{ - NS_ASSERTION(aCx, "Null context!"); - NS_ASSERTION(aNative, "Null pointer!"); - NS_ASSERTION(aResult.address(), "Null pointer!"); - NS_ASSERTION(mRequest, "Null request!"); - - nsresult rv = nsContentUtils::WrapNative(aCx, aNative, aResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -void -HelperBase::ReleaseMainThreadObjects() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mRequest = nullptr; -} - -AsyncConnectionHelper::AsyncConnectionHelper(IDBDatabase* aDatabase, - IDBRequest* aRequest) -: HelperBase(aRequest), - mDatabase(aDatabase), - mResultCode(NS_OK), - mDispatched(false) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} - -AsyncConnectionHelper::AsyncConnectionHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest) -: HelperBase(aRequest), - mDatabase(aTransaction->mDatabase), - mTransaction(aTransaction), - mResultCode(NS_OK), - mDispatched(false) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} - -AsyncConnectionHelper::~AsyncConnectionHelper() -{ - if (!NS_IsMainThread()) { - IDBDatabase* database; - mDatabase.forget(&database); - - IDBTransaction* transaction; - mTransaction.forget(&transaction); - - nsCOMPtr mainThread; - NS_GetMainThread(getter_AddRefs(mainThread)); - NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!"); - - if (mainThread) { - if (database) { - NS_ProxyRelease(mainThread, static_cast(database)); - } - if (transaction) { - NS_ProxyRelease(mainThread, static_cast(transaction)); - } - } - } - - NS_ASSERTION(!mOldProgressHandler, "Should not have anything here!"); -} - -NS_IMPL_ISUPPORTS(AsyncConnectionHelper, nsIRunnable, - mozIStorageProgressHandler) - -NS_IMETHODIMP -AsyncConnectionHelper::Run() -{ - if (NS_IsMainThread()) { - PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - if (mTransaction && - mTransaction->IsAborted()) { - // Always fire a "error" event with ABORT_ERR if the transaction was - // aborted, even if the request succeeded or failed with another error. - mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } - - IDBTransaction* oldTransaction = gCurrentTransaction; - gCurrentTransaction = mTransaction; - - ChildProcessSendResult sendResult = - IndexedDatabaseManager::IsMainProcess() ? - MaybeSendResponseToChildProcess(mResultCode) : - Success_NotSent; - - switch (sendResult) { - case Success_Sent: { - if (mRequest) { - mRequest->NotifyHelperSentResultsToChildProcess(NS_OK); - } - break; - } - - case Success_NotSent: { - if (mRequest) { - nsresult rv = mRequest->NotifyHelperCompleted(this); - if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { - mResultCode = rv; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread " - "response (rv = %lu)", - "IDBRequest[%llu] MT Done", - mRequest->GetSerialNumber(), mResultCode); - } - - // Call OnError if the database had an error or if the OnSuccess - // handler has an error. - if (NS_FAILED(mResultCode) || - NS_FAILED((mResultCode = OnSuccess()))) { - OnError(); - } - break; - } - - case Success_ActorDisconnected: { - // Nothing needs to be done here. - break; - } - - case Error: { - IDB_WARNING("MaybeSendResultsToChildProcess failed!"); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - if (mRequest) { - mRequest->NotifyHelperSentResultsToChildProcess(mResultCode); - } - break; - } - - default: - MOZ_CRASH("Unknown value for ChildProcessSendResult!"); - } - - NS_ASSERTION(gCurrentTransaction == mTransaction, "Should be unchanged!"); - gCurrentTransaction = oldTransaction; - - if (mDispatched && mTransaction) { - mTransaction->OnRequestFinished(); - } - - ReleaseMainThreadObjects(); - - NS_ASSERTION(!(mDatabase || mTransaction || mRequest), "Subclass didn't " - "call AsyncConnectionHelper::ReleaseMainThreadObjects!"); - - return NS_OK; - } - - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("AsyncConnectionHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - IDB_PROFILER_MARK_IF(mRequest, - "IndexedDB Request %llu: Beginning database work", - "IDBRequest[%llu] DT Start", - mRequest->GetSerialNumber()); - - nsresult rv = NS_OK; - nsCOMPtr connection; - - if (mTransaction) { - rv = mTransaction->GetOrCreateConnection(getter_AddRefs(connection)); - if (NS_SUCCEEDED(rv)) { - NS_ASSERTION(connection, "This should never be null!"); - } - } - - bool setProgressHandler = false; - if (connection) { - rv = connection->SetProgressHandler(kProgressHandlerGranularity, this, - getter_AddRefs(mOldProgressHandler)); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetProgressHandler failed!"); - if (NS_SUCCEEDED(rv)) { - setProgressHandler = true; - } - } - - if (NS_SUCCEEDED(rv)) { - bool hasSavepoint = false; - if (mDatabase) { - QuotaManager::SetCurrentWindow(mDatabase->GetOwner()); - - // Make the first savepoint. - if (mTransaction) { - if (!(hasSavepoint = mTransaction->StartSavepoint())) { - NS_WARNING("Failed to make savepoint!"); - } - } - } - - mResultCode = DoDatabaseWork(connection); - - if (mDatabase) { - // Release or roll back the savepoint depending on the error code. - if (hasSavepoint) { - NS_ASSERTION(mTransaction, "Huh?!"); - if (NS_SUCCEEDED(mResultCode)) { - mTransaction->ReleaseSavepoint(); - } - else { - mTransaction->RollbackSavepoint(); - } - } - - // Don't unset this until we're sure that all SQLite activity has - // completed! - QuotaManager::SetCurrentWindow(nullptr); - } - } - else { - // NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated" - // and we should fail with RECOVERABLE_ERR. - if (rv == NS_ERROR_NOT_AVAILABLE) { - mResultCode = NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR; - } - else { - IDB_REPORT_INTERNAL_ERR(); - mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - - if (setProgressHandler) { - nsCOMPtr handler; - rv = connection->RemoveProgressHandler(getter_AddRefs(handler)); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "RemoveProgressHandler failed!"); -#ifdef DEBUG - if (NS_SUCCEEDED(rv)) { - NS_ASSERTION(SameCOMIdentity(handler, static_cast(this)), - "Mismatch!"); - } -#endif - } - - IDB_PROFILER_MARK_IF(mRequest, - "IndexedDB Request %llu: Finished database work " - "(rv = %lu)", - "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(), - mResultCode); - - return NS_DispatchToMainThread(this); -} - -NS_IMETHODIMP -AsyncConnectionHelper::OnProgress(mozIStorageConnection* aConnection, - bool* _retval) -{ - if (mDatabase && mDatabase->IsInvalidated()) { - // Someone is trying to delete the database file. Exit lightningfast! - *_retval = true; - return NS_OK; - } - - if (mOldProgressHandler) { - return mOldProgressHandler->OnProgress(aConnection, _retval); - } - - *_retval = false; - return NS_OK; -} - -nsresult -AsyncConnectionHelper::Dispatch(nsIEventTarget* aTarget) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - nsresult rv = Init(); - if (NS_FAILED(rv)) { - return rv; - } - - rv = aTarget->Dispatch(this, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - if (mTransaction) { - mTransaction->OnNewRequest(); - } - - mDispatched = true; - - return NS_OK; -} - -nsresult -AsyncConnectionHelper::DispatchToTransactionPool() -{ - NS_ASSERTION(mTransaction, "Only ok to call this with a transaction!"); - TransactionPoolEventTarget target(mTransaction); - return Dispatch(&target); -} - -// static -void -AsyncConnectionHelper::SetCurrentTransaction(IDBTransaction* aTransaction) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aTransaction || !gCurrentTransaction, - "Stepping on another transaction!"); - - gCurrentTransaction = aTransaction; -} - -// static -IDBTransaction* -AsyncConnectionHelper::GetCurrentTransaction() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return gCurrentTransaction; -} - -nsresult -AsyncConnectionHelper::Init() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return NS_OK; -} - -already_AddRefed -AsyncConnectionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) -{ - return CreateGenericEvent(mRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR), - eDoesNotBubble, eNotCancelable); -} - -nsresult -AsyncConnectionHelper::OnSuccess() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mRequest, "Null request!"); - - PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnSuccess", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr event = CreateSuccessEvent(mRequest); - if (!event) { - IDB_WARNING("Failed to create event!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool dummy; - nsresult rv = mRequest->DispatchEvent(event, &dummy); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - WidgetEvent* internalEvent = event->GetInternalNSEvent(); - NS_ASSERTION(internalEvent, "This should never be null!"); - - NS_ASSERTION(!mTransaction || - mTransaction->IsOpen() || - mTransaction->IsAborted(), - "How else can this be closed?!"); - - if (internalEvent->mFlags.mExceptionHasBeenRisen && - mTransaction && - mTransaction->IsOpen()) { - rv = mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -void -AsyncConnectionHelper::OnError() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mRequest, "Null request!"); - - PROFILER_MAIN_THREAD_LABEL("AsyncConnectionHelper", "OnError", - js::ProfileEntry::Category::STORAGE); - - // Make an error event and fire it at the target. - nsRefPtr event = - CreateGenericEvent(mRequest, NS_LITERAL_STRING(ERROR_EVT_STR), eDoesBubble, - eCancelable); - if (!event) { - NS_ERROR("Failed to create event!"); - return; - } - - bool doDefault; - nsresult rv = mRequest->DispatchEvent(event, &doDefault); - if (NS_SUCCEEDED(rv)) { - NS_ASSERTION(!mTransaction || - mTransaction->IsOpen() || - mTransaction->IsAborted(), - "How else can this be closed?!"); - - WidgetEvent* internalEvent = event->GetInternalNSEvent(); - NS_ASSERTION(internalEvent, "This should never be null!"); - - if (internalEvent->mFlags.mExceptionHasBeenRisen && - mTransaction && - mTransaction->IsOpen() && - NS_FAILED(mTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR))) { - NS_WARNING("Failed to abort transaction!"); - } - - if (doDefault && - mTransaction && - mTransaction->IsOpen() && - NS_FAILED(mTransaction->Abort(mRequest))) { - NS_WARNING("Failed to abort transaction!"); - } - } - else { - NS_WARNING("DispatchEvent failed!"); - } -} - -nsresult -AsyncConnectionHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - aVal.setUndefined(); - return NS_OK; -} - -void -AsyncConnectionHelper::ReleaseMainThreadObjects() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mDatabase = nullptr; - mTransaction = nullptr; - - HelperBase::ReleaseMainThreadObjects(); -} - -AsyncConnectionHelper::ChildProcessSendResult -AsyncConnectionHelper::MaybeSendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - // If there's no request, there could never have been an actor, and so there - // is nothing to do. - if (!mRequest) { - return Success_NotSent; - } - - IDBTransaction* trans = GetCurrentTransaction(); - // We may not have a transaction, e.g. for deleteDatabase - if (!trans) { - return Success_NotSent; - } - - // Are we shutting down the child? - IndexedDBDatabaseParent* dbActor = trans->Database()->GetActorParent(); - if (dbActor && dbActor->IsDisconnected()) { - return Success_ActorDisconnected; - } - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - if (!actor) { - return Success_NotSent; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: Sending response to child " - "process (rv = %lu)", - "IDBRequest[%llu] MT Done", - mRequest->GetSerialNumber(), aResultCode); - - return SendResponseToChildProcess(aResultCode); -} - -nsresult -AsyncConnectionHelper::OnParentProcessRequestComplete( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - if (aResponseValue.type() == ResponseValue::Tnsresult) { - NS_ASSERTION(NS_FAILED(aResponseValue.get_nsresult()), "Huh?"); - SetError(aResponseValue.get_nsresult()); - } - else { - nsresult rv = UnpackResponseFromParentProcess(aResponseValue); - NS_ENSURE_SUCCESS(rv, rv); - } - - return Run(); -} - -// static -nsresult -AsyncConnectionHelper::ConvertToArrayAndCleanup( - JSContext* aCx, - nsTArray& aReadInfos, - JS::MutableHandle aResult) -{ - NS_ASSERTION(aCx, "Null context!"); - NS_ASSERTION(aResult.address(), "Null pointer!"); - - nsresult rv = ConvertCloneReadInfosToArrayInternal(aCx, aReadInfos, aResult); - - for (uint32_t index = 0; index < aReadInfos.Length(); index++) { - aReadInfos[index].mCloneBuffer.clear(); - } - aReadInfos.Clear(); - - return rv; -} - -NS_IMETHODIMP_(MozExternalRefCountType) -StackBasedEventTarget::AddRef() -{ - NS_NOTREACHED("Don't call me!"); - return 2; -} - -NS_IMETHODIMP_(MozExternalRefCountType) -StackBasedEventTarget::Release() -{ - NS_NOTREACHED("Don't call me!"); - return 1; -} - -NS_IMETHODIMP -StackBasedEventTarget::QueryInterface(REFNSIID aIID, - void** aInstancePtr) -{ - NS_NOTREACHED("Don't call me!"); - return NS_NOINTERFACE; -} - -NS_IMETHODIMP -ImmediateRunEventTarget::Dispatch(nsIRunnable* aRunnable, - uint32_t aFlags) -{ - NS_ASSERTION(aRunnable, "Null pointer!"); - - nsCOMPtr runnable(aRunnable); - DebugOnly rv = - runnable->Run(); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - return NS_OK; -} - -NS_IMETHODIMP -ImmediateRunEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) -{ - *aIsOnCurrentThread = true; - return NS_OK; -} - -NS_IMETHODIMP -TransactionPoolEventTarget::Dispatch(nsIRunnable* aRunnable, - uint32_t aFlags) -{ - NS_ASSERTION(aRunnable, "Null pointer!"); - NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "Unsupported!"); - - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - NS_ENSURE_TRUE(pool, NS_ERROR_UNEXPECTED); - - nsresult rv = pool->Dispatch(mTransaction, aRunnable, false, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -TransactionPoolEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) -{ - *aIsOnCurrentThread = false; - return NS_OK; -} - -NS_IMETHODIMP -NoDispatchEventTarget::Dispatch(nsIRunnable* aRunnable, - uint32_t aFlags) -{ - nsCOMPtr runnable = aRunnable; - return NS_OK; -} - -NS_IMETHODIMP -NoDispatchEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) -{ - *aIsOnCurrentThread = true; - return NS_OK; -} diff --git a/dom/indexedDB/AsyncConnectionHelper.h b/dom/indexedDB/AsyncConnectionHelper.h deleted file mode 100644 index e39a654a1ae8..000000000000 --- a/dom/indexedDB/AsyncConnectionHelper.h +++ /dev/null @@ -1,257 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_asyncconnectionhelper_h__ -#define mozilla_dom_indexeddb_asyncconnectionhelper_h__ - -// Only meant to be included in IndexedDB source files, not exported. -#include "DatabaseInfo.h" -#include "IndexedDatabase.h" -#include "IDBDatabase.h" -#include "IDBRequest.h" - -#include "mozIStorageProgressHandler.h" -#include "nsIEventTarget.h" -#include "nsIRunnable.h" - -class mozIStorageConnection; - -BEGIN_INDEXEDDB_NAMESPACE - -class AutoSetCurrentTransaction; -class IDBTransaction; - -namespace ipc { -class ResponseValue; -} - -// A common base class for AsyncConnectionHelper and OpenDatabaseHelper that -// IDBRequest can use. -class HelperBase : public nsIRunnable -{ - friend class IDBRequest; - -public: - - virtual nsresult GetResultCode() = 0; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) = 0; - - IDBRequest* GetRequest() const - { - return mRequest; - } - -protected: - explicit HelperBase(IDBRequest* aRequest) - : mRequest(aRequest) - { } - - virtual ~HelperBase(); - - /** - * Helper to wrap a native into a jsval. Uses the global object of the request - * to parent the native. - */ - nsresult WrapNative(JSContext* aCx, - nsISupports* aNative, - JS::MutableHandle aResult); - - /** - * Gives the subclass a chance to release any objects that must be released - * on the main thread, regardless of success or failure. Subclasses that - * implement this method *MUST* call the base class implementation as well. - */ - virtual void ReleaseMainThreadObjects(); - - nsRefPtr mRequest; -}; - -/** - * Must be subclassed. The subclass must implement DoDatabaseWork. It may then - * choose to implement OnSuccess and OnError depending on the needs of the - * subclass. If the default implementation of OnSuccess is desired then the - * subclass can implement GetSuccessResult to properly set the result of the - * success event. Call Dispatch to start the database operation. Must be created - * and Dispatched from the main thread only. Target thread may not be the main - * thread. - */ -class AsyncConnectionHelper : public HelperBase, - public mozIStorageProgressHandler -{ - friend class AutoSetCurrentTransaction; - -public: - typedef ipc::ResponseValue ResponseValue; - - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - NS_DECL_MOZISTORAGEPROGRESSHANDLER - - virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread); - - // Only for transactions! - nsresult DispatchToTransactionPool(); - - void SetError(nsresult aErrorCode) - { - NS_ASSERTION(NS_FAILED(aErrorCode), "Not a failure code!"); - mResultCode = aErrorCode; - } - - static IDBTransaction* GetCurrentTransaction(); - - bool HasTransaction() const - { - return !!mTransaction; - } - - IDBTransaction* GetTransaction() const - { - return mTransaction; - } - - virtual nsresult GetResultCode() MOZ_OVERRIDE - { - return mResultCode; - } - - enum ChildProcessSendResult - { - // The result was successfully sent to the child process - Success_Sent = 0, - - // The result was not sent, because this is not an out-of-process request. - Success_NotSent, - - // The result was not sent, because the actor has been disconnected - // (if the child process has shut down or crashed). - Success_ActorDisconnected, - - // An error occurred. - Error - }; - - ChildProcessSendResult - MaybeSendResponseToChildProcess(nsresult aResultCode); - - virtual nsresult OnParentProcessRequestComplete( - const ResponseValue& aResponseValue); - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; - -protected: - AsyncConnectionHelper(IDBDatabase* aDatabase, - IDBRequest* aRequest); - - AsyncConnectionHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest); - - virtual ~AsyncConnectionHelper(); - - /** - * This is called on the main thread after Dispatch is called but before the - * runnable is actually dispatched to the database thread. Allows the subclass - * to initialize itself. - */ - virtual nsresult Init(); - - /** - * This callback is run on the database thread. - */ - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) = 0; - - /** - * This function returns the event to be dispatched at the request when - * OnSuccess is called. A subclass can override this to fire an event other - * than "success" at the request. - */ - virtual already_AddRefed CreateSuccessEvent( - mozilla::dom::EventTarget* aOwner); - - /** - * This callback is run on the main thread if DoDatabaseWork returned NS_OK. - * The default implementation fires a "success" DOM event with its target set - * to the request. Returning anything other than NS_OK from the OnSuccess - * callback will trigger the OnError callback. - */ - virtual nsresult OnSuccess(); - - /** - * This callback is run on the main thread if DoDatabaseWork or OnSuccess - * returned an error code. The default implementation fires an "error" DOM - * event with its target set to the request. - */ - virtual void OnError(); - - /** - * This function is called by the request on the main thread when script - * accesses the result property of the request. - */ - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - /** - * Gives the subclass a chance to release any objects that must be released - * on the main thread, regardless of success or failure. Subclasses that - * implement this method *MUST* call the base class implementation as well. - */ - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - /** - * Helper to make a JS array object out of an array of clone buffers. - */ - static nsresult ConvertToArrayAndCleanup( - JSContext* aCx, - nsTArray& aReadInfos, - JS::MutableHandle aResult); - - /** - * This should only be called by AutoSetCurrentTransaction. - */ - static void SetCurrentTransaction(IDBTransaction* aTransaction); - - /** - * Allows the subclass to send its results to the child process. Will only - * be called if all of the IPC infrastructure is available (there is an - * actor, the child is stil alive and hasn't begun shutting down). - */ - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) = 0; - -protected: - nsRefPtr mDatabase; - nsRefPtr mTransaction; - -private: - nsCOMPtr mOldProgressHandler; - nsresult mResultCode; - bool mDispatched; -}; - -class MOZ_STACK_CLASS StackBasedEventTarget : public nsIEventTarget -{ -public: - NS_DECL_ISUPPORTS_INHERITED -}; - -class MOZ_STACK_CLASS ImmediateRunEventTarget : public StackBasedEventTarget -{ -public: - NS_DECL_NSIEVENTTARGET -}; - -class MOZ_STACK_CLASS NoDispatchEventTarget : public StackBasedEventTarget -{ -public: - NS_DECL_NSIEVENTTARGET -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_asyncconnectionhelper_h__ diff --git a/dom/indexedDB/CheckPermissionsHelper.cpp b/dom/indexedDB/CheckPermissionsHelper.cpp deleted file mode 100644 index b5b9c73ece19..000000000000 --- a/dom/indexedDB/CheckPermissionsHelper.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "CheckPermissionsHelper.h" - -#include "nsIDOMWindow.h" -#include "nsILoadContext.h" -#include "nsIWebNavigation.h" -#include "nsIObserverService.h" -#include "nsIPermissionManager.h" -#include "nsIPrincipal.h" -#include "nsIScriptObjectPrincipal.h" -#include "nsIURI.h" - -#include "CheckQuotaHelper.h" -#include "nsContentUtils.h" -#include "nsNetUtil.h" -#include "nsThreadUtils.h" -#include "mozilla/Services.h" - -#include "IndexedDatabaseManager.h" - -#define PERMISSION_INDEXEDDB "indexedDB" -#define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt" -#define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response" - -// This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to -// allow access without a prompt. If the "indexedDB" permission is set to -// ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise -// (DENY_ACTION) we deny access. -#define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION -#define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION -#define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION - -USING_INDEXEDDB_NAMESPACE -using namespace mozilla::services; -using mozilla::dom::quota::CheckQuotaHelper; - -namespace { - -inline -uint32_t -GetIndexedDBPermissions(nsIDOMWindow* aWindow) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!"); - - nsCOMPtr sop(do_QueryInterface(aWindow)); - NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION); - - NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()), - "Chrome windows shouldn't check the permission!"); - - nsCOMPtr webNav = do_GetInterface(aWindow); - nsCOMPtr loadContext = do_QueryInterface(webNav); - if (loadContext && loadContext->UsePrivateBrowsing()) { - // TODO Support private browsing indexedDB? - NS_WARNING("IndexedDB may not be used while in private browsing mode!"); - return PERMISSION_DENIED; - } - - nsCOMPtr permissionManager = GetPermissionManager(); - NS_ENSURE_TRUE(permissionManager, PERMISSION_DENIED); - - uint32_t permission; - nsresult rv = - permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(), - PERMISSION_INDEXEDDB, - &permission); - NS_ENSURE_SUCCESS(rv, PERMISSION_DENIED); - - return permission; -} - -} // anonymous namespace - -NS_IMPL_ISUPPORTS(CheckPermissionsHelper, nsIRunnable, - nsIInterfaceRequestor, - nsIObserver) - -NS_IMETHODIMP -CheckPermissionsHelper::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - uint32_t permission = mHasPrompted ? - mPromptResult : - GetIndexedDBPermissions(mWindow); - - nsresult rv; - if (mHasPrompted) { - // Add permissions to the database, but only if we are in the parent - // process (if we are in the child process, we have already - // set the permission when the prompt was shown in the parent, as - // we cannot set the permission from the child). - if (permission != PERMISSION_PROMPT && - IndexedDatabaseManager::IsMainProcess()) { - NS_ASSERTION(mWindow, "Null window!"); - - nsCOMPtr sop = do_QueryInterface(mWindow); - NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); - - nsIPrincipal* windowPrincipal = sop->GetPrincipal(); - NS_ASSERTION(windowPrincipal, "Null principal!"); - - nsCOMPtr permissionManager = GetPermissionManager(); - NS_ENSURE_STATE(permissionManager); - - rv = permissionManager->AddFromPrincipal(windowPrincipal, - PERMISSION_INDEXEDDB, permission, - nsIPermissionManager::EXPIRE_NEVER, - 0); - NS_ENSURE_SUCCESS(rv, rv); - } - } - else if (permission == PERMISSION_PROMPT && mPromptAllowed) { - nsCOMPtr obs = GetObserverService(); - rv = obs->NotifyObservers(static_cast(this), - TOPIC_PERMISSIONS_PROMPT, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - nsRefPtr helper; - helper.swap(mHelper); - - nsCOMPtr window; - window.swap(mWindow); - - if (permission == PERMISSION_ALLOWED) { - // If we're running from a window then we should check the quota permission - // as well. If we don't have a window then we're opening a chrome database - // and the quota will be unlimited already. - if (window) { - nsCOMPtr sop = do_QueryInterface(window); - NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); - - nsIPrincipal* windowPrincipal = sop->GetPrincipal(); - NS_ASSERTION(windowPrincipal, "Null principal!"); - - uint32_t quotaPermission = - CheckQuotaHelper::GetQuotaPermission(windowPrincipal); - - if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { - helper->SetUnlimitedQuotaAllowed(); - } - } - - return helper->DispatchToIOThread(); - } - - NS_ASSERTION(permission == PERMISSION_PROMPT || - permission == PERMISSION_DENIED, - "Unknown permission!"); - - helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - - return helper->RunImmediately(); -} - -NS_IMETHODIMP -CheckPermissionsHelper::GetInterface(const nsIID& aIID, - void** aResult) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (aIID.Equals(NS_GET_IID(nsIObserver))) { - return QueryInterface(aIID, aResult); - } - - if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) { - return mWindow->QueryInterface(aIID, aResult); - } - - *aResult = nullptr; - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -CheckPermissionsHelper::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!"); - NS_ASSERTION(mPromptAllowed, "How did we get here?"); - - mHasPrompted = true; - - nsresult rv; - uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Have to convert the permission we got from the user to our weird reversed - // permission type. - switch (promptResult) { - case nsIPermissionManager::ALLOW_ACTION: - mPromptResult = PERMISSION_ALLOWED; - break; - case nsIPermissionManager::DENY_ACTION: - mPromptResult = PERMISSION_DENIED; - break; - case nsIPermissionManager::UNKNOWN_ACTION: - mPromptResult = PERMISSION_PROMPT; - break; - - default: - NS_NOTREACHED("Unknown permission type!"); - mPromptResult = PERMISSION_DENIED; - } - - rv = NS_DispatchToCurrentThread(this); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} diff --git a/dom/indexedDB/CheckPermissionsHelper.h b/dom/indexedDB/CheckPermissionsHelper.h deleted file mode 100644 index ed6214ace671..000000000000 --- a/dom/indexedDB/CheckPermissionsHelper.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_checkpermissionshelper_h__ -#define mozilla_dom_indexeddb_checkpermissionshelper_h__ - -// Only meant to be included in IndexedDB source files, not exported. -#include "OpenDatabaseHelper.h" - -#include "nsIInterfaceRequestor.h" -#include "nsIObserver.h" -#include "nsIRunnable.h" - -class nsIDOMWindow; -class nsIThread; - -BEGIN_INDEXEDDB_NAMESPACE - -class CheckPermissionsHelper MOZ_FINAL : public nsIRunnable, - public nsIInterfaceRequestor, - public nsIObserver -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - NS_DECL_NSIINTERFACEREQUESTOR - NS_DECL_NSIOBSERVER - - CheckPermissionsHelper(OpenDatabaseHelper* aHelper, - nsIDOMWindow* aWindow) - : mHelper(aHelper), - mWindow(aWindow), - // If we're trying to delete the database, we should never prompt the user. - // Anything that would prompt is translated to denied. - mPromptAllowed(!aHelper->mForDeletion), - mHasPrompted(false), - mPromptResult(0) - { - NS_ASSERTION(aHelper, "Null pointer!"); - NS_ASSERTION(aHelper->mPersistenceType == quota::PERSISTENCE_TYPE_PERSISTENT, - "Checking permission for non persistent databases?!"); - } - -private: - ~CheckPermissionsHelper() {} - - nsRefPtr mHelper; - nsCOMPtr mWindow; - bool mPromptAllowed; - bool mHasPrompted; - uint32_t mPromptResult; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_checkpermissionshelper_h__ diff --git a/dom/indexedDB/Client.cpp b/dom/indexedDB/Client.cpp deleted file mode 100644 index 6157ed0d2605..000000000000 --- a/dom/indexedDB/Client.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "Client.h" - -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/dom/quota/UsageInfo.h" -#include "mozilla/dom/quota/Utilities.h" - -#include "IDBDatabase.h" -#include "IndexedDatabaseManager.h" -#include "TransactionThreadPool.h" -#include "nsISimpleEnumerator.h" - -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::quota::AssertIsOnIOThread; -using mozilla::dom::quota::QuotaManager; - -namespace { - -bool -GetDatabaseBaseFilename(const nsAString& aFilename, - nsAString& aDatabaseBaseFilename) -{ - NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!"); - - NS_NAMED_LITERAL_STRING(sqlite, ".sqlite"); - - if (!StringEndsWith(aFilename, sqlite)) { - return false; - } - - aDatabaseBaseFilename = - Substring(aFilename, 0, aFilename.Length() - sqlite.Length()); - - return true; -} - -} // anonymous namespace - -// This needs to be fully qualified to not confuse trace refcnt assertions. -NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client) -NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client) - -nsresult -Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, - const nsACString& aOrigin, UsageInfo* aUsageInfo) -{ - AssertIsOnIOThread(); - - nsCOMPtr directory; - nsresult rv = - GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); - NS_ENSURE_SUCCESS(rv, rv); - - // We need to see if there are any files in the directory already. If they - // are database files then we need to cleanup stored files (if it's needed) - // and also get the usage. - - nsAutoTArray subdirsToProcess; - nsAutoTArray, 20> unknownFiles; - nsTHashtable validSubdirs(20); - - nsCOMPtr entries; - rv = directory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && - hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr file = do_QueryInterface(entry); - NS_ENSURE_TRUE(file, NS_NOINTERFACE); - - nsString leafName; - rv = file->GetLeafName(leafName); - NS_ENSURE_SUCCESS(rv, rv); - - if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { - continue; - } - - if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { - continue; - } - - bool isDirectory; - rv = file->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - - if (isDirectory) { - if (!validSubdirs.GetEntry(leafName)) { - subdirsToProcess.AppendElement(leafName); - } - continue; - } - - nsString dbBaseFilename; - if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) { - unknownFiles.AppendElement(file); - continue; - } - - nsCOMPtr fmDirectory; - rv = directory->Clone(getter_AddRefs(fmDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = fmDirectory->Append(dbBaseFilename); - NS_ENSURE_SUCCESS(rv, rv); - - rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup, - aOrigin); - NS_ENSURE_SUCCESS(rv, rv); - - if (aUsageInfo) { - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(fileSize >= 0, "Negative size?!"); - - aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); - - uint64_t usage; - rv = FileManager::GetUsage(fmDirectory, &usage); - NS_ENSURE_SUCCESS(rv, rv); - - aUsageInfo->AppendToFileUsage(usage); - } - - validSubdirs.PutEntry(dbBaseFilename); - } - NS_ENSURE_SUCCESS(rv, rv); - - for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) { - const nsString& subdir = subdirsToProcess[i]; - if (!validSubdirs.GetEntry(subdir)) { - NS_WARNING("Unknown subdirectory found!"); - return NS_ERROR_UNEXPECTED; - } - } - - for (uint32_t i = 0; i < unknownFiles.Length(); i++) { - nsCOMPtr& unknownFile = unknownFiles[i]; - - // Some temporary SQLite files could disappear, so we have to check if the - // unknown file still exists. - bool exists; - rv = unknownFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - nsString leafName; - unknownFile->GetLeafName(leafName); - - // The journal file may exists even after db has been correctly opened. - if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) { - NS_WARNING("Unknown file found!"); - return NS_ERROR_UNEXPECTED; - } - } - } - - return NS_OK; -} - -nsresult -Client::GetUsageForOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, const nsACString& aOrigin, - UsageInfo* aUsageInfo) -{ - AssertIsOnIOThread(); - NS_ASSERTION(aUsageInfo, "Null pointer!"); - - nsCOMPtr directory; - nsresult rv = - GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -Client::OnOriginClearCompleted(PersistenceType aPersistenceType, - const OriginOrPatternString& aOriginOrPattern) -{ - AssertIsOnIOThread(); - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - if (mgr) { - mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern); - } -} - -void -Client::ReleaseIOThreadObjects() -{ - AssertIsOnIOThread(); - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - if (mgr) { - mgr->InvalidateAllFileManagers(); - } -} - -bool -Client::IsTransactionServiceActivated() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return !!TransactionThreadPool::Get(); -} - -void -Client::WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aStorages.IsEmpty(), "No storages to wait on!"); - NS_ASSERTION(aCallback, "Passed null callback!"); - - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "Should have checked if transaction service is active!"); - - nsTArray > databases(aStorages.Length()); - for (uint32_t index = 0; index < aStorages.Length(); index++) { - IDBDatabase* database = IDBDatabase::FromStorage(aStorages[index]); - if (!database) { - MOZ_CRASH(); - } - - databases.AppendElement(database); - } - - pool->WaitForDatabasesToComplete(databases, aCallback); -} - -void -Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aStorage, "Passed null storage!"); - - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "Should have checked if transaction service is active!"); - - IDBDatabase* database = IDBDatabase::FromStorage(aStorage); - NS_ASSERTION(database, "This shouldn't be null!"); - - pool->AbortTransactionsForDatabase(database); -} - -bool -Client::HasTransactionsForStorage(nsIOfflineStorage* aStorage) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - TransactionThreadPool* pool = TransactionThreadPool::Get(); - NS_ASSERTION(pool, "Should have checked if transaction service is active!"); - - IDBDatabase* database = IDBDatabase::FromStorage(aStorage); - NS_ASSERTION(database, "This shouldn't be null!"); - - return pool->HasTransactionsForDatabase(database); -} - -void -Client::ShutdownTransactionService() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - TransactionThreadPool::Shutdown(); -} - -nsresult -Client::GetDirectory(PersistenceType aPersistenceType, - const nsACString& aOrigin, nsIFile** aDirectory) -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsCOMPtr directory; - nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin, - getter_AddRefs(directory)); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(directory, "What?"); - - rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - NS_ENSURE_SUCCESS(rv, rv); - - directory.forget(aDirectory); - return NS_OK; -} - -nsresult -Client::GetUsageForDirectoryInternal(nsIFile* aDirectory, - UsageInfo* aUsageInfo, - bool aDatabaseFiles) -{ - NS_ASSERTION(aDirectory, "Null pointer!"); - NS_ASSERTION(aUsageInfo, "Null pointer!"); - - nsCOMPtr entries; - nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - if (!entries) { - return NS_OK; - } - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && - hasMore && !aUsageInfo->Canceled()) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr file(do_QueryInterface(entry)); - NS_ASSERTION(file, "Don't know what this is!"); - - bool isDirectory; - rv = file->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - - if (isDirectory) { - if (aDatabaseFiles) { - rv = GetUsageForDirectoryInternal(file, aUsageInfo, false); - NS_ENSURE_SUCCESS(rv, rv); - } - else { - nsString leafName; - rv = file->GetLeafName(leafName); - NS_ENSURE_SUCCESS(rv, rv); - - if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { - NS_WARNING("Unknown directory found!"); - } - } - - continue; - } - - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(fileSize >= 0, "Negative size?!"); - - if (aDatabaseFiles) { - aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize)); - } - else { - aUsageInfo->AppendToFileUsage(uint64_t(fileSize)); - } - } - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} diff --git a/dom/indexedDB/Client.h b/dom/indexedDB/Client.h deleted file mode 100644 index d9d580b307df..000000000000 --- a/dom/indexedDB/Client.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_client_h__ -#define mozilla_dom_indexeddb_client_h__ - -#include "IndexedDatabase.h" - -#include "mozilla/dom/quota/Client.h" - -#define JOURNAL_DIRECTORY_NAME "journals" - -BEGIN_INDEXEDDB_NAMESPACE - -class Client : public mozilla::dom::quota::Client -{ - typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString; - typedef mozilla::dom::quota::PersistenceType PersistenceType; - typedef mozilla::dom::quota::UsageInfo UsageInfo; - -public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() MOZ_OVERRIDE; - - NS_IMETHOD_(MozExternalRefCountType) - Release() MOZ_OVERRIDE; - - virtual Type - GetType() MOZ_OVERRIDE - { - return IDB; - } - - virtual nsresult - InitOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) MOZ_OVERRIDE; - - virtual nsresult - GetUsageForOrigin(PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - UsageInfo* aUsageInfo) MOZ_OVERRIDE; - - virtual void - OnOriginClearCompleted(PersistenceType aPersistenceType, - const OriginOrPatternString& aOriginOrPattern) - MOZ_OVERRIDE; - - virtual void - ReleaseIOThreadObjects() MOZ_OVERRIDE; - - virtual bool - IsFileServiceUtilized() MOZ_OVERRIDE - { - return true; - } - - virtual bool - IsTransactionServiceActivated() MOZ_OVERRIDE; - - virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) MOZ_OVERRIDE; - - virtual void - AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; - - virtual bool - HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE; - - virtual void - ShutdownTransactionService() MOZ_OVERRIDE; - -private: - ~Client() {} - - nsresult - GetDirectory(PersistenceType aPersistenceType, const nsACString& aOrigin, - nsIFile** aDirectory); - - nsresult - GetUsageForDirectoryInternal(nsIFile* aDirectory, - UsageInfo* aUsageInfo, - bool aDatabaseFiles); - - nsAutoRefCnt mRefCnt; - NS_DECL_OWNINGTHREAD -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_client_h__ diff --git a/dom/indexedDB/DatabaseInfo.cpp b/dom/indexedDB/DatabaseInfo.cpp deleted file mode 100644 index fd7b6887e6e5..000000000000 --- a/dom/indexedDB/DatabaseInfo.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "DatabaseInfo.h" - -#include "nsDataHashtable.h" -#include "nsThreadUtils.h" - -USING_INDEXEDDB_NAMESPACE - -namespace { - -typedef nsDataHashtable - DatabaseHash; - -DatabaseHash* gDatabaseHash = nullptr; - -PLDHashOperator -EnumerateObjectStoreNames(const nsAString& aKey, - ObjectStoreInfo* aData, - void* aUserArg) -{ - nsTArray* array = static_cast*>(aUserArg); - if (!array->InsertElementSorted(aData->name)) { - NS_ERROR("Out of memory?"); - return PL_DHASH_STOP; - } - return PL_DHASH_NEXT; -} - -PLDHashOperator -CloneObjectStoreInfo(const nsAString& aKey, - ObjectStoreInfo* aData, - void* aUserArg) -{ - ObjectStoreInfoHash* hash = static_cast(aUserArg); - - nsRefPtr newInfo(new ObjectStoreInfo(*aData)); - - hash->Put(aKey, newInfo); - - return PL_DHASH_NEXT; -} - -} - -DatabaseInfo::~DatabaseInfo() -{ - // Clones are never in the hash. - if (!cloned) { - DatabaseInfo::Remove(id); - } -} - -ObjectStoreInfo::ObjectStoreInfo(ObjectStoreInfo& aOther) -: nextAutoIncrementId(aOther.nextAutoIncrementId), - comittedAutoIncrementId(aOther.comittedAutoIncrementId) -{ - *static_cast(this) = - static_cast(aOther); - - // Doesn't copy the refcount - MOZ_COUNT_CTOR(ObjectStoreInfo); -} - -#ifdef NS_BUILD_REFCNT_LOGGING - -IndexInfo::IndexInfo() -: id(INT64_MIN), - keyPath(0), - unique(false), - multiEntry(false) -{ - MOZ_COUNT_CTOR(IndexInfo); -} - -IndexInfo::IndexInfo(const IndexInfo& aOther) -: name(aOther.name), - id(aOther.id), - keyPath(aOther.keyPath), - unique(aOther.unique), - multiEntry(aOther.multiEntry) -{ - MOZ_COUNT_CTOR(IndexInfo); -} - -IndexInfo::~IndexInfo() -{ - MOZ_COUNT_DTOR(IndexInfo); -} - -ObjectStoreInfo::ObjectStoreInfo() -: nextAutoIncrementId(0), - comittedAutoIncrementId(0) -{ - MOZ_COUNT_CTOR(ObjectStoreInfo); -} - -ObjectStoreInfo::~ObjectStoreInfo() -{ - MOZ_COUNT_DTOR(ObjectStoreInfo); -} - -IndexUpdateInfo::IndexUpdateInfo() -: indexId(0), - indexUnique(false) -{ - MOZ_COUNT_CTOR(IndexUpdateInfo); -} - -IndexUpdateInfo::IndexUpdateInfo(const IndexUpdateInfo& aOther) -: indexId(aOther.indexId), - indexUnique(aOther.indexUnique), - value(aOther.value) -{ - MOZ_COUNT_CTOR(IndexUpdateInfo); -} - -IndexUpdateInfo::~IndexUpdateInfo() -{ - MOZ_COUNT_DTOR(IndexUpdateInfo); -} - -#endif /* NS_BUILD_REFCNT_LOGGING */ - -// static -bool -DatabaseInfo::Get(const nsACString& aId, - DatabaseInfo** aInfo) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aId.IsEmpty(), "Bad id!"); - - if (gDatabaseHash && - gDatabaseHash->Get(aId, aInfo)) { - NS_IF_ADDREF(*aInfo); - return true; - } - return false; -} - -// static -bool -DatabaseInfo::Put(DatabaseInfo* aInfo) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aInfo, "Null pointer!"); - - if (!gDatabaseHash) { - nsAutoPtr databaseHash(new DatabaseHash()); - gDatabaseHash = databaseHash.forget(); - } - - if (gDatabaseHash->Get(aInfo->id, nullptr)) { - NS_ERROR("Already know about this database!"); - return false; - } - - gDatabaseHash->Put(aInfo->id, aInfo); - - return true; -} - -// static -void -DatabaseInfo::Remove(const nsACString& aId) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (gDatabaseHash) { - gDatabaseHash->Remove(aId); - - if (!gDatabaseHash->Count()) { - delete gDatabaseHash; - gDatabaseHash = nullptr; - } - } -} - -bool -DatabaseInfo::GetObjectStoreNames(nsTArray& aNames) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - aNames.Clear(); - if (objectStoreHash) { - objectStoreHash->EnumerateRead(EnumerateObjectStoreNames, &aNames); - } - return true; -} - -bool -DatabaseInfo::ContainsStoreName(const nsAString& aName) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return objectStoreHash && objectStoreHash->Get(aName, nullptr); -} - -ObjectStoreInfo* -DatabaseInfo::GetObjectStore(const nsAString& aName) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (objectStoreHash) { - return objectStoreHash->GetWeak(aName); - } - - return nullptr; -} - -bool -DatabaseInfo::PutObjectStore(ObjectStoreInfo* aInfo) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aInfo, "Null pointer!"); - - if (!objectStoreHash) { - nsAutoPtr hash(new ObjectStoreInfoHash()); - objectStoreHash = hash.forget(); - } - - if (objectStoreHash->Get(aInfo->name, nullptr)) { - NS_ERROR("Already have an entry for this objectstore!"); - return false; - } - - objectStoreHash->Put(aInfo->name, aInfo); - return true; -} - -void -DatabaseInfo::RemoveObjectStore(const nsAString& aName) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(GetObjectStore(aName), "Don't know about this one!"); - - if (objectStoreHash) { - objectStoreHash->Remove(aName); - } -} - -already_AddRefed -DatabaseInfo::Clone() -{ - nsRefPtr dbInfo(new DatabaseInfo()); - - dbInfo->cloned = true; - dbInfo->name = name; - dbInfo->group = group; - dbInfo->origin = origin; - dbInfo->version = version; - dbInfo->persistenceType = persistenceType; - dbInfo->id = id; - dbInfo->filePath = filePath; - dbInfo->nextObjectStoreId = nextObjectStoreId; - dbInfo->nextIndexId = nextIndexId; - - if (objectStoreHash) { - dbInfo->objectStoreHash = new ObjectStoreInfoHash(); - objectStoreHash->EnumerateRead(CloneObjectStoreInfo, - dbInfo->objectStoreHash); - } - - return dbInfo.forget(); -} diff --git a/dom/indexedDB/DatabaseInfo.h b/dom/indexedDB/DatabaseInfo.h deleted file mode 100644 index 3b9fa30b3a5d..000000000000 --- a/dom/indexedDB/DatabaseInfo.h +++ /dev/null @@ -1,203 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_databaseinfo_h__ -#define mozilla_dom_indexeddb_databaseinfo_h__ - -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "mozilla/dom/quota/PersistenceType.h" -#include "nsRefPtrHashtable.h" -#include "nsHashKeys.h" - -#include "mozilla/dom/indexedDB/Key.h" -#include "mozilla/dom/indexedDB/KeyPath.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" - -BEGIN_INDEXEDDB_NAMESPACE - -class IndexedDBDatabaseChild; -struct ObjectStoreInfo; - -typedef nsRefPtrHashtable - ObjectStoreInfoHash; - -struct DatabaseInfoGuts -{ - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - DatabaseInfoGuts() - : nextObjectStoreId(1), nextIndexId(1) - { } - - bool operator==(const DatabaseInfoGuts& aOther) const - { - return this->name == aOther.name && - this->group == aOther.group && - this->origin == aOther.origin && - this->version == aOther.version && - this->persistenceType == aOther.persistenceType && - this->nextObjectStoreId == aOther.nextObjectStoreId && - this->nextIndexId == aOther.nextIndexId; - }; - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - nsString name; - nsCString group; - nsCString origin; - uint64_t version; - PersistenceType persistenceType; - int64_t nextObjectStoreId; - int64_t nextIndexId; -}; - -struct DatabaseInfo MOZ_FINAL : public DatabaseInfoGuts -{ - DatabaseInfo() - : cloned(false) - { } - -private: - // Private destructor, to discourage deletion outside of Release(): - ~DatabaseInfo(); - -public: - static bool Get(const nsACString& aId, - DatabaseInfo** aInfo); - - static bool Put(DatabaseInfo* aInfo); - - static void Remove(const nsACString& aId); - - bool GetObjectStoreNames(nsTArray& aNames); - bool ContainsStoreName(const nsAString& aName); - - ObjectStoreInfo* GetObjectStore(const nsAString& aName); - - bool PutObjectStore(ObjectStoreInfo* aInfo); - - void RemoveObjectStore(const nsAString& aName); - - already_AddRefed Clone(); - - nsCString id; - nsString filePath; - bool cloned; - - nsAutoPtr objectStoreHash; - - NS_INLINE_DECL_REFCOUNTING(DatabaseInfo) -}; - -struct IndexInfo -{ -#ifdef NS_BUILD_REFCNT_LOGGING - IndexInfo(); - IndexInfo(const IndexInfo& aOther); - ~IndexInfo(); -#else - IndexInfo() - : id(INT64_MIN), keyPath(0), unique(false), multiEntry(false) { } -#endif - - bool operator==(const IndexInfo& aOther) const - { - return this->name == aOther.name && - this->id == aOther.id && - this->keyPath == aOther.keyPath && - this->unique == aOther.unique && - this->multiEntry == aOther.multiEntry; - }; - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - nsString name; - int64_t id; - KeyPath keyPath; - bool unique; - bool multiEntry; -}; - -struct ObjectStoreInfoGuts -{ - ObjectStoreInfoGuts() - : id(0), keyPath(0), autoIncrement(false) - { } - - bool operator==(const ObjectStoreInfoGuts& aOther) const - { - return this->name == aOther.name && - this->id == aOther.id; - }; - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - - // Constant members, can be gotten on any thread - nsString name; - int64_t id; - KeyPath keyPath; - bool autoIncrement; - - // Main-thread only members. This must *not* be touched on the database - // thread. - nsTArray indexes; -}; - -struct ObjectStoreInfo MOZ_FINAL : public ObjectStoreInfoGuts -{ -#ifdef NS_BUILD_REFCNT_LOGGING - ObjectStoreInfo(); -#else - ObjectStoreInfo() - : nextAutoIncrementId(0), comittedAutoIncrementId(0) { } -#endif - - ObjectStoreInfo(ObjectStoreInfo& aOther); - -private: - // Private destructor, to discourage deletion outside of Release(): -#ifdef NS_BUILD_REFCNT_LOGGING - ~ObjectStoreInfo(); -#else - ~ObjectStoreInfo() {} -#endif -public: - - // Database-thread members. After the ObjectStoreInfo has been initialized, - // these can *only* be touced on the database thread. - int64_t nextAutoIncrementId; - int64_t comittedAutoIncrementId; - - // This is threadsafe since the ObjectStoreInfos are created on the database - // thread but then only used from the main thread. Ideal would be if we - // could transfer ownership from the database thread to the main thread, but - // we don't have that ability yet. - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObjectStoreInfo) -}; - -struct IndexUpdateInfo -{ -#ifdef NS_BUILD_REFCNT_LOGGING - IndexUpdateInfo(); - IndexUpdateInfo(const IndexUpdateInfo& aOther); - ~IndexUpdateInfo(); -#endif - - bool operator==(const IndexUpdateInfo& aOther) const - { - return this->indexId == aOther.indexId && - this->indexUnique == aOther.indexUnique && - this->value == aOther.value; - }; - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - int64_t indexId; - bool indexUnique; - Key value; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_databaseinfo_h__ diff --git a/dom/indexedDB/FileInfo.cpp b/dom/indexedDB/FileInfo.cpp index c177e5d31c2c..b7ad44f9d2df 100644 --- a/dom/indexedDB/FileInfo.cpp +++ b/dom/indexedDB/FileInfo.cpp @@ -5,68 +5,113 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileInfo.h" -#include "nsThreadUtils.h" -#include "mozilla/dom/quota/QuotaManager.h" -USING_INDEXEDDB_NAMESPACE +#include "FileManager.h" +#include "IndexedDatabaseManager.h" +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Mutex.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "nsError.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace dom { +namespace indexedDB { + +using namespace mozilla::dom::quota; namespace { -class CleanupFileRunnable MOZ_FINAL : public nsIRunnable +template +class FileInfoImpl MOZ_FINAL + : public FileInfo { - ~CleanupFileRunnable() {} + IdType mFileId; public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId); + FileInfoImpl(FileManager* aFileManager, IdType aFileId) + : FileInfo(aFileManager) + , mFileId(aFileId) + { + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aFileId > 0); + } private: + ~FileInfoImpl() + { } + + virtual int64_t + Id() const MOZ_OVERRIDE + { + return int64_t(mFileId); + } +}; + +class CleanupFileRunnable MOZ_FINAL + : public nsRunnable +{ nsRefPtr mFileManager; int64_t mFileId; + +public: + static void + DoCleanup(FileManager* aFileManager, int64_t aFileId); + + CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId) + : mFileManager(aFileManager) + , mFileId(aFileId) + { + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aFileId > 0); + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~CleanupFileRunnable() + { } + + NS_DECL_NSIRUNNABLE }; } // anonymous namespace +FileInfo::FileInfo(FileManager* aFileManager) + : mFileManager(aFileManager) +{ + MOZ_ASSERT(aFileManager); +} + +FileInfo::~FileInfo() +{ +} + // static FileInfo* FileInfo::Create(FileManager* aFileManager, int64_t aId) { - MOZ_ASSERT(aId > 0, "Wrong id!"); + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aId > 0); if (aId <= INT16_MAX) { - return new FileInfo16(aFileManager, aId); + return new FileInfoImpl(aFileManager, aId); } if (aId <= INT32_MAX) { - return new FileInfo32(aFileManager, aId); + return new FileInfoImpl(aFileManager, aId); } - return new FileInfo64(aFileManager, aId); + return new FileInfoImpl(aFileManager, aId); } void -FileInfo::GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, +FileInfo::GetReferences(int32_t* aRefCnt, + int32_t* aDBRefCnt, int32_t* aSliceRefCnt) { - if (IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - - if (aRefCnt) { - *aRefCnt = -1; - } - - if (aDBRefCnt) { - *aDBRefCnt = -1; - } - - if (aSliceRefCnt) { - *aSliceRefCnt = -1; - } - - return; - } + MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); @@ -84,14 +129,31 @@ FileInfo::GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, } void -FileInfo::UpdateReferences(mozilla::ThreadSafeAutoRefCnt& aRefCount, - int32_t aDelta, bool aClear) +FileInfo::UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, + int32_t aDelta, + bool aClear) { + // XXX This can go away once DOM objects no longer hold FileInfo objects... + // Looking at you, IDBMutableFile... if (IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); + MOZ_ASSERT(&aRefCount == &mRefCnt); + MOZ_ASSERT(aDelta == 1 || aDelta == -1); + MOZ_ASSERT(!aClear); + + if (aDelta > 0) { + ++aRefCount; + } else { + nsrefcnt count = --aRefCount; + if (!count) { + mRefCnt = 1; + delete this; + } + } return; } + MOZ_ASSERT(!IndexedDatabaseManager::IsClosed()); + bool needsCleanup; { MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); @@ -117,40 +179,52 @@ FileInfo::UpdateReferences(mozilla::ThreadSafeAutoRefCnt& aRefCount, void FileInfo::Cleanup() { - nsRefPtr cleaner = - new CleanupFileRunnable(mFileManager, Id()); + int64_t id = Id(); // IndexedDatabaseManager is main-thread only. if (!NS_IsMainThread()) { - NS_DispatchToMainThread(cleaner); + nsRefPtr cleaner = + new CleanupFileRunnable(mFileManager, id); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(cleaner))); return; } - cleaner->Run(); + CleanupFileRunnable::DoCleanup(mFileManager, id); } -CleanupFileRunnable::CleanupFileRunnable(FileManager* aFileManager, - int64_t aFileId) -: mFileManager(aFileManager), mFileId(aFileId) +// static +void +CleanupFileRunnable::DoCleanup(FileManager* aFileManager, int64_t aFileId) { -} + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aFileId > 0); -NS_IMPL_ISUPPORTS(CleanupFileRunnable, - nsIRunnable) - -NS_IMETHODIMP -CleanupFileRunnable::Run() -{ - if (mozilla::dom::quota::QuotaManager::IsShuttingDown()) { - return NS_OK; + if (NS_WARN_IF(QuotaManager::IsShuttingDown())) { + return; } nsRefPtr mgr = IndexedDatabaseManager::Get(); MOZ_ASSERT(mgr); - if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, mFileId))) { + if (NS_FAILED(mgr->AsyncDeleteFile(aFileManager, aFileId))) { NS_WARNING("Failed to delete file asynchronously!"); } +} + +NS_IMPL_ISUPPORTS_INHERITED0(CleanupFileRunnable, nsRunnable) + +NS_IMETHODIMP +CleanupFileRunnable::Run() +{ + MOZ_ASSERT(NS_IsMainThread()); + + DoCleanup(mFileManager, mFileId); return NS_OK; } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/FileInfo.h b/dom/indexedDB/FileInfo.h index 9272bd16d7c1..83d786950362 100644 --- a/dom/indexedDB/FileInfo.h +++ b/dom/indexedDB/FileInfo.h @@ -7,112 +7,88 @@ #ifndef mozilla_dom_indexeddb_fileinfo_h__ #define mozilla_dom_indexeddb_fileinfo_h__ -#include "IndexedDatabase.h" +#include "nsAutoPtr.h" +#include "nsISupportsImpl.h" -#include "FileManager.h" -#include "IndexedDatabaseManager.h" +namespace mozilla { +namespace dom { +namespace indexedDB { -BEGIN_INDEXEDDB_NAMESPACE +class FileManager; class FileInfo { friend class FileManager; -public: - explicit FileInfo(FileManager* aFileManager) - : mFileManager(aFileManager) - { } - - virtual ~FileInfo() - { } - - static - FileInfo* Create(FileManager* aFileManager, int64_t aId); - - void AddRef() - { - if (IndexedDatabaseManager::IsClosed()) { - ++mRefCnt; - } - else { - UpdateReferences(mRefCnt, 1); - } - } - - void Release() - { - if (IndexedDatabaseManager::IsClosed()) { - nsrefcnt count = --mRefCnt; - if (count == 0) { - mRefCnt = 1; - delete this; - } - } - else { - UpdateReferences(mRefCnt, -1); - } - } - - void UpdateDBRefs(int32_t aDelta) - { - UpdateReferences(mDBRefCnt, aDelta); - } - - void ClearDBRefs() - { - UpdateReferences(mDBRefCnt, 0, true); - } - - void UpdateSliceRefs(int32_t aDelta) - { - UpdateReferences(mSliceRefCnt, aDelta); - } - - void GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, - int32_t* aSliceRefCnt); - - FileManager* Manager() const - { - return mFileManager; - } - - virtual int64_t Id() const = 0; - -private: - void UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, int32_t aDelta, - bool aClear = false); - void Cleanup(); - ThreadSafeAutoRefCnt mRefCnt; ThreadSafeAutoRefCnt mDBRefCnt; ThreadSafeAutoRefCnt mSliceRefCnt; nsRefPtr mFileManager; + +public: + static + FileInfo* Create(FileManager* aFileManager, int64_t aId); + + explicit FileInfo(FileManager* aFileManager); + + void + AddRef() + { + UpdateReferences(mRefCnt, 1); + } + + void + Release() + { + UpdateReferences(mRefCnt, -1); + } + + void + UpdateDBRefs(int32_t aDelta) + { + UpdateReferences(mDBRefCnt, aDelta); + } + + void + ClearDBRefs() + { + UpdateReferences(mDBRefCnt, 0, true); + } + + void + UpdateSliceRefs(int32_t aDelta) + { + UpdateReferences(mSliceRefCnt, aDelta); + } + + void + GetReferences(int32_t* aRefCnt, int32_t* aDBRefCnt, int32_t* aSliceRefCnt); + + FileManager* + Manager() const + { + return mFileManager; + } + + virtual int64_t + Id() const = 0; + +protected: + virtual ~FileInfo(); + +private: + void + UpdateReferences(ThreadSafeAutoRefCnt& aRefCount, + int32_t aDelta, + bool aClear = false); + + void + Cleanup(); }; -#define FILEINFO_SUBCLASS(_bits) \ -class FileInfo##_bits : public FileInfo \ -{ \ -public: \ - FileInfo##_bits(FileManager* aFileManager, int##_bits##_t aId) \ - : FileInfo(aFileManager), mId(aId) \ - { } \ - \ - virtual int64_t Id() const \ - { \ - return mId; \ - } \ - \ -private: \ - int##_bits##_t mId; \ -}; - -FILEINFO_SUBCLASS(16) -FILEINFO_SUBCLASS(32) -FILEINFO_SUBCLASS(64) - -#undef FILEINFO_SUBCLASS - -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_fileinfo_h__ diff --git a/dom/indexedDB/FileManager.cpp b/dom/indexedDB/FileManager.cpp deleted file mode 100644 index c0fc7ea94525..000000000000 --- a/dom/indexedDB/FileManager.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "FileManager.h" - -#include "mozIStorageConnection.h" -#include "mozIStorageStatement.h" -#include "nsIInputStream.h" -#include "nsISimpleEnumerator.h" - -#include "mozilla/dom/quota/Utilities.h" -#include "mozStorageCID.h" -#include "mozStorageHelper.h" - -#include "Client.h" -#include "FileInfo.h" -#include "IndexedDatabaseManager.h" -#include "OpenDatabaseHelper.h" - -#include "IndexedDatabaseInlines.h" -#include - -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::quota::AssertIsOnIOThread; - -namespace { - -PLDHashOperator -EnumerateToTArray(const uint64_t& aKey, - FileInfo* aValue, - void* aUserArg) -{ - NS_ASSERTION(aValue, "Null pointer!"); - NS_ASSERTION(aUserArg, "Null pointer!"); - - nsTArray* array = - static_cast*>(aUserArg); - - array->AppendElement(aValue); - - return PL_DHASH_NEXT; -} - -already_AddRefed -GetDirectoryFor(const nsAString& aDirectoryPath) -{ - nsCOMPtr directory = - do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); - NS_ENSURE_TRUE(directory, nullptr); - - nsresult rv = directory->InitWithPath(aDirectoryPath); - NS_ENSURE_SUCCESS(rv, nullptr); - - return directory.forget(); -} - -} // anonymous namespace - -nsresult -FileManager::Init(nsIFile* aDirectory, - mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(aDirectory, "Null directory!"); - NS_ASSERTION(aConnection, "Null connection!"); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - bool isDirectory; - rv = aDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); - } - else { - rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = aDirectory->GetPath(mDirectoryPath); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr journalDirectory; - rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = journalDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - bool isDirectory; - rv = journalDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); - } - - rv = journalDirectory->GetPath(mJournalDirectoryPath); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stmt; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, refcount " - "FROM file" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { - int64_t id; - rv = stmt->GetInt64(0, &id); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t refcount; - rv = stmt->GetInt32(1, &refcount); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(refcount, "This shouldn't happen!"); - - nsRefPtr fileInfo = FileInfo::Create(this, id); - fileInfo->mDBRefCnt = refcount; - - mFileInfos.Put(id, fileInfo); - - mLastFileId = std::max(id, mLastFileId); - } - - return NS_OK; -} - -nsresult -FileManager::Invalidate() -{ - if (IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - return NS_ERROR_UNEXPECTED; - } - - nsTArray fileInfos; - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - - NS_ASSERTION(!mInvalidated, "Invalidate more than once?!"); - mInvalidated = true; - - fileInfos.SetCapacity(mFileInfos.Count()); - mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos); - } - - for (uint32_t i = 0; i < fileInfos.Length(); i++) { - FileInfo* fileInfo = fileInfos.ElementAt(i); - fileInfo->ClearDBRefs(); - } - - return NS_OK; -} - -already_AddRefed -FileManager::GetDirectory() -{ - return GetDirectoryFor(mDirectoryPath); -} - -already_AddRefed -FileManager::GetJournalDirectory() -{ - return GetDirectoryFor(mJournalDirectoryPath); -} - -already_AddRefed -FileManager::EnsureJournalDirectory() -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - nsCOMPtr journalDirectory = GetDirectoryFor(mJournalDirectoryPath); - NS_ENSURE_TRUE(journalDirectory, nullptr); - - bool exists; - nsresult rv = journalDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, nullptr); - - if (exists) { - bool isDirectory; - rv = journalDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, nullptr); - NS_ENSURE_TRUE(isDirectory, nullptr); - } - else { - rv = journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - NS_ENSURE_SUCCESS(rv, nullptr); - } - - return journalDirectory.forget(); -} - -already_AddRefed -FileManager::GetFileInfo(int64_t aId) -{ - if (IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - return nullptr; - } - - FileInfo* fileInfo = nullptr; - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - fileInfo = mFileInfos.Get(aId); - } - nsRefPtr result = fileInfo; - return result.forget(); -} - -already_AddRefed -FileManager::GetNewFileInfo() -{ - if (IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - return nullptr; - } - - nsAutoPtr fileInfo; - - { - MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - - int64_t id = mLastFileId + 1; - - fileInfo = FileInfo::Create(this, id); - - mFileInfos.Put(id, fileInfo); - - mLastFileId = id; - } - - nsRefPtr result = fileInfo.forget(); - return result.forget(); -} - -// static -already_AddRefed -FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId) -{ - NS_ASSERTION(aDirectory, "Null pointer!"); - - nsAutoString id; - id.AppendInt(aId); - - nsCOMPtr file; - nsresult rv = aDirectory->Clone(getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, nullptr); - - rv = file->Append(id); - NS_ENSURE_SUCCESS(rv, nullptr); - - return file.forget(); -} - -// static -nsresult -FileManager::InitDirectory(nsIFile* aDirectory, - nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin) -{ - AssertIsOnIOThread(); - NS_ASSERTION(aDirectory, "Null directory!"); - NS_ASSERTION(aDatabaseFile, "Null database file!"); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (!exists) { - return NS_OK; - } - - bool isDirectory; - rv = aDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); - - nsCOMPtr journalDirectory; - rv = aDirectory->Clone(getter_AddRefs(journalDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = journalDirectory->Append(NS_LITERAL_STRING(JOURNAL_DIRECTORY_NAME)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = journalDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - rv = journalDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE); - - nsCOMPtr entries; - rv = journalDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasElements; - rv = entries->HasMoreElements(&hasElements); - NS_ENSURE_SUCCESS(rv, rv); - - if (hasElements) { - nsCOMPtr connection; - rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile, - aDirectory, NullString(), aPersistenceType, aGroup, aOrigin, - getter_AddRefs(connection)); - NS_ENSURE_SUCCESS(rv, rv); - - mozStorageTransaction transaction(connection, false); - - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE VIRTUAL TABLE fs USING filesystem;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stmt; - rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, (name IN (SELECT id FROM file)) FROM fs " - "WHERE path = :path" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - nsString path; - rv = journalDirectory->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { - nsString name; - rv = stmt->GetString(0, name); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t flag = stmt->AsInt32(1); - - if (!flag) { - nsCOMPtr file; - rv = aDirectory->Clone(getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = file->Append(name); - NS_ENSURE_SUCCESS(rv, rv); - - if (NS_FAILED(file->Remove(false))) { - NS_WARNING("Failed to remove orphaned file!"); - } - } - - nsCOMPtr journalFile; - rv = journalDirectory->Clone(getter_AddRefs(journalFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = journalFile->Append(name); - NS_ENSURE_SUCCESS(rv, rv); - - if (NS_FAILED(journalFile->Remove(false))) { - NS_WARNING("Failed to remove journal file!"); - } - } - - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE fs;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - transaction.Commit(); - } - } - - return NS_OK; -} - -// static -nsresult -FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) -{ - AssertIsOnIOThread(); - - bool exists; - nsresult rv = aDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (!exists) { - *aUsage = 0; - return NS_OK; - } - - nsCOMPtr entries; - rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - uint64_t usage = 0; - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr file = do_QueryInterface(entry); - NS_ENSURE_TRUE(file, NS_NOINTERFACE); - - nsString leafName; - rv = file->GetLeafName(leafName); - NS_ENSURE_SUCCESS(rv, rv); - - if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) { - continue; - } - - int64_t fileSize; - rv = file->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - quota::IncrementUsage(&usage, uint64_t(fileSize)); - } - - *aUsage = usage; - return NS_OK; -} diff --git a/dom/indexedDB/FileManager.h b/dom/indexedDB/FileManager.h index 83734a65320b..a50546dd1549 100644 --- a/dom/indexedDB/FileManager.h +++ b/dom/indexedDB/FileManager.h @@ -7,21 +7,23 @@ #ifndef mozilla_dom_indexeddb_filemanager_h__ #define mozilla_dom_indexeddb_filemanager_h__ -#include "IndexedDatabase.h" - -#include "nsIDOMFile.h" -#include "nsIFile.h" - +#include "mozilla/Attributes.h" #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/dom/quota/StoragePrivilege.h" #include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsISupportsImpl.h" +class nsIFile; class mozIStorageConnection; -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { class FileInfo; +// Implemented in ActorsParent.cpp. class FileManager MOZ_FINAL { friend class FileInfo; @@ -29,79 +31,6 @@ class FileManager MOZ_FINAL typedef mozilla::dom::quota::PersistenceType PersistenceType; typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; -public: - FileManager(PersistenceType aPersistenceType, const nsACString& aGroup, - const nsACString& aOrigin, StoragePrivilege aPrivilege, - const nsAString& aDatabaseName) - : mPersistenceType(aPersistenceType), mGroup(aGroup), mOrigin(aOrigin), - mPrivilege(aPrivilege), mDatabaseName(aDatabaseName), mLastFileId(0), - mInvalidated(false) - { } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager) - - PersistenceType Type() - { - return mPersistenceType; - } - - const nsACString& Group() const - { - return mGroup; - } - - const nsACString& Origin() const - { - return mOrigin; - } - - const StoragePrivilege& Privilege() const - { - return mPrivilege; - } - - const nsAString& DatabaseName() const - { - return mDatabaseName; - } - - bool Invalidated() const - { - return mInvalidated; - } - - nsresult Init(nsIFile* aDirectory, - mozIStorageConnection* aConnection); - - nsresult Invalidate(); - - already_AddRefed GetDirectory(); - - already_AddRefed GetJournalDirectory(); - - already_AddRefed EnsureJournalDirectory(); - - already_AddRefed GetFileInfo(int64_t aId); - - already_AddRefed GetNewFileInfo(); - - static already_AddRefed GetFileForId(nsIFile* aDirectory, - int64_t aId); - - static nsresult InitDirectory(nsIFile* aDirectory, - nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin); - - static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage); - -private: - // Private destructor, to discourage deletion outside of Release(): - ~FileManager() - { - } - PersistenceType mPersistenceType; nsCString mGroup; nsCString mOrigin; @@ -117,8 +46,92 @@ private: nsDataHashtable mFileInfos; bool mInvalidated; + +public: + static already_AddRefed + GetFileForId(nsIFile* aDirectory, int64_t aId); + + static nsresult + InitDirectory(nsIFile* aDirectory, + nsIFile* aDatabaseFile, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin); + + static nsresult + GetUsage(nsIFile* aDirectory, uint64_t* aUsage); + + FileManager(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + StoragePrivilege aPrivilege, + const nsAString& aDatabaseName); + + PersistenceType + Type() const + { + return mPersistenceType; + } + + const nsACString& + Group() const + { + return mGroup; + } + + const nsACString& + Origin() const + { + return mOrigin; + } + + const StoragePrivilege& + Privilege() const + { + return mPrivilege; + } + + const nsAString& + DatabaseName() const + { + return mDatabaseName; + } + + bool + Invalidated() const + { + return mInvalidated; + } + + nsresult + Init(nsIFile* aDirectory, mozIStorageConnection* aConnection); + + nsresult + Invalidate(); + + already_AddRefed + GetDirectory(); + + already_AddRefed + GetJournalDirectory(); + + already_AddRefed + EnsureJournalDirectory(); + + already_AddRefed + GetFileInfo(int64_t aId); + + already_AddRefed + GetNewFileInfo(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager) + +private: + ~FileManager(); }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_filemanager_h__ diff --git a/dom/indexedDB/FileSnapshot.cpp b/dom/indexedDB/FileSnapshot.cpp index 32f04c55f16f..c1048c67c1fd 100644 --- a/dom/indexedDB/FileSnapshot.cpp +++ b/dom/indexedDB/FileSnapshot.cpp @@ -7,39 +7,56 @@ #include "FileSnapshot.h" #include "IDBFileHandle.h" +#include "MainThreadUtils.h" #include "mozilla/Assertions.h" -#include "nsDebug.h" +#include "mozilla/dom/MetadataHelper.h" + +#ifdef DEBUG +#include "nsXULAppAPI.h" +#endif namespace mozilla { namespace dom { namespace indexedDB { -NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, DOMFileImpl) - // Create as a stored file FileImplSnapshot::FileImplSnapshot(const nsAString& aName, const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, + MetadataParameters* aMetadataParams, + nsIFile* aFile, IDBFileHandle* aFileHandle, FileInfo* aFileInfo) - : DOMFileImplBase(aName, aContentType, aLength), - mFile(aFile), mFileHandle(aFileHandle), mWholeFile(true) + : DOMFileImplBase(aName, + aContentType, + aMetadataParams->Size(), + aMetadataParams->LastModified()) + , mFile(aFile) + , mFileHandle(aFileHandle) + , mWholeFile(true) { - MOZ_ASSERT(mFile, "Null file!"); - MOZ_ASSERT(mFileHandle, "Null file handle!"); + AssertSanity(); + MOZ_ASSERT(aMetadataParams); + MOZ_ASSERT(aMetadataParams->Size() != UINT64_MAX); + MOZ_ASSERT(aMetadataParams->LastModified() != INT64_MAX); + MOZ_ASSERT(aFile); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aFileInfo); + mFileInfos.AppendElement(aFileInfo); } // Create slice FileImplSnapshot::FileImplSnapshot(const FileImplSnapshot* aOther, - uint64_t aStart, uint64_t aLength, + uint64_t aStart, + uint64_t aLength, const nsAString& aContentType) - : DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength), - mFile(aOther->mFile), mFileHandle(aOther->mFileHandle), - mWholeFile(false) + : DOMFileImplBase(aContentType, aOther->mStart + aStart, aLength) + , mFile(aOther->mFile) + , mFileHandle(aOther->mFileHandle) + , mWholeFile(false) { - MOZ_ASSERT(mFile, "Null file!"); - MOZ_ASSERT(mFileHandle, "Null file handle!"); + AssertSanity(); + MOZ_ASSERT(aOther); FileInfo* fileInfo; @@ -57,9 +74,25 @@ FileImplSnapshot::~FileImplSnapshot() { } +#ifdef DEBUG + +// static +void +FileImplSnapshot::AssertSanity() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); +} + +#endif // DEBUG + +NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, DOMFileImpl) + void FileImplSnapshot::Unlink() { + AssertSanity(); + FileImplSnapshot* tmp = this; NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileHandle); } @@ -67,43 +100,80 @@ FileImplSnapshot::Unlink() void FileImplSnapshot::Traverse(nsCycleCollectionTraversalCallback &cb) { + AssertSanity(); + FileImplSnapshot* tmp = this; NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileHandle); } +bool +FileImplSnapshot::IsCCed() const +{ + AssertSanity(); + + return true; +} + nsresult FileImplSnapshot::GetInternalStream(nsIInputStream** aStream) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertSanity(); nsresult rv = mFileHandle->OpenInputStream(mWholeFile, mStart, mLength, aStream); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + return rv; + } return NS_OK; } -already_AddRefed -FileImplSnapshot::CreateSlice(uint64_t aStart, uint64_t aLength, +already_AddRefed +FileImplSnapshot::CreateSlice(uint64_t aStart, + uint64_t aLength, const nsAString& aContentType) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertSanity(); - nsCOMPtr t = - new DOMFile(new FileImplSnapshot(this, aStart, aLength, aContentType)); + nsRefPtr impl = + new FileImplSnapshot(this, aStart, aLength, aContentType); - return t.forget(); + return impl.forget(); } nsresult FileImplSnapshot::GetMozFullPathInternal(nsAString& aFilename) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mIsFile, "Should only be called on files"); + AssertSanity(); + MOZ_ASSERT(mIsFile); return mFile->GetPath(aFilename); } +bool +FileImplSnapshot::IsStoredFile() const +{ + AssertSanity(); + + return true; +} + +bool +FileImplSnapshot::IsWholeFile() const +{ + AssertSanity(); + + return mWholeFile; +} + +bool +FileImplSnapshot::IsSnapshot() const +{ + AssertSanity(); + + return true; +} + } // namespace indexedDB } // namespace dom } // namespace mozilla diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index c3aa2749dfe7..5816d3ee15b1 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -10,26 +10,55 @@ #include "mozilla/Attributes.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" #include "nsDOMFile.h" namespace mozilla { namespace dom { + +class MetadataParameters; + namespace indexedDB { class IDBFileHandle; -class FileImplSnapshot : public DOMFileImplBase +class FileImplSnapshot MOZ_FINAL + : public DOMFileImplBase { -public: - NS_DECL_ISUPPORTS_INHERITED + typedef mozilla::dom::MetadataParameters MetadataParameters; + nsCOMPtr mFile; + nsRefPtr mFileHandle; + + bool mWholeFile; + +public: // Create as a stored file - FileImplSnapshot(const nsAString& aName, const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, IDBFileHandle* aFileHandle, + FileImplSnapshot(const nsAString& aName, + const nsAString& aContentType, + MetadataParameters* aMetadataParams, + nsIFile* aFile, + IDBFileHandle* aFileHandle, FileInfo* aFileInfo); - // Overrides + NS_DECL_ISUPPORTS_INHERITED + +private: + // Create slice + FileImplSnapshot(const FileImplSnapshot* aOther, + uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType); + + ~FileImplSnapshot(); + + static void + AssertSanity() +#ifdef DEBUG + ; +#else + { } +#endif + virtual nsresult GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE; @@ -43,45 +72,21 @@ public: Traverse(nsCycleCollectionTraversalCallback &aCb) MOZ_OVERRIDE; virtual bool - IsCCed() const MOZ_OVERRIDE - { - return true; - } + IsCCed() const MOZ_OVERRIDE; -protected: - // Create slice - FileImplSnapshot(const FileImplSnapshot* aOther, uint64_t aStart, - uint64_t aLength, const nsAString& aContentType); - - virtual ~FileImplSnapshot(); - - virtual already_AddRefed - CreateSlice(uint64_t aStart, uint64_t aLength, + virtual already_AddRefed + CreateSlice(uint64_t aStart, + uint64_t aLength, const nsAString& aContentType) MOZ_OVERRIDE; virtual bool - IsStoredFile() const MOZ_OVERRIDE - { - return true; - } + IsStoredFile() const MOZ_OVERRIDE; virtual bool - IsWholeFile() const MOZ_OVERRIDE - { - return mWholeFile; - } + IsWholeFile() const MOZ_OVERRIDE; virtual bool - IsSnapshot() const MOZ_OVERRIDE - { - return true; - } - -private: - nsCOMPtr mFile; - nsRefPtr mFileHandle; - - bool mWholeFile; + IsSnapshot() const MOZ_OVERRIDE; }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index ec985566ff1b..af9594773d10 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -4,338 +4,194 @@ * 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 "base/basictypes.h" - #include "IDBCursor.h" -#include "mozilla/storage.h" -#include "nsComponentManagerUtils.h" -#include "nsJSUtils.h" -#include "nsThreadUtils.h" - -#include "AsyncConnectionHelper.h" -#include "IDBEvents.h" +#include "IDBDatabase.h" #include "IDBIndex.h" #include "IDBObjectStore.h" +#include "IDBRequest.h" #include "IDBTransaction.h" +#include "IndexedDatabaseInlines.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/UnionTypes.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "nsString.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "TransactionThreadPool.h" -#include "ipc/IndexedDBChild.h" -#include "ipc/IndexedDBParent.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#include "IndexedDatabaseInlines.h" -#include "mozilla/dom/BindingDeclarations.h" -#include "mozilla/dom/UnionTypes.h" +namespace mozilla { +namespace dom { +namespace indexedDB { -USING_INDEXEDDB_NAMESPACE -using namespace mozilla::dom::indexedDB::ipc; -using mozilla::dom::Optional; -using mozilla::dom::OwningIDBObjectStoreOrIDBIndex; -using mozilla::ErrorResult; - -static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction), - "Relying on conversion between size_t and IDBCursor::Direction"); - -namespace { - -class CursorHelper : public AsyncConnectionHelper +IDBCursor::IDBCursor(Type aType, + IDBObjectStore* aSourceObjectStore, + IDBIndex* aSourceIndex, + IDBTransaction* aTransaction, + BackgroundCursorChild* aBackgroundActor, + Direction aDirection, + const Key& aKey) + : mSourceObjectStore(aSourceObjectStore) + , mSourceIndex(aSourceIndex) + , mTransaction(aTransaction) + , mBackgroundActor(aBackgroundActor) + , mScriptOwner(aTransaction->Database()->GetScriptOwner()) + , mCachedKey(JSVAL_VOID) + , mCachedPrimaryKey(JSVAL_VOID) + , mCachedValue(JSVAL_VOID) + , mKey(aKey) + , mType(aType) + , mDirection(aDirection) + , mHaveCachedKey(false) + , mHaveCachedPrimaryKey(false) + , mHaveCachedValue(false) + , mRooted(false) + , mContinueCalled(false) + , mHaveValue(true) { -public: - explicit CursorHelper(IDBCursor* aCursor) - : AsyncConnectionHelper(aCursor->Transaction(), aCursor->Request()), - mCursor(aCursor), mActor(nullptr) - { - NS_ASSERTION(aCursor, "Null cursor!"); + MOZ_ASSERT_IF(aType == Type_ObjectStore || aType == Type_ObjectStoreKey, + aSourceObjectStore); + MOZ_ASSERT_IF(aType == Type_Index || aType == Type_IndexKey, aSourceIndex); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!aKey.IsUnset()); + MOZ_ASSERT(mScriptOwner); + + SetIsDOMBinding(); + + if (mScriptOwner) { + mozilla::HoldJSObjects(this); + mRooted = true; } +} - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(CursorRequestParams& aParams) = 0; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; - -protected: - virtual ~CursorHelper() - { } - - nsRefPtr mCursor; - -private: - IndexedDBCursorRequestChild* mActor; -}; - -} // anonymous namespace - -BEGIN_INDEXEDDB_NAMESPACE - -class ContinueHelper : public CursorHelper +IDBCursor::~IDBCursor() { -public: - ContinueHelper(IDBCursor* aCursor, - int32_t aCount) - : CursorHelper(aCursor), mCount(aCount) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aCursor); - MOZ_ASSERT(aCount > 0); + AssertIsOnOwningThread(); + + DropJSObjects(); + + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) - MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(CursorRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - virtual ~ContinueHelper() - { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - } - - virtual nsresult - BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0; - - virtual nsresult - GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0; - - void UpdateCursorState() - { - mCursor->mCachedKey = JSVAL_VOID; - mCursor->mCachedPrimaryKey = JSVAL_VOID; - mCursor->mCachedValue = JSVAL_VOID; - mCursor->mHaveCachedKey = false; - mCursor->mHaveCachedPrimaryKey = false; - mCursor->mHaveCachedValue = false; - mCursor->mContinueCalled = false; - - if (mKey.IsUnset()) { - mCursor->mHaveValue = false; - } else { - MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE || - mCursor->mType == IDBCursor::OBJECTSTOREKEY || - !mObjectKey.IsUnset()); - - // Set new values. - mCursor->mKey = mKey; - mCursor->mObjectKey = mObjectKey; - mCursor->mContinueToKey.Unset(); - - mCursor->mCloneReadInfo = Move(mCloneReadInfo); - mCloneReadInfo.mCloneBuffer.clear(); - } - } - - int32_t mCount; - Key mKey; - Key mObjectKey; - StructuredCloneReadInfo mCloneReadInfo; -}; - -class ContinueObjectStoreHelper : public ContinueHelper -{ -public: - ContinueObjectStoreHelper(IDBCursor* aCursor, - uint32_t aCount) - : ContinueHelper(aCursor, aCount) - { } - -protected: - virtual ~ContinueObjectStoreHelper() - { } - -private: - nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); - nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); -}; - -class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper -{ -public: - ContinueObjectStoreKeyHelper(IDBCursor* aCursor, - uint32_t aCount) - : ContinueObjectStoreHelper(aCursor, aCount) - { } - -private: - virtual ~ContinueObjectStoreKeyHelper() - { } - - virtual nsresult - GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE; -}; - -class ContinueIndexHelper : public ContinueHelper -{ -public: - ContinueIndexHelper(IDBCursor* aCursor, - uint32_t aCount) - : ContinueHelper(aCursor, aCount) - { } - -protected: - virtual ~ContinueIndexHelper() - { } - -private: - nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement); - nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); -}; - -class ContinueIndexObjectHelper : public ContinueIndexHelper -{ -public: - ContinueIndexObjectHelper(IDBCursor* aCursor, - uint32_t aCount) - : ContinueIndexHelper(aCursor, aCount) - { } - -private: - virtual ~ContinueIndexObjectHelper() - { } - - nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement); -}; - -END_INDEXEDDB_NAMESPACE - -// static -already_AddRefed -IDBCursor::Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, - Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, - const Key& aKey, - StructuredCloneReadInfo&& aCloneReadInfo) -{ - NS_ASSERTION(aObjectStore, "Null pointer!"); - NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); - - nsRefPtr cursor = - IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, - aRangeKey, aContinueQuery, aContinueToQuery); - NS_ASSERTION(cursor, "This shouldn't fail!"); - - cursor->mObjectStore = aObjectStore; - cursor->mType = OBJECTSTORE; - cursor->mKey = aKey; - cursor->mCloneReadInfo = Move(aCloneReadInfo); - - return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, +IDBCursor::Create(IDBObjectStore* aObjectStore, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, - const Key& aKey) + const Key& aKey, + StructuredCloneReadInfo&& aCloneInfo) { MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); MOZ_ASSERT(!aKey.IsUnset()); nsRefPtr cursor = - IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection, - aRangeKey, aContinueQuery, aContinueToQuery); - NS_ASSERTION(cursor, "This shouldn't fail!"); + new IDBCursor(Type_ObjectStore, + aObjectStore, + nullptr, + aObjectStore->Transaction(), + aBackgroundActor, + aDirection, + aKey); - cursor->mObjectStore = aObjectStore; - cursor->mType = OBJECTSTOREKEY; - cursor->mKey = aKey; + cursor->mCloneInfo = Move(aCloneInfo); return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBIndex* aIndex, +IDBCursor::Create(IDBObjectStore* aObjectStore, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, - const Key& aKey, - const Key& aObjectKey) + const Key& aKey) { - NS_ASSERTION(aIndex, "Null pointer!"); - NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); - NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!"); + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!aKey.IsUnset()); nsRefPtr cursor = - IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), - aDirection, aRangeKey, aContinueQuery, - aContinueToQuery); - NS_ASSERTION(cursor, "This shouldn't fail!"); - - cursor->mIndex = aIndex; - cursor->mType = INDEXKEY; - cursor->mKey = aKey, - cursor->mObjectKey = aObjectKey; + new IDBCursor(Type_ObjectStoreKey, + aObjectStore, + nullptr, + aObjectStore->Transaction(), + aBackgroundActor, + aDirection, + aKey); return cursor.forget(); } // static already_AddRefed -IDBCursor::Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBIndex* aIndex, +IDBCursor::Create(IDBIndex* aIndex, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, const Key& aKey, - const Key& aObjectKey, - StructuredCloneReadInfo&& aCloneReadInfo) + const Key& aPrimaryKey, + StructuredCloneReadInfo&& aCloneInfo) { - NS_ASSERTION(aIndex, "Null pointer!"); - NS_ASSERTION(!aKey.IsUnset(), "Bad key!"); + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!aKey.IsUnset()); + MOZ_ASSERT(!aPrimaryKey.IsUnset()); nsRefPtr cursor = - IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(), - aDirection, aRangeKey, aContinueQuery, - aContinueToQuery); - NS_ASSERTION(cursor, "This shouldn't fail!"); + new IDBCursor(Type_Index, + nullptr, + aIndex, + aIndex->ObjectStore()->Transaction(), + aBackgroundActor, + aDirection, + aKey); - cursor->mObjectStore = aIndex->ObjectStore(); - cursor->mIndex = aIndex; - cursor->mType = INDEXOBJECT; - cursor->mKey = aKey; - cursor->mObjectKey = aObjectKey; - cursor->mCloneReadInfo = Move(aCloneReadInfo); + cursor->mPrimaryKey = Move(aPrimaryKey); + cursor->mCloneInfo = Move(aCloneInfo); return cursor.forget(); } // static -IDBCursor::Direction -IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection) +already_AddRefed +IDBCursor::Create(IDBIndex* aIndex, + BackgroundCursorChild* aBackgroundActor, + Direction aDirection, + const Key& aKey, + const Key& aPrimaryKey) +{ + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!aKey.IsUnset()); + MOZ_ASSERT(!aPrimaryKey.IsUnset()); + + nsRefPtr cursor = + new IDBCursor(Type_IndexKey, + nullptr, + aIndex, + aIndex->ObjectStore()->Transaction(), + aBackgroundActor, + aDirection, + aKey); + + cursor->mPrimaryKey = Move(aPrimaryKey); + + return cursor.forget(); +} + +// static +auto +IDBCursor::ConvertDirection(IDBCursorDirection aDirection) -> Direction { switch (aDirection) { case mozilla::dom::IDBCursorDirection::Next: @@ -355,232 +211,77 @@ IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection) } } -// static -already_AddRefed -IDBCursor::CreateCommon(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, - Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery) +#ifdef DEBUG + +void +IDBCursor::AssertIsOnOwningThread() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aRequest, "Null pointer!"); - NS_ASSERTION(aTransaction, "Null pointer!"); - NS_ASSERTION(aObjectStore, "Null pointer!"); - NS_ASSERTION(!aContinueQuery.IsEmpty() || - !IndexedDatabaseManager::IsMainProcess(), - "Empty query!"); - NS_ASSERTION(!aContinueToQuery.IsEmpty() || - !IndexedDatabaseManager::IsMainProcess(), - "Empty query!"); - - nsRefPtr cursor = new IDBCursor(); - - IDBDatabase* database = aTransaction->Database(); - cursor->mScriptOwner = database->GetScriptOwner(); - - if (cursor->mScriptOwner) { - mozilla::HoldJSObjects(cursor.get()); - cursor->mRooted = true; - } - - cursor->mRequest = aRequest; - cursor->mTransaction = aTransaction; - cursor->mObjectStore = aObjectStore; - cursor->mDirection = aDirection; - cursor->mContinueQuery = aContinueQuery; - cursor->mContinueToQuery = aContinueToQuery; - cursor->mRangeKey = aRangeKey; - - return cursor.forget(); + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); } -IDBCursor::IDBCursor() -: mScriptOwner(nullptr), - mType(OBJECTSTORE), - mDirection(IDBCursor::NEXT), - mCachedKey(JSVAL_VOID), - mCachedPrimaryKey(JSVAL_VOID), - mCachedValue(JSVAL_VOID), - mActorChild(nullptr), - mActorParent(nullptr), - mHaveCachedKey(false), - mHaveCachedPrimaryKey(false), - mHaveCachedValue(false), - mRooted(false), - mContinueCalled(false), - mHaveValue(true) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - SetIsDOMBinding(); -} - -IDBCursor::~IDBCursor() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); - } - - DropJSObjects(); - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); -} +#endif // DEBUG void IDBCursor::DropJSObjects() { + AssertIsOnOwningThread(); + + Reset(); + if (!mRooted) { return; } + mScriptOwner = nullptr; - mCachedKey = JSVAL_VOID; - mCachedPrimaryKey = JSVAL_VOID; - mCachedValue = JSVAL_VOID; - mHaveCachedKey = false; - mHaveCachedPrimaryKey = false; - mHaveCachedValue = false; mRooted = false; - mHaveValue = false; + mozilla::DropJSObjects(this); } void -IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv) +IDBCursor::Reset() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aCount > 0); + AssertIsOnOwningThread(); - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return; - } + mCachedKey.setUndefined(); + mCachedPrimaryKey.setUndefined(); + mCachedValue.setUndefined(); + IDBObjectStore::ClearCloneReadInfo(mCloneInfo); - if (!mHaveValue || mContinueCalled) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return; - } - - mContinueToKey = aKey; - - MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done); - - mRequest->Reset(); - - nsRefPtr helper; - switch (mType) { - case OBJECTSTORE: - helper = new ContinueObjectStoreHelper(this, aCount); - break; - - case OBJECTSTOREKEY: - helper = new ContinueObjectStoreKeyHelper(this, aCount); - break; - - case INDEXKEY: - helper = new ContinueIndexHelper(this, aCount); - break; - - case INDEXOBJECT: - helper = new ContinueIndexObjectHelper(this, aCount); - break; - - default: - MOZ_CRASH("Unknown cursor type!"); - } - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return; - } - - mContinueCalled = true; + mHaveCachedKey = false; + mHaveCachedPrimaryKey = false; + mHaveCachedValue = false; + mHaveValue = false; + mContinueCalled = false; } -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndex) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) - NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER - NS_ASSERTION(tmp->mHaveCachedKey || tmp->mCachedKey.isUndefined(), - "Should have a cached key"); - NS_ASSERTION(tmp->mHaveCachedPrimaryKey || - tmp->mCachedPrimaryKey.isUndefined(), - "Should have a cached primary key"); - NS_ASSERTION(tmp->mHaveCachedValue || tmp->mCachedValue.isUndefined(), - "Should have a cached value"); - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey) - NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) - NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER - // Don't unlink mObjectStore, mIndex, or mTransaction! - tmp->DropJSObjects(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) -NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) - -JSObject* -IDBCursor::WrapObject(JSContext* aCx) +nsPIDOMWindow* +IDBCursor::GetParentObject() const { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); + MOZ_ASSERT(mTransaction); - switch (mType) { - case OBJECTSTORE: - case INDEXOBJECT: - return IDBCursorWithValueBinding::Wrap(aCx, this); - - case OBJECTSTOREKEY: - case INDEXKEY: - return IDBCursorBinding::Wrap(aCx, this); - - default: - MOZ_CRASH("Bad type!"); - } + return mTransaction->GetParentObject(); } -mozilla::dom::IDBCursorDirection +IDBCursorDirection IDBCursor::GetDirection() const { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); switch (mDirection) { case NEXT: - return mozilla::dom::IDBCursorDirection::Next; + return IDBCursorDirection::Next; case NEXT_UNIQUE: - return mozilla::dom::IDBCursorDirection::Nextunique; + return IDBCursorDirection::Nextunique; case PREV: - return mozilla::dom::IDBCursorDirection::Prev; + return IDBCursorDirection::Prev; case PREV_UNIQUE: - return mozilla::dom::IDBCursorDirection::Prevunique; + return IDBCursorDirection::Prevunique; default: MOZ_CRASH("Bad direction!"); @@ -590,20 +291,20 @@ IDBCursor::GetDirection() const void IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); switch (mType) { - case OBJECTSTORE: - case OBJECTSTOREKEY: - MOZ_ASSERT(mObjectStore); - aSource.SetAsIDBObjectStore() = mObjectStore; - break; + case Type_ObjectStore: + case Type_ObjectStoreKey: + MOZ_ASSERT(mSourceObjectStore); + aSource.SetAsIDBObjectStore() = mSourceObjectStore; + return; - case INDEXKEY: - case INDEXOBJECT: - MOZ_ASSERT(mIndex); - aSource.SetAsIDBIndex() = mIndex; - break; + case Type_Index: + case Type_IndexKey: + MOZ_ASSERT(mSourceIndex); + aSource.SetAsIDBIndex() = mSourceIndex; + return; default: MOZ_ASSERT_UNREACHABLE("Bad type!"); @@ -614,7 +315,8 @@ void IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); + MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue); if (!mHaveValue) { @@ -644,7 +346,7 @@ void IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); if (!mHaveValue) { aResult.setUndefined(); @@ -658,7 +360,10 @@ IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, } const Key& key = - (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey; + (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) ? + mKey : + mPrimaryKey; + MOZ_ASSERT(!key.IsUnset()); aRv = key.ToJSVal(aCx, mCachedPrimaryKey); @@ -677,8 +382,8 @@ void IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); + AssertIsOnOwningThread(); + MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); if (!mHaveValue) { aResult.setUndefined(); @@ -692,12 +397,12 @@ IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle aResult, } JS::Rooted val(aCx); - if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) { + if (NS_WARN_IF(!IDBObjectStore::DeserializeValue(aCx, mCloneInfo, &val))) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } - mCloneReadInfo.mCloneBuffer.clear(); + IDBObjectStore::ClearCloneReadInfo(mCloneInfo); mCachedValue = val; mHaveCachedValue = true; @@ -712,24 +417,36 @@ IDBCursor::Continue(JSContext* aCx, JS::Handle aKey, ErrorResult &aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } + + if (!mHaveValue || mContinueCalled) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return; + } Key key; aRv = key.SetFromJSVal(aCx, aKey); - ENSURE_SUCCESS_VOID(aRv); + if (aRv.Failed()) { + return; + } if (!key.IsUnset()) { switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: + case NEXT: + case NEXT_UNIQUE: if (key <= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; } break; - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: + case PREV: + case PREV_UNIQUE: if (key >= mKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return; @@ -741,13 +458,12 @@ IDBCursor::Continue(JSContext* aCx, } } - ContinueInternal(key, 1, aRv); - if (aRv.Failed()) { - return; - } + mBackgroundActor->SendContinueInternal(ContinueParams(key)); + + mContinueCalled = true; #ifdef IDB_PROFILER_USE_MARKS - if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { + if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).cursor(%s)." "continue(%s)", @@ -755,11 +471,10 @@ IDBCursor::Continue(JSContext* aCx, Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(mSourceObjectStore), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } - else { + } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "cursor(%s).continue(%s)", @@ -767,46 +482,107 @@ IDBCursor::Continue(JSContext* aCx, Request()->GetSerialNumber(), IDB_PROFILER_STRING(Transaction()->Database()), IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(mIndex), + IDB_PROFILER_STRING(mSourceIndex->ObjectStore()), + IDB_PROFILER_STRING(mSourceIndex), IDB_PROFILER_STRING(mDirection), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); } #endif } +void +IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv) +{ + AssertIsOnOwningThread(); + + if (!mTransaction->IsOpen()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } + + if (!mHaveValue || mContinueCalled) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return; + } + + if (!aCount) { + aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT); + return; + } + + mBackgroundActor->SendContinueInternal(AdvanceParams(aCount)); + + mContinueCalled = true; + +#ifdef IDB_PROFILER_USE_MARKS + { + if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "cursor(%s).advance(%ld)", + "IDBRequest[%llu] MT IDBCursor.advance()", + Request()->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(mSourceObjectStore), + IDB_PROFILER_STRING(mDirection), aCount); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "index(%s).cursor(%s).advance(%ld)", + "IDBRequest[%llu] MT IDBCursor.advance()", + Request()->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(mSourceIndex->ObjectStore()), + IDB_PROFILER_STRING(mSourceIndex), + IDB_PROFILER_STRING(mDirection), aCount); + } + } +#endif +} + already_AddRefed IDBCursor::Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } + if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return nullptr; + MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); + MOZ_ASSERT(!mKey.IsUnset()); + MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset()); + + IDBObjectStore* objectStore; + if (mType == Type_ObjectStore) { + objectStore = mSourceObjectStore; + } else { + objectStore = mSourceIndex->ObjectStore(); } - MOZ_ASSERT(mObjectStore); - MOZ_ASSERT(!mKey.IsUnset()); - MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); - MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset()); + MOZ_ASSERT(objectStore); - const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; + const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; nsRefPtr request; - if (mObjectStore->HasValidKeyPath()) { + + if (objectStore->HasValidKeyPath()) { // Make sure the object given has the correct keyPath value set on it. - const KeyPath& keyPath = mObjectStore->GetKeyPath(); + const KeyPath& keyPath = objectStore->GetKeyPath(); Key key; aRv = keyPath.ExtractKey(aCx, aValue, key); @@ -814,32 +590,35 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, return nullptr; } - if (key != objectKey) { + if (key != primaryKey) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); return nullptr; } - request = mObjectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv); + request = objectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv); if (aRv.Failed()) { return nullptr; } } else { JS::Rooted keyVal(aCx); - aRv = objectKey.ToJSVal(aCx, &keyVal); - ENSURE_SUCCESS(aRv, nullptr); + aRv = primaryKey.ToJSVal(aCx, &keyVal); + if (aRv.Failed()) { + return nullptr; + } - request = mObjectStore->Put(aCx, aValue, keyVal, aRv); + request = objectStore->Put(aCx, aValue, keyVal, aRv); if (aRv.Failed()) { return nullptr; } } + request->SetSource(this); + #ifdef IDB_PROFILER_USE_MARKS { - uint64_t requestSerial = - static_cast(request.get())->GetSerialNumber(); - if (mType == OBJECTSTORE) { + uint64_t requestSerial = request->GetSerialNumber(); + if (mType == Type_ObjectStore) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).update(%s)", @@ -847,12 +626,11 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(objectStore), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(objectKey)); - } - else { + IDB_PROFILER_STRING(primaryKey)); + } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).update(%s)", @@ -860,11 +638,11 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(mIndex), + IDB_PROFILER_STRING(objectStore), + IDB_PROFILER_STRING(mSourceIndex), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(objectKey)); + IDB_PROFILER_STRING(primaryKey)); } } #endif @@ -875,40 +653,54 @@ IDBCursor::Update(JSContext* aCx, JS::Handle aValue, already_AddRefed IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } + if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index); + MOZ_ASSERT(!mKey.IsUnset()); + + IDBObjectStore* objectStore; + if (mType == Type_ObjectStore) { + objectStore = mSourceObjectStore; + } else { + objectStore = mSourceIndex->ObjectStore(); + } + + MOZ_ASSERT(objectStore); + + const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey; + + JS::Rooted key(aCx); + aRv = primaryKey.ToJSVal(aCx, &key); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - MOZ_ASSERT(mObjectStore); - MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT); - MOZ_ASSERT(!mKey.IsUnset()); + nsRefPtr request = objectStore->Delete(aCx, key, aRv); + if (aRv.Failed()) { + return nullptr; + } - const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey; - - JS::Rooted key(aCx); - aRv = objectKey.ToJSVal(aCx, &key); - ENSURE_SUCCESS(aRv, nullptr); - - nsRefPtr request = mObjectStore->Delete(aCx, key, aRv); - ENSURE_SUCCESS(aRv, nullptr); + request->SetSource(this); #ifdef IDB_PROFILER_USE_MARKS { uint64_t requestSerial = request->GetSerialNumber(); - if (mType == OBJECTSTORE) { + if (mType == Type_ObjectStore) { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "cursor(%s).delete(%s)", @@ -916,12 +708,11 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(objectStore), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(objectKey)); - } - else { + IDB_PROFILER_STRING(primaryKey)); + } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s)." "index(%s).cursor(%s).delete(%s)", @@ -929,11 +720,11 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) requestSerial, IDB_PROFILER_STRING(mTransaction->Database()), IDB_PROFILER_STRING(mTransaction), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(mIndex), + IDB_PROFILER_STRING(objectStore), + IDB_PROFILER_STRING(mSourceIndex), IDB_PROFILER_STRING(mDirection), mObjectStore->HasValidKeyPath() ? "" : - IDB_PROFILER_STRING(objectKey)); + IDB_PROFILER_STRING(primaryKey)); } } #endif @@ -942,408 +733,118 @@ IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv) } void -IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv) +IDBCursor::Reset(Key&& aKey, StructuredCloneReadInfo&& aValue) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); + MOZ_ASSERT(mType == Type_ObjectStore); - if (aCount < 1) { - aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT); - return; - } + Reset(); - Key key; - ContinueInternal(key, int32_t(aCount), aRv); - ENSURE_SUCCESS_VOID(aRv); + mKey = Move(aKey); + mCloneInfo = Move(aValue); -#ifdef IDB_PROFILER_USE_MARKS - { - if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "cursor(%s).advance(%ld)", - "IDBRequest[%llu] MT IDBCursor.advance()", - Request()->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(mDirection), aCount); - } - else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "index(%s).cursor(%s).advance(%ld)", - "IDBRequest[%llu] MT IDBCursor.advance()", - Request()->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(mObjectStore), - IDB_PROFILER_STRING(mIndex), - IDB_PROFILER_STRING(mDirection), aCount); - } - } -#endif + mHaveValue = !mKey.IsUnset(); } void -CursorHelper::ReleaseMainThreadObjects() +IDBCursor::Reset(Key&& aKey) { - mCursor = nullptr; - AsyncConnectionHelper::ReleaseMainThreadObjects(); -} + AssertIsOnOwningThread(); + MOZ_ASSERT(mType == Type_ObjectStoreKey); -nsresult -CursorHelper::Dispatch(nsIEventTarget* aDatabaseThread) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + Reset(); - PROFILER_MAIN_THREAD_LABEL("CursorHelper", "Dispatch", - js::ProfileEntry::Category::STORAGE); + mKey = Move(aKey); - if (IndexedDatabaseManager::IsMainProcess()) { - return AsyncConnectionHelper::Dispatch(aDatabaseThread); - } - - // If we've been invalidated then there's no point sending anything to the - // parent process. - if (mCursor->Transaction()->Database()->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IndexedDBCursorChild* cursorActor = mCursor->GetActorChild(); - NS_ASSERTION(cursorActor, "Must have an actor here!"); - - CursorRequestParams params; - nsresult rv = PackArgumentsForParentProcess(params); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NoDispatchEventTarget target; - rv = AsyncConnectionHelper::Dispatch(&target); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mActor = new IndexedDBCursorRequestChild(this, mCursor, params.type()); - cursorActor->SendPIndexedDBRequestConstructor(mActor, params); - - return NS_OK; -} - -nsresult -ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("ContinueHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - // We need to pick a query based on whether or not the cursor's mContinueToKey - // is set. If it is unset then othing was passed to continue so we'll grab the - // next item in the database that is greater than (less than, if we're running - // a PREV cursor) the current key. If it is set then a key was passed to - // continue so we'll grab the next item in the database that is greater than - // (less than, if we're running a PREV cursor) or equal to the key that was - // specified. - - nsAutoCString query; - if (mCursor->mContinueToKey.IsUnset()) { - query.Assign(mCursor->mContinueQuery); - } - else { - query.Assign(mCursor->mContinueToQuery); - } - NS_ASSERTION(!query.IsEmpty(), "Bad query!"); - - query.AppendInt(mCount); - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = BindArgumentsToStatement(stmt); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(mCount > 0, "Not ok!"); - - bool hasResult; - for (int32_t index = 0; index < mCount; index++) { - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - break; - } - } - - if (hasResult) { - rv = GatherResultsFromStatement(stmt); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - mKey.Unset(); - } - - return NS_OK; -} - -nsresult -ContinueHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - UpdateCursorState(); - - if (mKey.IsUnset()) { - aVal.setNull(); - } - else { - nsresult rv = WrapNative(aCx, mCursor, aVal); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; + mHaveValue = !mKey.IsUnset(); } void -ContinueHelper::ReleaseMainThreadObjects() +IDBCursor::Reset(Key&& aKey, + Key&& aPrimaryKey, + StructuredCloneReadInfo&& aValue) { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - CursorHelper::ReleaseMainThreadObjects(); + AssertIsOnOwningThread(); + MOZ_ASSERT(mType == Type_Index); + + Reset(); + + mKey = Move(aKey); + mPrimaryKey = Move(aPrimaryKey); + mCloneInfo = Move(aValue); + + mHaveValue = !mKey.IsUnset(); } -nsresult -ContinueHelper::PackArgumentsForParentProcess(CursorRequestParams& aParams) +void +IDBCursor::Reset(Key&& aKey, Key&& aPrimaryKey) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mType == Type_IndexKey); - PROFILER_MAIN_THREAD_LABEL("ContinueHelper", "PackArgumentsForParentProcess", - js::ProfileEntry::Category::STORAGE); + Reset(); - ContinueParams params; + mKey = Move(aKey); + mPrimaryKey = Move(aPrimaryKey); - params.key() = mCursor->mContinueToKey; - params.count() = uint32_t(mCount); - - aParams = params; - return NS_OK; + mHaveValue = !mKey.IsUnset(); } -AsyncConnectionHelper::ChildProcessSendResult -ContinueHelper::SendResponseToChildProcess(nsresult aResultCode) +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceObjectStore) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceIndex) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) + MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined()); + MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey, + tmp->mCachedPrimaryKey.isUndefined()); + MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined()); + + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) + // Don't unlink mSourceObjectStore or mSourceIndex or mTransaction! + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->DropJSObjects(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +JSObject* +IDBCursor::WrapObject(JSContext* aCx) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + AssertIsOnOwningThread(); - PROFILER_MAIN_THREAD_LABEL("ContinueHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); + switch (mType) { + case Type_ObjectStore: + case Type_Index: + return IDBCursorWithValueBinding::Wrap(aCx, this); - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); + case Type_ObjectStoreKey: + case Type_IndexKey: + return IDBCursorBinding::Wrap(aCx, this); - InfallibleTArray blobsParent; - - if (NS_SUCCEEDED(aResultCode)) { - IDBDatabase* database = mTransaction->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - const nsTArray& files = mCloneReadInfo.mFiles; - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobsParent); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - } + default: + MOZ_CRASH("Bad type!"); } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - ContinueResponse continueResponse; - continueResponse.key() = mKey; - continueResponse.objectKey() = mObjectKey; - continueResponse.cloneInfo() = mCloneReadInfo; - continueResponse.blobsParent().SwapElements(blobsParent); - response = continueResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - UpdateCursorState(); - - return Success_Sent; } -nsresult -ContinueHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TContinueResponse, - "Bad response type!"); - - const ContinueResponse& response = aResponseValue.get_ContinueResponse(); - - mKey = response.key(); - mObjectKey = response.objectKey(); - - const SerializedStructuredCloneReadInfo& cloneInfo = response.cloneInfo(); - - NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || - (cloneInfo.dataLength && cloneInfo.data), - "Inconsistent clone info!"); - - if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(), - mCloneReadInfo.mFiles); - return NS_OK; -} - -nsresult -ContinueObjectStoreHelper::BindArgumentsToStatement( - mozIStorageStatement* aStatement) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aStatement); - - // Bind object store id. - nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mCursor->mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); - - // Bind current key. - const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? - mCursor->mKey : - mCursor->mContinueToKey; - - rv = currentKey.BindToStatement(aStatement, currentKeyName); - NS_ENSURE_SUCCESS(rv, rv); - - // Bind range key if it is specified. - const Key& rangeKey = mCursor->mRangeKey; - - if (!rangeKey.IsUnset()) { - rv = rangeKey.BindToStatement(aStatement, rangeKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -ContinueObjectStoreHelper::GatherResultsFromStatement( - mozIStorageStatement* aStatement) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aStatement); - - // Figure out what kind of key we have next. - nsresult rv = mKey.SetFromStatement(aStatement, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2, - mDatabase, - mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -ContinueObjectStoreKeyHelper::GatherResultsFromStatement( - mozIStorageStatement* aStatement) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(aStatement); - - nsresult rv = mKey.SetFromStatement(aStatement, 0); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement) -{ - // Bind index id. - nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mCursor->mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key"); - - // Bind current key. - const Key& currentKey = mCursor->mContinueToKey.IsUnset() ? - mCursor->mKey : - mCursor->mContinueToKey; - - rv = currentKey.BindToStatement(aStatement, currentKeyName); - NS_ENSURE_SUCCESS(rv, rv); - - // Bind range key if it is specified. - if (!mCursor->mRangeKey.IsUnset()) { - NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key"); - rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Bind object key if duplicates are allowed and we're not continuing to a - // specific key. - if ((mCursor->mDirection == IDBCursor::NEXT || - mCursor->mDirection == IDBCursor::PREV) && - mCursor->mContinueToKey.IsUnset()) { - NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!"); - - NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key"); - rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -ContinueIndexHelper::GatherResultsFromStatement( - mozIStorageStatement* aStatement) -{ - nsresult rv = mKey.SetFromStatement(aStatement, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mObjectKey.SetFromStatement(aStatement, 1); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -ContinueIndexObjectHelper::GatherResultsFromStatement( - mozIStorageStatement* aStatement) -{ - nsresult rv = mKey.SetFromStatement(aStatement, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mObjectKey.SetFromStatement(aStatement, 1); - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3, - mDatabase, mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h index 3e0b46431bd1..3d2f52299ef4 100644 --- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -7,59 +7,38 @@ #ifndef mozilla_dom_indexeddb_idbcursor_h__ #define mozilla_dom_indexeddb_idbcursor_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - +#include "IndexedDatabase.h" +#include "js/RootingAPI.h" #include "mozilla/Attributes.h" #include "mozilla/dom/IDBCursorBinding.h" -#include "mozilla/ErrorResult.h" +#include "mozilla/dom/indexedDB/Key.h" +#include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsWrapperCache.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/Key.h" - -class nsIRunnable; -class nsIScriptContext; class nsPIDOMWindow; namespace mozilla { + +class ErrorResult; + namespace dom { + class OwningIDBObjectStoreOrIDBIndex; -} -} -BEGIN_INDEXEDDB_NAMESPACE +namespace indexedDB { -class ContinueHelper; -class ContinueObjectStoreHelper; -class ContinueIndexHelper; -class ContinueIndexObjectHelper; +class BackgroundCursorChild; class IDBIndex; +class IDBObjectStore; class IDBRequest; class IDBTransaction; -class IndexedDBCursorChild; -class IndexedDBCursorParent; -class IDBCursor MOZ_FINAL : public nsISupports, - public nsWrapperCache +class IDBCursor MOZ_FINAL + : public nsISupports + , public nsWrapperCache { - friend class ContinueHelper; - friend class ContinueObjectStoreHelper; - friend class ContinueIndexHelper; - friend class ContinueIndexObjectHelper; - public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor) - - enum Type - { - OBJECTSTORE = 0, - OBJECTSTOREKEY, - INDEXKEY, - INDEXOBJECT - }; - enum Direction { NEXT = 0, @@ -71,112 +50,84 @@ public: DIRECTION_INVALID }; - // For OBJECTSTORE cursors. - static - already_AddRefed - Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, - Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, - const Key& aKey, - StructuredCloneReadInfo&& aCloneReadInfo); +private: + enum Type + { + Type_ObjectStore, + Type_ObjectStoreKey, + Type_Index, + Type_IndexKey, + }; - // For OBJECTSTOREKEY cursors. - static - already_AddRefed - Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, + nsRefPtr mSourceObjectStore; + nsRefPtr mSourceIndex; + nsRefPtr mTransaction; + + BackgroundCursorChild* mBackgroundActor; + + JS::Heap mScriptOwner; + + // These are cycle-collected! + JS::Heap mCachedKey; + JS::Heap mCachedPrimaryKey; + JS::Heap mCachedValue; + + Key mKey; + Key mPrimaryKey; + StructuredCloneReadInfo mCloneInfo; + + const Type mType; + const Direction mDirection; + + bool mHaveCachedKey : 1; + bool mHaveCachedPrimaryKey : 1; + bool mHaveCachedValue : 1; + bool mRooted : 1; + bool mContinueCalled : 1; + bool mHaveValue : 1; + +public: + static already_AddRefed + Create(IDBObjectStore* aObjectStore, + BackgroundCursorChild* aBackgroundActor, + Direction aDirection, + const Key& aKey, + StructuredCloneReadInfo&& aCloneInfo); + + static already_AddRefed + Create(IDBObjectStore* aObjectStore, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, const Key& aKey); - // For INDEXKEY cursors. - static - already_AddRefed - Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBIndex* aIndex, + static already_AddRefed + Create(IDBIndex* aIndex, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, const Key& aKey, - const Key& aObjectKey); + const Key& aPrimaryKey, + StructuredCloneReadInfo&& aCloneInfo); - // For INDEXOBJECT cursors. - static - already_AddRefed - Create(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBIndex* aIndex, + static already_AddRefed + Create(IDBIndex* aIndex, + BackgroundCursorChild* aBackgroundActor, Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery, const Key& aKey, - const Key& aObjectKey, - StructuredCloneReadInfo&& aCloneReadInfo); - - IDBTransaction* Transaction() const - { - return mTransaction; - } - - IDBRequest* Request() const - { - return mRequest; - } + const Key& aPrimaryKey); static Direction ConvertDirection(IDBCursorDirection aDirection); void - SetActor(IndexedDBCursorChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif - void - SetActor(IndexedDBCursorParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBCursorChild* - GetActorChild() const - { - return mActorChild; - } - - IndexedDBCursorParent* - GetActorParent() const - { - return mActorParent; - } - - void - ContinueInternal(const Key& aKey, int32_t aCount, - ErrorResult& aRv); - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - IDBTransaction* - GetParentObject() const - { - return mTransaction; - } + nsPIDOMWindow* + GetParentObject() const; void GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const; @@ -185,80 +136,79 @@ public: GetDirection() const; void - GetKey(JSContext* aCx, JS::MutableHandle aResult, + GetKey(JSContext* aCx, + JS::MutableHandle aResult, ErrorResult& aRv); void - GetPrimaryKey(JSContext* aCx, JS::MutableHandle aResult, + GetPrimaryKey(JSContext* aCx, + JS::MutableHandle aResult, ErrorResult& aRv); - already_AddRefed - Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv); + void + GetValue(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv); + + void + Continue(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); void Advance(uint32_t aCount, ErrorResult& aRv); - void - Continue(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + already_AddRefed + Update(JSContext* aCx, JS::Handle aValue, ErrorResult& aRv); already_AddRefed Delete(JSContext* aCx, ErrorResult& aRv); void - GetValue(JSContext* aCx, JS::MutableHandle aResult, - ErrorResult& aRv); + Reset(); + + void + Reset(Key&& aKey, StructuredCloneReadInfo&& aValue); + + void + Reset(Key&& aKey); + + void + Reset(Key&& aKey, Key&& aPrimaryKey, StructuredCloneReadInfo&& aValue); + + void + Reset(Key&& aKey, Key&& aPrimaryKey); + + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor) + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + +private: + IDBCursor(Type aType, + IDBObjectStore* aSourceObjectStore, + IDBIndex* aSourceIndex, + IDBTransaction* aTransaction, + BackgroundCursorChild* aBackgroundActor, + Direction aDirection, + const Key& aKey); -protected: - IDBCursor(); ~IDBCursor(); - void DropJSObjects(); - - static - already_AddRefed - CreateCommon(IDBRequest* aRequest, - IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, - Direction aDirection, - const Key& aRangeKey, - const nsACString& aContinueQuery, - const nsACString& aContinueToQuery); - - nsRefPtr mRequest; - nsRefPtr mTransaction; - nsRefPtr mObjectStore; - nsRefPtr mIndex; - - JS::Heap mScriptOwner; - - Type mType; - Direction mDirection; - nsCString mContinueQuery; - nsCString mContinueToQuery; - - // These are cycle-collected! - JS::Heap mCachedKey; - JS::Heap mCachedPrimaryKey; - JS::Heap mCachedValue; - - Key mRangeKey; - - Key mKey; - Key mObjectKey; - StructuredCloneReadInfo mCloneReadInfo; - Key mContinueToKey; - - IndexedDBCursorChild* mActorChild; - IndexedDBCursorParent* mActorParent; - - bool mHaveCachedKey; - bool mHaveCachedPrimaryKey; - bool mHaveCachedValue; - bool mRooted; - bool mContinueCalled; - bool mHaveValue; + void + DropJSObjects(); }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbcursor_h__ diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 7fb922b8d486..768445f0b141 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -4,464 +4,507 @@ * 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 "base/basictypes.h" - #include "IDBDatabase.h" -#include "mozilla/EventDispatcher.h" -#include "mozilla/Mutex.h" -#include "mozilla/storage.h" -#include "mozilla/dom/nsIContentParent.h" -#include "mozilla/dom/DOMStringList.h" -#include "mozilla/dom/DOMStringListBinding.h" -#include "mozilla/dom/quota/Client.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "nsJSUtils.h" -#include "nsProxyRelease.h" -#include "nsThreadUtils.h" - -#include "AsyncConnectionHelper.h" -#include "DatabaseInfo.h" +#include "FileInfo.h" +#include "FileManager.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBIndex.h" #include "IDBMutableFile.h" #include "IDBObjectStore.h" +#include "IDBRequest.h" #include "IDBTransaction.h" #include "IDBFactory.h" +#include "IndexedDatabaseManager.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/EventDispatcher.h" +#include "MainThreadUtils.h" +#include "mozilla/Services.h" +#include "mozilla/storage.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/dom/DOMStringListBinding.h" +#include "mozilla/dom/IDBDatabaseBinding.h" +#include "mozilla/dom/IDBObjectStoreBinding.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/nsIRemoteBlob.h" +#include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/ipc/InputStreamParams.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "nsCOMPtr.h" +#include "nsDOMFile.h" +#include "nsIDocument.h" +#include "nsIDOMFile.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsISupportsPrimitives.h" +#include "nsThreadUtils.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "TransactionThreadPool.h" -#include "ipc/IndexedDBChild.h" -#include "ipc/IndexedDBParent.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#include "mozilla/dom/IDBDatabaseBinding.h" +namespace mozilla { +namespace dom { +namespace indexedDB { -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::nsIContentParent; -using mozilla::dom::quota::AssertIsOnIOThread; -using mozilla::dom::quota::Client; -using mozilla::dom::quota::QuotaManager; -using mozilla::ErrorResult; -using namespace mozilla; -using namespace mozilla::dom; +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; +using namespace mozilla::services; namespace { -class NoRequestDatabaseHelper : public AsyncConnectionHelper +const char kCycleCollectionObserverTopic[] = "cycle-collector-end"; +const char kMemoryPressureObserverTopic[] = "memory-pressure"; +const char kWindowObserverTopic[] = "inner-window-destroyed"; + +// XXX This should either be ported to PBackground or removed someday. +class CreateFileHelper MOZ_FINAL + : public nsRunnable { + nsRefPtr mDatabase; + nsRefPtr mRequest; + nsRefPtr mFileInfo; + + const nsString mName; + const nsString mType; + const nsString mDatabaseName; + const nsCString mOrigin; + + const PersistenceType mPersistenceType; + + nsresult mResultCode; + public: - explicit NoRequestDatabaseHelper(IDBTransaction* aTransaction) - : AsyncConnectionHelper(aTransaction, nullptr) - { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aTransaction, "Null transaction!"); - } + static nsresult + CreateAndDispatch(IDBDatabase* aDatabase, + IDBRequest* aRequest, + const nsAString& aName, + const nsAString& aType); - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - - virtual nsresult OnSuccess() MOZ_OVERRIDE; - - virtual void OnError() MOZ_OVERRIDE; -}; - -class CreateObjectStoreHelper : public NoRequestDatabaseHelper -{ -public: - CreateObjectStoreHelper(IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore) - : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; + NS_DECL_ISUPPORTS_INHERITED private: - nsRefPtr mObjectStore; -}; - -class DeleteObjectStoreHelper : public NoRequestDatabaseHelper -{ -public: - DeleteObjectStoreHelper(IDBTransaction* aTransaction, - int64_t aObjectStoreId) - : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - -private: - // In-params. - int64_t mObjectStoreId; -}; - -class CreateFileHelper : public AsyncConnectionHelper -{ -public: CreateFileHelper(IDBDatabase* aDatabase, IDBRequest* aRequest, const nsAString& aName, - const nsAString& aType) - : AsyncConnectionHelper(aDatabase, aRequest), - mName(aName), mType(aType) - { } + const nsAString& aType, + const nsACString& aOrigin); ~CreateFileHelper() - { } - - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); - nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal); - void ReleaseMainThreadObjects() { - mFileInfo = nullptr; - AsyncConnectionHelper::ReleaseMainThreadObjects(); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); } - virtual ChildProcessSendResult SendResponseToChildProcess( - nsresult aResultCode) - MOZ_OVERRIDE - { - return Success_NotSent; - } + nsresult + DoDatabaseWork(); - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE - { - MOZ_CRASH("Should never get here!"); - } + void + DoMainThreadWork(nsresult aResultCode); -private: - // In-params. - nsString mName; - nsString mType; - - // Out-params. - nsRefPtr mFileInfo; + NS_DECL_NSIRUNNABLE }; -class MOZ_STACK_CLASS AutoRemoveObjectStore +class DatabaseFile MOZ_FINAL + : public PBackgroundIDBDatabaseFileChild { + IDBDatabase* mDatabase; + public: - AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName) - : mInfo(aInfo), mName(aName) - { } - - ~AutoRemoveObjectStore() + DatabaseFile(IDBDatabase* aDatabase) + : mDatabase(aDatabase) { - if (mInfo) { - mInfo->RemoveObjectStore(mName); - } - } + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); - void forget() - { - mInfo = nullptr; + MOZ_COUNT_CTOR(DatabaseFile); } private: - DatabaseInfo* mInfo; - nsString mName; + ~DatabaseFile() + { + MOZ_ASSERT(!mDatabase); + + MOZ_COUNT_DTOR(DatabaseFile); + } + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE + { + MOZ_ASSERT(mDatabase); + mDatabase->AssertIsOnOwningThread(); + + if (aWhy != Deletion) { + nsRefPtr database = mDatabase; + database->NoteFinishedFileActor(this); + } + +#ifdef DEBUG + mDatabase = nullptr; +#endif + } }; } // anonymous namespace +class IDBDatabase::Observer MOZ_FINAL + : public nsIObserver +{ + IDBDatabase* mWeakDatabase; + const uint64_t mWindowId; + +public: + Observer(IDBDatabase* aDatabase, uint64_t aWindowId) + : mWeakDatabase(aDatabase) + , mWindowId(aWindowId) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDatabase); + } + + void + Revoke() + { + MOZ_ASSERT(NS_IsMainThread()); + + mWeakDatabase = nullptr; + } + + NS_DECL_ISUPPORTS + +private: + ~Observer() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mWeakDatabase); + } + + NS_DECL_NSIOBSERVER +}; + +IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache, + IDBFactory* aFactory, + BackgroundDatabaseChild* aActor, + DatabaseSpec* aSpec) + : IDBWrapperCache(aOwnerCache) + , mFactory(aFactory) + , mSpec(aSpec) + , mBackgroundActor(aActor) + , mClosed(false) + , mInvalidated(false) +{ + MOZ_ASSERT(aOwnerCache); + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aSpec); + + SetIsDOMBinding(); +} + +IDBDatabase::~IDBDatabase() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mBackgroundActor); +} + // static already_AddRefed IDBDatabase::Create(IDBWrapperCache* aOwnerCache, IDBFactory* aFactory, - already_AddRefed aDatabaseInfo, - const nsACString& aASCIIOrigin, - FileManager* aFileManager, - mozilla::dom::nsIContentParent* aContentParent) + BackgroundDatabaseChild* aActor, + DatabaseSpec* aSpec) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aFactory, "Null pointer!"); - NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!"); + MOZ_ASSERT(aOwnerCache); + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aSpec); - nsRefPtr databaseInfo(aDatabaseInfo); - NS_ASSERTION(databaseInfo, "Null pointer!"); - - nsRefPtr db(new IDBDatabase(aOwnerCache)); + nsRefPtr db = + new IDBDatabase(aOwnerCache, aFactory, aActor, aSpec); db->SetScriptOwner(aOwnerCache->GetScriptOwner()); - db->mFactory = aFactory; - db->mDatabaseId = databaseInfo->id; - db->mName = databaseInfo->name; - db->mFilePath = databaseInfo->filePath; - db->mPersistenceType = databaseInfo->persistenceType; - db->mGroup = databaseInfo->group; - databaseInfo.swap(db->mDatabaseInfo); - db->mASCIIOrigin = aASCIIOrigin; - db->mFileManager = aFileManager; - db->mContentParent = aContentParent; - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); + if (NS_IsMainThread()) { + if (nsPIDOMWindow* window = aFactory->GetParentObject()) { + MOZ_ASSERT(window->IsInnerWindow()); - db->mQuotaClient = quotaManager->GetClient(Client::IDB); - NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!"); + uint64_t windowId = window->WindowID(); - if (!quotaManager->RegisterStorage(db)) { - // Either out of memory or shutting down. - return nullptr; + nsRefPtr observer = new Observer(db, windowId); + + nsCOMPtr obsSvc = GetObserverService(); + MOZ_ASSERT(obsSvc); + + // This topic must be successfully registered. + if (NS_WARN_IF(NS_FAILED( + obsSvc->AddObserver(observer, kWindowObserverTopic, false)))) { + observer->Revoke(); + return nullptr; + } + + // These topics are not crucial. + if (NS_FAILED(obsSvc->AddObserver(observer, + kCycleCollectionObserverTopic, + false)) || + NS_FAILED(obsSvc->AddObserver(observer, + kMemoryPressureObserverTopic, + false))) { + NS_WARNING("Failed to add additional memory observers!"); + } + + db->mObserver.swap(observer); + } } - db->mRegistered = true; - return db.forget(); } -// static -IDBDatabase* -IDBDatabase::FromStorage(nsIOfflineStorage* aStorage) -{ - return aStorage->GetClient()->GetType() == Client::IDB ? - static_cast(aStorage) : nullptr; -} - -IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache) -: IDBWrapperCache(aOwnerCache), - mActorChild(nullptr), - mActorParent(nullptr), - mContentParent(nullptr), - mInvalidated(false), - mRegistered(false), - mClosed(false), - mRunningVersionChange(false) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} - -IDBDatabase::~IDBDatabase() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} +#ifdef DEBUG void -IDBDatabase::LastRelease() +IDBDatabase::AssertIsOnOwningThread() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); - } - - if (mRegistered) { - CloseInternal(true); - - QuotaManager* quotaManager = QuotaManager::Get(); - if (quotaManager) { - quotaManager->UnregisterStorage(this); - } - mRegistered = false; - } + MOZ_ASSERT(mFactory); + mFactory->AssertIsOnOwningThread(); } -NS_IMETHODIMP_(void) -IDBDatabase::Invalidate() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - InvalidateInternal(/* aIsDead */ false); -} +#endif // DEBUG void -IDBDatabase::InvalidateInternal(bool aIsDead) +IDBDatabase::CloseInternal() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (IsInvalidated()) { - return; - } - - mInvalidated = true; - - // Make sure we're closed too. - Close(); - - // When the IndexedDatabaseManager needs to invalidate databases, all it has - // is an origin, so we call into the quota manager here to cancel any prompts - // for our owner. - nsPIDOMWindow* owner = GetOwner(); - if (owner) { - QuotaManager::CancelPromptsForWindow(owner); - } - - // We want to forcefully remove in the child when the parent has invalidated - // us in IPC mode because the database might no longer exist. - // We don't want to forcefully remove in the parent when a child dies since - // other child processes may be using the referenced DatabaseInfo. - if (!aIsDead) { - DatabaseInfo::Remove(mDatabaseId); - } - - // And let the child process know as well. - if (mActorParent) { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorParent->Invalidate(); - } -} - -void -IDBDatabase::DisconnectFromActorParent() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Make sure we're closed too. - Close(); - - // Kill any outstanding prompts. - nsPIDOMWindow* owner = GetOwner(); - if (owner) { - QuotaManager::CancelPromptsForWindow(owner); - } -} - -void -IDBDatabase::CloseInternal(bool aIsDead) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mClosed) { mClosed = true; - // If we're getting called from Unlink, avoid cloning the DatabaseInfo. - { - nsRefPtr previousInfo; - mDatabaseInfo.swap(previousInfo); + ExpireFileActors(/* aExpireAll */ true); - if (!aIsDead) { - mDatabaseInfo = previousInfo->Clone(); + if (mObserver) { + mObserver->Revoke(); + + nsCOMPtr obsSvc = GetObserverService(); + if (obsSvc) { + // These might not have been registered. + obsSvc->RemoveObserver(mObserver, kCycleCollectionObserverTopic); + obsSvc->RemoveObserver(mObserver, kMemoryPressureObserverTopic); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + obsSvc->RemoveObserver(mObserver, kWindowObserverTopic))); } + + mObserver = nullptr; } - QuotaManager* quotaManager = QuotaManager::Get(); - if (quotaManager) { - quotaManager->OnStorageClosed(this); - } - - // And let the parent process know as well. - if (mActorChild && !IsInvalidated()) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->SendClose(aIsDead); + if (mBackgroundActor && !mInvalidated) { + mBackgroundActor->SendClose(); } } } -NS_IMETHODIMP_(bool) -IDBDatabase::IsClosed() +void +IDBDatabase::InvalidateInternal() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return mClosed; + AssertIsOnOwningThread(); + + InvalidateMutableFiles(); + AbortTransactions(); + + CloseInternal(); } void -IDBDatabase::EnterSetVersionTransaction() +IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion) { - NS_ASSERTION(!mRunningVersionChange, "How did that happen?"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aNewVersion); + MOZ_ASSERT(!RunningVersionChangeTransaction()); + MOZ_ASSERT(mSpec); + MOZ_ASSERT(!mPreviousSpec); - mPreviousDatabaseInfo = mDatabaseInfo->Clone(); + mPreviousSpec = new DatabaseSpec(*mSpec); - mRunningVersionChange = true; + mSpec->metadata().version() = aNewVersion; } void IDBDatabase::ExitSetVersionTransaction() { - NS_ASSERTION(mRunningVersionChange, "How did that happen?"); + AssertIsOnOwningThread(); - mPreviousDatabaseInfo = nullptr; - - mRunningVersionChange = false; + if (mPreviousSpec) { + mPreviousSpec = nullptr; + } } void IDBDatabase::RevertToPreviousState() { - mDatabaseInfo = mPreviousDatabaseInfo; - mPreviousDatabaseInfo = nullptr; + AssertIsOnOwningThread(); + MOZ_ASSERT(RunningVersionChangeTransaction()); + MOZ_ASSERT(mPreviousSpec); + + // Hold the current spec alive until RefreshTransactionsSpecEnumerator has + // finished! + nsAutoPtr currentSpec(mSpec.forget()); + + mSpec = mPreviousSpec.forget(); + + RefreshSpec(/* aMayDelete */ true); } void -IDBDatabase::OnUnlink() +IDBDatabase::RefreshSpec(bool aMayDelete) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - // We've been unlinked, at the very least we should be able to prevent further - // transactions from starting and unblock any other SetVersion callers. - CloseInternal(true); + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static PLDHashOperator + RefreshTransactionsSpec(nsPtrHashKey* aTransaction, + void* aClosure) + { + MOZ_ASSERT(aTransaction); + aTransaction->GetKey()->AssertIsOnOwningThread(); + MOZ_ASSERT(aClosure); - // No reason for the QuotaManager to track us any longer. - QuotaManager* quotaManager = QuotaManager::Get(); - if (mRegistered && quotaManager) { - quotaManager->UnregisterStorage(this); + bool mayDelete = *static_cast(aClosure); - // Don't try to unregister again in the destructor. - mRegistered = false; + nsRefPtr transaction = aTransaction->GetKey(); + transaction->RefreshSpec(mayDelete); + + return PL_DHASH_NEXT; + } + }; + + mTransactions.EnumerateEntries(Helper::RefreshTransactionsSpec, &aMayDelete); +} + +nsPIDOMWindow* +IDBDatabase::GetParentObject() const +{ + return mFactory->GetParentObject(); +} + +const nsString& +IDBDatabase::Name() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + + return mSpec->metadata().name(); +} + +uint64_t +IDBDatabase::Version() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + + return mSpec->metadata().version(); +} + +already_AddRefed +IDBDatabase::ObjectStoreNames() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + + const nsTArray& objectStores = mSpec->objectStores(); + + nsRefPtr list = new DOMStringList(); + + if (!objectStores.IsEmpty()) { + nsTArray& listNames = list->StringArray(); + listNames.SetCapacity(objectStores.Length()); + + for (uint32_t index = 0; index < objectStores.Length(); index++) { + listNames.InsertElementSorted(objectStores[index].metadata().name()); + } } + + return list.forget(); +} + +already_AddRefed +IDBDatabase::GetOwnerDocument() const +{ + if (nsPIDOMWindow* window = GetOwner()) { + nsCOMPtr doc = window->GetExtantDoc(); + return doc.forget(); + } + return nullptr; } already_AddRefed -IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction, - const ObjectStoreInfoGuts& aInfo, - ErrorResult& aRv) +IDBDatabase::CreateObjectStore( + JSContext* aCx, + const nsAString& aName, + const IDBObjectStoreParameters& aOptionalParameters, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null transaction!"); + AssertIsOnOwningThread(); - DatabaseInfo* databaseInfo = aTransaction->DBInfo(); + IDBTransaction* transaction = IDBTransaction::GetCurrent(); - nsRefPtr newInfo = new ObjectStoreInfo(); - *static_cast(newInfo.get()) = aInfo; - - newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0; - newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId; - - if (!databaseInfo->PutObjectStore(newInfo)) { - IDB_WARNING("Put failed!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (!transaction || + transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } - // Don't leave this in the hash if we fail below! - AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name); + MOZ_ASSERT(transaction->IsOpen()); - nsRefPtr objectStore = - aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true); - if (!objectStore) { - IDB_WARNING("Failed to get objectStore!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + KeyPath keyPath(0); + if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } - if (IndexedDatabaseManager::IsMainProcess()) { - nsRefPtr helper = - new CreateObjectStoreHelper(aTransaction, objectStore); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsTArray& objectStores = mSpec->objectStores(); + for (uint32_t count = objectStores.Length(), index = 0; + index < count; + index++) { + if (aName == objectStores[index].metadata().name()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); return nullptr; } } - autoRemove.forget(); + if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; + } + + const ObjectStoreSpec* oldSpecElements = + objectStores.IsEmpty() ? nullptr : objectStores.Elements(); + + ObjectStoreSpec* newSpec = objectStores.AppendElement(); + newSpec->metadata() = + ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName), + keyPath, aOptionalParameters.mAutoIncrement); + + if (oldSpecElements && + oldSpecElements != objectStores.Elements()) { + MOZ_ASSERT(objectStores.Length() > 1); + + // Array got moved, update the spec pointers for all live objectStores and + // indexes. + RefreshSpec(/* aMayDelete */ false); + } + + nsRefPtr objectStore = + transaction->CreateObjectStore(*newSpec); + MOZ_ASSERT(objectStore); IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).createObjectStore(%s)", @@ -473,107 +516,12 @@ IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction, return objectStore.forget(); } -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - // Don't unlink mFactory! - - // Do some cleanup. - tmp->OnUnlink(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) - NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) - -NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) - -JSObject* -IDBDatabase::WrapObject(JSContext* aCx) -{ - return IDBDatabaseBinding::Wrap(aCx, this); -} - -uint64_t -IDBDatabase::Version() const -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - DatabaseInfo* info = Info(); - return info->version; -} - -already_AddRefed -IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - DatabaseInfo* info = Info(); - - nsRefPtr list(new DOMStringList()); - if (!info->GetObjectStoreNames(list->StringArray())) { - IDB_WARNING("Couldn't get names!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - return list.forget(); -} - -already_AddRefed -IDBDatabase::CreateObjectStore( - JSContext* aCx, const nsAString& aName, - const IDBObjectStoreParameters& aOptionalParameters, - ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); - - if (!transaction || - transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return nullptr; - } - - DatabaseInfo* databaseInfo = transaction->DBInfo(); - - KeyPath keyPath(0); - if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return nullptr; - } - - if (databaseInfo->ContainsStoreName(aName)) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); - return nullptr; - } - - if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) { - aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); - return nullptr; - } - - ObjectStoreInfoGuts guts; - - guts.name = aName; - guts.id = databaseInfo->nextObjectStoreId++; - guts.keyPath = keyPath; - guts.autoIncrement = aOptionalParameters.mAutoIncrement; - - return CreateObjectStoreInternal(transaction, guts, aRv); -} - void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); + IDBTransaction* transaction = IDBTransaction::GetCurrent(); if (!transaction || transaction->GetMode() != IDBTransaction::VERSION_CHANGE) { @@ -581,33 +529,36 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) return; } - DatabaseInfo* info = transaction->DBInfo(); - ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName); - if (!objectStoreInfo) { + MOZ_ASSERT(transaction->IsOpen()); + + nsTArray& specArray = mSpec->objectStores(); + + int64_t objectStoreId = 0; + + for (uint32_t specCount = specArray.Length(), specIndex = 0; + specIndex < specCount; + specIndex++) { + const ObjectStoreMetadata& metadata = specArray[specIndex].metadata(); + MOZ_ASSERT(metadata.id()); + + if (aName == metadata.name()) { + objectStoreId = metadata.id(); + + // Must do this before altering the metadata array! + transaction->DeleteObjectStore(objectStoreId); + + specArray.RemoveElementAt(specIndex); + + RefreshSpec(/* aMayDelete */ false); + break; + } + } + + if (!objectStoreId) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return; } - if (IndexedDatabaseManager::IsMainProcess()) { - nsRefPtr helper = - new DeleteObjectStoreHelper(transaction, objectStoreInfo->id); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return; - } - } - else { - IndexedDBTransactionChild* actor = transaction->GetActorChild(); - NS_ASSERTION(actor, "Must have an actor here!"); - - actor->SendDeleteObjectStore(nsString(aName)); - } - - transaction->RemoveObjectStore(aName); - IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).deleteObjectStore(\"%s\")", "MT IDBDatabase.deleteObjectStore()", @@ -616,11 +567,28 @@ IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) NS_ConvertUTF16toUTF8(aName).get()); } -already_AddRefed -IDBDatabase::Transaction(const Sequence& aStoreNames, - IDBTransactionMode aMode, ErrorResult& aRv) +already_AddRefed +IDBDatabase::Transaction(const nsAString& aStoreName, + IDBTransactionMode aMode, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + + Sequence storeNames; + if (!storeNames.AppendElement(aStoreName)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + + return Transaction(storeNames, aMode, aRv); +} + +already_AddRefed +IDBDatabase::Transaction(const Sequence& aStoreNames, + IDBTransactionMode aMode, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); if (QuotaManager::IsShuttingDown()) { IDB_REPORT_INTERNAL_ERR(); @@ -633,7 +601,7 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return nullptr; } - if (mRunningVersionChange) { + if (RunningVersionChangeTransaction()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); return nullptr; } @@ -643,37 +611,70 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return nullptr; } - IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY; + IDBTransaction::Mode mode; switch (aMode) { case IDBTransactionMode::Readonly: - transactionMode = IDBTransaction::READ_ONLY; + mode = IDBTransaction::READ_ONLY; break; case IDBTransactionMode::Readwrite: - transactionMode = IDBTransaction::READ_WRITE; + mode = IDBTransaction::READ_WRITE; break; case IDBTransactionMode::Versionchange: - transactionMode = IDBTransaction::VERSION_CHANGE; - break; + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return nullptr; default: MOZ_CRASH("Unknown mode!"); } - // Now check to make sure the object store names we collected actually exist. - DatabaseInfo* info = Info(); - for (uint32_t index = 0; index < aStoreNames.Length(); index++) { - if (!info->ContainsStoreName(aStoreNames[index])) { + const nsTArray& objectStores = mSpec->objectStores(); + const uint32_t nameCount = aStoreNames.Length(); + + nsTArray sortedStoreNames; + sortedStoreNames.SetCapacity(nameCount); + + // Check to make sure the object store names we collected actually exist. + for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) { + const nsString& name = aStoreNames[nameIndex]; + + bool found = false; + + for (uint32_t objCount = objectStores.Length(), objIndex = 0; + objIndex < objCount; + objIndex++) { + if (objectStores[objIndex].metadata().name() == name) { + found = true; + break; + } + } + + if (!found) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return nullptr; } + + sortedStoreNames.InsertElementSorted(name); + } + + // Remove any duplicates. + for (uint32_t nameIndex = nameCount - 1; nameIndex > 0; nameIndex--) { + if (sortedStoreNames[nameIndex] == sortedStoreNames[nameIndex - 1]) { + sortedStoreNames.RemoveElementAt(nameIndex); + } } nsRefPtr transaction = - IDBTransaction::Create(this, aStoreNames, transactionMode, false); - if (!transaction) { - IDB_WARNING("Failed to create the transaction!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + IDBTransaction::Create(this, sortedStoreNames, mode); + MOZ_ASSERT(transaction); + + BackgroundTransactionChild* actor = + new BackgroundTransactionChild(transaction); + + MOZ_ALWAYS_TRUE( + mBackgroundActor->SendPBackgroundIDBTransactionConstructor(actor, + sortedStoreNames, + mode)); + + transaction->SetBackgroundActor(actor); IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)", "IDBTransaction[%llu] MT Started", @@ -683,15 +684,22 @@ IDBDatabase::Transaction(const Sequence& aStoreNames, return transaction.forget(); } +StorageType +IDBDatabase::Storage() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + + return PersistenceTypeToStorage(mSpec->metadata().persistenceType()); +} + already_AddRefed IDBDatabase::CreateMutableFile(const nsAString& aName, const Optional& aType, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!IndexedDatabaseManager::IsMainProcess()) { - IDB_WARNING("Not supported yet!"); + if (!IndexedDatabaseManager::IsMainProcess() || !NS_IsMainThread()) { + IDB_WARNING("Not supported!"); aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; } @@ -709,57 +717,474 @@ IDBDatabase::CreateMutableFile(const nsAString& aName, nsRefPtr request = IDBRequest::Create(this, nullptr); - nsRefPtr helper = - new CreateFileHelper(this, request, aName, - aType.WasPassed() ? aType.Value() : EmptyString()); + nsString type; + if (aType.WasPassed()) { + type = aType.Value(); + } - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "We should definitely have a manager here"); - - nsresult rv = helper->Dispatch(quotaManager->IOThread()); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + aRv = CreateFileHelper::CreateAndDispatch(this, request, aName, type); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } return request.forget(); } -NS_IMETHODIMP -IDBDatabase::Close() +void +IDBDatabase::RegisterTransaction(IDBTransaction* aTransaction) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(!mTransactions.Contains(aTransaction)); - CloseInternal(false); - - NS_ASSERTION(mClosed, "Should have set the closed flag!"); - - return NS_OK; + mTransactions.PutEntry(aTransaction); } -NS_IMETHODIMP_(const nsACString&) -IDBDatabase::Id() +void +IDBDatabase::UnregisterTransaction(IDBTransaction* aTransaction) { - return mDatabaseId; + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(mTransactions.Contains(aTransaction)); + + mTransactions.RemoveEntry(aTransaction); } -NS_IMETHODIMP_(mozilla::dom::quota::Client*) -IDBDatabase::GetClient() +void +IDBDatabase::AbortTransactions() { - return mQuotaClient; + AssertIsOnOwningThread(); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static void + AbortTransactions(nsTHashtable>& aTable) + { + const uint32_t count = aTable.Count(); + if (!count) { + return; + } + + nsTArray> transactions; + transactions.SetCapacity(count); + + aTable.EnumerateEntries(Collect, &transactions); + + MOZ_ASSERT(transactions.Length() == count); + + IDB_REPORT_INTERNAL_ERR(); + + for (uint32_t index = 0; index < count; index++) { + nsRefPtr transaction = transactions[index].forget(); + MOZ_ASSERT(transaction); + + transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } + } + + private: + static PLDHashOperator + Collect(nsPtrHashKey* aTransaction, void* aClosure) + { + MOZ_ASSERT(aTransaction); + aTransaction->GetKey()->AssertIsOnOwningThread(); + MOZ_ASSERT(aClosure); + + auto* array = static_cast>*>(aClosure); + array->AppendElement(aTransaction->GetKey()); + + return PL_DHASH_NEXT; + } + }; + + Helper::AbortTransactions(mTransactions); } -NS_IMETHODIMP_(bool) -IDBDatabase::IsOwned(nsPIDOMWindow* aOwner) +PBackgroundIDBDatabaseFileChild* +IDBDatabase::GetOrCreateFileActorForBlob(nsIDOMBlob* aBlob) { - return GetOwner() == aOwner; + AssertIsOnOwningThread(); + MOZ_ASSERT(aBlob); + MOZ_ASSERT(mBackgroundActor); + + // We use the DOMFile's nsIWeakReference as the key to the table because + // a) it is unique per blob, b) it is reference-counted so that we can + // guarantee that it stays alive, and c) it doesn't hold the actual DOMFile + // alive. + nsCOMPtr weakRef = do_GetWeakReference(aBlob); + MOZ_ASSERT(weakRef); + + PBackgroundIDBDatabaseFileChild* actor = nullptr; + + if (!mFileActors.Get(weakRef, &actor)) { + DOMFileImpl* blobImpl = static_cast(aBlob)->Impl(); + MOZ_ASSERT(blobImpl); + + if (mReceivedBlobs.GetEntry(weakRef)) { + // This blob was previously retrieved from the database. + nsCOMPtr remoteBlob = do_QueryObject(blobImpl); + MOZ_ASSERT(remoteBlob); + + BlobChild* blobChild = remoteBlob->GetBlobChild(); + MOZ_ASSERT(blobChild); + +#ifdef DEBUG + { + PBackgroundChild* backgroundManager = blobChild->GetBackgroundManager(); + MOZ_ASSERT(backgroundManager); + + PBackgroundChild* thisManager = mBackgroundActor->Manager()->Manager(); + MOZ_ASSERT(thisManager); + + MOZ_ASSERT(thisManager == backgroundManager); + } +#endif + auto* dbFile = new DatabaseFile(this); + + actor = + mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile, + blobChild); + if (NS_WARN_IF(!actor)) { + return nullptr; + } + } else { + nsCOMPtr inputStream; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + blobImpl->GetInternalStream(getter_AddRefs(inputStream)))); + + InputStreamParams params; + nsTArray fileDescriptors; + SerializeInputStream(inputStream, params, fileDescriptors); + + MOZ_ASSERT(fileDescriptors.IsEmpty()); + + auto* dbFile = new DatabaseFile(this); + + actor = + mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile, + params); + if (NS_WARN_IF(!actor)) { + return nullptr; + } + } + + MOZ_ASSERT(actor); + + mFileActors.Put(weakRef, actor); + } + + MOZ_ASSERT(actor); + + return actor; } -NS_IMETHODIMP_(const nsACString&) -IDBDatabase::Origin() +void +IDBDatabase::NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor) { - return mASCIIOrigin; + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileActor); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static PLDHashOperator + Remove(nsISupports* aKey, + PBackgroundIDBDatabaseFileChild*& aValue, + void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + if (aValue == static_cast(aClosure)) { + return PL_DHASH_REMOVE; + } + + return PL_DHASH_NEXT; + } + }; + + mFileActors.Enumerate(&Helper::Remove, aFileActor); +} + +void +IDBDatabase::NoteReceivedBlob(nsIDOMBlob* aBlob) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBlob); + MOZ_ASSERT(mBackgroundActor); + +#ifdef DEBUG + { + nsRefPtr blobImpl = static_cast(aBlob)->Impl(); + MOZ_ASSERT(blobImpl); + + nsCOMPtr remoteBlob = do_QueryObject(blobImpl); + MOZ_ASSERT(remoteBlob); + + BlobChild* blobChild = remoteBlob->GetBlobChild(); + MOZ_ASSERT(blobChild); + + PBackgroundChild* backgroundManager = blobChild->GetBackgroundManager(); + MOZ_ASSERT(backgroundManager); + + PBackgroundChild* thisManager = mBackgroundActor->Manager()->Manager(); + MOZ_ASSERT(thisManager); + + MOZ_ASSERT(thisManager == backgroundManager); + } +#endif + + nsCOMPtr weakRef = do_GetWeakReference(aBlob); + MOZ_ASSERT(weakRef); + + // It's ok if this entry already exists in the table. + mReceivedBlobs.PutEntry(weakRef); +} + +void +IDBDatabase::DelayedMaybeExpireFileActors() +{ + AssertIsOnOwningThread(); + + if (!mBackgroundActor || !mFileActors.Count()) { + return; + } + + nsCOMPtr runnable = + NS_NewRunnableMethodWithArg(this, + &IDBDatabase::ExpireFileActors, + /* aExpireAll */ false); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); +} + +nsresult +IDBDatabase::GetQuotaInfo(nsACString& aOrigin, + PersistenceType* aPersistenceType) +{ + using mozilla::dom::quota::QuotaManager; + + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (aPersistenceType) { + *aPersistenceType = mSpec->metadata().persistenceType(); + MOZ_ASSERT(*aPersistenceType != PERSISTENCE_TYPE_INVALID); + } + + PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo(); + MOZ_ASSERT(principalInfo); + + switch (principalInfo->type()) { + case PrincipalInfo::TNullPrincipalInfo: + MOZ_CRASH("Is this needed?!"); + + case PrincipalInfo::TSystemPrincipalInfo: + QuotaManager::GetInfoForChrome(nullptr, &aOrigin, nullptr, nullptr); + return NS_OK; + + case PrincipalInfo::TContentPrincipalInfo: { + nsresult rv; + nsCOMPtr principal = + PrincipalInfoToPrincipal(*principalInfo, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = QuotaManager::GetInfoFromPrincipal(principal, + nullptr, + &aOrigin, + nullptr, + nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + default: + MOZ_CRASH("Unknown PrincipalInfo type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +void +IDBDatabase::ExpireFileActors(bool aExpireAll) +{ + AssertIsOnOwningThread(); + + class MOZ_STACK_CLASS Helper MOZ_FINAL + { + public: + static PLDHashOperator + MaybeExpireFileActors(nsISupports* aKey, + PBackgroundIDBDatabaseFileChild*& aValue, + void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + const bool expiringAll = *static_cast(aClosure); + + bool shouldExpire = expiringAll; + if (!shouldExpire) { + nsCOMPtr weakRef = do_QueryInterface(aKey); + MOZ_ASSERT(weakRef); + + nsCOMPtr referent = do_QueryReferent(weakRef); + shouldExpire = !referent; + } + + if (shouldExpire) { + PBackgroundIDBDatabaseFileChild::Send__delete__(aValue); + + if (!expiringAll) { + return PL_DHASH_REMOVE; + } + } + + return PL_DHASH_NEXT; + } + + static PLDHashOperator + MaybeExpireReceivedBlobs(nsISupportsHashKey* aKey, + void* aClosure) + { + MOZ_ASSERT(aKey); + MOZ_ASSERT(!aClosure); + + nsISupports* key = aKey->GetKey(); + MOZ_ASSERT(key); + + nsCOMPtr weakRef = do_QueryInterface(key); + MOZ_ASSERT(weakRef); + + nsCOMPtr referent = do_QueryReferent(weakRef); + if (!referent) { + return PL_DHASH_REMOVE; + } + + return PL_DHASH_NEXT; + } + }; + + if (mBackgroundActor && mFileActors.Count()) { + mFileActors.Enumerate(&Helper::MaybeExpireFileActors, &aExpireAll); + if (aExpireAll) { + mFileActors.Clear(); + } + } else { + MOZ_ASSERT(!mFileActors.Count()); + } + + if (mReceivedBlobs.Count()) { + if (aExpireAll) { + mReceivedBlobs.Clear(); + } else { + mReceivedBlobs.EnumerateEntries(&Helper::MaybeExpireReceivedBlobs, + nullptr); + } + } +} + +void +IDBDatabase::NoteLiveMutableFile(IDBMutableFile* aMutableFile) +{ + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aMutableFile); + MOZ_ASSERT(!mLiveMutableFiles.Contains(aMutableFile)); + + mLiveMutableFiles.AppendElement(aMutableFile); +} + +void +IDBDatabase::NoteFinishedMutableFile(IDBMutableFile* aMutableFile) +{ + // This should always happen in the main process but occasionally it is called + // after the IndexedDatabaseManager has already shut down. + // MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aMutableFile); + + // It's ok if this is called more than once, so don't assert that aMutableFile + // is in the list already. + + mLiveMutableFiles.RemoveElement(aMutableFile); +} + +void +IDBDatabase::InvalidateMutableFiles() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mLiveMutableFiles.IsEmpty()) { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + for (uint32_t count = mLiveMutableFiles.Length(), index = 0; + index < count; + index++) { + mLiveMutableFiles[index]->Invalidate(); + } + + mLiveMutableFiles.Clear(); + } +} + +void +IDBDatabase::Invalidate() +{ + AssertIsOnOwningThread(); + + if (!mInvalidated) { + mInvalidated = true; + + InvalidateInternal(); + } +} + +NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) + +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + tmp->AssertIsOnOwningThread(); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + tmp->AssertIsOnOwningThread(); + + // Don't unlink mFactory! + + // We've been unlinked, at the very least we should be able to prevent further + // transactions from starting and unblock any other SetVersion callers. + tmp->CloseInternal(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +void +IDBDatabase::LastRelease() +{ + AssertIsOnOwningThread(); + + CloseInternal(); + + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); + } } nsresult @@ -768,171 +1193,239 @@ IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor) return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); } -AsyncConnectionHelper::ChildProcessSendResult -NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) +JSObject* +IDBDatabase::WrapObject(JSContext* aCx) { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - return Success_NotSent; + return IDBDatabaseBinding::Wrap(aCx, this); } +CreateFileHelper::CreateFileHelper(IDBDatabase* aDatabase, + IDBRequest* aRequest, + const nsAString& aName, + const nsAString& aType, + const nsACString& aOrigin) + : mDatabase(aDatabase) + , mRequest(aRequest) + , mName(aName) + , mType(aType) + , mDatabaseName(aDatabase->Name()) + , mOrigin(aOrigin) + , mPersistenceType(aDatabase->Spec()->metadata().persistenceType()) + , mResultCode(NS_OK) +{ + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDatabase); + MOZ_ASSERT(aRequest); + MOZ_ASSERT(mPersistenceType != PERSISTENCE_TYPE_INVALID); +} + +// static nsresult -NoRequestDatabaseHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) +CreateFileHelper::CreateAndDispatch(IDBDatabase* aDatabase, + IDBRequest* aRequest, + const nsAString& aName, + const nsAString& aType) { - MOZ_CRASH("Should never get here!"); -} + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(aDatabase->Factory()); + MOZ_ASSERT(aRequest); + MOZ_ASSERT(!QuotaManager::IsShuttingDown()); -nsresult -NoRequestDatabaseHelper::OnSuccess() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - return NS_OK; -} - -void -NoRequestDatabaseHelper::OnError() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mTransaction->Abort(GetResultCode()); -} - -nsresult -CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("CreateObjectStoreHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - NS_WARNING("Refusing to create additional objectStore because disk space " - "is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; + nsCString origin; + nsresult rv = aDatabase->GetQuotaInfo(origin, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - nsCOMPtr stmt = - mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( - "INSERT INTO object_store (id, auto_increment, name, key_path) " - "VALUES (:id, :auto_increment, :name, :key_path)" - )); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + MOZ_ASSERT(!origin.IsEmpty()); - mozStorageStatementScoper scoper(stmt); + nsRefPtr helper = + new CreateFileHelper(aDatabase, aRequest, aName, aType, origin); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"), - mObjectStore->IsAutoIncrement() ? 1 : 0); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsCOMPtr ioThread = quotaManager->IOThread(); + MOZ_ASSERT(ioThread); - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - const KeyPath& keyPath = mObjectStore->GetKeyPath(); - if (keyPath.IsValid()) { - nsAutoString keyPathSerialization; - keyPath.SerializeToString(keyPathSerialization); - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), - keyPathSerialization); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_WARN_IF(NS_FAILED(ioThread->Dispatch(helper, NS_DISPATCH_NORMAL)))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - else { - rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path")); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - rv = stmt->Execute(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -void -CreateObjectStoreHelper::ReleaseMainThreadObjects() -{ - mObjectStore = nullptr; - NoRequestDatabaseHelper::ReleaseMainThreadObjects(); -} - -nsresult -DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("DeleteObjectStoreHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement(NS_LITERAL_CSTRING( - "DELETE FROM object_store " - "WHERE id = :id " - )); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->Execute(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; } nsresult -CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection) +CreateFileHelper::DoDatabaseWork() { AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(!mFileInfo); - PROFILER_LABEL("CreateFileHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); + PROFILER_LABEL("IndexedDB", + "CreateFileHelper::DoDatabaseWork", + js::ProfileEntry::Category::STORAGE); if (IndexedDatabaseManager::InLowDiskSpaceMode()) { NS_WARNING("Refusing to create file because disk space is low!"); return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; } - FileManager* fileManager = mDatabase->Manager(); + IndexedDatabaseManager* idbManager = IndexedDatabaseManager::Get(); + MOZ_ASSERT(idbManager); - mFileInfo = fileManager->GetNewFileInfo(); - IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsRefPtr fileManager = + idbManager->GetFileManager(mPersistenceType, mOrigin, mDatabaseName); + MOZ_ASSERT(fileManager); - const int64_t& fileId = mFileInfo->Id(); + nsRefPtr fileInfo = fileManager->GetNewFileInfo(); + if (NS_WARN_IF(!fileInfo)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - nsCOMPtr directory = fileManager->EnsureJournalDirectory(); - NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); + const int64_t fileId = fileInfo->Id(); - nsCOMPtr file = fileManager->GetFileForId(directory, fileId); - NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); + nsCOMPtr journalDirectory = fileManager->EnsureJournalDirectory(); + if (NS_WARN_IF(!journalDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr journalFile = + fileManager->GetFileForId(journalDirectory, fileId); + if (NS_WARN_IF(!journalFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - directory = fileManager->GetDirectory(); - IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsresult rv = journalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - file = fileManager->GetFileForId(directory, fileId); - IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsCOMPtr fileDirectory = fileManager->GetDirectory(); + if (NS_WARN_IF(!fileDirectory)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsCOMPtr file = fileManager->GetFileForId(fileDirectory, fileId); + if (NS_WARN_IF(!file)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mFileInfo.swap(fileInfo); + return NS_OK; +} + +void +CreateFileHelper::DoMainThreadWork(nsresult aResultCode) +{ + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mDatabase->IsInvalidated()) { + IDB_REPORT_INTERNAL_ERR(); + aResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsRefPtr mutableFile; + if (NS_SUCCEEDED(aResultCode)) { + mutableFile = + IDBMutableFile::Create(mDatabase, mName, mType, mFileInfo.forget()); + MOZ_ASSERT(mutableFile); + } + + DispatchMutableFileResult(mRequest, aResultCode, mutableFile); +} + +NS_IMPL_ISUPPORTS_INHERITED0(CreateFileHelper, nsRunnable) + +NS_IMETHODIMP +CreateFileHelper::Run() +{ + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + + if (NS_IsMainThread()) { + DoMainThreadWork(mResultCode); + + mDatabase = nullptr; + mRequest = nullptr; + mFileInfo = nullptr; + + return NS_OK; + } + + AssertIsOnIOThread(); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + + nsresult rv = DoDatabaseWork(); + if (NS_WARN_IF(NS_FAILED(rv))) { + mResultCode = rv; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); return NS_OK; } -nsresult -CreateFileHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - nsRefPtr mutableFile = - IDBMutableFile::Create(mName, mType, mDatabase, mFileInfo.forget()); - IDB_ENSURE_TRUE(mutableFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); +NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver) - return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mutableFile), aVal); +NS_IMETHODIMP +IDBDatabase:: +Observer::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTopic); + + if (!strcmp(aTopic, kWindowObserverTopic)) { + if (mWeakDatabase) { + nsCOMPtr supportsInt = do_QueryInterface(aSubject); + MOZ_ASSERT(supportsInt); + + uint64_t windowId; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(supportsInt->GetData(&windowId))); + + if (windowId == mWindowId) { + nsRefPtr database = mWeakDatabase; + mWeakDatabase = nullptr; + + database->InvalidateInternal(); + } + } + + return NS_OK; + } + + if (!strcmp(aTopic, kCycleCollectionObserverTopic) || + !strcmp(aTopic, kMemoryPressureObserverTopic)) { + if (mWeakDatabase) { + nsRefPtr database = mWeakDatabase; + + database->ExpireFileActors(/* aExpireAll */ false); + } + + return NS_OK; + } + + NS_WARNING("Unknown observer topic!"); + return NS_OK; } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 91d8681ed34c..ab0a041475b9 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -7,274 +7,302 @@ #ifndef mozilla_dom_indexeddb_idbdatabase_h__ #define mozilla_dom_indexeddb_idbdatabase_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "nsIDocument.h" -#include "nsIOfflineStorage.h" - #include "mozilla/Attributes.h" -#include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/dom/IDBObjectStoreBinding.h" #include "mozilla/dom/IDBTransactionBinding.h" -#include "mozilla/dom/quota/PersistenceType.h" - -#include "mozilla/dom/indexedDB/FileManager.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" +#include "mozilla/dom/StorageTypeBinding.h" #include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "nsAutoPtr.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsString.h" +#include "nsTHashtable.h" -class nsIScriptContext; +class nsIDocument; +class nsIDOMBlob; +class nsIWeakReference; class nsPIDOMWindow; namespace mozilla { + +class ErrorResult; class EventChainPostVisitor; + namespace dom { -class nsIContentParent; -namespace quota { -class Client; -} -} -} -BEGIN_INDEXEDDB_NAMESPACE +class DOMStringList; +struct IDBObjectStoreParameters; +template class Sequence; -class AsyncConnectionHelper; -struct DatabaseInfo; +namespace indexedDB { + +class BackgroundDatabaseChild; +class DatabaseSpec; +class FileManager; class IDBFactory; -class IDBIndex; +class IDBMutableFile; class IDBObjectStore; +class IDBRequest; class IDBTransaction; -class IndexedDatabaseManager; -class IndexedDBDatabaseChild; -class IndexedDBDatabaseParent; -struct ObjectStoreInfoGuts; +class PBackgroundIDBDatabaseFileChild; -class IDBDatabase MOZ_FINAL : public IDBWrapperCache, - public nsIOfflineStorage +class IDBDatabase MOZ_FINAL + : public IDBWrapperCache { - friend class AsyncConnectionHelper; - friend class IndexedDatabaseManager; - friend class IndexedDBDatabaseParent; - friend class IndexedDBDatabaseChild; + typedef mozilla::dom::StorageType StorageType; + typedef mozilla::dom::quota::PersistenceType PersistenceType; -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIOFFLINESTORAGE - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) - - static already_AddRefed - Create(IDBWrapperCache* aOwnerCache, - IDBFactory* aFactory, - already_AddRefed aDatabaseInfo, - const nsACString& aASCIIOrigin, - FileManager* aFileManager, - mozilla::dom::nsIContentParent* aContentParent); - - static IDBDatabase* - FromStorage(nsIOfflineStorage* aStorage); - - // nsIDOMEventTarget - virtual nsresult PostHandleEvent( - EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; - - DatabaseInfo* Info() const - { - return mDatabaseInfo; - } - - const nsString& Name() const - { - return mName; - } - - const nsString& FilePath() const - { - return mFilePath; - } - - already_AddRefed GetOwnerDocument() - { - if (!GetOwner()) { - return nullptr; - } - - nsCOMPtr doc = GetOwner()->GetExtantDoc(); - return doc.forget(); - } - - // Whether or not the database has been invalidated. If it has then no further - // transactions for this database will be allowed to run. This function may be - // called on any thread. - bool IsInvalidated() const - { - return mInvalidated; - } - - void DisconnectFromActorParent(); - - void CloseInternal(bool aIsDead); - - void EnterSetVersionTransaction(); - void ExitSetVersionTransaction(); - - // Called when a versionchange transaction is aborted to reset the - // DatabaseInfo. - void RevertToPreviousState(); - - FileManager* Manager() const - { - return mFileManager; - } - - void - SetActor(IndexedDBDatabaseChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } - - void - SetActor(IndexedDBDatabaseParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBDatabaseChild* - GetActorChild() const - { - return mActorChild; - } - - IndexedDBDatabaseParent* - GetActorParent() const - { - return mActorParent; - } - - mozilla::dom::nsIContentParent* - GetContentParent() const - { - return mContentParent; - } - - already_AddRefed - CreateObjectStoreInternal(IDBTransaction* aTransaction, - const ObjectStoreInfoGuts& aInfo, - ErrorResult& aRv); - - IDBFactory* - Factory() const - { - return mFactory; - } - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - nsPIDOMWindow* - GetParentObject() const - { - return GetOwner(); - } - - void - GetName(nsString& aName) const - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - aName.Assign(mName); - } - - uint64_t - Version() const; - - already_AddRefed - GetObjectStoreNames(ErrorResult& aRv) const; - - already_AddRefed - CreateObjectStore(JSContext* aCx, const nsAString& aName, - const IDBObjectStoreParameters& aOptionalParameters, - ErrorResult& aRv); - - void - DeleteObjectStore(const nsAString& name, ErrorResult& aRv); - - already_AddRefed - Transaction(const nsAString& aStoreName, IDBTransactionMode aMode, - ErrorResult& aRv) - { - Sequence list; - list.AppendElement(aStoreName); - return Transaction(list, aMode, aRv); - } - - already_AddRefed - Transaction(const Sequence& aStoreNames, IDBTransactionMode aMode, - ErrorResult& aRv); - - IMPL_EVENT_HANDLER(abort) - IMPL_EVENT_HANDLER(error) - IMPL_EVENT_HANDLER(versionchange) - - mozilla::dom::StorageType - Storage() const - { - return PersistenceTypeToStorage(mPersistenceType); - } - - already_AddRefed - CreateMutableFile(const nsAString& aName, const Optional& aType, - ErrorResult& aRv); - - already_AddRefed - MozCreateFileHandle(const nsAString& aName, const Optional& aType, - ErrorResult& aRv) - { - return CreateMutableFile(aName, aType, aRv); - } - - virtual void LastRelease() MOZ_OVERRIDE; - -private: - explicit IDBDatabase(IDBWrapperCache* aOwnerCache); - ~IDBDatabase(); - - void OnUnlink(); - void InvalidateInternal(bool aIsDead); + class Observer; + friend class Observer; // The factory must be kept alive when IndexedDB is used in multiple // processes. If it dies then the entire actor tree will be destroyed with it // and the world will explode. nsRefPtr mFactory; - nsRefPtr mDatabaseInfo; + nsAutoPtr mSpec; - // Set to a copy of the existing DatabaseInfo when starting a versionchange - // transaction. - nsRefPtr mPreviousDatabaseInfo; - nsCString mDatabaseId; - nsString mName; - nsString mFilePath; - nsCString mASCIIOrigin; + // Normally null except during a versionchange transaction. + nsAutoPtr mPreviousSpec; nsRefPtr mFileManager; - IndexedDBDatabaseChild* mActorChild; - IndexedDBDatabaseParent* mActorParent; + BackgroundDatabaseChild* mBackgroundActor; - mozilla::dom::nsIContentParent* mContentParent; + nsTHashtable> mTransactions; - nsRefPtr mQuotaClient; + nsDataHashtable + mFileActors; + + nsTHashtable mReceivedBlobs; + + nsRefPtr mObserver; + + // Weak refs, IDBMutableFile strongly owns this IDBDatabase object. + nsTArray mLiveMutableFiles; - bool mInvalidated; - bool mRegistered; bool mClosed; - bool mRunningVersionChange; + bool mInvalidated; + +public: + static already_AddRefed + Create(IDBWrapperCache* aOwnerCache, + IDBFactory* aFactory, + BackgroundDatabaseChild* aActor, + DatabaseSpec* aSpec); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + const nsString& + Name() const; + + void + GetName(nsAString& aName) const + { + AssertIsOnOwningThread(); + + aName = Name(); + } + + uint64_t + Version() const; + + already_AddRefed + GetOwnerDocument() const; + + void + Close() + { + AssertIsOnOwningThread(); + + CloseInternal(); + } + + bool + IsClosed() const + { + AssertIsOnOwningThread(); + + return mClosed; + } + + void + Invalidate(); + + // Whether or not the database has been invalidated. If it has then no further + // transactions for this database will be allowed to run. + bool + IsInvalidated() const + { + AssertIsOnOwningThread(); + + return mInvalidated; + } + + void + EnterSetVersionTransaction(uint64_t aNewVersion); + + void + ExitSetVersionTransaction(); + + // Called when a versionchange transaction is aborted to reset the + // DatabaseInfo. + void + RevertToPreviousState(); + + IDBFactory* + Factory() const + { + AssertIsOnOwningThread(); + + return mFactory; + } + + void + RegisterTransaction(IDBTransaction* aTransaction); + + void + UnregisterTransaction(IDBTransaction* aTransaction); + + void + AbortTransactions(); + + PBackgroundIDBDatabaseFileChild* + GetOrCreateFileActorForBlob(nsIDOMBlob* aBlob); + + void + NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor); + + void + NoteReceivedBlob(nsIDOMBlob* aBlob); + + void + DelayedMaybeExpireFileActors(); + + // XXX This doesn't really belong here... It's only needed for IDBMutableFile + // serialization and should be removed someday. + nsresult + GetQuotaInfo(nsACString& aOrigin, PersistenceType* aPersistenceType); + + void + NoteLiveMutableFile(IDBMutableFile* aMutableFile); + + void + NoteFinishedMutableFile(IDBMutableFile* aMutableFile); + + nsPIDOMWindow* + GetParentObject() const; + + already_AddRefed + ObjectStoreNames() const; + + already_AddRefed + CreateObjectStore(JSContext* aCx, + const nsAString& aName, + const IDBObjectStoreParameters& aOptionalParameters, + ErrorResult& aRv); + + void + DeleteObjectStore(const nsAString& name, ErrorResult& aRv); + + already_AddRefed + Transaction(const nsAString& aStoreName, + IDBTransactionMode aMode, + ErrorResult& aRv); + + already_AddRefed + Transaction(const Sequence& aStoreNames, + IDBTransactionMode aMode, + ErrorResult& aRv); + + StorageType + Storage() const; + + IMPL_EVENT_HANDLER(abort) + IMPL_EVENT_HANDLER(error) + IMPL_EVENT_HANDLER(versionchange) + + already_AddRefed + CreateMutableFile(const nsAString& aName, + const Optional& aType, + ErrorResult& aRv); + + already_AddRefed + MozCreateFileHandle(const nsAString& aName, + const Optional& aType, + ErrorResult& aRv) + { + return CreateMutableFile(aName, aType, aRv); + } + + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } + + const DatabaseSpec* + Spec() const + { + return mSpec; + } + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) + + // nsIDOMEventTarget + virtual void + LastRelease() MOZ_OVERRIDE; + + virtual nsresult + PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + +private: + IDBDatabase(IDBWrapperCache* aOwnerCache, + IDBFactory* aFactory, + BackgroundDatabaseChild* aActor, + DatabaseSpec* aSpec); + + ~IDBDatabase(); + + void + CloseInternal(); + + void + InvalidateInternal(); + + bool + RunningVersionChangeTransaction() const + { + AssertIsOnOwningThread(); + + return !!mPreviousSpec; + } + + void + RefreshSpec(bool aMayDelete); + + void + ExpireFileActors(bool aExpireAll); + + void + InvalidateMutableFiles(); }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbdatabase_h__ diff --git a/dom/indexedDB/IDBEvents.cpp b/dom/indexedDB/IDBEvents.cpp index a4c58bee3a62..cd9ce79cf1d5 100644 --- a/dom/indexedDB/IDBEvents.cpp +++ b/dom/indexedDB/IDBEvents.cpp @@ -6,49 +6,45 @@ #include "IDBEvents.h" -#include "nsJSON.h" -#include "nsThreadUtils.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/EventTarget.h" +#include "mozilla/dom/IDBVersionChangeEventBinding.h" +#include "nsString.h" -#include "IDBRequest.h" -#include "IDBTransaction.h" - -USING_INDEXEDDB_NAMESPACE +using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::dom::indexedDB; -namespace { +namespace mozilla { +namespace dom { +namespace indexedDB { -class EventFiringRunnable : public nsRunnable -{ -public: - EventFiringRunnable(EventTarget* aTarget, - nsIDOMEvent* aEvent) - : mTarget(aTarget), mEvent(aEvent) - { } - - NS_IMETHOD Run() { - bool dummy; - return mTarget->DispatchEvent(mEvent, &dummy); - } - -private: - nsCOMPtr mTarget; - nsCOMPtr mEvent; -}; - -} // anonymous namespace +const char16_t* kAbortEventType = MOZ_UTF16("abort"); +const char16_t* kBlockedEventType = MOZ_UTF16("blocked"); +const char16_t* kCompleteEventType = MOZ_UTF16("complete"); +const char16_t* kErrorEventType = MOZ_UTF16("error"); +const char16_t* kSuccessEventType = MOZ_UTF16("success"); +const char16_t* kUpgradeNeededEventType = MOZ_UTF16("upgradeneeded"); +const char16_t* kVersionChangeEventType = MOZ_UTF16("versionchange"); already_AddRefed -mozilla::dom::indexedDB::CreateGenericEvent(mozilla::dom::EventTarget* aOwner, - const nsAString& aType, - Bubbles aBubbles, - Cancelable aCancelable) +CreateGenericEvent(EventTarget* aOwner, + const nsDependentString& aType, + Bubbles aBubbles, + Cancelable aCancelable) { nsCOMPtr event; - NS_NewDOMEvent(getter_AddRefs(event), aOwner, nullptr, nullptr); - nsresult rv = event->InitEvent(aType, - aBubbles == eDoesBubble ? true : false, - aCancelable == eCancelable ? true : false); - NS_ENSURE_SUCCESS(rv, nullptr); + nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), aOwner, nullptr, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + rv = event->InitEvent(aType, + aBubbles == eDoesBubble ? true : false, + aCancelable == eCancelable ? true : false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } event->SetTrusted(true); @@ -57,37 +53,39 @@ mozilla::dom::indexedDB::CreateGenericEvent(mozilla::dom::EventTarget* aOwner, // static already_AddRefed -IDBVersionChangeEvent::CreateInternal(mozilla::dom::EventTarget* aOwner, +IDBVersionChangeEvent::CreateInternal(EventTarget* aOwner, const nsAString& aType, uint64_t aOldVersion, - uint64_t aNewVersion) + Nullable aNewVersion) { - nsRefPtr event(new IDBVersionChangeEvent(aOwner)); + nsRefPtr event = + new IDBVersionChangeEvent(aOwner, aOldVersion); + if (!aNewVersion.IsNull()) { + event->mNewVersion.SetValue(aNewVersion.Value()); + } nsresult rv = event->InitEvent(aType, false, false); - NS_ENSURE_SUCCESS(rv, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } event->SetTrusted(true); - event->mOldVersion = aOldVersion; - event->mNewVersion = aNewVersion; - return event.forget(); } -// static -already_AddRefed -IDBVersionChangeEvent::CreateRunnableInternal(mozilla::dom::EventTarget* aTarget, - const nsAString& aType, - uint64_t aOldVersion, - uint64_t aNewVersion) +already_AddRefed +IDBVersionChangeEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const IDBVersionChangeEventInit& aOptions, + ErrorResult& aRv) { - nsRefPtr event = - CreateInternal(aTarget, aType, aOldVersion, aNewVersion); - NS_ENSURE_TRUE(event, nullptr); + nsCOMPtr target = do_QueryInterface(aGlobal.GetAsSupports()); - nsCOMPtr runnable(new EventFiringRunnable(aTarget, event)); - return runnable.forget(); + return CreateInternal(target, + aType, + aOptions.mOldVersion, + aOptions.mNewVersion); } NS_IMPL_ADDREF_INHERITED(IDBVersionChangeEvent, Event) @@ -96,3 +94,13 @@ NS_IMPL_RELEASE_INHERITED(IDBVersionChangeEvent, Event) NS_INTERFACE_MAP_BEGIN(IDBVersionChangeEvent) NS_INTERFACE_MAP_ENTRY(IDBVersionChangeEvent) NS_INTERFACE_MAP_END_INHERITING(Event) + +JSObject* +IDBVersionChangeEvent::WrapObject(JSContext* aCx) +{ + return IDBVersionChangeEventBinding::Wrap(aCx, this); +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBEvents.h b/dom/indexedDB/IDBEvents.h index af942d286d79..c4844c7ee7e0 100644 --- a/dom/indexedDB/IDBEvents.h +++ b/dom/indexedDB/IDBEvents.h @@ -7,28 +7,28 @@ #ifndef mozilla_dom_indexeddb_idbevents_h__ #define mozilla_dom_indexeddb_idbevents_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "nsIRunnable.h" - +#include "js/RootingAPI.h" +#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/Nullable.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/IDBVersionChangeEventBinding.h" - -#define SUCCESS_EVT_STR "success" -#define ERROR_EVT_STR "error" -#define COMPLETE_EVT_STR "complete" -#define ABORT_EVT_STR "abort" -#define VERSIONCHANGE_EVT_STR "versionchange" -#define BLOCKED_EVT_STR "blocked" -#define UPGRADENEEDED_EVT_STR "upgradeneeded" #define IDBVERSIONCHANGEEVENT_IID \ - { 0x3b65d4c3, 0x73ad, 0x492e, \ - { 0xb1, 0x2d, 0x15, 0xf9, 0xda, 0xc2, 0x08, 0x4b } } + {0x3b65d4c3, 0x73ad, 0x492e, {0xb1, 0x2d, 0x15, 0xf9, 0xda, 0xc2, 0x08, 0x4b}} -BEGIN_INDEXEDDB_NAMESPACE +class nsAString; +class nsDependentString; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class EventTarget; +class GlobalObject; +struct IDBVersionChangeEventInit; + +namespace indexedDB { enum Bubbles { eDoesNotBubble, @@ -40,125 +40,94 @@ enum Cancelable { eCancelable }; +extern const char16_t* kAbortEventType; +extern const char16_t* kBlockedEventType; +extern const char16_t* kCompleteEventType; +extern const char16_t* kErrorEventType; +extern const char16_t* kSuccessEventType; +extern const char16_t* kUpgradeNeededEventType; +extern const char16_t* kVersionChangeEventType; + already_AddRefed -CreateGenericEvent(mozilla::dom::EventTarget* aOwner, - const nsAString& aType, +CreateGenericEvent(EventTarget* aOwner, + const nsDependentString& aType, Bubbles aBubbles, Cancelable aCancelable); -class IDBVersionChangeEvent : public Event +class IDBVersionChangeEvent MOZ_FINAL : public Event { -public: - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_TO_EVENT - NS_DECLARE_STATIC_IID_ACCESSOR(IDBVERSIONCHANGEEVENT_IID) + uint64_t mOldVersion; + Nullable mNewVersion; - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE +public: + static already_AddRefed + Create(EventTarget* aOwner, + const nsDependentString& aName, + uint64_t aOldVersion, + uint64_t aNewVersion) { - return mozilla::dom::IDBVersionChangeEventBinding::Wrap(aCx, this); + Nullable newVersion(aNewVersion); + return CreateInternal(aOwner, aName, aOldVersion, newVersion); + } + + static already_AddRefed + Create(EventTarget* aOwner, + const nsDependentString& aName, + uint64_t aOldVersion) + { + Nullable newVersion(0); + newVersion.SetNull(); + return CreateInternal(aOwner, aName, aOldVersion, newVersion); } static already_AddRefed Constructor(const GlobalObject& aGlobal, const nsAString& aType, const IDBVersionChangeEventInit& aOptions, - ErrorResult& aRv) - { - uint64_t newVersion = 0; - if (!aOptions.mNewVersion.IsNull()) { - newVersion = aOptions.mNewVersion.Value(); - } - nsCOMPtr target = do_QueryInterface(aGlobal.GetAsSupports()); - return CreateInternal(target, aType, aOptions.mOldVersion, newVersion); - } + ErrorResult& aRv); - uint64_t OldVersion() + uint64_t + OldVersion() const { return mOldVersion; } - mozilla::dom::Nullable GetNewVersion() + Nullable + GetNewVersion() const { - return mNewVersion - ? mozilla::dom::Nullable(mNewVersion) - : mozilla::dom::Nullable(); + return mNewVersion; } - inline static already_AddRefed - Create(mozilla::dom::EventTarget* aOwner, - int64_t aOldVersion, - int64_t aNewVersion) - { - return CreateInternal(aOwner, - NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), - aOldVersion, aNewVersion); - } + NS_DECLARE_STATIC_IID_ACCESSOR(IDBVERSIONCHANGEEVENT_IID) - inline static already_AddRefed - CreateBlocked(mozilla::dom::EventTarget* aOwner, - uint64_t aOldVersion, - uint64_t aNewVersion) - { - return CreateInternal(aOwner, NS_LITERAL_STRING(BLOCKED_EVT_STR), - aOldVersion, aNewVersion); - } + NS_DECL_ISUPPORTS_INHERITED + NS_FORWARD_TO_EVENT - inline static already_AddRefed - CreateUpgradeNeeded(mozilla::dom::EventTarget* aOwner, - uint64_t aOldVersion, - uint64_t aNewVersion) - { - return CreateInternal(aOwner, - NS_LITERAL_STRING(UPGRADENEEDED_EVT_STR), - aOldVersion, aNewVersion); - } + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; - inline static already_AddRefed - CreateRunnable(mozilla::dom::EventTarget* aTarget, - uint64_t aOldVersion, - uint64_t aNewVersion) - { - return CreateRunnableInternal(aTarget, - NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), - aOldVersion, aNewVersion); - } - - static already_AddRefed - CreateBlockedRunnable(mozilla::dom::EventTarget* aTarget, - uint64_t aOldVersion, - uint64_t aNewVersion) - { - return CreateRunnableInternal(aTarget, - NS_LITERAL_STRING(BLOCKED_EVT_STR), - aOldVersion, aNewVersion); - } - -protected: - explicit IDBVersionChangeEvent(mozilla::dom::EventTarget* aOwner) +private: + IDBVersionChangeEvent(EventTarget* aOwner, uint64_t aOldVersion) : Event(aOwner, nullptr, nullptr) + , mOldVersion(aOldVersion) { SetIsDOMBinding(); } - virtual ~IDBVersionChangeEvent() { } + + ~IDBVersionChangeEvent() + { } static already_AddRefed - CreateInternal(mozilla::dom::EventTarget* aOwner, - const nsAString& aType, + CreateInternal(EventTarget* aOwner, + const nsAString& aName, uint64_t aOldVersion, - uint64_t aNewVersion); - - static already_AddRefed - CreateRunnableInternal(mozilla::dom::EventTarget* aOwner, - const nsAString& aType, - uint64_t aOldVersion, - uint64_t aNewVersion); - - uint64_t mOldVersion; - uint64_t mNewVersion; + Nullable aNewVersion); }; NS_DEFINE_STATIC_IID_ACCESSOR(IDBVersionChangeEvent, IDBVERSIONCHANGEEVENT_IID) -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbevents_h__ diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index a17ce5d1f865..0516bb572d55 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -6,538 +6,698 @@ #include "IDBFactory.h" -#include "nsIFile.h" -#include "nsIPrincipal.h" -#include "nsIScriptContext.h" -#include "nsIScriptSecurityManager.h" -#include "nsIXPConnect.h" -#include "nsIXPCScriptable.h" - -#include -#include "mozilla/dom/nsIContentParent.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/IDBFactoryBinding.h" -#include "mozilla/dom/PBrowserChild.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/dom/TabChild.h" -#include "mozilla/Preferences.h" -#include "mozilla/storage.h" -#include "nsComponentManagerUtils.h" -#include "nsCharSeparatedTokenizer.h" -#include "nsContentUtils.h" -#include "nsDOMClassInfoID.h" -#include "nsGlobalWindow.h" -#include "nsHashKeys.h" -#include "nsPIDOMWindow.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "nsXPCOMCID.h" - -#include "AsyncConnectionHelper.h" -#include "CheckPermissionsHelper.h" -#include "DatabaseInfo.h" -#include "IDBDatabase.h" -#include "IDBEvents.h" -#include "IDBKeyRange.h" +#include "IDBRequest.h" #include "IndexedDatabaseManager.h" -#include "Key.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/IDBFactoryBinding.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackground.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "nsGlobalWindow.h" +#include "nsIIPCBackgroundChildCreateCallback.h" +#include "nsILoadContext.h" +#include "nsIPrincipal.h" +#include "nsIWebNavigation.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "nsNetUtil.h" -#include "ipc/IndexedDBChild.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled" +#ifdef DEBUG +#include "nsContentUtils.h" // For IsCallerChrome assertions. +#endif -USING_INDEXEDDB_NAMESPACE -USING_QUOTA_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { -using mozilla::dom::ContentChild; -using mozilla::dom::nsIContentParent; -using mozilla::dom::IDBOpenDBOptions; -using mozilla::dom::NonNull; -using mozilla::dom::Optional; -using mozilla::dom::TabChild; -using mozilla::ErrorResult; -using mozilla::Preferences; +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; namespace { -struct ObjectStoreInfoMap -{ - ObjectStoreInfoMap() - : id(INT64_MIN), info(nullptr) { } +const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled"; - int64_t id; - ObjectStoreInfo* info; -}; +nsresult +GetPrincipalInfoFromPrincipal(nsIPrincipal* aPrincipal, + PrincipalInfo* aPrincipalInfo) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aPrincipalInfo); + + bool isNullPrincipal; + nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (isNullPrincipal) { + NS_WARNING("IndexedDB not supported from this principal!"); + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + rv = PrincipalToPrincipalInfo(aPrincipal, aPrincipalInfo); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; +} } // anonymous namespace -IDBFactory::IDBFactory() -: mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY), - mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr), - mContentParent(nullptr), mRootedOwningObject(false) +class IDBFactory::BackgroundCreateCallback MOZ_FINAL + : public nsIIPCBackgroundChildCreateCallback { + nsRefPtr mFactory; + +public: + BackgroundCreateCallback(IDBFactory* aFactory) + : mFactory(aFactory) + { + MOZ_ASSERT(aFactory); + } + + NS_DECL_ISUPPORTS + +private: + ~BackgroundCreateCallback() + { } + + NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK +}; + +struct IDBFactory::PendingRequestInfo +{ + nsRefPtr mRequest; + FactoryRequestParams mParams; + + PendingRequestInfo(IDBOpenDBRequest* aRequest, + const FactoryRequestParams& aParams) + : mRequest(aRequest), mParams(aParams) + { + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None); + } +}; + +IDBFactory::IDBFactory() + : mOwningObject(nullptr) + , mBackgroundActor(nullptr) + , mRootedOwningObject(false) + , mBackgroundActorFailed(false) + , mPrivateBrowsingMode(false) +{ +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + AssertIsOnOwningThread(); + SetIsDOMBinding(); } IDBFactory::~IDBFactory() { - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); - } + MOZ_ASSERT_IF(mBackgroundActorFailed, !mBackgroundActor); + if (mRootedOwningObject) { mOwningObject = nullptr; mozilla::DropJSObjects(this); } + + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); + } } // static nsresult -IDBFactory::Create(nsPIDOMWindow* aWindow, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - nsIContentParent* aContentParent, - IDBFactory** aFactory) +IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow, + IDBFactory** aFactory) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(), - "Non-chrome may not supply their own origin!"); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsInnerWindow()); + MOZ_ASSERT(aFactory); - IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (aWindow->IsOuterWindow()) { - aWindow = aWindow->GetCurrentInnerWindow(); - IDB_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { + *aFactory = nullptr; + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; } - // Make sure that the manager is up before we do anything here since lots of - // decisions depend on which process we're running in. - indexedDB::IndexedDatabaseManager* mgr = - indexedDB::IndexedDatabaseManager::GetOrCreate(); - IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsresult rv; - - nsCString group(aGroup); - nsCString origin(aASCIIOrigin); - StoragePrivilege privilege; - PersistenceType defaultPersistenceType; - if (origin.IsEmpty()) { - NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!"); - - rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege, - &defaultPersistenceType); + nsCOMPtr sop = do_QueryInterface(aWindow); + if (NS_WARN_IF(!sop)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - else { - rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege, - &defaultPersistenceType); + + nsCOMPtr principal = sop->GetPrincipal(); + if (NS_WARN_IF(!principal)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - if (NS_FAILED(rv)) { + + nsAutoPtr principalInfo(new PrincipalInfo()); + + if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(principal, + principalInfo)))) { // Not allowed. *aFactory = nullptr; return NS_OK; } - nsRefPtr factory = new IDBFactory(); - factory->mGroup = group; - factory->mASCIIOrigin = origin; - factory->mPrivilege = privilege; - factory->mDefaultPersistenceType = defaultPersistenceType; - factory->mWindow = aWindow; - factory->mContentParent = aContentParent; - - if (!IndexedDatabaseManager::IsMainProcess()) { - TabChild* tabChild = TabChild::GetFrom(aWindow); - IDB_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - IndexedDBChild* actor = new IndexedDBChild(tabChild, origin); - - bool allowed; - tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed); - - if (!allowed) { - actor->Send__delete__(actor); - *aFactory = nullptr; - return NS_OK; - } - - actor->SetFactory(factory); - } - - factory.forget(aFactory); - return NS_OK; -} - -// static -nsresult -IDBFactory::Create(JSContext* aCx, - JS::Handle aOwningObject, - nsIContentParent* aContentParent, - IDBFactory** aFactory) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aCx, "Null context!"); - NS_ASSERTION(aOwningObject, "Null object!"); - NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, - "Not a global object!"); - NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); - - // Make sure that the manager is up before we do anything here since lots of - // decisions depend on which process we're running in. IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); - IDB_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_WARN_IF(!mgr)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } - nsCString group; - nsCString origin; - StoragePrivilege privilege; - PersistenceType defaultPersistenceType; - QuotaManager::GetInfoForChrome(&group, &origin, &privilege, - &defaultPersistenceType); + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr loadContext = do_QueryInterface(webNav); + + bool privateBrowsingMode = loadContext && loadContext->UsePrivateBrowsing(); nsRefPtr factory = new IDBFactory(); - factory->mGroup = group; - factory->mASCIIOrigin = origin; - factory->mPrivilege = privilege; - factory->mDefaultPersistenceType = defaultPersistenceType; + factory->mPrincipalInfo = Move(principalInfo); + factory->mWindow = aWindow; + factory->mTabChild = TabChild::GetFrom(aWindow); + factory->mPrivateBrowsingMode = privateBrowsingMode; + + factory.forget(aFactory); + return NS_OK; +} + +// static +nsresult +IDBFactory::CreateForChromeJS(JSContext* aCx, + JS::Handle aOwningObject, + IDBFactory** aFactory) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); + + nsAutoPtr principalInfo( + new PrincipalInfo(SystemPrincipalInfo())); + + nsresult rv = + CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!principalInfo); + + return NS_OK; +} + +nsresult +IDBFactory::CreateForDatastore(JSContext* aCx, + JS::Handle aOwningObject, + IDBFactory** aFactory) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // There should be a null principal pushed here, but it's still chrome... + MOZ_ASSERT(!nsContentUtils::IsCallerChrome()); + + nsAutoPtr principalInfo( + new PrincipalInfo(SystemPrincipalInfo())); + + nsresult rv = + CreateForJSInternal(aCx, aOwningObject, principalInfo, aFactory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!principalInfo); + + return NS_OK; +} + +// static +nsresult +IDBFactory::CreateForJSInternal(JSContext* aCx, + JS::Handle aOwningObject, + nsAutoPtr& aPrincipalInfo, + IDBFactory** aFactory) +{ + MOZ_ASSERT(aCx); + MOZ_ASSERT(aOwningObject); + MOZ_ASSERT(aPrincipalInfo); + MOZ_ASSERT(aFactory); + MOZ_ASSERT(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, + "Not a global object!"); + + if (!NS_IsMainThread()) { + MOZ_CRASH("Not yet supported off the main thread!"); + } + + if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) { + *aFactory = nullptr; + return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + } + + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsRefPtr factory = new IDBFactory(); + factory->mPrincipalInfo = aPrincipalInfo.forget(); factory->mOwningObject = aOwningObject; - factory->mContentParent = aContentParent; mozilla::HoldJSObjects(factory.get()); factory->mRootedOwningObject = true; - if (!IndexedDatabaseManager::IsMainProcess()) { - ContentChild* contentChild = ContentChild::GetSingleton(); - IDB_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + factory.forget(aFactory); + return NS_OK; +} - IndexedDBChild* actor = new IndexedDBChild(contentChild, origin); +#ifdef DEBUG - contentChild->SendPIndexedDBConstructor(actor); +void +IDBFactory::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} - actor->SetFactory(factory); +#endif // DEBUG + +void +IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!mBackgroundActor); + + mBackgroundActor = aBackgroundActor; +} + +already_AddRefed +IDBFactory::Open(const nsAString& aName, + uint64_t aVersion, + ErrorResult& aRv) +{ + return OpenInternal(/* aPrincipal */ nullptr, + aName, + Optional(aVersion), + Optional(), + /* aDeleting */ false, + aRv); +} + +already_AddRefed +IDBFactory::Open(const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) +{ + return OpenInternal(/* aPrincipal */ nullptr, + aName, + aOptions.mVersion, + aOptions.mStorage, + /* aDeleting */ false, + aRv); +} + +already_AddRefed +IDBFactory::DeleteDatabase(const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) +{ + return OpenInternal(/* aPrincipal */ nullptr, + aName, + Optional(), + aOptions.mStorage, + /* aDeleting */ true, + aRv); +} + +int16_t +IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, + JS::Handle aSecond, ErrorResult& aRv) +{ + Key first, second; + nsresult rv = first.SetFromJSVal(aCx, aFirst); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return 0; } - factory.forget(aFactory); - return NS_OK; + rv = second.SetFromJSVal(aCx, aSecond); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return 0; + } + + if (first.IsUnset() || second.IsUnset()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); + return 0; + } + + return Key::CompareKeys(first, second); } -// static -nsresult -IDBFactory::Create(nsIContentParent* aContentParent, - IDBFactory** aFactory) +already_AddRefed +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + uint64_t aVersion, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!"); + MOZ_ASSERT(aPrincipal); + if (!NS_IsMainThread()) { + MOZ_CRASH("Figure out security checks for workers!"); + } + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - // We need to get this information before we push a null principal to avoid - // IsCallerChrome() assertion in quota manager. - nsCString group; - nsCString origin; - StoragePrivilege privilege; - PersistenceType defaultPersistenceType; - QuotaManager::GetInfoForChrome(&group, &origin, &privilege, - &defaultPersistenceType); - - nsCOMPtr principal = - do_CreateInstance("@mozilla.org/nullprincipal;1"); - NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE); - - AutoSafeJSContext cx; - - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - NS_ASSERTION(xpc, "This should never be null!"); - - nsCOMPtr globalHolder; - nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder)); - NS_ENSURE_SUCCESS(rv, rv); - - JS::Rooted global(cx, globalHolder->GetJSObject()); - NS_ENSURE_STATE(global); - - // The CreateSandbox call returns a proxy to the actual sandbox object. We - // don't need a proxy here. - global = js::UncheckedUnwrap(global); - - JSAutoCompartment ac(cx, global); - - nsRefPtr factory = new IDBFactory(); - factory->mGroup = group; - factory->mASCIIOrigin = origin; - factory->mPrivilege = privilege; - factory->mDefaultPersistenceType = defaultPersistenceType; - factory->mOwningObject = global; - factory->mContentParent = aContentParent; - - mozilla::HoldJSObjects(factory.get()); - factory->mRootedOwningObject = true; - - factory.forget(aFactory); - return NS_OK; + return OpenInternal(aPrincipal, + aName, + Optional(aVersion), + Optional(), + /* aDeleting */ false, + aRv); } -// static -already_AddRefed -IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin) +already_AddRefed +IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) { - nsCOMPtr uri; - nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); - NS_ENSURE_SUCCESS(rv, nullptr); + MOZ_ASSERT(aPrincipal); + if (!NS_IsMainThread()) { + MOZ_CRASH("Figure out security checks for workers!"); + } + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - nsCOMPtr fileUrl = do_QueryInterface(uri); - NS_ASSERTION(fileUrl, "This should always succeed!"); - - nsAutoCString type; - PersistenceTypeToText(aPersistenceType, type); - - rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + - NS_LITERAL_CSTRING("&group=") + aGroup + - NS_LITERAL_CSTRING("&origin=") + aOrigin); - NS_ENSURE_SUCCESS(rv, nullptr); - - return fileUrl.forget(); + return OpenInternal(aPrincipal, + aName, + aOptions.mVersion, + aOptions.mStorage, + /* aDeleting */ false, + aRv); } -// static -already_AddRefed -IDBFactory::GetConnection(const nsAString& aDatabaseFilePath, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin) +already_AddRefed +IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv) { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")), - "Bad file path!"); + MOZ_ASSERT(aPrincipal); + if (!NS_IsMainThread()) { + MOZ_CRASH("Figure out security checks for workers!"); + } + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - nsCOMPtr dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); - NS_ENSURE_TRUE(dbFile, nullptr); - - nsresult rv = dbFile->InitWithPath(aDatabaseFilePath); - NS_ENSURE_SUCCESS(rv, nullptr); - - bool exists; - rv = dbFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, nullptr); - NS_ENSURE_TRUE(exists, nullptr); - - nsCOMPtr dbFileUrl = - GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin); - NS_ENSURE_TRUE(dbFileUrl, nullptr); - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(ss, nullptr); - - nsCOMPtr connection; - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - NS_ENSURE_SUCCESS(rv, nullptr); - - rv = SetDefaultPragmas(connection); - NS_ENSURE_SUCCESS(rv, nullptr); - - return connection.forget(); + return OpenInternal(aPrincipal, + aName, + Optional(), + aOptions.mStorage, + /* aDeleting */ true, + aRv); } -// static -nsresult -IDBFactory::SetDefaultPragmas(mozIStorageConnection* aConnection) +already_AddRefed +IDBFactory::OpenInternal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const Optional& aVersion, + const Optional& aStorageType, + bool aDeleting, + ErrorResult& aRv) { - NS_ASSERTION(aConnection, "Null connection!"); + MOZ_ASSERT(mWindow || mOwningObject); + MOZ_ASSERT_IF(!mWindow, !mPrivateBrowsingMode); - static const char query[] = -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - // Switch the journaling mode to TRUNCATE to avoid changing the directory - // structure at the conclusion of every transaction for devices with slower - // file systems. - "PRAGMA journal_mode = TRUNCATE; " -#endif - // We use foreign keys in lots of places. - "PRAGMA foreign_keys = ON; " - // The "INSERT OR REPLACE" statement doesn't fire the update trigger, - // instead it fires only the insert trigger. This confuses the update - // refcount function. This behavior changes with enabled recursive triggers, - // so the statement fires the delete trigger first and then the insert - // trigger. - "PRAGMA recursive_triggers = ON;"; + CommonFactoryRequestParams commonParams; + commonParams.privateBrowsingMode() = mPrivateBrowsingMode; - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(query)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + PrincipalInfo& principalInfo = commonParams.principalInfo(); - return NS_OK; -} - -inline -bool -IgnoreWhitespace(char16_t c) -{ - return false; -} - -// static -nsresult -IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection, - const nsACString& aDatabaseId, - uint64_t* aVersion, - ObjectStoreInfoArray& aObjectStores) -{ - AssertIsOnIOThread(); - NS_ASSERTION(aConnection, "Null pointer!"); - - aObjectStores.Clear(); - - // Load object store names and ids. - nsCOMPtr stmt; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, id, key_path, auto_increment " - "FROM object_store" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoTArray infoMap; - - bool hasResult; - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - nsRefPtr* element = - aObjectStores.AppendElement(new ObjectStoreInfo()); - - ObjectStoreInfo* info = element->get(); - - rv = stmt->GetString(0, info->name); - NS_ENSURE_SUCCESS(rv, rv); - - info->id = stmt->AsInt64(1); - - int32_t columnType; - nsresult rv = stmt->GetTypeOfIndex(2, &columnType); - NS_ENSURE_SUCCESS(rv, rv); - - // NB: We don't have to handle the NULL case, since that is the default - // for a new KeyPath. - if (columnType != mozIStorageStatement::VALUE_TYPE_NULL) { - NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT, - "Should be a string"); - nsString keyPathSerialization; - rv = stmt->GetString(2, keyPathSerialization); - NS_ENSURE_SUCCESS(rv, rv); - - info->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); + if (aPrincipal) { + if (!NS_IsMainThread()) { + MOZ_CRASH("Figure out security checks for workers!"); } + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); - info->nextAutoIncrementId = stmt->AsInt64(3); - info->comittedAutoIncrementId = info->nextAutoIncrementId; - - info->autoIncrement = !!info->nextAutoIncrementId; - - ObjectStoreInfoMap* mapEntry = infoMap.AppendElement(); - NS_ENSURE_TRUE(mapEntry, NS_ERROR_OUT_OF_MEMORY); - - mapEntry->id = info->id; - mapEntry->info = info; + if (NS_WARN_IF(NS_FAILED(GetPrincipalInfoFromPrincipal(aPrincipal, + &principalInfo)))) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + } else { + principalInfo = *mPrincipalInfo; } - NS_ENSURE_SUCCESS(rv, rv); - // Load index information - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT object_store_id, id, name, key_path, unique_index, multientry " - "FROM object_store_index" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); + uint64_t version = 0; + if (!aDeleting && aVersion.WasPassed()) { + if (aVersion.Value() < 1) { + aRv.ThrowTypeError(MSG_INVALID_VERSION); + return nullptr; + } + version = aVersion.Value(); + } - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - int64_t objectStoreId = stmt->AsInt64(0); + // Nothing can be done here if we have previously failed to create a + // background actor. + if (mBackgroundActorFailed) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } - ObjectStoreInfo* objectStoreInfo = nullptr; - for (uint32_t index = 0; index < infoMap.Length(); index++) { - if (infoMap[index].id == objectStoreId) { - objectStoreInfo = infoMap[index].info; - break; + // XXX We need a bug to switch to temporary storage by default. + + PersistenceType persistenceType; + bool persistenceTypeIsExplicit; + + if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { + // Chrome privilege always gets persistent storage. + persistenceType = PERSISTENCE_TYPE_PERSISTENT; + persistenceTypeIsExplicit = false; + } else { + persistenceType = + PersistenceTypeFromStorage(aStorageType, PERSISTENCE_TYPE_PERSISTENT); + persistenceTypeIsExplicit = aStorageType.WasPassed(); + } + + DatabaseMetadata& metadata = commonParams.metadata(); + metadata.name() = aName; + metadata.persistenceType() = persistenceType; + metadata.persistenceTypeIsExplicit() = persistenceTypeIsExplicit; + + FactoryRequestParams params; + if (aDeleting) { + metadata.version() = 0; + params = DeleteDatabaseRequestParams(commonParams); + } else { + metadata.version() = version; + params = OpenDatabaseRequestParams(commonParams); + } + + if (!mBackgroundActor) { + // If another consumer has already created a background actor for this + // thread then we can start this request immediately. + if (PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread()) { + nsresult rv = BackgroundActorCreated(bgActor); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + MOZ_ASSERT(mBackgroundActor); + } else if (mPendingRequests.IsEmpty()) { + // We need to start the sequence to create a background actor for this + // thread. + nsRefPtr cb = + new BackgroundCreateCallback(this); + if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } } + } - if (!objectStoreInfo) { - NS_ERROR("Index for nonexistant object store!"); - return NS_ERROR_UNEXPECTED; + AutoJSAPI autoJS; + nsRefPtr request; + + if (mWindow) { + if (NS_WARN_IF(!autoJS.Init(mWindow))) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; } - IndexInfo* indexInfo = objectStoreInfo->indexes.AppendElement(); - NS_ENSURE_TRUE(indexInfo, NS_ERROR_OUT_OF_MEMORY); + JS::Rooted scriptOwner(autoJS.cx(), + static_cast(mWindow.get())->FastGetGlobalJSObject()); + MOZ_ASSERT(scriptOwner); - indexInfo->id = stmt->AsInt64(1); + request = IDBOpenDBRequest::CreateForWindow(this, mWindow, scriptOwner); + } else { + autoJS.Init(); + JS::Rooted scriptOwner(autoJS.cx(), mOwningObject); - rv = stmt->GetString(2, indexInfo->name); - NS_ENSURE_SUCCESS(rv, rv); - - nsString keyPathSerialization; - rv = stmt->GetString(3, keyPathSerialization); - NS_ENSURE_SUCCESS(rv, rv); - - // XXX bent wants to assert here - indexInfo->keyPath = KeyPath::DeserializeFromString(keyPathSerialization); - indexInfo->unique = !!stmt->AsInt32(4); - indexInfo->multiEntry = !!stmt->AsInt32(5); - } - NS_ENSURE_SUCCESS(rv, rv); - - // Load version information. - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT version " - "FROM database" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - - if (!hasResult) { - NS_ERROR("Database has no version!"); - return NS_ERROR_UNEXPECTED; + request = IDBOpenDBRequest::CreateForJS(this, scriptOwner); } - int64_t version = 0; - rv = stmt->GetInt64(0, &version); + MOZ_ASSERT(request); - *aVersion = std::max(version, 0); + // If we already have a background actor then we can start this request now. + if (mBackgroundActor) { + nsresult rv = InitiateRequest(request, params); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return nullptr; + } + } else { + mPendingRequests.AppendElement(new PendingRequestInfo(request, params)); + } + +#ifdef IDB_PROFILER_USE_MARKS + { + NS_ConvertUTF16toUTF8 profilerName(aName); + if (aDeleting) { + IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", + "MT IDBFactory.deleteDatabase()", + request->GetSerialNumber(), profilerName.get()); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", + "MT IDBFactory.open()", + request->GetSerialNumber(), profilerName.get(), + aVersion); + } + } +#endif + + return request.forget(); +} + +nsresult +IDBFactory::BackgroundActorCreated(PBackgroundChild* aBackgroundActor) +{ + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!mBackgroundActor); + MOZ_ASSERT(!mBackgroundActorFailed); + + { + BackgroundFactoryChild* actor = new BackgroundFactoryChild(this); + + MOZ_ASSERT(NS_IsMainThread(), "Fix this windowId stuff for workers!"); + + OptionalWindowId windowId; + if (mWindow && IndexedDatabaseManager::IsMainProcess()) { + MOZ_ASSERT(mWindow->IsInnerWindow()); + windowId = mWindow->WindowID(); + } else { + windowId = void_t(); + } + + mBackgroundActor = + static_cast( + aBackgroundActor->SendPBackgroundIDBFactoryConstructor(actor, + windowId)); + } + + if (NS_WARN_IF(!mBackgroundActor)) { + BackgroundActorFailed(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + nsresult rv = NS_OK; + + for (uint32_t index = 0, count = mPendingRequests.Length(); + index < count; + index++) { + nsAutoPtr info(mPendingRequests[index].forget()); + + nsresult rv2 = InitiateRequest(info->mRequest, info->mParams); + + // Warn for every failure, but just return the first failure if there are + // multiple failures. + if (NS_WARN_IF(NS_FAILED(rv2)) && NS_SUCCEEDED(rv)) { + rv = rv2; + } + } + + mPendingRequests.Clear(); return rv; } -// static -nsresult -IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - uint64_t aVersion, - ObjectStoreInfoArray& aObjectStores) +void +IDBFactory::BackgroundActorFailed() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aDatabaseInfo, "Null pointer!"); + MOZ_ASSERT(!mPendingRequests.IsEmpty()); + MOZ_ASSERT(!mBackgroundActor); + MOZ_ASSERT(!mBackgroundActorFailed); - ObjectStoreInfoArray objectStores; - objectStores.SwapElements(aObjectStores); + mBackgroundActorFailed = true; -#ifdef DEBUG - { - nsTArray existingNames; - aDatabaseInfo->GetObjectStoreNames(existingNames); - NS_ASSERTION(existingNames.IsEmpty(), "Should be an empty DatabaseInfo"); + for (uint32_t index = 0, count = mPendingRequests.Length(); + index < count; + index++) { + nsAutoPtr info(mPendingRequests[index].forget()); + info->mRequest-> + DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } -#endif - aDatabaseInfo->version = aVersion; + mPendingRequests.Clear(); +} - for (uint32_t index = 0; index < objectStores.Length(); index++) { - nsRefPtr& info = objectStores[index]; +nsresult +IDBFactory::InitiateRequest(IDBOpenDBRequest* aRequest, + const FactoryRequestParams& aParams) +{ + MOZ_ASSERT(aRequest); + MOZ_ASSERT(mBackgroundActor); + MOZ_ASSERT(!mBackgroundActorFailed); - if (!aDatabaseInfo->PutObjectStore(info)) { - NS_WARNING("Out of memory!"); - return NS_ERROR_OUT_OF_MEMORY; + bool deleting; + uint64_t requestedVersion; + PersistenceType persistenceType; + + switch (aParams.type()) { + case FactoryRequestParams::TDeleteDatabaseRequestParams: { + const DatabaseMetadata& metadata = + aParams.get_DeleteDatabaseRequestParams().commonParams().metadata(); + deleting = true; + requestedVersion = metadata.version(); + persistenceType = metadata.persistenceType(); + break; } + + case FactoryRequestParams::TOpenDatabaseRequestParams: { + const DatabaseMetadata& metadata = + aParams.get_OpenDatabaseRequestParams().commonParams().metadata(); + deleting = false; + requestedVersion = metadata.version(); + persistenceType = metadata.persistenceType(); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + auto actor = + new BackgroundFactoryRequestChild(this, aRequest, deleting, + requestedVersion, persistenceType); + + if (!mBackgroundActor->SendPBackgroundIDBFactoryRequestConstructor(actor, + aParams)) { + aRequest->DispatchNonTransactionError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } return NS_OK; @@ -575,257 +735,38 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject) NS_IMPL_CYCLE_COLLECTION_TRACE_END -nsresult -IDBFactory::OpenInternal(const nsAString& aName, - int64_t aVersion, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - StoragePrivilege aPrivilege, - bool aDeleting, - IDBOpenDBRequest** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); - - AutoJSContext cx; - nsCOMPtr window; - JS::Rooted scriptOwner(cx); - - if (mWindow) { - window = mWindow; - scriptOwner = - static_cast(window.get())->FastGetGlobalJSObject(); - } - else { - scriptOwner = mOwningObject; - } - - if (aPrivilege == Chrome) { - // Chrome privilege, ignore the persistence type parameter. - aPersistenceType = PERSISTENCE_TYPE_PERSISTENT; - } - - nsRefPtr request = - IDBOpenDBRequest::Create(this, window, scriptOwner); - IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsresult rv; - - if (IndexedDatabaseManager::IsMainProcess()) { - nsRefPtr openHelper = - new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion, - aPersistenceType, aDeleting, mContentParent, - aPrivilege); - - rv = openHelper->Init(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) { - openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - rv = openHelper->WaitForOpenAllowed(); - } - else { - if (mPrivilege != Chrome && - aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) { - nsRefPtr permissionHelper = - new CheckPermissionsHelper(openHelper, window); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - rv = quotaManager-> - WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin), - Nullable(aPersistenceType), - openHelper->Id(), permissionHelper); - } - else { - // Chrome and temporary storage doesn't need to check the permission. - rv = openHelper->WaitForOpenAllowed(); - } - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else if (aDeleting) { - nsCString databaseId; - QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, Client::IDB, - aName, databaseId); - MOZ_ASSERT(!databaseId.IsEmpty()); - - IndexedDBDeleteDatabaseRequestChild* actor = - new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId); - - mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor( - actor, - nsString(aName), - aPersistenceType); - } - else { - IndexedDBDatabaseChild* dbActor = - static_cast( - mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName), - aVersion, - aPersistenceType)); - - dbActor->SetRequest(request); - } - -#ifdef IDB_PROFILER_USE_MARKS - { - NS_ConvertUTF16toUTF8 profilerName(aName); - if (aDeleting) { - IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")", - "MT IDBFactory.deleteDatabase()", - request->GetSerialNumber(), profilerName.get()); - } - else { - IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)", - "MT IDBFactory.open()", - request->GetSerialNumber(), profilerName.get(), - aVersion); - } - } -#endif - - request.forget(_retval); - return NS_OK; -} - JSObject* IDBFactory::WrapObject(JSContext* aCx) { return IDBFactoryBinding::Wrap(aCx, this); } -already_AddRefed -IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) +NS_IMPL_ISUPPORTS(IDBFactory::BackgroundCreateCallback, + nsIIPCBackgroundChildCreateCallback) + +void +IDBFactory::BackgroundCreateCallback::ActorCreated(PBackgroundChild* aActor) { - return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv); + MOZ_ASSERT(aActor); + MOZ_ASSERT(mFactory); + + nsRefPtr factory; + mFactory.swap(factory); + + factory->BackgroundActorCreated(aActor); } -already_AddRefed -IDBFactory::DeleteDatabase(const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) +void +IDBFactory::BackgroundCreateCallback::ActorFailed() { - return Open(nullptr, aName, Optional(), aOptions.mStorage, true, - aRv); + MOZ_ASSERT(mFactory); + + nsRefPtr factory; + mFactory.swap(factory); + + factory->BackgroundActorFailed(); } -int16_t -IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, - JS::Handle aSecond, ErrorResult& aRv) -{ - Key first, second; - nsresult rv = first.SetFromJSVal(aCx, aFirst); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return 0; - } - - rv = second.SetFromJSVal(aCx, aSecond); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return 0; - } - - if (first.IsUnset() || second.IsUnset()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); - return 0; - } - - return Key::CompareKeys(first, second); -} - -already_AddRefed -IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - uint64_t aVersion, ErrorResult& aRv) -{ - // Just to be on the extra-safe side - if (!nsContentUtils::IsCallerChrome()) { - MOZ_CRASH(); - } - - return Open(aPrincipal, aName, Optional(aVersion), - Optional(), false, aRv); -} - -already_AddRefed -IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - const IDBOpenDBOptions& aOptions, ErrorResult& aRv) -{ - // Just to be on the extra-safe side - if (!nsContentUtils::IsCallerChrome()) { - MOZ_CRASH(); - } - - return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false, - aRv); -} - -already_AddRefed -IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - const IDBOpenDBOptions& aOptions, - ErrorResult& aRv) -{ - // Just to be on the extra-safe side - if (!nsContentUtils::IsCallerChrome()) { - MOZ_CRASH(); - } - - return Open(aPrincipal, aName, Optional(), aOptions.mStorage, true, - aRv); -} - -already_AddRefed -IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName, - const Optional& aVersion, - const Optional& aStorageType, - bool aDelete, ErrorResult& aRv) -{ - nsresult rv; - - nsCString group; - nsCString origin; - StoragePrivilege privilege; - PersistenceType defaultPersistenceType; - if (aPrincipal) { - rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin, - &privilege, - &defaultPersistenceType); - if (NS_FAILED(rv)) { - IDB_REPORT_INTERNAL_ERR(); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - } - else { - group = mGroup; - origin = mASCIIOrigin; - privilege = mPrivilege; - defaultPersistenceType = mDefaultPersistenceType; - } - - uint64_t version = 0; - if (!aDelete && aVersion.WasPassed()) { - if (aVersion.Value() < 1) { - aRv.ThrowTypeError(MSG_INVALID_VERSION); - return nullptr; - } - version = aVersion.Value(); - } - - PersistenceType persistenceType = - PersistenceTypeFromStorage(aStorageType, defaultPersistenceType); - - nsRefPtr request; - rv = OpenInternal(aName, version, persistenceType, group, origin, privilege, - aDelete, getter_AddRefs(request)); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - - return request.forget(); -} +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 284df8113268..df3292dd78ec 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -7,219 +7,203 @@ #ifndef mozilla_dom_indexeddb_idbfactory_h__ #define mozilla_dom_indexeddb_idbfactory_h__ -#include "mozilla/dom/BindingDeclarations.h" // for Optional +#include "mozilla/Attributes.h" #include "mozilla/dom/StorageTypeBinding.h" -#include "mozilla/dom/quota/PersistenceType.h" -#include "mozilla/dom/quota/StoragePrivilege.h" +#include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" +#include "nsISupports.h" +#include "nsString.h" +#include "nsTArray.h" #include "nsWrapperCache.h" -class mozIStorageConnection; -class nsIFile; -class nsIFileURL; class nsIPrincipal; class nsPIDOMWindow; -template class nsRefPtr; +struct PRThread; namespace mozilla { + class ErrorResult; +namespace ipc { + +class PBackgroundChild; +class PrincipalInfo; + +} // namespace ipc + namespace dom { -class nsIContentParent; + struct IDBOpenDBOptions; +class TabChild; namespace indexedDB { -struct DatabaseInfo; -class IDBDatabase; +class BackgroundFactoryChild; +class FactoryRequestParams; class IDBOpenDBRequest; -class IndexedDBChild; -class IndexedDBParent; -struct ObjectStoreInfo; - -class IDBFactory MOZ_FINAL : public nsISupports, - public nsWrapperCache +class IDBFactory MOZ_FINAL + : public nsISupports + , public nsWrapperCache { - typedef mozilla::dom::nsIContentParent nsIContentParent; - typedef mozilla::dom::quota::PersistenceType PersistenceType; - typedef nsTArray > ObjectStoreInfoArray; - typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; + typedef mozilla::dom::StorageType StorageType; + typedef mozilla::ipc::PBackgroundChild PBackgroundChild; + typedef mozilla::ipc::PrincipalInfo PrincipalInfo; -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) + class BackgroundCreateCallback; + struct PendingRequestInfo; - // Called when using IndexedDB from a window in a different process. - static nsresult Create(nsPIDOMWindow* aWindow, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - nsIContentParent* aContentParent, - IDBFactory** aFactory); - - // Called when using IndexedDB from a window in the current process. - static nsresult Create(nsPIDOMWindow* aWindow, - nsIContentParent* aContentParent, - IDBFactory** aFactory) - { - return Create(aWindow, EmptyCString(), EmptyCString(), aContentParent, - aFactory); - } - - // Called when using IndexedDB from a JS component or a JSM in the current - // process. - static nsresult Create(JSContext* aCx, - JS::Handle aOwningObject, - nsIContentParent* aContentParent, - IDBFactory** aFactory); - - // Called when using IndexedDB from a JS component or a JSM in a different - // process or from a C++ component. - static nsresult Create(nsIContentParent* aContentParent, - IDBFactory** aFactory); - - static already_AddRefed - GetDatabaseFileURL(nsIFile* aDatabaseFile, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin); - - static already_AddRefed - GetConnection(const nsAString& aDatabaseFilePath, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin); - - static nsresult - SetDefaultPragmas(mozIStorageConnection* aConnection); - - static nsresult - LoadDatabaseInformation(mozIStorageConnection* aConnection, - const nsACString& aDatabaseId, - uint64_t* aVersion, - ObjectStoreInfoArray& aObjectStores); - - static nsresult - SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, - uint64_t aVersion, - ObjectStoreInfoArray& aObjectStores); - - nsresult - OpenInternal(const nsAString& aName, - int64_t aVersion, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - StoragePrivilege aStoragePrivilege, - bool aDeleting, - IDBOpenDBRequest** _retval); - - nsresult - OpenInternal(const nsAString& aName, - int64_t aVersion, - PersistenceType aPersistenceType, - bool aDeleting, - IDBOpenDBRequest** _retval) - { - return OpenInternal(aName, aVersion, aPersistenceType, mGroup, mASCIIOrigin, - mPrivilege, aDeleting, _retval); - } - - void - SetActor(IndexedDBChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } - - void - SetActor(IndexedDBParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - const nsCString& - GetASCIIOrigin() const - { - return mASCIIOrigin; - } - - bool - FromIPC() - { - return !!mContentParent; - } - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - nsPIDOMWindow* - GetParentObject() const - { - return mWindow; - } - - already_AddRefed - Open(const nsAString& aName, uint64_t aVersion, ErrorResult& aRv) - { - return Open(nullptr, aName, Optional(aVersion), - Optional(), false, aRv); - } - - already_AddRefed - Open(const nsAString& aName, const IDBOpenDBOptions& aOptions, - ErrorResult& aRv); - - already_AddRefed - DeleteDatabase(const nsAString& aName, const IDBOpenDBOptions& aOptions, - ErrorResult& aRv); - - int16_t - Cmp(JSContext* aCx, JS::Handle aFirst, - JS::Handle aSecond, ErrorResult& aRv); - - already_AddRefed - OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - uint64_t aVersion, ErrorResult& aRv); - - already_AddRefed - OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - const IDBOpenDBOptions& aOptions, ErrorResult& aRv); - - already_AddRefed - DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName, - const IDBOpenDBOptions& aOptions, ErrorResult& aRv); - -private: - IDBFactory(); - ~IDBFactory(); - - already_AddRefed - Open(nsIPrincipal* aPrincipal, const nsAString& aName, - const Optional& aVersion, - const Optional& aStorageType, bool aDelete, - ErrorResult& aRv); - - nsCString mGroup; - nsCString mASCIIOrigin; - StoragePrivilege mPrivilege; - PersistenceType mDefaultPersistenceType; + nsAutoPtr mPrincipalInfo; // If this factory lives on a window then mWindow must be non-null. Otherwise // mOwningObject must be non-null. nsCOMPtr mWindow; JS::Heap mOwningObject; - IndexedDBChild* mActorChild; - IndexedDBParent* mActorParent; + // This will only be set if the factory belongs to a window in a child + // process. + nsRefPtr mTabChild; - mozilla::dom::nsIContentParent* mContentParent; + nsTArray> mPendingRequests; + + BackgroundFactoryChild* mBackgroundActor; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif bool mRootedOwningObject; + bool mBackgroundActorFailed; + bool mPrivateBrowsingMode; + +public: + static nsresult + CreateForWindow(nsPIDOMWindow* aWindow, + IDBFactory** aFactory); + + static nsresult + CreateForChromeJS(JSContext* aCx, + JS::Handle aOwningObject, + IDBFactory** aFactory); + + static nsresult + CreateForDatastore(JSContext* aCx, + JS::Handle aOwningObject, + IDBFactory** aFactory); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor); + + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } + + nsPIDOMWindow* + GetParentObject() const + { + return mWindow; + } + + TabChild* + GetTabChild() const + { + return mTabChild; + } + + PrincipalInfo* + GetPrincipalInfo() const + { + AssertIsOnOwningThread(); + + return mPrincipalInfo; + } + + already_AddRefed + Open(const nsAString& aName, + uint64_t aVersion, + ErrorResult& aRv); + + already_AddRefed + Open(const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv); + + already_AddRefed + DeleteDatabase(const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv); + + int16_t + Cmp(JSContext* aCx, + JS::Handle aFirst, + JS::Handle aSecond, + ErrorResult& aRv); + + already_AddRefed + OpenForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + uint64_t aVersion, + ErrorResult& aRv); + + already_AddRefed + OpenForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv); + + already_AddRefed + DeleteForPrincipal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const IDBOpenDBOptions& aOptions, + ErrorResult& aRv); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + +private: + IDBFactory(); + ~IDBFactory(); + + static nsresult + CreateForJSInternal(JSContext* aCx, + JS::Handle aOwningObject, + nsAutoPtr& aPrincipalInfo, + IDBFactory** aFactory); + + already_AddRefed + OpenInternal(nsIPrincipal* aPrincipal, + const nsAString& aName, + const Optional& aVersion, + const Optional& aStorageType, + bool aDeleting, + ErrorResult& aRv); + + nsresult + BackgroundActorCreated(PBackgroundChild* aBackgroundActor); + + void + BackgroundActorFailed(); + + nsresult + InitiateRequest(IDBOpenDBRequest* aRequest, + const FactoryRequestParams& aParams); }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index 336aa2f007bd..eb043e829a1a 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -16,16 +16,16 @@ #include "nsServiceManagerUtils.h" #include "nsWidgetsCID.h" +namespace mozilla { +namespace dom { +namespace indexedDB { + namespace { NS_DEFINE_CID(kAppShellCID2, NS_APPSHELL_CID); } // anonymous namespace -namespace mozilla { -namespace dom { -namespace indexedDB { - IDBFileHandle::IDBFileHandle(FileMode aMode, RequestMode aRequestMode, IDBMutableFile* aMutableFile) @@ -158,10 +158,10 @@ IDBFileHandle::OnCompleteOrAbort(bool aAborted) { nsCOMPtr event; if (aAborted) { - event = CreateGenericEvent(this, NS_LITERAL_STRING(ABORT_EVT_STR), + event = CreateGenericEvent(this, nsDependentString(kAbortEventType), eDoesBubble, eNotCancelable); } else { - event = CreateGenericEvent(this, NS_LITERAL_STRING(COMPLETE_EVT_STR), + event = CreateGenericEvent(this, nsDependentString(kCompleteEventType), eDoesNotBubble, eNotCancelable); } if (NS_WARN_IF(!event)) { diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index a2a3e154a59d..1d0cda770337 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -4,432 +4,226 @@ * 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 "base/basictypes.h" - #include "IDBIndex.h" -#include -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/ipc/Blob.h" -#include "mozilla/storage.h" -#include "nsThreadUtils.h" -#include "xpcpublic.h" - -#include "AsyncConnectionHelper.h" -#include "DatabaseInfo.h" +#include "FileInfo.h" #include "IDBCursor.h" #include "IDBEvents.h" #include "IDBKeyRange.h" #include "IDBObjectStore.h" +#include "IDBRequest.h" #include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "ipc/IndexedDBChild.h" -#include "ipc/IndexedDBParent.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#include "IndexedDatabaseInlines.h" - -USING_INDEXEDDB_NAMESPACE -using namespace mozilla::dom; -using namespace mozilla::dom::indexedDB::ipc; -using mozilla::ErrorResult; -using mozilla::Move; +namespace mozilla { +namespace dom { +namespace indexedDB { namespace { -class IndexHelper : public AsyncConnectionHelper -{ -public: - IndexHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex) - : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex), - mActor(nullptr) - { - NS_ASSERTION(aTransaction, "Null transaction!"); - NS_ASSERTION(aRequest, "Null request!"); - NS_ASSERTION(aIndex, "Null index!"); - } - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) = 0; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; - -protected: - nsRefPtr mIndex; - -private: - IndexedDBIndexRequestChild* mActor; -}; - -class GetKeyHelper : public IndexHelper -{ -public: - GetKeyHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange) - : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - // In-params. - nsRefPtr mKeyRange; - - // Out-params. - Key mKey; -}; - -class GetHelper : public GetKeyHelper -{ -public: - GetHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange) - : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange) - { } - - ~GetHelper() - { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - StructuredCloneReadInfo mCloneReadInfo; -}; - -class GetAllKeysHelper : public GetKeyHelper -{ -public: - GetAllKeysHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange, - const uint32_t aLimit) - : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - const uint32_t mLimit; - nsTArray mKeys; -}; - -class GetAllHelper : public GetKeyHelper -{ -public: - GetAllHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange, - const uint32_t aLimit) - : GetKeyHelper(aTransaction, aRequest, aIndex, aKeyRange), mLimit(aLimit) - { } - - ~GetAllHelper() - { - for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); - } - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - const uint32_t mLimit; - nsTArray mCloneReadInfos; -}; - -class OpenKeyCursorHelper : public IndexHelper -{ -public: - OpenKeyCursorHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange, - IDBCursor::Direction aDirection) - : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), - mDirection(aDirection) - { } - - ~OpenKeyCursorHelper() - { - NS_ASSERTION(true, "bas"); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - virtual nsresult EnsureCursor(); - - // In-params. - nsRefPtr mKeyRange; - const IDBCursor::Direction mDirection; - - // Out-params. - Key mKey; - Key mObjectKey; - nsCString mContinueQuery; - nsCString mContinueToQuery; - Key mRangeKey; - - // Only used in the parent process. - nsRefPtr mCursor; -}; - -class OpenCursorHelper : public OpenKeyCursorHelper -{ -public: - OpenCursorHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange, - IDBCursor::Direction aDirection) - : OpenKeyCursorHelper(aTransaction, aRequest, aIndex, aKeyRange, aDirection) - { } - - ~OpenCursorHelper() - { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - -private: - virtual nsresult EnsureCursor(); - - StructuredCloneReadInfo mCloneReadInfo; - - // Only used in the parent process. - SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; -}; - -class CountHelper : public IndexHelper -{ -public: - CountHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBIndex* aIndex, - IDBKeyRange* aKeyRange) - : IndexHelper(aTransaction, aRequest, aIndex), mKeyRange(aKeyRange), mCount(0) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - nsRefPtr mKeyRange; - uint64_t mCount; -}; - -inline already_AddRefed GenerateRequest(IDBIndex* aIndex) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + IDBTransaction* transaction = aIndex->ObjectStore()->Transaction(); - IDBDatabase* database = transaction->Database(); - return IDBRequest::Create(aIndex, database, transaction); + + nsRefPtr request = + IDBRequest::Create(aIndex, transaction->Database(), transaction); + MOZ_ASSERT(request); + + return request.forget(); } } // anonymous namespace -// static -already_AddRefed -IDBIndex::Create(IDBObjectStore* aObjectStore, - const IndexInfo* aIndexInfo, - bool aCreating) +IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata) + : mObjectStore(aObjectStore) + , mCachedKeyPath(JSVAL_VOID) + , mMetadata(aMetadata) + , mId(aMetadata->id()) + , mRooted(false) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aObjectStore, "Null pointer!"); - NS_ASSERTION(aIndexInfo, "Null pointer!"); - - nsRefPtr index = new IDBIndex(); - - index->mObjectStore = aObjectStore; - index->mId = aIndexInfo->id; - index->mName = aIndexInfo->name; - index->mKeyPath = aIndexInfo->keyPath; - index->mUnique = aIndexInfo->unique; - index->mMultiEntry = aIndexInfo->multiEntry; - - if (!IndexedDatabaseManager::IsMainProcess()) { - IndexedDBObjectStoreChild* objectStoreActor = aObjectStore->GetActorChild(); - NS_ASSERTION(objectStoreActor, "Must have an actor here!"); - - nsAutoPtr actor(new IndexedDBIndexChild(index)); - - IndexConstructorParams params; - - if (aCreating) { - CreateIndexParams createParams; - createParams.info() = *aIndexInfo; - params = createParams; - } - else { - GetIndexParams getParams; - getParams.name() = aIndexInfo->name; - params = getParams; - } - - objectStoreActor->SendPIndexedDBIndexConstructor(actor.forget(), params); - } - - return index.forget(); -} - -IDBIndex::IDBIndex() -: mId(INT64_MIN), - mKeyPath(0), - mCachedKeyPath(JSVAL_VOID), - mActorChild(nullptr), - mActorParent(nullptr), - mUnique(false), - mMultiEntry(false), - mRooted(false) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(aMetadata); SetIsDOMBinding(); } IDBIndex::~IDBIndex() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); + AssertIsOnOwningThread(); if (mRooted) { mCachedKeyPath = JSVAL_VOID; mozilla::DropJSObjects(this); } +} - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); +already_AddRefed +IDBIndex::Create(IDBObjectStore* aObjectStore, + const IndexMetadata& aMetadata) +{ + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + + nsRefPtr index = new IDBIndex(aObjectStore, &aMetadata); + + return index.forget(); +} + +#ifdef DEBUG + +void +IDBIndex::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mObjectStore); + mObjectStore->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +IDBIndex::RefreshMetadata(bool aMayDelete) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata); + + const nsTArray& indexes = mObjectStore->Spec().indexes(); + + bool found = false; + + for (uint32_t count = indexes.Length(), index = 0; + index < count; + index++) { + const IndexMetadata& metadata = indexes[index]; + + if (metadata.id() == Id()) { + mMetadata = &metadata; + + found = true; + break; + } + } + + MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found); + + if (found) { + MOZ_ASSERT(mMetadata != mDeletedMetadata); + mDeletedMetadata = nullptr; + } else { + NoteDeletion(); } } -already_AddRefed -IDBIndex::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) +void +IDBIndex::NoteDeletion() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + MOZ_ASSERT(Id() == mMetadata->id()); + + if (mDeletedMetadata) { + MOZ_ASSERT(mMetadata == mDeletedMetadata); + return; + } + + mDeletedMetadata = new IndexMetadata(*mMetadata); + + mMetadata = mDeletedMetadata; +} + +const nsString& +IDBIndex::Name() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->name(); +} + +bool +IDBIndex::Unique() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->unique(); +} + +bool +IDBIndex::MultiEntry() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->multiEntry(); +} + +const KeyPath& +IDBIndex::GetKeyPath() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->keyPath(); +} + +nsPIDOMWindow* +IDBIndex::GetParentObject() const +{ + AssertIsOnOwningThread(); + + return mObjectStore->GetParentObject(); +} + +void +IDBIndex::GetKeyPath(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + if (!mCachedKeyPath.isUndefined()) { + MOZ_ASSERT(mRooted); + JS::ExposeValueToActiveJS(mCachedKeyPath); + aResult.set(mCachedKeyPath); + return; + } + + MOZ_ASSERT(!mRooted); + + aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (mCachedKeyPath.isGCThing()) { + mozilla::HoldJSObjects(this); + mRooted = true; + } + + JS::ExposeValueToActiveJS(mCachedKeyPath); + aResult.set(mCachedKeyPath); +} + +already_AddRefed +IDBIndex::GetInternal(bool aKeyOnly, + JSContext* aCx, + JS::Handle aKey, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -437,84 +231,79 @@ IDBIndex::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) return nullptr; } + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + if (!keyRange) { + // Must specify a key or keyRange for get() and getKey(). + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); + return nullptr; + } + + const int64_t objectStoreId = mObjectStore->Id(); + const int64_t indexId = Id(); + + OptionalKeyRange optionalKeyRange; + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + optionalKeyRange = serializedKeyRange; + } else { + optionalKeyRange = void_t(); + } + + RequestParams params; + + if (aKeyOnly) { + params = IndexGetKeyParams(objectStoreId, indexId, optionalKeyRange); + } else { + params = IndexGetParams(objectStoreId, indexId, optionalKeyRange); + } + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; + MOZ_ASSERT(request); + + BackgroundRequestChild* actor = new BackgroundRequestChild(request); + + transaction->StartRequest(actor, params); + + if (aKeyOnly) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getKey(%s)", + "IDBRequest[%llu] MT IDBIndex.getKey()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(transaction->Database()), + IDB_PROFILER_STRING(transaction), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKey)); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "get(%s)", + "IDBRequest[%llu] MT IDBIndex.get()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(transaction->Database()), + IDB_PROFILER_STRING(transaction), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKey)); } - - nsRefPtr helper = - new GetHelper(transaction, request, this, aKeyRange); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "get(%s)", - "IDBRequest[%llu] MT IDBIndex.get()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - return request.forget(); } already_AddRefed -IDBIndex::GetKeyInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new GetKeyHelper(transaction, request, this, aKeyRange); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getKey(%s)", - "IDBRequest[%llu] MT IDBIndex.getKey()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - - return request.forget(); -} - -already_AddRefed -IDBIndex::GetAllInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, +IDBIndex::GetAllInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -522,43 +311,78 @@ IDBIndex::GetAllInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, return nullptr; } + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + const int64_t objectStoreId = mObjectStore->Id(); + const int64_t indexId = Id(); + + OptionalKeyRange optionalKeyRange; + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + optionalKeyRange = serializedKeyRange; + } else { + optionalKeyRange = void_t(); + } + + const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; + + RequestParams params; + if (aKeysOnly) { + params = IndexGetAllKeysParams(objectStoreId, indexId, optionalKeyRange, + limit); + } else { + params = IndexGetAllParams(objectStoreId, indexId, optionalKeyRange, limit); + } + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; + MOZ_ASSERT(request); + + BackgroundRequestChild* actor = new BackgroundRequestChild(request); + + transaction->StartRequest(actor, params); + + if (aKeysOnly) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAllKeys(%s, %lu)", + "IDBRequest[%llu] MT IDBIndex.getAllKeys()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(transaction->Database()), + IDB_PROFILER_STRING(transaction), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKeyRange), + aLimit); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAll(%s, %lu)", + "IDBRequest[%llu] MT IDBIndex.getAll()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(transaction->Database()), + IDB_PROFILER_STRING(transaction), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKeyRange), + aLimit); } - nsRefPtr helper = - new GetAllHelper(transaction, request, this, aKeyRange, aLimit); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getAll(%s, %lu)", - "IDBRequest[%llu] MT IDBIndex.getAll()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), - IDB_PROFILER_STRING(aKeyRange), aLimit); - return request.forget(); } already_AddRefed -IDBIndex::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, +IDBIndex::OpenCursorInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -566,42 +390,64 @@ IDBIndex::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, return nullptr; } + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + int64_t objectStoreId = mObjectStore->Id(); + int64_t indexId = Id(); + + OptionalKeyRange optionalKeyRange; + + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + + optionalKeyRange = Move(serializedKeyRange); + } else { + optionalKeyRange = void_t(); + } + + IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); + + OpenCursorParams params; + if (aKeysOnly) { + IndexOpenKeyCursorParams openParams; + openParams.objectStoreId() = objectStoreId; + openParams.indexId() = indexId; + openParams.optionalKeyRange() = Move(optionalKeyRange); + openParams.direction() = direction; + + params = Move(openParams); + } else { + IndexOpenCursorParams openParams; + openParams.objectStoreId() = objectStoreId; + openParams.indexId() = indexId; + openParams.optionalKeyRange() = Move(optionalKeyRange); + openParams.direction() = direction; + + params = Move(openParams); + } + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + MOZ_ASSERT(request); - nsRefPtr helper = - new GetAllKeysHelper(transaction, request, this, aKeyRange, aLimit); + BackgroundCursorChild* actor = + new BackgroundCursorChild(request, this, direction); - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "getAllKeys(%s, %lu)", - "IDBRequest[%llu] MT IDBIndex.getAllKeys()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - aLimit); + mObjectStore->Transaction()->OpenCursor(actor, params); return request.forget(); } already_AddRefed -IDBIndex::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) +IDBIndex::Count(JSContext* aCx, + JS::Handle aKey, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); IDBTransaction* transaction = mObjectStore->Transaction(); if (!transaction->IsOpen()) { @@ -609,183 +455,52 @@ IDBIndex::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) return nullptr; } + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + if (aRv.Failed()) { + return nullptr; + } + + IndexCountParams params; + params.objectStoreId() = mObjectStore->Id(); + params.indexId() = Id(); + + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + params.optionalKeyRange() = serializedKeyRange; + } else { + params.optionalKeyRange() = void_t(); + } + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + MOZ_ASSERT(request); - nsRefPtr helper = - new CountHelper(transaction, request, this, aKeyRange); + BackgroundRequestChild* actor = new BackgroundRequestChild(request); - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + transaction->StartRequest(actor, params); IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).index(%s)." "count(%s)", - "IDBRequest[%llu] MT IDBIndex.count()", + "IDBRequest[%llu] MT IDBObjectStore.count()", request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + IDB_PROFILER_STRING(transaction->Database()), + IDB_PROFILER_STRING(transaction), + IDB_PROFILER_STRING(mObjectStore), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKey)); return request.forget(); } -already_AddRefed -IDBIndex::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, - ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - IDBCursor::Direction direction = - static_cast(aDirection); - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new OpenKeyCursorHelper(transaction, request, this, aKeyRange, direction); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "openKeyCursor(%s)", - "IDBRequest[%llu] MT IDBIndex.openKeyCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - - return request.forget(); -} - -nsresult -IDBIndex::OpenCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, - IDBRequest** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; - } - - IDBCursor::Direction direction = - static_cast(aDirection); - - nsRefPtr request = GenerateRequest(this); - IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsRefPtr helper = - new OpenCursorHelper(transaction, request, this, aKeyRange, direction); - - nsresult rv = helper->DispatchToTransactionPool(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).index(%s)." - "openCursor(%s)", - "IDBRequest[%llu] MT IDBIndex.openCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(ObjectStore()->Transaction()-> - Database()), - IDB_PROFILER_STRING(ObjectStore()->Transaction()), - IDB_PROFILER_STRING(ObjectStore()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - - request.forget(_retval); - return NS_OK; -} - -nsresult -IDBIndex::OpenCursorFromChildProcess(IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const Key& aObjectKey, - IDBCursor** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBCursor::Direction direction = - static_cast(aDirection); - - nsRefPtr cursor = - IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, - Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - cursor.forget(_retval); - return NS_OK; -} - -nsresult -IDBIndex::OpenCursorFromChildProcess( - IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const Key& aObjectKey, - const SerializedStructuredCloneReadInfo& aCloneInfo, - nsTArray& aBlobs, - IDBCursor** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) || - (aCloneInfo.dataLength && aCloneInfo.data), - "Inconsistent clone info!"); - - IDBCursor::Direction direction = - static_cast(aDirection); - - StructuredCloneReadInfo cloneInfo; - - if (!cloneInfo.SetFromSerialized(aCloneInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - cloneInfo.mFiles.SwapElements(aBlobs); - - nsRefPtr cursor = - IDBCursor::Create(aRequest, mObjectStore->Transaction(), this, direction, - Key(), EmptyCString(), EmptyCString(), aKey, aObjectKey, - Move(cloneInfo)); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!"); - - cursor.forget(_retval); - return NS_OK; -} +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) @@ -812,1765 +527,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) } NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex) -NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) - JSObject* IDBIndex::WrapObject(JSContext* aCx) { return IDBIndexBinding::Wrap(aCx, this); } -void -IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, - ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!mCachedKeyPath.isUndefined()) { - JS::ExposeValueToActiveJS(mCachedKeyPath); - aResult.set(mCachedKeyPath); - return; - } - - aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - if (mCachedKeyPath.isGCThing()) { - mozilla::HoldJSObjects(this); - mRooted = true; - } - - JS::ExposeValueToActiveJS(mCachedKeyPath); - aResult.set(mCachedKeyPath); -} - -already_AddRefed -IDBIndex::Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - if (!keyRange) { - // Must specify a key or keyRange for getKey(). - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); - return nullptr; - } - - return GetInternal(keyRange, aRv); -} - -already_AddRefed -IDBIndex::GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - if (!keyRange) { - // Must specify a key or keyRange for get(). - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR); - return nullptr; - } - - return GetKeyInternal(keyRange, aRv); -} - -already_AddRefed -IDBIndex::GetAll(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - uint32_t limit = UINT32_MAX; - if (aLimit.WasPassed() && aLimit.Value() > 0) { - limit = aLimit.Value(); - } - - return GetAllInternal(keyRange, limit, aRv); -} - -already_AddRefed -IDBIndex::GetAllKeys(JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - uint32_t limit = UINT32_MAX; - if (aLimit.WasPassed() && aLimit.Value() > 0) { - limit = aLimit.Value(); - } - - return GetAllKeysInternal(keyRange, limit, aRv); -} - -already_AddRefed -IDBIndex::OpenCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new OpenCursorHelper(transaction, request, this, keyRange, direction); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - return request.forget(); -} - -already_AddRefed -IDBIndex::OpenKeyCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - - return OpenKeyCursorInternal(keyRange, direction, aRv); -} - -already_AddRefed -IDBIndex::Count(JSContext* aCx, JS::Handle aKey, - ErrorResult& aRv) -{ - IDBTransaction* transaction = mObjectStore->Transaction(); - if (!transaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - return CountInternal(keyRange, aRv); -} - -void -IndexHelper::ReleaseMainThreadObjects() -{ - mIndex = nullptr; - AsyncConnectionHelper::ReleaseMainThreadObjects(); -} - -nsresult -IndexHelper::Dispatch(nsIEventTarget* aDatabaseThread) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("IndexHelper", "Dispatch", - js::ProfileEntry::Category::STORAGE); - - if (IndexedDatabaseManager::IsMainProcess()) { - return AsyncConnectionHelper::Dispatch(aDatabaseThread); - } - - // If we've been invalidated then there's no point sending anything to the - // parent process. - if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IndexedDBIndexChild* indexActor = mIndex->GetActorChild(); - NS_ASSERTION(indexActor, "Must have an actor here!"); - - IndexRequestParams params; - nsresult rv = PackArgumentsForParentProcess(params); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NoDispatchEventTarget target; - rv = AsyncConnectionHelper::Dispatch(&target); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mActor = new IndexedDBIndexRequestChild(this, mIndex, params.type()); - indexActor->SendPIndexedDBRequestConstructor(mActor, params); - - return NS_OK; -} - -nsresult -GetKeyHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "Must have a key range here!"); - - PROFILER_LABEL("GetKeyHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); - } - else { - indexTable.AssignLiteral("index_data"); - } - - nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); - - NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - - nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + - indexTable + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + - keyRangeClause + - NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (hasResult) { - rv = mKey.SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -GetKeyHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - return mKey.ToJSVal(aCx, aVal); -} - -void -GetKeyHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - IndexHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetKeyHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "This should never be null!"); - - PROFILER_MAIN_THREAD_LABEL("GetKeyHelper", "PackArgumentsForParentProcess", - js::ProfileEntry::Category::STORAGE); - - GetKeyParams params; - - mKeyRange->ToSerializedKeyRange(params.keyRange()); - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetKeyHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetKeyHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - GetKeyResponse getKeyResponse; - getKeyResponse.key() = mKey; - response = getKeyResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetKeyHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetKeyResponse, - "Bad response type!"); - - mKey = aResponseValue.get_GetKeyResponse().key(); - return NS_OK; -} - -nsresult -GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "Must have a key range here!"); - - PROFILER_LABEL("GetHelper", "DoDatabaseWork [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); - } - else { - indexTable.AssignLiteral("index_data"); - } - - nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); - - NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "INNER JOIN ") + indexTable + - NS_LITERAL_CSTRING(" AS index_table ON object_data.id = ") + - NS_LITERAL_CSTRING("index_table.object_data_id WHERE " - "index_id = :index_id") + - keyRangeClause + - NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (hasResult) { - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, - mDatabase, mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -GetHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal); - - mCloneReadInfo.mCloneBuffer.clear(); - - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - return NS_OK; -} - -void -GetHelper::ReleaseMainThreadObjects() -{ - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - GetKeyHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "This should never be null!"); - - PROFILER_MAIN_THREAD_LABEL("GetHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetParams params; - - mKeyRange->ToSerializedKeyRange(params.keyRange()); - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetHelper", "SendResponseToChildProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - InfallibleTArray blobsParent; - - if (NS_SUCCEEDED(aResultCode)) { - IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - const nsTArray& files = mCloneReadInfo.mFiles; - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobsParent); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobActors failed!"); - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - GetResponse getResponse; - getResponse.cloneInfo() = mCloneReadInfo; - getResponse.blobsParent().SwapElements(blobsParent); - response = getResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse, - "Bad response type!"); - - const GetResponse& getResponse = aResponseValue.get_GetResponse(); - const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo(); - - NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || - (cloneInfo.dataLength && cloneInfo.data), - "Inconsistent clone info!"); - - if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(), - mCloneReadInfo.mFiles); - return NS_OK; -} - -nsresult -GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - PROFILER_LABEL("GetAllKeysHelper", "DoDatabaseWork [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString tableName; - if (mIndex->IsUnique()) { - tableName.AssignLiteral("unique_index_data"); - } - else { - tableName.AssignLiteral("index_data"); - } - - nsCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); - } - - nsCString limitClause; - if (mLimit != UINT32_MAX) { - limitClause = NS_LITERAL_CSTRING(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = NS_LITERAL_CSTRING("SELECT object_data_key FROM ") + - tableName + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + - keyRangeClause + limitClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - mKeys.SetCapacity(std::min(50, mLimit)); - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - if (mKeys.Capacity() == mKeys.Length()) { - mKeys.SetCapacity(mKeys.Capacity() * 2); - } - - Key* key = mKeys.AppendElement(); - NS_ASSERTION(key, "This shouldn't fail!"); - - rv = key->SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -GetAllKeysHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mKeys.Length() <= mLimit); - - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "GetSuccessResult [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsTArray keys; - mKeys.SwapElements(keys); - - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (!array) { - IDB_WARNING("Failed to make array!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!keys.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) { - IDB_WARNING("Failed to set array length!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t index = 0, count = keys.Length(); index < count; index++) { - const Key& key = keys[index]; - NS_ASSERTION(!key.IsUnset(), "Bad key!"); - - JS::Rooted value(aCx); - nsresult rv = key.ToJSVal(aCx, &value); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to get jsval for key!"); - return rv; - } - - if (!JS_SetElement(aCx, array, index, value)) { - IDB_WARNING("Failed to set array element!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - } - - aVal.setObject(*array); - return NS_OK; -} - -nsresult -GetAllKeysHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetAllKeysParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.limit() = mLimit; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "SendResponseToChildProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - GetAllKeysResponse getAllKeysResponse; - getAllKeysResponse.keys().AppendElements(mKeys); - response = getAllKeysResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetAllKeysHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse); - - mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys()); - return NS_OK; -} - -nsresult -GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("GetAllHelper", "DoDatabaseWork [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); - } - else { - indexTable.AssignLiteral("index_data"); - } - - nsCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause); - } - - nsCString limitClause; - if (mLimit != UINT32_MAX) { - limitClause = NS_LITERAL_CSTRING(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "INNER JOIN ") + indexTable + - NS_LITERAL_CSTRING(" AS index_table ON object_data.id = " - "index_table.object_data_id " - "WHERE index_id = :index_id") + - keyRangeClause + limitClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - mCloneReadInfos.SetCapacity(50); - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) { - mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2); - } - - StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement(); - NS_ASSERTION(readInfo, "This shouldn't fail!"); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, - mDatabase, *readInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -GetAllHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!"); - - nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal); - - NS_ASSERTION(mCloneReadInfos.IsEmpty(), - "Should have cleared in ConvertToArrayAndCleanup"); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -GetAllHelper::ReleaseMainThreadObjects() -{ - for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); - } - GetKeyHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetAllHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetAllParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.limit() = mLimit; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetAllHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "SendResponseToChildProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - GetAllResponse getAllResponse; - - if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { - IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - uint32_t length = mCloneReadInfos.Length(); - - InfallibleTArray& infos = - getAllResponse.cloneInfos(); - infos.SetCapacity(length); - - InfallibleTArray& blobArrays = getAllResponse.blobs(); - blobArrays.SetCapacity(length); - - for (uint32_t index = 0; - NS_SUCCEEDED(aResultCode) && index < length; - index++) { - const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; - - // Append the structured clone data. - SerializedStructuredCloneReadInfo* info = infos.AppendElement(); - *info = clone; - - const nsTArray& files = clone.mFiles; - - // Now take care of the files. - BlobArray* blobArray = blobArrays.AppendElement(); - - InfallibleTArray& blobs = blobArray->blobsParent(); - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobs); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - break; - } - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - response = getAllResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetAllHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse, - "Bad response type!"); - - const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse(); - const InfallibleTArray& cloneInfos = - getAllResponse.cloneInfos(); - const InfallibleTArray& blobArrays = getAllResponse.blobs(); - - mCloneReadInfos.SetCapacity(cloneInfos.Length()); - - for (uint32_t index = 0; index < cloneInfos.Length(); index++) { - const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index]; - const InfallibleTArray& blobs = blobArrays[index].blobsChild(); - - StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement(); - if (!destInfo->SetFromSerialized(srcInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles); - } - - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aConnection, "Passed a null connection!"); - - PROFILER_LABEL("OpenKeyCursorHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCString table; - if (mIndex->IsUnique()) { - table.AssignLiteral("unique_index_data"); - } - else { - table.AssignLiteral("index_data"); - } - - NS_NAMED_LITERAL_CSTRING(value, "value"); - - nsCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(value, keyRangeClause); - } - - nsAutoCString directionClause(" ORDER BY value "); - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause += NS_LITERAL_CSTRING("ASC, object_data_key ASC"); - break; - - case IDBCursor::PREV: - directionClause += NS_LITERAL_CSTRING("DESC, object_data_key DESC"); - break; - - case IDBCursor::PREV_UNIQUE: - directionClause += NS_LITERAL_CSTRING("DESC, object_data_key ASC"); - break; - - default: - NS_NOTREACHED("Unknown direction!"); - } - nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, object_data_key " - "FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :index_id") + - keyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement(firstQuery); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - mKey.Unset(); - return NS_OK; - } - - rv = mKey.SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mObjectKey.SetFromStatement(stmt, 1); - NS_ENSURE_SUCCESS(rv, rv); - - // Now we need to make the query to get the next match. - nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT value, object_data_key" - " FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :id"); - - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mDirection) { - case IDBCursor::NEXT: - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), - queryStart); - mRangeKey = mKeyRange->Upper(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key AND " - "( value > :current_key OR " - " object_data_key > :object_key )") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value >= :current_key ") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - break; - - case IDBCursor::NEXT_UNIQUE: - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), - queryStart); - mRangeKey = mKeyRange->Upper(); - } - mContinueQuery = - queryStart + NS_LITERAL_CSTRING(" AND value > :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + NS_LITERAL_CSTRING(" AND value >= :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - break; - - case IDBCursor::PREV: - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), - queryStart); - mRangeKey = mKeyRange->Lower(); - } - - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key AND " - "( value < :current_key OR " - " object_data_key < :object_key )") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key ") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - break; - - case IDBCursor::PREV_UNIQUE: - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), - queryStart); - mRangeKey = mKeyRange->Lower(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value < :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND value <= :current_key") + - directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - break; - - default: - NS_NOTREACHED("Unknown direction type!"); - } - - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::EnsureCursor() -{ - if (mCursor || mKey.IsUnset()) { - return NS_OK; - } - - nsRefPtr cursor = - IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, - mContinueQuery, mContinueToQuery, mKey, mObjectKey); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mCursor.swap(cursor); - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - nsresult rv = EnsureCursor(); - NS_ENSURE_SUCCESS(rv, rv); - - if (mCursor) { - rv = WrapNative(aCx, mCursor, aVal); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - aVal.setUndefined(); - } - - return NS_OK; -} - -void -OpenKeyCursorHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - mCursor = nullptr; - IndexHelper::ReleaseMainThreadObjects(); -} - -nsresult -OpenKeyCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - OpenKeyCursorParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.direction() = mDirection; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - if (NS_SUCCEEDED(aResultCode)) { - nsresult rv = EnsureCursor(); - if (NS_FAILED(rv)) { - NS_WARNING("EnsureCursor failed!"); - aResultCode = rv; - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - OpenCursorResponse openCursorResponse; - - if (!mCursor) { - openCursorResponse = mozilla::void_t(); - } - else { - IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); - NS_ASSERTION(indexActor, "Must have an actor here!"); - - IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); - NS_ASSERTION(requestActor, "Must have an actor here!"); - - IndexCursorConstructorParams params; - params.requestParent() = requestActor; - params.direction() = mDirection; - params.key() = mKey; - params.objectKey() = mObjectKey; - params.optionalCloneInfo() = mozilla::void_t(); - - if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { - return Error; - } - } - - response = openCursorResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -OpenKeyCursorHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse, - "Bad response type!"); - NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::Tvoid_t || - aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::TPIndexedDBCursorChild, - "Bad response union type!"); - NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); - - const OpenCursorResponse& response = - aResponseValue.get_OpenCursorResponse(); - - switch (response.type()) { - case OpenCursorResponse::Tvoid_t: - break; - - case OpenCursorResponse::TPIndexedDBCursorChild: { - IndexedDBCursorChild* actor = - static_cast( - response.get_PIndexedDBCursorChild()); - - mCursor = actor->ForgetStrongCursor(); - NS_ASSERTION(mCursor, "This should never be null!"); - - } break; - - default: - MOZ_CRASH(); - } - - return NS_OK; -} - -nsresult -OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aConnection, "Passed a null connection!"); - - PROFILER_LABEL("OpenCursorHelper", "DoDatabaseWork [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString indexTable; - if (mIndex->IsUnique()) { - indexTable.AssignLiteral("unique_index_data"); - } - else { - indexTable.AssignLiteral("index_data"); - } - - NS_NAMED_LITERAL_CSTRING(value, "index_table.value"); - - nsCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(value, keyRangeClause); - } - - nsAutoCString directionClause(" ORDER BY index_table.value "); - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause += - NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC"); - break; - - case IDBCursor::PREV: - directionClause += - NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC"); - break; - - case IDBCursor::PREV_UNIQUE: - directionClause += - NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC"); - break; - - default: - NS_NOTREACHED("Unknown direction!"); - } - - nsCString firstQuery = - NS_LITERAL_CSTRING("SELECT index_table.value, " - "index_table.object_data_key, object_data.data, " - "object_data.file_ids FROM ") + - indexTable + - NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " - "index_table.object_data_id = object_data.id " - "WHERE index_table.index_id = :id") + - keyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement(firstQuery); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - mKey.Unset(); - return NS_OK; - } - - rv = mKey.SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mObjectKey.SetFromStatement(stmt, 1); - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3, - mDatabase, mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - - // Now we need to make the query to get the next match. - nsAutoCString queryStart = - NS_LITERAL_CSTRING("SELECT index_table.value, " - "index_table.object_data_key, object_data.data, " - "object_data.file_ids FROM ") + - indexTable + - NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON " - "index_table.object_data_id = object_data.id " - "WHERE index_table.index_id = :id"); - - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - NS_NAMED_LITERAL_CSTRING(limit, " LIMIT "); - - switch (mDirection) { - case IDBCursor::NEXT: - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), - queryStart); - mRangeKey = mKeyRange->Upper(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND " - "( index_table.value > :current_key OR " - " index_table.object_data_key > :object_key ) ") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + limit; - break; - - case IDBCursor::NEXT_UNIQUE: - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(), - queryStart); - mRangeKey = mKeyRange->Upper(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value > :current_key") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") + - directionClause + limit; - break; - - case IDBCursor::PREV: - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), - queryStart); - mRangeKey = mKeyRange->Lower(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND " - "( index_table.value < :current_key OR " - " index_table.object_data_key < :object_key ) ") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + limit; - break; - - case IDBCursor::PREV_UNIQUE: - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(), - queryStart); - mRangeKey = mKeyRange->Lower(); - } - mContinueQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value < :current_key") + - directionClause + limit; - mContinueToQuery = - queryStart + - NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") + - directionClause + limit; - break; - - default: - NS_NOTREACHED("Unknown direction type!"); - } - - return NS_OK; -} - -nsresult -OpenCursorHelper::EnsureCursor() -{ - if (mCursor || mKey.IsUnset()) { - return NS_OK; - } - - mSerializedCloneReadInfo = mCloneReadInfo; - - NS_ASSERTION(mSerializedCloneReadInfo.data && - mSerializedCloneReadInfo.dataLength, - "Shouldn't be possible!"); - - nsRefPtr cursor = - IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey, - mContinueQuery, mContinueToQuery, mKey, mObjectKey, - Move(mCloneReadInfo)); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!"); - - mCursor.swap(cursor); - return NS_OK; -} - -void -OpenCursorHelper::ReleaseMainThreadObjects() -{ - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - - // These don't need to be released on the main thread but they're only valid - // as long as mCursor is set. - mSerializedCloneReadInfo.data = nullptr; - mSerializedCloneReadInfo.dataLength = 0; - - OpenKeyCursorHelper::ReleaseMainThreadObjects(); -} - -nsresult -OpenCursorHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - OpenCursorParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.direction() = mDirection; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); - - PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "SendResponseToChildProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - InfallibleTArray blobsParent; - - if (NS_SUCCEEDED(aResultCode)) { - IDBDatabase* database = mIndex->ObjectStore()->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - const nsTArray& files = mCloneReadInfo.mFiles; - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobsParent); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - } - } - - if (NS_SUCCEEDED(aResultCode)) { - nsresult rv = EnsureCursor(); - if (NS_FAILED(rv)) { - NS_WARNING("EnsureCursor failed!"); - aResultCode = rv; - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - OpenCursorResponse openCursorResponse; - - if (!mCursor) { - openCursorResponse = mozilla::void_t(); - } - else { - IndexedDBIndexParent* indexActor = mIndex->GetActorParent(); - NS_ASSERTION(indexActor, "Must have an actor here!"); - - IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); - NS_ASSERTION(requestActor, "Must have an actor here!"); - - NS_ASSERTION(mSerializedCloneReadInfo.data && - mSerializedCloneReadInfo.dataLength, - "Shouldn't be possible!"); - - IndexCursorConstructorParams params; - params.requestParent() = requestActor; - params.direction() = mDirection; - params.key() = mKey; - params.objectKey() = mObjectKey; - params.optionalCloneInfo() = mSerializedCloneReadInfo; - params.blobsParent().SwapElements(blobsParent); - - if (!indexActor->OpenCursor(mCursor, params, openCursorResponse)) { - return Error; - } - } - - response = openCursorResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("CountHelper", "DoDatabaseWork [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString table; - if (mIndex->IsUnique()) { - table.AssignLiteral("unique_index_data"); - } - else { - table.AssignLiteral("index_data"); - } - - NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); - NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); - NS_NAMED_LITERAL_CSTRING(value, "value"); - - nsAutoCString keyRangeClause; - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - AppendConditionClause(value, lowerKeyName, false, - !mKeyRange->IsLowerOpen(), keyRangeClause); - } - if (!mKeyRange->Upper().IsUnset()) { - AppendConditionClause(value, upperKeyName, true, - !mKeyRange->IsUpperOpen(), keyRangeClause); - } - } - - nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM ") + table + - NS_LITERAL_CSTRING(" WHERE index_id = :id") + - keyRangeClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - if (!mKeyRange->Upper().IsUnset()) { - rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mCount = stmt->AsInt64(0); - return NS_OK; -} - -nsresult -CountHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - aVal.setNumber(static_cast(mCount)); - return NS_OK; -} - -void -CountHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - IndexHelper::ReleaseMainThreadObjects(); -} - -nsresult -CountHelper::PackArgumentsForParentProcess(IndexRequestParams& aParams) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("CountHelper", "PackArgumentsForParentProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - CountParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -CountHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("CountHelper", "SendResponseToChildProcess [IDBIndex.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - CountResponse countResponse = mCount; - response = countResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -CountHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse, - "Bad response type!"); - - mCount = aResponseValue.get_CountResponse().count(); - return NS_OK; -} +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index 806f4ec93e9a..8b40e1fd16fa 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -7,245 +7,207 @@ #ifndef mozilla_dom_indexeddb_idbindex_h__ #define mozilla_dom_indexeddb_idbindex_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - +#include "js/RootingAPI.h" #include "mozilla/Attributes.h" #include "mozilla/dom/IDBCursorBinding.h" -#include "mozilla/ErrorResult.h" +#include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" +#include "nsISupports.h" +#include "nsTArrayForwardDeclare.h" #include "nsWrapperCache.h" -#include "mozilla/dom/indexedDB/IDBObjectStore.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/KeyPath.h" - -class nsIScriptContext; class nsPIDOMWindow; -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { + +class ErrorResult; + +namespace dom { + +template class Sequence; + +namespace indexedDB { -class AsyncConnectionHelper; -class IDBCursor; -class IDBKeyRange; class IDBObjectStore; class IDBRequest; -class IndexedDBIndexChild; -class IndexedDBIndexParent; +class IndexMetadata; class Key; +class KeyPath; -struct IndexInfo; - -class IDBIndex MOZ_FINAL : public nsISupports, - public nsWrapperCache +class IDBIndex MOZ_FINAL + : public nsISupports + , public nsWrapperCache { + nsRefPtr mObjectStore; + + JS::Heap mCachedKeyPath; + + // This normally points to the IndexMetadata owned by the parent IDBDatabase + // object. However, if this index is part of a versionchange transaction and + // it gets deleted then the metadata is copied into mDeletedMetadata and + // mMetadata is set to point at mDeletedMetadata. + const IndexMetadata* mMetadata; + nsAutoPtr mDeletedMetadata; + + const int64_t mId; + bool mRooted; + public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBIndex) static already_AddRefed - Create(IDBObjectStore* aObjectStore, - const IndexInfo* aIndexInfo, - bool aCreating); + Create(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); - IDBObjectStore* ObjectStore() + int64_t + Id() const { - return mObjectStore; - } + AssertIsOnOwningThread(); - const int64_t Id() const - { return mId; } - const nsString& Name() const - { - return mName; - } + const nsString& + Name() const; - bool IsUnique() const - { - return mUnique; - } + bool + Unique() const; - bool IsMultiEntry() const - { - return mMultiEntry; - } + bool + MultiEntry() const; - const KeyPath& GetKeyPath() const - { - return mKeyPath; - } - - void - SetActor(IndexedDBIndexChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } - - void - SetActor(IndexedDBIndexParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBIndexChild* - GetActorChild() const - { - return mActorChild; - } - - IndexedDBIndexParent* - GetActorParent() const - { - return mActorParent; - } - - already_AddRefed - GetInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - already_AddRefed - GetKeyInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - already_AddRefed - GetAllInternal(IDBKeyRange* aKeyRange, - uint32_t aLimit, - ErrorResult& aRv); - - already_AddRefed - GetAllKeysInternal(IDBKeyRange* aKeyRange, - uint32_t aLimit, - ErrorResult& aRv); - - already_AddRefed - CountInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - nsresult OpenCursorFromChildProcess( - IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const Key& aObjectKey, - IDBCursor** _retval); - - already_AddRefed - OpenKeyCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, - ErrorResult& aRv); - - nsresult OpenCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, - IDBRequest** _retval); - - nsresult OpenCursorFromChildProcess( - IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const Key& aObjectKey, - const SerializedStructuredCloneReadInfo& aCloneInfo, - nsTArray& aBlobs, - IDBCursor** _retval); - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - IDBObjectStore* - GetParentObject() const - { - return mObjectStore; - } - - void - GetName(nsString& aName) const - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - aName.Assign(mName); - } + const KeyPath& + GetKeyPath() const; IDBObjectStore* ObjectStore() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); return mObjectStore; } + nsPIDOMWindow* + GetParentObject() const; + void - GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, + GetName(nsString& aName) const + { + aName = Name(); + } + + void + GetKeyPath(JSContext* aCx, + JS::MutableHandle aResult, ErrorResult& aRv); - bool - MultiEntry() const + already_AddRefed + OpenCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return mMultiEntry; - } + AssertIsOnOwningThread(); - bool - Unique() const - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return mUnique; + return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection, + aRv); } already_AddRefed - OpenCursor(JSContext* aCx, JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv); + OpenKeyCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, + aRv); + } already_AddRefed - OpenKeyCursor(JSContext* aCx, JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv); + Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv); + } already_AddRefed - Get(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) + { + AssertIsOnOwningThread(); - already_AddRefed - GetKey(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); + return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv); + } already_AddRefed Count(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); - void - GetStoreName(nsString& aStoreName) const + already_AddRefed + GetAll(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - mObjectStore->GetName(aStoreName); + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv); } - already_AddRefed - GetAll(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv); - already_AddRefed GetAllKeys(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv); + const Optional& aLimit, ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv); + } + + void + RefreshMetadata(bool aMayDelete); + + void + NoteDeletion(); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; private: - IDBIndex(); + IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata); + ~IDBIndex(); - nsRefPtr mObjectStore; + already_AddRefed + GetInternal(bool aKeyOnly, + JSContext* aCx, + JS::Handle aKey, + ErrorResult& aRv); - int64_t mId; - nsString mName; - KeyPath mKeyPath; - JS::Heap mCachedKeyPath; + already_AddRefed + GetAllInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv); - IndexedDBIndexChild* mActorChild; - IndexedDBIndexParent* mActorParent; - - bool mUnique; - bool mMultiEntry; - bool mRooted; + already_AddRefed + OpenCursorInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv); }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbindex_h__ diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index 7c69077a01a6..f7b2ed354891 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -4,30 +4,21 @@ * 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 "base/basictypes.h" - #include "IDBKeyRange.h" -#include "nsIXPConnect.h" - -#include "nsJSUtils.h" -#include "nsThreadUtils.h" -#include "nsContentUtils.h" -#include "nsDOMClassInfoID.h" #include "Key.h" - +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/IDBKeyRangeBinding.h" -#include "mozilla/dom/indexedDB/PIndexedDBIndex.h" -#include "mozilla/dom/indexedDB/PIndexedDBObjectStore.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" -using namespace mozilla; -using namespace mozilla::dom; -USING_INDEXEDDB_NAMESPACE -using namespace mozilla::dom::indexedDB::ipc; +namespace mozilla { +namespace dom { +namespace indexedDB { namespace { -inline nsresult +nsresult GetKeyFromJSVal(JSContext* aCx, JS::Handle aVal, Key& aKey, @@ -35,8 +26,7 @@ GetKeyFromJSVal(JSContext* aCx, { nsresult rv = aKey.SetFromJSVal(aCx, aVal); if (NS_FAILED(rv)) { - NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB, - "Bad error code!"); + MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB); return rv; } @@ -49,6 +39,42 @@ GetKeyFromJSVal(JSContext* aCx, } // anonymous namespace +IDBKeyRange::IDBKeyRange(nsISupports* aGlobal, + bool aLowerOpen, + bool aUpperOpen, + bool aIsOnly) + : mGlobal(aGlobal) + , mCachedLowerVal(JSVAL_VOID) + , mCachedUpperVal(JSVAL_VOID) + , mLowerOpen(aLowerOpen) + , mUpperOpen(aUpperOpen) + , mIsOnly(aIsOnly) + , mHaveCachedLowerVal(false) + , mHaveCachedUpperVal(false) + , mRooted(false) +{ +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + AssertIsOnOwningThread(); +} + +IDBKeyRange::~IDBKeyRange() +{ + DropJSObjects(); +} + +#ifdef DEBUG + +void +IDBKeyRange::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} + +#endif // DEBUG + // static nsresult IDBKeyRange::FromJSVal(JSContext* aCx, @@ -87,9 +113,8 @@ IDBKeyRange::FromJSVal(JSContext* aCx, } // static -template already_AddRefed -IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange) +IDBKeyRange::FromSerialized(const SerializedKeyRange& aKeyRange) { nsRefPtr keyRange = new IDBKeyRange(nullptr, aKeyRange.lowerOpen(), aKeyRange.upperOpen(), @@ -101,12 +126,11 @@ IDBKeyRange::FromSerializedKeyRange(const T& aKeyRange) return keyRange.forget(); } -template void -IDBKeyRange::ToSerializedKeyRange(T& aKeyRange) +IDBKeyRange::ToSerialized(SerializedKeyRange& aKeyRange) const { - aKeyRange.lowerOpen() = IsLowerOpen(); - aKeyRange.upperOpen() = IsUpperOpen(); + aKeyRange.lowerOpen() = LowerOpen(); + aKeyRange.upperOpen() = UpperOpen(); aKeyRange.isOnly() = IsOnly(); aKeyRange.lower() = Lower(); @@ -115,6 +139,76 @@ IDBKeyRange::ToSerializedKeyRange(T& aKeyRange) } } +void +IDBKeyRange::GetBindingClause(const nsACString& aKeyColumnName, + nsACString& _retval) const +{ + NS_NAMED_LITERAL_CSTRING(andStr, " AND "); + NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + + if (IsOnly()) { + // Both keys are set and they're equal. + _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + + spacecolon + lowerKey; + return; + } + + nsAutoCString clause; + + if (!Lower().IsUnset()) { + // Lower key is set. + clause.Append(andStr + aKeyColumnName); + clause.AppendLiteral(" >"); + if (!LowerOpen()) { + clause.Append('='); + } + clause.Append(spacecolon + lowerKey); + } + + if (!Upper().IsUnset()) { + // Upper key is set. + clause.Append(andStr + aKeyColumnName); + clause.AppendLiteral(" <"); + if (!UpperOpen()) { + clause.Append('='); + } + clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); + } + + _retval = clause; +} + +nsresult +IDBKeyRange::BindToStatement(mozIStorageStatement* aStatement) const +{ + MOZ_ASSERT(aStatement); + + NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + + if (IsOnly()) { + return Lower().BindToStatement(aStatement, lowerKey); + } + + nsresult rv; + + if (!Lower().IsUnset()) { + rv = Lower().BindToStatement(aStatement, lowerKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (!Upper().IsUnset()) { + rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange) @@ -145,19 +239,14 @@ IDBKeyRange::DropJSObjects() if (!mRooted) { return; } - mCachedLowerVal = JS::UndefinedValue(); - mCachedUpperVal = JS::UndefinedValue(); + mCachedLowerVal.setUndefined(); + mCachedUpperVal.setUndefined(); mHaveCachedLowerVal = false; mHaveCachedUpperVal = false; mRooted = false; mozilla::DropJSObjects(this); } -IDBKeyRange::~IDBKeyRange() -{ - DropJSObjects(); -} - JSObject* IDBKeyRange::WrapObject(JSContext* aCx) { @@ -168,7 +257,7 @@ void IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mHaveCachedLowerVal) { if (!mRooted) { @@ -192,7 +281,7 @@ void IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mHaveCachedUpperVal) { if (!mRooted) { @@ -215,10 +304,9 @@ IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle aResult, // static already_AddRefed IDBKeyRange::Only(const GlobalObject& aGlobal, - JS::Handle aValue, ErrorResult& aRv) + JS::Handle aValue, + ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), false, false, true); @@ -233,11 +321,10 @@ IDBKeyRange::Only(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::LowerBound(const GlobalObject& aGlobal, - JS::Handle aValue, bool aOpen, + JS::Handle aValue, + bool aOpen, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), aOpen, true, false); @@ -252,11 +339,10 @@ IDBKeyRange::LowerBound(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::UpperBound(const GlobalObject& aGlobal, - JS::Handle aValue, bool aOpen, + JS::Handle aValue, + bool aOpen, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), true, aOpen, false); @@ -271,11 +357,12 @@ IDBKeyRange::UpperBound(const GlobalObject& aGlobal, // static already_AddRefed IDBKeyRange::Bound(const GlobalObject& aGlobal, - JS::Handle aLower, JS::Handle aUpper, - bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv) + JS::Handle aLower, + JS::Handle aUpper, + bool aLowerOpen, + bool aUpperOpen, + ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr keyRange = new IDBKeyRange(aGlobal.GetAsSupports(), aLowerOpen, aUpperOpen, false); @@ -298,9 +385,6 @@ IDBKeyRange::Bound(const GlobalObject& aGlobal, return keyRange.forget(); } -// Explicitly instantiate for all our key range types... Grumble. -template already_AddRefed -IDBKeyRange::FromSerializedKeyRange (const KeyRange& aKeyRange); - -template void -IDBKeyRange::ToSerializedKeyRange (KeyRange& aKeyRange); +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBKeyRange.h b/dom/indexedDB/IDBKeyRange.h index 85ade2510917..ba307594d351 100644 --- a/dom/indexedDB/IDBKeyRange.h +++ b/dom/indexedDB/IDBKeyRange.h @@ -7,144 +7,137 @@ #ifndef mozilla_dom_indexeddb_idbkeyrange_h__ #define mozilla_dom_indexeddb_idbkeyrange_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "js/RootingAPI.h" +#include "js/Value.h" +#include "mozilla/Attributes.h" #include "mozilla/dom/indexedDB/Key.h" - -#include "nsISupports.h" - -#include "mozilla/ErrorResult.h" +#include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" +#include "nsISupports.h" +#include "nsString.h" class mozIStorageStatement; +struct PRThread; namespace mozilla { + +class ErrorResult; + namespace dom { + class GlobalObject; -} // namespace dom -} // namespace mozilla -BEGIN_INDEXEDDB_NAMESPACE +namespace indexedDB { -namespace ipc { -class KeyRange; -} // namespace ipc +class SerializedKeyRange; -class IDBKeyRange MOZ_FINAL : public nsISupports +class IDBKeyRange MOZ_FINAL + : public nsISupports { + nsCOMPtr mGlobal; + Key mLower; + Key mUpper; + JS::Heap mCachedLowerVal; + JS::Heap mCachedUpperVal; + + const bool mLowerOpen : 1; + const bool mUpperOpen : 1; + const bool mIsOnly : 1; + bool mHaveCachedLowerVal : 1; + bool mHaveCachedUpperVal : 1; + bool mRooted : 1; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange) - static nsresult FromJSVal(JSContext* aCx, - JS::Handle aVal, - IDBKeyRange** aKeyRange); + static nsresult + FromJSVal(JSContext* aCx, + JS::Handle aVal, + IDBKeyRange** aKeyRange); - template static already_AddRefed - FromSerializedKeyRange(const T& aKeyRange); + FromSerialized(const SerializedKeyRange& aKeyRange); - const Key& Lower() const + static already_AddRefed + Only(const GlobalObject& aGlobal, + JS::Handle aValue, + ErrorResult& aRv); + + static already_AddRefed + LowerBound(const GlobalObject& aGlobal, + JS::Handle aValue, + bool aOpen, + ErrorResult& aRv); + + static already_AddRefed + UpperBound(const GlobalObject& aGlobal, + JS::Handle aValue, + bool aOpen, + ErrorResult& aRv); + + static already_AddRefed + Bound(const GlobalObject& aGlobal, + JS::Handle aLower, + JS::Handle aUpper, + bool aLowerOpen, + bool aUpperOpen, + ErrorResult& aRv); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + ToSerialized(SerializedKeyRange& aKeyRange) const; + + const Key& + Lower() const { return mLower; } - Key& Lower() + Key& + Lower() { return mLower; } - const Key& Upper() const + const Key& + Upper() const { return mIsOnly ? mLower : mUpper; } - Key& Upper() + Key& + Upper() { return mIsOnly ? mLower : mUpper; } - // TODO: Remove these in favour of LowerOpen() / UpperOpen(), bug 900578. - bool IsLowerOpen() const - { - return mLowerOpen; - } - - bool IsUpperOpen() const - { - return mUpperOpen; - } - - bool IsOnly() const + bool + IsOnly() const { return mIsOnly; } - void GetBindingClause(const nsACString& aKeyColumnName, - nsACString& _retval) const - { - NS_NAMED_LITERAL_CSTRING(andStr, " AND "); - NS_NAMED_LITERAL_CSTRING(spacecolon, " :"); - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); + void + GetBindingClause(const nsACString& aKeyColumnName, + nsACString& _retval) const; - if (IsOnly()) { - // Both keys are set and they're equal. - _retval = andStr + aKeyColumnName + NS_LITERAL_CSTRING(" =") + - spacecolon + lowerKey; - } - else { - nsAutoCString clause; + nsresult + BindToStatement(mozIStorageStatement* aStatement) const; - if (!Lower().IsUnset()) { - // Lower key is set. - clause.Append(andStr + aKeyColumnName); - clause.AppendLiteral(" >"); - if (!IsLowerOpen()) { - clause.Append('='); - } - clause.Append(spacecolon + lowerKey); - } - - if (!Upper().IsUnset()) { - // Upper key is set. - clause.Append(andStr + aKeyColumnName); - clause.AppendLiteral(" <"); - if (!IsUpperOpen()) { - clause.Append('='); - } - clause.Append(spacecolon + NS_LITERAL_CSTRING("upper_key")); - } - - _retval = clause; - } - } - - nsresult BindToStatement(mozIStorageStatement* aStatement) const - { - NS_NAMED_LITERAL_CSTRING(lowerKey, "lower_key"); - - if (IsOnly()) { - return Lower().BindToStatement(aStatement, lowerKey); - } - - nsresult rv; - - if (!Lower().IsUnset()) { - rv = Lower().BindToStatement(aStatement, lowerKey); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - if (!Upper().IsUnset()) { - rv = Upper().BindToStatement(aStatement, NS_LITERAL_CSTRING("upper_key")); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - return NS_OK; - } - - template - void ToSerializedKeyRange(T& aKeyRange); - - void DropJSObjects(); + void + DropJSObjects(); // WebIDL JSObject* @@ -176,48 +169,17 @@ public: return mUpperOpen; } - static already_AddRefed - Only(const GlobalObject& aGlobal, - JS::Handle aValue, ErrorResult& aRv); - - static already_AddRefed - LowerBound(const GlobalObject& aGlobal, - JS::Handle aValue, bool aOpen, ErrorResult& aRv); - - static already_AddRefed - UpperBound(const GlobalObject& aGlobal, - JS::Handle aValue, bool aOpen, ErrorResult& aRv); - - static already_AddRefed - Bound(const GlobalObject& aGlobal, - JS::Handle aLower, JS::Handle aUpper, - bool aLowerOpen, bool aUpperOpen, ErrorResult& aRv); - private: IDBKeyRange(nsISupports* aGlobal, bool aLowerOpen, bool aUpperOpen, - bool aIsOnly) - : mGlobal(aGlobal), mCachedLowerVal(JSVAL_VOID), mCachedUpperVal(JSVAL_VOID), - mLowerOpen(aLowerOpen), mUpperOpen(aUpperOpen), mIsOnly(aIsOnly), - mHaveCachedLowerVal(false), mHaveCachedUpperVal(false), mRooted(false) - { } + bool aIsOnly); ~IDBKeyRange(); - - nsCOMPtr mGlobal; - Key mLower; - Key mUpper; - JS::Heap mCachedLowerVal; - JS::Heap mCachedUpperVal; - const bool mLowerOpen; - const bool mUpperOpen; - const bool mIsOnly; - bool mHaveCachedLowerVal; - bool mHaveCachedUpperVal; - bool mRooted; }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbkeyrange_h__ diff --git a/dom/indexedDB/IDBMutableFile.cpp b/dom/indexedDB/IDBMutableFile.cpp index 7b4f3f040779..038554f2dc67 100644 --- a/dom/indexedDB/IDBMutableFile.cpp +++ b/dom/indexedDB/IDBMutableFile.cpp @@ -6,26 +6,36 @@ #include "IDBMutableFile.h" -#include "nsIDOMFile.h" - +#include "FileSnapshot.h" +#include "FileInfo.h" +#include "IDBDatabase.h" +#include "IDBFactory.h" +#include "IDBFileHandle.h" +#include "IDBFileRequest.h" +#include "IndexedDatabaseManager.h" +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileService.h" #include "mozilla/dom/IDBMutableFileBinding.h" #include "mozilla/dom/MetadataHelper.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "mozilla/dom/quota/FileStreams.h" #include "mozilla/dom/quota/QuotaManager.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsError.h" +#include "nsIDOMFile.h" +#include "nsIPrincipal.h" -#include "FileSnapshot.h" -#include "IDBDatabase.h" -#include "IDBFileHandle.h" -#include "IDBFileRequest.h" +namespace mozilla { +namespace dom { +namespace indexedDB { -using namespace mozilla::dom; -USING_INDEXEDDB_NAMESPACE -USING_QUOTA_NAMESPACE +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; namespace { @@ -48,7 +58,6 @@ public: ReleaseObjects() MOZ_OVERRIDE { mMutableFile = nullptr; - MetadataHelper::ReleaseObjects(); } @@ -56,109 +65,213 @@ private: nsRefPtr mMutableFile; }; -inline already_AddRefed GetFileFor(FileInfo* aFileInfo) - { - FileManager* fileManager = aFileInfo->Manager(); - nsCOMPtr directory = fileManager->GetDirectory(); - NS_ENSURE_TRUE(directory, nullptr); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFileInfo); - nsCOMPtr file = fileManager->GetFileForId(directory, - aFileInfo->Id()); - NS_ENSURE_TRUE(file, nullptr); + FileManager* fileManager = aFileInfo->Manager(); + MOZ_ASSERT(fileManager); + + nsCOMPtr directory = fileManager->GetDirectory(); + if (NS_WARN_IF(!directory)) { + return nullptr; + } + + nsCOMPtr file = + fileManager->GetFileForId(directory, aFileInfo->Id()); + if (NS_WARN_IF(!file)) { + return nullptr; + } return file.forget(); } } // anonymous namespace -namespace mozilla { -namespace dom { -namespace indexedDB { - -IDBMutableFile::IDBMutableFile(IDBDatabase* aOwner) - : DOMEventTargetHelper(aOwner) +IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase, + const nsAString& aName, + const nsAString& aType, + already_AddRefed aFileInfo, + const nsACString& aGroup, + const nsACString& aOrigin, + const nsACString& aStorageId, + PersistenceType aPersistenceType, + already_AddRefed aFile) + : DOMEventTargetHelper(aDatabase) + , mDatabase(aDatabase) + , mFileInfo(aFileInfo) + , mGroup(aGroup) + , mOrigin(aOrigin) + , mPersistenceType(aPersistenceType) + , mInvalidated(false) { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mDatabase); + MOZ_ASSERT(mFileInfo); + + mName = aName; + mType = aType; + mFile = aFile; + mStorageId = aStorageId; + mFileName.AppendInt(mFileInfo->Id()); + + MOZ_ASSERT(mFile); + + mDatabase->NoteLiveMutableFile(this); } IDBMutableFile::~IDBMutableFile() { + // XXX This is always in the main process but it sometimes happens too late in + // shutdown and the IndexedDatabaseManager has already been torn down. + // MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + if (mDatabase) { + mDatabase->NoteFinishedMutableFile(this); + } } -NS_IMPL_CYCLE_COLLECTION_INHERITED(IDBMutableFile, DOMEventTargetHelper, - mDatabase) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBMutableFile) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper) - // static already_AddRefed -IDBMutableFile::Create(const nsAString& aName, +IDBMutableFile::Create(IDBDatabase* aDatabase, + const nsAString& aName, const nsAString& aType, - IDBDatabase* aDatabase, already_AddRefed aFileInfo) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); nsRefPtr fileInfo(aFileInfo); - NS_ASSERTION(fileInfo, "Null pointer!"); + MOZ_ASSERT(fileInfo); - nsRefPtr newFile = new IDBMutableFile(aDatabase); + PrincipalInfo* principalInfo = aDatabase->Factory()->GetPrincipalInfo(); + MOZ_ASSERT(principalInfo); - newFile->mName = aName; - newFile->mType = aType; + nsCOMPtr principal = PrincipalInfoToPrincipal(*principalInfo); + if (NS_WARN_IF(!principal)) { + return nullptr; + } - newFile->mFile = GetFileFor(fileInfo); - NS_ENSURE_TRUE(newFile->mFile, nullptr); + nsCString group; + nsCString origin; + if (NS_WARN_IF(NS_FAILED(QuotaManager::GetInfoFromPrincipal(principal, + &group, + &origin, + nullptr, + nullptr)))) { + return nullptr; + } - newFile->mStorageId = aDatabase->Id(); - newFile->mFileName.AppendInt(fileInfo->Id()); + const DatabaseSpec* spec = aDatabase->Spec(); + MOZ_ASSERT(spec); - newFile->mDatabase = aDatabase; - fileInfo.swap(newFile->mFileInfo); + PersistenceType persistenceType = spec->metadata().persistenceType(); + + nsCString storageId; + QuotaManager::GetStorageId(persistenceType, + origin, + Client::IDB, + aDatabase->Name(), + storageId); + + nsCOMPtr file = GetFileFor(fileInfo); + if (NS_WARN_IF(!file)) { + return nullptr; + } + + nsRefPtr newFile = + new IDBMutableFile(aDatabase, + aName, + aType, + fileInfo.forget(), + group, + origin, + storageId, + persistenceType, + file.forget()); return newFile.forget(); } +void +IDBMutableFile::Invalidate() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mInvalidated); + + mInvalidated = true; +} + +NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBMutableFile) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBMutableFile) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBMutableFile, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBMutableFile, + DOMEventTargetHelper) + MOZ_ASSERT(tmp->mDatabase); + tmp->mDatabase->NoteFinishedMutableFile(tmp); + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDatabase) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + bool IDBMutableFile::IsInvalid() { - return mDatabase->IsInvalidated(); + return mInvalidated; } nsIOfflineStorage* IDBMutableFile::Storage() { - return mDatabase; + MOZ_CRASH("Don't call me!"); } already_AddRefed IDBMutableFile::CreateStream(bool aReadOnly) { - PersistenceType persistenceType = mDatabase->Type(); - const nsACString& group = mDatabase->Group(); - const nsACString& origin = mDatabase->Origin(); + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); nsCOMPtr result; if (aReadOnly) { nsRefPtr stream = - FileInputStream::Create(persistenceType, group, origin, mFile, -1, -1, + FileInputStream::Create(mPersistenceType, + mGroup, + mOrigin, + mFile, + -1, + -1, nsIFileInputStream::DEFER_OPEN); result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream); - } - else { + } else { nsRefPtr stream = - FileStream::Create(persistenceType, group, origin, mFile, -1, -1, + FileStream::Create(mPersistenceType, + mGroup, + mOrigin, + mFile, + -1, + -1, nsIFileStream::DEFER_OPEN); result = NS_ISUPPORTS_CAST(nsIFileStream*, stream); } - NS_ENSURE_TRUE(result, nullptr); + + if (NS_WARN_IF(!result)) { + return nullptr; + } return result.forget(); } @@ -166,33 +279,37 @@ IDBMutableFile::CreateStream(bool aReadOnly) void IDBMutableFile::SetThreadLocals() { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); MOZ_ASSERT(mDatabase->GetOwner(), "Should have owner!"); + QuotaManager::SetCurrentWindow(mDatabase->GetOwner()); } void IDBMutableFile::UnsetThreadLocals() { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + QuotaManager::SetCurrentWindow(nullptr); } -already_AddRefed -IDBMutableFile::CreateFileObject(IDBFileHandle* aFileHandle, uint32_t aFileSize) -{ - nsCOMPtr fileSnapshot = new DOMFile( - new FileImplSnapshot(mName, mType, aFileSize, mFile, aFileHandle, - mFileInfo)); - - return fileSnapshot.forget(); -} - -// virtual JSObject* IDBMutableFile::WrapObject(JSContext* aCx) { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + return IDBMutableFileBinding::Wrap(aCx, this); } +IDBDatabase* +IDBMutableFile::Database() const +{ + MOZ_ASSERT(NS_IsMainThread()); + + return mDatabase; +} + already_AddRefed IDBMutableFile::Open(FileMode aMode, ErrorResult& aError) { @@ -213,6 +330,31 @@ IDBMutableFile::Open(FileMode aMode, ErrorResult& aError) return fileHandle.forget(); } +int64_t +IDBMutableFile::GetFileId() const +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mFileInfo); + + return mFileInfo->Id(); +} + +already_AddRefed +IDBMutableFile::CreateFileObject(IDBFileHandle* aFileHandle, + MetadataParameters* aMetadataParams) +{ + nsRefPtr impl = + new FileImplSnapshot(mName, + mType, + aMetadataParams, + mFile, + aFileHandle, + mFileInfo); + + nsCOMPtr fileSnapshot = new DOMFile(impl); + return fileSnapshot.forget(); +} + already_AddRefed IDBMutableFile::GetFile(ErrorResult& aError) { @@ -227,10 +369,11 @@ IDBMutableFile::GetFile(ErrorResult& aError) IDBFileHandle::Create(FileMode::Readonly, FileHandleBase::PARALLEL, this); nsRefPtr request = - IDBFileRequest::Create(GetOwner(), fileHandle, /* aWrapAsDOMRequest */ - true); + IDBFileRequest::Create(GetOwner(), + fileHandle, + /* aWrapAsDOMRequest */ true); - nsRefPtr params = new MetadataParameters(true, false); + nsRefPtr params = new MetadataParameters(true, true); nsRefPtr helper = new GetFileHelper(fileHandle, request, params, this); @@ -244,10 +387,6 @@ IDBMutableFile::GetFile(ErrorResult& aError) return request.forget(); } -} // namespace indexedDB -} // namespace dom -} // namespace mozilla - nsresult GetFileHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) @@ -257,7 +396,7 @@ GetFileHelper::GetSuccessResult(JSContext* aCx, auto fileHandle = static_cast(mFileHandle.get()); nsCOMPtr domFile = - mMutableFile->CreateFileObject(fileHandle, mParams->Size()); + mMutableFile->CreateFileObject(fileHandle, mParams); nsresult rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), aVal); @@ -267,3 +406,7 @@ GetFileHelper::GetSuccessResult(JSContext* aCx, return NS_OK; } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBMutableFile.h b/dom/indexedDB/IDBMutableFile.h index bf28971cabc5..53c3c00be64e 100644 --- a/dom/indexedDB/IDBMutableFile.h +++ b/dom/indexedDB/IDBMutableFile.h @@ -8,13 +8,13 @@ #define mozilla_dom_indexeddb_idbmutablefile_h__ #include "js/TypeDecls.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/dom/FileModeBinding.h" -#include "mozilla/dom/indexedDB/FileInfo.h" -#include "mozilla/dom/MutableFile.h" #include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/FileModeBinding.h" +#include "mozilla/dom/MutableFile.h" +#include "mozilla/dom/quota/PersistenceType.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" @@ -28,23 +28,39 @@ class ErrorResult; namespace dom { class DOMRequest; +class MetadataParameters; namespace indexedDB { +class FileInfo; class IDBDatabase; class IDBFileHandle; -class IDBMutableFile MOZ_FINAL : public DOMEventTargetHelper, - public MutableFileBase +class IDBMutableFile MOZ_FINAL + : public DOMEventTargetHelper + , public MutableFileBase { + typedef mozilla::dom::MetadataParameters MetadataParameters; + typedef mozilla::dom::quota::PersistenceType PersistenceType; + + nsString mName; + nsString mType; + + nsRefPtr mDatabase; + nsRefPtr mFileInfo; + + const nsCString mGroup; + const nsCString mOrigin; + const PersistenceType mPersistenceType; + + Atomic mInvalidated; + public: - NS_DECL_ISUPPORTS_INHERITED - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper) - static already_AddRefed - Create(const nsAString& aName, const nsAString& aType, - IDBDatabase* aDatabase, already_AddRefed aFileInfo); + Create(IDBDatabase* aDatabase, + const nsAString& aName, + const nsAString& aType, + already_AddRefed aFileInfo); const nsAString& Name() const @@ -59,10 +75,7 @@ public: } int64_t - GetFileId() const - { - return mFileInfo->Id(); - } + GetFileId() const; FileInfo* GetFileInfo() const @@ -70,6 +83,16 @@ public: return mFileInfo; } + already_AddRefed + CreateFileObject(IDBFileHandle* aFileHandle, + MetadataParameters* aMetadataParams); + + void + Invalidate(); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBMutableFile, DOMEventTargetHelper) + virtual bool IsInvalid() MOZ_OVERRIDE; @@ -85,9 +108,6 @@ public: virtual void UnsetThreadLocals() MOZ_OVERRIDE; - already_AddRefed - CreateFileObject(IDBFileHandle* aFileHandle, uint32_t aFileSize); - // nsWrapperCache virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; @@ -112,12 +132,7 @@ public: } IDBDatabase* - Database() - { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - - return mDatabase; - } + Database() const; already_AddRefed Open(FileMode aMode, ErrorResult& aError); @@ -129,14 +144,17 @@ public: IMPL_EVENT_HANDLER(error) private: - explicit IDBMutableFile(IDBDatabase* aOwner); + IDBMutableFile(IDBDatabase* aDatabase, + const nsAString& aName, + const nsAString& aType, + already_AddRefed aFileInfo, + const nsACString& aGroup, + const nsACString& aOrigin, + const nsACString& aStorageId, + PersistenceType aPersistenceType, + already_AddRefed aFile); + ~IDBMutableFile(); - - nsString mName; - nsString mType; - - nsRefPtr mDatabase; - nsRefPtr mFileInfo; }; } // namespace indexedDB diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 554d64c40de4..5e2012a924ad 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -4,756 +4,441 @@ * 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 "base/basictypes.h" - #include "IDBObjectStore.h" -#include "mozilla/dom/ipc/nsIRemoteBlob.h" -#include "nsIOutputStream.h" - -#include -#include "jsfriendapi.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/IDBMutableFileBinding.h" -#include "mozilla/dom/nsIContentParent.h" -#include "mozilla/dom/StructuredCloneTags.h" -#include "mozilla/dom/TabChild.h" -#include "mozilla/dom/ipc/Blob.h" -#include "mozilla/dom/quota/FileStreams.h" -#include "mozilla/Endian.h" -#include "mozilla/storage.h" -#include "nsContentUtils.h" -#include "nsDOMClassInfo.h" -#include "nsDOMFile.h" -#include "mozilla/dom/DOMStringList.h" -#include "nsJSUtils.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadUtils.h" -#include "snappy/snappy.h" - -#include "AsyncConnectionHelper.h" +#include "FileInfo.h" #include "IDBCursor.h" +#include "IDBDatabase.h" #include "IDBEvents.h" +#include "IDBFactory.h" #include "IDBIndex.h" #include "IDBKeyRange.h" #include "IDBMutableFile.h" +#include "IDBRequest.h" #include "IDBTransaction.h" -#include "DatabaseInfo.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "IndexedDatabaseManager.h" +#include "js/Class.h" +#include "js/StructuredClone.h" #include "KeyPath.h" +#include "mozilla/Endian.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/Move.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/dom/IDBMutableFileBinding.h" +#include "mozilla/dom/IDBObjectStoreBinding.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/ipc/nsIRemoteBlob.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "nsCOMPtr.h" +#include "nsDOMFile.h" +#include "nsIDOMFile.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "ipc/IndexedDBChild.h" -#include "ipc/IndexedDBParent.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#include "IndexedDatabaseInlines.h" -#include "nsCharSeparatedTokenizer.h" +namespace mozilla { +namespace dom { +namespace indexedDB { -#define FILE_COPY_BUFFER_SIZE 32768 +using namespace mozilla::dom::quota; +using namespace mozilla::ipc; -USING_INDEXEDDB_NAMESPACE -using namespace mozilla::dom; -using namespace mozilla::dom::indexedDB::ipc; -using mozilla::dom::quota::FileOutputStream; -using mozilla::ErrorResult; -using mozilla::fallible_t; -using mozilla::LittleEndian; -using mozilla::Move; -using mozilla::NativeEndian; +struct IDBObjectStore::StructuredCloneWriteInfo +{ + struct BlobOrFileInfo + { + nsCOMPtr mBlob; + nsRefPtr mFileInfo; -BEGIN_INDEXEDDB_NAMESPACE + bool + operator==(const BlobOrFileInfo& aOther) const + { + return this->mBlob == aOther.mBlob && this->mFileInfo == aOther.mFileInfo; + } + }; -struct MutableFileData + JSAutoStructuredCloneBuffer mCloneBuffer; + nsTArray mBlobOrFileInfos; + IDBDatabase* mDatabase; + uint64_t mOffsetToKeyProp; + + StructuredCloneWriteInfo(IDBDatabase* aDatabase) + : mDatabase(aDatabase) + , mOffsetToKeyProp(0) + { + MOZ_ASSERT(aDatabase); + + MOZ_COUNT_CTOR(StructuredCloneWriteInfo); + } + + StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo) + : mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer)) + , mDatabase(aCloneWriteInfo.mDatabase) + , mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) + { + MOZ_ASSERT(mDatabase); + + MOZ_COUNT_CTOR(StructuredCloneWriteInfo); + + mBlobOrFileInfos.SwapElements(aCloneWriteInfo.mBlobOrFileInfos); + aCloneWriteInfo.mOffsetToKeyProp = 0; + } + + ~StructuredCloneWriteInfo() + { + MOZ_COUNT_DTOR(StructuredCloneWriteInfo); + } + + bool + operator==(const StructuredCloneWriteInfo& aOther) const + { + return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() && + this->mCloneBuffer.data() == aOther.mCloneBuffer.data() && + this->mBlobOrFileInfos == aOther.mBlobOrFileInfos && + this->mDatabase == aOther.mDatabase && + this->mOffsetToKeyProp == aOther.mOffsetToKeyProp; + } + + bool + SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther) + { + if (aOther.data().IsEmpty()) { + mCloneBuffer.clear(); + } else { + auto* aOtherBuffer = + reinterpret_cast( + const_cast(aOther.data().Elements())); + if (!mCloneBuffer.copy(aOtherBuffer, aOther.data().Length())) { + return false; + } + } + + mBlobOrFileInfos.Clear(); + + mOffsetToKeyProp = aOther.offsetToKeyProp(); + return true; + } +}; + +namespace { + +struct MOZ_STACK_CLASS MutableFileData MOZ_FINAL { nsString type; nsString name; + + MutableFileData() + { + MOZ_COUNT_CTOR(MutableFileData); + } + + ~MutableFileData() + { + MOZ_COUNT_DTOR(MutableFileData); + } }; -struct BlobOrFileData +struct MOZ_STACK_CLASS BlobOrFileData MOZ_FINAL { - BlobOrFileData() - : tag(0), size(0), lastModifiedDate(UINT64_MAX) - { } - uint32_t tag; uint64_t size; nsString type; nsString name; uint64_t lastModifiedDate; + + BlobOrFileData() + : tag(0) + , size(0) + , lastModifiedDate(UINT64_MAX) + { + MOZ_COUNT_CTOR(BlobOrFileData); + } + + ~BlobOrFileData() + { + MOZ_COUNT_DTOR(BlobOrFileData); + } }; -END_INDEXEDDB_NAMESPACE - -namespace { - -inline -bool -IgnoreNothing(char16_t c) +struct MOZ_STACK_CLASS GetAddInfoClosure MOZ_FINAL { - return false; -} + IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo; + JS::Handle mValue; -class ObjectStoreHelper : public AsyncConnectionHelper -{ -public: - ObjectStoreHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore) - : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore), - mActor(nullptr) + GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo, + JS::Handle aValue) + : mCloneWriteInfo(aCloneWriteInfo) + , mValue(aValue) { - NS_ASSERTION(aTransaction, "Null transaction!"); - NS_ASSERTION(aRequest, "Null request!"); - NS_ASSERTION(aObjectStore, "Null object store!"); + MOZ_COUNT_CTOR(GetAddInfoClosure); } - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) = 0; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0; - -protected: - nsRefPtr mObjectStore; - -private: - IndexedDBObjectStoreRequestChild* mActor; -}; - -class NoRequestObjectStoreHelper : public AsyncConnectionHelper -{ -public: - NoRequestObjectStoreHelper(IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore) - : AsyncConnectionHelper(aTransaction, nullptr), mObjectStore(aObjectStore) + ~GetAddInfoClosure() { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aTransaction, "Null transaction!"); - NS_ASSERTION(aObjectStore, "Null object store!"); - } - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult OnSuccess() MOZ_OVERRIDE; - - virtual void OnError() MOZ_OVERRIDE; - -protected: - nsRefPtr mObjectStore; -}; - -class AddHelper : public ObjectStoreHelper -{ -public: - AddHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - StructuredCloneWriteInfo&& aCloneWriteInfo, - const Key& aKey, - bool aOverwrite, - nsTArray& aIndexUpdateInfo) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mCloneWriteInfo(Move(aCloneWriteInfo)), - mKey(aKey), - mOverwrite(aOverwrite) - { - mIndexUpdateInfo.SwapElements(aIndexUpdateInfo); - } - - ~AddHelper() - { - IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - // These may change in the autoincrement case. - StructuredCloneWriteInfo mCloneWriteInfo; - Key mKey; - nsTArray mIndexUpdateInfo; - const bool mOverwrite; -}; - -class GetHelper : public ObjectStoreHelper -{ -public: - GetHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange) - { - NS_ASSERTION(aKeyRange, "Null key range!"); - } - - ~GetHelper() - { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - // In-params. - nsRefPtr mKeyRange; - -private: - // Out-params. - StructuredCloneReadInfo mCloneReadInfo; -}; - -class DeleteHelper : public GetHelper -{ -public: - DeleteHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange) - : GetHelper(aTransaction, aRequest, aObjectStore, aKeyRange) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; -}; - -class ClearHelper : public ObjectStoreHelper -{ -public: - ClearHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; -}; - -class OpenCursorHelper : public ObjectStoreHelper -{ -public: - OpenCursorHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange, - IDBCursor::Direction aDirection) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange), mDirection(aDirection) - { } - - ~OpenCursorHelper() - { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - nsresult EnsureCursor(); - - // In-params. - nsRefPtr mKeyRange; - const IDBCursor::Direction mDirection; - - // Out-params. - Key mKey; - StructuredCloneReadInfo mCloneReadInfo; - nsCString mContinueQuery; - nsCString mContinueToQuery; - Key mRangeKey; - - // Only used in the parent process. - nsRefPtr mCursor; - SerializedStructuredCloneReadInfo mSerializedCloneReadInfo; -}; - -class OpenKeyCursorHelper MOZ_FINAL : public ObjectStoreHelper -{ -public: - OpenKeyCursorHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange, - IDBCursor::Direction aDirection) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange), mDirection(aDirection) - { } - - virtual nsresult - DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) - MOZ_OVERRIDE; - - virtual void - ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - ~OpenKeyCursorHelper() - { } - - nsresult EnsureCursor(); - - // In-params. - nsRefPtr mKeyRange; - const IDBCursor::Direction mDirection; - - // Out-params. - Key mKey; - nsCString mContinueQuery; - nsCString mContinueToQuery; - Key mRangeKey; - - // Only used in the parent process. - nsRefPtr mCursor; -}; - -class CreateIndexHelper : public NoRequestObjectStoreHelper -{ -public: - CreateIndexHelper(IDBTransaction* aTransaction, IDBIndex* aIndex) - : NoRequestObjectStoreHelper(aTransaction, aIndex->ObjectStore()), - mIndex(aIndex) - { - if (sTLSIndex == BAD_TLS_INDEX) { - PR_NewThreadPrivateIndex(&sTLSIndex, DestroyTLSEntry); - } - - NS_ASSERTION(sTLSIndex != BAD_TLS_INDEX, - "PR_NewThreadPrivateIndex failed!"); - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - -private: - nsresult InsertDataFromObjectStore(mozIStorageConnection* aConnection); - - static void DestroyTLSEntry(void* aPtr); - - static unsigned sTLSIndex; - - // In-params. - nsRefPtr mIndex; -}; - -unsigned CreateIndexHelper::sTLSIndex = unsigned(BAD_TLS_INDEX); - -class DeleteIndexHelper : public NoRequestObjectStoreHelper -{ -public: - DeleteIndexHelper(IDBTransaction* aTransaction, - IDBObjectStore* aObjectStore, - const nsAString& aName) - : NoRequestObjectStoreHelper(aTransaction, aObjectStore), mName(aName) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - -private: - // In-params - nsString mName; -}; - -class GetAllHelper : public ObjectStoreHelper -{ -public: - GetAllHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange, - const uint32_t aLimit) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange), mLimit(aLimit) - { } - - ~GetAllHelper() - { - for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); - } - } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -protected: - // In-params. - nsRefPtr mKeyRange; - const uint32_t mLimit; - -private: - // Out-params. - nsTArray mCloneReadInfos; -}; - -class GetAllKeysHelper MOZ_FINAL : public ObjectStoreHelper -{ -public: - GetAllKeysHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange, - const uint32_t aLimit) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange), mLimit(aLimit) - { } - - virtual nsresult - DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) - MOZ_OVERRIDE; - - virtual void - ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - ~GetAllKeysHelper() - { } - - nsRefPtr mKeyRange; - const uint32_t mLimit; - nsTArray mKeys; -}; - -class CountHelper : public ObjectStoreHelper -{ -public: - CountHelper(IDBTransaction* aTransaction, - IDBRequest* aRequest, - IDBObjectStore* aObjectStore, - IDBKeyRange* aKeyRange) - : ObjectStoreHelper(aTransaction, aRequest, aObjectStore), - mKeyRange(aKeyRange), mCount(0) - { } - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - virtual nsresult - PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - -private: - nsRefPtr mKeyRange; - uint64_t mCount; -}; - -class MOZ_STACK_CLASS AutoRemoveIndex -{ -public: - AutoRemoveIndex(ObjectStoreInfo* aObjectStoreInfo, - const nsAString& aIndexName) - : mObjectStoreInfo(aObjectStoreInfo), mIndexName(aIndexName) - { } - - ~AutoRemoveIndex() - { - if (mObjectStoreInfo) { - for (uint32_t i = 0; i < mObjectStoreInfo->indexes.Length(); i++) { - if (mObjectStoreInfo->indexes[i].name == mIndexName) { - mObjectStoreInfo->indexes.RemoveElementAt(i); - break; - } - } - } - } - - void forget() - { - mObjectStoreInfo = nullptr; - } - -private: - ObjectStoreInfo* mObjectStoreInfo; - nsString mIndexName; -}; - -class ThreadLocalJSRuntime -{ - JSRuntime* mRuntime; - JSContext* mContext; - JSObject* mGlobal; - - static const JSClass sGlobalClass; - static const unsigned sRuntimeHeapSize = 768 * 1024; - - ThreadLocalJSRuntime() - : mRuntime(nullptr), mContext(nullptr), mGlobal(nullptr) - { - MOZ_COUNT_CTOR(ThreadLocalJSRuntime); - } - - nsresult Init() - { - mRuntime = JS_NewRuntime(sRuntimeHeapSize); - NS_ENSURE_TRUE(mRuntime, NS_ERROR_OUT_OF_MEMORY); - - /* - * Not setting this will cause JS_CHECK_RECURSION to report false - * positives - */ - JS_SetNativeStackQuota(mRuntime, 128 * sizeof(size_t) * 1024); - - mContext = JS_NewContext(mRuntime, 0); - NS_ENSURE_TRUE(mContext, NS_ERROR_OUT_OF_MEMORY); - - JSAutoRequest ar(mContext); - - mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr, - JS::FireOnNewGlobalHook); - NS_ENSURE_TRUE(mGlobal, NS_ERROR_OUT_OF_MEMORY); - - return NS_OK; - } - - public: - static ThreadLocalJSRuntime *Create() - { - ThreadLocalJSRuntime *entry = new ThreadLocalJSRuntime(); - NS_ENSURE_TRUE(entry, nullptr); - - if (NS_FAILED(entry->Init())) { - delete entry; - return nullptr; - } - - return entry; - } - - JSContext *Context() const - { - return mContext; - } - - JSObject *Global() const - { - return mGlobal; - } - - ~ThreadLocalJSRuntime() - { - MOZ_COUNT_DTOR(ThreadLocalJSRuntime); - - if (mContext) { - JS_DestroyContext(mContext); - } - - if (mRuntime) { - JS_DestroyRuntime(mRuntime); - } + MOZ_COUNT_DTOR(GetAddInfoClosure); } }; -const JSClass ThreadLocalJSRuntime::sGlobalClass = { - "IndexedDBTransactionThreadGlobal", - JSCLASS_GLOBAL_FLAGS, - JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, - nullptr, nullptr, nullptr, nullptr, - JS_GlobalObjectTraceHook -}; - -inline already_AddRefed GenerateRequest(IDBObjectStore* aObjectStore) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - IDBDatabase* database = aObjectStore->Transaction()->Database(); - return IDBRequest::Create(aObjectStore, database, - aObjectStore->Transaction()); + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + + IDBTransaction* transaction = aObjectStore->Transaction(); + + nsRefPtr request = + IDBRequest::Create(aObjectStore, transaction->Database(), transaction); + MOZ_ASSERT(request); + + return request.forget(); } -struct MOZ_STACK_CLASS GetAddInfoClosure +bool +StructuredCloneWriteCallback(JSContext* aCx, + JSStructuredCloneWriter* aWriter, + JS::Handle aObj, + void* aClosure) { - IDBObjectStore* mThis; - StructuredCloneWriteInfo& mCloneWriteInfo; - JS::Handle mValue; -}; + MOZ_ASSERT(aCx); + MOZ_ASSERT(aWriter); + MOZ_ASSERT(aClosure); + + auto* cloneWriteInfo = + static_cast(aClosure); + + if (JS_GetClass(aObj) == IDBObjectStore::DummyPropClass()) { + MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp); + cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter); + + uint64_t value = 0; + // Omit endian swap + return JS_WriteBytes(aWriter, &value, sizeof(value)); + } + + IDBMutableFile* mutableFile; + if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) { + IDBDatabase* database = mutableFile->Database(); + MOZ_ASSERT(database); + + // Throw when trying to store IDBMutableFile objects that live in a + // different database. + if (database != cloneWriteInfo->mDatabase) { + MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase)); + + if (database->Name() != cloneWriteInfo->mDatabase->Name()) { + return false; + } + + nsCString fileOrigin, databaseOrigin; + PersistenceType filePersistenceType, databasePersistenceType; + + if (NS_WARN_IF(NS_FAILED(database->GetQuotaInfo(fileOrigin, + &filePersistenceType)))) { + return false; + } + + if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo( + databaseOrigin, + &databasePersistenceType)))) { + return false; + } + + if (filePersistenceType != databasePersistenceType || + fileOrigin != databaseOrigin) { + return false; + } + } + + nsRefPtr fileInfo = mutableFile->GetFileInfo(); + MOZ_ASSERT(fileInfo); + + if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) { + MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!"); + return false; + } + + const uint32_t index = uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length()); + + NS_ConvertUTF16toUTF8 convType(mutableFile->Type()); + uint32_t convTypeLength = + NativeEndian::swapToLittleEndian(convType.Length()); + + NS_ConvertUTF16toUTF8 convName(mutableFile->Name()); + uint32_t convNameLength = + NativeEndian::swapToLittleEndian(convName.Length()); + + if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) || + !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) || + !JS_WriteBytes(aWriter, convType.get(), convType.Length()) || + !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) || + !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { + return false; + } + + IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo* + newBlobOrFileInfo = + cloneWriteInfo->mBlobOrFileInfos.AppendElement(); + newBlobOrFileInfo->mFileInfo.swap(fileInfo); + + return true; + } + + MOZ_ASSERT(NS_IsMainThread(), "This can't work off the main thread!"); + + nsCOMPtr wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); + + if (wrappedNative) { + nsISupports* supports = wrappedNative->Native(); + + nsCOMPtr blob = do_QueryInterface(supports); + if (blob) { + uint64_t size; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetSize(&size))); + + size = NativeEndian::swapToLittleEndian(size); + + nsString type; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetType(type))); + + NS_ConvertUTF16toUTF8 convType(type); + uint32_t convTypeLength = + NativeEndian::swapToLittleEndian(convType.Length()); + + nsCOMPtr file = do_QueryInterface(blob); + + if (cloneWriteInfo->mBlobOrFileInfos.Length() > size_t(UINT32_MAX)) { + MOZ_ASSERT(false, + "Fix the structured clone data to use a bigger type!"); + return false; + } + + const uint32_t index = + uint32_t(cloneWriteInfo->mBlobOrFileInfos.Length()); + + if (!JS_WriteUint32Pair(aWriter, + file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, + index) || + !JS_WriteBytes(aWriter, &size, sizeof(size)) || + !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) || + !JS_WriteBytes(aWriter, convType.get(), convType.Length())) { + return false; + } + + if (file) { + uint64_t lastModifiedDate; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + file->GetMozLastModifiedDate(&lastModifiedDate))); + + lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate); + + nsString name; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(file->GetName(name))); + + NS_ConvertUTF16toUTF8 convName(name); + uint32_t convNameLength = + NativeEndian::swapToLittleEndian(convName.Length()); + + if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) || + !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) || + !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { + return false; + } + } + + IDBObjectStore::StructuredCloneWriteInfo::BlobOrFileInfo* + newBlobOrFileInfo = + cloneWriteInfo->mBlobOrFileInfos.AppendElement(); + newBlobOrFileInfo->mBlob.swap(blob); + + return true; + } + } + + // Try using the runtime callbacks + const JSStructuredCloneCallbacks* runtimeCallbacks = + js::GetContextStructuredCloneCallbacks(aCx); + if (runtimeCallbacks) { + return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr); + } + + return false; +} nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) { - GetAddInfoClosure* data = static_cast(aClosure); + static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = { + nullptr /* read */, + StructuredCloneWriteCallback /* write */, + nullptr /* reportError */, + nullptr /* readTransfer */, + nullptr /* writeTransfer */, + nullptr /* freeTransfer */ + }; + + MOZ_ASSERT(aCx); + + auto* data = static_cast(aClosure); + MOZ_ASSERT(data); data->mCloneWriteInfo.mOffsetToKeyProp = 0; - data->mCloneWriteInfo.mTransaction = data->mThis->Transaction(); - if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) { + if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx, + data->mValue, + &kStructuredCloneCallbacks, + &data->mCloneWriteInfo)) { return NS_ERROR_DOM_DATA_CLONE_ERR; } return NS_OK; } -inline BlobChild* ActorFromRemoteBlob(nsIDOMBlob* aBlob) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(aBlob); nsRefPtr blob = static_cast(aBlob); nsCOMPtr remoteBlob = do_QueryInterface(blob->Impl()); if (remoteBlob) { - BlobChild* actor = - static_cast(static_cast(remoteBlob->GetPBlob())); - NS_ASSERTION(actor, "Null actor?!"); + BlobChild* actor = remoteBlob->GetBlobChild(); + MOZ_ASSERT(actor); + + if (actor->GetContentManager()) { + return nullptr; + } + + MOZ_ASSERT(actor->GetBackgroundManager()); + MOZ_ASSERT(BackgroundChild::GetForCurrentThread()); + MOZ_ASSERT(actor->GetBackgroundManager() == + BackgroundChild::GetForCurrentThread(), + "Blob actor is not bound to this thread!"); + return actor; } + return nullptr; } -inline bool -ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName, - const nsString& aContentType, uint64_t aSize, +ResolveMysteryFile(nsIDOMBlob* aBlob, + const nsString& aName, + const nsString& aContentType, + uint64_t aSize, uint64_t aLastModifiedDate) { BlobChild* actor = ActorFromRemoteBlob(aBlob); @@ -764,9 +449,9 @@ ResolveMysteryFile(nsIDOMBlob* aBlob, const nsString& aName, return true; } -inline bool -ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType, +ResolveMysteryBlob(nsIDOMBlob* aBlob, + const nsString& aContentType, uint64_t aSize) { BlobChild* actor = ActorFromRemoteBlob(aBlob); @@ -776,624 +461,7 @@ ResolveMysteryBlob(nsIDOMBlob* aBlob, const nsString& aContentType, return true; } -class MainThreadDeserializationTraits -{ -public: - static bool CreateAndWrapMutableFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const MutableFileData& aData, - JS::MutableHandle aResult) - { - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr& fileInfo = aFile.mFileInfo; - - nsRefPtr mutableFile = IDBMutableFile::Create(aData.name, - aData.type, aDatabase, fileInfo.forget()); - - JS::Rooted result(aCx, mutableFile->WrapObject(aCx)); - if (NS_WARN_IF(!result)) { - return false; - } - - aResult.set(result); - return true; - } - - static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const BlobOrFileData& aData) - { - MOZ_ASSERT(NS_IsMainThread()); - - MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || - aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || - aData.tag == SCTAG_DOM_BLOB); - - nsresult rv = NS_OK; - - nsRefPtr& fileInfo = aFile.mFileInfo; - - nsCOMPtr nativeFile; - if (!aFile.mFile) { - FileManager* fileManager = aDatabase->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - nsCOMPtr directory = fileManager->GetDirectory(); - if (!directory) { - NS_WARNING("Failed to get directory!"); - return nullptr; - } - - nativeFile = fileManager->GetFileForId(directory, fileInfo->Id()); - if (!nativeFile) { - NS_WARNING("Failed to get file!"); - return nullptr; - } - } - - if (aData.tag == SCTAG_DOM_BLOB) { - nsCOMPtr domBlob; - if (aFile.mFile) { - if (!ResolveMysteryBlob(aFile.mFile, aData.type, aData.size)) { - return nullptr; - } - domBlob = aFile.mFile; - } - else { - domBlob = DOMFile::CreateFromFile(aData.type, aData.size, nativeFile, - fileInfo); - } - - JS::Rooted wrappedBlob(aCx); - rv = nsContentUtils::WrapNative(aCx, domBlob, &NS_GET_IID(nsIDOMBlob), - &wrappedBlob); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to wrap native!"); - return nullptr; - } - - return wrappedBlob.toObjectOrNull(); - } - - nsCOMPtr domFile; - if (aFile.mFile) { - if (!ResolveMysteryFile(aFile.mFile, aData.name, aData.type, aData.size, - aData.lastModifiedDate)) { - return nullptr; - } - domFile = do_QueryInterface(aFile.mFile); - NS_ASSERTION(domFile, "This should never fail!"); - } - else { - domFile = DOMFile::CreateFromFile(aData.name, aData.type, aData.size, - nativeFile, fileInfo); - } - - JS::Rooted wrappedFile(aCx); - rv = nsContentUtils::WrapNative(aCx, domFile, &NS_GET_IID(nsIDOMFile), - &wrappedFile); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to wrap native!"); - return nullptr; - } - - return wrappedFile.toObjectOrNull(); - } -}; - - -class CreateIndexDeserializationTraits -{ -public: - static bool CreateAndWrapMutableFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const MutableFileData& aData, - JS::MutableHandle aResult) - { - // MutableFile can't be used in index creation, so just make a dummy object. - JS::Rooted obj(aCx, - JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - - if (NS_WARN_IF(!obj)) { - return false; - } - - aResult.set(obj); - return true; - } - - static JSObject* CreateAndWrapBlobOrFile(JSContext* aCx, - IDBDatabase* aDatabase, - StructuredCloneFile& aFile, - const BlobOrFileData& aData) - { - MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || - aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || - aData.tag == SCTAG_DOM_BLOB); - - // The following properties are available for use in index creation - // Blob.size - // Blob.type - // File.name - // File.lastModifiedDate - - JS::Rooted obj(aCx, - JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - if (!obj) { - NS_WARNING("Failed to create object!"); - return nullptr; - } - - // Technically these props go on the proto, but this detail won't change - // the results of index creation. - - JS::Rooted type(aCx, - JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length())); - if (!type || - !JS_DefineProperty(aCx, obj, "size", double(aData.size), 0) || - !JS_DefineProperty(aCx, obj, "type", type, 0)) { - return nullptr; - } - - if (aData.tag == SCTAG_DOM_BLOB) { - return obj; - } - - JS::Rooted name(aCx, - JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length())); - JS::Rooted date(aCx, - JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); - if (!name || !date || - !JS_DefineProperty(aCx, obj, "name", name, 0) || - !JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0)) { - return nullptr; - } - - return obj; - } -}; - -} // anonymous namespace - -const JSClass IDBObjectStore::sDummyPropJSClass = { - "dummy", 0, - JS_PropertyStub, JS_DeletePropertyStub, - JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub -}; - -// static -already_AddRefed -IDBObjectStore::Create(IDBTransaction* aTransaction, - ObjectStoreInfo* aStoreInfo, - const nsACString& aDatabaseId, - bool aCreating) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - nsRefPtr objectStore = new IDBObjectStore(); - - objectStore->mTransaction = aTransaction; - objectStore->mName = aStoreInfo->name; - objectStore->mId = aStoreInfo->id; - objectStore->mKeyPath = aStoreInfo->keyPath; - objectStore->mAutoIncrement = aStoreInfo->autoIncrement; - objectStore->mDatabaseId = aDatabaseId; - objectStore->mInfo = aStoreInfo; - - if (!IndexedDatabaseManager::IsMainProcess()) { - IndexedDBTransactionChild* transactionActor = aTransaction->GetActorChild(); - NS_ASSERTION(transactionActor, "Must have an actor here!"); - - ObjectStoreConstructorParams params; - - if (aCreating) { - CreateObjectStoreParams createParams; - createParams.info() = *aStoreInfo; - params = createParams; - } - else { - GetObjectStoreParams getParams; - getParams.name() = aStoreInfo->name; - params = getParams; - } - - IndexedDBObjectStoreChild* actor = - new IndexedDBObjectStoreChild(objectStore); - - transactionActor->SendPIndexedDBObjectStoreConstructor(actor, params); - } - - return objectStore.forget(); -} - -// static -nsresult -IDBObjectStore::AppendIndexUpdateInfo( - int64_t aIndexID, - const KeyPath& aKeyPath, - bool aUnique, - bool aMultiEntry, - JSContext* aCx, - JS::Handle aVal, - nsTArray& aUpdateInfoArray) -{ - nsresult rv; - - if (!aMultiEntry) { - Key key; - rv = aKeyPath.ExtractKey(aCx, aVal, key); - - // If an index's keypath doesn't match an object, we ignore that object. - if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { - return NS_OK; - } - - if (NS_FAILED(rv)) { - return rv; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId = aIndexID; - updateInfo->indexUnique = aUnique; - updateInfo->value = key; - - return NS_OK; - } - - JS::Rooted val(aCx); - if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) { - return NS_OK; - } - - if (JS_IsArrayObject(aCx, val)) { - JS::Rooted array(aCx, &val.toObject()); - uint32_t arrayLength; - if (!JS_GetArrayLength(aCx, array, &arrayLength)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { - JS::Rooted arrayItem(aCx); - if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || - value.IsUnset()) { - // Not a value we can do anything with, ignore it. - continue; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId = aIndexID; - updateInfo->indexUnique = aUnique; - updateInfo->value = value; - } - } - else { - Key value; - if (NS_FAILED(value.SetFromJSVal(aCx, val)) || - value.IsUnset()) { - // Not a value we can do anything with, ignore it. - return NS_OK; - } - - IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); - updateInfo->indexId = aIndexID; - updateInfo->indexUnique = aUnique; - updateInfo->value = value; - } - - return NS_OK; -} - -// static -nsresult -IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction, - int64_t aObjectStoreId, - const Key& aObjectStoreKey, - bool aOverwrite, - int64_t aObjectDataId, - const nsTArray& aUpdateInfoArray) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("IDBObjectStore", "UpdateIndexes", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - NS_ASSERTION(aObjectDataId != INT64_MIN, "Bad objectData id!"); - - NS_NAMED_LITERAL_CSTRING(objectDataId, "object_data_id"); - - if (aOverwrite) { - nsCOMPtr deleteStmt = - aTransaction->GetCachedStatement( - "DELETE FROM unique_index_data " - "WHERE object_data_id = :object_data_id; " - "DELETE FROM index_data " - "WHERE object_data_id = :object_data_id"); - NS_ENSURE_TRUE(deleteStmt, NS_ERROR_FAILURE); - - mozStorageStatementScoper scoper(deleteStmt); - - rv = deleteStmt->BindInt64ByName(objectDataId, aObjectDataId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = deleteStmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Avoid lots of hash lookups for objectStores with lots of indexes by lazily - // holding the necessary statements on the stack outside the loop. - nsCOMPtr insertUniqueStmt; - nsCOMPtr insertStmt; - - uint32_t infoCount = aUpdateInfoArray.Length(); - for (uint32_t i = 0; i < infoCount; i++) { - const IndexUpdateInfo& updateInfo = aUpdateInfoArray[i]; - - nsCOMPtr& stmt = - updateInfo.indexUnique ? insertUniqueStmt : insertStmt; - - if (!stmt) { - stmt = updateInfo.indexUnique ? - aTransaction->GetCachedStatement( - "INSERT INTO unique_index_data " - "(index_id, object_data_id, object_data_key, value) " - "VALUES (:index_id, :object_data_id, :object_data_key, :value)") : - aTransaction->GetCachedStatement( - "INSERT OR IGNORE INTO index_data (" - "index_id, object_data_id, object_data_key, value) " - "VALUES (:index_id, :object_data_id, :object_data_key, :value)"); - } - NS_ENSURE_TRUE(stmt, NS_ERROR_FAILURE); - - mozStorageStatementScoper scoper(stmt); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), - updateInfo.indexId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(objectDataId, aObjectDataId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aObjectStoreKey.BindToStatement(stmt, - NS_LITERAL_CSTRING("object_data_key")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = updateInfo.value.BindToStatement(stmt, NS_LITERAL_CSTRING("value")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - if (rv == NS_ERROR_STORAGE_CONSTRAINT && updateInfo.indexUnique) { - // If we're inserting multiple entries for the same unique index, then - // we might have failed to insert due to colliding with another entry for - // the same index in which case we should ignore it. - - for (int32_t j = (int32_t)i - 1; - j >= 0 && aUpdateInfoArray[j].indexId == updateInfo.indexId; - --j) { - if (updateInfo.value == aUpdateInfoArray[j].value) { - // We found a key with the same value for the same index. So we - // must have had a collision with a value we just inserted. - rv = NS_OK; - break; - } - } - } - - if (NS_FAILED(rv)) { - return rv; - } - } - - return NS_OK; -} - -// static -nsresult -IDBObjectStore::GetStructuredCloneReadInfoFromStatement( - mozIStorageStatement* aStatement, - uint32_t aDataIndex, - uint32_t aFileIdsIndex, - IDBDatabase* aDatabase, - StructuredCloneReadInfo& aInfo) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("IDBObjectStore", "GetStructuredCloneReadInfoFromStatement", - js::ProfileEntry::Category::STORAGE); - -#ifdef DEBUG - { - int32_t type; - NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) && - type == mozIStorageStatement::VALUE_TYPE_BLOB, - "Bad value type!"); - } -#endif - - const uint8_t* blobData; - uint32_t blobDataLength; - nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength, - &blobData); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - const char* compressed = reinterpret_cast(blobData); - size_t compressedLength = size_t(blobDataLength); - - static const fallible_t fallible = fallible_t(); - - size_t uncompressedLength; - if (!snappy::GetUncompressedLength(compressed, compressedLength, - &uncompressedLength)) { - IDB_WARNING("Snappy can't determine uncompressed length!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsAutoArrayPtr uncompressed(new (fallible) char[uncompressedLength]); - NS_ENSURE_TRUE(uncompressed, NS_ERROR_OUT_OF_MEMORY); - - if (!snappy::RawUncompress(compressed, compressedLength, - uncompressed.get())) { - IDB_WARNING("Snappy can't determine uncompressed length!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer; - if (!buffer.copy(reinterpret_cast(uncompressed.get()), - uncompressedLength)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool isNull; - rv = aStatement->GetIsNull(aFileIdsIndex, &isNull); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!isNull) { - nsString ids; - rv = aStatement->GetString(aFileIdsIndex, ids); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsAutoTArray array; - rv = ConvertFileIdsToArray(ids, array); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - FileManager* fileManager = aDatabase->Manager(); - - for (uint32_t i = 0; i < array.Length(); i++) { - const int64_t& id = array[i]; - - nsRefPtr fileInfo = fileManager->GetFileInfo(id); - NS_ASSERTION(fileInfo, "Null file info!"); - - StructuredCloneFile* file = aInfo.mFiles.AppendElement(); - file->mFileInfo.swap(fileInfo); - } - } - - aInfo.mDatabase = aDatabase; - - return NS_OK; -} - -// static -void -IDBObjectStore::ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo) -{ - // This is kind of tricky, we only want to release stuff on the main thread, - // but we can end up being called on other threads if we have already been - // cleared on the main thread. - if (!aWriteInfo.mCloneBuffer.data() && !aWriteInfo.mFiles.Length()) { - return; - } - - // If there's something to clear, we should be on the main thread. - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - ClearStructuredCloneBuffer(aWriteInfo.mCloneBuffer); - aWriteInfo.mFiles.Clear(); -} - -// static -void -IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) -{ - // This is kind of tricky, we only want to release stuff on the main thread, - // but we can end up being called on other threads if we have already been - // cleared on the main thread. - if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) { - return; - } - - // If there's something to clear, we should be on the main thread. - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer); - aReadInfo.mFiles.Clear(); -} - -// static -void -IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer) -{ - if (aBuffer.data()) { - aBuffer.clear(); - } -} - -// static bool -IDBObjectStore::DeserializeValue(JSContext* aCx, - StructuredCloneReadInfo& aCloneReadInfo, - JS::MutableHandle aValue) -{ - NS_ASSERTION(NS_IsMainThread(), - "Should only be deserializing on the main thread!"); - NS_ASSERTION(aCx, "A JSContext is required!"); - - JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer; - - if (!buffer.data()) { - aValue.setUndefined(); - return true; - } - - JSAutoRequest ar(aCx); - - JSStructuredCloneCallbacks callbacks = { - IDBObjectStore::StructuredCloneReadCallback, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr - }; - - return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo); -} - -// static -bool -IDBObjectStore::SerializeValue(JSContext* aCx, - StructuredCloneWriteInfo& aCloneWriteInfo, - JS::Handle aValue) -{ - NS_ASSERTION(NS_IsMainThread(), - "Should only be serializing on the main thread!"); - NS_ASSERTION(aCx, "A JSContext is required!"); - - JSAutoRequest ar(aCx); - - JSStructuredCloneCallbacks callbacks = { - nullptr, - StructuredCloneWriteCallback, - nullptr, - nullptr, - nullptr, - nullptr - }; - - JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer; - - return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo); -} - -static inline bool StructuredCloneReadString(JSStructuredCloneReader* aReader, nsCString& aString) { @@ -1418,13 +486,11 @@ StructuredCloneReadString(JSStructuredCloneReader* aReader, return true; } -// static bool -IDBObjectStore::ReadMutableFile(JSStructuredCloneReader* aReader, - MutableFileData* aRetval) +ReadFileHandle(JSStructuredCloneReader* aReader, + MutableFileData* aRetval) { - static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, - "Update me!"); + static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!"); MOZ_ASSERT(aReader && aRetval); nsCString type; @@ -1442,35 +508,36 @@ IDBObjectStore::ReadMutableFile(JSStructuredCloneReader* aReader, return true; } -// static bool -IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader, - uint32_t aTag, - BlobOrFileData* aRetval) +ReadBlobOrFile(JSStructuredCloneReader* aReader, + uint32_t aTag, + BlobOrFileData* aRetval) { - static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && - SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && - SCTAG_DOM_FILE == 0xFFFF8005, + static_assert(SCTAG_DOM_BLOB == 0xffff8001 && + SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 && + SCTAG_DOM_FILE == 0xffff8005, "Update me!"); - MOZ_ASSERT(aReader && aRetval); + + MOZ_ASSERT(aReader); MOZ_ASSERT(aTag == SCTAG_DOM_FILE || aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || aTag == SCTAG_DOM_BLOB); + MOZ_ASSERT(aRetval); aRetval->tag = aTag; - // If it's not a MutableFile, it's a Blob or a File. uint64_t size; - if (!JS_ReadBytes(aReader, &size, sizeof(uint64_t))) { - NS_WARNING("Failed to read size!"); + if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) { return false; } + aRetval->size = NativeEndian::swapFromLittleEndian(size); nsCString type; - if (!StructuredCloneReadString(aReader, type)) { + if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) { return false; } + CopyUTF8toUTF16(type, aRetval->type); // Blobs are done. @@ -1478,73 +545,261 @@ IDBObjectStore::ReadBlobOrFile(JSStructuredCloneReader* aReader, return true; } - NS_ASSERTION(aTag == SCTAG_DOM_FILE || - aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE, "Huh?!"); + MOZ_ASSERT(aTag == SCTAG_DOM_FILE || + aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE); uint64_t lastModifiedDate; if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) { lastModifiedDate = UINT64_MAX; - } - else { - if(!JS_ReadBytes(aReader, &lastModifiedDate, sizeof(lastModifiedDate))) { - NS_WARNING("Failed to read lastModifiedDate"); + } else { + if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate, + sizeof(lastModifiedDate)))) { return false; } lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate); } + aRetval->lastModifiedDate = lastModifiedDate; nsCString name; - if (!StructuredCloneReadString(aReader, name)) { + if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) { return false; } + CopyUTF8toUTF16(name, aRetval->name); return true; } -// static +class ValueDeserializationHelper +{ +public: + static bool + CreateAndWrapMutableFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const MutableFileData& aData, + JS::MutableHandle aResult) + { + MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aDatabase); + MOZ_ASSERT(aFile.mFileInfo); + + nsRefPtr mutableFile = + IDBMutableFile::Create(aDatabase, + aData.name, + aData.type, + aFile.mFileInfo.forget()); + MOZ_ASSERT(mutableFile); + + JS::Rooted result(aCx, mutableFile->WrapObject(aCx)); + if (NS_WARN_IF(!result)) { + return false; + } + + aResult.set(result); + return true; + } + + static bool + CreateAndWrapBlobOrFile(JSContext* aCx, + StructuredCloneFile& aFile, + const BlobOrFileData& aData, + JS::MutableHandle aResult) + { + MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || + aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || + aData.tag == SCTAG_DOM_BLOB); + MOZ_ASSERT(aFile.mFile); + + MOZ_ASSERT(NS_IsMainThread(), + "This wrapping currently only works on the main thread!"); + + if (aData.tag == SCTAG_DOM_BLOB) { + if (NS_WARN_IF(!ResolveMysteryBlob(aFile.mFile, + aData.type, + aData.size))) { + return false; + } + + JS::Rooted wrappedBlob(aCx); + nsresult rv = + nsContentUtils::WrapNative(aCx, + aFile.mFile, + &NS_GET_IID(nsIDOMBlob), + &wrappedBlob); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + aResult.set(&wrappedBlob.toObject()); + return true; + } + + nsCOMPtr domFile = do_QueryInterface(aFile.mFile); + MOZ_ASSERT(domFile); + + if (NS_WARN_IF(!ResolveMysteryFile(domFile, + aData.name, + aData.type, + aData.size, + aData.lastModifiedDate))) { + return false; + } + + JS::Rooted wrappedFile(aCx); + nsresult rv = + nsContentUtils::WrapNative(aCx, + aFile.mFile, + &NS_GET_IID(nsIDOMFile), + &wrappedFile); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + aResult.set(&wrappedFile.toObject()); + return true; + } +}; + +class IndexDeserializationHelper +{ +public: + static bool + CreateAndWrapMutableFile(JSContext* aCx, + IDBDatabase* aDatabase, + StructuredCloneFile& aFile, + const MutableFileData& aData, + JS::MutableHandle aResult) + { + MOZ_ASSERT(!aDatabase); + + // MutableFile can't be used in index creation, so just make a dummy object. + JS::Rooted obj(aCx, + JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + + if (NS_WARN_IF(!obj)) { + return false; + } + + aResult.set(obj); + return true; + } + + static bool + CreateAndWrapBlobOrFile(JSContext* aCx, + StructuredCloneFile& aFile, + const BlobOrFileData& aData, + JS::MutableHandle aResult) + { + MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || + aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || + aData.tag == SCTAG_DOM_BLOB); + + // The following properties are available for use in index creation + // Blob.size + // Blob.type + // File.name + // File.lastModifiedDate + + JS::Rooted obj(aCx, + JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + if (NS_WARN_IF(!obj)) { + return false; + } + + // Technically these props go on the proto, but this detail won't change + // the results of index creation. + + JS::Rooted type(aCx, + JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length())); + if (NS_WARN_IF(!type)) { + return false; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, + obj, + "size", + double(aData.size), + 0))) { + return false; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "type", type, 0))) { + return false; + } + + if (aData.tag == SCTAG_DOM_BLOB) { + aResult.set(obj); + return true; + } + + JS::Rooted name(aCx, + JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length())); + if (NS_WARN_IF(!name)) { + return false; + } + + JS::Rooted date(aCx, + JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); + if (NS_WARN_IF(!date)) { + return false; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "name", name, 0))) { + return false; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0))) { + return false; + } + + aResult.set(obj); + return true; + } +}; + template JSObject* -IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx, - JSStructuredCloneReader* aReader, - uint32_t aTag, - uint32_t aData, - void* aClosure) +CommonStructuredCloneReadCallback(JSContext* aCx, + JSStructuredCloneReader* aReader, + uint32_t aTag, + uint32_t aData, + void* aClosure) { // We need to statically assert that our tag values are what we expect // so that if people accidentally change them they notice. - static_assert(SCTAG_DOM_BLOB == 0xFFFF8001 && - SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xFFFF8002 && - SCTAG_DOM_MUTABLEFILE == 0xFFFF8004 && - SCTAG_DOM_FILE == 0xFFFF8005, + static_assert(SCTAG_DOM_BLOB == 0xffff8001 && + SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 && + SCTAG_DOM_MUTABLEFILE == 0xffff8004 && + SCTAG_DOM_FILE == 0xffff8005, "You changed our structured clone tag values and just ate " "everyone's IndexedDB data. I hope you are happy."); if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || - aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_BLOB || - aTag == SCTAG_DOM_FILE) { - StructuredCloneReadInfo* cloneReadInfo = - reinterpret_cast(aClosure); + aTag == SCTAG_DOM_FILE || + aTag == SCTAG_DOM_MUTABLEFILE) { + auto* cloneReadInfo = static_cast(aClosure); if (aData >= cloneReadInfo->mFiles.Length()) { - NS_ERROR("Bad blob index!"); + MOZ_ASSERT(false, "Bad index value!"); return nullptr; } StructuredCloneFile& file = cloneReadInfo->mFiles[aData]; - IDBDatabase* database = cloneReadInfo->mDatabase; + + JS::Rooted result(aCx); if (aTag == SCTAG_DOM_MUTABLEFILE) { MutableFileData data; - if (!ReadMutableFile(aReader, &data)) { + if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) { return nullptr; } - JS::Rooted result(aCx); if (NS_WARN_IF(!Traits::CreateAndWrapMutableFile(aCx, - database, + cloneReadInfo->mDatabase, file, data, &result))) { @@ -1555,11 +810,18 @@ IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx, } BlobOrFileData data; - if (!ReadBlobOrFile(aReader, aTag, &data)) { + if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) { return nullptr; } - return Traits::CreateAndWrapBlobOrFile(aCx, database, file, data); + if (NS_WARN_IF(!Traits::CreateAndWrapBlobOrFile(aCx, + file, + data, + &result))) { + return nullptr; + } + + return result; } const JSStructuredCloneCallbacks* runtimeCallbacks = @@ -1573,182 +835,144 @@ IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx, } // static -bool -IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx, - JSStructuredCloneWriter* aWriter, - JS::Handle aObj, - void* aClosure) +void +ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer) { - StructuredCloneWriteInfo* cloneWriteInfo = - reinterpret_cast(aClosure); - - if (JS_GetClass(aObj) == &sDummyPropJSClass) { - NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0, - "We should not have been here before!"); - cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter); - - uint64_t value = 0; - // Omit endian swap - return JS_WriteBytes(aWriter, &value, sizeof(value)); + if (aBuffer.data()) { + aBuffer.clear(); } +} - IDBTransaction* transaction = cloneWriteInfo->mTransaction; - FileManager* fileManager = transaction->Database()->Manager(); +} // anonymous namespace - IDBMutableFile* mutableFile = nullptr; - if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) { - nsRefPtr fileInfo = mutableFile->GetFileInfo(); - MOZ_ASSERT(fileInfo); +const JSClass IDBObjectStore::sDummyPropJSClass = { + "IDBObjectStore Dummy", + 0 /* flags */, + JS_PropertyStub /* addProperty */, + JS_DeletePropertyStub /* delProperty */, + JS_PropertyStub /* getProperty */, + JS_StrictPropertyStub /* setProperty */, + JS_EnumerateStub /* enumerate */, + JS_ResolveStub /* resolve */, + JS_ConvertStub /* convert */, + JSCLASS_NO_OPTIONAL_MEMBERS +}; - // Throw when trying to store mutable files across databases. - if (fileInfo->Manager() != fileManager) { - return false; - } +IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction, + const ObjectStoreSpec* aSpec) + : mTransaction(aTransaction) + , mCachedKeyPath(JSVAL_VOID) + , mSpec(aSpec) + , mId(aSpec->metadata().id()) + , mRooted(false) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); + MOZ_ASSERT(aSpec); - NS_ConvertUTF16toUTF8 convType(mutableFile->Type()); - uint32_t convTypeLength = - NativeEndian::swapToLittleEndian(convType.Length()); + SetIsDOMBinding(); +} - NS_ConvertUTF16toUTF8 convName(mutableFile->Name()); - uint32_t convNameLength = - NativeEndian::swapToLittleEndian(convName.Length()); +IDBObjectStore::~IDBObjectStore() +{ + AssertIsOnOwningThread(); - if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, - cloneWriteInfo->mFiles.Length()) || - !JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) || - !JS_WriteBytes(aWriter, convType.get(), convType.Length()) || - !JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) || - !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { - return false; - } - - StructuredCloneFile* file = cloneWriteInfo->mFiles.AppendElement(); - file->mFileInfo = fileInfo.forget(); - - return true; + if (mRooted) { + mCachedKeyPath = JSVAL_VOID; + mozilla::DropJSObjects(this); } +} - nsCOMPtr wrappedNative; - nsContentUtils::XPConnect()-> - GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); +// static +already_AddRefed +IDBObjectStore::Create(IDBTransaction* aTransaction, + const ObjectStoreSpec& aSpec) +{ + MOZ_ASSERT(aTransaction); + aTransaction->AssertIsOnOwningThread(); - if (wrappedNative) { - nsISupports* supports = wrappedNative->Native(); + nsRefPtr objectStore = + new IDBObjectStore(aTransaction, &aSpec); - nsCOMPtr blob = do_QueryInterface(supports); - if (blob) { - nsCOMPtr inputStream; - - // Check if it is a blob created from this db or the blob was already - // stored in this db - nsRefPtr fileInfo = transaction->GetFileInfo(blob); - if (!fileInfo && fileManager) { - fileInfo = blob->GetFileInfo(fileManager); - - if (!fileInfo) { - fileInfo = fileManager->GetNewFileInfo(); - if (!fileInfo) { - NS_WARNING("Failed to get new file info!"); - return false; - } - - if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { - NS_WARNING("Failed to get internal steam!"); - return false; - } - - transaction->AddFileInfo(blob, fileInfo); - } - } - - uint64_t size; - if (NS_FAILED(blob->GetSize(&size))) { - NS_WARNING("Failed to get size!"); - return false; - } - size = NativeEndian::swapToLittleEndian(size); - - nsString type; - if (NS_FAILED(blob->GetType(type))) { - NS_WARNING("Failed to get type!"); - return false; - } - NS_ConvertUTF16toUTF8 convType(type); - uint32_t convTypeLength = - NativeEndian::swapToLittleEndian(convType.Length()); - - nsCOMPtr file = do_QueryInterface(blob); - - if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB, - cloneWriteInfo->mFiles.Length()) || - !JS_WriteBytes(aWriter, &size, sizeof(size)) || - !JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) || - !JS_WriteBytes(aWriter, convType.get(), convType.Length())) { - return false; - } - - if (file) { - uint64_t lastModifiedDate = 0; - if (NS_FAILED(file->GetMozLastModifiedDate(&lastModifiedDate))) { - NS_WARNING("Failed to get last modified date!"); - return false; - } - - lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate); - - nsString name; - if (NS_FAILED(file->GetName(name))) { - NS_WARNING("Failed to get name!"); - return false; - } - NS_ConvertUTF16toUTF8 convName(name); - uint32_t convNameLength = - NativeEndian::swapToLittleEndian(convName.Length()); - - if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) || - !JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) || - !JS_WriteBytes(aWriter, convName.get(), convName.Length())) { - return false; - } - } - - StructuredCloneFile* cloneFile = cloneWriteInfo->mFiles.AppendElement(); - cloneFile->mFile = blob.forget(); - cloneFile->mFileInfo = fileInfo.forget(); - cloneFile->mInputStream = inputStream.forget(); - - return true; - } - } - - // try using the runtime callbacks - const JSStructuredCloneCallbacks* runtimeCallbacks = - js::GetContextStructuredCloneCallbacks(aCx); - if (runtimeCallbacks) { - return runtimeCallbacks->write(aCx, aWriter, aObj, nullptr); - } - - return false; + return objectStore.forget(); } // static nsresult -IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds, - nsTArray& aResult) +IDBObjectStore::AppendIndexUpdateInfo( + int64_t aIndexID, + const KeyPath& aKeyPath, + bool aUnique, + bool aMultiEntry, + JSContext* aCx, + JS::Handle aVal, + nsTArray& aUpdateInfoArray) { - nsCharSeparatedTokenizerTemplate tokenizer(aFileIds, ' '); + nsresult rv; - while (tokenizer.hasMoreTokens()) { - nsString token(tokenizer.nextToken()); + if (!aMultiEntry) { + Key key; + rv = aKeyPath.ExtractKey(aCx, aVal, key); - NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!"); + // If an index's keyPath doesn't match an object, we ignore that object. + if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { + return NS_OK; + } - nsresult rv; - int32_t id = token.ToInteger(&rv); - NS_ENSURE_SUCCESS(rv, rv); - - int64_t* element = aResult.AppendElement(); - *element = id; + if (NS_FAILED(rv)) { + return rv; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId() = aIndexID; + updateInfo->value() = key; + + return NS_OK; + } + + JS::Rooted val(aCx); + if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) { + return NS_OK; + } + + if (JS_IsArrayObject(aCx, val)) { + JS::Rooted array(aCx, &val.toObject()); + uint32_t arrayLength; + if (NS_WARN_IF(!JS_GetArrayLength(aCx, array, &arrayLength))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + JS::Rooted arrayItem(aCx); + if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + Key value; + if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || + value.IsUnset()) { + // Not a value we can do anything with, ignore it. + continue; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId() = aIndexID; + updateInfo->value() = value; + } + } + else { + Key value; + if (NS_FAILED(value.SetFromJSVal(aCx, val)) || + value.IsUnset()) { + // Not a value we can do anything with, ignore it. + return NS_OK; + } + + IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement(); + updateInfo->indexId() = aIndexID; + updateInfo->value() = value; } return NS_OK; @@ -1756,111 +980,108 @@ IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds, // static void -IDBObjectStore::ConvertActorsToBlobs( - const InfallibleTArray& aActors, - nsTArray& aFiles) +IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aFiles.IsEmpty(), "Should be empty!"); - - if (!aActors.IsEmpty()) { - NS_ASSERTION(ContentChild::GetSingleton(), "This should never be null!"); - - uint32_t length = aActors.Length(); - aFiles.SetCapacity(length); - - for (uint32_t index = 0; index < length; index++) { - BlobChild* actor = static_cast(aActors[index]); - - StructuredCloneFile* file = aFiles.AppendElement(); - file->mFile = actor->GetBlob(); - } + // This is kind of tricky, we only want to release stuff on the main thread, + // but we can end up being called on other threads if we have already been + // cleared on the main thread. + if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) { + return; } + + // If there's something to clear, we should be on the main thread. + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer); + aReadInfo.mFiles.Clear(); } // static -nsresult -IDBObjectStore::ConvertBlobsToActors( - nsIContentParent* aContentParent, - FileManager* aFileManager, - const nsTArray& aFiles, - InfallibleTArray& aActors) +bool +IDBObjectStore::DeserializeValue(JSContext* aCx, + StructuredCloneReadInfo& aCloneReadInfo, + JS::MutableHandle aValue) { - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aContentParent, "Null contentParent!"); - NS_ASSERTION(aFileManager, "Null file manager!"); + MOZ_ASSERT(aCx); - if (!aFiles.IsEmpty()) { - nsCOMPtr directory = aFileManager->GetDirectory(); - if (!directory) { - IDB_WARNING("Failed to get directory!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - uint32_t fileCount = aFiles.Length(); - aActors.SetCapacity(fileCount); - - for (uint32_t index = 0; index < fileCount; index++) { - const StructuredCloneFile& file = aFiles[index]; - NS_ASSERTION(file.mFileInfo, "This should never be null!"); - - nsCOMPtr nativeFile = - aFileManager->GetFileForId(directory, file.mFileInfo->Id()); - if (!nativeFile) { - IDB_WARNING("Failed to get file!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - nsCOMPtr blob = DOMFile::CreateFromFile(nativeFile, - file.mFileInfo); - - BlobParent* actor = - aContentParent->GetOrCreateActorForBlob(blob); - if (!actor) { - // This can only fail if the child has crashed. - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - aActors.AppendElement(actor); - } + if (aCloneReadInfo.mData.IsEmpty()) { + aValue.setUndefined(); + return true; } - return NS_OK; -} + auto* data = reinterpret_cast(aCloneReadInfo.mData.Elements()); + size_t dataLen = aCloneReadInfo.mData.Length(); -IDBObjectStore::IDBObjectStore() -: mId(INT64_MIN), - mKeyPath(0), - mCachedKeyPath(JSVAL_VOID), - mRooted(false), - mAutoIncrement(false), - mActorChild(nullptr), - mActorParent(nullptr) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(!(dataLen % sizeof(*data))); - SetIsDOMBinding(); -} + JSAutoRequest ar(aCx); -IDBObjectStore::~IDBObjectStore() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); + static JSStructuredCloneCallbacks callbacks = { + CommonStructuredCloneReadCallback, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr + }; + + if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION, + aValue, &callbacks, &aCloneReadInfo)) { + return false; } - if (mRooted) { - mCachedKeyPath = JSVAL_VOID; - mozilla::DropJSObjects(this); - } + return true; } +// static +bool +IDBObjectStore::DeserializeIndexValue(JSContext* aCx, + StructuredCloneReadInfo& aCloneReadInfo, + JS::MutableHandle aValue) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aCx); + + if (aCloneReadInfo.mData.IsEmpty()) { + aValue.setUndefined(); + return true; + } + + size_t dataLen = aCloneReadInfo.mData.Length(); + + uint64_t* data = + const_cast(reinterpret_cast( + aCloneReadInfo.mData.Elements())); + + MOZ_ASSERT(!(dataLen % sizeof(*data))); + + JSAutoRequest ar(aCx); + + static JSStructuredCloneCallbacks callbacks = { + CommonStructuredCloneReadCallback, + nullptr, + nullptr + }; + + if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION, + aValue, &callbacks, &aCloneReadInfo)) { + return false; + } + + return true; +} + +#ifdef DEBUG + +void +IDBObjectStore::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mTransaction); + mTransaction->AssertIsOnOwningThread(); +} + +#endif // DEBUG + nsresult IDBObjectStore::GetAddInfo(JSContext* aCx, JS::Handle aValue, @@ -1869,15 +1090,15 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, Key& aKey, nsTArray& aUpdateInfoArray) { - nsresult rv; - // Return DATA_ERR if a key was passed in and this objectStore uses inline // keys. if (!aKeyVal.isUndefined() && HasValidKeyPath()) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } - JSAutoRequest ar(aCx); + bool isAutoIncrement = AutoIncrement(); + + nsresult rv; if (!HasValidKeyPath()) { // Out-of-line keys must be passed in. @@ -1885,8 +1106,7 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, if (NS_FAILED(rv)) { return rv; } - } - else if (!mAutoIncrement) { + } else if (!isAutoIncrement) { rv = GetKeyPath().ExtractKey(aCx, aValue, aKey); if (NS_FAILED(rv)) { return rv; @@ -1895,31 +1115,38 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, // Return DATA_ERR if no key was specified this isn't an autoIncrement // objectStore. - if (aKey.IsUnset() && !mAutoIncrement) { + if (aKey.IsUnset() && !isAutoIncrement) { return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; } // Figure out indexes and the index values to update here. - uint32_t count = mInfo->indexes.Length(); - aUpdateInfoArray.SetCapacity(count); // Pretty good estimate - for (uint32_t indexesIndex = 0; indexesIndex < count; indexesIndex++) { - const IndexInfo& indexInfo = mInfo->indexes[indexesIndex]; + const nsTArray& indexes = mSpec->indexes(); - rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath, - indexInfo.unique, indexInfo.multiEntry, aCx, + const uint32_t idxCount = indexes.Length(); + aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate + + for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) { + const IndexMetadata& metadata = indexes[idxIndex]; + + rv = AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(), + metadata.unique(), metadata.multiEntry(), aCx, aValue, aUpdateInfoArray); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } - GetAddInfoClosure data = {this, aCloneWriteInfo, aValue}; + GetAddInfoClosure data(aCloneWriteInfo, aValue); - if (mAutoIncrement && HasValidKeyPath()) { - NS_ASSERTION(aKey.IsUnset(), "Shouldn't have gotten the key yet!"); + if (isAutoIncrement && HasValidKeyPath()) { + MOZ_ASSERT(aKey.IsUnset()); - rv = GetKeyPath().ExtractOrCreateKey(aCx, aValue, aKey, - &GetAddInfoCallback, &data); - } - else { + rv = GetKeyPath().ExtractOrCreateKey(aCx, + aValue, + aKey, + &GetAddInfoCallback, + &data); + } else { rv = GetAddInfoCallback(aCx, &data); } @@ -1927,157 +1154,127 @@ IDBObjectStore::GetAddInfo(JSContext* aCx, } already_AddRefed -IDBObjectStore::AddOrPut(JSContext* aCx, JS::Handle aValue, +IDBObjectStore::AddOrPut(JSContext* aCx, + JS::Handle aValue, JS::Handle aKey, - bool aOverwrite, ErrorResult& aRv) + bool aOverwrite, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aCx); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!IsWriteAllowed()) { + if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } - StructuredCloneWriteInfo cloneWriteInfo; + JS::Rooted value(aCx, aValue); Key key; + StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database()); nsTArray updateInfo; - JS::Rooted value(aCx, aValue); aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo); if (aRv.Failed()) { return nullptr; } - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + FallibleTArray cloneData; + if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes()))) { + aRv = NS_ERROR_OUT_OF_MEMORY; return nullptr; } - nsRefPtr helper = - new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, - aOverwrite, updateInfo); + // XXX Remove this + memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(), + cloneWriteInfo.mCloneBuffer.nbytes()); - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + cloneWriteInfo.mCloneBuffer.clear(); -#ifdef IDB_PROFILER_USE_MARKS - if (aOverwrite) { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).%s(%s)", - "IDBRequest[%llu] MT IDBObjectStore.put()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), - key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } - else { - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).add(%s)", - "IDBRequest[%llu] MT IDBObjectStore.add()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), - key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } -#endif + ObjectStoreAddPutParams commonParams; + commonParams.objectStoreId() = Id(); + commonParams.cloneInfo().data().SwapElements(cloneData); + commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp; + commonParams.key() = key; + commonParams.indexUpdateInfos().SwapElements(updateInfo); - return request.forget(); -} + // Convert any blobs or fileIds into DatabaseFileOrMutableFileId. + nsTArray& blobOrFileInfos = + cloneWriteInfo.mBlobOrFileInfos; -nsresult -IDBObjectStore::AddOrPutInternal( - const SerializedStructuredCloneWriteInfo& aCloneWriteInfo, - const Key& aKey, - const InfallibleTArray& aUpdateInfoArray, - const nsTArray >& aBlobs, - bool aOverwrite, - IDBRequest** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + FallibleTArray> fileInfosToKeepAlive; - if (!mTransaction->IsOpen()) { - return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR; - } + if (!blobOrFileInfos.IsEmpty()) { + const uint32_t count = blobOrFileInfos.Length(); - if (!IsWriteAllowed()) { - return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR; - } - - nsRefPtr request = GenerateRequest(this); - IDB_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - StructuredCloneWriteInfo cloneWriteInfo; - if (!cloneWriteInfo.SetFromSerialized(aCloneWriteInfo)) { - IDB_WARNING("Failed to copy structured clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!aBlobs.IsEmpty()) { - FileManager* fileManager = Transaction()->Database()->Manager(); - NS_ASSERTION(fileManager, "Null file manager?!"); - - uint32_t length = aBlobs.Length(); - cloneWriteInfo.mFiles.SetCapacity(length); - - for (uint32_t index = 0; index < length; index++) { - const nsCOMPtr& blob = aBlobs[index]; - - nsCOMPtr inputStream; - - nsRefPtr fileInfo = Transaction()->GetFileInfo(blob); - if (!fileInfo) { - fileInfo = blob->GetFileInfo(fileManager); - - if (!fileInfo) { - fileInfo = fileManager->GetNewFileInfo(); - if (!fileInfo) { - IDB_WARNING("Failed to get new file info!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_FAILED(blob->GetInternalStream(getter_AddRefs(inputStream)))) { - IDB_WARNING("Failed to get internal steam!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - // XXXbent This is where we should send a message back to the child to - // update the file id. - - Transaction()->AddFileInfo(blob, fileInfo); - } - } - - StructuredCloneFile* file = cloneWriteInfo.mFiles.AppendElement(); - file->mFile = blob; - file->mFileInfo.swap(fileInfo); - file->mInputStream.swap(inputStream); + FallibleTArray fileActorOrMutableFileIds; + if (NS_WARN_IF(!fileActorOrMutableFileIds.SetCapacity(count))) { + aRv = NS_ERROR_OUT_OF_MEMORY; + return nullptr; } + + IDBDatabase* database = mTransaction->Database(); + + for (uint32_t index = 0; index < count; index++) { + StructuredCloneWriteInfo::BlobOrFileInfo& blobOrFileInfo = + blobOrFileInfos[index]; + MOZ_ASSERT((blobOrFileInfo.mBlob && !blobOrFileInfo.mFileInfo) || + (!blobOrFileInfo.mBlob && blobOrFileInfo.mFileInfo)); + + if (blobOrFileInfo.mBlob) { + PBackgroundIDBDatabaseFileChild* fileActor = + database->GetOrCreateFileActorForBlob(blobOrFileInfo.mBlob); + if (NS_WARN_IF(!fileActor)) { + IDB_REPORT_INTERNAL_ERR(); + aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + return nullptr; + } + + MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileActor)); + } else { + const int64_t fileId = blobOrFileInfo.mFileInfo->Id(); + MOZ_ASSERT(fileId > 0); + + MOZ_ALWAYS_TRUE(fileActorOrMutableFileIds.AppendElement(fileId)); + + nsRefPtr* newFileInfo = fileInfosToKeepAlive.AppendElement(); + if (NS_WARN_IF(!newFileInfo)) { + aRv = NS_ERROR_OUT_OF_MEMORY; + return nullptr; + } + + newFileInfo->swap(blobOrFileInfo.mFileInfo); + } + } + + commonParams.files().SwapElements(fileActorOrMutableFileIds); } - Key key(aKey); + RequestParams params; + if (aOverwrite) { + params = ObjectStorePutParams(commonParams); + } else { + params = ObjectStoreAddParams(commonParams); + } - nsTArray updateInfo(aUpdateInfoArray); + nsRefPtr request = GenerateRequest(this); + MOZ_ASSERT(request); - nsRefPtr helper = - new AddHelper(mTransaction, request, this, Move(cloneWriteInfo), key, - aOverwrite, updateInfo); + BackgroundRequestChild* actor = new BackgroundRequestChild(request); - nsresult rv = helper->DispatchToTransactionPool(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + mTransaction->StartRequest(actor, params); + + if (!fileInfosToKeepAlive.IsEmpty()) { + nsTArray> fileInfos; + fileInfosToKeepAlive.SwapElements(fileInfos); + + actor->HoldFileInfosUntilComplete(fileInfos); + MOZ_ASSERT(fileInfos.IsEmpty()); + } #ifdef IDB_PROFILER_USE_MARKS if (aOverwrite) { @@ -2089,8 +1286,7 @@ IDBObjectStore::AddOrPutInternal( IDB_PROFILER_STRING(Transaction()), IDB_PROFILER_STRING(this), key.IsUnset() ? "" : IDB_PROFILER_STRING(key)); - } - else { + } else { IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).add(%s)", "IDBRequest[%llu] MT IDBObjectStore.add()", @@ -2102,172 +1298,81 @@ IDBObjectStore::AddOrPutInternal( } #endif - request.forget(_retval); - return NS_OK; -} - -already_AddRefed -IDBObjectStore::GetInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aKeyRange, "Null pointer!"); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new GetHelper(mTransaction, request, this, aKeyRange); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).get(%s)", - "IDBRequest[%llu] MT IDBObjectStore.get()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - return request.forget(); } already_AddRefed -IDBObjectStore::GetAllInternal(IDBKeyRange* aKeyRange, - uint32_t aLimit, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new GetAllHelper(mTransaction, request, this, aKeyRange, aLimit); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "getAll(%s, %lu)", - "IDBRequest[%llu] MT IDBObjectStore.getAll()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - aLimit); - - return request.forget(); -} - -already_AddRefed -IDBObjectStore::GetAllKeysInternal(IDBKeyRange* aKeyRange, uint32_t aLimit, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new GetAllKeysHelper(mTransaction, request, this, aKeyRange, aLimit); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "getAllKeys(%s, %lu)", - "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - aLimit); - - return request.forget(); -} - -already_AddRefed -IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange, +IDBObjectStore::GetAllInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aKeyRange, "Null key range!"); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!IsWriteAllowed()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); + nsRefPtr keyRange; + aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); + if (NS_WARN_IF(aRv.Failed())) { return nullptr; } + const int64_t id = Id(); + + OptionalKeyRange optionalKeyRange; + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + optionalKeyRange = serializedKeyRange; + } else { + optionalKeyRange = void_t(); + } + + const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; + + RequestParams params; + if (aKeysOnly) { + params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit); + } else { + params = ObjectStoreGetAllParams(id, optionalKeyRange, limit); + } + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; + MOZ_ASSERT(request); + + BackgroundRequestChild* actor = new BackgroundRequestChild(request); + + mTransaction->StartRequest(actor, params); + +#ifdef IDB_PROFILER_USE_MARKS + if (aKeysOnly) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "getAllKeys(%s, %lu)", + "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKeyRange), + aLimit); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "getAll(%s, %lu)", + "IDBRequest[%llu] MT IDBObjectStore.getAll()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), + IDB_PROFILER_STRING(aKeyRange), + aLimit); } - - nsRefPtr helper = - new DeleteHelper(mTransaction, request, this, aKeyRange); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).delete(%s)", - "IDBRequest[%llu] MT IDBObjectStore.delete()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); +#endif return request.forget(); } @@ -2275,33 +1380,27 @@ IDBObjectStore::DeleteInternal(IDBKeyRange* aKeyRange, already_AddRefed IDBObjectStore::Clear(ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!IsWriteAllowed()) { + if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } + ObjectStoreClearParams params; + params.objectStoreId() = Id(); + nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + MOZ_ASSERT(request); - nsRefPtr helper(new ClearHelper(mTransaction, request, this)); + BackgroundRequestChild* actor = new BackgroundRequestChild(request); - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + mTransaction->StartRequest(actor, params); IDB_PROFILER_MARK("IndexedDB Request %llu: " "database(%s).transaction(%s).objectStore(%s).clear()", @@ -2314,290 +1413,58 @@ IDBObjectStore::Clear(ErrorResult& aRv) return request.forget(); } -already_AddRefed -IDBObjectStore::CountInternal(IDBKeyRange* aKeyRange, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new CountHelper(mTransaction, request, this, aKeyRange); - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s).count(%s)", - "IDBRequest[%llu] MT IDBObjectStore.count()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - - return request.forget(); -} - -already_AddRefed -IDBObjectStore::OpenCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - IDBCursor::Direction direction = - static_cast(aDirection); - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - nsRefPtr helper = - new OpenCursorHelper(mTransaction, request, this, aKeyRange, direction); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "openCursor(%s, %s)", - "IDBRequest[%llu] MT IDBObjectStore.openCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - - return request.forget(); -} - -nsresult -IDBObjectStore::OpenCursorFromChildProcess( - IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const SerializedStructuredCloneReadInfo& aCloneInfo, - nsTArray& aBlobs, - IDBCursor** _retval) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION((!aCloneInfo.dataLength && !aCloneInfo.data) || - (aCloneInfo.dataLength && aCloneInfo.data), - "Inconsistent clone info!"); - - IDBCursor::Direction direction = - static_cast(aDirection); - - StructuredCloneReadInfo cloneInfo; - - if (!cloneInfo.SetFromSerialized(aCloneInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - cloneInfo.mFiles.SwapElements(aBlobs); - - nsRefPtr cursor = - IDBCursor::Create(aRequest, mTransaction, this, direction, Key(), - EmptyCString(), EmptyCString(), aKey, Move(cloneInfo)); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(!cloneInfo.mCloneBuffer.data(), "Should have swapped!"); - - cursor.forget(_retval); - return NS_OK; -} - -nsresult -IDBObjectStore::OpenCursorFromChildProcess(IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - IDBCursor** _retval) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRequest); - - auto direction = static_cast(aDirection); - - nsRefPtr cursor = - IDBCursor::Create(aRequest, mTransaction, this, direction, Key(), - EmptyCString(), EmptyCString(), aKey); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - cursor.forget(_retval); - return NS_OK; -} - -already_AddRefed -IDBObjectStore::OpenKeyCursorInternal(IDBKeyRange* aKeyRange, size_t aDirection, - ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr request = GenerateRequest(this); - if (!request) { - IDB_WARNING("Failed to generate request!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - auto direction = static_cast(aDirection); - - nsRefPtr helper = - new OpenKeyCursorHelper(mTransaction, request, this, aKeyRange, direction); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - IDB_PROFILER_MARK("IndexedDB Request %llu: " - "database(%s).transaction(%s).objectStore(%s)." - "openKeyCursor(%s, %s)", - "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", - request->GetSerialNumber(), - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), - IDB_PROFILER_STRING(direction)); - - return request.forget(); -} - -void -IDBObjectStore::SetInfo(ObjectStoreInfo* aInfo) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); - NS_ASSERTION(aInfo != mInfo, "This is nonsense"); - - mInfo = aInfo; -} - -already_AddRefed -IDBObjectStore::CreateIndexInternal(const IndexInfo& aInfo, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - IndexInfo* indexInfo = mInfo->indexes.AppendElement(); - - indexInfo->name = aInfo.name; - indexInfo->id = aInfo.id; - indexInfo->keyPath = aInfo.keyPath; - indexInfo->unique = aInfo.unique; - indexInfo->multiEntry = aInfo.multiEntry; - - // Don't leave this in the list if we fail below! - AutoRemoveIndex autoRemove(mInfo, aInfo.name); - - nsRefPtr index = IDBIndex::Create(this, indexInfo, true); - - mCreatedIndexes.AppendElement(index); - - if (IndexedDatabaseManager::IsMainProcess()) { - nsRefPtr helper = - new CreateIndexHelper(mTransaction, index); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - } - - autoRemove.forget(); - - IDB_PROFILER_MARK("IndexedDB Pseudo-request: " - "database(%s).transaction(%s).objectStore(%s)." - "createIndex(%s)", - "MT IDBObjectStore.createIndex()", - IDB_PROFILER_STRING(Transaction()->Database()), - IDB_PROFILER_STRING(Transaction()), - IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index)); - - return index.forget(); -} - already_AddRefed IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (mTransaction->IsFinished()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } - IndexInfo* indexInfo = nullptr; - uint32_t indexCount = mInfo->indexes.Length(); - for (uint32_t index = 0; index < indexCount; index++) { - if (mInfo->indexes[index].name == aName) { - indexInfo = &(mInfo->indexes[index]); + const nsTArray& indexes = mSpec->indexes(); + + const IndexMetadata* metadata = nullptr; + + for (uint32_t idxCount = indexes.Length(), idxIndex = 0; + idxIndex < idxCount; + idxIndex++) { + const IndexMetadata& index = indexes[idxIndex]; + if (index.name() == aName) { + metadata = &index; break; } } - if (!indexInfo) { + if (!metadata) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return nullptr; } - nsRefPtr retval; - for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { - nsRefPtr& index = mCreatedIndexes[i]; - if (index->Name() == aName) { - retval = index; + const int64_t desiredId = metadata->id(); + + nsRefPtr index; + + for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0; + idxIndex < idxCount; + idxIndex++) { + nsRefPtr& existingIndex = mIndexes[idxIndex]; + + if (existingIndex->Id() == desiredId) { + index = existingIndex; break; } } - if (!retval) { - retval = IDBIndex::Create(this, indexInfo, false); - if (!retval) { - IDB_WARNING("Failed to create index!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + if (!index) { + index = IDBIndex::Create(this, *metadata); + MOZ_ASSERT(index); - if (!mCreatedIndexes.AppendElement(retval)) { - IDB_WARNING("Out of memory!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } + mIndexes.AppendElement(index); } - return retval.forget(); + return index.forget(); } NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore) @@ -2610,11 +1477,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) - - for (uint32_t i = 0; i < tmp->mCreatedIndexes.Length(); i++) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]"); - cb.NoteXPCOMChild(static_cast(tmp->mCreatedIndexes[i].get())); - } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) @@ -2622,7 +1485,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) // Don't unlink mTransaction! - tmp->mCreatedIndexes.Clear(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes); tmp->mCachedKeyPath = JSVAL_VOID; @@ -2646,8 +1509,15 @@ IDBObjectStore::WrapObject(JSContext* aCx) return IDBObjectStoreBinding::Wrap(aCx, this); } +nsPIDOMWindow* +IDBObjectStore::GetParentObject() const +{ + return mTransaction->GetParentObject(); +} + void -IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, +IDBObjectStore::GetKeyPath(JSContext* aCx, + JS::MutableHandle aResult, ErrorResult& aRv) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -2673,28 +1543,32 @@ IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, } already_AddRefed -IDBObjectStore::GetIndexNames(ErrorResult& aRv) +IDBObjectStore::IndexNames() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - nsRefPtr list(new DOMStringList()); + const nsTArray& indexes = mSpec->indexes(); - nsTArray& names = list->StringArray(); - uint32_t count = mInfo->indexes.Length(); - names.SetCapacity(count); + nsRefPtr list = new DOMStringList(); - for (uint32_t index = 0; index < count; index++) { - names.InsertElementSorted(mInfo->indexes[index].name); + if (!indexes.IsEmpty()) { + nsTArray& listNames = list->StringArray(); + listNames.SetCapacity(indexes.Length()); + + for (uint32_t index = 0; index < indexes.Length(); index++) { + listNames.InsertElementSorted(indexes[index].name()); + } } return list.forget(); } already_AddRefed -IDBObjectStore::Get(JSContext* aCx, JS::Handle aKey, +IDBObjectStore::Get(JSContext* aCx, + JS::Handle aKey, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); @@ -2703,7 +1577,9 @@ IDBObjectStore::Get(JSContext* aCx, JS::Handle aKey, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); + if (aRv.Failed()) { + return nullptr; + } if (!keyRange) { // Must specify a key or keyRange for get(). @@ -2711,52 +1587,50 @@ IDBObjectStore::Get(JSContext* aCx, JS::Handle aKey, return nullptr; } - return GetInternal(keyRange, aRv); + ObjectStoreGetParams params; + params.objectStoreId() = Id(); + keyRange->ToSerialized(params.keyRange()); + + nsRefPtr request = GenerateRequest(this); + MOZ_ASSERT(request); + + BackgroundRequestChild* actor = new BackgroundRequestChild(request); + + mTransaction->StartRequest(actor, params); + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).get(%s)", + "IDBRequest[%llu] MT IDBObjectStore.get()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + + return request.forget(); } already_AddRefed -IDBObjectStore::GetAll(JSContext* aCx, +IDBObjectStore::Delete(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } - - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - uint32_t limit = UINT32_MAX; - if (aLimit.WasPassed() && aLimit.Value() != 0) { - limit = aLimit.Value(); - } - - return GetAllInternal(keyRange, limit, aRv); -} - -already_AddRefed -IDBObjectStore::Delete(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); return nullptr; } - if (!IsWriteAllowed()) { + if (!mTransaction->IsWriteAllowed()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR); return nullptr; } nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); + if (NS_WARN_IF((aRv.Failed()))) { + return nullptr; + } if (!keyRange) { // Must specify a key or keyRange for delete(). @@ -2764,38 +1638,36 @@ IDBObjectStore::Delete(JSContext* aCx, JS::Handle aKey, return nullptr; } - return DeleteInternal(keyRange, aRv); -} + ObjectStoreDeleteParams params; + params.objectStoreId() = Id(); + keyRange->ToSerialized(params.keyRange()); -already_AddRefed -IDBObjectStore::OpenCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + nsRefPtr request = GenerateRequest(this); + MOZ_ASSERT(request); - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); - return nullptr; - } + BackgroundRequestChild* actor = new BackgroundRequestChild(request); - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); + mTransaction->StartRequest(actor, params); - IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - size_t argDirection = static_cast(direction); + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).delete(%s)", + "IDBRequest[%llu] MT IDBObjectStore.delete()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); - return OpenCursorInternal(keyRange, argDirection, aRv); + return request.forget(); } already_AddRefed -IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, +IDBObjectStore::CreateIndex(JSContext* aCx, + const nsAString& aName, const nsAString& aKeyPath, const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); KeyPath keyPath(0); if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) || @@ -2804,39 +1676,40 @@ IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, return nullptr; } - return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); + return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv); } already_AddRefed -IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, +IDBObjectStore::CreateIndex(JSContext* aCx, + const nsAString& aName, const Sequence& aKeyPath, const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv) { - NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!"); - - if (!aKeyPath.Length()) { - aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); - return nullptr; - } + AssertIsOnOwningThread(); KeyPath keyPath(0); - if (NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath))) { + if (aKeyPath.IsEmpty() || + NS_FAILED(KeyPath::Parse(aCx, aKeyPath, &keyPath)) || + !keyPath.IsValid()) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } - return CreateIndex(aCx, aName, keyPath, aOptionalParameters, aRv); + return CreateIndexInternal(aCx, aName, keyPath, aOptionalParameters, aRv); } already_AddRefed -IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, - KeyPath& aKeyPath, - const IDBIndexParameters& aOptionalParameters, - ErrorResult& aRv) +IDBObjectStore::CreateIndexInternal( + JSContext* aCx, + const nsAString& aName, + const KeyPath& aKeyPath, + const IDBIndexParameters& aOptionalParameters, + ErrorResult& aRv) { - // Check name and current mode - IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); + AssertIsOnOwningThread(); + + IDBTransaction* transaction = IDBTransaction::GetCurrent(); if (!transaction || transaction != mTransaction || @@ -2845,54 +1718,71 @@ IDBObjectStore::CreateIndex(JSContext* aCx, const nsAString& aName, return nullptr; } - bool found = false; - uint32_t indexCount = mInfo->indexes.Length(); - for (uint32_t index = 0; index < indexCount; index++) { - if (mInfo->indexes[index].name == aName) { - found = true; - break; + MOZ_ASSERT(transaction->IsOpen()); + + auto& indexes = const_cast&>(mSpec->indexes()); + for (uint32_t count = indexes.Length(), index = 0; + index < count; + index++) { + if (aName == indexes[index].name()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); + return nullptr; } } - if (found) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR); - return nullptr; - } - - NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); - -#ifdef DEBUG - for (uint32_t index = 0; index < mCreatedIndexes.Length(); index++) { - if (mCreatedIndexes[index]->Name() == aName) { - NS_ERROR("Already created this one!"); - } - } -#endif - if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return nullptr; } - DatabaseInfo* databaseInfo = mTransaction->DBInfo(); +#ifdef DEBUG + for (uint32_t count = mIndexes.Length(), index = 0; + index < count; + index++) { + MOZ_ASSERT(mIndexes[index]->Name() != aName); + } +#endif - IndexInfo info; + const IndexMetadata* oldMetadataElements = + indexes.IsEmpty() ? nullptr : indexes.Elements(); - info.name = aName; - info.id = databaseInfo->nextIndexId++; - info.keyPath = aKeyPath; - info.unique = aOptionalParameters.mUnique; - info.multiEntry = aOptionalParameters.mMultiEntry; + IndexMetadata* metadata = indexes.AppendElement( + IndexMetadata(transaction->NextIndexId(), nsString(aName), aKeyPath, + aOptionalParameters.mUnique, + aOptionalParameters.mMultiEntry)); - return CreateIndexInternal(info, aRv); + if (oldMetadataElements && + oldMetadataElements != indexes.Elements()) { + MOZ_ASSERT(indexes.Length() > 1); + + // Array got moved, update the spec pointers for all live indexes. + RefreshSpec(/* aMayDelete */ false); + } + + transaction->CreateIndex(this, *metadata); + + nsRefPtr index = IDBIndex::Create(this, *metadata); + MOZ_ASSERT(index); + + mIndexes.AppendElement(index); + + IDB_PROFILER_MARK("IndexedDB Pseudo-request: " + "database(%s).transaction(%s).objectStore(%s)." + "createIndex(%s)", + "MT IDBObjectStore.createIndex()", + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index)); + + return index.forget(); } void IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction(); + IDBTransaction* transaction = IDBTransaction::GetCurrent(); if (!transaction || transaction != mTransaction || @@ -2901,45 +1791,47 @@ IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv) return; } - NS_ASSERTION(mTransaction->IsOpen(), "Impossible!"); + MOZ_ASSERT(transaction->IsOpen()); - uint32_t index = 0; - for (; index < mInfo->indexes.Length(); index++) { - if (mInfo->indexes[index].name == aName) { + auto& metadataArray = const_cast&>(mSpec->indexes()); + + int64_t foundId = 0; + + for (uint32_t metadataCount = metadataArray.Length(), metadataIndex = 0; + metadataIndex < metadataCount; + metadataIndex++) { + const IndexMetadata& metadata = metadataArray[metadataIndex]; + MOZ_ASSERT(metadata.id()); + + if (aName == metadata.name()) { + foundId = metadata.id(); + + // Must do this before altering the metadata array! + for (uint32_t indexCount = mIndexes.Length(), indexIndex = 0; + indexIndex < indexCount; + indexIndex++) { + nsRefPtr& index = mIndexes[indexIndex]; + + if (index->Id() == foundId) { + index->NoteDeletion(); + mIndexes.RemoveElementAt(indexIndex); + break; + } + } + + metadataArray.RemoveElementAt(metadataIndex); + + RefreshSpec(/* aMayDelete */ false); break; } } - if (index == mInfo->indexes.Length()) { + if (!foundId) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); return; } - if (IndexedDatabaseManager::IsMainProcess()) { - nsRefPtr helper = - new DeleteIndexHelper(mTransaction, this, aName); - - nsresult rv = helper->DispatchToTransactionPool(); - if (NS_FAILED(rv)) { - IDB_WARNING("Failed to dispatch!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return; - } - } - else { - NS_ASSERTION(mActorChild, "Must have an actor here!"); - - mActorChild->SendDeleteIndex(nsString(aName)); - } - - mInfo->indexes.RemoveElementAt(index); - - for (uint32_t i = 0; i < mCreatedIndexes.Length(); i++) { - if (mCreatedIndexes[i]->Name() == aName) { - mCreatedIndexes.RemoveElementAt(i); - break; - } - } + transaction->DeleteIndex(this, foundId); IDB_PROFILER_MARK("IndexedDB Pseudo-request: " "database(%s).transaction(%s).objectStore(%s)." @@ -2963,41 +1855,47 @@ IDBObjectStore::Count(JSContext* aCx, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); - - return CountInternal(keyRange, aRv); -} - -already_AddRefed -IDBObjectStore::GetAllKeys(JSContext* aCx, - JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!mTransaction->IsOpen()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + if (aRv.Failed()) { return nullptr; } - nsRefPtr keyRange; - aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); + ObjectStoreCountParams params; + params.objectStoreId() = Id(); - uint32_t limit = UINT32_MAX; - if (aLimit.WasPassed() && aLimit.Value() != 0) { - limit = aLimit.Value(); + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + params.optionalKeyRange() = serializedKeyRange; + } else { + params.optionalKeyRange() = void_t(); } - return GetAllKeysInternal(keyRange, limit, aRv); + nsRefPtr request = GenerateRequest(this); + MOZ_ASSERT(request); + + BackgroundRequestChild* actor = new BackgroundRequestChild(request); + + mTransaction->StartRequest(actor, params); + + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s).count(%s)", + "IDBRequest[%llu] MT IDBObjectStore.count()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange)); + + return request.forget(); } already_AddRefed -IDBObjectStore::OpenKeyCursor(JSContext* aCx, - JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv) +IDBObjectStore::OpenCursorInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread()); + AssertIsOnOwningThread(); if (!mTransaction->IsOpen()) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); @@ -3006,2231 +1904,190 @@ IDBObjectStore::OpenKeyCursor(JSContext* aCx, nsRefPtr keyRange; aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange)); - ENSURE_SUCCESS(aRv, nullptr); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + int64_t objectStoreId = Id(); + + OptionalKeyRange optionalKeyRange; + + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + + optionalKeyRange = Move(serializedKeyRange); + } else { + optionalKeyRange = void_t(); + } IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection); - return OpenKeyCursorInternal(keyRange, static_cast(direction), aRv); -} - -inline nsresult -CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("IDBObjectStore", "CopyData", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - do { - char copyBuffer[FILE_COPY_BUFFER_SIZE]; - - uint32_t numRead; - rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead); - NS_ENSURE_SUCCESS(rv, rv); - - if (!numRead) { - break; - } - - uint32_t numWrite; - rv = aOutputStream->Write(copyBuffer, numRead, &numWrite); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - - NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE); - } while (true); - - rv = aOutputStream->Flush(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -ObjectStoreHelper::ReleaseMainThreadObjects() -{ - mObjectStore = nullptr; - AsyncConnectionHelper::ReleaseMainThreadObjects(); -} - -nsresult -ObjectStoreHelper::Dispatch(nsIEventTarget* aDatabaseThread) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("ObjectStoreHelper", "Dispatch", - js::ProfileEntry::Category::STORAGE); - - if (IndexedDatabaseManager::IsMainProcess()) { - return AsyncConnectionHelper::Dispatch(aDatabaseThread); - } - - // If we've been invalidated then there's no point sending anything to the - // parent process. - if (mObjectStore->Transaction()->Database()->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IndexedDBObjectStoreChild* objectStoreActor = mObjectStore->GetActorChild(); - NS_ASSERTION(objectStoreActor, "Must have an actor here!"); - - ObjectStoreRequestParams params; - - // Our "parent" process may be either the root process or another content - // process if this indexedDB is managed by a PBrowser that is managed by a - // PContentBridge. We need to find which one it is so that we can create - // PBlobs that are managed by the right nsIContentChild. - IndexedDBChild* rootActor = - static_cast(objectStoreActor->Manager()-> - Manager()->Manager()); - nsIContentChild* blobCreator; - if (rootActor->GetManagerContent()) { - blobCreator = rootActor->GetManagerContent(); - } else { - blobCreator = rootActor->GetManagerTab()->Manager(); - } - - nsresult rv = PackArgumentsForParentProcess(params, blobCreator); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NoDispatchEventTarget target; - rv = AsyncConnectionHelper::Dispatch(&target); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mActor = - new IndexedDBObjectStoreRequestChild(this, mObjectStore, params.type()); - objectStoreActor->SendPIndexedDBRequestConstructor(mActor, params); - - return NS_OK; -} - -void -NoRequestObjectStoreHelper::ReleaseMainThreadObjects() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mObjectStore = nullptr; - AsyncConnectionHelper::ReleaseMainThreadObjects(); -} - -nsresult -NoRequestObjectStoreHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - MOZ_CRASH(); -} - -AsyncConnectionHelper::ChildProcessSendResult -NoRequestObjectStoreHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - return Success_NotSent; -} - -nsresult -NoRequestObjectStoreHelper::OnSuccess() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - return NS_OK; -} - -void -NoRequestObjectStoreHelper::OnError() -{ - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mTransaction->Abort(GetResultCode()); -} - -// This is a duplicate of the js engine's byte munging in StructuredClone.cpp -uint64_t -ReinterpretDoubleAsUInt64(double d) -{ - union { - double d; - uint64_t u; - } pun; - pun.d = d; - return pun.u; -} - -nsresult -AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aConnection, "Passed a null connection!"); - - PROFILER_LABEL("AddHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - NS_WARNING("Refusing to add more data because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - - nsresult rv; - bool keyUnset = mKey.IsUnset(); - int64_t osid = mObjectStore->Id(); - const KeyPath& keyPath = mObjectStore->GetKeyPath(); - - // The "|| keyUnset" here is mostly a debugging tool. If a key isn't - // specified we should never have a collision and so it shouldn't matter - // if we allow overwrite or not. By not allowing overwrite we raise - // detectable errors rather than corrupting data - nsCOMPtr stmt = !mOverwrite || keyUnset ? - mTransaction->GetCachedStatement( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)") : - mTransaction->GetCachedStatement( - "INSERT OR REPLACE INTO object_data (object_store_id, key_value, data, " - "file_ids) " - "VALUES (:osid, :key_value, :data, :file_ids)"); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(!keyUnset || mObjectStore->IsAutoIncrement(), - "Should have key unless autoincrement"); - - int64_t autoIncrementNum = 0; - - if (mObjectStore->IsAutoIncrement()) { - if (keyUnset) { - autoIncrementNum = mObjectStore->Info()->nextAutoIncrementId; - - MOZ_ASSERT(autoIncrementNum > 0, - "Generated key must always be a positive integer"); - - if (autoIncrementNum > (1LL << 53)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - mKey.SetFromInteger(autoIncrementNum); - } - else if (mKey.IsFloat() && - mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) { - autoIncrementNum = floor(mKey.ToFloat()); - } - - if (keyUnset && keyPath.IsValid()) { - // Special case where someone put an object into an autoIncrement'ing - // objectStore with no key in its keyPath set. We needed to figure out - // which row id we would get above before we could set that properly. - - LittleEndian::writeUint64((char*)mCloneWriteInfo.mCloneBuffer.data() + - mCloneWriteInfo.mOffsetToKeyProp, - ReinterpretDoubleAsUInt64(static_cast( - autoIncrementNum))); - } - } - - mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value")); - - - // Compress the bytes before adding into the database. - const char* uncompressed = - reinterpret_cast(mCloneWriteInfo.mCloneBuffer.data()); - size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes(); - - // We don't have a smart pointer class that calls moz_free, so we need to - // manage | compressed | manually. - { - size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); - // moz_malloc is equivalent to NS_Alloc, which we use because mozStorage - // expects to be able to free the adopted pointer with NS_Free. - char* compressed = (char*)moz_malloc(compressedLength); - NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY); - - snappy::RawCompress(uncompressed, uncompressedLength, compressed, - &compressedLength); - - uint8_t* dataBuffer = reinterpret_cast(compressed); - size_t dataBufferLength = compressedLength; - - // If this call succeeds, | compressed | is now owned by the statement, and - // we are no longer responsible for it. - rv = stmt->BindAdoptedBlobByName(NS_LITERAL_CSTRING("data"), dataBuffer, - dataBufferLength); - if (NS_FAILED(rv)) { - moz_free(compressed); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - // Handle blobs - uint32_t length = mCloneWriteInfo.mFiles.Length(); - if (length) { - nsRefPtr fileManager = mDatabase->Manager(); - - nsCOMPtr directory = fileManager->GetDirectory(); - IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr journalDirectory = fileManager->EnsureJournalDirectory(); - IDB_ENSURE_TRUE(journalDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsAutoString fileIds; - - for (uint32_t index = 0; index < length; index++) { - StructuredCloneFile& cloneFile = mCloneWriteInfo.mFiles[index]; - - FileInfo* fileInfo = cloneFile.mFileInfo; - nsIInputStream* inputStream = cloneFile.mInputStream; - - int64_t id = fileInfo->Id(); - if (inputStream) { - // Create a journal file first - nsCOMPtr nativeFile = - fileManager->GetFileForId(journalDirectory, id); - IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // Now we can copy the blob - nativeFile = fileManager->GetFileForId(directory, id); - IDB_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - IDBDatabase* database = mObjectStore->Transaction()->Database(); - nsRefPtr outputStream = - FileOutputStream::Create(database->Type(), database->Group(), - database->Origin(), nativeFile); - IDB_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = CopyData(inputStream, outputStream); - if (NS_FAILED(rv) && - NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { - IDB_REPORT_INTERNAL_ERR(); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - - cloneFile.mFile->AddFileInfo(fileInfo); - } - - if (index) { - fileIds.Append(' '); - } - fileIds.AppendInt(id); - } - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds); - } - else { - rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids")); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->Execute(); - if (rv == NS_ERROR_STORAGE_CONSTRAINT) { - NS_ASSERTION(!keyUnset, "Generated key had a collision!?"); - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - int64_t objectDataId; - rv = aConnection->GetLastInsertRowID(&objectDataId); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // Update our indexes if needed. - if (mOverwrite || !mIndexUpdateInfo.IsEmpty()) { - rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey, mOverwrite, - objectDataId, mIndexUpdateInfo); - if (rv == NS_ERROR_STORAGE_CONSTRAINT) { - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - if (autoIncrementNum) { - mObjectStore->Info()->nextAutoIncrementId = autoIncrementNum + 1; - } - - return NS_OK; -} - -nsresult -AddHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - NS_ASSERTION(!mKey.IsUnset(), "Badness!"); - - mCloneWriteInfo.mCloneBuffer.clear(); - - return mKey.ToJSVal(aCx, aVal); -} - -void -AddHelper::ReleaseMainThreadObjects() -{ - IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo); - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -AddHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("AddHelper", "PackArgumentsForParentProcess", - js::ProfileEntry::Category::STORAGE); - - AddPutParams commonParams; - commonParams.cloneInfo() = mCloneWriteInfo; - commonParams.key() = mKey; - commonParams.indexUpdateInfos().AppendElements(mIndexUpdateInfo); - - const nsTArray& files = mCloneWriteInfo.mFiles; - - if (!files.IsEmpty()) { - uint32_t fileCount = files.Length(); - - InfallibleTArray& blobsChild = commonParams.blobsChild(); - blobsChild.SetCapacity(fileCount); - - NS_ASSERTION(aBlobCreator, "This should never be null!"); - - for (uint32_t index = 0; index < fileCount; index++) { - const StructuredCloneFile& file = files[index]; - - NS_ASSERTION(file.mFile, "This should never be null!"); - NS_ASSERTION(!file.mFileInfo, "This is not yet supported!"); - - BlobChild* actor = - aBlobCreator->GetOrCreateActorForBlob(file.mFile); - if (!actor) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - blobsChild.AppendElement(actor); - } - } - - if (mOverwrite) { - PutParams putParams; - putParams.commonParams() = commonParams; - aParams = putParams; - } - else { - AddParams addParams; - addParams.commonParams() = commonParams; - aParams = addParams; - } - - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -AddHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("AddHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else if (mOverwrite) { - PutResponse putResponse; - putResponse.key() = mKey; - response = putResponse; - } - else { - AddResponse addResponse; - addResponse.key() = mKey; - response = addResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -AddHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TAddResponse || - aResponseValue.type() == ResponseValue::TPutResponse, - "Bad response type!"); - - mKey = mOverwrite ? - aResponseValue.get_PutResponse().key() : - aResponseValue.get_AddResponse().key(); - - return NS_OK; -} - -nsresult -GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "Must have a key range here!"); - - PROFILER_LABEL("GetHelper", "DoDatabaseWork [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); - - NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = - stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (hasResult) { - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, - mDatabase, mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -GetHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal); - - mCloneReadInfo.mCloneBuffer.clear(); - - NS_ENSURE_TRUE(result, NS_ERROR_DOM_DATA_CLONE_ERR); - return NS_OK; -} - -void -GetHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "This should never be null!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("GetHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetParams params; - - mKeyRange->ToSerializedKeyRange(params.keyRange()); - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - InfallibleTArray blobsParent; - - if (NS_SUCCEEDED(aResultCode)) { - IDBDatabase* database = mObjectStore->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - const nsTArray& files = mCloneReadInfo.mFiles; - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobsParent); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - GetResponse getResponse; - getResponse.cloneInfo() = mCloneReadInfo; - getResponse.blobsParent().SwapElements(blobsParent); - response = getResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetHelper::UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetResponse, - "Bad response type!"); - - const GetResponse& getResponse = aResponseValue.get_GetResponse(); - const SerializedStructuredCloneReadInfo& cloneInfo = getResponse.cloneInfo(); - - NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) || - (cloneInfo.dataLength && cloneInfo.data), - "Inconsistent clone info!"); - - if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IDBObjectStore::ConvertActorsToBlobs(getResponse.blobsChild(), - mCloneReadInfo.mFiles); - return NS_OK; -} - -nsresult -DeleteHelper::DoDatabaseWork(mozIStorageConnection* /*aConnection */) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "Must have a key range here!"); - - PROFILER_LABEL("DeleteHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCString keyRangeClause; - mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("key_value"), keyRangeClause); - - NS_ASSERTION(!keyRangeClause.IsEmpty(), "Huh?!"); - - nsCString query = NS_LITERAL_CSTRING("DELETE FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -DeleteHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - aVal.setUndefined(); - return NS_OK; -} - -nsresult -DeleteHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(mKeyRange, "This should never be null!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "PackArgumentsForParentProcess", - js::ProfileEntry::Category::STORAGE); - - DeleteParams params; - - mKeyRange->ToSerializedKeyRange(params.keyRange()); - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -DeleteHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("DeleteHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - response = DeleteResponse(); - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -DeleteHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TDeleteResponse, - "Bad response type!"); - - return NS_OK; -} - -nsresult -ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aConnection, "Passed a null connection!"); - - PROFILER_LABEL("ClearHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - NS_LITERAL_CSTRING("DELETE FROM object_data " - "WHERE object_store_id = :osid")); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->Execute(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -ClearHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("ClearHelper", "PackArgumentsForParentProcess", - js::ProfileEntry::Category::STORAGE); - - aParams = ClearParams(); - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -ClearHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("ClearHelper", "SendResponseToChildProcess", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - response = ClearResponse(); - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -ClearHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TClearResponse, - "Bad response type!"); - - return NS_OK; -} - -nsresult -OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); - - nsCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(keyValue, keyRangeClause); - } - - nsAutoCString directionClause; - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AssignLiteral(" ORDER BY key_value ASC"); - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - directionClause.AssignLiteral(" ORDER BY key_value DESC"); - break; - - default: - NS_NOTREACHED("Unknown direction type!"); - } - - nsCString firstQuery = NS_LITERAL_CSTRING("SELECT key_value, data, file_ids " - "FROM object_data " - "WHERE object_store_id = :id") + - keyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT 1"); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement(firstQuery); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - mKey.Unset(); - return NS_OK; - } - - rv = mKey.SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, - mDatabase, mCloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - - // Now we need to make the query to get the next match. - keyRangeClause.Truncate(); - nsAutoCString continueToKeyRangeClause; - - NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - AppendConditionClause(keyValue, currentKey, false, false, - keyRangeClause); - AppendConditionClause(keyValue, currentKey, false, true, - continueToKeyRangeClause); - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, true, - !mKeyRange->IsUpperOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, true, - !mKeyRange->IsUpperOpen(), - continueToKeyRangeClause); - mRangeKey = mKeyRange->Upper(); - } - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, true, true, - continueToKeyRangeClause); - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, false, - !mKeyRange->IsLowerOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, false, - !mKeyRange->IsLowerOpen(), - continueToKeyRangeClause); - mRangeKey = mKeyRange->Lower(); - } - break; - - default: - NS_NOTREACHED("Unknown direction type!"); - } - - NS_NAMED_LITERAL_CSTRING(queryStart, "SELECT key_value, data, file_ids " - "FROM object_data " - "WHERE object_store_id = :id"); - - mContinueQuery = queryStart + keyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - - mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + - NS_LITERAL_CSTRING(" LIMIT "); - - return NS_OK; -} - -nsresult -OpenCursorHelper::EnsureCursor() -{ - if (mCursor || mKey.IsUnset()) { - return NS_OK; - } - - mSerializedCloneReadInfo = mCloneReadInfo; - - NS_ASSERTION(mSerializedCloneReadInfo.data && - mSerializedCloneReadInfo.dataLength, - "Shouldn't be possible!"); - - nsRefPtr cursor = - IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, - mRangeKey, mContinueQuery, mContinueToQuery, mKey, - Move(mCloneReadInfo)); - IDB_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!"); - - mCursor.swap(cursor); - return NS_OK; -} - -nsresult -OpenCursorHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - nsresult rv = EnsureCursor(); - NS_ENSURE_SUCCESS(rv, rv); - - if (mCursor) { - rv = WrapNative(aCx, mCursor, aVal); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - aVal.setUndefined(); - } - - return NS_OK; -} - -void -OpenCursorHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo); - - mCursor = nullptr; - - // These don't need to be released on the main thread but they're only valid - // as long as mCursor is set. - mSerializedCloneReadInfo.data = nullptr; - mSerializedCloneReadInfo.dataLength = 0; - - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -OpenCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - OpenCursorParams params; + if (aKeysOnly) { + ObjectStoreOpenKeyCursorParams openParams; + openParams.objectStoreId() = objectStoreId; + openParams.optionalKeyRange() = Move(optionalKeyRange); + openParams.direction() = direction; - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.direction() = mDirection; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -OpenCursorHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); - - PROFILER_MAIN_THREAD_LABEL("OpenCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - InfallibleTArray blobsParent; - - if (NS_SUCCEEDED(aResultCode)) { - IDBDatabase* database = mObjectStore->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - const nsTArray& files = mCloneReadInfo.mFiles; - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobsParent); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - } - } - - if (NS_SUCCEEDED(aResultCode)) { - nsresult rv = EnsureCursor(); - if (NS_FAILED(rv)) { - NS_WARNING("EnsureCursor failed!"); - aResultCode = rv; - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - OpenCursorResponse openCursorResponse; - - if (!mCursor) { - openCursorResponse = mozilla::void_t(); - } - else { - IndexedDBObjectStoreParent* objectStoreActor = - mObjectStore->GetActorParent(); - NS_ASSERTION(objectStoreActor, "Must have an actor here!"); - - IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); - NS_ASSERTION(requestActor, "Must have an actor here!"); - - NS_ASSERTION(mSerializedCloneReadInfo.data && - mSerializedCloneReadInfo.dataLength, - "Shouldn't be possible!"); - - ObjectStoreCursorConstructorParams params; - params.requestParent() = requestActor; - params.direction() = mDirection; - params.key() = mKey; - params.optionalCloneInfo() = mSerializedCloneReadInfo; - params.blobsParent().SwapElements(blobsParent); - - if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) { - return Error; - } - } - - response = openCursorResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -OpenCursorHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TOpenCursorResponse, - "Bad response type!"); - NS_ASSERTION(aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::Tvoid_t || - aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::TPIndexedDBCursorChild, - "Bad response union type!"); - NS_ASSERTION(!mCursor, "Shouldn't have this yet!"); - - const OpenCursorResponse& response = - aResponseValue.get_OpenCursorResponse(); - - switch (response.type()) { - case OpenCursorResponse::Tvoid_t: - break; - - case OpenCursorResponse::TPIndexedDBCursorChild: { - IndexedDBCursorChild* actor = - static_cast( - response.get_PIndexedDBCursorChild()); - - mCursor = actor->ForgetStrongCursor(); - NS_ASSERTION(mCursor, "This should never be null!"); - - } break; - - default: - MOZ_CRASH(); - } - - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - PROFILER_LABEL("OpenKeyCursorHelper", "DoDatabaseWork [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); - NS_NAMED_LITERAL_CSTRING(id, "id"); - NS_NAMED_LITERAL_CSTRING(openLimit, " LIMIT "); - - nsAutoCString queryStart = NS_LITERAL_CSTRING("SELECT ") + keyValue + - NS_LITERAL_CSTRING(" FROM object_data WHERE " - "object_store_id = :") + - id; - - nsAutoCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(keyValue, keyRangeClause); - } - - nsAutoCString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyValue; - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - directionClause.AppendLiteral(" ASC"); - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - directionClause.AppendLiteral(" DESC"); - break; - - default: - MOZ_CRASH("Unknown direction type!"); - } - - nsCString firstQuery = queryStart + keyRangeClause + directionClause + - openLimit + NS_LITERAL_CSTRING("1"); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement(firstQuery); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!hasResult) { - mKey.Unset(); - return NS_OK; - } - - rv = mKey.SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - - // Now we need to make the query to get the next match. - keyRangeClause.Truncate(); - nsAutoCString continueToKeyRangeClause; - - NS_NAMED_LITERAL_CSTRING(currentKey, "current_key"); - NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key"); - - switch (mDirection) { - case IDBCursor::NEXT: - case IDBCursor::NEXT_UNIQUE: - AppendConditionClause(keyValue, currentKey, false, false, - keyRangeClause); - AppendConditionClause(keyValue, currentKey, false, true, - continueToKeyRangeClause); - if (mKeyRange && !mKeyRange->Upper().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, true, - !mKeyRange->IsUpperOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, true, - !mKeyRange->IsUpperOpen(), - continueToKeyRangeClause); - mRangeKey = mKeyRange->Upper(); - } - break; - - case IDBCursor::PREV: - case IDBCursor::PREV_UNIQUE: - AppendConditionClause(keyValue, currentKey, true, false, keyRangeClause); - AppendConditionClause(keyValue, currentKey, true, true, - continueToKeyRangeClause); - if (mKeyRange && !mKeyRange->Lower().IsUnset()) { - AppendConditionClause(keyValue, rangeKey, false, - !mKeyRange->IsLowerOpen(), keyRangeClause); - AppendConditionClause(keyValue, rangeKey, false, - !mKeyRange->IsLowerOpen(), - continueToKeyRangeClause); - mRangeKey = mKeyRange->Lower(); - } - break; - - default: - MOZ_CRASH("Unknown direction type!"); - } - - mContinueQuery = queryStart + keyRangeClause + directionClause + openLimit; - mContinueToQuery = queryStart + continueToKeyRangeClause + directionClause + - openLimit; - - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::EnsureCursor() -{ - MOZ_ASSERT(NS_IsMainThread()); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "EnsureCursor [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - if (mCursor || mKey.IsUnset()) { - return NS_OK; - } - - mCursor = IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, - mRangeKey, mContinueQuery, mContinueToQuery, - mKey); - IDB_ENSURE_TRUE(mCursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -OpenKeyCursorHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - MOZ_ASSERT(NS_IsMainThread()); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "GetSuccessResult [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = EnsureCursor(); - NS_ENSURE_SUCCESS(rv, rv); - - if (mCursor) { - rv = WrapNative(aCx, mCursor, aVal); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - aVal.setUndefined(); - } - - return NS_OK; -} - -void -OpenKeyCursorHelper::ReleaseMainThreadObjects() -{ - MOZ_ASSERT(NS_IsMainThread()); - - mKeyRange = nullptr; - mCursor = nullptr; - - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -OpenKeyCursorHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - OpenKeyCursorParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.direction() = mDirection; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -OpenKeyCursorHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(!mCursor); - - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - MOZ_ASSERT(actor); - - if (NS_SUCCEEDED(aResultCode)) { - nsresult rv = EnsureCursor(); - if (NS_FAILED(rv)) { - NS_WARNING("EnsureCursor failed!"); - aResultCode = rv; - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; + params = Move(openParams); } else { - OpenCursorResponse openCursorResponse; + ObjectStoreOpenCursorParams openParams; + openParams.objectStoreId() = objectStoreId; + openParams.optionalKeyRange() = Move(optionalKeyRange); + openParams.direction() = direction; - if (!mCursor) { - openCursorResponse = mozilla::void_t(); - } - else { - IndexedDBObjectStoreParent* objectStoreActor = - mObjectStore->GetActorParent(); - MOZ_ASSERT(objectStoreActor); - - IndexedDBRequestParentBase* requestActor = mRequest->GetActorParent(); - MOZ_ASSERT(requestActor); - - ObjectStoreCursorConstructorParams params; - params.requestParent() = requestActor; - params.direction() = mDirection; - params.key() = mKey; - params.optionalCloneInfo() = mozilla::void_t(); - - if (!objectStoreActor->OpenCursor(mCursor, params, openCursorResponse)) { - return Error; - } - } - - response = openCursorResponse; + params = Move(openParams); } - if (!actor->SendResponse(response)) { - return Error; - } + nsRefPtr request = GenerateRequest(this); + MOZ_ASSERT(request); - return Success_Sent; -} + BackgroundCursorChild* actor = + new BackgroundCursorChild(request, this, direction); -nsresult -OpenKeyCursorHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(aResponseValue.type() == ResponseValue::TOpenCursorResponse); - MOZ_ASSERT(aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::Tvoid_t || - aResponseValue.get_OpenCursorResponse().type() == - OpenCursorResponse::TPIndexedDBCursorChild); - MOZ_ASSERT(!mCursor); + mTransaction->OpenCursor(actor, params); - PROFILER_MAIN_THREAD_LABEL("OpenKeyCursorHelper", "UnpackResponseFromParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - const OpenCursorResponse& response = - aResponseValue.get_OpenCursorResponse(); - - switch (response.type()) { - case OpenCursorResponse::Tvoid_t: - break; - - case OpenCursorResponse::TPIndexedDBCursorChild: { - IndexedDBCursorChild* actor = - static_cast( - response.get_PIndexedDBCursorChild()); - - mCursor = actor->ForgetStrongCursor(); - NS_ASSERTION(mCursor, "This should never be null!"); - - } break; - - default: - MOZ_CRASH("Unknown response union type!"); - } - - return NS_OK; -} - -nsresult -CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("CreateIndexHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - NS_WARNING("Refusing to create index because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - - // Insert the data into the database. - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - "INSERT INTO object_store_index (id, name, key_path, unique_index, " - "multientry, object_store_id) " - "VALUES (:id, :name, :key_path, :unique, :multientry, :osid)" - ); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), - mIndex->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsAutoString keyPathSerialization; - mIndex->GetKeyPath().SerializeToString(keyPathSerialization); - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"), - keyPathSerialization); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"), - mIndex->IsUnique() ? 1 : 0); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("multientry"), - mIndex->IsMultiEntry() ? 1 : 0); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mIndex->ObjectStore()->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (NS_FAILED(stmt->Execute())) { - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - -#ifdef DEBUG - { - int64_t id; - aConnection->GetLastInsertRowID(&id); - NS_ASSERTION(mIndex->Id() == id, "Bad index id!"); +#ifdef IDB_PROFILER_USE_MARKS + if (aKeysOnly) { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "openKeyCursor(%s, %s)", + "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); + } else { + IDB_PROFILER_MARK("IndexedDB Request %llu: " + "database(%s).transaction(%s).objectStore(%s)." + "openCursor(%s, %s)", + "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()", + request->GetSerialNumber(), + IDB_PROFILER_STRING(Transaction()->Database()), + IDB_PROFILER_STRING(Transaction()), + IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange), + IDB_PROFILER_STRING(direction)); } #endif - - // Now we need to populate the index with data from the object store. - rv = InsertDataFromObjectStore(aConnection); - if (NS_FAILED(rv)) { - return rv; - } - - return NS_OK; + return request.forget(); } void -CreateIndexHelper::ReleaseMainThreadObjects() +IDBObjectStore::RefreshSpec(bool aMayDelete) { - mIndex = nullptr; - NoRequestObjectStoreHelper::ReleaseMainThreadObjects(); -} + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec); -nsresult -CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection) -{ - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, data, file_ids, key_value FROM " - "object_data WHERE object_store_id = :osid")); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + const DatabaseSpec* dbSpec = mTransaction->Database()->Spec(); + MOZ_ASSERT(dbSpec); - mozStorageStatementScoper scoper(stmt); + const nsTArray& objectStores = dbSpec->objectStores(); - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mIndex->ObjectStore()->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + bool found = false; - IDB_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, - NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + for (uint32_t objCount = objectStores.Length(), objIndex = 0; + objIndex < objCount; + objIndex++) { + const ObjectStoreSpec& objSpec = objectStores[objIndex]; - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - if (!hasResult) { - // Bail early if we have no data to avoid creating the below runtime - return NS_OK; - } + if (objSpec.metadata().id() == Id()) { + mSpec = &objSpec; - ThreadLocalJSRuntime* tlsEntry = - reinterpret_cast(PR_GetThreadPrivate(sTLSIndex)); - - if (!tlsEntry) { - tlsEntry = ThreadLocalJSRuntime::Create(); - IDB_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - PR_SetThreadPrivate(sTLSIndex, tlsEntry); - } - - JSContext* cx = tlsEntry->Context(); - JSAutoRequest ar(cx); - JSAutoCompartment ac(cx, tlsEntry->Global()); - - do { - StructuredCloneReadInfo cloneReadInfo; - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2, - mDatabase, cloneReadInfo); - NS_ENSURE_SUCCESS(rv, rv); - - JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer; - - JSStructuredCloneCallbacks callbacks = { - IDBObjectStore::StructuredCloneReadCallback, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr - }; - - JS::Rooted clone(cx); - if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) { - NS_WARNING("Failed to deserialize structured clone data!"); - return NS_ERROR_DOM_DATA_CLONE_ERR; - } - - nsTArray updateInfo; - rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(), - mIndex->GetKeyPath(), - mIndex->IsUnique(), - mIndex->IsMultiEntry(), - tlsEntry->Context(), - clone, updateInfo); - NS_ENSURE_SUCCESS(rv, rv); - - int64_t objectDataID = stmt->AsInt64(0); - - Key key; - rv = key.SetFromStatement(stmt, 3); - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBObjectStore::UpdateIndexes(mTransaction, mIndex->Id(), - key, false, objectDataID, updateInfo); - NS_ENSURE_SUCCESS(rv, rv); - - } while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasResult)) && hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -void -CreateIndexHelper::DestroyTLSEntry(void* aPtr) -{ - delete reinterpret_cast(aPtr); -} - -nsresult -DeleteIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("DeleteIndexHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr stmt = - mTransaction->GetCachedStatement( - "DELETE FROM object_store_index " - "WHERE name = :name " - ); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (NS_FAILED(stmt->Execute())) { - return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR; - } - - return NS_OK; -} - -nsresult -GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("GetAllHelper", "DoDatabaseWork [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); - NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); - - nsAutoCString keyRangeClause; - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); - if (mKeyRange->IsLowerOpen()) { - keyRangeClause.AppendLiteral(" > :"); - } - else { - keyRangeClause.AppendLiteral(" >= :"); - } - keyRangeClause.Append(lowerKeyName); - } - - if (!mKeyRange->Upper().IsUnset()) { - keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); - if (mKeyRange->IsUpperOpen()) { - keyRangeClause.AppendLiteral(" < :"); - } - else { - keyRangeClause.AppendLiteral(" <= :"); - } - keyRangeClause.Append(upperKeyName); - } - } - - nsAutoCString limitClause; - if (mLimit != UINT32_MAX) { - limitClause.AssignLiteral(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause + - NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + - limitClause; - - mCloneReadInfos.SetCapacity(50); - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - if (!mKeyRange->Upper().IsUnset()) { - rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - bool hasResult; - while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) { - mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2); - } - - StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement(); - NS_ASSERTION(readInfo, "Shouldn't fail since SetCapacity succeeded!"); - - rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1, - mDatabase, *readInfo); - NS_ENSURE_SUCCESS(rv, rv); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -GetAllHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!"); - - nsresult rv = ConvertToArrayAndCleanup(aCx, mCloneReadInfos, aVal); - - NS_ASSERTION(mCloneReadInfos.IsEmpty(), - "Should have cleared in ConvertToArrayAndCleanup"); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -GetAllHelper::ReleaseMainThreadObjects() -{ - mKeyRange = nullptr; - for (uint32_t index = 0; index < mCloneReadInfos.Length(); index++) { - IDBObjectStore::ClearCloneReadInfo(mCloneReadInfos[index]); - } - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetAllHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetAllParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - params.limit() = mLimit; - - aParams = params; - return NS_OK; -} - -AsyncConnectionHelper::ChildProcessSendResult -GetAllHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_MAIN_THREAD_LABEL("GetAllHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - GetAllResponse getAllResponse; - if (NS_SUCCEEDED(aResultCode) && !mCloneReadInfos.IsEmpty()) { - IDBDatabase* database = mObjectStore->Transaction()->Database(); - NS_ASSERTION(database, "This should never be null!"); - - nsIContentParent* contentParent = database->GetContentParent(); - NS_ASSERTION(contentParent, "This should never be null!"); - - FileManager* fileManager = database->Manager(); - NS_ASSERTION(fileManager, "This should never be null!"); - - uint32_t length = mCloneReadInfos.Length(); - - InfallibleTArray& infos = - getAllResponse.cloneInfos(); - infos.SetCapacity(length); - - InfallibleTArray& blobArrays = getAllResponse.blobs(); - blobArrays.SetCapacity(length); - - for (uint32_t index = 0; - NS_SUCCEEDED(aResultCode) && index < length; - index++) { - // Append the structured clone data. - const StructuredCloneReadInfo& clone = mCloneReadInfos[index]; - SerializedStructuredCloneReadInfo* info = infos.AppendElement(); - *info = clone; - - // Now take care of the files. - const nsTArray& files = clone.mFiles; - BlobArray* blobArray = blobArrays.AppendElement(); - InfallibleTArray& blobs = blobArray->blobsParent(); - - aResultCode = - IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files, - blobs); - if (NS_FAILED(aResultCode)) { - NS_WARNING("ConvertBlobsToActors failed!"); - break; - } - } - } - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - response = getAllResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetAllHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_ASSERTION(aResponseValue.type() == ResponseValue::TGetAllResponse, - "Bad response type!"); - - const GetAllResponse& getAllResponse = aResponseValue.get_GetAllResponse(); - const InfallibleTArray& cloneInfos = - getAllResponse.cloneInfos(); - const InfallibleTArray& blobArrays = getAllResponse.blobs(); - - mCloneReadInfos.SetCapacity(cloneInfos.Length()); - - for (uint32_t index = 0; index < cloneInfos.Length(); index++) { - const SerializedStructuredCloneReadInfo srcInfo = cloneInfos[index]; - const InfallibleTArray& blobs = blobArrays[index].blobsChild(); - - StructuredCloneReadInfo* destInfo = mCloneReadInfos.AppendElement(); - if (!destInfo->SetFromSerialized(srcInfo)) { - IDB_WARNING("Failed to copy clone buffer!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - IDBObjectStore::ConvertActorsToBlobs(blobs, destInfo->mFiles); - } - - return NS_OK; -} - -nsresult -GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - PROFILER_LABEL("GetAllKeysHelper", "DoDatabaseWork [IDObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(keyValue, "key_value"); - - nsAutoCString keyRangeClause; - if (mKeyRange) { - mKeyRange->GetBindingClause(keyValue, keyRangeClause); - } - - nsAutoCString limitClause; - if (mLimit != UINT32_MAX) { - limitClause = NS_LITERAL_CSTRING(" LIMIT "); - limitClause.AppendInt(mLimit); - } - - NS_NAMED_LITERAL_CSTRING(osid, "osid"); - - nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyValue + - NS_LITERAL_CSTRING(" FROM object_data WHERE " - "object_store_id = :") + - osid + keyRangeClause + - NS_LITERAL_CSTRING(" ORDER BY key_value ASC") + - limitClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - rv = mKeyRange->BindToStatement(stmt); - NS_ENSURE_SUCCESS(rv, rv); - } - - mKeys.SetCapacity(std::min(50, mLimit)); - - bool hasResult; - while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) { - if (mKeys.Capacity() == mKeys.Length()) { - mKeys.SetCapacity(mKeys.Capacity() * 2); - } - - Key* key = mKeys.AppendElement(); - NS_ASSERTION(key, "This shouldn't fail!"); - - rv = key->SetFromStatement(stmt, 0); - NS_ENSURE_SUCCESS(rv, rv); - } - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - return NS_OK; -} - -nsresult -GetAllKeysHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mKeys.Length() <= mLimit); - - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "GetSuccessResult [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - nsTArray keys; - mKeys.SwapElements(keys); - - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (!array) { - IDB_WARNING("Failed to make array!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (!keys.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, keys.Length())) { - IDB_WARNING("Failed to set array length!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - for (uint32_t index = 0, count = keys.Length(); index < count; index++) { - const Key& key = keys[index]; - MOZ_ASSERT(!key.IsUnset()); - - JS::Rooted value(aCx); - nsresult rv = key.ToJSVal(aCx, &value); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to get jsval for key!"); - return rv; + for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0; + idxIndex < idxCount; + idxIndex++) { + mIndexes[idxIndex]->RefreshMetadata(aMayDelete); } - if (!JS_SetElement(aCx, array, index, value)) { - IDB_WARNING("Failed to set array element!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + found = true; + break; } } - aVal.setObject(*array); - return NS_OK; -} + MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, found); -void -GetAllKeysHelper::ReleaseMainThreadObjects() -{ - MOZ_ASSERT(NS_IsMainThread()); - - mKeyRange = nullptr; - - ObjectStoreHelper::ReleaseMainThreadObjects(); -} - -nsresult -GetAllKeysHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); - - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - GetAllKeysParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; + if (found) { + MOZ_ASSERT(mSpec != mDeletedSpec); + mDeletedSpec = nullptr; } else { - params.optionalKeyRange() = mozilla::void_t(); + NoteDeletion(); } - - params.limit() = mLimit; - - aParams = params; - return NS_OK; } -AsyncConnectionHelper::ChildProcessSendResult -GetAllKeysHelper::SendResponseToChildProcess(nsresult aResultCode) +const ObjectStoreSpec& +IDBObjectStore::Spec() const { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); - PROFILER_MAIN_THREAD_LABEL("GetAllKeysHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - MOZ_ASSERT(actor); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - GetAllKeysResponse getAllKeysResponse; - getAllKeysResponse.keys().AppendElements(mKeys); - response = getAllKeysResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; -} - -nsresult -GetAllKeysHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(aResponseValue.type() == ResponseValue::TGetAllKeysResponse); - - mKeys.AppendElements(aResponseValue.get_GetAllKeysResponse().keys()); - return NS_OK; -} - -nsresult -CountHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("CountHelper", "DoDatabaseWork [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key"); - NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key"); - - nsAutoCString keyRangeClause; - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - keyRangeClause = NS_LITERAL_CSTRING(" AND key_value"); - if (mKeyRange->IsLowerOpen()) { - keyRangeClause.AppendLiteral(" > :"); - } - else { - keyRangeClause.AppendLiteral(" >= :"); - } - keyRangeClause.Append(lowerKeyName); - } - - if (!mKeyRange->Upper().IsUnset()) { - keyRangeClause += NS_LITERAL_CSTRING(" AND key_value"); - if (mKeyRange->IsUpperOpen()) { - keyRangeClause.AppendLiteral(" < :"); - } - else { - keyRangeClause.AppendLiteral(" <= :"); - } - keyRangeClause.Append(upperKeyName); - } - } - - nsCString query = NS_LITERAL_CSTRING("SELECT count(*) FROM object_data " - "WHERE object_store_id = :osid") + - keyRangeClause; - - nsCOMPtr stmt = mTransaction->GetCachedStatement(query); - IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), - mObjectStore->Id()); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mKeyRange) { - if (!mKeyRange->Lower().IsUnset()) { - rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - if (!mKeyRange->Upper().IsUnset()) { - rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - bool hasResult; - rv = stmt->ExecuteStep(&hasResult); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - IDB_ENSURE_TRUE(hasResult, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mCount = stmt->AsInt64(0); - return NS_OK; -} - -nsresult -CountHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - aVal.setNumber(static_cast(mCount)); - return NS_OK; + return *mSpec; } void -CountHelper::ReleaseMainThreadObjects() +IDBObjectStore::NoteDeletion() { - mKeyRange = nullptr; - ObjectStoreHelper::ReleaseMainThreadObjects(); + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + MOZ_ASSERT(Id() == mSpec->metadata().id()); + + if (mDeletedSpec) { + MOZ_ASSERT(mDeletedSpec == mSpec); + return; + } + + // Copy the spec here. + mDeletedSpec = new ObjectStoreSpec(*mSpec); + mDeletedSpec->indexes().Clear(); + + mSpec = mDeletedSpec; + + if (!mIndexes.IsEmpty()) { + for (uint32_t count = mIndexes.Length(), index = 0; + index < count; + index++) { + mIndexes[index]->NoteDeletion(); + } + } } -nsresult -CountHelper::PackArgumentsForParentProcess(ObjectStoreRequestParams& aParams, - nsIContentChild* aBlobCreator) +const nsString& +IDBObjectStore::Name() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aBlobCreator, "Must have a valid creator!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); - PROFILER_MAIN_THREAD_LABEL("CountHelper", "PackArgumentsForParentProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - CountParams params; - - if (mKeyRange) { - KeyRange keyRange; - mKeyRange->ToSerializedKeyRange(keyRange); - params.optionalKeyRange() = keyRange; - } - else { - params.optionalKeyRange() = mozilla::void_t(); - } - - aParams = params; - return NS_OK; + return mSpec->metadata().name(); } -AsyncConnectionHelper::ChildProcessSendResult -CountHelper::SendResponseToChildProcess(nsresult aResultCode) +bool +IDBObjectStore::AutoIncrement() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); - PROFILER_MAIN_THREAD_LABEL("CountHelper", "SendResponseToChildProcess [IDBObjectStore.cpp]", - js::ProfileEntry::Category::STORAGE); - - IndexedDBRequestParentBase* actor = mRequest->GetActorParent(); - NS_ASSERTION(actor, "How did we get this far without an actor?"); - - ResponseValue response; - if (NS_FAILED(aResultCode)) { - response = aResultCode; - } - else { - CountResponse countResponse = mCount; - response = countResponse; - } - - if (!actor->SendResponse(response)) { - return Error; - } - - return Success_Sent; + return mSpec->metadata().autoIncrement(); } -nsresult -CountHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) +const KeyPath& +IDBObjectStore::GetKeyPath() const { - NS_ASSERTION(aResponseValue.type() == ResponseValue::TCountResponse, - "Bad response type!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); - mCount = aResponseValue.get_CountResponse().count(); - return NS_OK; + return mSpec->metadata().keyPath(); } + +bool +IDBObjectStore::HasValidKeyPath() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mSpec); + + return GetKeyPath().IsValid(); +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 0d164c8ffaee..1ce9c869bac6 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -7,61 +7,68 @@ #ifndef mozilla_dom_indexeddb_idbobjectstore_h__ #define mozilla_dom_indexeddb_idbobjectstore_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "js/TypeDecls.h" +#include "js/RootingAPI.h" #include "mozilla/dom/IDBCursorBinding.h" #include "mozilla/dom/IDBIndexBinding.h" -#include "mozilla/dom/IDBObjectStoreBinding.h" +#include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" -#include "MainThreadUtils.h" +#include "nsISupports.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsWrapperCache.h" -#include "mozilla/dom/indexedDB/IDBRequest.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" -#include "mozilla/dom/indexedDB/KeyPath.h" - -class nsIDOMBlob; -class nsIScriptContext; +struct JSClass; class nsPIDOMWindow; namespace mozilla { + +class ErrorResult; + namespace dom { + +class DOMStringList; class nsIContentParent; -class PBlobChild; -class PBlobParent; -} -} +template class Sequence; -BEGIN_INDEXEDDB_NAMESPACE +namespace indexedDB { -class AsyncConnectionHelper; class FileManager; -class IDBCursor; class IDBKeyRange; class IDBRequest; -class IndexedDBObjectStoreChild; -class IndexedDBObjectStoreParent; +class IDBTransaction; +class IndexUpdateInfo; class Key; +class KeyPath; +class ObjectStoreSpec; +struct StructuredCloneFile; +struct StructuredCloneReadInfo; -struct IndexInfo; -struct IndexUpdateInfo; -struct ObjectStoreInfo; - -struct MutableFileData; -struct BlobOrFileData; - -class IDBObjectStore MOZ_FINAL : public nsISupports, - public nsWrapperCache +class IDBObjectStore MOZ_FINAL + : public nsISupports + , public nsWrapperCache { + static const JSClass sDummyPropJSClass; + + nsRefPtr mTransaction; + JS::Heap mCachedKeyPath; + + // This normally points to the ObjectStoreSpec owned by the parent IDBDatabase + // object. However, if this objectStore is part of a versionchange transaction + // and it gets deleted then the spec is copied into mDeletedSpec and mSpec is + // set to point at mDeletedSpec. + const ObjectStoreSpec* mSpec; + nsAutoPtr mDeletedSpec; + + nsTArray> mIndexes; + + const int64_t mId; + bool mRooted; + public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore) + struct StructuredCloneWriteInfo; static already_AddRefed - Create(IDBTransaction* aTransaction, - ObjectStoreInfo* aInfo, - const nsACString& aDatabaseId, - bool aCreating); + Create(IDBTransaction* aTransaction, const ObjectStoreSpec& aSpec); static nsresult AppendIndexUpdateInfo(int64_t aIndexID, @@ -72,214 +79,62 @@ public: JS::Handle aObject, nsTArray& aUpdateInfoArray); - static nsresult - UpdateIndexes(IDBTransaction* aTransaction, - int64_t aObjectStoreId, - const Key& aObjectStoreKey, - bool aOverwrite, - int64_t aObjectDataId, - const nsTArray& aUpdateInfoArray); - - static nsresult - GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement, - uint32_t aDataIndex, - uint32_t aFileIdsIndex, - IDBDatabase* aDatabase, - StructuredCloneReadInfo& aInfo); - static void ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo); - static void - ClearCloneWriteInfo(StructuredCloneWriteInfo& aWriteInfo); - static bool DeserializeValue(JSContext* aCx, StructuredCloneReadInfo& aCloneReadInfo, JS::MutableHandle aValue); static bool - SerializeValue(JSContext* aCx, - StructuredCloneWriteInfo& aCloneWriteInfo, - JS::Handle aValue); + DeserializeIndexValue(JSContext* aCx, + StructuredCloneReadInfo& aCloneReadInfo, + JS::MutableHandle aValue); - template - static JSObject* - StructuredCloneReadCallback(JSContext* aCx, - JSStructuredCloneReader* aReader, - uint32_t aTag, - uint32_t aData, - void* aClosure); - static bool - StructuredCloneWriteCallback(JSContext* aCx, - JSStructuredCloneWriter* aWriter, - JS::Handle aObj, - void* aClosure); - - static nsresult - ConvertFileIdsToArray(const nsAString& aFileIds, - nsTArray& aResult); - - // Called only in the main process. - static nsresult - ConvertBlobsToActors(nsIContentParent* aContentParent, - FileManager* aFileManager, - const nsTArray& aFiles, - InfallibleTArray& aActors); - - // Called only in the child process. - static void - ConvertActorsToBlobs(const InfallibleTArray& aActors, - nsTArray& aFiles); - - const nsString& Name() const + static const JSClass* + DummyPropClass() { - return mName; + return &sDummyPropJSClass; } - bool IsAutoIncrement() const - { - return mAutoIncrement; - } + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif - bool IsWriteAllowed() const + int64_t + Id() const { - return mTransaction->IsWriteAllowed(); - } + AssertIsOnOwningThread(); - int64_t Id() const - { - NS_ASSERTION(mId != INT64_MIN, "Don't ask for this yet!"); return mId; } - const KeyPath& GetKeyPath() const - { - return mKeyPath; - } + const nsString& + Name() const; - const bool HasValidKeyPath() const - { - return mKeyPath.IsValid(); - } + bool + AutoIncrement() const; - IDBTransaction* Transaction() - { - return mTransaction; - } + const KeyPath& + GetKeyPath() const; - ObjectStoreInfo* Info() - { - return mInfo; - } + bool + HasValidKeyPath() const; - void - SetActor(IndexedDBObjectStoreChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } - - void - SetActor(IndexedDBObjectStoreParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBObjectStoreChild* - GetActorChild() const - { - return mActorChild; - } - - IndexedDBObjectStoreParent* - GetActorParent() const - { - return mActorParent; - } - - already_AddRefed - CreateIndexInternal(const IndexInfo& aInfo, - ErrorResult& aRv); - - nsresult AddOrPutInternal( - const SerializedStructuredCloneWriteInfo& aCloneWriteInfo, - const Key& aKey, - const InfallibleTArray& aUpdateInfoArray, - const nsTArray >& aBlobs, - bool aOverwrite, - IDBRequest** _retval); - - already_AddRefed - GetInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - already_AddRefed - GetAllInternal(IDBKeyRange* aKeyRange, - uint32_t aLimit, - ErrorResult& aRv); - - already_AddRefed - GetAllKeysInternal(IDBKeyRange* aKeyRange, - uint32_t aLimit, - ErrorResult& aRv); - - already_AddRefed - DeleteInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - already_AddRefed - CountInternal(IDBKeyRange* aKeyRange, - ErrorResult& aRv); - - already_AddRefed - OpenCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, - ErrorResult& aRv); - - already_AddRefed - OpenKeyCursorInternal(IDBKeyRange* aKeyRange, - size_t aDirection, - ErrorResult& aRv); - - nsresult - OpenCursorFromChildProcess( - IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - const SerializedStructuredCloneReadInfo& aCloneInfo, - nsTArray& aBlobs, - IDBCursor** _retval); - - nsresult - OpenCursorFromChildProcess(IDBRequest* aRequest, - size_t aDirection, - const Key& aKey, - IDBCursor** _retval); - - void - SetInfo(ObjectStoreInfo* aInfo); - - static const JSClass sDummyPropJSClass; - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - IDBTransaction* - GetParentObject() const - { - return mTransaction; - } + nsPIDOMWindow* + GetParentObject() const; void GetName(nsString& aName) const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - aName.Assign(mName); + AssertIsOnOwningThread(); + + aName = Name(); } void @@ -287,38 +142,38 @@ public: ErrorResult& aRv); already_AddRefed - GetIndexNames(ErrorResult& aRv); + IndexNames(); IDBTransaction* Transaction() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + return mTransaction; } - bool - AutoIncrement() const - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return mAutoIncrement; - } - already_AddRefed - Put(JSContext* aCx, JS::Handle aValue, - JS::Handle aKey, ErrorResult& aRv) + Add(JSContext* aCx, + JS::Handle aValue, + JS::Handle aKey, + ErrorResult& aRv) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return AddOrPut(aCx, aValue, aKey, true, aRv); - } + AssertIsOnOwningThread(); - already_AddRefed - Add(JSContext* aCx, JS::Handle aValue, - JS::Handle aKey, ErrorResult& aRv) - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); return AddOrPut(aCx, aValue, aKey, false, aRv); } + already_AddRefed + Put(JSContext* aCx, + JS::Handle aValue, + JS::Handle aKey, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return AddOrPut(aCx, aValue, aKey, true, aRv); + } + already_AddRefed Delete(JSContext* aCx, JS::Handle aKey, ErrorResult& aRv); @@ -328,18 +183,19 @@ public: already_AddRefed Clear(ErrorResult& aRv); - already_AddRefed - OpenCursor(JSContext* aCx, JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv); + already_AddRefed + CreateIndex(JSContext* aCx, + const nsAString& aName, + const nsAString& aKeyPath, + const IDBIndexParameters& aOptionalParameters, + ErrorResult& aRv); already_AddRefed - CreateIndex(JSContext* aCx, const nsAString& aName, const nsAString& aKeyPath, - const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); - - already_AddRefed - CreateIndex(JSContext* aCx, const nsAString& aName, + CreateIndex(JSContext* aCx, + const nsAString& aName, const Sequence& aKeyPath, - const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); + const IDBIndexParameters& aOptionalParameters, + ErrorResult& aRv); already_AddRefed Index(const nsAString& aName, ErrorResult &aRv); @@ -348,70 +204,116 @@ public: DeleteIndex(const nsAString& aIndexName, ErrorResult& aRv); already_AddRefed - Count(JSContext* aCx, JS::Handle aKey, + Count(JSContext* aCx, + JS::Handle aKey, ErrorResult& aRv); already_AddRefed - GetAll(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv); + GetAll(JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv); + } already_AddRefed - GetAllKeys(JSContext* aCx, JS::Handle aKey, - const Optional& aLimit, ErrorResult& aRv); + GetAllKeys(JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv); + } already_AddRefed - OpenKeyCursor(JSContext* aCx, JS::Handle aRange, - IDBCursorDirection aDirection, ErrorResult& aRv); + OpenCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection, + aRv); + } + + already_AddRefed + OpenKeyCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) + { + AssertIsOnOwningThread(); + + return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, + aRv); + } + + void + RefreshSpec(bool aMayDelete); + + const ObjectStoreSpec& + Spec() const; + + void + NoteDeletion(); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBObjectStore) + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + +private: + IDBObjectStore(IDBTransaction* aTransaction, const ObjectStoreSpec* aSpec); -protected: - IDBObjectStore(); ~IDBObjectStore(); - nsresult GetAddInfo(JSContext* aCx, - JS::Handle aValue, - JS::Handle aKeyVal, - StructuredCloneWriteInfo& aCloneWriteInfo, - Key& aKey, - nsTArray& aUpdateInfoArray); + nsresult + GetAddInfo(JSContext* aCx, + JS::Handle aValue, + JS::Handle aKeyVal, + StructuredCloneWriteInfo& aCloneWriteInfo, + Key& aKey, + nsTArray& aUpdateInfoArray); already_AddRefed - AddOrPut(JSContext* aCx, JS::Handle aValue, - JS::Handle aKey, bool aOverwrite, + AddOrPut(JSContext* aCx, + JS::Handle aValue, + JS::Handle aKey, + bool aOverwrite, ErrorResult& aRv); + already_AddRefed + GetAllInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv); + already_AddRefed - CreateIndex(JSContext* aCx, const nsAString& aName, KeyPath& aKeyPath, - const IDBIndexParameters& aOptionalParameters, ErrorResult& aRv); + CreateIndexInternal(JSContext* aCx, + const nsAString& aName, + const KeyPath& aKeyPath, + const IDBIndexParameters& aOptionalParameters, + ErrorResult& aRv); - static void - ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer); - - static bool - ReadMutableFile(JSStructuredCloneReader* aReader, - MutableFileData* aRetval); - - static bool - ReadBlobOrFile(JSStructuredCloneReader* aReader, - uint32_t aTag, - BlobOrFileData* aRetval); -private: - nsRefPtr mTransaction; - - int64_t mId; - nsString mName; - KeyPath mKeyPath; - JS::Heap mCachedKeyPath; - bool mRooted; - bool mAutoIncrement; - nsCString mDatabaseId; - nsRefPtr mInfo; - - nsTArray > mCreatedIndexes; - - IndexedDBObjectStoreChild* mActorChild; - IndexedDBObjectStoreParent* mActorParent; + already_AddRefed + OpenCursorInternal(bool aKeysOnly, + JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv); }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbobjectstore_h__ diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index e778a4bfdeec..265257918859 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -6,78 +6,88 @@ #include "IDBRequest.h" -#include "nsIScriptContext.h" - -#include "mozilla/ContentEvents.h" -#include "mozilla/EventDispatcher.h" -#include "mozilla/dom/ErrorEventBinding.h" -#include "mozilla/dom/IDBOpenDBRequestBinding.h" -#include "mozilla/dom/ScriptSettings.h" -#include "mozilla/dom/UnionTypes.h" -#include "nsComponentManagerUtils.h" -#include "nsDOMClassInfoID.h" -#include "nsDOMJSUtils.h" -#include "nsContentUtils.h" -#include "nsJSUtils.h" -#include "nsPIDOMWindow.h" -#include "nsString.h" -#include "nsThreadUtils.h" -#include "nsWrapperCacheInlines.h" - -#include "AsyncConnectionHelper.h" +#include "BackgroundChildImpl.h" #include "IDBCursor.h" +#include "IDBDatabase.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBIndex.h" #include "IDBObjectStore.h" #include "IDBTransaction.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/ErrorEventBinding.h" +#include "mozilla/dom/IDBOpenDBRequestBinding.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/UnionTypes.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIScriptContext.h" +#include "nsJSUtils.h" +#include "nsPIDOMWindow.h" +#include "nsString.h" #include "ReportInternalError.h" -namespace { +namespace mozilla { +namespace dom { +namespace indexedDB { -#ifdef MOZ_ENABLE_PROFILER_SPS -uint64_t gNextRequestSerialNumber = 1; -#endif - -} // anonymous namespace - -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::OwningIDBObjectStoreOrIDBIndexOrIDBCursor; -using mozilla::dom::ErrorEventInit; -using namespace mozilla; +using namespace mozilla::ipc; IDBRequest::IDBRequest(IDBDatabase* aDatabase) -: IDBWrapperCache(aDatabase), - mResultVal(JSVAL_VOID), - mActorParent(nullptr), -#ifdef MOZ_ENABLE_PROFILER_SPS - mSerialNumber(gNextRequestSerialNumber++), -#endif - mErrorCode(NS_OK), - mLineNo(0), - mHaveResultOrErrorCode(false) + : IDBWrapperCache(aDatabase) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + + InitMembers(); } IDBRequest::IDBRequest(nsPIDOMWindow* aOwner) -: IDBWrapperCache(aOwner), - mResultVal(JSVAL_VOID), - mActorParent(nullptr), -#ifdef MOZ_ENABLE_PROFILER_SPS - mSerialNumber(gNextRequestSerialNumber++), -#endif - mErrorCode(NS_OK), - mLineNo(0), - mHaveResultOrErrorCode(false) + : IDBWrapperCache(aOwner) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + InitMembers(); } IDBRequest::~IDBRequest() { - mResultVal = JSVAL_VOID; - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); +} + +#ifdef DEBUG + +void +IDBRequest::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} + +#endif // DEBUG + +void +IDBRequest::InitMembers() +{ +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + AssertIsOnOwningThread(); + + mResultVal.setUndefined(); + mErrorCode = NS_OK; + mLineNo = 0; + mHaveResultOrErrorCode = false; + +#ifdef MOZ_ENABLE_PROFILER_SPS + { + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + mSerialNumber = threadLocal->mNextRequestSerialNumber++; + } +#endif } // static @@ -85,16 +95,15 @@ already_AddRefed IDBRequest::Create(IDBDatabase* aDatabase, IDBTransaction* aTransaction) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsRefPtr request(new IDBRequest(aDatabase)); + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + + nsRefPtr request = new IDBRequest(aDatabase); request->mTransaction = aTransaction; request->SetScriptOwner(aDatabase->GetScriptOwner()); - if (!aDatabase->Factory()->FromIPC()) { - request->CaptureCaller(); - } - + request->CaptureCaller(); return request.forget(); } @@ -105,6 +114,9 @@ IDBRequest::Create(IDBObjectStore* aSourceAsObjectStore, IDBDatabase* aDatabase, IDBTransaction* aTransaction) { + MOZ_ASSERT(aSourceAsObjectStore); + aSourceAsObjectStore->AssertIsOnOwningThread(); + nsRefPtr request = Create(aDatabase, aTransaction); request->mSourceAsObjectStore = aSourceAsObjectStore; @@ -118,6 +130,9 @@ IDBRequest::Create(IDBIndex* aSourceAsIndex, IDBDatabase* aDatabase, IDBTransaction* aTransaction) { + MOZ_ASSERT(aSourceAsIndex); + aSourceAsIndex->AssertIsOnOwningThread(); + nsRefPtr request = Create(aDatabase, aTransaction); request->mSourceAsIndex = aSourceAsIndex; @@ -125,31 +140,24 @@ IDBRequest::Create(IDBIndex* aSourceAsIndex, return request.forget(); } -#ifdef DEBUG void -IDBRequest::AssertSourceIsCorrect() const +IDBRequest::GetSource( + Nullable& aSource) const { - // At most one of mSourceAs* is allowed to be non-null. Check that by - // summing the double negation of each one and asserting the sum is at most - // 1. + AssertIsOnOwningThread(); - MOZ_ASSERT(!!mSourceAsObjectStore + !!mSourceAsIndex + !!mSourceAsCursor <= 1); -} -#endif + MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex); + MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore); + MOZ_ASSERT_IF(mSourceAsCursor, mSourceAsObjectStore || mSourceAsIndex); -void -IDBRequest::GetSource(Nullable& aSource) const -{ - MOZ_ASSERT(NS_IsMainThread()); - - AssertSourceIsCorrect(); - - if (mSourceAsObjectStore) { + // Always check cursor first since cursor requests hold both the cursor and + // the objectStore or index the cursor came from. + if (mSourceAsCursor) { + aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; + } else if (mSourceAsObjectStore) { aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore; } else if (mSourceAsIndex) { aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex; - } else if (mSourceAsCursor) { - aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; } else { aSource.SetNull(); } @@ -158,116 +166,63 @@ IDBRequest::GetSource(Nullable& aSour void IDBRequest::Reset() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - mResultVal = JSVAL_VOID; + AssertIsOnOwningThread(); + + mResultVal.setUndefined(); mHaveResultOrErrorCode = false; mError = nullptr; } -nsresult -IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); - NS_ASSERTION(mResultVal.isUndefined(), "Should be undefined!"); - - mHaveResultOrErrorCode = true; - - nsresult rv = aHelper->GetResultCode(); - - // If the request failed then set the error code and return. - if (NS_FAILED(rv)) { - SetError(rv); - return NS_OK; - } - - // See if our window is still valid. If not then we're going to pretend that - // we never completed. - if (NS_FAILED(CheckInnerWindowCorrectness())) { - return NS_OK; - } - - // Otherwise we need to get the result from the helper. - AutoJSAPI jsapi; - Maybe ac; - if (GetScriptOwner()) { - // If we have a script owner we want the SafeJSContext and then to enter - // the script owner's compartment. - jsapi.Init(); - ac.emplace(jsapi.cx(), GetScriptOwner()); - } else { - // Otherwise our owner is a window and we use that to initialize. - if (!jsapi.InitWithLegacyErrorReporting(GetOwner())) { - IDB_WARNING("Failed to initialise AutoJSAPI!"); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - SetError(rv); - return rv; - } - } - JSContext* cx = jsapi.cx(); - - AssertIsRooted(); - - JS::Rooted value(cx); - rv = aHelper->GetSuccessResult(cx, &value); - if (NS_FAILED(rv)) { - NS_WARNING("GetSuccessResult failed!"); - } - - if (NS_SUCCEEDED(rv)) { - mError = nullptr; - mResultVal = value; - } - else { - SetError(rv); - mResultVal = JSVAL_VOID; - } - - return rv; -} - void -IDBRequest::NotifyHelperSentResultsToChildProcess(nsresult aRv) +IDBRequest::DispatchNonTransactionError(nsresult aErrorCode) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); - NS_ASSERTION(mResultVal.isUndefined(), "Should be undefined!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); - // See if our window is still valid. If not then we're going to pretend that - // we never completed. - if (NS_FAILED(CheckInnerWindowCorrectness())) { + SetError(aErrorCode); + + // Make an error event and fire it at the target. + nsCOMPtr event = + CreateGenericEvent(this, + nsDependentString(kErrorEventType), + eDoesBubble, + eCancelable); + if (NS_WARN_IF(!event)) { return; } - mHaveResultOrErrorCode = true; - - if (NS_FAILED(aRv)) { - SetError(aRv); - } + bool ignored; + NS_WARN_IF(NS_FAILED(DispatchEvent(event, &ignored))); } void IDBRequest::SetError(nsresult aRv) { - NS_ASSERTION(NS_FAILED(aRv), "Er, what?"); - NS_ASSERTION(!mError, "Already have an error?"); + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aRv)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv) == NS_ERROR_MODULE_DOM_INDEXEDDB); + MOZ_ASSERT(!mError); mHaveResultOrErrorCode = true; - mError = new mozilla::dom::DOMError(GetOwner(), aRv); + mError = new DOMError(GetOwner(), aRv); mErrorCode = aRv; - mResultVal = JSVAL_VOID; + mResultVal.setUndefined(); } #ifdef DEBUG + nsresult IDBRequest::GetErrorCode() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mHaveResultOrErrorCode); + return mErrorCode; } -#endif + +#endif // DEBUG void IDBRequest::CaptureCaller() @@ -277,7 +232,6 @@ IDBRequest::CaptureCaller() const char* filename = nullptr; uint32_t lineNo = 0; if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) { - NS_WARNING("Failed to get caller."); return; } @@ -292,16 +246,25 @@ IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const aEventInit.mFilename = mFilename; } -mozilla::dom::IDBRequestReadyState +IDBRequestReadyState IDBRequest::ReadyState() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - if (IsPending()) { - return IDBRequestReadyState::Pending; - } + return IsPending() ? + IDBRequestReadyState::Pending : + IDBRequestReadyState::Done; +} - return IDBRequestReadyState::Done; +void +IDBRequest::SetSource(IDBCursor* aSource) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aSource); + MOZ_ASSERT(mSourceAsObjectStore || mSourceAsIndex); + MOZ_ASSERT(!mSourceAsCursor); + + mSourceAsCursor = aSource; } JSObject* @@ -314,21 +277,72 @@ void IDBRequest::GetResult(JS::MutableHandle aResult, ErrorResult& aRv) const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (!mHaveResultOrErrorCode) { - // XXX Need a real error code here. - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; } JS::ExposeValueToActiveJS(mResultVal); aResult.set(mResultVal); } -mozilla::dom::DOMError* -IDBRequest::GetError(mozilla::ErrorResult& aRv) +void +IDBRequest::SetResultCallback(ResultCallback* aCallback) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + MOZ_ASSERT(!mHaveResultOrErrorCode); + MOZ_ASSERT(mResultVal.isUndefined()); + MOZ_ASSERT(!mError); + + // See if our window is still valid. + if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) { + IDB_REPORT_INTERNAL_ERR(); + SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return; + } + + AutoJSAPI autoJS; + Maybe ac; + + if (GetScriptOwner()) { + // If we have a script owner we want the SafeJSContext and then to enter the + // script owner's compartment. + autoJS.Init(); + ac.emplace(autoJS.cx(), GetScriptOwner()); + } else { + // Otherwise our owner is a window and we use that to initialize. + MOZ_ASSERT(GetOwner()); + if (!autoJS.InitWithLegacyErrorReporting(GetOwner())) { + IDB_WARNING("Failed to initialize AutoJSAPI!"); + SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + return; + } + } + + JSContext* cx = autoJS.cx(); + + AssertIsRooted(); + + JS::Rooted result(cx); + nsresult rv = aCallback->GetResult(cx, &result); + if (NS_WARN_IF(NS_FAILED(rv))) { + SetError(rv); + mResultVal.setUndefined(); + } else { + mError = nullptr; + mResultVal = result; + } + + mHaveResultOrErrorCode = true; +} + +DOMError* +IDBRequest::GetError(ErrorResult& aRv) +{ + AssertIsOnOwningThread(); if (!mHaveResultOrErrorCode) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); @@ -351,7 +365,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) - tmp->mResultVal = JSVAL_VOID; + tmp->mResultVal.setUndefined(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor) @@ -374,41 +388,60 @@ NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) nsresult IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); aVisitor.mCanHandle = true; aVisitor.mParentTarget = mTransaction; return NS_OK; } -IDBOpenDBRequest::IDBOpenDBRequest(nsPIDOMWindow* aOwner) +IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner) : IDBRequest(aOwner) + , mFactory(aFactory) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aFactory); + + // aOwner may be null. } IDBOpenDBRequest::~IDBOpenDBRequest() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); } // static already_AddRefed -IDBOpenDBRequest::Create(IDBFactory* aFactory, - nsPIDOMWindow* aOwner, - JS::Handle aScriptOwner) +IDBOpenDBRequest::CreateForWindow(IDBFactory* aFactory, + nsPIDOMWindow* aOwner, + JS::Handle aScriptOwner) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aFactory, "Null pointer!"); + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aOwner); + MOZ_ASSERT(aScriptOwner); - nsRefPtr request = new IDBOpenDBRequest(aOwner); + nsRefPtr request = new IDBOpenDBRequest(aFactory, aOwner); + request->CaptureCaller(); request->SetScriptOwner(aScriptOwner); - request->mFactory = aFactory; - if (!aFactory->FromIPC()) { - request->CaptureCaller(); - } + return request.forget(); +} + +// static +already_AddRefed +IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory, + JS::Handle aScriptOwner) +{ + MOZ_ASSERT(aFactory); + aFactory->AssertIsOnOwningThread(); + MOZ_ASSERT(aScriptOwner); + + nsRefPtr request = new IDBOpenDBRequest(aFactory, nullptr); + request->CaptureCaller(); + + request->SetScriptOwner(aScriptOwner); return request.forget(); } @@ -416,10 +449,9 @@ IDBOpenDBRequest::Create(IDBFactory* aFactory, void IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); - NS_ASSERTION(!aTransaction || !mTransaction, - "Shouldn't have a transaction here!"); + MOZ_ASSERT(!aTransaction || !mTransaction); mTransaction = aTransaction; } @@ -445,11 +477,20 @@ NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) nsresult IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) { + // XXX Fix me! + MOZ_ASSERT(NS_IsMainThread()); + return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); } JSObject* IDBOpenDBRequest::WrapObject(JSContext* aCx) { + AssertIsOnOwningThread(); + return IDBOpenDBRequestBinding::Wrap(aCx, this); } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index d628b5a7998f..eda2d4e41cf9 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -7,72 +7,98 @@ #ifndef mozilla_dom_indexeddb_idbrequest_h__ #define mozilla_dom_indexeddb_idbrequest_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - +#include "js/RootingAPI.h" #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" -#include "mozilla/dom/DOMError.h" #include "mozilla/dom/IDBRequestBinding.h" -#include "mozilla/ErrorResult.h" -#include "nsCycleCollectionParticipant.h" -#include "nsWrapperCache.h" - #include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "nsAutoPtr.h" +#include "nsCycleCollectionParticipant.h" -class nsIScriptContext; class nsPIDOMWindow; +struct PRThread; namespace mozilla { -class EventChainPostVisitor; -class EventChainPreVisitor; + +class ErrorResult; + namespace dom { -class OwningIDBObjectStoreOrIDBIndexOrIDBCursor; + +class DOMError; struct ErrorEventInit; -} -} +template struct Nullable; +class OwningIDBObjectStoreOrIDBIndexOrIDBCursor; -BEGIN_INDEXEDDB_NAMESPACE +namespace indexedDB { -class HelperBase; class IDBCursor; +class IDBDatabase; class IDBFactory; class IDBIndex; class IDBObjectStore; class IDBTransaction; -class IndexedDBRequestParentBase; -class IDBRequest : public IDBWrapperCache +class IDBRequest + : public IDBWrapperCache { +protected: + // mSourceAsObjectStore and mSourceAsIndex are exclusive and one must always + // be set. mSourceAsCursor is sometimes set also. + nsRefPtr mSourceAsObjectStore; + nsRefPtr mSourceAsIndex; + nsRefPtr mSourceAsCursor; + + nsRefPtr mTransaction; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + + JS::Heap mResultVal; + nsRefPtr mError; + + nsString mFilename; +#ifdef MOZ_ENABLE_PROFILER_SPS + uint64_t mSerialNumber; +#endif + nsresult mErrorCode; + uint32_t mLineNo; + bool mHaveResultOrErrorCode; + public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, - IDBWrapperCache) + class ResultCallback; - static - already_AddRefed Create(IDBDatabase* aDatabase, - IDBTransaction* aTransaction); + static already_AddRefed + Create(IDBDatabase* aDatabase, IDBTransaction* aTransaction); - static - already_AddRefed Create(IDBObjectStore* aSource, - IDBDatabase* aDatabase, - IDBTransaction* aTransaction); + static already_AddRefed + Create(IDBObjectStore* aSource, + IDBDatabase* aDatabase, + IDBTransaction* aTransaction); - static - already_AddRefed Create(IDBIndex* aSource, - IDBDatabase* aDatabase, - IDBTransaction* aTransaction); + static already_AddRefed + Create(IDBIndex* aSource, + IDBDatabase* aDatabase, + IDBTransaction* aTransaction); // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; + virtual nsresult + PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; - void GetSource(Nullable& aSource) const; + void + GetSource(Nullable& aSource) const; - void Reset(); + void + Reset(); - nsresult NotifyHelperCompleted(HelperBase* aHelper); - void NotifyHelperSentResultsToChildProcess(nsresult aRv); + void + DispatchNonTransactionError(nsresult aErrorCode); - void SetError(nsresult aRv); + void + SetResultCallback(ResultCallback* aCallback); + + void + SetError(nsresult aRv); nsresult GetErrorCode() const @@ -84,25 +110,11 @@ public: } #endif - DOMError* GetError(ErrorResult& aRv); + DOMError* + GetError(ErrorResult& aRv); void - SetActor(IndexedDBRequestParentBase* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBRequestParentBase* - GetActorParent() const - { - return mActorParent; - } - - void CaptureCaller(); - - void FillScriptErrorEvent(ErrorEventInit& aEventInit) const; + FillScriptErrorEvent(ErrorEventInit& aEventInit) const; bool IsPending() const @@ -118,11 +130,6 @@ public: } #endif - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL nsPIDOMWindow* GetParentObject() const { @@ -142,66 +149,87 @@ public: IDBTransaction* GetTransaction() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + return mTransaction; } IDBRequestReadyState ReadyState() const; + void + SetSource(IDBCursor* aSource); + IMPL_EVENT_HANDLER(success); IMPL_EVENT_HANDLER(error); + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, + IDBWrapperCache) + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + protected: explicit IDBRequest(IDBDatabase* aDatabase); explicit IDBRequest(nsPIDOMWindow* aOwner); ~IDBRequest(); - // At most one of these three fields can be non-null. - nsRefPtr mSourceAsObjectStore; - nsRefPtr mSourceAsIndex; - nsRefPtr mSourceAsCursor; + void + InitMembers(); - // Check that the above condition holds. -#ifdef DEBUG - void AssertSourceIsCorrect() const; -#else - void AssertSourceIsCorrect() const {} -#endif + void + ConstructResult(); - nsRefPtr mTransaction; - - JS::Heap mResultVal; - nsRefPtr mError; - IndexedDBRequestParentBase* mActorParent; - nsString mFilename; -#ifdef MOZ_ENABLE_PROFILER_SPS - uint64_t mSerialNumber; -#endif - nsresult mErrorCode; - uint32_t mLineNo; - bool mHaveResultOrErrorCode; + void + CaptureCaller(); }; -class IDBOpenDBRequest : public IDBRequest +class NS_NO_VTABLE IDBRequest::ResultCallback { public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle aResult) = 0; - static - already_AddRefed - Create(IDBFactory* aFactory, - nsPIDOMWindow* aOwner, - JS::Handle aScriptOwner); +protected: + ResultCallback() + { } +}; - void SetTransaction(IDBTransaction* aTransaction); +class IDBOpenDBRequest MOZ_FINAL + : public IDBRequest +{ + // Only touched on the owning thread. + nsRefPtr mFactory; + +public: + static already_AddRefed + CreateForWindow(IDBFactory* aFactory, + nsPIDOMWindow* aOwner, + JS::Handle aScriptOwner); + + static already_AddRefed + CreateForJS(IDBFactory* aFactory, + JS::Handle aScriptOwner); + + void + SetTransaction(IDBTransaction* aTransaction); // nsIDOMEventTarget - virtual nsresult PostHandleEvent( - EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; + virtual nsresult + PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; - DOMError* GetError(ErrorResult& aRv) + DOMError* + GetError(ErrorResult& aRv) { return IDBRequest::GetError(aRv); } @@ -212,22 +240,24 @@ public: return mFactory; } + IMPL_EVENT_HANDLER(blocked); + IMPL_EVENT_HANDLER(upgradeneeded); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) + // nsWrapperCache virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - // WebIDL - IMPL_EVENT_HANDLER(blocked); - IMPL_EVENT_HANDLER(upgradeneeded); +private: + IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner); -protected: - explicit IDBOpenDBRequest(nsPIDOMWindow* aOwner); ~IDBOpenDBRequest(); - - // Only touched on the main thread. - nsRefPtr mFactory; }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbrequest_h__ diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index 219dec7bf70e..b210e7bda2f6 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -4,458 +4,374 @@ * 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 "base/basictypes.h" - #include "IDBTransaction.h" -#include "nsIAppShell.h" -#include "nsIScriptContext.h" - -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/storage.h" -#include "nsDOMClassInfoID.h" -#include "mozilla/dom/DOMStringList.h" -#include "mozilla/EventDispatcher.h" -#include "nsPIDOMWindow.h" -#include "nsProxyRelease.h" -#include "nsThreadUtils.h" -#include "nsWidgetsCID.h" - -#include "AsyncConnectionHelper.h" -#include "DatabaseInfo.h" -#include "IDBCursor.h" +#include "BackgroundChildImpl.h" +#include "IDBDatabase.h" #include "IDBEvents.h" -#include "IDBFactory.h" #include "IDBObjectStore.h" -#include "IndexedDatabaseManager.h" +#include "IDBRequest.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/DOMError.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "nsIAppShell.h" +#include "nsIDOMFile.h" +#include "nsPIDOMWindow.h" +#include "nsServiceManagerUtils.h" +#include "nsTHashtable.h" +#include "nsWidgetsCID.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" -#include "TransactionThreadPool.h" -#include "ipc/IndexedDBChild.h" +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" -#define SAVEPOINT_NAME "savepoint" - -using namespace mozilla; -using namespace mozilla::dom; -USING_INDEXEDDB_NAMESPACE -using mozilla::dom::quota::QuotaManager; -using mozilla::ErrorResult; +namespace mozilla { +namespace dom { +namespace indexedDB { namespace { NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); -#ifdef MOZ_ENABLE_PROFILER_SPS -uint64_t gNextTransactionSerialNumber = 1; -#endif - -PLDHashOperator -DoomCachedStatements(const nsACString& aQuery, - nsCOMPtr& aStatement, - void* aUserArg) -{ - CommitHelper* helper = static_cast(aUserArg); - helper->AddDoomedObject(aStatement); - return PL_DHASH_REMOVE; -} - -// This runnable doesn't actually do anything beyond "prime the pump" and get -// transactions in the right order on the transaction thread pool. -class StartTransactionRunnable : public nsIRunnable -{ -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD Run() - { - // NOP - return NS_OK; - } -}; - -// Could really use those NS_REFCOUNTING_HAHA_YEAH_RIGHT macros here. -NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::AddRef() -{ - return 2; -} - -NS_IMETHODIMP_(MozExternalRefCountType) StartTransactionRunnable::Release() -{ - return 1; -} - -NS_IMPL_QUERY_INTERFACE(StartTransactionRunnable, nsIRunnable) - } // anonymous namespace -// static -already_AddRefed -IDBTransaction::CreateInternal(IDBDatabase* aDatabase, - const Sequence& aObjectStoreNames, - Mode aMode, - bool aDispatchDelayed, - bool aIsVersionChangeTransactionChild) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess() || !aDispatchDelayed, - "No support for delayed-dispatch transactions in child " - "process!"); - NS_ASSERTION(!aIsVersionChangeTransactionChild || - (!IndexedDatabaseManager::IsMainProcess() && - aMode == IDBTransaction::VERSION_CHANGE), - "Busted logic!"); - - nsRefPtr transaction = new IDBTransaction(aDatabase); - - transaction->SetScriptOwner(aDatabase->GetScriptOwner()); - transaction->mDatabase = aDatabase; - transaction->mMode = aMode; - transaction->mDatabaseInfo = aDatabase->Info(); - transaction->mObjectStoreNames.AppendElements(aObjectStoreNames); - transaction->mObjectStoreNames.Sort(); - - // Remove any duplicate object store names - const uint32_t count = transaction->mObjectStoreNames.Length(); - for (uint32_t index = count - 1; index > 0 && count > 0; index--) { - if (transaction->mObjectStoreNames[index] == - transaction->mObjectStoreNames[index - 1]) { - transaction->mObjectStoreNames.RemoveElementAt(index); - } - } - - IndexedDBTransactionChild* actor = nullptr; - - if (IndexedDatabaseManager::IsMainProcess()) { - if (aMode != IDBTransaction::VERSION_CHANGE) { - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - NS_ENSURE_TRUE(pool, nullptr); - - static StartTransactionRunnable sStartTransactionRunnable; - pool->Dispatch(transaction, &sStartTransactionRunnable, false, nullptr); - } - } - else if (!aIsVersionChangeTransactionChild) { - IndexedDBDatabaseChild* dbActor = aDatabase->GetActorChild(); - NS_ASSERTION(dbActor, "Must have an actor here!"); - - ipc::NormalTransactionParams params; - params.names().AppendElements(aObjectStoreNames); - params.mode() = aMode; - - actor = new IndexedDBTransactionChild(); - - dbActor->SendPIndexedDBTransactionConstructor(actor, params); - } - - if (!aDispatchDelayed) { - nsCOMPtr appShell = do_GetService(kAppShellCID); - NS_ENSURE_TRUE(appShell, nullptr); - - nsresult rv = appShell->RunBeforeNextEvent(transaction); - NS_ENSURE_SUCCESS(rv, nullptr); - - transaction->mCreating = true; - } - - if (actor) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - actor->SetTransaction(transaction); - } - - return transaction.forget(); -} - -IDBTransaction::IDBTransaction(IDBDatabase* aDatabase) -: IDBWrapperCache(aDatabase), - mReadyState(IDBTransaction::INITIAL), - mMode(IDBTransaction::READ_ONLY), - mPendingRequests(0), - mSavepointCount(0), - mActorChild(nullptr), - mActorParent(nullptr), - mAbortCode(NS_OK), -#ifdef MOZ_ENABLE_PROFILER_SPS - mSerialNumber(gNextTransactionSerialNumber++), -#endif - mCreating(false) +IDBTransaction::IDBTransaction(IDBDatabase* aDatabase, + const nsTArray& aObjectStoreNames, + Mode aMode) + : IDBWrapperCache(aDatabase) + , mDatabase(aDatabase) + , mObjectStoreNames(aObjectStoreNames) + , mNextObjectStoreId(0) + , mNextIndexId(0) + , mAbortCode(NS_OK) + , mPendingRequestCount(0) + , mReadyState(IDBTransaction::INITIAL) + , mMode(aMode) + , mCreating(false) + , mAbortedByScript(false) #ifdef DEBUG + , mSentCommitOrAbort(false) , mFiredCompleteOrAbort(false) #endif { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + + mBackgroundActor.mNormalBackgroundActor = nullptr; + +#ifdef MOZ_ENABLE_PROFILER_SPS + { + using namespace mozilla::ipc; + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + mSerialNumber = threadLocal->mNextTransactionSerialNumber++; + } +#endif + +#ifdef DEBUG + if (!aObjectStoreNames.IsEmpty()) { + nsTArray sortedNames(aObjectStoreNames); + sortedNames.Sort(); + + const uint32_t count = sortedNames.Length(); + MOZ_ASSERT(count == aObjectStoreNames.Length()); + + // Make sure the array is properly sorted. + for (uint32_t index = 0; index < count; index++) { + MOZ_ASSERT(aObjectStoreNames[index] == sortedNames[index]); + } + + // Make sure there are no duplicates in our objectStore names. + for (uint32_t index = 0; index < count - 1; index++) { + MOZ_ASSERT(sortedNames[index] != sortedNames[index + 1]); + } + } +#endif } IDBTransaction::~IDBTransaction() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mPendingRequests, "Should have no pending requests here!"); - NS_ASSERTION(!mSavepointCount, "Should have released them all!"); - NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!"); - NS_ASSERTION(!mCreating, "Should have been cleared already!"); - NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(!mPendingRequestCount); + MOZ_ASSERT(!mCreating); + MOZ_ASSERT(mSentCommitOrAbort); + MOZ_ASSERT_IF(mMode == VERSION_CHANGE && + mBackgroundActor.mVersionChangeBackgroundActor, + mFiredCompleteOrAbort); + MOZ_ASSERT_IF(mMode != VERSION_CHANGE && + mBackgroundActor.mNormalBackgroundActor, + mFiredCompleteOrAbort); - NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!"); - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->Send__delete__(mActorChild); - NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!"); + mDatabase->UnregisterTransaction(this); + + if (mMode == VERSION_CHANGE) { + if (mBackgroundActor.mVersionChangeBackgroundActor) { + mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor.mVersionChangeBackgroundActor, + "SendDeleteMeInternal should have cleared!"); + } + } else if (mBackgroundActor.mNormalBackgroundActor) { + mBackgroundActor.mNormalBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor, + "SendDeleteMeInternal should have cleared!"); + } +} + +// static +already_AddRefed +IDBTransaction::CreateVersionChange( + IDBDatabase* aDatabase, + BackgroundVersionChangeTransactionChild* aActor, + int64_t aNextObjectStoreId, + int64_t aNextIndexId) +{ + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aNextObjectStoreId > 0); + MOZ_ASSERT(aNextIndexId > 0); + + nsTArray emptyObjectStoreNames; + + nsRefPtr transaction = + new IDBTransaction(aDatabase, emptyObjectStoreNames, VERSION_CHANGE); + + transaction->SetScriptOwner(aDatabase->GetScriptOwner()); + transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor; + transaction->mNextObjectStoreId = aNextObjectStoreId; + transaction->mNextIndexId = aNextIndexId; + + // XXX Fix! + MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!"); + + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (NS_WARN_IF(!appShell) || + NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) { + return nullptr; + } + + transaction->mCreating = true; + + aDatabase->RegisterTransaction(transaction); + + return transaction.forget(); +} + +// static +already_AddRefed +IDBTransaction::Create(IDBDatabase* aDatabase, + const nsTArray& aObjectStoreNames, + Mode aMode) +{ + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(!aObjectStoreNames.IsEmpty()); + MOZ_ASSERT(aMode == READ_ONLY || aMode == READ_WRITE); + + nsRefPtr transaction = + new IDBTransaction(aDatabase, aObjectStoreNames, aMode); + + transaction->SetScriptOwner(aDatabase->GetScriptOwner()); + + // XXX Fix! + MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!"); + + nsCOMPtr appShell = do_GetService(kAppShellCID); + if (NS_WARN_IF(!appShell) || + NS_WARN_IF(NS_FAILED(appShell->RunBeforeNextEvent(transaction)))) { + return nullptr; + } + + transaction->mCreating = true; + + aDatabase->RegisterTransaction(transaction); + + return transaction.forget(); +} + +// static +IDBTransaction* +IDBTransaction::GetCurrent() +{ + using namespace mozilla::ipc; + + MOZ_ASSERT(BackgroundChild::GetForCurrentThread()); + + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + return threadLocal->mCurrentTransaction; +} + +#ifdef DEBUG + +void +IDBTransaction::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mDatabase); + mDatabase->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +void +IDBTransaction::SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor); + MOZ_ASSERT(mMode != VERSION_CHANGE); + + mBackgroundActor.mNormalBackgroundActor = aBackgroundActor; +} + +void +IDBTransaction::StartRequest(BackgroundRequestChild* aBackgroundActor, + const RequestParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(aParams.type() != RequestParams::T__None); + + if (mMode == VERSION_CHANGE) { + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + + mBackgroundActor.mVersionChangeBackgroundActor-> + SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams); + } else { + MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); + + mBackgroundActor.mNormalBackgroundActor-> + SendPBackgroundIDBRequestConstructor(aBackgroundActor, aParams); + } +} + +void +IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor, + const OpenCursorParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBackgroundActor); + MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None); + + if (mMode == VERSION_CHANGE) { + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + + mBackgroundActor.mVersionChangeBackgroundActor-> + SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams); + } else { + MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); + + mBackgroundActor.mNormalBackgroundActor-> + SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams); + } + + // Balanced in BackgroundCursorChild::RecvResponse(). + OnNewRequest(); +} + +void +IDBTransaction::RefreshSpec(bool aMayDelete) +{ + AssertIsOnOwningThread(); + + for (uint32_t count = mObjectStores.Length(), index = 0; + index < count; + index++) { + mObjectStores[index]->RefreshSpec(aMayDelete); + } + + for (uint32_t count = mDeletedObjectStores.Length(), index = 0; + index < count; + index++) { + mDeletedObjectStores[index]->RefreshSpec(false); } } void IDBTransaction::OnNewRequest() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (!mPendingRequests) { - NS_ASSERTION(mReadyState == IDBTransaction::INITIAL, - "Reusing a transaction!"); - mReadyState = IDBTransaction::LOADING; + AssertIsOnOwningThread(); + + if (!mPendingRequestCount) { + MOZ_ASSERT(INITIAL == mReadyState); + mReadyState = LOADING; } - ++mPendingRequests; + + ++mPendingRequestCount; } void IDBTransaction::OnRequestFinished() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mPendingRequests, "Mismatched calls!"); - --mPendingRequests; - if (!mPendingRequests) { - NS_ASSERTION(NS_FAILED(mAbortCode) || mReadyState == IDBTransaction::LOADING, - "Bad state!"); - mReadyState = IDBTransaction::COMMITTING; - CommitOrRollback(); - } -} + AssertIsOnOwningThread(); + MOZ_ASSERT(mPendingRequestCount); -void -IDBTransaction::OnRequestDisconnected() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mPendingRequests, "Mismatched calls!"); - --mPendingRequests; -} + --mPendingRequestCount; -void -IDBTransaction::RemoveObjectStore(const nsAString& aName) -{ - NS_ASSERTION(mMode == IDBTransaction::VERSION_CHANGE, - "Only remove object stores on VERSION_CHANGE transactions"); + if (!mPendingRequestCount && !mDatabase->IsInvalidated()) { + mReadyState = COMMITTING; - mDatabaseInfo->RemoveObjectStore(aName); - - for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { - if (mCreatedObjectStores[i]->Name() == aName) { - nsRefPtr objectStore = mCreatedObjectStores[i]; - mCreatedObjectStores.RemoveElementAt(i); - mDeletedObjectStores.AppendElement(objectStore); - break; + if (NS_SUCCEEDED(mAbortCode)) { + SendCommit(); + } else { + SendAbort(mAbortCode); } } } void -IDBTransaction::SetTransactionListener(IDBTransactionListener* aListener) +IDBTransaction::SendCommit() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mListener, "Shouldn't already have a listener!"); - mListener = aListener; -} + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_SUCCEEDED(mAbortCode)); + MOZ_ASSERT(IsFinished()); + MOZ_ASSERT(!mSentCommitOrAbort); -nsresult -IDBTransaction::CommitOrRollback() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (!IndexedDatabaseManager::IsMainProcess()) { - if (mActorChild) { - mActorChild->SendAllRequestsFinished(); - } - - return NS_OK; + if (mMode == VERSION_CHANGE) { + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + mBackgroundActor.mVersionChangeBackgroundActor->SendCommit(); + } else { + MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); + mBackgroundActor.mNormalBackgroundActor->SendCommit(); } - nsRefPtr helper = - new CommitHelper(this, mListener, mCreatedObjectStores); - - TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); - NS_ENSURE_STATE(pool); - - mCachedStatements.Enumerate(DoomCachedStatements, helper); - NS_ASSERTION(!mCachedStatements.Count(), "Statements left!"); - - nsresult rv = pool->Dispatch(this, helper, true, helper); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -bool -IDBTransaction::StartSavepoint() -{ - NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); - NS_PRECONDITION(mConnection, "No connection!"); - - nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( - "SAVEPOINT " SAVEPOINT_NAME - )); - NS_ENSURE_TRUE(stmt, false); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, false); - - if (IsWriteAllowed()) { - mUpdateFileRefcountFunction->StartSavepoint(); - } - - ++mSavepointCount; - - return true; -} - -nsresult -IDBTransaction::ReleaseSavepoint() -{ - NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); - NS_PRECONDITION(mConnection, "No connection!"); - - NS_ASSERTION(mSavepointCount, "Mismatch!"); - - nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( - "RELEASE SAVEPOINT " SAVEPOINT_NAME - )); - NS_ENSURE_TRUE(stmt, NS_OK); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, NS_OK); - - if (IsWriteAllowed()) { - mUpdateFileRefcountFunction->ReleaseSavepoint(); - } - - --mSavepointCount; - - return NS_OK; -} - -void -IDBTransaction::RollbackSavepoint() -{ - NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!"); - NS_PRECONDITION(mConnection, "No connection!"); - - NS_ASSERTION(mSavepointCount == 1, "Mismatch!"); - mSavepointCount = 0; - - nsCOMPtr stmt = GetCachedStatement(NS_LITERAL_CSTRING( - "ROLLBACK TO SAVEPOINT " SAVEPOINT_NAME - )); - NS_ENSURE_TRUE_VOID(stmt); - - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->Execute(); - NS_ENSURE_SUCCESS_VOID(rv); - - if (IsWriteAllowed()) { - mUpdateFileRefcountFunction->RollbackSavepoint(); - } -} - -nsresult -IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("IDBTransaction", "GetOrCreateConnection", - js::ProfileEntry::Category::STORAGE); - - if (mDatabase->IsInvalidated()) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (!mConnection) { - nsCOMPtr connection = - IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(), - mDatabase->Group(), mDatabase->Origin()); - NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE); - - nsresult rv; - - nsRefPtr function; - nsCString beginTransaction; - if (mMode != IDBTransaction::READ_ONLY) { - function = new UpdateRefcountFunction(Database()->Manager()); - NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY); - - rv = connection->CreateFunction( - NS_LITERAL_CSTRING("update_refcount"), 2, function); - NS_ENSURE_SUCCESS(rv, rv); - - beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;"); - } - else { - beginTransaction.AssignLiteral("BEGIN TRANSACTION;"); - } - - nsCOMPtr stmt; - rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - function.swap(mUpdateFileRefcountFunction); - connection.swap(mConnection); - } - - nsCOMPtr result(mConnection); - result.forget(aResult); - return NS_OK; -} - -already_AddRefed -IDBTransaction::GetCachedStatement(const nsACString& aQuery) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aQuery.IsEmpty(), "Empty sql statement!"); - NS_ASSERTION(mConnection, "No connection!"); - - nsCOMPtr stmt; - - if (!mCachedStatements.Get(aQuery, getter_AddRefs(stmt))) { - nsresult rv = mConnection->CreateStatement(aQuery, getter_AddRefs(stmt)); #ifdef DEBUG - if (NS_FAILED(rv)) { - nsCString error; - error.AppendLiteral("The statement `"); - error.Append(aQuery); - error.AppendLiteral("` failed to compile with the error message `"); - nsCString msg; - (void)mConnection->GetLastErrorString(msg); - error.Append(msg); - error.AppendLiteral("`."); - NS_ERROR(error.get()); - } + mSentCommitOrAbort = true; #endif - NS_ENSURE_SUCCESS(rv, nullptr); +} - mCachedStatements.Put(aQuery, stmt); +void +IDBTransaction::SendAbort(nsresult aResultCode) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResultCode)); + MOZ_ASSERT(IsFinished()); + MOZ_ASSERT(!mSentCommitOrAbort); + + if (mMode == VERSION_CHANGE) { + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode); + } else { + MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor); + mBackgroundActor.mNormalBackgroundActor->SendAbort(aResultCode); } - return stmt.forget(); +#ifdef DEBUG + mSentCommitOrAbort = true; +#endif } bool IDBTransaction::IsOpen() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // If we haven't started anything then we're open. if (mReadyState == IDBTransaction::INITIAL) { @@ -467,268 +383,449 @@ IDBTransaction::IsOpen() const // from the time we were created) then we are open. Otherwise check the // currently running transaction to see if it's the same. We only allow other // requests to be made if this transaction is currently running. - if (mReadyState == IDBTransaction::LOADING) { - if (mCreating) { - return true; - } - - if (AsyncConnectionHelper::GetCurrentTransaction() == this) { - return true; - } + if (mReadyState == IDBTransaction::LOADING && + (mCreating || GetCurrent() == this)) { + return true; } return false; } already_AddRefed -IDBTransaction::GetOrCreateObjectStore(const nsAString& aName, - ObjectStoreInfo* aObjectStoreInfo, - bool aCreating) +IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aObjectStoreInfo, "Null pointer!"); - NS_ASSERTION(!aCreating || GetMode() == IDBTransaction::VERSION_CHANGE, - "How else can we create here?!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aSpec.metadata().id()); + MOZ_ASSERT(VERSION_CHANGE == mMode); + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + MOZ_ASSERT(IsOpen()); - nsRefPtr retval; +#ifdef DEBUG + { + const nsString& name = aSpec.metadata().name(); - for (uint32_t index = 0; index < mCreatedObjectStores.Length(); index++) { - nsRefPtr& objectStore = mCreatedObjectStores[index]; - if (objectStore->Name() == aName) { - retval = objectStore; - return retval.forget(); + for (uint32_t count = mObjectStores.Length(), index = 0; + index < count; + index++) { + MOZ_ASSERT(mObjectStores[index]->Name() != name); } } +#endif - retval = IDBObjectStore::Create(this, aObjectStoreInfo, mDatabaseInfo->id, - aCreating); + MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> + SendCreateObjectStore(aSpec.metadata())); - mCreatedObjectStores.AppendElement(retval); + nsRefPtr objectStore = IDBObjectStore::Create(this, aSpec); + MOZ_ASSERT(objectStore); - return retval.forget(); -} + mObjectStores.AppendElement(objectStore); -already_AddRefed -IDBTransaction::GetFileInfo(nsIDOMBlob* aBlob) -{ - nsRefPtr fileInfo; - mCreatedFileInfos.Get(aBlob, getter_AddRefs(fileInfo)); - return fileInfo.forget(); + return objectStore.forget(); } void -IDBTransaction::AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo) +IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId) { - mCreatedFileInfos.Put(aBlob, aFileInfo); + AssertIsOnOwningThread(); + MOZ_ASSERT(aObjectStoreId); + MOZ_ASSERT(VERSION_CHANGE == mMode); + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + MOZ_ASSERT(IsOpen()); + + MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> + SendDeleteObjectStore(aObjectStoreId)); + + for (uint32_t count = mObjectStores.Length(), index = 0; + index < count; + index++) { + nsRefPtr& objectStore = mObjectStores[index]; + + if (objectStore->Id() == aObjectStoreId) { + objectStore->NoteDeletion(); + + nsRefPtr* deletedObjectStore = + mDeletedObjectStores.AppendElement(); + deletedObjectStore->swap(mObjectStores[index]); + + mObjectStores.RemoveElementAt(index); + break; + } + } } void -IDBTransaction::ClearCreatedFileInfos() +IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore, + const IndexMetadata& aMetadata) { - mCreatedFileInfos.Clear(); + AssertIsOnOwningThread(); + MOZ_ASSERT(aObjectStore); + MOZ_ASSERT(aMetadata.id()); + MOZ_ASSERT(VERSION_CHANGE == mMode); + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + MOZ_ASSERT(IsOpen()); + + MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> + SendCreateIndex(aObjectStore->Id(), aMetadata)); } -nsresult +void +IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore, + int64_t aIndexId) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aObjectStore); + MOZ_ASSERT(aIndexId); + MOZ_ASSERT(VERSION_CHANGE == mMode); + MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor); + MOZ_ASSERT(IsOpen()); + + MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor-> + SendDeleteIndex(aObjectStore->Id(), aIndexId)); +} + +void IDBTransaction::AbortInternal(nsresult aAbortCode, already_AddRefed aError) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aAbortCode)); nsRefPtr error = aError; if (IsFinished()) { - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + // Already finished, nothing to do here. + return; } - if (mActorChild) { - NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - mActorChild->SendAbort(aAbortCode); - } + const bool isVersionChange = mMode == VERSION_CHANGE; + const bool isInvalidated = mDatabase->IsInvalidated(); + bool needToSendAbort = mReadyState == INITIAL && !isInvalidated; - bool needToCommitOrRollback = mReadyState == IDBTransaction::INITIAL; +#ifdef DEBUG + if (isInvalidated) { + mSentCommitOrAbort = true; + } +#endif mAbortCode = aAbortCode; - mReadyState = IDBTransaction::DONE; + mReadyState = DONE; mError = error.forget(); - if (GetMode() == IDBTransaction::VERSION_CHANGE) { + if (isVersionChange) { // If a version change transaction is aborted, we must revert the world - // back to its previous state. - mDatabase->RevertToPreviousState(); - - DatabaseInfo* dbInfo = mDatabase->Info(); - - for (uint32_t i = 0; i < mCreatedObjectStores.Length(); i++) { - nsRefPtr& objectStore = mCreatedObjectStores[i]; - ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); - - if (!info) { - info = new ObjectStoreInfo(*objectStore->Info()); - info->indexes.Clear(); - } - - objectStore->SetInfo(info); + // back to its previous state unless we're being invalidated after the + // transaction already completed. + if (!isInvalidated) { + mDatabase->RevertToPreviousState(); } - for (uint32_t i = 0; i < mDeletedObjectStores.Length(); i++) { - nsRefPtr& objectStore = mDeletedObjectStores[i]; - ObjectStoreInfo* info = dbInfo->GetObjectStore(objectStore->Name()); + const nsTArray& specArray = + mDatabase->Spec()->objectStores(); - if (!info) { - info = new ObjectStoreInfo(*objectStore->Info()); - info->indexes.Clear(); + if (specArray.IsEmpty()) { + mObjectStores.Clear(); + mDeletedObjectStores.Clear(); + } else { + nsTHashtable validIds(specArray.Length()); + + for (uint32_t specCount = specArray.Length(), specIndex = 0; + specIndex < specCount; + specIndex++) { + const int64_t objectStoreId = specArray[specIndex].metadata().id(); + MOZ_ASSERT(objectStoreId); + + validIds.PutEntry(uint64_t(objectStoreId)); } - objectStore->SetInfo(info); - } + for (uint32_t objCount = mObjectStores.Length(), objIndex = 0; + objIndex < objCount; + /* incremented conditionally */) { + const int64_t objectStoreId = mObjectStores[objIndex]->Id(); + MOZ_ASSERT(objectStoreId); - // and then the db must be closed - mDatabase->Close(); + if (validIds.Contains(uint64_t(objectStoreId))) { + objIndex++; + } else { + mObjectStores.RemoveElementAt(objIndex); + objCount--; + } + } + + if (!mDeletedObjectStores.IsEmpty()) { + for (uint32_t objCount = mDeletedObjectStores.Length(), objIndex = 0; + objIndex < objCount; + objIndex++) { + const int64_t objectStoreId = mDeletedObjectStores[objIndex]->Id(); + MOZ_ASSERT(objectStoreId); + + if (validIds.Contains(uint64_t(objectStoreId))) { + nsRefPtr* objectStore = + mObjectStores.AppendElement(); + objectStore->swap(mDeletedObjectStores[objIndex]); + } + } + mDeletedObjectStores.Clear(); + } + } } // Fire the abort event if there are no outstanding requests. Otherwise the // abort event will be fired when all outstanding requests finish. - if (needToCommitOrRollback) { - return CommitOrRollback(); + if (needToSendAbort) { + SendAbort(aAbortCode); } - return NS_OK; + if (isVersionChange) { + mDatabase->Close(); + } } -nsresult +void IDBTransaction::Abort(IDBRequest* aRequest) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aRequest, "This is undesirable."); + AssertIsOnOwningThread(); + MOZ_ASSERT(aRequest); ErrorResult rv; nsRefPtr error = aRequest->GetError(rv); - return AbortInternal(aRequest->GetErrorCode(), error.forget()); + AbortInternal(aRequest->GetErrorCode(), error.forget()); } -nsresult +void IDBTransaction::Abort(nsresult aErrorCode) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); nsRefPtr error = new DOMError(GetOwner(), aErrorCode); - return AbortInternal(aErrorCode, error.forget()); + AbortInternal(aErrorCode, error.forget()); } +void +IDBTransaction::Abort(ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + if (IsFinished()) { + aRv = NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; + return; + } + + AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); + + MOZ_ASSERT(!mAbortedByScript); + mAbortedByScript = true; +} + +void +IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mFiredCompleteOrAbort); + + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", + "IDBTransaction[%llu] MT Complete", + mTransaction->GetSerialNumber(), mAbortCode); + + mReadyState = DONE; + +#ifdef DEBUG + mFiredCompleteOrAbort = true; +#endif + + nsCOMPtr event; + if (NS_SUCCEEDED(aResult)) { + event = CreateGenericEvent(this, + nsDependentString(kCompleteEventType), + eDoesNotBubble, + eNotCancelable); + } else { + if (!mError && !mAbortedByScript) { + mError = new DOMError(GetOwner(), aResult); + } + + event = CreateGenericEvent(this, + nsDependentString(kAbortEventType), + eDoesBubble, + eNotCancelable); + } + + if (NS_WARN_IF(!event)) { + return; + } + + bool dummy; + if (NS_FAILED(DispatchEvent(event, &dummy))) { + NS_WARNING("DispatchEvent failed!"); + } + + mDatabase->DelayedMaybeExpireFileActors(); +} + +int64_t +IDBTransaction::NextObjectStoreId() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(VERSION_CHANGE == mMode); + + return mNextObjectStoreId++; +} + +int64_t +IDBTransaction::NextIndexId() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(VERSION_CHANGE == mMode); + + return mNextIndexId++; +} + +nsPIDOMWindow* +IDBTransaction::GetParentObject() const +{ + AssertIsOnOwningThread(); + + return mDatabase->GetParentObject(); +} + +IDBTransactionMode +IDBTransaction::GetMode(ErrorResult& aRv) const +{ + AssertIsOnOwningThread(); + + switch (mMode) { + case READ_ONLY: + return IDBTransactionMode::Readonly; + + case READ_WRITE: + return IDBTransactionMode::Readwrite; + + case VERSION_CHANGE: + return IDBTransactionMode::Versionchange; + + case MODE_INVALID: + default: + MOZ_CRASH("Bad mode!"); + } +} + +DOMError* +IDBTransaction::GetError() const +{ + AssertIsOnOwningThread(); + + return mError; +} + +already_AddRefed +IDBTransaction::ObjectStoreNames() +{ + AssertIsOnOwningThread(); + + if (mMode == IDBTransaction::VERSION_CHANGE) { + return mDatabase->ObjectStoreNames(); + } + + nsRefPtr list = new DOMStringList(); + list->StringArray() = mObjectStoreNames; + return list.forget(); +} + +already_AddRefed +IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + if (IsFinished()) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + + const ObjectStoreSpec* spec = nullptr; + + if (IDBTransaction::VERSION_CHANGE == mMode || + mObjectStoreNames.Contains(aName)) { + const nsTArray& objectStores = + mDatabase->Spec()->objectStores(); + + for (uint32_t count = objectStores.Length(), index = 0; + index < count; + index++) { + const ObjectStoreSpec& objectStore = objectStores[index]; + if (objectStore.metadata().name() == aName) { + spec = &objectStore; + break; + } + } + } + + if (!spec) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); + return nullptr; + } + + const int64_t desiredId = spec->metadata().id(); + + nsRefPtr objectStore; + + for (uint32_t count = mObjectStores.Length(), index = 0; + index < count; + index++) { + nsRefPtr& existingObjectStore = mObjectStores[index]; + + if (existingObjectStore->Id() == desiredId) { + objectStore = existingObjectStore; + break; + } + } + + if (!objectStore) { + objectStore = IDBObjectStore::Create(this, *spec); + MOZ_ASSERT(objectStore); + + mObjectStores.AppendElement(objectStore); + } + + return objectStore.forget(); +} + +NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) + NS_INTERFACE_MAP_ENTRY(nsIRunnable) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) + NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCreatedObjectStores) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStores) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) // Don't unlink mDatabase! NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mCreatedObjectStores) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mObjectStores) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) - NS_INTERFACE_MAP_ENTRY(nsIRunnable) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) - -NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) - JSObject* IDBTransaction::WrapObject(JSContext* aCx) { + AssertIsOnOwningThread(); + return IDBTransactionBinding::Wrap(aCx, this); } -mozilla::dom::IDBTransactionMode -IDBTransaction::GetMode(ErrorResult& aRv) const -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - switch (mMode) { - case READ_ONLY: - return mozilla::dom::IDBTransactionMode::Readonly; - - case READ_WRITE: - return mozilla::dom::IDBTransactionMode::Readwrite; - - case VERSION_CHANGE: - return mozilla::dom::IDBTransactionMode::Versionchange; - - case MODE_INVALID: - default: - aRv.Throw(NS_ERROR_UNEXPECTED); - return mozilla::dom::IDBTransactionMode::Readonly; - } -} - -DOMError* -IDBTransaction::GetError() const -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return mError; -} - -already_AddRefed -IDBTransaction::GetObjectStoreNames(ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - nsRefPtr list(new DOMStringList()); - - if (mMode == IDBTransaction::VERSION_CHANGE) { - mDatabaseInfo->GetObjectStoreNames(list->StringArray()); - } - else { - list->StringArray() = mObjectStoreNames; - } - - return list.forget(); -} - -already_AddRefed -IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (IsFinished()) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - return nullptr; - } - - ObjectStoreInfo* info = nullptr; - - if (mMode == IDBTransaction::VERSION_CHANGE || - mObjectStoreNames.Contains(aName)) { - info = mDatabaseInfo->GetObjectStore(aName); - } - - if (!info) { - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR); - return nullptr; - } - - nsRefPtr objectStore = - GetOrCreateObjectStore(aName, info, false); - if (!objectStore) { - IDB_WARNING("Failed to get or create object store!"); - aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - return nullptr; - } - - return objectStore.forget(); -} - nsresult IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) { + AssertIsOnOwningThread(); + aVisitor.mCanHandle = true; aVisitor.mParentTarget = mDatabase; return NS_OK; @@ -737,545 +834,21 @@ IDBTransaction::PreHandleEvent(EventChainPreVisitor& aVisitor) NS_IMETHODIMP IDBTransaction::Run() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // We're back at the event loop, no longer newborn. mCreating = false; - // Maybe set the readyState to DONE if there were no requests generated. + // Maybe commit if there were no requests generated. if (mReadyState == IDBTransaction::INITIAL) { - mReadyState = IDBTransaction::DONE; + mReadyState = DONE; - if (NS_FAILED(CommitOrRollback())) { - NS_WARNING("Failed to commit!"); - } + SendCommit(); } return NS_OK; } -CommitHelper::CommitHelper( - IDBTransaction* aTransaction, - IDBTransactionListener* aListener, - const nsTArray >& aUpdatedObjectStores) -: mTransaction(aTransaction), - mListener(aListener), - mAbortCode(aTransaction->mAbortCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mConnection.swap(aTransaction->mConnection); - mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction); - - for (uint32_t i = 0; i < aUpdatedObjectStores.Length(); i++) { - ObjectStoreInfo* info = aUpdatedObjectStores[i]->Info(); - if (info->comittedAutoIncrementId != info->nextAutoIncrementId) { - mAutoIncrementObjectStores.AppendElement(aUpdatedObjectStores[i]); - } - } -} - -CommitHelper::CommitHelper(IDBTransaction* aTransaction, - nsresult aAbortCode) -: mTransaction(aTransaction), - mAbortCode(aAbortCode) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} - -CommitHelper::~CommitHelper() -{ -} - -NS_IMPL_ISUPPORTS(CommitHelper, nsIRunnable) - -NS_IMETHODIMP -CommitHelper::Run() -{ - if (NS_IsMainThread()) { - PROFILER_MAIN_THREAD_LABEL("CommitHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - NS_ASSERTION(mDoomedObjects.IsEmpty(), "Didn't release doomed objects!"); - - mTransaction->mReadyState = IDBTransaction::DONE; - - // Release file infos on the main thread, so they will eventually get - // destroyed on correct thread. - mTransaction->ClearCreatedFileInfos(); - if (mUpdateFileRefcountFunction) { - mUpdateFileRefcountFunction->ClearFileInfoEntries(); - mUpdateFileRefcountFunction = nullptr; - } - - nsCOMPtr event; - if (NS_FAILED(mAbortCode)) { - if (mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) { - // This will make the database take a snapshot of it's DatabaseInfo - mTransaction->Database()->Close(); - // Then remove the info from the hash as it contains invalid data. - DatabaseInfo::Remove(mTransaction->Database()->Id()); - } - - event = CreateGenericEvent(mTransaction, - NS_LITERAL_STRING(ABORT_EVT_STR), - eDoesBubble, eNotCancelable); - - // The transaction may already have an error object (e.g. if one of the - // requests failed). If it doesn't, and it wasn't aborted - // programmatically, create one now. - if (!mTransaction->mError && - mAbortCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { - mTransaction->mError = new DOMError(mTransaction->GetOwner(), mAbortCode); - } - } - else { - event = CreateGenericEvent(mTransaction, - NS_LITERAL_STRING(COMPLETE_EVT_STR), - eDoesNotBubble, eNotCancelable); - } - IDB_ENSURE_TRUE(event, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mListener) { - mListener->NotifyTransactionPreComplete(mTransaction); - } - - IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)", - "IDBTransaction[%llu] MT Complete", - mTransaction->GetSerialNumber(), mAbortCode); - - bool dummy; - if (NS_FAILED(mTransaction->DispatchEvent(event, &dummy))) { - NS_WARNING("Dispatch failed!"); - } - -#ifdef DEBUG - mTransaction->mFiredCompleteOrAbort = true; -#endif - - if (mListener) { - mListener->NotifyTransactionPostComplete(mTransaction); - } - - mTransaction = nullptr; - - return NS_OK; - } - - PROFILER_LABEL("CommitHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - IDBDatabase* database = mTransaction->Database(); - if (database->IsInvalidated()) { - IDB_REPORT_INTERNAL_ERR(); - mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (mConnection) { - QuotaManager::SetCurrentWindow(database->GetOwner()); - - if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction && - NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) { - IDB_REPORT_INTERNAL_ERR(); - mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) { - IDB_REPORT_INTERNAL_ERR(); - mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (NS_SUCCEEDED(mAbortCode)) { - NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION"); - nsresult rv = mConnection->ExecuteSimpleSQL(release); - if (NS_SUCCEEDED(rv)) { - if (mUpdateFileRefcountFunction) { - mUpdateFileRefcountFunction->DidCommit(); - } - CommitAutoIncrementCounts(); - } - else if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, - // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. - mAbortCode = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - else { - IDB_REPORT_INTERNAL_ERR(); - mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } - - if (NS_FAILED(mAbortCode)) { - if (mUpdateFileRefcountFunction) { - mUpdateFileRefcountFunction->DidAbort(); - } - RevertAutoIncrementCounts(); - NS_NAMED_LITERAL_CSTRING(rollback, "ROLLBACK TRANSACTION"); - if (NS_FAILED(mConnection->ExecuteSimpleSQL(rollback))) { - NS_WARNING("Failed to rollback transaction!"); - } - } - } - - mDoomedObjects.Clear(); - - if (mConnection) { - if (mUpdateFileRefcountFunction) { - nsresult rv = mConnection->RemoveFunction( - NS_LITERAL_CSTRING("update_refcount")); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to remove function!"); - } - } - - mConnection->Close(); - mConnection = nullptr; - - QuotaManager::SetCurrentWindow(nullptr); - } - - return NS_OK; -} - -nsresult -CommitHelper::WriteAutoIncrementCounts() -{ - nsCOMPtr stmt; - nsresult rv; - for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - if (!stmt) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE object_store SET auto_increment = :ai " - "WHERE id = :osid;"), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - } - else { - stmt->Reset(); - } - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), info->id); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("ai"), - info->nextAutoIncrementId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -void -CommitHelper::CommitAutoIncrementCounts() -{ - for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - info->comittedAutoIncrementId = info->nextAutoIncrementId; - } -} - -void -CommitHelper::RevertAutoIncrementCounts() -{ - for (uint32_t i = 0; i < mAutoIncrementObjectStores.Length(); i++) { - ObjectStoreInfo* info = mAutoIncrementObjectStores[i]->Info(); - info->nextAutoIncrementId = info->comittedAutoIncrementId; - } -} - -NS_IMPL_ISUPPORTS(UpdateRefcountFunction, mozIStorageFunction) - -NS_IMETHODIMP -UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues, - nsIVariant** _retval) -{ - *_retval = nullptr; - - uint32_t numEntries; - nsresult rv = aValues->GetNumEntries(&numEntries); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(numEntries == 2, "unexpected number of arguments"); - -#ifdef DEBUG - int32_t type1 = mozIStorageValueArray::VALUE_TYPE_NULL; - aValues->GetTypeOfIndex(0, &type1); - - int32_t type2 = mozIStorageValueArray::VALUE_TYPE_NULL; - aValues->GetTypeOfIndex(1, &type2); - - NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL && - type2 == mozIStorageValueArray::VALUE_TYPE_NULL), - "Shouldn't be called!"); -#endif - - rv = ProcessValue(aValues, 0, eDecrement); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ProcessValue(aValues, 1, eIncrement); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpdateRefcountFunction::WillCommit(mozIStorageConnection* aConnection) -{ - DatabaseUpdateFunction function(aConnection, this); - - mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function); - - nsresult rv = function.ErrorCode(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CreateJournals(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -UpdateRefcountFunction::DidCommit() -{ - mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nullptr); - - nsresult rv = RemoveJournals(mJournalsToRemoveAfterCommit); - NS_ENSURE_SUCCESS_VOID(rv); -} - -void -UpdateRefcountFunction::DidAbort() -{ - nsresult rv = RemoveJournals(mJournalsToRemoveAfterAbort); - NS_ENSURE_SUCCESS_VOID(rv); -} - -nsresult -UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues, - int32_t aIndex, - UpdateType aUpdateType) -{ - int32_t type; - aValues->GetTypeOfIndex(aIndex, &type); - if (type == mozIStorageValueArray::VALUE_TYPE_NULL) { - return NS_OK; - } - - nsString ids; - aValues->GetString(aIndex, ids); - - nsTArray fileIds; - nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds); - NS_ENSURE_SUCCESS(rv, rv); - - for (uint32_t i = 0; i < fileIds.Length(); i++) { - int64_t id = fileIds.ElementAt(i); - - FileInfoEntry* entry; - if (!mFileInfoEntries.Get(id, &entry)) { - nsRefPtr fileInfo = mFileManager->GetFileInfo(id); - NS_ASSERTION(fileInfo, "Shouldn't be null!"); - - nsAutoPtr newEntry(new FileInfoEntry(fileInfo)); - mFileInfoEntries.Put(id, newEntry); - entry = newEntry.forget(); - } - - if (mInSavepoint) { - mSavepointEntriesIndex.Put(id, entry); - } - - switch (aUpdateType) { - case eIncrement: - entry->mDelta++; - if (mInSavepoint) { - entry->mSavepointDelta++; - } - break; - case eDecrement: - entry->mDelta--; - if (mInSavepoint) { - entry->mSavepointDelta--; - } - break; - default: - NS_NOTREACHED("Unknown update type!"); - } - } - - return NS_OK; -} - -nsresult -UpdateRefcountFunction::CreateJournals() -{ - nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); - NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); - - for (uint32_t i = 0; i < mJournalsToCreateBeforeCommit.Length(); i++) { - int64_t id = mJournalsToCreateBeforeCommit[i]; - - nsCOMPtr file = - mFileManager->GetFileForId(journalDirectory, id); - NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); - - nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - NS_ENSURE_SUCCESS(rv, rv); - - mJournalsToRemoveAfterAbort.AppendElement(id); - } - - return NS_OK; -} - -nsresult -UpdateRefcountFunction::RemoveJournals(const nsTArray& aJournals) -{ - nsCOMPtr journalDirectory = mFileManager->GetJournalDirectory(); - NS_ENSURE_TRUE(journalDirectory, NS_ERROR_FAILURE); - - for (uint32_t index = 0; index < aJournals.Length(); index++) { - nsCOMPtr file = - mFileManager->GetFileForId(journalDirectory, aJournals[index]); - NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); - - if (NS_FAILED(file->Remove(false))) { - NS_WARNING("Failed to removed journal!"); - } - } - - return NS_OK; -} - -PLDHashOperator -UpdateRefcountFunction::DatabaseUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg) -{ - if (!aValue->mDelta) { - return PL_DHASH_NEXT; - } - - DatabaseUpdateFunction* function = - static_cast(aUserArg); - - if (!function->Update(aKey, aValue->mDelta)) { - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; -} - -PLDHashOperator -UpdateRefcountFunction::FileInfoUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg) -{ - if (aValue->mDelta) { - aValue->mFileInfo->UpdateDBRefs(aValue->mDelta); - } - - return PL_DHASH_NEXT; -} - -PLDHashOperator -UpdateRefcountFunction::RollbackSavepointCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg) -{ - aValue->mDelta -= aValue->mSavepointDelta; - - return PL_DHASH_NEXT; -} - -bool -UpdateRefcountFunction::DatabaseUpdateFunction::Update(int64_t aId, - int32_t aDelta) -{ - nsresult rv = UpdateInternal(aId, aDelta); - if (NS_FAILED(rv)) { - mErrorCode = rv; - return false; - } - - return true; -} - -nsresult -UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(int64_t aId, - int32_t aDelta) -{ - nsresult rv; - - if (!mUpdateStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE file SET refcount = refcount + :delta WHERE id = :id" - ), getter_AddRefs(mUpdateStatement)); - NS_ENSURE_SUCCESS(rv, rv); - } - - mozStorageStatementScoper updateScoper(mUpdateStatement); - - rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mUpdateStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t rows; - rv = mConnection->GetAffectedRows(&rows); - NS_ENSURE_SUCCESS(rv, rv); - - if (rows > 0) { - if (!mSelectStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id FROM file where id = :id" - ), getter_AddRefs(mSelectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - } - - mozStorageStatementScoper selectScoper(mSelectStatement); - - rv = mSelectStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasResult; - rv = mSelectStatement->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - - if (!hasResult) { - // Don't have to create the journal here, we can create all at once, - // just before commit - mFunction->mJournalsToCreateBeforeCommit.AppendElement(aId); - } - - return NS_OK; - } - - if (!mInsertStatement) { - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO file (id, refcount) VALUES(:id, :delta)" - ), getter_AddRefs(mInsertStatement)); - NS_ENSURE_SUCCESS(rv, rv); - } - - mozStorageStatementScoper insertScoper(mInsertStatement); - - rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - mFunction->mJournalsToRemoveAfterCommit.AppendElement(aId); - - return NS_OK; -} +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index db8149323e98..d07d5480a9cb 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -8,71 +8,48 @@ #define mozilla_dom_indexeddb_idbtransaction_h__ #include "mozilla/Attributes.h" -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "mozIStorageConnection.h" -#include "mozIStorageStatement.h" -#include "mozIStorageFunction.h" -#include "mozilla/dom/DOMError.h" -#include "nsIRunnable.h" - -#include "nsAutoPtr.h" -#include "nsClassHashtable.h" -#include "nsHashKeys.h" -#include "nsInterfaceHashtable.h" -#include "nsRefPtrHashtable.h" - #include "mozilla/dom/IDBTransactionBinding.h" -#include "mozilla/dom/indexedDB/IDBDatabase.h" #include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "mozilla/dom/indexedDB/FileInfo.h" +#include "nsAutoPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIRunnable.h" +#include "nsString.h" +#include "nsTArray.h" -class nsIThread; +class nsIDOMBlob; class nsPIDOMWindow; namespace mozilla { + +class ErrorResult; class EventChainPreVisitor; -} // namespace mozilla -BEGIN_INDEXEDDB_NAMESPACE +namespace dom { -class AsyncConnectionHelper; -class CommitHelper; +class DOMError; +class DOMStringList; +class PBlobChild; + +namespace indexedDB { + +class BackgroundCursorChild; +class BackgroundRequestChild; +class BackgroundTransactionChild; +class BackgroundVersionChangeTransactionChild; +class IDBDatabase; +class IDBObjectStore; class IDBRequest; -class IndexedDBDatabaseChild; -class IndexedDBTransactionChild; -class IndexedDBTransactionParent; -struct ObjectStoreInfo; -class TransactionThreadPool; -class UpdateRefcountFunction; +class IndexMetadata; +class ObjectStoreSpec; +class OpenCursorParams; +class PBackgroundIDBDatabaseFileChild; +class RequestParams; -class IDBTransactionListener +class IDBTransaction MOZ_FINAL + : public IDBWrapperCache + , public nsIRunnable { public: - NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; - NS_IMETHOD_(MozExternalRefCountType) Release() = 0; - - // Called just before dispatching the final events on the transaction. - virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) = 0; - // Called just after dispatching the final events on the transaction. - virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) = 0; -}; - -class IDBTransaction : public IDBWrapperCache, - public nsIRunnable -{ - friend class AsyncConnectionHelper; - friend class CommitHelper; - friend class IndexedDBDatabaseChild; - friend class ThreadObserver; - friend class TransactionThreadPool; - -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIRUNNABLE - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) - enum Mode { READ_ONLY = 0, @@ -91,156 +68,184 @@ public: DONE }; +private: + nsRefPtr mDatabase; + nsRefPtr mError; + nsTArray mObjectStoreNames; + nsTArray> mObjectStores; + nsTArray> mDeletedObjectStores; + + // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be + // a BackgroundVersionChangeTransactionChild. Otherwise it will be a + // BackgroundTransactionChild. + union { + BackgroundTransactionChild* mNormalBackgroundActor; + BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor; + } mBackgroundActor; + + + // Only used for VERSION_CHANGE transactions. + int64_t mNextObjectStoreId; + int64_t mNextIndexId; + +#ifdef MOZ_ENABLE_PROFILER_SPS + uint64_t mSerialNumber; +#endif + + nsresult mAbortCode; + uint32_t mPendingRequestCount; + + ReadyState mReadyState; + Mode mMode; + + bool mCreating; + bool mAbortedByScript; + +#ifdef DEBUG + bool mSentCommitOrAbort; + bool mFiredCompleteOrAbort; +#endif + +public: + static already_AddRefed + CreateVersionChange(IDBDatabase* aDatabase, + BackgroundVersionChangeTransactionChild* aActor, + int64_t aNextObjectStoreId, + int64_t aNextIndexId); + static already_AddRefed Create(IDBDatabase* aDatabase, - const Sequence& aObjectStoreNames, - Mode aMode, - bool aDispatchDelayed) + const nsTArray& aObjectStoreNames, + Mode aMode); + + static IDBTransaction* + GetCurrent(); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + + void + SetBackgroundActor(BackgroundTransactionChild* aBackgroundActor); + + void + ClearBackgroundActor() { - return CreateInternal(aDatabase, aObjectStoreNames, aMode, aDispatchDelayed, - false); + AssertIsOnOwningThread(); + + if (mMode == VERSION_CHANGE) { + mBackgroundActor.mVersionChangeBackgroundActor = nullptr; + } else { + mBackgroundActor.mNormalBackgroundActor = nullptr; + } } - // nsIDOMEventTarget - virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; + void + StartRequest(BackgroundRequestChild* aBackgroundActor, + const RequestParams& aParams); - void OnNewRequest(); - void OnRequestFinished(); - void OnRequestDisconnected(); + void + OpenCursor(BackgroundCursorChild* aBackgroundActor, + const OpenCursorParams& aParams); - void RemoveObjectStore(const nsAString& aName); + void + RefreshSpec(bool aMayDelete); - void SetTransactionListener(IDBTransactionListener* aListener); + void + OnNewRequest(); - bool StartSavepoint(); - nsresult ReleaseSavepoint(); - void RollbackSavepoint(); + void + OnRequestFinished(); - // Only meant to be called on mStorageThread! - nsresult GetOrCreateConnection(mozIStorageConnection** aConnection); + bool + IsOpen() const; - already_AddRefed - GetCachedStatement(const nsACString& aQuery); - - template - already_AddRefed - GetCachedStatement(const char (&aQuery)[N]) - { - return GetCachedStatement(NS_LITERAL_CSTRING(aQuery)); - } - - bool IsOpen() const; - - bool IsFinished() const + bool + IsFinished() const { + AssertIsOnOwningThread(); return mReadyState > LOADING; } - bool IsWriteAllowed() const + bool + IsWriteAllowed() const { + AssertIsOnOwningThread(); return mMode == READ_WRITE || mMode == VERSION_CHANGE; } - bool IsAborted() const + bool + IsAborted() const { + AssertIsOnOwningThread(); return NS_FAILED(mAbortCode); } // 'Get' prefix is to avoid name collisions with the enum - Mode GetMode() + Mode + GetMode() const { + AssertIsOnOwningThread(); return mMode; } - IDBDatabase* Database() + IDBDatabase* + Database() const { - NS_ASSERTION(mDatabase, "This should never be null!"); + AssertIsOnOwningThread(); return mDatabase; } - DatabaseInfo* DBInfo() const - { - return mDatabaseInfo; - } - - already_AddRefed - GetOrCreateObjectStore(const nsAString& aName, - ObjectStoreInfo* aObjectStoreInfo, - bool aCreating); - - already_AddRefed GetFileInfo(nsIDOMBlob* aBlob); - void AddFileInfo(nsIDOMBlob* aBlob, FileInfo* aFileInfo); - - void ClearCreatedFileInfos(); - - void - SetActor(IndexedDBTransactionChild* aActorChild) - { - NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!"); - mActorChild = aActorChild; - } - - void - SetActor(IndexedDBTransactionParent* aActorParent) - { - NS_ASSERTION(!aActorParent || !mActorParent, - "Shouldn't have more than one!"); - mActorParent = aActorParent; - } - - IndexedDBTransactionChild* - GetActorChild() const - { - return mActorChild; - } - - IndexedDBTransactionParent* - GetActorParent() const - { - return mActorParent; - } - - nsresult - Abort(IDBRequest* aRequest); - - nsresult - Abort(nsresult aAbortCode); - - nsresult - GetAbortCode() const - { - return mAbortCode; - } - -#ifdef MOZ_ENABLE_PROFILER_SPS - uint64_t - GetSerialNumber() const - { - return mSerialNumber; - } -#endif - - // nsWrapperCache - virtual JSObject* - WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - // WebIDL - nsPIDOMWindow* - GetParentObject() const - { - return GetOwner(); - } - - IDBTransactionMode - GetMode(ErrorResult& aRv) const; - IDBDatabase* Db() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - return mDatabase; + return Database(); } + const nsTArray& + ObjectStoreNamesInternal() const + { + AssertIsOnOwningThread(); + return mObjectStoreNames; + } + + already_AddRefed + CreateObjectStore(const ObjectStoreSpec& aSpec); + + void + DeleteObjectStore(int64_t aObjectStoreId); + + void + CreateIndex(IDBObjectStore* aObjectStore, const IndexMetadata& aMetadata); + + void + DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId); + + void + Abort(IDBRequest* aRequest); + + void + Abort(nsresult aAbortCode); + +#ifdef MOZ_ENABLE_PROFILER_SPS + uint32_t + GetSerialNumber() const + { + AssertIsOnOwningThread(); + return mSerialNumber; + } +#endif + + nsPIDOMWindow* + GetParentObject() const; + + IDBTransactionMode + GetMode(ErrorResult& aRv) const; + DOMError* GetError() const; @@ -248,255 +253,56 @@ public: ObjectStore(const nsAString& aName, ErrorResult& aRv); void - Abort(ErrorResult& aRv) - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - aRv = AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr); - } + Abort(ErrorResult& aRv); IMPL_EVENT_HANDLER(abort) IMPL_EVENT_HANDLER(complete) IMPL_EVENT_HANDLER(error) already_AddRefed - GetObjectStoreNames(ErrorResult& aRv); + ObjectStoreNames(); + + void + FireCompleteOrAbortEvents(nsresult aResult); + + // Only for VERSION_CHANGE transactions. + int64_t + NextObjectStoreId(); + + // Only for VERSION_CHANGE transactions. + int64_t + NextIndexId(); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) + + // nsWrapperCache + virtual JSObject* + WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // nsIDOMEventTarget + virtual nsresult + PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE; private: - nsresult - AbortInternal(nsresult aAbortCode, - already_AddRefed aError); - - // Should only be called directly through IndexedDBDatabaseChild. - static already_AddRefed - CreateInternal(IDBDatabase* aDatabase, - const Sequence& aObjectStoreNames, - Mode aMode, - bool aDispatchDelayed, - bool aIsVersionChangeTransactionChild); - - explicit IDBTransaction(IDBDatabase* aDatabase); + IDBTransaction(IDBDatabase* aDatabase, + const nsTArray& aObjectStoreNames, + Mode aMode); ~IDBTransaction(); - nsresult CommitOrRollback(); + void + AbortInternal(nsresult aAbortCode, already_AddRefed aError); - nsRefPtr mDatabase; - nsRefPtr mDatabaseInfo; - nsRefPtr mError; - nsTArray mObjectStoreNames; - ReadyState mReadyState; - Mode mMode; - uint32_t mPendingRequests; + void + SendCommit(); - nsInterfaceHashtable - mCachedStatements; - - nsRefPtr mListener; - - // Only touched on the database thread. - nsCOMPtr mConnection; - - // Only touched on the database thread. - uint32_t mSavepointCount; - - nsTArray > mCreatedObjectStores; - nsTArray > mDeletedObjectStores; - - nsRefPtr mUpdateFileRefcountFunction; - nsRefPtrHashtable mCreatedFileInfos; - - IndexedDBTransactionChild* mActorChild; - IndexedDBTransactionParent* mActorParent; - - nsresult mAbortCode; -#ifdef MOZ_ENABLE_PROFILER_SPS - uint64_t mSerialNumber; -#endif - bool mCreating; - -#ifdef DEBUG - bool mFiredCompleteOrAbort; -#endif + void + SendAbort(nsresult aResultCode); }; -class CommitHelper MOZ_FINAL : public nsIRunnable -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - CommitHelper(IDBTransaction* aTransaction, - IDBTransactionListener* aListener, - const nsTArray >& mUpdatedObjectStores); - CommitHelper(IDBTransaction* aTransaction, - nsresult aAbortCode); - - template - bool AddDoomedObject(nsCOMPtr& aCOMPtr) - { - if (aCOMPtr) { - if (!mDoomedObjects.AppendElement(do_QueryInterface(aCOMPtr))) { - NS_ERROR("Out of memory!"); - return false; - } - aCOMPtr = nullptr; - } - return true; - } - -private: - ~CommitHelper(); - - // Writes new autoincrement counts to database - nsresult WriteAutoIncrementCounts(); - - // Updates counts after a successful commit - void CommitAutoIncrementCounts(); - - // Reverts counts when a transaction is aborted - void RevertAutoIncrementCounts(); - - nsRefPtr mTransaction; - nsRefPtr mListener; - nsCOMPtr mConnection; - nsRefPtr mUpdateFileRefcountFunction; - nsAutoTArray, 10> mDoomedObjects; - nsAutoTArray, 10> mAutoIncrementObjectStores; - - nsresult mAbortCode; -}; - -class UpdateRefcountFunction MOZ_FINAL : public mozIStorageFunction -{ - ~UpdateRefcountFunction() - { } - -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_MOZISTORAGEFUNCTION - - explicit UpdateRefcountFunction(FileManager* aFileManager) - : mFileManager(aFileManager), mInSavepoint(false) - { } - - void StartSavepoint() - { - MOZ_ASSERT(!mInSavepoint); - MOZ_ASSERT(!mSavepointEntriesIndex.Count()); - - mInSavepoint = true; - } - - void ReleaseSavepoint() - { - MOZ_ASSERT(mInSavepoint); - - mSavepointEntriesIndex.Clear(); - - mInSavepoint = false; - } - - void RollbackSavepoint() - { - MOZ_ASSERT(mInSavepoint); - - mInSavepoint = false; - - mSavepointEntriesIndex.EnumerateRead(RollbackSavepointCallback, nullptr); - - mSavepointEntriesIndex.Clear(); - } - - void ClearFileInfoEntries() - { - mFileInfoEntries.Clear(); - } - - nsresult WillCommit(mozIStorageConnection* aConnection); - void DidCommit(); - void DidAbort(); - -private: - class FileInfoEntry - { - public: - explicit FileInfoEntry(FileInfo* aFileInfo) - : mFileInfo(aFileInfo), mDelta(0), mSavepointDelta(0) - { } - - ~FileInfoEntry() - { } - - nsRefPtr mFileInfo; - int32_t mDelta; - int32_t mSavepointDelta; - }; - - enum UpdateType { - eIncrement, - eDecrement - }; - - class DatabaseUpdateFunction - { - public: - DatabaseUpdateFunction(mozIStorageConnection* aConnection, - UpdateRefcountFunction* aFunction) - : mConnection(aConnection), mFunction(aFunction), mErrorCode(NS_OK) - { } - - bool Update(int64_t aId, int32_t aDelta); - nsresult ErrorCode() - { - return mErrorCode; - } - - private: - nsresult UpdateInternal(int64_t aId, int32_t aDelta); - - nsCOMPtr mConnection; - nsCOMPtr mUpdateStatement; - nsCOMPtr mSelectStatement; - nsCOMPtr mInsertStatement; - - UpdateRefcountFunction* mFunction; - - nsresult mErrorCode; - }; - - nsresult ProcessValue(mozIStorageValueArray* aValues, - int32_t aIndex, - UpdateType aUpdateType); - - nsresult CreateJournals(); - - nsresult RemoveJournals(const nsTArray& aJournals); - - static PLDHashOperator - DatabaseUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg); - - static PLDHashOperator - FileInfoUpdateCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg); - - static PLDHashOperator - RollbackSavepointCallback(const uint64_t& aKey, - FileInfoEntry* aValue, - void* aUserArg); - - FileManager* mFileManager; - nsClassHashtable mFileInfoEntries; - nsDataHashtable mSavepointEntriesIndex; - - nsTArray mJournalsToCreateBeforeCommit; - nsTArray mJournalsToRemoveAfterCommit; - nsTArray mJournalsToRemoveAfterAbort; - - bool mInSavepoint; -}; - -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbtransaction_h__ diff --git a/dom/indexedDB/IDBWrapperCache.cpp b/dom/indexedDB/IDBWrapperCache.cpp index 964f2992ea06..8a7b46249b27 100644 --- a/dom/indexedDB/IDBWrapperCache.cpp +++ b/dom/indexedDB/IDBWrapperCache.cpp @@ -5,9 +5,19 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "IDBWrapperCache.h" -#include "nsCycleCollector.h" -USING_INDEXEDDB_NAMESPACE +#include "mozilla/HoldDropJSObjects.h" +#include "nsCOMPtr.h" +#include "nsIScriptGlobalObject.h" +#include "nsPIDOMWindow.h" + +#ifdef DEBUG +#include "nsCycleCollector.h" +#endif + +namespace mozilla { +namespace dom { +namespace indexedDB { NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache) @@ -38,6 +48,14 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, DOMEventTargetHelper) +IDBWrapperCache::IDBWrapperCache(DOMEventTargetHelper* aOwner) + : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) +{ } + +IDBWrapperCache::IDBWrapperCache(nsPIDOMWindow* aOwner) + : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) +{ } + IDBWrapperCache::~IDBWrapperCache() { mScriptOwner = nullptr; @@ -48,7 +66,7 @@ IDBWrapperCache::~IDBWrapperCache() void IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner) { - NS_ASSERTION(aScriptOwner, "This should never be null!"); + MOZ_ASSERT(aScriptOwner); mScriptOwner = aScriptOwner; mozilla::HoldJSObjects(this); @@ -62,3 +80,7 @@ IDBWrapperCache::AssertIsRooted() const "Why aren't we rooted?!"); } #endif + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IDBWrapperCache.h b/dom/indexedDB/IDBWrapperCache.h index 129cc0b3129a..d77e9dd93e07 100644 --- a/dom/indexedDB/IDBWrapperCache.h +++ b/dom/indexedDB/IDBWrapperCache.h @@ -7,47 +7,51 @@ #ifndef mozilla_dom_indexeddb_idbwrappercache_h__ #define mozilla_dom_indexeddb_idbwrappercache_h__ +#include "js/RootingAPI.h" #include "mozilla/DOMEventTargetHelper.h" -#include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" -BEGIN_INDEXEDDB_NAMESPACE +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { +namespace indexedDB { class IDBWrapperCache : public DOMEventTargetHelper { + JS::Heap mScriptOwner; + public: NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( - IDBWrapperCache, - DOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBWrapperCache, + DOMEventTargetHelper) - JSObject* GetScriptOwner() const + JSObject* + GetScriptOwner() const { return mScriptOwner; } - void SetScriptOwner(JSObject* aScriptOwner); + void + SetScriptOwner(JSObject* aScriptOwner); + + void AssertIsRooted() const #ifdef DEBUG - void AssertIsRooted() const; + ; #else - inline void AssertIsRooted() const - { - } + { } #endif protected: - explicit IDBWrapperCache(DOMEventTargetHelper* aOwner) - : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) - { } - explicit IDBWrapperCache(nsPIDOMWindow* aOwner) - : DOMEventTargetHelper(aOwner), mScriptOwner(nullptr) - { } + explicit IDBWrapperCache(DOMEventTargetHelper* aOwner); + explicit IDBWrapperCache(nsPIDOMWindow* aOwner); virtual ~IDBWrapperCache(); - -private: - JS::Heap mScriptOwner; }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_idbwrappercache_h__ diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index 1afeab71b0de..634e684760f6 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -9,148 +9,70 @@ #include "nsIProgrammingLanguage.h" -#include "mozilla/Attributes.h" #include "js/StructuredClone.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" -#include "nsDebug.h" -#include "nsError.h" -#include "nsString.h" #include "nsTArray.h" -#include "nsIInputStream.h" - -#define BEGIN_INDEXEDDB_NAMESPACE \ - namespace mozilla { namespace dom { namespace indexedDB { - -#define END_INDEXEDDB_NAMESPACE \ - } /* namespace indexedDB */ } /* namepsace dom */ } /* namespace mozilla */ - -#define USING_INDEXEDDB_NAMESPACE \ - using namespace mozilla::dom::indexedDB; class nsIDOMBlob; +class nsIInputStream; -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { class FileInfo; class IDBDatabase; class IDBTransaction; +class SerializedStructuredCloneReadInfo; +class SerializedStructuredCloneWriteInfo; struct StructuredCloneFile { - bool operator==(const StructuredCloneFile& aOther) const - { - return this->mFile == aOther.mFile && - this->mFileInfo == aOther.mFileInfo && - this->mInputStream == aOther.mInputStream; - } - nsCOMPtr mFile; nsRefPtr mFileInfo; - nsCOMPtr mInputStream; -}; -struct SerializedStructuredCloneReadInfo; + // In IndexedDatabaseInlines.h + inline + StructuredCloneFile(); + + // In IndexedDatabaseInlines.h + inline + ~StructuredCloneFile(); + + // In IndexedDatabaseInlines.h + inline bool + operator==(const StructuredCloneFile& aOther) const; +}; struct StructuredCloneReadInfo { - // In IndexedDatabaseInlines.h - inline StructuredCloneReadInfo(); - - inline StructuredCloneReadInfo& - operator=(StructuredCloneReadInfo&& aCloneReadInfo); - - // In IndexedDatabaseInlines.h - inline bool - SetFromSerialized(const SerializedStructuredCloneReadInfo& aOther); - - JSAutoStructuredCloneBuffer mCloneBuffer; + nsTArray mData; nsTArray mFiles; IDBDatabase* mDatabase; -}; - -struct SerializedStructuredCloneReadInfo -{ - SerializedStructuredCloneReadInfo() - : data(nullptr), dataLength(0) - { } - - bool - operator==(const SerializedStructuredCloneReadInfo& aOther) const - { - return this->data == aOther.data && - this->dataLength == aOther.dataLength; - } - - SerializedStructuredCloneReadInfo& - operator=(const StructuredCloneReadInfo& aOther) - { - data = aOther.mCloneBuffer.data(); - dataLength = aOther.mCloneBuffer.nbytes(); - return *this; - } - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - uint64_t* data; - size_t dataLength; -}; - -struct SerializedStructuredCloneWriteInfo; - -struct StructuredCloneWriteInfo -{ - // In IndexedDatabaseInlines.h - inline StructuredCloneWriteInfo(); - inline StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo); - - bool operator==(const StructuredCloneWriteInfo& aOther) const - { - return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() && - this->mCloneBuffer.data() == aOther.mCloneBuffer.data() && - this->mFiles == aOther.mFiles && - this->mTransaction == aOther.mTransaction && - this->mOffsetToKeyProp == aOther.mOffsetToKeyProp; - } - - // In IndexedDatabaseInlines.h - inline bool - SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther); + // XXX Remove! JSAutoStructuredCloneBuffer mCloneBuffer; - nsTArray mFiles; - IDBTransaction* mTransaction; - uint64_t mOffsetToKeyProp; + + // In IndexedDatabaseInlines.h + inline + StructuredCloneReadInfo(); + + // In IndexedDatabaseInlines.h + inline + ~StructuredCloneReadInfo(); + + // In IndexedDatabaseInlines.h + inline StructuredCloneReadInfo& + operator=(StructuredCloneReadInfo&& aOther); + + // In IndexedDatabaseInlines.h + inline + StructuredCloneReadInfo(SerializedStructuredCloneReadInfo&& aOther); }; -struct SerializedStructuredCloneWriteInfo -{ - SerializedStructuredCloneWriteInfo() - : data(nullptr), dataLength(0), offsetToKeyProp(0) - { } - - bool - operator==(const SerializedStructuredCloneWriteInfo& aOther) const - { - return this->data == aOther.data && - this->dataLength == aOther.dataLength && - this->offsetToKeyProp == aOther.offsetToKeyProp; - } - - SerializedStructuredCloneWriteInfo& - operator=(const StructuredCloneWriteInfo& aOther) - { - data = aOther.mCloneBuffer.data(); - dataLength = aOther.mCloneBuffer.nbytes(); - offsetToKeyProp = aOther.mOffsetToKeyProp; - return *this; - } - - // Make sure to update ipc/SerializationHelpers.h when changing members here! - uint64_t* data; - size_t dataLength; - uint64_t offsetToKeyProp; -}; - -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_indexeddatabase_h__ diff --git a/dom/indexedDB/IndexedDatabaseInlines.h b/dom/indexedDB/IndexedDatabaseInlines.h index b856af67d55d..1b632217e5ee 100644 --- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -11,48 +11,55 @@ #error Must include IndexedDatabase.h first #endif -BEGIN_INDEXEDDB_NAMESPACE +#include "FileInfo.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "nsIDOMFile.h" +#include "nsIInputStream.h" + +namespace mozilla { +namespace dom { +namespace indexedDB { inline -StructuredCloneWriteInfo::StructuredCloneWriteInfo() -: mTransaction(nullptr), - mOffsetToKeyProp(0) +StructuredCloneFile::StructuredCloneFile() { + MOZ_COUNT_CTOR(StructuredCloneFile); } inline -StructuredCloneWriteInfo::StructuredCloneWriteInfo( - StructuredCloneWriteInfo&& aCloneWriteInfo) -: mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer)) -, mTransaction(aCloneWriteInfo.mTransaction) -, mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp) +StructuredCloneFile::~StructuredCloneFile() { - mFiles.SwapElements(aCloneWriteInfo.mFiles); - aCloneWriteInfo.mTransaction = nullptr; - aCloneWriteInfo.mOffsetToKeyProp = 0; + MOZ_COUNT_DTOR(StructuredCloneFile); } inline bool -StructuredCloneWriteInfo::SetFromSerialized( - const SerializedStructuredCloneWriteInfo& aOther) +StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const { - if (!aOther.dataLength) { - mCloneBuffer.clear(); - } - else if (!mCloneBuffer.copy(aOther.data, aOther.dataLength)) { - return false; - } - - mFiles.Clear(); - mOffsetToKeyProp = aOther.offsetToKeyProp; - return true; + return this->mFile == aOther.mFile && + this->mFileInfo == aOther.mFileInfo; } inline StructuredCloneReadInfo::StructuredCloneReadInfo() -: mDatabase(nullptr) + : mDatabase(nullptr) { + MOZ_COUNT_CTOR(StructuredCloneReadInfo); +} + +inline +StructuredCloneReadInfo::StructuredCloneReadInfo( + SerializedStructuredCloneReadInfo&& aCloneReadInfo) + : mData(Move(aCloneReadInfo.data())) + , mDatabase(nullptr) +{ + MOZ_COUNT_CTOR(StructuredCloneReadInfo); +} + +inline +StructuredCloneReadInfo::~StructuredCloneReadInfo() +{ + MOZ_COUNT_DTOR(StructuredCloneReadInfo); } inline StructuredCloneReadInfo& @@ -60,6 +67,7 @@ StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo) { MOZ_ASSERT(&aCloneReadInfo != this); + mData = Move(aCloneReadInfo.mData); mCloneBuffer = Move(aCloneReadInfo.mCloneBuffer); mFiles.Clear(); mFiles.SwapElements(aCloneReadInfo.mFiles); @@ -68,45 +76,8 @@ StructuredCloneReadInfo::operator=(StructuredCloneReadInfo&& aCloneReadInfo) return *this; } -inline -bool -StructuredCloneReadInfo::SetFromSerialized( - const SerializedStructuredCloneReadInfo& aOther) -{ - if (aOther.dataLength && - !mCloneBuffer.copy(aOther.data, aOther.dataLength)) { - return false; - } +} // namespace indexedDB +} // namespace dom +} // namespace mozilla - mFiles.Clear(); - return true; -} - -inline -void -AppendConditionClause(const nsACString& aColumnName, - const nsACString& aArgName, - bool aLessThan, - bool aEquals, - nsACString& aResult) -{ - aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName + - NS_LITERAL_CSTRING(" "); - - if (aLessThan) { - aResult.Append('<'); - } - else { - aResult.Append('>'); - } - - if (aEquals) { - aResult.Append('='); - } - - aResult += NS_LITERAL_CSTRING(" :") + aArgName; -} - -END_INDEXEDDB_NAMESPACE - -#endif +#endif // IndexedDatabaseInlines_h diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 3f4010d55767..fab61353d767 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -16,12 +16,16 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/CondVar.h" #include "mozilla/ContentEvents.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ErrorEventBinding.h" +#include "mozilla/dom/PBlobChild.h" #include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/Utilities.h" #include "mozilla/dom/TabContext.h" #include "mozilla/EventDispatcher.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "mozilla/storage.h" @@ -53,12 +57,11 @@ #define LOW_DISK_SPACE_DATA_FULL "full" #define LOW_DISK_SPACE_DATA_FREE "free" -USING_INDEXEDDB_NAMESPACE -using namespace mozilla; -using namespace mozilla::dom; -USING_QUOTA_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { -BEGIN_INDEXEDDB_NAMESPACE +using namespace mozilla::dom::quota; class FileManagerInfo { @@ -103,14 +106,15 @@ private: nsTArray > mTemporaryStorageFileManagers; }; -END_INDEXEDDB_NAMESPACE - namespace { +const char kTestingPref[] = "dom.indexedDB.testing"; + mozilla::StaticRefPtr gDBManager; mozilla::Atomic gInitialized(false); mozilla::Atomic gClosed(false); +mozilla::Atomic gTestingMode(false); class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable { @@ -183,6 +187,16 @@ struct MOZ_STACK_CLASS InvalidateInfo const nsACString& pattern; }; +void +TestingPrefChangedCallback(const char* aPrefName, void* aClosure) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!strcmp(aPrefName, kTestingPref)); + MOZ_ASSERT(!aClosure); + + gTestingMode = Preferences::GetBool(aPrefName); +} + } // anonymous namespace IndexedDatabaseManager::IndexedDatabaseManager() @@ -281,6 +295,9 @@ IndexedDatabaseManager::Init() NS_ENSURE_SUCCESS(rv, rv); } + Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback, + kTestingPref); + return NS_OK; } @@ -293,6 +310,8 @@ IndexedDatabaseManager::Destroy() NS_ERROR("Shutdown more than once?!"); } + Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref); + delete this; } @@ -314,7 +333,7 @@ IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner, nsresult rv = aVisitor.mDOMEvent->GetType(type); NS_ENSURE_SUCCESS(rv, rv); - if (!type.EqualsLiteral(ERROR_EVT_STR)) { + if (nsDependentString(kErrorEventType) != type) { return NS_OK; } @@ -425,8 +444,9 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, } nsRefPtr factory; - if (NS_FAILED(IDBFactory::Create(aCx, aGlobal, nullptr, - getter_AddRefs(factory)))) { + if (NS_FAILED(IDBFactory::CreateForChromeJS(aCx, + aGlobal, + getter_AddRefs(factory)))) { return false; } @@ -471,6 +491,16 @@ IndexedDatabaseManager::InLowDiskSpaceMode() } #endif +// static +bool +IndexedDatabaseManager::InTestingMode() +{ + MOZ_ASSERT(gDBManager, + "InTestingMode() called before indexedDB has been initialized!"); + + return gTestingMode; +} + already_AddRefed IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, @@ -626,13 +656,38 @@ IndexedDatabaseManager::BlockAndGetFileReferences( int32_t* aSliceRefCnt, bool* aResult) { - nsRefPtr helper = - new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, - aFileId); + if (NS_WARN_IF(!InTestingMode())) { + return NS_ERROR_UNEXPECTED; + } - nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, - aSliceRefCnt, aResult); - NS_ENSURE_SUCCESS(rv, rv); + if (IsMainProcess()) { + nsRefPtr helper = + new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName, + aFileId); + + nsresult rv = + helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt, + aSliceRefCnt, aResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + ContentChild* contentChild = ContentChild::GetSingleton(); + if (NS_WARN_IF(!contentChild)) { + return NS_ERROR_FAILURE; + } + + if (!contentChild->SendGetFileReferences(aPersistenceType, + nsCString(aOrigin), + nsString(aDatabaseName), + aFileId, + aRefCnt, + aDBRefCnt, + aSliceRefCnt, + aResult)) { + return NS_ERROR_FAILURE; + } + } return NS_OK; } @@ -886,3 +941,7 @@ GetFileReferencesHelper::Run() return NS_OK; } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 27bad25a8749..4ce140274ef2 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -7,8 +7,6 @@ #ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__ #define mozilla_dom_indexeddb_indexeddatabasemanager_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - #include "nsIObserver.h" #include "js/TypeDecls.h" @@ -21,16 +19,20 @@ class nsPIDOMWindow; namespace mozilla { -class EventChainPostVisitor; -namespace dom { -class TabContext; -namespace quota { -class OriginOrPatternString; -} -} -} -BEGIN_INDEXEDDB_NAMESPACE +class EventChainPostVisitor; + +namespace dom { + +class TabContext; + +namespace quota { + +class OriginOrPatternString; + +} // namespace quota + +namespace indexedDB { class FileManager; class FileManagerInfo; @@ -75,6 +77,9 @@ public: } #endif + static bool + InTestingMode(); + already_AddRefed GetFileManager(PersistenceType aPersistenceType, const nsACString& aOrigin, @@ -160,6 +165,8 @@ private: static mozilla::Atomic sLowDiskSpaceMode; }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla -#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */ +#endif // mozilla_dom_indexeddb_indexeddatabasemanager_h__ diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index bc1183f91656..5a847c517791 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -4,19 +4,23 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/FloatingPoint.h" #include "Key.h" -#include "ReportInternalError.h" +#include +#include "js/Value.h" #include "jsfriendapi.h" +#include "mozilla/Endian.h" +#include "mozilla/FloatingPoint.h" +#include "mozIStorageStatement.h" #include "nsAlgorithm.h" #include "nsJSUtils.h" +#include "ReportInternalError.h" #include "xpcpublic.h" -#include "mozilla/Endian.h" -#include -USING_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { /* Here's how we encode keys: @@ -262,6 +266,14 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd, #define TWO_BYTE_ADJUST (-0x7F) #define THREE_BYTE_SHIFT 6 +nsresult +Key::EncodeJSVal(JSContext* aCx, + JS::Handle aVal, + uint8_t aTypeOffset) +{ + return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); +} + void Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset) { @@ -314,6 +326,17 @@ Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset) NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes"); } +// static +nsresult +Key::DecodeJSVal(const unsigned char*& aPos, + const unsigned char* aEnd, + JSContext* aCx, + uint8_t aTypeOffset, + JS::MutableHandle aVal) +{ + return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); +} + // static void Key::DecodeString(const unsigned char*& aPos, const unsigned char* aEnd, @@ -428,3 +451,108 @@ Key::DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd) return pun.d; } + +nsresult +Key::BindToStatement(mozIStorageStatement* aStatement, + const nsACString& aParamName) const +{ + nsresult rv = aStatement->BindBlobByName(aParamName, + reinterpret_cast(mBuffer.get()), mBuffer.Length()); + + return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; +} + +nsresult +Key::SetFromStatement(mozIStorageStatement* aStatement, + uint32_t aIndex) +{ + uint8_t* data; + uint32_t dataLength = 0; + + nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + mBuffer.Adopt( + reinterpret_cast(const_cast(data)), dataLength); + + return NS_OK; +} + +nsresult +Key::SetFromJSVal(JSContext* aCx, + JS::Handle aVal) +{ + mBuffer.Truncate(); + + if (aVal.isNull() || aVal.isUndefined()) { + Unset(); + return NS_OK; + } + + nsresult rv = EncodeJSVal(aCx, aVal, 0); + if (NS_FAILED(rv)) { + Unset(); + return rv; + } + TrimBuffer(); + + return NS_OK; +} + +nsresult +Key::ToJSVal(JSContext* aCx, + JS::MutableHandle aVal) const +{ + if (IsUnset()) { + aVal.setUndefined(); + return NS_OK; + } + + const unsigned char* pos = BufferStart(); + nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(pos >= BufferEnd()); + + return NS_OK; +} + +nsresult +Key::ToJSVal(JSContext* aCx, + JS::Heap& aVal) const +{ + JS::Rooted value(aCx); + nsresult rv = ToJSVal(aCx, &value); + if (NS_SUCCEEDED(rv)) { + aVal = value; + } + return rv; +} + +nsresult +Key::AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle aVal) +{ + nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); + if (NS_FAILED(rv)) { + Unset(); + return rv; + } + + return NS_OK; +} + +#ifdef DEBUG + +void +Key::Assert(bool aCondition) const +{ + MOZ_ASSERT(aCondition); +} + +#endif // DEBUG + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 4ab780c225ed..f29443722c46 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -7,84 +7,91 @@ #ifndef mozilla_dom_indexeddb_key_h__ #define mozilla_dom_indexeddb_key_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "js/RootingAPI.h" +#include "nsString.h" -#include "mozIStorageStatement.h" - -#include "js/Value.h" +class mozIStorageStatement; namespace IPC { -template struct ParamTraits; + +template struct ParamTraits; + } // namespace IPC -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { class Key { friend struct IPC::ParamTraits; + nsCString mBuffer; + public: Key() { Unset(); } - Key& operator=(const nsAString& aString) + Key& + operator=(const nsAString& aString) { SetFromString(aString); return *this; } - Key& operator=(int64_t aInt) + Key& + operator=(int64_t aInt) { SetFromInteger(aInt); return *this; } - bool operator==(const Key& aOther) const + bool + operator==(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return mBuffer.Equals(aOther.mBuffer); } - bool operator!=(const Key& aOther) const + bool + operator!=(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return !mBuffer.Equals(aOther.mBuffer); } - bool operator<(const Key& aOther) const + bool + operator<(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return Compare(mBuffer, aOther.mBuffer) < 0; } - bool operator>(const Key& aOther) const + bool + operator>(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return Compare(mBuffer, aOther.mBuffer) > 0; } - bool operator<=(const Key& aOther) const + bool + operator<=(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return Compare(mBuffer, aOther.mBuffer) <= 0; } - bool operator>=(const Key& aOther) const + bool + operator>=(const Key& aOther) const { - NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), - "Don't compare unset keys!"); + Assert(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid()); return Compare(mBuffer, aOther.mBuffer) >= 0; } @@ -95,169 +102,114 @@ public: mBuffer.SetIsVoid(true); } - bool IsUnset() const + bool + IsUnset() const { return mBuffer.IsVoid(); } - bool IsFloat() const + bool + IsFloat() const { return !IsUnset() && mBuffer.First() == eFloat; } - bool IsDate() const + bool + IsDate() const { return !IsUnset() && mBuffer.First() == eDate; } - bool IsString() const + bool + IsString() const { return !IsUnset() && mBuffer.First() == eString; } - bool IsArray() const + bool + IsArray() const { return !IsUnset() && mBuffer.First() >= eArray; } - double ToFloat() const + double + ToFloat() const { - NS_ASSERTION(IsFloat(), "Why'd you call this?"); + Assert(IsFloat()); const unsigned char* pos = BufferStart(); double res = DecodeNumber(pos, BufferEnd()); - NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); + Assert(pos >= BufferEnd()); return res; } - double ToDateMsec() const + double + ToDateMsec() const { - NS_ASSERTION(IsDate(), "Why'd you call this?"); + Assert(IsDate()); const unsigned char* pos = BufferStart(); double res = DecodeNumber(pos, BufferEnd()); - NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); + Assert(pos >= BufferEnd()); return res; } - void ToString(nsString& aString) const + void + ToString(nsString& aString) const { - NS_ASSERTION(IsString(), "Why'd you call this?"); + Assert(IsString()); const unsigned char* pos = BufferStart(); DecodeString(pos, BufferEnd(), aString); - NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); + Assert(pos >= BufferEnd()); } - void SetFromString(const nsAString& aString) + void + SetFromString(const nsAString& aString) { mBuffer.Truncate(); EncodeString(aString, 0); TrimBuffer(); } - void SetFromInteger(int64_t aInt) + void + SetFromInteger(int64_t aInt) { mBuffer.Truncate(); EncodeNumber(double(aInt), eFloat); TrimBuffer(); } - nsresult SetFromJSVal(JSContext* aCx, - JS::Handle aVal) - { - mBuffer.Truncate(); + nsresult + SetFromJSVal(JSContext* aCx, JS::Handle aVal); - if (aVal.isNull() || aVal.isUndefined()) { - Unset(); - return NS_OK; - } + nsresult + ToJSVal(JSContext* aCx, JS::MutableHandle aVal) const; - nsresult rv = EncodeJSVal(aCx, aVal, 0); - if (NS_FAILED(rv)) { - Unset(); - return rv; - } - TrimBuffer(); + nsresult + ToJSVal(JSContext* aCx, JS::Heap& aVal) const; - return NS_OK; - } + nsresult + AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle aVal); - nsresult ToJSVal(JSContext* aCx, - JS::MutableHandle aVal) const - { - if (IsUnset()) { - aVal.setUndefined(); - return NS_OK; - } - - const unsigned char* pos = BufferStart(); - nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(pos >= BufferEnd(), - "Didn't consume whole buffer"); - - return NS_OK; - } - - nsresult ToJSVal(JSContext* aCx, - JS::Heap& aVal) const - { - JS::Rooted value(aCx); - nsresult rv = ToJSVal(aCx, &value); - if (NS_SUCCEEDED(rv)) { - aVal = value; - } - return rv; - } - - nsresult AppendItem(JSContext* aCx, - bool aFirstOfArray, - JS::Handle aVal) - { - nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); - if (NS_FAILED(rv)) { - Unset(); - return rv; - } - - return NS_OK; - } - - void FinishArray() + void + FinishArray() { TrimBuffer(); } - const nsCString& GetBuffer() const + const nsCString& + GetBuffer() const { return mBuffer; } - nsresult BindToStatement(mozIStorageStatement* aStatement, - const nsACString& aParamName) const - { - nsresult rv = aStatement->BindBlobByName(aParamName, - reinterpret_cast(mBuffer.get()), mBuffer.Length()); + nsresult + BindToStatement(mozIStorageStatement* aStatement, + const nsACString& aParamName) const; - return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + nsresult + SetFromStatement(mozIStorageStatement* aStatement, uint32_t aIndex); - nsresult SetFromStatement(mozIStorageStatement* aStatement, - uint32_t aIndex) - { - uint8_t* data; - uint32_t dataLength = 0; - - nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mBuffer.Adopt( - reinterpret_cast(const_cast(data)), dataLength); - - return NS_OK; - } - - static - int16_t CompareKeys(Key& aFirst, Key& aSecond) + static int16_t + CompareKeys(Key& aFirst, Key& aSecond) { int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer); @@ -273,12 +225,14 @@ public: } private: - const unsigned char* BufferStart() const + const unsigned char* + BufferStart() const { return reinterpret_cast(mBuffer.BeginReading()); } - const unsigned char* BufferEnd() const + const unsigned char* + BufferEnd() const { return reinterpret_cast(mBuffer.EndReading()); } @@ -294,7 +248,8 @@ private: // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing // step. - void TrimBuffer() + void + TrimBuffer() { const char* end = mBuffer.EndReading() - 1; while (!*end) { @@ -305,41 +260,57 @@ private: } // Encoding functions. These append the encoded value to the end of mBuffer - inline nsresult EncodeJSVal(JSContext* aCx, JS::Handle aVal, - uint8_t aTypeOffset) - { - return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); - } - void EncodeString(const nsAString& aString, uint8_t aTypeOffset); - void EncodeNumber(double aFloat, uint8_t aType); + nsresult + EncodeJSVal(JSContext* aCx, JS::Handle aVal, uint8_t aTypeOffset); + + void + EncodeString(const nsAString& aString, uint8_t aTypeOffset); + + void + EncodeNumber(double aFloat, uint8_t aType); // Decoding functions. aPos points into mBuffer and is adjusted to point // past the consumed value. - static inline nsresult DecodeJSVal(const unsigned char*& aPos, - const unsigned char* aEnd, JSContext* aCx, - uint8_t aTypeOffset, JS::MutableHandle aVal) - { - return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); - } + static nsresult + DecodeJSVal(const unsigned char*& aPos, + const unsigned char* aEnd, + JSContext* aCx, + uint8_t aTypeOffset, + JS::MutableHandle aVal); - static void DecodeString(const unsigned char*& aPos, - const unsigned char* aEnd, - nsString& aString); - static double DecodeNumber(const unsigned char*& aPos, - const unsigned char* aEnd); + static void + DecodeString(const unsigned char*& aPos, + const unsigned char* aEnd, + nsString& aString); - nsCString mBuffer; + static double + DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd); -private: - nsresult EncodeJSValInternal(JSContext* aCx, JS::Handle aVal, - uint8_t aTypeOffset, uint16_t aRecursionDepth); + nsresult + EncodeJSValInternal(JSContext* aCx, + JS::Handle aVal, + uint8_t aTypeOffset, + uint16_t aRecursionDepth); - static nsresult DecodeJSValInternal(const unsigned char*& aPos, - const unsigned char* aEnd, - JSContext* aCx, uint8_t aTypeOffset, - JS::MutableHandle aVal, uint16_t aRecursionDepth); + static nsresult + DecodeJSValInternal(const unsigned char*& aPos, + const unsigned char* aEnd, + JSContext* aCx, + uint8_t aTypeOffset, + JS::MutableHandle aVal, + uint16_t aRecursionDepth); + + void + Assert(bool aCondition) const +#ifdef DEBUG + ; +#else + { } +#endif }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla -#endif /* mozilla_dom_indexeddb_key_h__ */ +#endif // mozilla_dom_indexeddb_key_h__ diff --git a/dom/indexedDB/KeyPath.cpp b/dom/indexedDB/KeyPath.cpp index e3ad5b3d2891..9f440746372c 100644 --- a/dom/indexedDB/KeyPath.cpp +++ b/dom/indexedDB/KeyPath.cpp @@ -15,7 +15,9 @@ #include "mozilla/dom/BindingDeclarations.h" -USING_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { namespace { @@ -178,8 +180,9 @@ GetJSValFromKeyPathString(JSContext* aCx, obj = dummy; } else { - JS::Rooted dummy(aCx, JS_NewObject(aCx, &IDBObjectStore::sDummyPropJSClass, - JS::NullPtr(), JS::NullPtr())); + JS::Rooted dummy(aCx, + JS_NewObject(aCx, IDBObjectStore::DummyPropClass(), JS::NullPtr(), + JS::NullPtr())); if (!dummy) { IDB_REPORT_INTERNAL_ERR(); rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -569,3 +572,7 @@ KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const // Everything else is ok. return true; } + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/KeyPath.h b/dom/indexedDB/KeyPath.h index bee0c2cad0e8..abcd22c0022e 100644 --- a/dom/indexedDB/KeyPath.h +++ b/dom/indexedDB/KeyPath.h @@ -7,16 +7,28 @@ #ifndef mozilla_dom_indexeddb_keypath_h__ #define mozilla_dom_indexeddb_keypath_h__ -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - #include "mozilla/dom/BindingDeclarations.h" -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { +class IndexMetadata; class Key; +class ObjectStoreMetadata; class KeyPath { + // This private constructor is only to be used by IPDL-generated classes. + friend class IndexMetadata; + friend class ObjectStoreMetadata; + + KeyPath() + : mType(NONEXISTENT) + { + MOZ_COUNT_CTOR(KeyPath); + } + public: enum KeyPathType { NONEXISTENT, @@ -105,6 +117,8 @@ public: nsTArray mStrings; }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla -#endif /* mozilla_dom_indexeddb_keypath_h__ */ +#endif // mozilla_dom_indexeddb_keypath_h__ diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp deleted file mode 100644 index 1f3cde303a33..000000000000 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ /dev/null @@ -1,2851 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/DebugOnly.h" - -#include "OpenDatabaseHelper.h" - -#include "nsIBFCacheEntry.h" -#include "nsIFile.h" - -#include -#include "mozilla/dom/quota/AcquireListener.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/storage.h" -#include "nsEscape.h" -#include "nsNetUtil.h" -#include "nsThreadUtils.h" -#include "snappy/snappy.h" - -#include "Client.h" -#include "IDBEvents.h" -#include "IDBFactory.h" -#include "IndexedDatabaseManager.h" -#include "ProfilerHelpers.h" -#include "ReportInternalError.h" - -using namespace mozilla; -using namespace mozilla::dom; -USING_INDEXEDDB_NAMESPACE -USING_QUOTA_NAMESPACE - -namespace { - -// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major -// schema version. -static_assert(JS_STRUCTURED_CLONE_VERSION == 4, - "Need to update the major schema version."); - -// Major schema version. Bump for almost everything. -const uint32_t kMajorSchemaVersion = 16; - -// Minor schema version. Should almost always be 0 (maybe bump on release -// branches if we have to). -const uint32_t kMinorSchemaVersion = 0; - -// The schema version we store in the SQLite database is a (signed) 32-bit -// integer. The major version is left-shifted 4 bits so the max value is -// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF. -static_assert(kMajorSchemaVersion <= 0xFFFFFFF, - "Major version needs to fit in 28 bits."); -static_assert(kMinorSchemaVersion <= 0xF, - "Minor version needs to fit in 4 bits."); - -inline -int32_t -MakeSchemaVersion(uint32_t aMajorSchemaVersion, - uint32_t aMinorSchemaVersion) -{ - return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion); -} - -const int32_t kSQLiteSchemaVersion = int32_t((kMajorSchemaVersion << 4) + - kMinorSchemaVersion); - -const uint32_t kGoldenRatioU32 = 0x9E3779B9U; - -inline -uint32_t -RotateBitsLeft32(uint32_t value, uint8_t bits) -{ - MOZ_ASSERT(bits < 32); - return (value << bits) | (value >> (32 - bits)); -} - -inline -uint32_t -HashName(const nsAString& aName) -{ - const char16_t* str = aName.BeginReading(); - size_t length = aName.Length(); - - uint32_t hash = 0; - for (size_t i = 0; i < length; i++) { - hash = kGoldenRatioU32 * (RotateBitsLeft32(hash, 5) ^ str[i]); - } - - return hash; -} - -nsresult -GetDatabaseFilename(const nsAString& aName, - nsAString& aDatabaseFilename) -{ - aDatabaseFilename.AppendInt(HashName(aName)); - - nsCString escapedName; - if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) { - NS_WARNING("Can't escape database name!"); - return NS_ERROR_UNEXPECTED; - } - - const char* forwardIter = escapedName.BeginReading(); - const char* backwardIter = escapedName.EndReading() - 1; - - nsCString substring; - while (forwardIter <= backwardIter && substring.Length() < 21) { - if (substring.Length() % 2) { - substring.Append(*backwardIter--); - } - else { - substring.Append(*forwardIter++); - } - } - - aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring)); - - return NS_OK; -} - -nsresult -CreateFileTables(mozIStorageConnection* aDBConn) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "CreateFileTables", - js::ProfileEntry::Category::STORAGE); - - // Table `file` - nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE file (" - "id INTEGER PRIMARY KEY, " - "refcount INTEGER NOT NULL" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_update_trigger " - "AFTER UPDATE OF file_ids ON object_data " - "FOR EACH ROW " - "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_delete_trigger " - "AFTER DELETE ON object_data " - "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NULL); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER file_update_trigger " - "AFTER UPDATE ON file " - "FOR EACH ROW WHEN NEW.refcount = 0 " - "BEGIN " - "DELETE FROM file WHERE id = OLD.id; " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -CreateTables(mozIStorageConnection* aDBConn) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aDBConn, "Passing a null database connection!"); - - PROFILER_LABEL("OpenDatabaseHelper", "CreateTables", - js::ProfileEntry::Category::STORAGE); - - // Table `database` - nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE database (" - "name TEXT NOT NULL, " - "version INTEGER NOT NULL DEFAULT 0" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `object_store` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store (" - "id INTEGER PRIMARY KEY, " - "auto_increment INTEGER NOT NULL DEFAULT 0, " - "name TEXT NOT NULL, " - "key_path TEXT, " - "UNIQUE (name)" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `object_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value BLOB DEFAULT NULL, " - "file_ids TEXT, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `index` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Need this to make cascading deletes from object_data and object_store fast. - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Table `unique_index_data` - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Need this to make cascading deletes from object_data and object_store fast. - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CreateFileTables(aDBConn); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom4To5", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - - // All we changed is the type of the version column, so lets try to - // convert that to an integer, and if we fail, set it to 0. - nsCOMPtr stmt; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name, version, dataVersion " - "FROM database" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - nsString name; - int32_t intVersion; - int64_t dataVersion; - - { - mozStorageStatementScoper scoper(stmt); - - bool hasResults; - rv = stmt->ExecuteStep(&hasResults); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE); - - nsString version; - rv = stmt->GetString(1, version); - NS_ENSURE_SUCCESS(rv, rv); - - intVersion = version.ToInteger(&rv); - if (NS_FAILED(rv)) { - intVersion = 0; - } - - rv = stmt->GetString(0, name); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->GetInt64(2, &dataVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE database" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE database (" - "name TEXT NOT NULL, " - "version INTEGER NOT NULL DEFAULT 0, " - "dataVersion INTEGER NOT NULL" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO database (name, version, dataVersion) " - "VALUES (:name, :version, :dataVersion)" - ), getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - { - mozStorageStatementScoper scoper(stmt); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), name); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), intVersion); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"), dataVersion); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = aConnection->SetSchemaVersion(5); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom5To6", - js::ProfileEntry::Category::STORAGE); - - // First, drop all the indexes we're no longer going to use. - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX key_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX ai_key_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX value_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP INDEX ai_value_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Now, reorder the columns of object_data to put the blob data last. We do - // this by copying into a temporary table, dropping the original, then copying - // back into a newly created table. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "key_value, " - "data " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, key_value, data " - "FROM object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value DEFAULT NULL, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data " - "SELECT id, object_store_id, key_value, data " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // We need to add a unique constraint to our ai_object_data table. Copy all - // the data out of it using a temporary table as before. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "data " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, data " - "FROM ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_object_data (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "object_store_id INTEGER NOT NULL, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, id), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO ai_object_data " - "SELECT id, object_store_id, data " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Fix up the index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "object_data_key NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT OR IGNORE INTO index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Fix up the unique_index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "object_data_key NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Fix up the ai_index_data table. We're reordering the columns as well as - // changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "ai_object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, ai_object_data_id " - "FROM ai_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, ai_object_data_id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT OR IGNORE INTO ai_index_data " - "SELECT index_id, value, ai_object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX ai_index_data_ai_object_data_id_index " - "ON ai_index_data (ai_object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - // Fix up the ai_unique_index_data table. We're reordering the columns as well - // as changing the primary key from being a simple id to being a composite. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "ai_object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, value, ai_object_data_id " - "FROM ai_unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE ai_unique_index_data (" - "index_id INTEGER NOT NULL, " - "value NOT NULL, " - "ai_object_data_id INTEGER NOT NULL, " - "UNIQUE (index_id, value), " - "PRIMARY KEY (index_id, value, ai_object_data_id), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO ai_unique_index_data " - "SELECT index_id, value, ai_object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX ai_unique_index_data_ai_object_data_id_index " - "ON ai_unique_index_data (ai_object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(6); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom6To7", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "name, " - "key_path, " - "auto_increment" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, name, key_path, auto_increment " - "FROM object_store;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store (" - "id INTEGER PRIMARY KEY, " - "auto_increment INTEGER NOT NULL DEFAULT 0, " - "name TEXT NOT NULL, " - "key_path TEXT, " - "UNIQUE (name)" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store " - "SELECT id, auto_increment, name, nullif(key_path, '') " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(7); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom7To8", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "object_store_id, " - "name, " - "key_path, " - "unique_index, " - "object_store_autoincrement" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, name, key_path, " - "unique_index, object_store_autoincrement " - "FROM object_store_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "object_store_autoincrement INTERGER NOT NULL, " - "PRIMARY KEY (id), " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store_index " - "SELECT id, object_store_id, name, key_path, " - "unique_index, 0, object_store_autoincrement " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(8); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -class CompressDataBlobsFunction MOZ_FINAL : public mozIStorageFunction -{ - ~CompressDataBlobsFunction() {} - -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD - OnFunctionCall(mozIStorageValueArray* aArguments, - nsIVariant** aResult) - { - PROFILER_LABEL("CompressDataBlobsFunction", "OnFunctionCall", - js::ProfileEntry::Category::STORAGE); - - uint32_t argc; - nsresult rv = aArguments->GetNumEntries(&argc); - NS_ENSURE_SUCCESS(rv, rv); - - if (argc != 1) { - NS_WARNING("Don't call me with the wrong number of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - int32_t type; - rv = aArguments->GetTypeOfIndex(0, &type); - NS_ENSURE_SUCCESS(rv, rv); - - if (type != mozIStorageStatement::VALUE_TYPE_BLOB) { - NS_WARNING("Don't call me with the wrong type of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - const uint8_t* uncompressed; - uint32_t uncompressedLength; - rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed); - NS_ENSURE_SUCCESS(rv, rv); - - size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength); - char* compressed = (char*)moz_malloc(compressedLength); - NS_ENSURE_TRUE(compressed, NS_ERROR_OUT_OF_MEMORY); - - snappy::RawCompress(reinterpret_cast(uncompressed), - uncompressedLength, compressed, &compressedLength); - - std::pair data((uint8_t*)compressed, - int(compressedLength)); - // The variant takes ownership of | compressed |. - nsCOMPtr result = new mozilla::storage::AdoptedBlobVariant(data); - - result.forget(aResult); - return NS_OK; - } -}; - -NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction) - -nsresult -UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom8To9_0", - js::ProfileEntry::Category::STORAGE); - - // We no longer use the dataVersion column. - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE database SET dataVersion = 0;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr compressor = new CompressDataBlobsFunction(); - - NS_NAMED_LITERAL_CSTRING(compressorName, "compress"); - - rv = aConnection->CreateFunction(compressorName, 1, compressor); - NS_ENSURE_SUCCESS(rv, rv); - - // Turn off foreign key constraints before we do anything here. - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE object_data SET data = compress(data);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE ai_object_data SET data = compress(data);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->RemoveFunction(compressorName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom9_0To10_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE object_data ADD COLUMN file_ids TEXT;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CreateFileTables(aConnection); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom10_0To11_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id, " - "object_store_id, " - "name, " - "key_path, " - "unique_index, " - "multientry" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, name, key_path, " - "unique_index, multientry " - "FROM object_store_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_store_index;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_store_index (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "key_path TEXT NOT NULL, " - "unique_index INTEGER NOT NULL, " - "multientry INTEGER NOT NULL, " - "UNIQUE (object_store_id, name), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_store_index " - "SELECT id, object_store_id, name, key_path, " - "unique_index, multientry " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TRIGGER object_data_insert_trigger;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data (object_store_id, key_value, data, file_ids) " - "SELECT object_store_id, id, data, file_ids " - "FROM ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id " - "FROM ai_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_index_data.ai_object_data_id;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) " - "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id " - "FROM ai_unique_index_data " - "INNER JOIN object_store_index ON " - "object_store_index.id = ai_unique_index_data.index_id " - "INNER JOIN object_data ON " - "object_data.object_store_id = object_store_index.object_store_id AND " - "object_data.key_value = ai_unique_index_data.ai_object_data_id;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "UPDATE object_store " - "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 " - "WHERE auto_increment;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE ai_object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -class EncodeKeysFunction MOZ_FINAL : public mozIStorageFunction -{ - ~EncodeKeysFunction() {} - -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD - OnFunctionCall(mozIStorageValueArray* aArguments, - nsIVariant** aResult) - { - PROFILER_LABEL("EncodeKeysFunction", "OnFunctionCall", - js::ProfileEntry::Category::STORAGE); - - uint32_t argc; - nsresult rv = aArguments->GetNumEntries(&argc); - NS_ENSURE_SUCCESS(rv, rv); - - if (argc != 1) { - NS_WARNING("Don't call me with the wrong number of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - int32_t type; - rv = aArguments->GetTypeOfIndex(0, &type); - NS_ENSURE_SUCCESS(rv, rv); - - Key key; - if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) { - int64_t intKey; - aArguments->GetInt64(0, &intKey); - key.SetFromInteger(intKey); - } - else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) { - nsString stringKey; - aArguments->GetString(0, stringKey); - key.SetFromString(stringKey); - } - else { - NS_WARNING("Don't call me with the wrong type of arguments!"); - return NS_ERROR_UNEXPECTED; - } - - const nsCString& buffer = key.GetBuffer(); - - std::pair data(static_cast(buffer.get()), - int(buffer.Length())); - - nsCOMPtr result = new mozilla::storage::BlobVariant(data); - - result.forget(aResult); - return NS_OK; - } -}; - -NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction) - -nsresult -UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom11_0To12_0", - js::ProfileEntry::Category::STORAGE); - - NS_NAMED_LITERAL_CSTRING(encoderName, "encode"); - - nsCOMPtr encoder = new EncodeKeysFunction(); - - nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "id INTEGER PRIMARY KEY, " - "object_store_id, " - "key_value, " - "data, " - "file_ids " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT id, object_store_id, encode(key_value), data, file_ids " - "FROM object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE object_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE object_data (" - "id INTEGER PRIMARY KEY, " - "object_store_id INTEGER NOT NULL, " - "key_value BLOB DEFAULT NULL, " - "file_ids TEXT, " - "data BLOB NOT NULL, " - "UNIQUE (object_store_id, key_value), " - "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO object_data " - "SELECT id, object_store_id, key_value, file_ids, data " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_insert_trigger " - "AFTER INSERT ON object_data " - "FOR EACH ROW " - "WHEN NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(NULL, NEW.file_ids); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_update_trigger " - "AFTER UPDATE OF file_ids ON object_data " - "FOR EACH ROW " - "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NEW.file_ids); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TRIGGER object_data_delete_trigger " - "AFTER DELETE ON object_data " - "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL " - "BEGIN " - "SELECT update_refcount(OLD.file_ids, NULL); " - "END;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, encode(value), encode(object_data_key), object_data_id " - "FROM index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE, " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX index_data_object_data_id_index " - "ON index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TEMPORARY TABLE temp_upgrade (" - "index_id, " - "value, " - "object_data_key, " - "object_data_id " - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO temp_upgrade " - "SELECT index_id, encode(value), encode(object_data_key), object_data_id " - "FROM unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE unique_index_data;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE unique_index_data (" - "index_id INTEGER NOT NULL, " - "value BLOB NOT NULL, " - "object_data_key BLOB NOT NULL, " - "object_data_id INTEGER NOT NULL, " - "PRIMARY KEY (index_id, value, object_data_key), " - "UNIQUE (index_id, value), " - "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE " - "CASCADE " - "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE " - "CASCADE" - ");" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO unique_index_data " - "SELECT index_id, value, object_data_key, object_data_id " - "FROM temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE temp_upgrade;" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX unique_index_data_object_data_id_index " - "ON unique_index_data (object_data_id);" - )); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->RemoveFunction(encoderName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection, - bool* aVacuumNeeded) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "UpgradeSchemaFrom12_0To13_0", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - int32_t defaultPageSize; - rv = aConnection->GetDefaultPageSize(&defaultPageSize); - NS_ENSURE_SUCCESS(rv, rv); - - // Enable auto_vacuum mode and update the page size to the platform default. - nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = "); - upgradeQuery.AppendInt(defaultPageSize); - - rv = aConnection->ExecuteSimpleSQL(upgradeQuery); - NS_ENSURE_SUCCESS(rv, rv); - - *aVacuumNeeded = true; -#endif - - rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection) -{ - // The only change between 13 and 14 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection) -{ - // The only change between 14 and 15 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection) -{ - // The only change between 15 and 16 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -class VersionChangeEventsRunnable; - -class SetVersionHelper : public AsyncConnectionHelper, - public IDBTransactionListener, - public AcquireListener -{ - friend class VersionChangeEventsRunnable; - -public: - SetVersionHelper(IDBTransaction* aTransaction, - IDBOpenDBRequest* aRequest, - OpenDatabaseHelper* aHelper, - uint64_t aRequestedVersion, - uint64_t aCurrentVersion) - : AsyncConnectionHelper(aTransaction, aRequest), - mOpenRequest(aRequest), mOpenHelper(aHelper), - mRequestedVersion(aRequestedVersion), - mCurrentVersion(aCurrentVersion) - { - mTransaction->SetTransactionListener(this); - } - - NS_DECL_ISUPPORTS_INHERITED - - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual nsresult - OnExclusiveAccessAcquired() MOZ_OVERRIDE; - -protected: - virtual ~SetVersionHelper() {} - - virtual nsresult Init() MOZ_OVERRIDE; - - virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection) - MOZ_OVERRIDE; - - // SetVersionHelper never fires an error event at the request. It hands that - // responsibility back to the OpenDatabaseHelper - virtual void OnError() MOZ_OVERRIDE - { } - - // Need an upgradeneeded event here. - virtual already_AddRefed CreateSuccessEvent( - mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE; - - virtual nsresult NotifyTransactionPreComplete(IDBTransaction* aTransaction) - MOZ_OVERRIDE; - virtual nsresult NotifyTransactionPostComplete(IDBTransaction* aTransaction) - MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE - { - return Success_NotSent; - } - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE - { - MOZ_CRASH("Should never get here!"); - } - - uint64_t RequestedVersion() const - { - return mRequestedVersion; - } - -private: - // In-params - nsRefPtr mOpenRequest; - nsRefPtr mOpenHelper; - uint64_t mRequestedVersion; - uint64_t mCurrentVersion; -}; - -class DeleteDatabaseHelper : public AsyncConnectionHelper, - public AcquireListener -{ - friend class VersionChangeEventsRunnable; -public: - DeleteDatabaseHelper(IDBOpenDBRequest* aRequest, - OpenDatabaseHelper* aHelper, - uint64_t aCurrentVersion, - const nsAString& aName, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - PersistenceType aPersistenceType) - : AsyncConnectionHelper(static_cast(nullptr), aRequest), - mOpenHelper(aHelper), mOpenRequest(aRequest), - mCurrentVersion(aCurrentVersion), mName(aName), - mGroup(aGroup), mASCIIOrigin(aASCIIOrigin), - mPersistenceType(aPersistenceType) - { } - - NS_DECL_ISUPPORTS_INHERITED - - nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal); - - void ReleaseMainThreadObjects() - { - mOpenHelper = nullptr; - mOpenRequest = nullptr; - - AsyncConnectionHelper::ReleaseMainThreadObjects(); - } - - virtual nsresult - OnExclusiveAccessAcquired() MOZ_OVERRIDE; - -protected: - virtual ~DeleteDatabaseHelper() {} - - nsresult DoDatabaseWork(mozIStorageConnection* aConnection); - nsresult Init(); - - // DeleteDatabaseHelper never fires events at the request. It hands that - // responsibility back to the OpenDatabaseHelper - void OnError() - { - mOpenHelper->NotifyDeleteFinished(); - } - - nsresult OnSuccess() - { - return mOpenHelper->NotifyDeleteFinished(); - } - - uint64_t RequestedVersion() const - { - return 0; - } - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE - { - return Success_NotSent; - } - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE - { - MOZ_CRASH("Should never get here!"); - } - -private: - // In-params - nsRefPtr mOpenHelper; - nsRefPtr mOpenRequest; - uint64_t mCurrentVersion; - nsString mName; - nsCString mGroup; - nsCString mASCIIOrigin; - PersistenceType mPersistenceType; -}; - -// Responsible for firing "versionchange" events at all live and non-closed -// databases, and for firing a "blocked" event at the requesting database if any -// databases fail to close. -class VersionChangeEventsRunnable : public nsRunnable -{ -public: - VersionChangeEventsRunnable( - IDBDatabase* aRequestingDatabase, - IDBOpenDBRequest* aRequest, - nsTArray >& aWaitingDatabases, - int64_t aOldVersion, - int64_t aNewVersion) - : mRequestingDatabase(aRequestingDatabase), - mRequest(aRequest), - mOldVersion(aOldVersion), - mNewVersion(aNewVersion) - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aRequestingDatabase, "Null pointer!"); - NS_ASSERTION(aRequest, "Null pointer!"); - - mWaitingDatabases.SwapElements(aWaitingDatabases); - } - - NS_IMETHOD Run() - { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "VersionChangeEventsRunnable::Run", - js::ProfileEntry::Category::STORAGE); - - // Fire version change events at all of the databases that are not already - // closed. Also kick bfcached documents out of bfcache. - uint32_t count = mWaitingDatabases.Length(); - for (uint32_t index = 0; index < count; index++) { - IDBDatabase* database = - IDBDatabase::FromStorage(mWaitingDatabases[index]); - NS_ASSERTION(database, "This shouldn't be null!"); - - if (database->IsClosed()) { - continue; - } - - // First check if the document the IDBDatabase is part of is bfcached. - nsCOMPtr ownerDoc = database->GetOwnerDocument(); - nsIBFCacheEntry* bfCacheEntry; - if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) { - bfCacheEntry->RemoveFromBFCacheSync(); - NS_ASSERTION(database->IsClosed(), - "Kicking doc out of bfcache should have closed database"); - continue; - } - - // Next check if it's in the process of being bfcached. - nsPIDOMWindow* owner = database->GetOwner(); - if (owner && owner->IsFrozen()) { - // We can't kick the document out of the bfcache because it's not yet - // fully in the bfcache. Instead we'll abort everything for the window - // and mark it as not-bfcacheable. - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "Huh?"); - quotaManager->AbortCloseStoragesForWindow(owner); - - NS_ASSERTION(database->IsClosed(), - "AbortCloseStoragesForWindow should have closed database"); - if (ownerDoc) { - ownerDoc->DisallowBFCaching(); - } - continue; - } - - // Otherwise fire a versionchange event. - nsRefPtr event = - IDBVersionChangeEvent::Create(database, mOldVersion, mNewVersion); - NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); - - bool dummy; - database->DispatchEvent(event, &dummy); - } - - // Now check to see if any didn't close. If there are some running still - // then fire the blocked event. - for (uint32_t index = 0; index < count; index++) { - if (!mWaitingDatabases[index]->IsClosed()) { - nsRefPtr event = - IDBVersionChangeEvent::CreateBlocked(mRequest, - mOldVersion, mNewVersion); - NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); - - bool dummy; - mRequest->DispatchEvent(event, &dummy); - - break; - } - } - - return NS_OK; - } - - template - static - void QueueVersionChange(nsTArray >& aDatabases, - void* aClosure); -private: - nsRefPtr mRequestingDatabase; - nsRefPtr mRequest; - nsTArray > mWaitingDatabases; - int64_t mOldVersion; - int64_t mNewVersion; -}; - -} // anonymous namespace - -NS_IMPL_ISUPPORTS(OpenDatabaseHelper, nsIRunnable) - -nsresult -OpenDatabaseHelper::Init() -{ - QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, Client::IDB, - mName, mDatabaseId); - MOZ_ASSERT(!mDatabaseId.IsEmpty()); - - return NS_OK; -} - -nsresult -OpenDatabaseHelper::WaitForOpenAllowed() -{ - NS_ASSERTION(mState == eCreated, "We've already been dispatched?"); - NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!"); - - mState = eOpenPending; - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - return quotaManager-> - WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin), - Nullable(mPersistenceType), mDatabaseId, - this); -} - -nsresult -OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget) -{ - NS_ASSERTION(mState == eCreated || mState == eOpenPending, - "We've already been dispatched?"); - - mState = eDBWork; - - return aTarget->Dispatch(this, NS_DISPATCH_NORMAL); -} - -nsresult -OpenDatabaseHelper::DispatchToIOThread() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - return Dispatch(quotaManager->IOThread()); -} - -nsresult -OpenDatabaseHelper::RunImmediately() -{ - NS_ASSERTION(mState == eCreated || mState == eOpenPending, - "We've already been dispatched?"); - NS_ASSERTION(NS_FAILED(mResultCode), - "Should only be short-circuiting if we failed!"); - NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!"); - - mState = eFiringEvents; - - return this->Run(); -} - -nsresult -OpenDatabaseHelper::DoDatabaseWork() -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - mState = eFiringEvents; // In case we fail somewhere along the line. - - if (QuotaManager::IsShuttingDown()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - NS_ASSERTION(mOpenDBRequest, "This should never be null!"); - - // This will be null for non-window contexts. - nsPIDOMWindow* window = mOpenDBRequest->GetOwner(); - - AutoEnterWindow autoWindow(window); - - nsCOMPtr dbDirectory; - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - nsresult rv = - quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup, - mASCIIOrigin, mTrackingQuota, - getter_AddRefs(dbDirectory)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - bool exists; - rv = dbDirectory->Exists(&exists); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (!exists) { - rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } -#ifdef DEBUG - else { - bool isDirectory; - NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) && - isDirectory, "Should have caught this earlier!"); - } -#endif - - nsAutoString filename; - rv = GetDatabaseFilename(mName, filename); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr dbFile; - rv = dbDirectory->Clone(getter_AddRefs(dbFile)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbFile->GetPath(mDatabaseFilePath); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr fmDirectory; - rv = dbDirectory->Clone(getter_AddRefs(fmDirectory)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = fmDirectory->Append(filename); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr connection; - rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType, - mGroup, mASCIIOrigin, - getter_AddRefs(connection)); - if (NS_FAILED(rv) && - NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) { - IDB_REPORT_INTERNAL_ERR(); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, - &mCurrentVersion, mObjectStores); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (mForDeletion) { - mState = eDeletePending; - return NS_OK; - } - - for (uint32_t i = 0; i < mObjectStores.Length(); i++) { - nsRefPtr& objectStoreInfo = mObjectStores[i]; - for (uint32_t j = 0; j < objectStoreInfo->indexes.Length(); j++) { - IndexInfo& indexInfo = objectStoreInfo->indexes[j]; - mLastIndexId = std::max(indexInfo.id, mLastIndexId); - } - mLastObjectStoreId = std::max(objectStoreInfo->id, mLastObjectStoreId); - } - - // See if we need to do a VERSION_CHANGE transaction - - // Optional version semantics. - if (!mRequestedVersion) { - // If the requested version was not specified and the database was created, - // treat it as if version 1 were requested. - if (mCurrentVersion == 0) { - mRequestedVersion = 1; - } - else { - // Otherwise, treat it as if the current version were requested. - mRequestedVersion = mCurrentVersion; - } - } - - if (mCurrentVersion > mRequestedVersion) { - return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR; - } - - if (mCurrentVersion != mRequestedVersion) { - mState = eSetVersionPending; - } - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - NS_ASSERTION(mgr, "This should never be null!"); - - nsRefPtr fileManager = - mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName); - if (!fileManager) { - fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin, - mPrivilege, mName); - - rv = fileManager->Init(fmDirectory, connection); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - mgr->AddFileManager(fileManager); - } - - mFileManager = fileManager.forget(); - - return NS_OK; -} - -// static -nsresult -OpenDatabaseHelper::CreateDatabaseConnection( - nsIFile* aDBFile, - nsIFile* aFMDirectory, - const nsAString& aName, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - mozIStorageConnection** aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("OpenDatabaseHelper", "CreateDatabaseConnection", - js::ProfileEntry::Category::STORAGE); - - nsresult rv; - bool exists; - - if (IndexedDatabaseManager::InLowDiskSpaceMode()) { - rv = aDBFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (!exists) { - NS_WARNING("Refusing to create database because disk space is low!"); - return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - } - - nsCOMPtr dbFileUrl = - IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin); - NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE); - - nsCOMPtr ss = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); - NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); - - nsCOMPtr connection; - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - if (rv == NS_ERROR_FILE_CORRUPTED) { - // If we're just opening the database during origin initialization, then - // we don't want to erase any files. The failure here will fail origin - // initialization too. - if (aName.IsVoid()) { - return rv; - } - - // Nuke the database file. The web services can recreate their data. - rv = aDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aFMDirectory->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - bool isDirectory; - rv = aFMDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = aFMDirectory->Remove(true); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection)); - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = IDBFactory::SetDefaultPragmas(connection); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem")); - NS_ENSURE_SUCCESS(rv, rv); - - // Check to make sure that the database schema is correct. - int32_t schemaVersion; - rv = connection->GetSchemaVersion(&schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - - // Unknown schema will fail origin initialization too - if (!schemaVersion && aName.IsVoid()) { - IDB_WARNING("Unable to open IndexedDB database, schema is not set!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - if (schemaVersion > kSQLiteSchemaVersion) { - IDB_WARNING("Unable to open IndexedDB database, schema is too high!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - bool vacuumNeeded = false; - - if (schemaVersion != kSQLiteSchemaVersion) { -#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) - if (!schemaVersion) { - // Have to do this before opening a transaction. - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - // Turn on auto_vacuum mode to reclaim disk space on mobile devices. - "PRAGMA auto_vacuum = FULL; " - )); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, - // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - } -#endif - - mozStorageTransaction transaction(connection, false, - mozIStorageConnection::TRANSACTION_IMMEDIATE); - - if (!schemaVersion) { - // Brand new file, initialize our tables. - rv = CreateTables(connection); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) && - schemaVersion == kSQLiteSchemaVersion, - "CreateTables set a bad schema version!"); - - nsCOMPtr stmt; - nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO database (name) " - "VALUES (:name)" - ), getter_AddRefs(stmt)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->Execute(); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - else { - // This logic needs to change next time we change the schema! - static_assert(kSQLiteSchemaVersion == int32_t((16 << 4) + 0), - "Need upgrade code from schema version increase."); - - while (schemaVersion != kSQLiteSchemaVersion) { - if (schemaVersion == 4) { - rv = UpgradeSchemaFrom4To5(connection); - } - else if (schemaVersion == 5) { - rv = UpgradeSchemaFrom5To6(connection); - } - else if (schemaVersion == 6) { - rv = UpgradeSchemaFrom6To7(connection); - } - else if (schemaVersion == 7) { - rv = UpgradeSchemaFrom7To8(connection); - } - else if (schemaVersion == 8) { - rv = UpgradeSchemaFrom8To9_0(connection); - vacuumNeeded = true; - } - else if (schemaVersion == MakeSchemaVersion(9, 0)) { - rv = UpgradeSchemaFrom9_0To10_0(connection); - } - else if (schemaVersion == MakeSchemaVersion(10, 0)) { - rv = UpgradeSchemaFrom10_0To11_0(connection); - } - else if (schemaVersion == MakeSchemaVersion(11, 0)) { - rv = UpgradeSchemaFrom11_0To12_0(connection); - } - else if (schemaVersion == MakeSchemaVersion(12, 0)) { - rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded); - } - else if (schemaVersion == MakeSchemaVersion(13, 0)) { - rv = UpgradeSchemaFrom13_0To14_0(connection); - } - else if (schemaVersion == MakeSchemaVersion(14, 0)) { - rv = UpgradeSchemaFrom14_0To15_0(connection); - } - else if (schemaVersion == MakeSchemaVersion(15, 0)) { - rv = UpgradeSchemaFrom15_0To16_0(connection); - } - else { - NS_WARNING("Unable to open IndexedDB database, no upgrade path is " - "available!"); - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->GetSchemaVersion(&schemaVersion); - NS_ENSURE_SUCCESS(rv, rv); - } - - NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!"); - } - - rv = transaction.Commit(); - if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { - // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE, - // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR. - rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR; - } - NS_ENSURE_SUCCESS(rv, rv); - } - - if (vacuumNeeded) { - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;")); - NS_ENSURE_SUCCESS(rv, rv); - } - - connection.forget(aConnection); - return NS_OK; -} - -nsresult -OpenDatabaseHelper::StartSetVersion() -{ - NS_ASSERTION(mState == eSetVersionPending, "Why are we here?"); - - // In case we fail, fire error events - mState = eFiringEvents; - - nsresult rv = EnsureSuccessResult(); - NS_ENSURE_SUCCESS(rv, rv); - - Sequence storesToOpen; - nsRefPtr transaction = - IDBTransaction::Create(mDatabase, storesToOpen, - IDBTransaction::VERSION_CHANGE, true); - IDB_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsRefPtr helper = - new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion, - mCurrentVersion); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - rv = quotaManager->AcquireExclusiveAccess( - mDatabase, mDatabase->Origin(), - Nullable(mDatabase->Type()), helper, - &VersionChangeEventsRunnable::QueueVersionChange, - helper); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // The SetVersionHelper is responsible for dispatching us back to the - // main thread again and changing the state to eSetVersionCompleted. - mState = eSetVersionPending; - return NS_OK; -} - -nsresult -OpenDatabaseHelper::StartDelete() -{ - NS_ASSERTION(mState == eDeletePending, "Why are we here?"); - - // In case we fail, fire error events - mState = eFiringEvents; - - nsresult rv = EnsureSuccessResult(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr helper = - new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName, - mGroup, mASCIIOrigin, mPersistenceType); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - rv = quotaManager->AcquireExclusiveAccess( - mDatabase, mDatabase->Origin(), - Nullable(mDatabase->Type()), helper, - &VersionChangeEventsRunnable::QueueVersionChange, - helper); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - // The DeleteDatabaseHelper is responsible for dispatching us back to the - // main thread again and changing the state to eDeleteCompleted. - mState = eDeletePending; - return NS_OK; -} - -NS_IMETHODIMP -OpenDatabaseHelper::Run() -{ - NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?"); - - if (NS_IsMainThread()) { - PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - if (mState == eOpenPending) { - if (NS_FAILED(mResultCode)) { - return RunImmediately(); - } - - return DispatchToIOThread(); - } - - // If we need to queue up a SetVersionHelper, do that here. - if (mState == eSetVersionPending) { - nsresult rv = StartSetVersion(); - - if (NS_SUCCEEDED(rv)) { - return rv; - } - - SetError(rv); - // fall through and run the default error processing - } - else if (mState == eDeletePending) { - nsresult rv = StartDelete(); - - if (NS_SUCCEEDED(rv)) { - return rv; - } - - SetError(rv); - // fall through and run the default error processing - } - - // We've done whatever work we need to do on the DB thread, and any - // SetVersion/DeleteDatabase stuff is done by now. - NS_ASSERTION(mState == eFiringEvents || - mState == eSetVersionCompleted || - mState == eDeleteCompleted, "Why are we here?"); - - switch (mState) { - case eSetVersionCompleted: { - mState = eFiringEvents; - break; - } - - case eDeleteCompleted: { - // Destroy the database now (we should have the only ref). - mDatabase = nullptr; - - DatabaseInfo::Remove(mDatabaseId); - - mState = eFiringEvents; - break; - } - - case eFiringEvents: { - // Notify the request that we're done, but only if we didn't just - // finish a [SetVersion/DeleteDatabase]Helper. In that case, the - // helper tells the request that it is done, and we avoid calling - // NotifyHelperCompleted twice. - - nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this); - if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { - mResultCode = rv; - } - break; - } - - default: - NS_NOTREACHED("Shouldn't get here!"); - } - - NS_ASSERTION(mState == eFiringEvents, "Why are we here?"); - - IDB_PROFILER_MARK("IndexedDB Request %llu: Running main thread " - "response (rv = %lu)", - "IDBRequest[%llu] MT Done", - mRequest->GetSerialNumber(), mResultCode); - - if (NS_FAILED(mResultCode)) { - DispatchErrorEvent(); - } else { - DispatchSuccessEvent(); - } - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never be null!"); - - quotaManager-> - AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin), - Nullable(mPersistenceType), - mDatabaseId); - - ReleaseMainThreadObjects(); - - return NS_OK; - } - - PROFILER_LABEL("OpenDatabaseHelper", "Run", - js::ProfileEntry::Category::STORAGE); - - // We're on the DB thread. - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - IDB_PROFILER_MARK("IndexedDB Request %llu: Beginning database work", - "IDBRequest[%llu] DT Start", mRequest->GetSerialNumber()); - - NS_ASSERTION(mState == eDBWork, "Why are we here?"); - mResultCode = DoDatabaseWork(); - NS_ASSERTION(mState != eDBWork, "We should be doing something else now."); - - IDB_PROFILER_MARK("IndexedDB Request %llu: Finished database work (rv = %lu)", - "IDBRequest[%llu] DT Done", mRequest->GetSerialNumber(), - mResultCode); - - return NS_DispatchToMainThread(this); -} - -nsresult -OpenDatabaseHelper::EnsureSuccessResult() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "EnsureSuccessResult", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr dbInfo; - if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) { - -#ifdef DEBUG - { - NS_ASSERTION(dbInfo->name == mName && - dbInfo->version == mCurrentVersion && - dbInfo->persistenceType == mPersistenceType && - dbInfo->id == mDatabaseId && - dbInfo->filePath == mDatabaseFilePath, - "Metadata mismatch!"); - - uint32_t objectStoreCount = mObjectStores.Length(); - for (uint32_t index = 0; index < objectStoreCount; index++) { - nsRefPtr& info = mObjectStores[index]; - - ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name); - NS_ASSERTION(otherInfo, "ObjectStore not known!"); - - NS_ASSERTION(info->name == otherInfo->name && - info->id == otherInfo->id && - info->keyPath == otherInfo->keyPath, - "Metadata mismatch!"); - NS_ASSERTION(dbInfo->ContainsStoreName(info->name), - "Object store names out of date!"); - NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(), - "Bad index length!"); - - uint32_t indexCount = info->indexes.Length(); - for (uint32_t indexIndex = 0; indexIndex < indexCount; indexIndex++) { - const IndexInfo& indexInfo = info->indexes[indexIndex]; - const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex]; - NS_ASSERTION(indexInfo.id == otherIndexInfo.id, - "Bad index id!"); - NS_ASSERTION(indexInfo.name == otherIndexInfo.name, - "Bad index name!"); - NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath, - "Bad index keyPath!"); - NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique, - "Bad index unique value!"); - } - } - } -#endif - - } - else { - nsRefPtr newInfo(new DatabaseInfo()); - - newInfo->name = mName; - newInfo->group = mGroup; - newInfo->origin = mASCIIOrigin; - newInfo->persistenceType = mPersistenceType; - newInfo->id = mDatabaseId; - newInfo->filePath = mDatabaseFilePath; - - if (!DatabaseInfo::Put(newInfo)) { - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - newInfo.swap(dbInfo); - - nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion, - mObjectStores); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!"); - } - - dbInfo->nextObjectStoreId = mLastObjectStoreId + 1; - dbInfo->nextIndexId = mLastIndexId + 1; - - nsRefPtr database = - IDBDatabase::Create(mOpenDBRequest, mOpenDBRequest->Factory(), - dbInfo.forget(), mASCIIOrigin, mFileManager, - mContentParent); - if (!database) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!"); - mDatabase.swap(database); - - return NS_OK; -} - -nsresult -OpenDatabaseHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - // Be careful not to load the database twice. - if (!mDatabase) { - nsresult rv = EnsureSuccessResult(); - NS_ENSURE_SUCCESS(rv, rv); - } - - return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), - aVal); -} - -nsresult -OpenDatabaseHelper::NotifySetVersionFinished() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); - NS_ASSERTION(mState = eSetVersionPending, "How did we get here?"); - - // Allow transaction creation to proceed. - mDatabase->ExitSetVersionTransaction(); - - mState = eSetVersionCompleted; - - // Dispatch ourself back to the main thread - return NS_DispatchToCurrentThread(this); -} - -nsresult -OpenDatabaseHelper::NotifyDeleteFinished() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread"); - NS_ASSERTION(mState == eDeletePending, "How did we get here?"); - - mState = eDeleteCompleted; - - // Dispatch ourself back to the main thread - return NS_DispatchToCurrentThread(this); -} - -void -OpenDatabaseHelper::BlockDatabase() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mDatabase, "This is going bad fast."); - - mDatabase->EnterSetVersionTransaction(); -} - -void -OpenDatabaseHelper::DispatchSuccessEvent() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchSuccessEvent", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr event = - CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(SUCCESS_EVT_STR), - eDoesNotBubble, eNotCancelable); - if (!event) { - NS_ERROR("Failed to create event!"); - return; - } - - bool dummy; - mOpenDBRequest->DispatchEvent(event, &dummy); -} - -void -OpenDatabaseHelper::DispatchErrorEvent() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - PROFILER_MAIN_THREAD_LABEL("OpenDatabaseHelper", "DispatchErrorEvent", - js::ProfileEntry::Category::STORAGE); - - nsRefPtr event = - CreateGenericEvent(mOpenDBRequest, NS_LITERAL_STRING(ERROR_EVT_STR), - eDoesBubble, eCancelable); - if (!event) { - NS_ERROR("Failed to create event!"); - return; - } - - ErrorResult rv; - nsRefPtr error = mOpenDBRequest->GetError(rv); - - NS_ASSERTION(!rv.Failed(), "This shouldn't be failing at this point!"); - if (!error) { - mOpenDBRequest->SetError(mResultCode); - } - - bool dummy; - mOpenDBRequest->DispatchEvent(event, &dummy); -} - -void -OpenDatabaseHelper::ReleaseMainThreadObjects() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mOpenDBRequest = nullptr; - mDatabase = nullptr; - - HelperBase::ReleaseMainThreadObjects(); -} - -NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper) - -nsresult -SetVersionHelper::Init() -{ - // Block transaction creation until we are done. - mOpenHelper->BlockDatabase(); - - return NS_OK; -} - -nsresult -SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(aConnection, "Passing a null connection!"); - - PROFILER_LABEL("SetVersionHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - nsCOMPtr stmt; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE database " - "SET version = :version" - ), getter_AddRefs(stmt)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"), - mRequestedVersion); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (NS_FAILED(stmt->Execute())) { - return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; - } - - return NS_OK; -} - -nsresult -SetVersionHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) -{ - DatabaseInfo* info = mDatabase->Info(); - info->version = mRequestedVersion; - - NS_ASSERTION(mTransaction, "Better have a transaction!"); - - mOpenRequest->SetTransaction(mTransaction); - - return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), - aVal); -} - -nsresult -SetVersionHelper::OnExclusiveAccessAcquired() -{ - nsresult rv = DispatchToTransactionPool(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -// static -template -void -VersionChangeEventsRunnable::QueueVersionChange( - nsTArray >& aDatabases, - void* aClosure) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?"); - - T* closure = static_cast(aClosure); - - nsRefPtr eventsRunnable = - new VersionChangeEventsRunnable(closure->mOpenHelper->Database(), - closure->mOpenRequest, - aDatabases, - closure->mCurrentVersion, - closure->RequestedVersion()); - - NS_DispatchToCurrentThread(eventsRunnable); -} - -already_AddRefed -SetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) -{ - NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?"); - - return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner, - mCurrentVersion, - mRequestedVersion); -} - -nsresult -SetVersionHelper::NotifyTransactionPreComplete(IDBTransaction* aTransaction) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "This is unexpected."); - NS_ASSERTION(mOpenRequest, "Why don't we have a request?"); - - return mOpenHelper->NotifySetVersionFinished(); -} - -nsresult -SetVersionHelper::NotifyTransactionPostComplete(IDBTransaction* aTransaction) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "This is unexpected."); - NS_ASSERTION(mOpenRequest, "Why don't we have a request?"); - - // If we hit an error, the OpenDatabaseHelper needs to get that error too. - nsresult rv = GetResultCode(); - if (NS_FAILED(rv)) { - mOpenHelper->SetError(rv); - } - - // If the transaction was aborted, we should throw an error message. - if (aTransaction->IsAborted()) { - mOpenHelper->SetError(aTransaction->GetAbortCode()); - } - - mOpenRequest->SetTransaction(nullptr); - mOpenRequest = nullptr; - - mOpenHelper = nullptr; - - return rv; -} - -NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper); - -nsresult -DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - AssertIsOnIOThread(); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - NS_ASSERTION(!aConnection, "How did we get a connection here?"); - - PROFILER_LABEL("DeleteDatabaseHelper", "DoDatabaseWork", - js::ProfileEntry::Category::STORAGE); - - const StoragePrivilege& privilege = mOpenHelper->Privilege(); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsCOMPtr directory; - nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType, - mASCIIOrigin, - getter_AddRefs(directory)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - NS_ASSERTION(directory, "What?"); - - rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsAutoString filename; - rv = GetDatabaseFilename(mName, filename); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - nsCOMPtr dbFile; - rv = directory->Clone(getter_AddRefs(dbFile)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite")); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - bool exists = false; - rv = dbFile->Exists(&exists); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (exists) { - int64_t fileSize; - - if (privilege != Chrome) { - rv = dbFile->GetFileSize(&fileSize); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - rv = dbFile->Remove(false); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (privilege != Chrome) { - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "Shouldn't be null!"); - - quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup, - mASCIIOrigin, fileSize); - } - } - - nsCOMPtr dbJournalFile; - rv = directory->Clone(getter_AddRefs(dbJournalFile)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal")); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = dbJournalFile->Exists(&exists); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (exists) { - rv = dbJournalFile->Remove(false); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - nsCOMPtr fmDirectory; - rv = directory->Clone(getter_AddRefs(fmDirectory)); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = fmDirectory->Append(filename); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - rv = fmDirectory->Exists(&exists); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (exists) { - bool isDirectory; - rv = fmDirectory->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS(rv, rv); - IDB_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - uint64_t usage = 0; - - if (privilege != Chrome) { - rv = FileManager::GetUsage(fmDirectory, &usage); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - rv = fmDirectory->Remove(true); - IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - if (privilege != Chrome) { - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "Shouldn't be null!"); - - quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup, - mASCIIOrigin, usage); - } - } - - IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get(); - NS_ASSERTION(mgr, "This should never fail!"); - - mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName); - - return NS_OK; -} - -nsresult -DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) -{ - return NS_OK; -} - -nsresult -DeleteDatabaseHelper::OnExclusiveAccessAcquired() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "We should definitely have a manager here"); - - nsresult rv = Dispatch(quotaManager->IOThread()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -DeleteDatabaseHelper::Init() -{ - // Note that there's no need to block the database here, since the page - // never gets to touch it, and all other databases must be closed. - - return NS_OK; -} diff --git a/dom/indexedDB/OpenDatabaseHelper.h b/dom/indexedDB/OpenDatabaseHelper.h deleted file mode 100644 index 00a5a0fdd66d..000000000000 --- a/dom/indexedDB/OpenDatabaseHelper.h +++ /dev/null @@ -1,175 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_opendatabasehelper_h__ -#define mozilla_dom_indexeddb_opendatabasehelper_h__ - -#include "AsyncConnectionHelper.h" - -#include "nsIRunnable.h" - -#include "mozilla/dom/quota/StoragePrivilege.h" - -#include "DatabaseInfo.h" -#include "IDBDatabase.h" -#include "IDBRequest.h" - -class mozIStorageConnection; - -namespace mozilla { -namespace dom { -class nsIContentParent; -} -} - -BEGIN_INDEXEDDB_NAMESPACE - -class CheckPermissionsHelper; - -class OpenDatabaseHelper : public HelperBase -{ - friend class CheckPermissionsHelper; - - typedef mozilla::dom::quota::PersistenceType PersistenceType; - typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege; - - ~OpenDatabaseHelper() {} - -public: - OpenDatabaseHelper(IDBOpenDBRequest* aRequest, - const nsAString& aName, - const nsACString& aGroup, - const nsACString& aASCIIOrigin, - uint64_t aRequestedVersion, - PersistenceType aPersistenceType, - bool aForDeletion, - mozilla::dom::nsIContentParent* aContentParent, - StoragePrivilege aPrivilege) - : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName), - mGroup(aGroup), mASCIIOrigin(aASCIIOrigin), - mRequestedVersion(aRequestedVersion), mPersistenceType(aPersistenceType), - mForDeletion(aForDeletion), mPrivilege(aPrivilege), - mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0), - mLastIndexId(0), mState(eCreated), mResultCode(NS_OK), - mLoadDBMetadata(false), - mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome) - { - NS_ASSERTION(!aForDeletion || !aRequestedVersion, - "Can't be for deletion and request a version!"); - } - - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - nsresult Init(); - - nsresult WaitForOpenAllowed(); - nsresult Dispatch(nsIEventTarget* aDatabaseThread); - nsresult DispatchToIOThread(); - nsresult RunImmediately(); - - void SetError(nsresult rv) - { - NS_ASSERTION(NS_FAILED(rv), "Why are you telling me?"); - mResultCode = rv; - } - - virtual nsresult GetResultCode() MOZ_OVERRIDE - { - return mResultCode; - } - - nsresult NotifySetVersionFinished(); - nsresult NotifyDeleteFinished(); - void BlockDatabase(); - - const nsACString& Id() const - { - return mDatabaseId; - } - - IDBDatabase* Database() const - { - NS_ASSERTION(mDatabase, "Calling at the wrong time!"); - return mDatabase; - } - - const StoragePrivilege& Privilege() const - { - return mPrivilege; - } - - static - nsresult CreateDatabaseConnection(nsIFile* aDBFile, - nsIFile* aFMDirectory, - const nsAString& aName, - PersistenceType aPersistenceType, - const nsACString& aGroup, - const nsACString& aOrigin, - mozIStorageConnection** aConnection); - -protected: - // Methods only called on the main thread - nsresult EnsureSuccessResult(); - nsresult StartSetVersion(); - nsresult StartDelete(); - virtual nsresult GetSuccessResult(JSContext* aCx, - JS::MutableHandle aVal) MOZ_OVERRIDE; - void DispatchSuccessEvent(); - void DispatchErrorEvent(); - virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE; - - // Called by CheckPermissionsHelper on the main thread before dispatch. - void SetUnlimitedQuotaAllowed() - { - mTrackingQuota = false; - } - - // Methods only called on the DB thread - nsresult DoDatabaseWork(); - - // In-params. - nsRefPtr mOpenDBRequest; - nsString mName; - nsCString mGroup; - nsCString mASCIIOrigin; - uint64_t mRequestedVersion; - PersistenceType mPersistenceType; - bool mForDeletion; - StoragePrivilege mPrivilege; - nsCString mDatabaseId; - mozilla::dom::nsIContentParent* mContentParent; - - // Out-params. - nsTArray > mObjectStores; - uint64_t mCurrentVersion; - nsString mDatabaseFilePath; - int64_t mLastObjectStoreId; - int64_t mLastIndexId; - nsRefPtr mDatabase; - - // State variables - enum OpenDatabaseState { - eCreated = 0, // Not yet dispatched to the DB thread - eOpenPending, // Waiting for open allowed/open allowed - eDBWork, // Waiting to do/doing work on the DB thread - eFiringEvents, // Waiting to fire/firing events on the main thread - eSetVersionPending, // Waiting on a SetVersionHelper - eSetVersionCompleted, // SetVersionHelper is done - eDeletePending, // Waiting on a DeleteDatabaseHelper - eDeleteCompleted, // DeleteDatabaseHelper is done - }; - OpenDatabaseState mState; - nsresult mResultCode; - - nsRefPtr mFileManager; - - nsRefPtr mDBInfo; - bool mLoadDBMetadata; - bool mTrackingQuota; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_opendatabasehelper_h__ diff --git a/dom/indexedDB/PBackgroundIDBCursor.ipdl b/dom/indexedDB/PBackgroundIDBCursor.ipdl new file mode 100644 index 000000000000..3c755e903f26 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBCursor.ipdl @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBTransaction; +include protocol PBackgroundIDBVersionChangeTransaction; +include protocol PBlob; + +include PBackgroundIDBSharedTypes; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +using class mozilla::dom::indexedDB::Key + from "mozilla/dom/indexedDB/Key.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +struct ContinueParams +{ + Key key; +}; + +struct AdvanceParams +{ + uint32_t count; +}; + +union CursorRequestParams +{ + ContinueParams; + AdvanceParams; +}; + +struct ObjectStoreCursorResponse +{ + Key key; + SerializedStructuredCloneReadInfo cloneInfo; +}; + +struct ObjectStoreKeyCursorResponse +{ + Key key; +}; + +struct IndexCursorResponse +{ + Key key; + Key objectKey; + SerializedStructuredCloneReadInfo cloneInfo; +}; + +struct IndexKeyCursorResponse +{ + Key key; + Key objectKey; +}; + +union CursorResponse +{ + void_t; + nsresult; + ObjectStoreCursorResponse; + ObjectStoreKeyCursorResponse; + IndexCursorResponse; + IndexKeyCursorResponse; +}; + +protocol PBackgroundIDBCursor +{ + manager PBackgroundIDBTransaction or PBackgroundIDBVersionChangeTransaction; + +parent: + DeleteMe(); + + Continue(CursorRequestParams params); + +child: + __delete__(); + + Response(CursorResponse response); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBDatabase.ipdl b/dom/indexedDB/PBackgroundIDBDatabase.ipdl new file mode 100644 index 000000000000..40cecd959c77 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBDatabase.ipdl @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBDatabaseFile; +include protocol PBackgroundIDBFactory; +include protocol PBackgroundIDBTransaction; +include protocol PBackgroundIDBVersionChangeTransaction; +include protocol PBlob; + +include InputStreamParams; +include PBackgroundIDBSharedTypes; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::null_t + from "ipc/IPCMessageUtils.h"; + +using mozilla::dom::indexedDB::IDBTransaction::Mode + from "mozilla/dom/indexedDB/IDBTransaction.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +union NullableVersion +{ + null_t; + uint64_t; +}; + +union BlobOrInputStream +{ + PBlob; + InputStreamParams; +}; + +protocol PBackgroundIDBDatabase +{ + manager PBackgroundIDBFactory; + + manages PBackgroundIDBDatabaseFile; + manages PBackgroundIDBTransaction; + manages PBackgroundIDBVersionChangeTransaction; + +parent: + DeleteMe(); + + Blocked(); + + Close(); + + PBackgroundIDBDatabaseFile(BlobOrInputStream blobOrInputStream); + + PBackgroundIDBTransaction(nsString[] objectStoreNames, Mode mode); + +child: + __delete__(); + + VersionChange(uint64_t oldVersion, NullableVersion newVersion); + + Invalidate(); + + PBackgroundIDBVersionChangeTransaction(uint64_t currentVersion, + uint64_t requestedVersion, + int64_t nextObjectStoreId, + int64_t nextIndexId); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl b/dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl similarity index 67% rename from dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl rename to dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl index 4e1f70482580..db66e773394e 100644 --- a/dom/indexedDB/ipc/PIndexedDBDeleteDatabaseRequest.ipdl +++ b/dom/indexedDB/PBackgroundIDBDatabaseFile.ipdl @@ -2,20 +2,18 @@ * 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 protocol PIndexedDB; +include protocol PBackgroundIDBDatabase; namespace mozilla { namespace dom { namespace indexedDB { -protocol PIndexedDBDeleteDatabaseRequest +protocol PBackgroundIDBDatabaseFile { - manager PIndexedDB; + manager PBackgroundIDBDatabase; -child: - __delete__(nsresult rv); - - Blocked(uint64_t currentVersion); +parent: + __delete__(); }; } // namespace indexedDB diff --git a/dom/indexedDB/PBackgroundIDBFactory.ipdl b/dom/indexedDB/PBackgroundIDBFactory.ipdl new file mode 100644 index 000000000000..1fac81f93b16 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBFactory.ipdl @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackground; +include protocol PBackgroundIDBDatabase; +include protocol PBackgroundIDBFactoryRequest; + +include PBackgroundIDBSharedTypes; +include PBackgroundSharedTypes; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +struct CommonFactoryRequestParams +{ + DatabaseMetadata metadata; + PrincipalInfo principalInfo; + bool privateBrowsingMode; +}; + +struct OpenDatabaseRequestParams +{ + CommonFactoryRequestParams commonParams; +}; + +struct DeleteDatabaseRequestParams +{ + CommonFactoryRequestParams commonParams; +}; + +union FactoryRequestParams +{ + OpenDatabaseRequestParams; + DeleteDatabaseRequestParams; +}; + +protocol PBackgroundIDBFactory +{ + manager PBackground; + + manages PBackgroundIDBDatabase; + manages PBackgroundIDBFactoryRequest; + +parent: + DeleteMe(); + + PBackgroundIDBFactoryRequest(FactoryRequestParams params); + +child: + __delete__(); + + PBackgroundIDBDatabase(DatabaseSpec spec, + PBackgroundIDBFactoryRequest request); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl b/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl new file mode 100644 index 000000000000..1e198fc96bb3 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBFactoryRequest.ipdl @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBFactory; +include protocol PBackgroundIDBDatabase; + +include PBackgroundSharedTypes; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +struct OpenDatabaseRequestResponse +{ + PBackgroundIDBDatabase database; +}; + +struct DeleteDatabaseRequestResponse +{ + uint64_t previousVersion; +}; + +union FactoryRequestResponse +{ + nsresult; + OpenDatabaseRequestResponse; + DeleteDatabaseRequestResponse; +}; + +protocol PBackgroundIDBFactoryRequest +{ + manager PBackgroundIDBFactory; + +child: + __delete__(FactoryRequestResponse response); + + PermissionChallenge(PrincipalInfo principalInfo); + + Blocked(uint64_t currentVersion); + +parent: + PermissionRetry(); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBRequest.ipdl b/dom/indexedDB/PBackgroundIDBRequest.ipdl new file mode 100644 index 000000000000..e63cfbd16563 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBRequest.ipdl @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBTransaction; +include protocol PBackgroundIDBVersionChangeTransaction; +include protocol PBlob; + +include PBackgroundIDBSharedTypes; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +using class mozilla::dom::indexedDB::Key + from "mozilla/dom/indexedDB/Key.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +struct ObjectStoreAddResponse +{ + Key key; +}; + +struct ObjectStorePutResponse +{ + Key key; +}; + +struct ObjectStoreGetResponse +{ + SerializedStructuredCloneReadInfo cloneInfo; +}; + +struct ObjectStoreGetAllResponse +{ + SerializedStructuredCloneReadInfo[] cloneInfos; +}; + +struct ObjectStoreGetAllKeysResponse +{ + Key[] keys; +}; + +struct ObjectStoreDeleteResponse +{ }; + +struct ObjectStoreClearResponse +{ }; + +struct ObjectStoreCountResponse +{ + uint64_t count; +}; + +struct IndexGetResponse +{ + SerializedStructuredCloneReadInfo cloneInfo; +}; + +struct IndexGetKeyResponse +{ + Key key; +}; + +struct IndexGetAllResponse +{ + SerializedStructuredCloneReadInfo[] cloneInfos; +}; + +struct IndexGetAllKeysResponse +{ + Key[] keys; +}; + +struct IndexCountResponse +{ + uint64_t count; +}; + +union RequestResponse +{ + nsresult; + ObjectStoreGetResponse; + ObjectStoreAddResponse; + ObjectStorePutResponse; + ObjectStoreDeleteResponse; + ObjectStoreClearResponse; + ObjectStoreCountResponse; + ObjectStoreGetAllResponse; + ObjectStoreGetAllKeysResponse; + IndexGetResponse; + IndexGetKeyResponse; + IndexGetAllResponse; + IndexGetAllKeysResponse; + IndexCountResponse; +}; + +protocol PBackgroundIDBRequest +{ + manager PBackgroundIDBTransaction or PBackgroundIDBVersionChangeTransaction; + +child: + __delete__(RequestResponse response); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh new file mode 100644 index 000000000000..d1e176906367 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh @@ -0,0 +1,259 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBlob; +include protocol PBackgroundIDBDatabaseFile; + +include "mozilla/dom/indexedDB/SerializationHelpers.h"; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +using mozilla::dom::indexedDB::IDBCursor::Direction + from "mozilla/dom/indexedDB/IDBCursor.h"; + +using class mozilla::dom::indexedDB::Key + from "mozilla/dom/indexedDB/Key.h"; + +using class mozilla::dom::indexedDB::KeyPath + from "mozilla/dom/indexedDB/KeyPath.h"; + +using mozilla::dom::quota::PersistenceType + from "mozilla/dom/quota/PersistenceType.h"; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +struct SerializedKeyRange +{ + Key lower; + Key upper; + bool lowerOpen; + bool upperOpen; + bool isOnly; +}; + +struct SerializedStructuredCloneReadInfo +{ + uint8_t[] data; + PBlob[] blobs; + + // This will only be valid for parent process actors. + intptr_t[] fileInfos; +}; + +struct SerializedStructuredCloneWriteInfo +{ + uint8_t[] data; + uint64_t offsetToKeyProp; +}; + +struct IndexUpdateInfo +{ + int64_t indexId; + Key value; +}; + +union OptionalKeyRange +{ + SerializedKeyRange; + void_t; +}; + +struct DatabaseMetadata +{ + nsString name; + uint64_t version; + PersistenceType persistenceType; + bool persistenceTypeIsExplicit; +}; + +struct ObjectStoreMetadata +{ + int64_t id; + nsString name; + KeyPath keyPath; + bool autoIncrement; +}; + +struct IndexMetadata +{ + int64_t id; + nsString name; + KeyPath keyPath; + bool unique; + bool multiEntry; +}; + +struct DatabaseSpec +{ + DatabaseMetadata metadata; + ObjectStoreSpec[] objectStores; +}; + +struct ObjectStoreSpec +{ + ObjectStoreMetadata metadata; + IndexMetadata[] indexes; +}; + +struct ObjectStoreOpenCursorParams +{ + int64_t objectStoreId; + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +struct ObjectStoreOpenKeyCursorParams +{ + int64_t objectStoreId; + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +struct IndexOpenCursorParams +{ + int64_t objectStoreId; + int64_t indexId; + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +struct IndexOpenKeyCursorParams +{ + int64_t objectStoreId; + int64_t indexId; + OptionalKeyRange optionalKeyRange; + Direction direction; +}; + +union OpenCursorParams +{ + ObjectStoreOpenCursorParams; + ObjectStoreOpenKeyCursorParams; + IndexOpenCursorParams; + IndexOpenKeyCursorParams; +}; + +// XXX Remove this once MutableFile has been ported to PBackground. +union DatabaseFileOrMutableFileId +{ + PBackgroundIDBDatabaseFile; + int64_t; +}; + +struct ObjectStoreAddPutParams +{ + int64_t objectStoreId; + SerializedStructuredCloneWriteInfo cloneInfo; + Key key; + IndexUpdateInfo[] indexUpdateInfos; + DatabaseFileOrMutableFileId[] files; +}; + +struct ObjectStoreAddParams +{ + ObjectStoreAddPutParams commonParams; +}; + +struct ObjectStorePutParams +{ + ObjectStoreAddPutParams commonParams; +}; + +struct ObjectStoreGetParams +{ + int64_t objectStoreId; + SerializedKeyRange keyRange; +}; + +struct ObjectStoreGetAllParams +{ + int64_t objectStoreId; + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct ObjectStoreGetAllKeysParams +{ + int64_t objectStoreId; + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct ObjectStoreDeleteParams +{ + int64_t objectStoreId; + SerializedKeyRange keyRange; +}; + +struct ObjectStoreClearParams +{ + int64_t objectStoreId; +}; + +struct ObjectStoreCountParams +{ + int64_t objectStoreId; + OptionalKeyRange optionalKeyRange; +}; + +struct IndexGetParams +{ + int64_t objectStoreId; + int64_t indexId; + SerializedKeyRange keyRange; +}; + +struct IndexGetKeyParams +{ + int64_t objectStoreId; + int64_t indexId; + SerializedKeyRange keyRange; +}; + +struct IndexGetAllParams +{ + int64_t objectStoreId; + int64_t indexId; + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct IndexGetAllKeysParams +{ + int64_t objectStoreId; + int64_t indexId; + OptionalKeyRange optionalKeyRange; + uint32_t limit; +}; + +struct IndexCountParams +{ + int64_t objectStoreId; + int64_t indexId; + OptionalKeyRange optionalKeyRange; +}; + +union RequestParams +{ + ObjectStoreAddParams; + ObjectStorePutParams; + ObjectStoreGetParams; + ObjectStoreGetAllParams; + ObjectStoreGetAllKeysParams; + ObjectStoreDeleteParams; + ObjectStoreClearParams; + ObjectStoreCountParams; + IndexGetParams; + IndexGetKeyParams; + IndexGetAllParams; + IndexGetAllKeysParams; + IndexCountParams; +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBTransaction.ipdl b/dom/indexedDB/PBackgroundIDBTransaction.ipdl new file mode 100644 index 000000000000..0db17629207c --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBTransaction.ipdl @@ -0,0 +1,41 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBCursor; +include protocol PBackgroundIDBDatabase; +include protocol PBackgroundIDBDatabaseFile; +include protocol PBackgroundIDBRequest; + +include PBackgroundIDBSharedTypes; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +protocol PBackgroundIDBTransaction +{ + manager PBackgroundIDBDatabase; + + manages PBackgroundIDBCursor; + manages PBackgroundIDBRequest; + +parent: + DeleteMe(); + + Commit(); + Abort(nsresult resultCode); + + PBackgroundIDBCursor(OpenCursorParams params); + + PBackgroundIDBRequest(RequestParams params); + +child: + __delete__(); + + Complete(nsresult result); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl b/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl new file mode 100644 index 000000000000..f0b475f4a4a7 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundIDBCursor; +include protocol PBackgroundIDBDatabase; +include protocol PBackgroundIDBRequest; +include protocol PBlob; + +include PBackgroundIDBSharedTypes; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +protocol PBackgroundIDBVersionChangeTransaction +{ + manager PBackgroundIDBDatabase; + + manages PBackgroundIDBCursor; + manages PBackgroundIDBRequest; + +parent: + DeleteMe(); + + Commit(); + Abort(nsresult resultCode); + + CreateObjectStore(ObjectStoreMetadata metadata); + DeleteObjectStore(int64_t objectStoreId); + + CreateIndex(int64_t objectStoreId, + IndexMetadata metadata); + DeleteIndex(int64_t objectStoreId, + int64_t indexId); + + PBackgroundIDBCursor(OpenCursorParams params); + + PBackgroundIDBRequest(RequestParams params); + +child: + __delete__(); + + Complete(nsresult result); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PIndexedDBPermissionRequest.ipdl b/dom/indexedDB/PIndexedDBPermissionRequest.ipdl new file mode 100644 index 000000000000..4eecb2b1c0ad --- /dev/null +++ b/dom/indexedDB/PIndexedDBPermissionRequest.ipdl @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBrowser; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +protocol PIndexedDBPermissionRequest +{ + manager PBrowser; + +child: + /** + * Called when the user makes a choice or the permission request times out. + * + * @param permission + * The permission result (see nsIPermissionManager.idl for valid values). + */ + __delete__(uint32_t permission); +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PermissionRequestBase.cpp b/dom/indexedDB/PermissionRequestBase.cpp new file mode 100644 index 000000000000..9f7f38e8ba2b --- /dev/null +++ b/dom/indexedDB/PermissionRequestBase.cpp @@ -0,0 +1,267 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PermissionRequestBase.h" + +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Services.h" +#include "nsIDOMWindow.h" +#include "nsIObserverService.h" +#include "nsIPrincipal.h" +#include "nsPIDOMWindow.h" +#include "nsXULAppAPI.h" + +namespace mozilla { +namespace dom { +namespace indexedDB { + +using namespace mozilla::services; + +namespace { + +#define IDB_PREFIX "indexedDB" +#define TOPIC_PREFIX IDB_PREFIX "-permissions-" + +const char kPermissionString[] = IDB_PREFIX; + +const char kPermissionPromptTopic[] = TOPIC_PREFIX "prompt"; +const char kPermissionResponseTopic[] = TOPIC_PREFIX "response"; + +#undef TOPIC_PREFIX +#undef IDB_PREFIX + +const uint32_t kPermissionDefault = nsIPermissionManager::UNKNOWN_ACTION; + +void +AssertSanity() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); +} + +} // anonymous namespace + +PermissionRequestBase::PermissionRequestBase(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal) + : mWindow(aWindow) + , mPrincipal(aPrincipal) +{ + AssertSanity(); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aPrincipal); +} + +PermissionRequestBase::~PermissionRequestBase() +{ + AssertSanity(); +} + +// static +nsresult +PermissionRequestBase::GetCurrentPermission(nsIPrincipal* aPrincipal, + PermissionValue* aCurrentValue) +{ + AssertSanity(); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aCurrentValue); + + nsCOMPtr permMan = GetPermissionManager(); + if (NS_WARN_IF(!permMan)) { + return NS_ERROR_FAILURE; + } + + uint32_t intPermission; + nsresult rv = permMan->TestExactPermissionFromPrincipal( + aPrincipal, + kPermissionString, + &intPermission); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + PermissionValue permission = + PermissionValueForIntPermission(intPermission); + + MOZ_ASSERT(permission == kPermissionAllowed || + permission == kPermissionDenied || + permission == kPermissionPrompt); + + *aCurrentValue = permission; + return NS_OK; +} + +// static +auto +PermissionRequestBase::PermissionValueForIntPermission(uint32_t aIntPermission) + -> PermissionValue +{ + AssertSanity(); + + // The 'indexedDB' permission is unusual in that the default action is to + // allow access. Switch that here to make the logic clearer. + switch (aIntPermission) { + case kPermissionDefault: + return kPermissionAllowed; + case kPermissionAllowed: + return kPermissionPrompt; + case kPermissionDenied: + return kPermissionDenied; + default: + MOZ_CRASH("Bad permission!"); + } + + MOZ_CRASH("Should never get here!"); +} + +nsresult +PermissionRequestBase::PromptIfNeeded(PermissionValue* aCurrentValue) +{ + AssertSanity(); + MOZ_ASSERT(aCurrentValue); + MOZ_ASSERT(mPrincipal); + + // Tricky, we want to release the window and principal in all cases except + // when we successfully prompt. + nsCOMPtr window; + mWindow.swap(window); + + nsCOMPtr principal; + mPrincipal.swap(principal); + + PermissionValue currentValue; + nsresult rv = GetCurrentPermission(principal, ¤tValue); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(currentValue != kPermissionDefault); + + if (currentValue == kPermissionPrompt) { + nsCOMPtr obsSvc = GetObserverService(); + if (NS_WARN_IF(!obsSvc)) { + return NS_ERROR_FAILURE; + } + + // We're about to prompt so swap the members back. + window.swap(mWindow); + principal.swap(mPrincipal); + + rv = obsSvc->NotifyObservers(static_cast(this), + kPermissionPromptTopic, + nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Finally release if we failed the prompt. + mWindow = nullptr; + mPrincipal = nullptr; + return rv; + } + } + + *aCurrentValue = currentValue; + return NS_OK; +} + +void +PermissionRequestBase::SetExplicitPermission(nsIPrincipal* aPrincipal, + uint32_t aIntPermission) +{ + AssertSanity(); + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aIntPermission == kPermissionAllowed || + aIntPermission == kPermissionDenied); + + nsCOMPtr permMan = GetPermissionManager(); + if (NS_WARN_IF(!permMan)) { + return; + } + + nsresult rv = aIntPermission == kPermissionAllowed ? + permMan->RemoveFromPrincipal(aPrincipal, kPermissionString) : + permMan->AddFromPrincipal(aPrincipal, + kPermissionString, + aIntPermission, + nsIPermissionManager::EXPIRE_NEVER, + /* aExpireTime */ 0); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } +} + +NS_IMPL_ISUPPORTS(PermissionRequestBase, nsIObserver, nsIInterfaceRequestor) + +NS_IMETHODIMP +PermissionRequestBase::GetInterface(const nsIID& aIID, + void** aResult) +{ + AssertSanity(); + + if (aIID.Equals(NS_GET_IID(nsIObserver))) { + return QueryInterface(aIID, aResult); + } + + if (aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { + return mWindow->QueryInterface(aIID, aResult); + } + + *aResult = nullptr; + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +PermissionRequestBase::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + AssertSanity(); + MOZ_ASSERT(!strcmp(aTopic, kPermissionResponseTopic)); + MOZ_ASSERT(mWindow); + MOZ_ASSERT(mPrincipal); + + nsCOMPtr window; + mWindow.swap(window); + + nsCOMPtr principal; + mPrincipal.swap(principal); + + nsresult rv; + uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); + + // The UI prompt code will only return one of these three values. We have to + // transform it to our values. + MOZ_ASSERT(promptResult == kPermissionDefault || + promptResult == kPermissionAllowed || + promptResult == kPermissionDenied); + + if (promptResult != kPermissionDefault) { + // Save explicitly allowed or denied permissions now. + SetExplicitPermission(principal, promptResult); + } + + PermissionValue permission; + switch (promptResult) { + case kPermissionDefault: + permission = kPermissionPrompt; + break; + + case kPermissionAllowed: + permission = kPermissionAllowed; + break; + + case kPermissionDenied: + permission = kPermissionDenied; + break; + + default: + MOZ_CRASH("Bad prompt result!"); + } + + OnPromptComplete(permission); + return NS_OK; +} + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/PermissionRequestBase.h b/dom/indexedDB/PermissionRequestBase.h new file mode 100644 index 000000000000..3ebf9b81f559 --- /dev/null +++ b/dom/indexedDB/PermissionRequestBase.h @@ -0,0 +1,77 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_permissionrequestbase_h__ +#define mozilla_dom_indexeddb_permissionrequestbase_h__ + +#include "mozilla/Attributes.h" +#include "nsCOMPtr.h" +#include "nsIInterfaceRequestor.h" +#include "nsIObserver.h" +#include "nsIPermissionManager.h" +#include "nsISupportsImpl.h" +#include "nsString.h" + +class nsIPrincipal; +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { +namespace indexedDB { + +class PermissionRequestBase + : public nsIObserver + , public nsIInterfaceRequestor +{ + nsCOMPtr mWindow; + nsCOMPtr mPrincipal; + +public: + enum PermissionValue { + kPermissionAllowed = nsIPermissionManager::ALLOW_ACTION, + kPermissionDenied = nsIPermissionManager::DENY_ACTION, + kPermissionPrompt = nsIPermissionManager::PROMPT_ACTION + }; + + NS_DECL_ISUPPORTS + + // This function will not actually prompt. It will never return + // kPermissionDefault but will instead translate the permission manager value + // into the correct value for the given type. + static nsresult + GetCurrentPermission(nsIPrincipal* aPrincipal, + PermissionValue* aCurrentValue); + + static PermissionValue + PermissionValueForIntPermission(uint32_t aIntPermission); + + // This function will prompt if needed. It may only be called once. + nsresult + PromptIfNeeded(PermissionValue* aCurrentValue); + +protected: + PermissionRequestBase(nsPIDOMWindow* aWindow, + nsIPrincipal* aPrincipal); + + // Reference counted. + virtual + ~PermissionRequestBase(); + + virtual void + OnPromptComplete(PermissionValue aPermissionValue) = 0; + +private: + void + SetExplicitPermission(nsIPrincipal* aPrincipal, + uint32_t aIntPermission); + + NS_DECL_NSIOBSERVER + NS_DECL_NSIINTERFACEREQUESTOR +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_indexeddb_permissionrequestbase_h__ diff --git a/dom/indexedDB/ProfilerHelpers.h b/dom/indexedDB/ProfilerHelpers.h index 46752955d4d9..4afab8469c65 100644 --- a/dom/indexedDB/ProfilerHelpers.h +++ b/dom/indexedDB/ProfilerHelpers.h @@ -36,7 +36,9 @@ #include "IDBTransaction.h" #include "Key.h" -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { class ProfilerString : public nsAutoCString { @@ -159,7 +161,9 @@ public: } }; -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #define IDB_PROFILER_MARK(_detailedFmt, _conciseFmt, ...) \ do { \ diff --git a/dom/indexedDB/ReportInternalError.cpp b/dom/indexedDB/ReportInternalError.cpp index d02eedc30440..1441099ec7bd 100644 --- a/dom/indexedDB/ReportInternalError.cpp +++ b/dom/indexedDB/ReportInternalError.cpp @@ -11,7 +11,9 @@ #include "nsContentUtils.h" #include "nsPrintfCString.h" -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) @@ -29,4 +31,6 @@ ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) "indexedDB"); } -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/ReportInternalError.h b/dom/indexedDB/ReportInternalError.h index 882c1469fd44..aa014a091a8f 100644 --- a/dom/indexedDB/ReportInternalError.h +++ b/dom/indexedDB/ReportInternalError.h @@ -41,11 +41,15 @@ } while(0) -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { void ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr); -END_INDEXEDDB_NAMESPACE +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_reportinternalerror_h__ diff --git a/dom/indexedDB/SerializationHelpers.h b/dom/indexedDB/SerializationHelpers.h new file mode 100644 index 000000000000..45cb5b6fb78a --- /dev/null +++ b/dom/indexedDB/SerializationHelpers.h @@ -0,0 +1,103 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_serializationhelpers_h__ +#define mozilla_dom_indexeddb_serializationhelpers_h__ + +#include "ipc/IPCMessageUtils.h" + +#include "mozilla/dom/indexedDB/Key.h" +#include "mozilla/dom/indexedDB/KeyPath.h" +#include "mozilla/dom/indexedDB/IDBCursor.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" +#include "mozilla/dom/quota/PersistenceType.h" +#include "mozilla/dom/quota/StoragePrivilege.h" + +namespace IPC { + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::quota::PersistenceType, + mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT, + mozilla::dom::quota::PERSISTENCE_TYPE_INVALID> +{ }; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer +{ }; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::Key paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mBuffer); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mBuffer); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.mBuffer, aLog); + } +}; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer +{ }; + +template <> +struct ParamTraits +{ + typedef mozilla::dom::indexedDB::KeyPath paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mStrings); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mType) && + ReadParam(aMsg, aIter, &aResult->mStrings); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + LogParam(aParam.mStrings, aLog); + } +}; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::indexedDB::IDBCursor::Direction, + mozilla::dom::indexedDB::IDBCursor::NEXT, + mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID> +{ }; + +template <> +struct ParamTraits : + public ContiguousEnumSerializer< + mozilla::dom::indexedDB::IDBTransaction::Mode, + mozilla::dom::indexedDB::IDBTransaction::READ_ONLY, + mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID> +{ }; + +} // namespace IPC + +#endif // mozilla_dom_indexeddb_serializationhelpers_h__ diff --git a/dom/indexedDB/TransactionThreadPool.cpp b/dom/indexedDB/TransactionThreadPool.cpp index 6a59f5b3d997..a8732dddc959 100644 --- a/dom/indexedDB/TransactionThreadPool.cpp +++ b/dom/indexedDB/TransactionThreadPool.cpp @@ -6,19 +6,25 @@ #include "TransactionThreadPool.h" -#include "nsIObserverService.h" -#include "nsIThreadPool.h" - +#include "IDBTransaction.h" +#include "mozilla/Monitor.h" +#include "mozilla/Move.h" +#include "mozilla/ipc/BackgroundParent.h" #include "nsComponentManagerUtils.h" +#include "nsIEventTarget.h" +#include "nsIRunnable.h" +#include "nsISupportsPriority.h" +#include "nsIThreadPool.h" #include "nsThreadUtils.h" #include "nsServiceManagerUtils.h" #include "nsXPCOMCIDInternal.h" - #include "ProfilerHelpers.h" -using mozilla::MonitorAutoLock; +namespace mozilla { +namespace dom { +namespace indexedDB { -USING_INDEXEDDB_NAMESPACE +using mozilla::ipc::AssertIsOnBackgroundThread; namespace { @@ -26,168 +32,414 @@ const uint32_t kThreadLimit = 20; const uint32_t kIdleThreadLimit = 5; const uint32_t kIdleThreadTimeoutMs = 30000; -TransactionThreadPool* gThreadPool = nullptr; -bool gShutdown = false; +#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS) +#define BUILD_THREADPOOL_LISTENER +#endif -#ifdef MOZ_ENABLE_PROFILER_SPS +#ifdef DEBUG -class TransactionThreadPoolListener : public nsIThreadPoolListener +const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; +const uint32_t kDEBUGThreadSleepMS = 0; + +#endif // DEBUG + +#ifdef BUILD_THREADPOOL_LISTENER + +class TransactionThreadPoolListener MOZ_FINAL + : public nsIThreadPoolListener { public: + TransactionThreadPoolListener() + { } + NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSITHREADPOOLLISTENER private: virtual ~TransactionThreadPoolListener() { } + + NS_DECL_NSITHREADPOOLLISTENER }; -#endif // MOZ_ENABLE_PROFILER_SPS +#endif // BUILD_THREADPOOL_LISTENER } // anonymous namespace -BEGIN_INDEXEDDB_NAMESPACE - -class FinishTransactionRunnable MOZ_FINAL : public nsIRunnable +class TransactionThreadPool::FinishTransactionRunnable MOZ_FINAL + : public nsRunnable { -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE + typedef TransactionThreadPool::FinishCallback FinishCallback; - inline FinishTransactionRunnable(IDBTransaction* aTransaction, - nsCOMPtr& aFinishRunnable); + nsRefPtr mThreadPool; + nsRefPtr mFinishCallback; + uint64_t mTransactionId; + const nsCString mDatabaseId; + const nsTArray mObjectStoreNames; + uint16_t mMode; + +public: + FinishTransactionRunnable(already_AddRefed aThreadPool, + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode, + already_AddRefed aFinishCallback); + + void + Dispatch() + { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mThreadPool->mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL))); + } + + NS_DECL_ISUPPORTS_INHERITED private: - ~FinishTransactionRunnable() {} + ~FinishTransactionRunnable() + { } - IDBTransaction* mTransaction; - nsCOMPtr mFinishRunnable; + NS_DECL_NSIRUNNABLE }; -END_INDEXEDDB_NAMESPACE +struct TransactionThreadPool::DatabaseTransactionInfo MOZ_FINAL +{ + typedef nsClassHashtable + TransactionHashtable; + TransactionHashtable transactions; + nsClassHashtable blockingTransactions; + + DatabaseTransactionInfo() + { + MOZ_COUNT_CTOR(DatabaseTransactionInfo); + } + + ~DatabaseTransactionInfo() + { + MOZ_COUNT_DTOR(DatabaseTransactionInfo); + } +}; + +struct TransactionThreadPool::DatabasesCompleteCallback MOZ_FINAL +{ + friend class nsAutoPtr; + + nsTArray mDatabaseIds; + nsCOMPtr mCallback; + + DatabasesCompleteCallback() + { + MOZ_COUNT_CTOR(DatabasesCompleteCallback); + } + +private: + ~DatabasesCompleteCallback() + { + MOZ_COUNT_DTOR(DatabasesCompleteCallback); + } +}; + +class TransactionThreadPool::TransactionQueue MOZ_FINAL + : public nsRunnable +{ + Monitor mMonitor; + + nsRefPtr mOwningThreadPool; + uint64_t mTransactionId; + const nsCString mDatabaseId; + const nsTArray mObjectStoreNames; + uint16_t mMode; + + nsAutoTArray, 10> mQueue; + nsRefPtr mFinishCallback; + bool mShouldFinish; + +public: + TransactionQueue(TransactionThreadPool* aThreadPool, + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode); + + NS_DECL_ISUPPORTS_INHERITED + + void Unblock(); + + void Dispatch(nsIRunnable* aRunnable); + + void Finish(FinishCallback* aFinishCallback); + +private: + ~TransactionQueue() + { } + + NS_DECL_NSIRUNNABLE +}; + +struct TransactionThreadPool::TransactionInfo MOZ_FINAL +{ + uint64_t transactionId; + nsCString databaseId; + nsRefPtr queue; + nsTHashtable> blockedOn; + nsTHashtable> blocking; + + TransactionInfo(TransactionThreadPool* aThreadPool, + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode) + : transactionId(aTransactionId), databaseId(aDatabaseId) + { + MOZ_COUNT_CTOR(TransactionInfo); + + queue = new TransactionQueue(aThreadPool, aTransactionId, aDatabaseId, + aObjectStoreNames, aMode); + } + + ~TransactionInfo() + { + MOZ_COUNT_DTOR(TransactionInfo); + } +}; + +struct TransactionThreadPool::TransactionInfoPair MOZ_FINAL +{ + // Multiple reading transactions can block future writes. + nsTArray lastBlockingWrites; + // But only a single writing transaction can block future reads. + TransactionInfo* lastBlockingReads; + + TransactionInfoPair() + : lastBlockingReads(nullptr) + { + MOZ_COUNT_CTOR(TransactionInfoPair); + } + + ~TransactionInfoPair() + { + MOZ_COUNT_DTOR(TransactionInfoPair); + } +}; TransactionThreadPool::TransactionThreadPool() + : mOwningThread(NS_GetCurrentThread()) + , mNextTransactionId(0) + , mShutdownRequested(false) + , mShutdownComplete(false) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!gThreadPool, "More than one instance!"); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mOwningThread); + AssertIsOnOwningThread(); } TransactionThreadPool::~TransactionThreadPool() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(gThreadPool == this, "Different instances!"); - gThreadPool = nullptr; + AssertIsOnOwningThread(); + MOZ_ASSERT(HasCompletedShutdown()); } -// static -TransactionThreadPool* -TransactionThreadPool::GetOrCreate() -{ - if (!gThreadPool && !gShutdown) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsAutoPtr pool(new TransactionThreadPool()); +#ifdef DEBUG - nsresult rv = pool->Init(); - NS_ENSURE_SUCCESS(rv, nullptr); - - gThreadPool = pool.forget(); - } - return gThreadPool; -} - -// static -TransactionThreadPool* -TransactionThreadPool::Get() -{ - return gThreadPool; -} - -// static void -TransactionThreadPool::Shutdown() +TransactionThreadPool::AssertIsOnOwningThread() const { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(mOwningThread); - gShutdown = true; + bool current; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} - if (gThreadPool) { - if (NS_FAILED(gThreadPool->Cleanup())) { - NS_WARNING("Failed to shutdown thread pool!"); - } - delete gThreadPool; - gThreadPool = nullptr; +#endif // DEBUG + +// static +already_AddRefed +TransactionThreadPool::Create() +{ + AssertIsOnBackgroundThread(); + + nsRefPtr threadPool = new TransactionThreadPool(); + threadPool->AssertIsOnOwningThread(); + + if (NS_WARN_IF(NS_FAILED(threadPool->Init()))) { + threadPool->ShutdownAsync(); + return nullptr; } + + return threadPool.forget(); +} + +void +TransactionThreadPool::ShutdownAndSpin() +{ + AssertIsOnOwningThread(); + + if (mShutdownComplete) { + return; + } + + ShutdownAsync(); + + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + while (!mShutdownComplete) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } +} + +void +TransactionThreadPool::ShutdownAsync() +{ + AssertIsOnOwningThread(); + + if (mShutdownRequested) { + return; + } + + mShutdownRequested = true; + + if (!mThreadPool) { + MOZ_ASSERT(!mTransactionsInProgress.Count()); + MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); + + mShutdownComplete = true; + return; + } + + if (!mTransactionsInProgress.Count()) { + CleanupAsync(); + } +} + +bool +TransactionThreadPool::HasCompletedShutdown() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mShutdownComplete, mShutdownRequested); + + return mShutdownComplete; +} + +// static +uint64_t +TransactionThreadPool::NextTransactionId() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mNextTransactionId < UINT64_MAX); + + return ++mNextTransactionId; } nsresult TransactionThreadPool::Init() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); nsresult rv; mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = mThreadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Trans")); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = mThreadPool->SetThreadLimit(kThreadLimit); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } -#ifdef MOZ_ENABLE_PROFILER_SPS +#ifdef BUILD_THREADPOOL_LISTENER nsCOMPtr listener = new TransactionThreadPoolListener(); rv = mThreadPool->SetListener(listener); - NS_ENSURE_SUCCESS(rv, rv); -#endif + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } +#endif // BUILD_THREADPOOL_LISTENER + + NS_WARN_IF_FALSE(!kDEBUGThreadSleepMS, + "TransactionThreadPool thread debugging enabled, sleeping " + "after every event!"); return NS_OK; } -nsresult +void TransactionThreadPool::Cleanup() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(mThreadPool); + MOZ_ASSERT(mShutdownRequested); + MOZ_ASSERT(!mShutdownComplete); + MOZ_ASSERT(!mTransactionsInProgress.Count()); - PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "Cleanup", - js::ProfileEntry::Category::STORAGE); - - nsresult rv = mThreadPool->Shutdown(); - NS_ENSURE_SUCCESS(rv, rv); - - // Make sure the pool is still accessible while any callbacks generated from - // the other threads are processed. - rv = NS_ProcessPendingEvents(nullptr); - NS_ENSURE_SUCCESS(rv, rv); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThreadPool->Shutdown())); if (!mCompleteCallbacks.IsEmpty()) { // Run all callbacks manually now. - for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { - mCompleteCallbacks[index].mCallback->Run(); + for (uint32_t count = mCompleteCallbacks.Length(), index = 0; + index < count; + index++) { + nsAutoPtr& completeCallback = + mCompleteCallbacks[index]; + MOZ_ASSERT(completeCallback); + MOZ_ASSERT(completeCallback->mCallback); + + completeCallback->mCallback->Run(); + + completeCallback = nullptr; } + mCompleteCallbacks.Clear(); // And make sure they get processed. - rv = NS_ProcessPendingEvents(nullptr); - NS_ENSURE_SUCCESS(rv, rv); + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProcessPendingEvents(currentThread))); } - return NS_OK; + mShutdownComplete = true; +} + +void +TransactionThreadPool::CleanupAsync() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mThreadPool); + MOZ_ASSERT(mShutdownRequested); + MOZ_ASSERT(!mShutdownComplete); + MOZ_ASSERT(!mTransactionsInProgress.Count()); + + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &TransactionThreadPool::Cleanup); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable))); } // static PLDHashOperator -TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey* aKey, - void* aUserArg) +TransactionThreadPool::MaybeUnblockTransaction( + nsPtrHashKey* aKey, + void* aUserArg) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnBackgroundThread(); TransactionInfo* maybeUnblockedInfo = aKey->GetKey(); TransactionInfo* finishedInfo = static_cast(aUserArg); @@ -204,21 +456,20 @@ TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey* aK } void -TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) +TransactionThreadPool::FinishTransaction( + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null pointer!"); + AssertIsOnOwningThread(); - PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "FinishTransaction", - js::ProfileEntry::Category::STORAGE); - - // AddRef here because removing from the hash will call Release. - nsRefPtr transaction(aTransaction); - - const nsACString& databaseId = aTransaction->mDatabase->Id(); + PROFILER_LABEL("IndexedDB", + "TransactionThreadPool::FinishTransaction", + js::ProfileEntry::Category::STORAGE); DatabaseTransactionInfo* dbTransactionInfo; - if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { + if (!mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { NS_ERROR("We don't know anyting about this database?!"); return; } @@ -229,7 +480,7 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) uint32_t transactionCount = transactionsInProgress.Count(); #ifdef DEBUG - if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { + if (aMode == IDBTransaction::VERSION_CHANGE) { NS_ASSERTION(transactionCount == 1, "More transactions running than should be!"); } @@ -238,36 +489,40 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) if (transactionCount == 1) { #ifdef DEBUG { - const TransactionInfo* info = transactionsInProgress.Get(aTransaction); - NS_ASSERTION(info->transaction == aTransaction, "Transaction mismatch!"); + const TransactionInfo* info = transactionsInProgress.Get(aTransactionId); + NS_ASSERTION(info->transactionId == aTransactionId, "Transaction mismatch!"); } #endif - mTransactionsInProgress.Remove(databaseId); + mTransactionsInProgress.Remove(aDatabaseId); // See if we need to fire any complete callbacks. uint32_t index = 0; while (index < mCompleteCallbacks.Length()) { if (MaybeFireCallback(mCompleteCallbacks[index])) { mCompleteCallbacks.RemoveElementAt(index); - } - else { + } else { index++; } } + if (mShutdownRequested) { + CleanupAsync(); + } + return; } - TransactionInfo* info = transactionsInProgress.Get(aTransaction); + + TransactionInfo* info = transactionsInProgress.Get(aTransactionId); NS_ASSERTION(info, "We've never heard of this transaction?!?"); - const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; + const nsTArray& objectStoreNames = aObjectStoreNames; for (size_t index = 0, count = objectStoreNames.Length(); index < count; index++) { TransactionInfoPair* blockInfo = dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); NS_ASSERTION(blockInfo, "Huh?"); - if (aTransaction->mMode == IDBTransaction::READ_WRITE && + if (aMode == IDBTransaction::READ_WRITE && blockInfo->lastBlockingReads == info) { blockInfo->lastBlockingReads = nullptr; } @@ -280,48 +535,78 @@ TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) info->blocking.EnumerateEntries(MaybeUnblockTransaction, info); - transactionsInProgress.Remove(aTransaction); + transactionsInProgress.Remove(aTransactionId); +} + +TransactionThreadPool::TransactionQueue* +TransactionThreadPool::GetQueueForTransaction(uint64_t aTransactionId, + const nsACString& aDatabaseId) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransactionId <= mNextTransactionId); + + DatabaseTransactionInfo* dbTransactionInfo; + if (mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { + DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = + dbTransactionInfo->transactions; + TransactionInfo* info = transactionsInProgress.Get(aTransactionId); + if (info) { + // We recognize this one. + return info->queue; + } + } + + return nullptr; } TransactionThreadPool::TransactionQueue& -TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) +TransactionThreadPool::GetQueueForTransaction( + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null pointer!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aTransactionId <= mNextTransactionId); - const nsACString& databaseId = aTransaction->mDatabase->Id(); - - const nsTArray& objectStoreNames = aTransaction->mObjectStoreNames; - const uint16_t mode = aTransaction->mMode; + TransactionQueue* existingQueue = + GetQueueForTransaction(aTransactionId, aDatabaseId); + if (existingQueue) { + return *existingQueue; + } // See if we can run this transaction now. DatabaseTransactionInfo* dbTransactionInfo; - if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { + if (!mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) { // First transaction for this database. dbTransactionInfo = new DatabaseTransactionInfo(); - mTransactionsInProgress.Put(databaseId, dbTransactionInfo); + mTransactionsInProgress.Put(aDatabaseId, dbTransactionInfo); } DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = dbTransactionInfo->transactions; - TransactionInfo* info = transactionsInProgress.Get(aTransaction); + TransactionInfo* info = transactionsInProgress.Get(aTransactionId); if (info) { // We recognize this one. return *info->queue; } - TransactionInfo* transactionInfo = new TransactionInfo(aTransaction); + TransactionInfo* transactionInfo = new TransactionInfo(this, + aTransactionId, + aDatabaseId, + aObjectStoreNames, + aMode); - dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);; + dbTransactionInfo->transactions.Put(aTransactionId, transactionInfo);; - for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; + for (uint32_t index = 0, count = aObjectStoreNames.Length(); index < count; index++) { TransactionInfoPair* blockInfo = - dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); + dbTransactionInfo->blockingTransactions.Get(aObjectStoreNames[index]); if (!blockInfo) { blockInfo = new TransactionInfoPair(); blockInfo->lastBlockingReads = nullptr; - dbTransactionInfo->blockingTransactions.Put(objectStoreNames[index], + dbTransactionInfo->blockingTransactions.Put(aObjectStoreNames[index], blockInfo); } @@ -332,7 +617,7 @@ TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) blockingInfo->blocking.PutEntry(transactionInfo); } - if (mode == IDBTransaction::READ_WRITE && + if (aMode == IDBTransaction::READ_WRITE && blockInfo->lastBlockingWrites.Length()) { for (uint32_t index = 0, count = blockInfo->lastBlockingWrites.Length(); index < count; @@ -343,7 +628,7 @@ TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) } } - if (mode == IDBTransaction::READ_WRITE) { + if (aMode == IDBTransaction::READ_WRITE) { blockInfo->lastBlockingReads = transactionInfo; blockInfo->lastBlockingWrites.Clear(); } @@ -359,176 +644,168 @@ TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) return *transactionInfo->queue; } -nsresult -TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, +void +TransactionThreadPool::Dispatch(uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode, nsIRunnable* aRunnable, bool aFinish, - nsIRunnable* aFinishRunnable) + FinishCallback* aFinishCallback) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null pointer!"); - NS_ASSERTION(aRunnable, "Null pointer!"); + MOZ_ASSERT(aTransactionId <= mNextTransactionId); + MOZ_ASSERT(!mShutdownRequested); - if (aTransaction->mDatabase->IsInvalidated() && !aFinish) { - return NS_ERROR_NOT_AVAILABLE; - } - - TransactionQueue& queue = GetQueueForTransaction(aTransaction); + TransactionQueue& queue = GetQueueForTransaction(aTransactionId, + aDatabaseId, + aObjectStoreNames, + aMode); queue.Dispatch(aRunnable); if (aFinish) { - queue.Finish(aFinishRunnable); + queue.Finish(aFinishCallback); + } +} + +void +TransactionThreadPool::Dispatch(uint64_t aTransactionId, + const nsACString& aDatabaseId, + nsIRunnable* aRunnable, + bool aFinish, + FinishCallback* aFinishCallback) +{ + MOZ_ASSERT(aTransactionId <= mNextTransactionId); + + TransactionQueue* queue = GetQueueForTransaction(aTransactionId, aDatabaseId); + MOZ_ASSERT(queue, "Passed an invalid transaction id!"); + + queue->Dispatch(aRunnable); + if (aFinish) { + queue->Finish(aFinishCallback); } - return NS_OK; } void TransactionThreadPool::WaitForDatabasesToComplete( - nsTArray >& aDatabases, - nsIRunnable* aCallback) + nsTArray& aDatabaseIds, + nsIRunnable* aCallback) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!"); + AssertIsOnOwningThread(); + NS_ASSERTION(!aDatabaseIds.IsEmpty(), "No databases to wait on!"); NS_ASSERTION(aCallback, "Null pointer!"); - DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); - + nsAutoPtr callback( + new DatabasesCompleteCallback()); callback->mCallback = aCallback; - callback->mDatabases.SwapElements(aDatabases); + callback->mDatabaseIds.SwapElements(aDatabaseIds); - if (MaybeFireCallback(*callback)) { - mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); + if (!MaybeFireCallback(callback)) { + mCompleteCallbacks.AppendElement(callback.forget()); } } // static PLDHashOperator -TransactionThreadPool::CollectTransactions(IDBTransaction* aKey, +TransactionThreadPool::CollectTransactions(const uint64_t& aTransactionId, TransactionInfo* aValue, void* aUserArg) { - nsAutoTArray, 50>* transactionArray = - static_cast, 50>*>(aUserArg); - transactionArray->AppendElement(aKey); + nsAutoTArray* transactionArray = + static_cast*>(aUserArg); + transactionArray->AppendElement(aValue); return PL_DHASH_NEXT; } -void -TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aDatabase, "Null pointer!"); - - PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "AbortTransactionsForDatabase", - js::ProfileEntry::Category::STORAGE); - - // Get list of transactions for this database id - DatabaseTransactionInfo* dbTransactionInfo; - if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) { - // If there are no transactions, we're done. - return; - } - - // Collect any running transactions - DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = - dbTransactionInfo->transactions; - - NS_ASSERTION(transactionsInProgress.Count(), "Should never be 0!"); - - nsAutoTArray, 50> transactions; - transactionsInProgress.EnumerateRead(CollectTransactions, &transactions); - - // Abort transactions. Do this after collecting the transactions in case - // calling Abort() modifies the data structures we're iterating above. - for (uint32_t index = 0; index < transactions.Length(); index++) { - if (transactions[index]->Database() != aDatabase) { - continue; - } - - // This can fail, for example if the transaction is in the process of - // being comitted. That is expected and fine, so we ignore any returned - // errors. - ErrorResult rv; - transactions[index]->Abort(rv); - } -} - struct MOZ_STACK_CLASS TransactionSearchInfo { - explicit TransactionSearchInfo(nsIOfflineStorage* aDatabase) - : db(aDatabase), found(false) + explicit TransactionSearchInfo(const nsACString& aDatabaseId) + : databaseId(aDatabaseId) + , found(false) { } - nsIOfflineStorage* db; + nsCString databaseId; bool found; }; // static PLDHashOperator -TransactionThreadPool::FindTransaction(IDBTransaction* aKey, +TransactionThreadPool::FindTransaction(const uint64_t& aTransactionId, TransactionInfo* aValue, void* aUserArg) { TransactionSearchInfo* info = static_cast(aUserArg); - if (aKey->Database() == info->db) { + if (aValue->databaseId == info->databaseId) { info->found = true; return PL_DHASH_STOP; } return PL_DHASH_NEXT; } + bool -TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase) +TransactionThreadPool::HasTransactionsForDatabase(const nsACString& aDatabaseId) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aDatabase, "Null pointer!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(!aDatabaseId.IsEmpty(), "An empty DatabaseId!"); DatabaseTransactionInfo* dbTransactionInfo = nullptr; - dbTransactionInfo = mTransactionsInProgress.Get(aDatabase->Id()); + dbTransactionInfo = mTransactionsInProgress.Get(aDatabaseId); if (!dbTransactionInfo) { return false; } - TransactionSearchInfo info(aDatabase); + TransactionSearchInfo info(aDatabaseId); dbTransactionInfo->transactions.EnumerateRead(FindTransaction, &info); return info.found; } bool -TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback aCallback) +TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + MOZ_ASSERT(!aCallback->mDatabaseIds.IsEmpty()); + MOZ_ASSERT(aCallback->mCallback); - PROFILER_MAIN_THREAD_LABEL("TransactionThreadPool", "MaybeFireCallback", - js::ProfileEntry::Category::STORAGE); + PROFILER_LABEL("IndexedDB", + "TransactionThreadPool::MaybeFireCallback", + js::ProfileEntry::Category::STORAGE); - for (uint32_t index = 0; index < aCallback.mDatabases.Length(); index++) { - IDBDatabase* database = aCallback.mDatabases[index]; - if (!database) { - MOZ_CRASH(); - } + for (uint32_t count = aCallback->mDatabaseIds.Length(), index = 0; + index < count; + index++) { + const nsCString& databaseId = aCallback->mDatabaseIds[index]; + MOZ_ASSERT(!databaseId.IsEmpty()); - if (mTransactionsInProgress.Get(database->Id(), nullptr)) { + if (mTransactionsInProgress.Get(databaseId, nullptr)) { return false; } } - aCallback.mCallback->Run(); + aCallback->mCallback->Run(); return true; } TransactionThreadPool:: -TransactionQueue::TransactionQueue(IDBTransaction* aTransaction) +TransactionQueue::TransactionQueue(TransactionThreadPool* aThreadPool, + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode) : mMonitor("TransactionQueue::mMonitor"), - mTransaction(aTransaction), + mOwningThreadPool(aThreadPool), + mTransactionId(aTransactionId), + mDatabaseId(aDatabaseId), + mObjectStoreNames(aObjectStoreNames), + mMode(aMode), mShouldFinish(false) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null pointer!"); + MOZ_ASSERT(aThreadPool); + aThreadPool->AssertIsOnOwningThread(); } void @@ -538,8 +815,8 @@ TransactionThreadPool::TransactionQueue::Unblock() // NB: Finish may be called before Unblock. - TransactionThreadPool::Get()->mThreadPool-> - Dispatch(this, NS_DISPATCH_NORMAL); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mOwningThreadPool->mThreadPool->Dispatch(this, NS_DISPATCH_NORMAL))); } void @@ -555,35 +832,34 @@ TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable) } void -TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable) +TransactionThreadPool::TransactionQueue::Finish(FinishCallback* aFinishCallback) { MonitorAutoLock lock(mMonitor); NS_ASSERTION(!mShouldFinish, "Finish called more than once!"); mShouldFinish = true; - mFinishRunnable = aFinishRunnable; + mFinishCallback = aFinishCallback; mMonitor.Notify(); } -NS_IMPL_ISUPPORTS(TransactionThreadPool::TransactionQueue, nsIRunnable) +NS_IMPL_ISUPPORTS_INHERITED0(TransactionThreadPool::TransactionQueue, + nsRunnable) NS_IMETHODIMP TransactionThreadPool::TransactionQueue::Run() { - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); - - PROFILER_LABEL("TransactionQueue", "Run", - js::ProfileEntry::Category::STORAGE); + PROFILER_LABEL("IndexedDB", + "TransactionThreadPool::TransactionQueue""Run", + js::ProfileEntry::Category::STORAGE); IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work", "IDBTransaction[%llu] DT Start", mTransaction->GetSerialNumber()); nsAutoTArray, 10> queue; - nsCOMPtr finishRunnable; + nsRefPtr finishCallback; bool shouldFinish = false; do { @@ -599,13 +875,21 @@ TransactionThreadPool::TransactionQueue::Run() mQueue.SwapElements(queue); if (mShouldFinish) { - mFinishRunnable.swap(finishRunnable); + mFinishCallback.swap(finishCallback); shouldFinish = true; } } uint32_t count = queue.Length(); for (uint32_t index = 0; index < count; index++) { +#ifdef DEBUG + if (kDEBUGThreadSleepMS) { + MOZ_ALWAYS_TRUE( + PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == + PR_SUCCESS); + } +#endif // DEBUG + nsCOMPtr& runnable = queue[index]; runnable->Run(); runnable = nullptr; @@ -616,55 +900,84 @@ TransactionThreadPool::TransactionQueue::Run() } } while (!shouldFinish); +#ifdef DEBUG + if (kDEBUGThreadSleepMS) { + MOZ_ALWAYS_TRUE( + PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == PR_SUCCESS); + } +#endif // DEBUG + IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work", "IDBTransaction[%llu] DT Done", mTransaction->GetSerialNumber()); - nsCOMPtr finishTransactionRunnable = - new FinishTransactionRunnable(mTransaction, finishRunnable); - if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable))) { - NS_WARNING("Failed to dispatch finishTransactionRunnable!"); - } + nsRefPtr finishTransactionRunnable = + new FinishTransactionRunnable(mOwningThreadPool.forget(), + mTransactionId, + mDatabaseId, + mObjectStoreNames, + mMode, + finishCallback.forget()); + finishTransactionRunnable->Dispatch(); return NS_OK; } +TransactionThreadPool:: FinishTransactionRunnable::FinishTransactionRunnable( - IDBTransaction* aTransaction, - nsCOMPtr& aFinishRunnable) -: mTransaction(aTransaction) + already_AddRefed aThreadPool, + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode, + already_AddRefed aFinishCallback) +: mThreadPool(Move(aThreadPool)), + mFinishCallback(aFinishCallback), + mTransactionId(aTransactionId), + mDatabaseId(aDatabaseId), + mObjectStoreNames(aObjectStoreNames), + mMode(aMode) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aTransaction, "Null pointer!"); - mFinishRunnable.swap(aFinishRunnable); } -NS_IMPL_ISUPPORTS(FinishTransactionRunnable, nsIRunnable) +NS_IMPL_ISUPPORTS_INHERITED0(TransactionThreadPool::FinishTransactionRunnable, + nsRunnable) NS_IMETHODIMP +TransactionThreadPool:: FinishTransactionRunnable::Run() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(mThreadPool); + mThreadPool->AssertIsOnOwningThread(); - PROFILER_MAIN_THREAD_LABEL("FinishTransactionRunnable", "Run", - js::ProfileEntry::Category::STORAGE); + PROFILER_LABEL("IndexedDB", + "TransactionThreadPool::FinishTransactionRunnable::Run", + js::ProfileEntry::Category::STORAGE); - if (!gThreadPool) { - NS_ERROR("Running after shutdown!"); - return NS_ERROR_FAILURE; + nsRefPtr threadPool; + mThreadPool.swap(threadPool); + + nsRefPtr callback; + mFinishCallback.swap(callback); + + if (callback) { + callback->TransactionFinishedBeforeUnblock(); } - gThreadPool->FinishTransaction(mTransaction); + threadPool->FinishTransaction(mTransactionId, + mDatabaseId, + mObjectStoreNames, + mMode); - if (mFinishRunnable) { - mFinishRunnable->Run(); - mFinishRunnable = nullptr; + if (callback) { + callback->TransactionFinishedAfterUnblock(); } return NS_OK; } -#ifdef MOZ_ENABLE_PROFILER_SPS +#ifdef BUILD_THREADPOOL_LISTENER NS_IMPL_ISUPPORTS(TransactionThreadPoolListener, nsIThreadPoolListener) @@ -672,8 +985,24 @@ NS_IMETHODIMP TransactionThreadPoolListener::OnThreadCreated() { MOZ_ASSERT(!NS_IsMainThread()); + +#ifdef MOZ_ENABLE_PROFILER_SPS char aLocal; profiler_register_thread("IndexedDB Transaction", &aLocal); +#endif // MOZ_ENABLE_PROFILER_SPS + +#ifdef DEBUG + if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { + NS_WARNING("TransactionThreadPool thread debugging enabled, priority has " + "been modified!"); + nsCOMPtr thread = + do_QueryInterface(NS_GetCurrentThread()); + MOZ_ASSERT(thread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->SetPriority(kDEBUGThreadPriority))); + } +#endif // DEBUG + return NS_OK; } @@ -681,8 +1010,16 @@ NS_IMETHODIMP TransactionThreadPoolListener::OnThreadShuttingDown() { MOZ_ASSERT(!NS_IsMainThread()); + +#ifdef MOZ_ENABLE_PROFILER_SPS profiler_unregister_thread(); +#endif // MOZ_ENABLE_PROFILER_SPS + return NS_OK; } -#endif // MOZ_ENABLE_PROFILER_SPS +#endif // BUILD_THREADPOOL_LISTENER + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla diff --git a/dom/indexedDB/TransactionThreadPool.h b/dom/indexedDB/TransactionThreadPool.h index 4e90cc186fda..7ed275928e28 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -7,145 +7,96 @@ #ifndef mozilla_dom_indexeddb_transactionthreadpool_h__ #define mozilla_dom_indexeddb_transactionthreadpool_h__ -// Only meant to be included in IndexedDB source files, not exported. -#include "IndexedDatabase.h" - -#include "nsIObserver.h" -#include "nsIRunnable.h" - -#include "mozilla/Monitor.h" +#include "mozilla/Attributes.h" +#include "nsAutoPtr.h" #include "nsClassHashtable.h" +#include "nsCOMPtr.h" #include "nsHashKeys.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" -#include "IDBTransaction.h" - +class nsIEventTarget; +class nsIRunnable; class nsIThreadPool; -BEGIN_INDEXEDDB_NAMESPACE +namespace mozilla { +namespace dom { +namespace indexedDB { -class FinishTransactionRunnable; -class QueuedDispatchInfo; - -class TransactionThreadPool +class TransactionThreadPool MOZ_FINAL { - friend class nsAutoPtr; + class FinishTransactionRunnable; friend class FinishTransactionRunnable; -public: - // returns a non-owning ref! - static TransactionThreadPool* GetOrCreate(); - - // returns a non-owning ref! - static TransactionThreadPool* Get(); - - static void Shutdown(); - - nsresult Dispatch(IDBTransaction* aTransaction, - nsIRunnable* aRunnable, - bool aFinish, - nsIRunnable* aFinishRunnable); - - void WaitForDatabasesToComplete(nsTArray >& aDatabases, - nsIRunnable* aCallback); - - // Abort all transactions, unless they are already in the process of being - // committed, for aDatabase. - void AbortTransactionsForDatabase(IDBDatabase* aDatabase); - - // Returns true if there are running or pending transactions for aDatabase. - bool HasTransactionsForDatabase(IDBDatabase* aDatabase); - -protected: - class TransactionQueue MOZ_FINAL : public nsIRunnable - { - public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - explicit TransactionQueue(IDBTransaction* aTransaction); - - void Unblock(); - - void Dispatch(nsIRunnable* aRunnable); - - void Finish(nsIRunnable* aFinishRunnable); - - private: - ~TransactionQueue() {} - - mozilla::Monitor mMonitor; - IDBTransaction* mTransaction; - nsAutoTArray, 10> mQueue; - nsCOMPtr mFinishRunnable; - bool mShouldFinish; - }; - + class TransactionQueue; friend class TransactionQueue; - struct TransactionInfo - { - explicit TransactionInfo(IDBTransaction* aTransaction) - { - MOZ_COUNT_CTOR(TransactionInfo); + struct DatabaseTransactionInfo; + struct DatabasesCompleteCallback; + struct TransactionInfo; + struct TransactionInfoPair; - transaction = aTransaction; - queue = new TransactionQueue(aTransaction); - } + nsCOMPtr mThreadPool; + nsCOMPtr mOwningThread; - ~TransactionInfo() - { - MOZ_COUNT_DTOR(TransactionInfo); - } + nsClassHashtable + mTransactionsInProgress; - nsRefPtr transaction; - nsRefPtr queue; - nsTHashtable > blockedOn; - nsTHashtable > blocking; - }; + nsTArray> mCompleteCallbacks; - struct TransactionInfoPair - { - TransactionInfoPair() - : lastBlockingReads(nullptr) - { - MOZ_COUNT_CTOR(TransactionInfoPair); - } + uint64_t mNextTransactionId; + bool mShutdownRequested; + bool mShutdownComplete; - ~TransactionInfoPair() - { - MOZ_COUNT_DTOR(TransactionInfoPair); - } - // Multiple reading transactions can block future writes. - nsTArray lastBlockingWrites; - // But only a single writing transaction can block future reads. - TransactionInfo* lastBlockingReads; - }; +public: + class FinishCallback; - struct DatabaseTransactionInfo - { - DatabaseTransactionInfo() - { - MOZ_COUNT_CTOR(DatabaseTransactionInfo); - } + static already_AddRefed Create(); - ~DatabaseTransactionInfo() - { - MOZ_COUNT_DTOR(DatabaseTransactionInfo); - } + uint64_t NextTransactionId(); - typedef nsClassHashtable, TransactionInfo > - TransactionHashtable; - TransactionHashtable transactions; - nsClassHashtable blockingTransactions; - }; + void Dispatch(uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode, + nsIRunnable* aRunnable, + bool aFinish, + FinishCallback* aFinishCallback); + void Dispatch(uint64_t aTransactionId, + const nsACString& aDatabaseId, + nsIRunnable* aRunnable, + bool aFinish, + FinishCallback* aFinishCallback); + + void WaitForDatabasesToComplete(nsTArray& aDatabaseIds, + nsIRunnable* aCallback); + + // Returns true if there are running or pending transactions for aDatabase. + bool HasTransactionsForDatabase(const nsACString& aDatabaseId); + + NS_INLINE_DECL_REFCOUNTING(TransactionThreadPool) + + void ShutdownAndSpin(); + void ShutdownAsync(); + + bool HasCompletedShutdown() const; + + void AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: static PLDHashOperator - CollectTransactions(IDBTransaction* aKey, + CollectTransactions(const uint64_t& aTransactionId, TransactionInfo* aValue, void* aUserArg); static PLDHashOperator - FindTransaction(IDBTransaction* aKey, + FindTransaction(const uint64_t& aTransactionId, TransactionInfo* aValue, void* aUserArg); @@ -153,32 +104,59 @@ protected: MaybeUnblockTransaction(nsPtrHashKey* aKey, void* aUserArg); - struct DatabasesCompleteCallback - { - nsTArray > mDatabases; - nsCOMPtr mCallback; - }; - TransactionThreadPool(); + + // Reference counted. ~TransactionThreadPool(); nsresult Init(); - nsresult Cleanup(); + void Cleanup(); - void FinishTransaction(IDBTransaction* aTransaction); + void FinishTransaction(uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode); - TransactionQueue& GetQueueForTransaction(IDBTransaction* aTransaction); + TransactionQueue* GetQueueForTransaction(uint64_t aTransactionId, + const nsACString& aDatabaseId); - bool MaybeFireCallback(DatabasesCompleteCallback aCallback); + TransactionQueue& GetQueueForTransaction( + uint64_t aTransactionId, + const nsACString& aDatabaseId, + const nsTArray& aObjectStoreNames, + uint16_t aMode); - nsCOMPtr mThreadPool; + bool MaybeFireCallback(DatabasesCompleteCallback* aCallback); - nsClassHashtable - mTransactionsInProgress; - - nsTArray mCompleteCallbacks; + void CleanupAsync(); }; -END_INDEXEDDB_NAMESPACE +class NS_NO_VTABLE TransactionThreadPool::FinishCallback +{ +public: + NS_IMETHOD_(MozExternalRefCountType) + AddRef() = 0; + + NS_IMETHOD_(MozExternalRefCountType) + Release() = 0; + + // Called on the owning thread before any additional transactions are + // unblocked. + virtual void + TransactionFinishedBeforeUnblock() = 0; + + // Called on the owning thread after additional transactions may have been + // unblocked. + virtual void + TransactionFinishedAfterUnblock() = 0; + +protected: + FinishCallback() + { } +}; + +} // namespace indexedDB +} // namespace dom +} // namespace mozilla #endif // mozilla_dom_indexeddb_transactionthreadpool_h__ diff --git a/dom/indexedDB/ipc/IndexedDBChild.cpp b/dom/indexedDB/ipc/IndexedDBChild.cpp deleted file mode 100644 index 5f2754cd9ce5..000000000000 --- a/dom/indexedDB/ipc/IndexedDBChild.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "base/basictypes.h" - -#include "IndexedDBChild.h" - -#include "nsIInputStream.h" - -#include "mozilla/Assertions.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/quota/Client.h" -#include "mozilla/dom/quota/QuotaManager.h" - -#include "AsyncConnectionHelper.h" -#include "DatabaseInfo.h" -#include "IDBEvents.h" -#include "IDBFactory.h" -#include "IDBIndex.h" -#include "IDBObjectStore.h" -#include "IDBTransaction.h" - -USING_INDEXEDDB_NAMESPACE - -using namespace mozilla::dom; -using mozilla::dom::quota::Client; -using mozilla::dom::quota::QuotaManager; - -namespace { - -class IPCOpenDatabaseHelper : public AsyncConnectionHelper -{ -public: - IPCOpenDatabaseHelper(IDBDatabase* aDatabase, IDBOpenDBRequest* aRequest) - : AsyncConnectionHelper(aDatabase, aRequest) - { - MOZ_ASSERT(aRequest); - } - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual nsresult - OnSuccess() MOZ_OVERRIDE - { - static_cast(mRequest.get())->SetTransaction(nullptr); - return AsyncConnectionHelper::OnSuccess(); - } - - virtual void - OnError() MOZ_OVERRIDE - { - static_cast(mRequest.get())->SetTransaction(nullptr); - AsyncConnectionHelper::OnError(); - } - - virtual nsresult - DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; -}; - -class IPCSetVersionHelper : public AsyncConnectionHelper -{ - nsRefPtr mOpenRequest; - uint64_t mOldVersion; - uint64_t mRequestedVersion; - -public: - IPCSetVersionHelper(IDBTransaction* aTransaction, IDBOpenDBRequest* aRequest, - uint64_t aOldVersion, uint64_t aRequestedVersion) - : AsyncConnectionHelper(aTransaction, aRequest), - mOpenRequest(aRequest), mOldVersion(aOldVersion), - mRequestedVersion(aRequestedVersion) - { - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(aRequest); - } - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; - - virtual already_AddRefed - CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) MOZ_OVERRIDE; - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; -}; - -class IPCDeleteDatabaseHelper : public AsyncConnectionHelper -{ -public: - explicit IPCDeleteDatabaseHelper(IDBRequest* aRequest) - : AsyncConnectionHelper(static_cast(nullptr), aRequest) - { } - - virtual nsresult UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) - MOZ_OVERRIDE; - - virtual ChildProcessSendResult - SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE; - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) MOZ_OVERRIDE; - - virtual nsresult - DoDatabaseWork(mozIStorageConnection* aConnection) MOZ_OVERRIDE; -}; - -class VersionChangeRunnable : public nsRunnable -{ - nsRefPtr mDatabase; - uint64_t mOldVersion; - uint64_t mNewVersion; - -public: - VersionChangeRunnable(IDBDatabase* aDatabase, const uint64_t& aOldVersion, - const uint64_t& aNewVersion) - : mDatabase(aDatabase), mOldVersion(aOldVersion), mNewVersion(aNewVersion) - { - MOZ_ASSERT(aDatabase); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - if (mDatabase->IsClosed()) { - return NS_OK; - } - - nsRefPtr event = - IDBVersionChangeEvent::Create(mDatabase, mOldVersion, mNewVersion); - MOZ_ASSERT(event); - - bool dummy; - nsresult rv = mDatabase->DispatchEvent(event, &dummy); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } -}; - -} // anonymous namespace - -/******************************************************************************* - * IndexedDBChild - ******************************************************************************/ - -IndexedDBChild::IndexedDBChild(ContentChild* aContentChild, - const nsCString& aASCIIOrigin) -: mFactory(nullptr) -, mManagerContent(aContentChild) -, mManagerTab(nullptr) -, mASCIIOrigin(aASCIIOrigin) -#ifdef DEBUG -, mDisconnected(false) -#endif -{ - MOZ_ASSERT(aContentChild); - MOZ_COUNT_CTOR(IndexedDBChild); -} - -IndexedDBChild::IndexedDBChild(TabChild* aTabChild, - const nsCString& aASCIIOrigin) -: mFactory(nullptr) -, mManagerContent(nullptr) -, mManagerTab(aTabChild) -, mASCIIOrigin(aASCIIOrigin) -#ifdef DEBUG -, mDisconnected(false) -#endif -{ - MOZ_ASSERT(aTabChild); - MOZ_COUNT_CTOR(IndexedDBChild); -} - -IndexedDBChild::~IndexedDBChild() -{ - MOZ_COUNT_DTOR(IndexedDBChild); - MOZ_ASSERT(!mFactory); -} - -void -IndexedDBChild::SetFactory(IDBFactory* aFactory) -{ - MOZ_ASSERT(aFactory); - MOZ_ASSERT(!mFactory); - - aFactory->SetActor(this); - mFactory = aFactory; -} - -void -IndexedDBChild::Disconnect() -{ -#ifdef DEBUG - MOZ_ASSERT(!mDisconnected); - mDisconnected = true; -#endif - - const InfallibleTArray& databases = - ManagedPIndexedDBDatabaseChild(); - for (uint32_t i = 0; i < databases.Length(); ++i) { - static_cast(databases[i])->Disconnect(); - } -} - -void -IndexedDBChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mFactory) { - mFactory->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mFactory = nullptr; -#endif - } -} - -PIndexedDBDatabaseChild* -IndexedDBChild::AllocPIndexedDBDatabaseChild( - const nsString& aName, - const uint64_t& aVersion, - const PersistenceType& aPersistenceType) -{ - return new IndexedDBDatabaseChild(aName, aVersion); -} - -bool -IndexedDBChild::DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBDeleteDatabaseRequestChild* -IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild( - const nsString& aName, - const PersistenceType& aPersistenceType) -{ - MOZ_CRASH("Caller is supposed to manually construct a request!"); -} - -bool -IndexedDBChild::DeallocPIndexedDBDeleteDatabaseRequestChild( - PIndexedDBDeleteDatabaseRequestChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBDatabaseChild - ******************************************************************************/ - -IndexedDBDatabaseChild::IndexedDBDatabaseChild(const nsString& aName, - uint64_t aVersion) -: mDatabase(nullptr), mName(aName), mVersion(aVersion) -{ - MOZ_COUNT_CTOR(IndexedDBDatabaseChild); -} - -IndexedDBDatabaseChild::~IndexedDBDatabaseChild() -{ - MOZ_COUNT_DTOR(IndexedDBDatabaseChild); - MOZ_ASSERT(!mDatabase); - MOZ_ASSERT(!mStrongDatabase); -} - -void -IndexedDBDatabaseChild::SetRequest(IDBOpenDBRequest* aRequest) -{ - MOZ_ASSERT(aRequest); - MOZ_ASSERT(!mRequest); - - mRequest = aRequest; -} - -void -IndexedDBDatabaseChild::Disconnect() -{ - const InfallibleTArray& transactions = - ManagedPIndexedDBTransactionChild(); - for (uint32_t i = 0; i < transactions.Length(); ++i) { - static_cast(transactions[i])->Disconnect(); - } -} - -bool -IndexedDBDatabaseChild::EnsureDatabase( - IDBOpenDBRequest* aRequest, - const DatabaseInfoGuts& aDBInfo, - const InfallibleTArray& aOSInfo) -{ - nsCString databaseId; - if (mDatabase) { - databaseId = mDatabase->Id(); - } - else { - QuotaManager::GetStorageId(aDBInfo.persistenceType, aDBInfo.origin, - Client::IDB, aDBInfo.name, databaseId); - } - MOZ_ASSERT(!databaseId.IsEmpty()); - - nsRefPtr dbInfo; - if (DatabaseInfo::Get(databaseId, getter_AddRefs(dbInfo))) { - dbInfo->version = aDBInfo.version; - } - else { - nsRefPtr newInfo = new DatabaseInfo(); - - *static_cast(newInfo.get()) = aDBInfo; - newInfo->id = databaseId; - - if (!DatabaseInfo::Put(newInfo)) { - NS_WARNING("Out of memory!"); - return false; - } - - newInfo.swap(dbInfo); - - // This is more or less copied from IDBFactory::SetDatabaseMetadata. - for (uint32_t i = 0; i < aOSInfo.Length(); i++) { - nsRefPtr newInfo = new ObjectStoreInfo(); - *static_cast(newInfo.get()) = aOSInfo[i]; - - if (!dbInfo->PutObjectStore(newInfo)) { - NS_WARNING("Out of memory!"); - return false; - } - } - } - - if (!mDatabase) { - nsRefPtr database = - IDBDatabase::Create(aRequest, aRequest->Factory(), dbInfo.forget(), - aDBInfo.origin, nullptr, nullptr); - if (!database) { - NS_WARNING("Failed to create database!"); - return false; - } - - database->SetActor(this); - - mDatabase = database; - mStrongDatabase = database.forget(); - } - - return true; -} - -void -IndexedDBDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mDatabase) { - mDatabase->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mDatabase = nullptr; -#endif - } -} - -bool -IndexedDBDatabaseChild::RecvSuccess( - const DatabaseInfoGuts& aDBInfo, - const InfallibleTArray& aOSInfo) -{ -#ifdef DEBUG - { - IndexedDBChild* manager = static_cast(Manager()); - MOZ_ASSERT(aDBInfo.origin == manager->ASCIIOrigin()); - MOZ_ASSERT(aDBInfo.name == mName); - MOZ_ASSERT(!mVersion || aDBInfo.version == mVersion); - } -#endif - - MOZ_ASSERT(mRequest); - - nsRefPtr request; - mRequest.swap(request); - - nsRefPtr openHelper; - mOpenHelper.swap(openHelper); - - if (!EnsureDatabase(request, aDBInfo, aOSInfo)) { - return false; - } - - MOZ_ASSERT(mStrongDatabase); - nsRefPtr database; - mStrongDatabase.swap(database); - - if (openHelper) { - request->Reset(); - } - else { - openHelper = new IPCOpenDatabaseHelper(mDatabase, request); - } - - ImmediateRunEventTarget target; - if (NS_FAILED(openHelper->Dispatch(&target))) { - NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!"); - return false; - } - - return true; -} - -bool -IndexedDBDatabaseChild::RecvError(const nsresult& aRv) -{ - MOZ_ASSERT(mRequest); - - nsRefPtr request; - mRequest.swap(request); - - nsRefPtr database; - mStrongDatabase.swap(database); - - nsRefPtr openHelper; - mOpenHelper.swap(openHelper); - - if (openHelper) { - request->Reset(); - } - else { - openHelper = new IPCOpenDatabaseHelper(nullptr, request); - } - - openHelper->SetError(aRv); - - ImmediateRunEventTarget target; - if (NS_FAILED(openHelper->Dispatch(&target))) { - NS_WARNING("Dispatch of IPCOpenDatabaseHelper failed!"); - return false; - } - - return true; -} - -bool -IndexedDBDatabaseChild::RecvBlocked(const uint64_t& aOldVersion) -{ - MOZ_ASSERT(mRequest); - MOZ_ASSERT(!mDatabase); - - nsCOMPtr runnable = - IDBVersionChangeEvent::CreateBlockedRunnable(mRequest, aOldVersion, mVersion); - - ImmediateRunEventTarget target; - if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { - NS_WARNING("Dispatch of blocked event failed!"); - } - - return true; -} - -bool -IndexedDBDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, - const uint64_t& aNewVersion) -{ - MOZ_ASSERT(mDatabase); - - nsCOMPtr runnable = - new VersionChangeRunnable(mDatabase, aOldVersion, aNewVersion); - - ImmediateRunEventTarget target; - if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { - NS_WARNING("Dispatch of versionchange event failed!"); - } - - return true; -} - -bool -IndexedDBDatabaseChild::RecvInvalidate() -{ - if (mDatabase) { - mDatabase->Invalidate(); - } - return true; -} - -bool -IndexedDBDatabaseChild::RecvPIndexedDBTransactionConstructor( - PIndexedDBTransactionChild* aActor, - const TransactionParams& aParams) -{ - // This only happens when the parent has created a version-change transaction - // for us. - - IndexedDBTransactionChild* actor = - static_cast(aActor); - MOZ_ASSERT(!actor->GetTransaction()); - - MOZ_ASSERT(aParams.type() == - TransactionParams::TVersionChangeTransactionParams); - - const VersionChangeTransactionParams& params = - aParams.get_VersionChangeTransactionParams(); - - const DatabaseInfoGuts& dbInfo = params.dbInfo(); - const InfallibleTArray& osInfo = params.osInfo(); - uint64_t oldVersion = params.oldVersion(); - - MOZ_ASSERT(dbInfo.origin == - static_cast(Manager())->ASCIIOrigin()); - MOZ_ASSERT(dbInfo.name == mName); - MOZ_ASSERT(!mVersion || dbInfo.version == mVersion); - MOZ_ASSERT(!mVersion || oldVersion < mVersion); - - MOZ_ASSERT(mRequest); - MOZ_ASSERT(!mDatabase); - MOZ_ASSERT(!mOpenHelper); - - if (!EnsureDatabase(mRequest, dbInfo, osInfo)) { - return false; - } - - nsRefPtr helper = - new IPCOpenDatabaseHelper(mDatabase, mRequest); - - Sequence storesToOpen; - nsRefPtr transaction = - IDBTransaction::CreateInternal(mDatabase, storesToOpen, - IDBTransaction::VERSION_CHANGE, false, true); - NS_ENSURE_TRUE(transaction, false); - - nsRefPtr versionHelper = - new IPCSetVersionHelper(transaction, mRequest, oldVersion, mVersion); - - mDatabase->EnterSetVersionTransaction(); - mDatabase->mPreviousDatabaseInfo->version = oldVersion; - - actor->SetTransaction(transaction); - - ImmediateRunEventTarget target; - if (NS_FAILED(versionHelper->Dispatch(&target))) { - NS_WARNING("Dispatch of IPCSetVersionHelper failed!"); - return false; - } - - mOpenHelper = helper.forget(); - return true; -} - -PIndexedDBTransactionChild* -IndexedDBDatabaseChild::AllocPIndexedDBTransactionChild( - const TransactionParams& aParams) -{ - MOZ_ASSERT(aParams.type() == - TransactionParams::TVersionChangeTransactionParams); - return new IndexedDBTransactionChild(); -} - -bool -IndexedDBDatabaseChild::DeallocPIndexedDBTransactionChild( - PIndexedDBTransactionChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBTransactionChild - ******************************************************************************/ - -IndexedDBTransactionChild::IndexedDBTransactionChild() -: mTransaction(nullptr) -{ - MOZ_COUNT_CTOR(IndexedDBTransactionChild); -} - -IndexedDBTransactionChild::~IndexedDBTransactionChild() -{ - MOZ_COUNT_DTOR(IndexedDBTransactionChild); - MOZ_ASSERT(!mTransaction); - MOZ_ASSERT(!mStrongTransaction); -} - -void -IndexedDBTransactionChild::SetTransaction(IDBTransaction* aTransaction) -{ - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(!mTransaction); - - aTransaction->SetActor(this); - - mTransaction = aTransaction; - mStrongTransaction = aTransaction; -} - -void -IndexedDBTransactionChild::Disconnect() -{ - const InfallibleTArray& objectStores = - ManagedPIndexedDBObjectStoreChild(); - for (uint32_t i = 0; i < objectStores.Length(); ++i) { - static_cast(objectStores[i])->Disconnect(); - } -} - -void -IndexedDBTransactionChild::FireCompleteEvent(nsresult aRv) -{ - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mStrongTransaction); - - nsRefPtr transaction; - mStrongTransaction.swap(transaction); - - if (transaction->GetMode() == IDBTransaction::VERSION_CHANGE) { - transaction->Database()->ExitSetVersionTransaction(); - } - - nsRefPtr helper = new CommitHelper(transaction, aRv); - - ImmediateRunEventTarget target; - if (NS_FAILED(target.Dispatch(helper, NS_DISPATCH_NORMAL))) { - NS_WARNING("Dispatch of CommitHelper failed!"); - } -} - -void -IndexedDBTransactionChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mStrongTransaction) { - // We're being torn down before we received a complete event from the parent - // so fake one here. - FireCompleteEvent(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - MOZ_ASSERT(!mStrongTransaction); - } - - if (mTransaction) { - mTransaction->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mTransaction = nullptr; -#endif - } -} - -bool -IndexedDBTransactionChild::RecvComplete(const CompleteParams& aParams) -{ - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mStrongTransaction); - - nsresult resultCode; - - switch (aParams.type()) { - case CompleteParams::TCompleteResult: - resultCode = NS_OK; - break; - case CompleteParams::TAbortResult: - resultCode = aParams.get_AbortResult().errorCode(); - if (NS_SUCCEEDED(resultCode)) { - resultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR; - } - break; - - default: - MOZ_CRASH("Unknown union type!"); - } - - FireCompleteEvent(resultCode); - return true; -} - -PIndexedDBObjectStoreChild* -IndexedDBTransactionChild::AllocPIndexedDBObjectStoreChild( - const ObjectStoreConstructorParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct an object store!"); -} - -bool -IndexedDBTransactionChild::DeallocPIndexedDBObjectStoreChild( - PIndexedDBObjectStoreChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBObjectStoreChild - ******************************************************************************/ - -IndexedDBObjectStoreChild::IndexedDBObjectStoreChild( - IDBObjectStore* aObjectStore) -: mObjectStore(aObjectStore) -{ - MOZ_COUNT_CTOR(IndexedDBObjectStoreChild); - aObjectStore->SetActor(this); -} - -IndexedDBObjectStoreChild::~IndexedDBObjectStoreChild() -{ - MOZ_COUNT_DTOR(IndexedDBObjectStoreChild); - MOZ_ASSERT(!mObjectStore); -} - -void -IndexedDBObjectStoreChild::Disconnect() -{ - const InfallibleTArray& requests = - ManagedPIndexedDBRequestChild(); - for (uint32_t i = 0; i < requests.Length(); ++i) { - static_cast(requests[i])->Disconnect(); - } - - const InfallibleTArray& indexes = - ManagedPIndexedDBIndexChild(); - for (uint32_t i = 0; i < indexes.Length(); ++i) { - static_cast(indexes[i])->Disconnect(); - } - - const InfallibleTArray& cursors = - ManagedPIndexedDBCursorChild(); - for (uint32_t i = 0; i < cursors.Length(); ++i) { - static_cast(cursors[i])->Disconnect(); - } -} - -void -IndexedDBObjectStoreChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mObjectStore) { - mObjectStore->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mObjectStore = nullptr; -#endif - } -} - -bool -IndexedDBObjectStoreChild::RecvPIndexedDBCursorConstructor( - PIndexedDBCursorChild* aActor, - const ObjectStoreCursorConstructorParams& aParams) -{ - IndexedDBCursorChild* actor = static_cast(aActor); - - IndexedDBObjectStoreRequestChild* requestActor = - static_cast(aParams.requestChild()); - NS_ASSERTION(requestActor, "Must have an actor here!"); - - nsRefPtr request = requestActor->GetRequest(); - NS_ASSERTION(request, "Must have a request here!"); - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr cursor; - nsresult rv; - - typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType; - - switch (aParams.optionalCloneInfo().type()) { - case CursorUnionType::TSerializedStructuredCloneReadInfo: { - nsTArray blobs; - IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs); - - const SerializedStructuredCloneReadInfo& cloneInfo = - aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo(); - - rv = mObjectStore->OpenCursorFromChildProcess(request, direction, - aParams.key(), cloneInfo, - blobs, - getter_AddRefs(cursor)); - NS_ENSURE_SUCCESS(rv, false); - - MOZ_ASSERT(blobs.IsEmpty(), "Should have swapped blob elements!"); - } break; - - case CursorUnionType::Tvoid_t: - MOZ_ASSERT(aParams.blobsChild().IsEmpty()); - - rv = mObjectStore->OpenCursorFromChildProcess(request, direction, - aParams.key(), - getter_AddRefs(cursor)); - NS_ENSURE_SUCCESS(rv, false); - break; - - default: - MOZ_CRASH("Unknown union type!"); - } - - actor->SetCursor(cursor); - return true; -} - -PIndexedDBRequestChild* -IndexedDBObjectStoreChild::AllocPIndexedDBRequestChild( - const ObjectStoreRequestParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct a request!"); -} - -bool -IndexedDBObjectStoreChild::DeallocPIndexedDBRequestChild( - PIndexedDBRequestChild* aActor) -{ - delete aActor; - return false; -} - -PIndexedDBIndexChild* -IndexedDBObjectStoreChild::AllocPIndexedDBIndexChild( - const IndexConstructorParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct an index!"); -} - -bool -IndexedDBObjectStoreChild::DeallocPIndexedDBIndexChild(PIndexedDBIndexChild* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBCursorChild* -IndexedDBObjectStoreChild::AllocPIndexedDBCursorChild( - const ObjectStoreCursorConstructorParams& aParams) -{ - return new IndexedDBCursorChild(); -} - -bool -IndexedDBObjectStoreChild::DeallocPIndexedDBCursorChild( - PIndexedDBCursorChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBIndexChild - ******************************************************************************/ - -IndexedDBIndexChild::IndexedDBIndexChild(IDBIndex* aIndex) -: mIndex(aIndex) -{ - MOZ_COUNT_CTOR(IndexedDBIndexChild); - aIndex->SetActor(this); -} - -IndexedDBIndexChild::~IndexedDBIndexChild() -{ - MOZ_COUNT_DTOR(IndexedDBIndexChild); - MOZ_ASSERT(!mIndex); -} - -void -IndexedDBIndexChild::Disconnect() -{ - const InfallibleTArray& requests = - ManagedPIndexedDBRequestChild(); - for (uint32_t i = 0; i < requests.Length(); ++i) { - static_cast(requests[i])->Disconnect(); - } - - const InfallibleTArray& cursors = - ManagedPIndexedDBCursorChild(); - for (uint32_t i = 0; i < cursors.Length(); ++i) { - static_cast(cursors[i])->Disconnect(); - } -} - -void -IndexedDBIndexChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mIndex) { - mIndex->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mIndex = nullptr; -#endif - } -} - -bool -IndexedDBIndexChild::RecvPIndexedDBCursorConstructor( - PIndexedDBCursorChild* aActor, - const IndexCursorConstructorParams& aParams) -{ - IndexedDBCursorChild* actor = static_cast(aActor); - - IndexedDBObjectStoreRequestChild* requestActor = - static_cast(aParams.requestChild()); - NS_ASSERTION(requestActor, "Must have an actor here!"); - - nsRefPtr request = requestActor->GetRequest(); - NS_ASSERTION(request, "Must have a request here!"); - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr cursor; - nsresult rv; - - typedef ipc::OptionalStructuredCloneReadInfo CursorUnionType; - - switch (aParams.optionalCloneInfo().type()) { - case CursorUnionType::TSerializedStructuredCloneReadInfo: { - nsTArray blobs; - IDBObjectStore::ConvertActorsToBlobs(aParams.blobsChild(), blobs); - - const SerializedStructuredCloneReadInfo& cloneInfo = - aParams.optionalCloneInfo().get_SerializedStructuredCloneReadInfo(); - - rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(), - aParams.objectKey(), cloneInfo, - blobs, - getter_AddRefs(cursor)); - NS_ENSURE_SUCCESS(rv, false); - } break; - - case CursorUnionType::Tvoid_t: - MOZ_ASSERT(aParams.blobsChild().IsEmpty()); - - rv = mIndex->OpenCursorFromChildProcess(request, direction, aParams.key(), - aParams.objectKey(), - getter_AddRefs(cursor)); - NS_ENSURE_SUCCESS(rv, false); - break; - - default: - MOZ_CRASH("Unknown union type!"); - } - - actor->SetCursor(cursor); - return true; -} - -PIndexedDBRequestChild* -IndexedDBIndexChild::AllocPIndexedDBRequestChild(const IndexRequestParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct a request!"); -} - -bool -IndexedDBIndexChild::DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBCursorChild* -IndexedDBIndexChild::AllocPIndexedDBCursorChild( - const IndexCursorConstructorParams& aParams) -{ - return new IndexedDBCursorChild(); -} - -bool -IndexedDBIndexChild::DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBCursorChild - ******************************************************************************/ - -IndexedDBCursorChild::IndexedDBCursorChild() -: mCursor(nullptr) -{ - MOZ_COUNT_CTOR(IndexedDBCursorChild); -} - -IndexedDBCursorChild::~IndexedDBCursorChild() -{ - MOZ_COUNT_DTOR(IndexedDBCursorChild); - MOZ_ASSERT(!mCursor); - MOZ_ASSERT(!mStrongCursor); -} - -void -IndexedDBCursorChild::SetCursor(IDBCursor* aCursor) -{ - MOZ_ASSERT(aCursor); - MOZ_ASSERT(!mCursor); - - aCursor->SetActor(this); - - mCursor = aCursor; - mStrongCursor = aCursor; -} - -void -IndexedDBCursorChild::Disconnect() -{ - const InfallibleTArray& requests = - ManagedPIndexedDBRequestChild(); - for (uint32_t i = 0; i < requests.Length(); ++i) { - static_cast(requests[i])->Disconnect(); - } -} - -void -IndexedDBCursorChild::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mCursor) { - mCursor->SetActor(static_cast(nullptr)); -#ifdef DEBUG - mCursor = nullptr; -#endif - } -} - -PIndexedDBRequestChild* -IndexedDBCursorChild::AllocPIndexedDBRequestChild(const CursorRequestParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct a request!"); -} - -bool -IndexedDBCursorChild::DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBRequestChildBase - ******************************************************************************/ - -IndexedDBRequestChildBase::IndexedDBRequestChildBase( - AsyncConnectionHelper* aHelper) -: mHelper(aHelper) -{ - MOZ_COUNT_CTOR(IndexedDBRequestChildBase); -} - -IndexedDBRequestChildBase::~IndexedDBRequestChildBase() -{ - MOZ_COUNT_DTOR(IndexedDBRequestChildBase); -} - -IDBRequest* -IndexedDBRequestChildBase::GetRequest() const -{ - return mHelper ? mHelper->GetRequest() : nullptr; -} - -void -IndexedDBRequestChildBase::Disconnect() -{ - if (mHelper) { - IDBRequest* request = mHelper->GetRequest(); - - if (request->IsPending()) { - request->SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - IDBTransaction* transaction = mHelper->GetTransaction(); - if (transaction) { - transaction->OnRequestDisconnected(); - } - } - } -} - -bool -IndexedDBRequestChildBase::Recv__delete__(const ResponseValue& aResponse) -{ - MOZ_CRASH("This should be overridden!"); -} - -/******************************************************************************* - * IndexedDBObjectStoreRequestChild - ******************************************************************************/ - -IndexedDBObjectStoreRequestChild::IndexedDBObjectStoreRequestChild( - AsyncConnectionHelper* aHelper, - IDBObjectStore* aObjectStore, - RequestType aRequestType) -: IndexedDBRequestChildBase(aHelper), mObjectStore(aObjectStore), - mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBObjectStoreRequestChild); - MOZ_ASSERT(aHelper); - MOZ_ASSERT(aObjectStore); - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBObjectStoreRequestChild::~IndexedDBObjectStoreRequestChild() -{ - MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestChild); -} - -bool -IndexedDBObjectStoreRequestChild::Recv__delete__(const ResponseValue& aResponse) -{ - switch (aResponse.type()) { - case ResponseValue::Tnsresult: - break; - case ResponseValue::TGetResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); - break; - case ResponseValue::TGetAllResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); - break; - case ResponseValue::TGetAllKeysResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); - break; - case ResponseValue::TAddResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams); - break; - case ResponseValue::TPutResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams); - break; - case ResponseValue::TDeleteResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TDeleteParams); - break; - case ResponseValue::TClearResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams); - break; - case ResponseValue::TCountResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); - break; - case ResponseValue::TOpenCursorResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams || - mRequestType == ParamsUnionType::TOpenKeyCursorParams); - break; - - default: - MOZ_CRASH("Received invalid response parameters!"); - } - - nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -/******************************************************************************* - * IndexedDBIndexRequestChild - ******************************************************************************/ - -IndexedDBIndexRequestChild::IndexedDBIndexRequestChild( - AsyncConnectionHelper* aHelper, - IDBIndex* aIndex, - RequestType aRequestType) -: IndexedDBRequestChildBase(aHelper), mIndex(aIndex), mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBIndexRequestChild); - MOZ_ASSERT(aHelper); - MOZ_ASSERT(aIndex); - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBIndexRequestChild::~IndexedDBIndexRequestChild() -{ - MOZ_COUNT_DTOR(IndexedDBIndexRequestChild); -} - -bool -IndexedDBIndexRequestChild::Recv__delete__(const ResponseValue& aResponse) -{ - switch (aResponse.type()) { - case ResponseValue::Tnsresult: - break; - case ResponseValue::TGetResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); - break; - case ResponseValue::TGetKeyResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetKeyParams); - break; - case ResponseValue::TGetAllResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); - break; - case ResponseValue::TGetAllKeysResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); - break; - case ResponseValue::TCountResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); - break; - case ResponseValue::TOpenCursorResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams || - mRequestType == ParamsUnionType::TOpenKeyCursorParams); - break; - - default: - MOZ_CRASH("Received invalid response parameters!"); - } - - nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -/******************************************************************************* - * IndexedDBCursorRequestChild - ******************************************************************************/ - -IndexedDBCursorRequestChild::IndexedDBCursorRequestChild( - AsyncConnectionHelper* aHelper, - IDBCursor* aCursor, - RequestType aRequestType) -: IndexedDBRequestChildBase(aHelper), mCursor(aCursor), - mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBCursorRequestChild); - MOZ_ASSERT(aHelper); - MOZ_ASSERT(aCursor); - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBCursorRequestChild::~IndexedDBCursorRequestChild() -{ - MOZ_COUNT_DTOR(IndexedDBCursorRequestChild); -} - -bool -IndexedDBCursorRequestChild::Recv__delete__(const ResponseValue& aResponse) -{ - switch (aResponse.type()) { - case ResponseValue::Tnsresult: - break; - case ResponseValue::TContinueResponse: - MOZ_ASSERT(mRequestType == ParamsUnionType::TContinueParams); - break; - - default: - MOZ_CRASH("Received invalid response parameters!"); - } - - nsresult rv = mHelper->OnParentProcessRequestComplete(aResponse); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -/******************************************************************************* - * IndexedDBDeleteDatabaseRequestChild - ******************************************************************************/ - -IndexedDBDeleteDatabaseRequestChild::IndexedDBDeleteDatabaseRequestChild( - IDBFactory* aFactory, - IDBOpenDBRequest* aOpenRequest, - const nsACString& aDatabaseId) -: mFactory(aFactory), mOpenRequest(aOpenRequest), mDatabaseId(aDatabaseId) -{ - MOZ_COUNT_CTOR(IndexedDBDeleteDatabaseRequestChild); - MOZ_ASSERT(aFactory); - MOZ_ASSERT(aOpenRequest); - MOZ_ASSERT(!aDatabaseId.IsEmpty()); -} - -IndexedDBDeleteDatabaseRequestChild::~IndexedDBDeleteDatabaseRequestChild() -{ - MOZ_COUNT_DTOR(IndexedDBDeleteDatabaseRequestChild); -} - -bool -IndexedDBDeleteDatabaseRequestChild::Recv__delete__(const nsresult& aRv) -{ - nsRefPtr helper = - new IPCDeleteDatabaseHelper(mOpenRequest); - - if (NS_SUCCEEDED(aRv)) { - DatabaseInfo::Remove(mDatabaseId); - } - else { - helper->SetError(aRv); - } - - ImmediateRunEventTarget target; - if (NS_FAILED(helper->Dispatch(&target))) { - NS_WARNING("Dispatch of IPCSetVersionHelper failed!"); - return false; - } - - return true; -} - -bool -IndexedDBDeleteDatabaseRequestChild::RecvBlocked( - const uint64_t& aCurrentVersion) -{ - MOZ_ASSERT(mOpenRequest); - - nsCOMPtr runnable = - IDBVersionChangeEvent::CreateBlockedRunnable(mOpenRequest, - aCurrentVersion, 0); - - ImmediateRunEventTarget target; - if (NS_FAILED(target.Dispatch(runnable, NS_DISPATCH_NORMAL))) { - NS_WARNING("Dispatch of blocked event failed!"); - } - - return true; -} - -/******************************************************************************* - * Helpers - ******************************************************************************/ - -nsresult -IPCOpenDatabaseHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; -} - -AsyncConnectionHelper::ChildProcessSendResult -IPCOpenDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - MOZ_CRASH("Don't call me!"); -} - -nsresult -IPCOpenDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - MOZ_CRASH("Don't call me!"); -} - -nsresult -IPCOpenDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) -{ - return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), - aVal); -} - -nsresult -IPCSetVersionHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; -} - -AsyncConnectionHelper::ChildProcessSendResult -IPCSetVersionHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - MOZ_CRASH("Don't call me!"); -} - -nsresult -IPCSetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - MOZ_CRASH("Don't call me!"); -} - -already_AddRefed -IPCSetVersionHelper::CreateSuccessEvent(mozilla::dom::EventTarget* aOwner) -{ - return IDBVersionChangeEvent::CreateUpgradeNeeded(aOwner, - mOldVersion, - mRequestedVersion); -} - -nsresult -IPCSetVersionHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) -{ - mOpenRequest->SetTransaction(mTransaction); - - return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, mDatabase), - aVal); -} - -nsresult -IPCDeleteDatabaseHelper::UnpackResponseFromParentProcess( - const ResponseValue& aResponseValue) -{ - MOZ_CRASH("Don't call me!"); -} - -AsyncConnectionHelper::ChildProcessSendResult -IPCDeleteDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode) -{ - MOZ_CRASH("Don't call me!"); -} - -nsresult -IPCDeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle aVal) -{ - aVal.setUndefined(); - return NS_OK; -} - -nsresult -IPCDeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection) -{ - MOZ_CRASH("Don't call me!"); -} diff --git a/dom/indexedDB/ipc/IndexedDBChild.h b/dom/indexedDB/ipc/IndexedDBChild.h deleted file mode 100644 index a003a0402ac1..000000000000 --- a/dom/indexedDB/ipc/IndexedDBChild.h +++ /dev/null @@ -1,457 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_ipc_indexeddbchild_h__ -#define mozilla_dom_indexeddb_ipc_indexeddbchild_h__ - -#include "mozilla/Attributes.h" -#include "mozilla/DebugOnly.h" - -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "mozilla/dom/indexedDB/PIndexedDBChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBCursorChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBDatabaseChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBDeleteDatabaseRequestChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBIndexChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBObjectStoreChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBRequestChild.h" -#include "mozilla/dom/indexedDB/PIndexedDBTransactionChild.h" - -namespace mozilla { -namespace dom { -class ContentChild; -class TabChild; -} // dom -} // mozilla - -BEGIN_INDEXEDDB_NAMESPACE - -class AsyncConnectionHelper; -class IDBCursor; -class IDBFactory; -class IDBIndex; -class IDBOpenDBRequest; -class IDBRequest; -class IDBTransactionListener; - -/******************************************************************************* - * IndexedDBChild - ******************************************************************************/ - -class IndexedDBChild : public PIndexedDBChild -{ - IDBFactory* mFactory; - ContentChild* mManagerContent; - TabChild* mManagerTab; - - nsCString mASCIIOrigin; - -#ifdef DEBUG - bool mDisconnected; -#endif - -public: - IndexedDBChild(ContentChild* aContentChild, const nsCString& aASCIIOrigin); - IndexedDBChild(TabChild* aTabChild, const nsCString& aASCIIOrigin); - virtual ~IndexedDBChild(); - - const nsCString& - ASCIIOrigin() const - { - return mASCIIOrigin; - } - - ContentChild* - GetManagerContent() const - { - return mManagerContent; - } - - TabChild* - GetManagerTab() const - { - return mManagerTab; - } - - void - SetFactory(IDBFactory* aFactory); - - void - Disconnect(); - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PIndexedDBDatabaseChild* - AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBDeleteDatabaseRequestChild* - AllocPIndexedDBDeleteDatabaseRequestChild( - const nsString& aName, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBDeleteDatabaseRequestChild( - PIndexedDBDeleteDatabaseRequestChild* aActor) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBDatabaseChild - ******************************************************************************/ - -class IndexedDBDatabaseChild : public PIndexedDBDatabaseChild -{ - IDBDatabase* mDatabase; - nsString mName; - uint64_t mVersion; - - nsRefPtr mRequest; - nsRefPtr mOpenHelper; - - // Only used during version change transactions and blocked events. - nsRefPtr mStrongDatabase; - -public: - IndexedDBDatabaseChild(const nsString& aName, uint64_t aVersion); - virtual ~IndexedDBDatabaseChild(); - - void - SetRequest(IDBOpenDBRequest* aRequest); - - void - Disconnect(); - -protected: - bool - EnsureDatabase(IDBOpenDBRequest* aRequest, - const DatabaseInfoGuts& aDBInfo, - const InfallibleTArray& aOSInfo); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvSuccess(const DatabaseInfoGuts& aDBInfo, - const InfallibleTArray& aOSInfo) - MOZ_OVERRIDE; - - virtual bool - RecvError(const nsresult& aRv) MOZ_OVERRIDE; - - virtual bool - RecvBlocked(const uint64_t& aOldVersion) MOZ_OVERRIDE; - - virtual bool - RecvVersionChange(const uint64_t& aOldVersion, const uint64_t& aNewVersion) - MOZ_OVERRIDE; - - virtual bool - RecvInvalidate() MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBTransactionConstructor(PIndexedDBTransactionChild* aActor, - const TransactionParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBTransactionChild* - AllocPIndexedDBTransactionChild(const TransactionParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBTransactionChild(PIndexedDBTransactionChild* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBTransactionChild - ******************************************************************************/ - -class IndexedDBTransactionChild : public PIndexedDBTransactionChild -{ - IDBTransaction* mTransaction; - - nsRefPtr mStrongTransaction; - nsRefPtr mTransactionListener; - -public: - IndexedDBTransactionChild(); - virtual ~IndexedDBTransactionChild(); - - void - SetTransaction(IDBTransaction* aTransaction); - - IDBTransaction* - GetTransaction() const - { - return mTransaction; - } - - void - Disconnect(); - -protected: - void - FireCompleteEvent(nsresult aRv); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvComplete(const CompleteParams& aParams) MOZ_OVERRIDE; - - virtual PIndexedDBObjectStoreChild* - AllocPIndexedDBObjectStoreChild(const ObjectStoreConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBObjectStoreChild(PIndexedDBObjectStoreChild* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBObjectStoreChild - ******************************************************************************/ - -class IndexedDBObjectStoreChild : public PIndexedDBObjectStoreChild -{ - IDBObjectStore* mObjectStore; - -public: - explicit IndexedDBObjectStoreChild(IDBObjectStore* aObjectStore); - virtual ~IndexedDBObjectStoreChild(); - - void - Disconnect(); - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBCursorConstructor( - PIndexedDBCursorChild* aActor, - const ObjectStoreCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBRequestChild* - AllocPIndexedDBRequestChild(const ObjectStoreRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBIndexChild* - AllocPIndexedDBIndexChild(const IndexConstructorParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBIndexChild(PIndexedDBIndexChild* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBCursorChild* - AllocPIndexedDBCursorChild(const ObjectStoreCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBIndexChild - ******************************************************************************/ - -class IndexedDBIndexChild : public PIndexedDBIndexChild -{ - IDBIndex* mIndex; - -public: - explicit IndexedDBIndexChild(IDBIndex* aIndex); - virtual ~IndexedDBIndexChild(); - - void - Disconnect(); - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBCursorConstructor(PIndexedDBCursorChild* aActor, - const IndexCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBRequestChild* - AllocPIndexedDBRequestChild(const IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBCursorChild* - AllocPIndexedDBCursorChild(const IndexCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBCursorChild(PIndexedDBCursorChild* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBCursorChild - ******************************************************************************/ - -class IndexedDBCursorChild : public PIndexedDBCursorChild -{ - IDBCursor* mCursor; - - nsRefPtr mStrongCursor; - -public: - IndexedDBCursorChild(); - virtual ~IndexedDBCursorChild(); - - void - SetCursor(IDBCursor* aCursor); - - already_AddRefed - ForgetStrongCursor() - { - return mStrongCursor.forget(); - } - - void - Disconnect(); - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PIndexedDBRequestChild* - AllocPIndexedDBRequestChild(const CursorRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestChild(PIndexedDBRequestChild* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBRequestChildBase - ******************************************************************************/ - -class IndexedDBRequestChildBase : public PIndexedDBRequestChild -{ -protected: - nsRefPtr mHelper; - -public: - IDBRequest* - GetRequest() const; - - void - Disconnect(); - -protected: - explicit IndexedDBRequestChildBase(AsyncConnectionHelper* aHelper); - virtual ~IndexedDBRequestChildBase(); - - virtual bool - Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBObjectStoreRequestChild - ******************************************************************************/ - -class IndexedDBObjectStoreRequestChild : public IndexedDBRequestChildBase -{ - nsRefPtr mObjectStore; - - typedef ipc::ObjectStoreRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - -public: - IndexedDBObjectStoreRequestChild(AsyncConnectionHelper* aHelper, - IDBObjectStore* aObjectStore, - RequestType aRequestType); - virtual ~IndexedDBObjectStoreRequestChild(); - -protected: - virtual bool - Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBIndexRequestChild - ******************************************************************************/ - -class IndexedDBIndexRequestChild : public IndexedDBRequestChildBase -{ - nsRefPtr mIndex; - - typedef ipc::IndexRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - -public: - IndexedDBIndexRequestChild(AsyncConnectionHelper* aHelper, IDBIndex* aIndex, - RequestType aRequestType); - virtual ~IndexedDBIndexRequestChild(); - -protected: - virtual bool - Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBCursorRequestChild - ******************************************************************************/ - -class IndexedDBCursorRequestChild : public IndexedDBRequestChildBase -{ - nsRefPtr mCursor; - - typedef ipc::CursorRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - -public: - IndexedDBCursorRequestChild(AsyncConnectionHelper* aHelper, - IDBCursor* aCursor, - RequestType aRequestType); - virtual ~IndexedDBCursorRequestChild(); - -protected: - virtual bool - Recv__delete__(const ResponseValue& aResponse) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBDeleteDatabaseRequestChild - ******************************************************************************/ - -class IndexedDBDeleteDatabaseRequestChild : - public PIndexedDBDeleteDatabaseRequestChild -{ - nsRefPtr mFactory; - nsRefPtr mOpenRequest; - nsCString mDatabaseId; - -public: - IndexedDBDeleteDatabaseRequestChild(IDBFactory* aFactory, - IDBOpenDBRequest* aOpenRequest, - const nsACString& aDatabaseId); - virtual ~IndexedDBDeleteDatabaseRequestChild(); - -protected: - virtual bool - Recv__delete__(const nsresult& aRv) MOZ_OVERRIDE; - - virtual bool - RecvBlocked(const uint64_t& aCurrentVersion) MOZ_OVERRIDE; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_ipc_indexeddbchild_h__ diff --git a/dom/indexedDB/ipc/IndexedDBParams.ipdlh b/dom/indexedDB/ipc/IndexedDBParams.ipdlh deleted file mode 100644 index d39e2cad9857..000000000000 --- a/dom/indexedDB/ipc/IndexedDBParams.ipdlh +++ /dev/null @@ -1,76 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; -using mozilla::dom::indexedDB::IDBCursor::Direction from "mozilla/dom/indexedDB/IDBCursor.h"; -using struct mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; - -using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { -namespace ipc { - -struct KeyRange -{ - Key lower; - Key upper; - bool lowerOpen; - bool upperOpen; - bool isOnly; -}; - -union OptionalKeyRange -{ - KeyRange; - void_t; -}; - -struct GetParams -{ - KeyRange keyRange; -}; - -struct GetAllParams -{ - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct GetAllKeysParams -{ - OptionalKeyRange optionalKeyRange; - uint32_t limit; -}; - -struct CountParams -{ - OptionalKeyRange optionalKeyRange; -}; - -struct OpenCursorParams -{ - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -struct OpenKeyCursorParams -{ - OptionalKeyRange optionalKeyRange; - Direction direction; -}; - -union OptionalStructuredCloneReadInfo -{ - SerializedStructuredCloneReadInfo; - void_t; -}; - -} // namespace ipc -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/IndexedDBParent.cpp b/dom/indexedDB/ipc/IndexedDBParent.cpp deleted file mode 100644 index 0265d0327db4..000000000000 --- a/dom/indexedDB/ipc/IndexedDBParent.cpp +++ /dev/null @@ -1,2262 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -#include "IndexedDBParent.h" - -#include "nsIDOMEvent.h" -#include "nsIDOMFile.h" -#include "nsIXPConnect.h" - -#include "mozilla/AppProcessChecker.h" -#include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/IDBDatabaseBinding.h" -#include "mozilla/dom/ipc/Blob.h" -#include "mozilla/dom/TabParent.h" -#include "mozilla/unused.h" - -#include "AsyncConnectionHelper.h" -#include "DatabaseInfo.h" -#include "IDBDatabase.h" -#include "IDBEvents.h" -#include "IDBFactory.h" -#include "IDBIndex.h" -#include "IDBKeyRange.h" -#include "IDBObjectStore.h" -#include "IDBTransaction.h" - -#define CHROME_ORIGIN "chrome" -#define PERMISSION_PREFIX "indexedDB-chrome-" -#define PERMISSION_SUFFIX_READ "-read" -#define PERMISSION_SUFFIX_WRITE "-write" - -USING_INDEXEDDB_NAMESPACE - -using namespace mozilla; -using namespace mozilla::dom; - -/******************************************************************************* - * AutoSetCurrentTransaction - ******************************************************************************/ - -AutoSetCurrentTransaction::AutoSetCurrentTransaction( - IDBTransaction* aTransaction) -{ - MOZ_ASSERT(aTransaction); - AsyncConnectionHelper::SetCurrentTransaction(aTransaction); -} - -AutoSetCurrentTransaction::~AutoSetCurrentTransaction() -{ - AsyncConnectionHelper::SetCurrentTransaction(nullptr); -} - -/******************************************************************************* - * IndexedDBParent - ******************************************************************************/ - -IndexedDBParent::IndexedDBParent(ContentParent* aContentParent) -: mManagerContent(aContentParent), mManagerTab(nullptr), mDisconnected(false) -{ - MOZ_COUNT_CTOR(IndexedDBParent); - MOZ_ASSERT(aContentParent); -} - -IndexedDBParent::IndexedDBParent(TabParent* aTabParent) -: mManagerContent(nullptr), mManagerTab(aTabParent), mDisconnected(false) -{ - MOZ_COUNT_CTOR(IndexedDBParent); - MOZ_ASSERT(aTabParent); -} - -IndexedDBParent::~IndexedDBParent() -{ - MOZ_COUNT_DTOR(IndexedDBParent); -} - -void -IndexedDBParent::Disconnect() -{ - if (mDisconnected) { - return; - } - - mDisconnected = true; - - const InfallibleTArray& databases = - ManagedPIndexedDBDatabaseParent(); - for (uint32_t i = 0; i < databases.Length(); ++i) { - static_cast(databases[i])->Disconnect(); - } -} - -bool -IndexedDBParent::CheckReadPermission(const nsAString& aDatabaseName) -{ - NS_NAMED_LITERAL_CSTRING(permission, PERMISSION_SUFFIX_READ); - return CheckPermissionInternal(aDatabaseName, permission); -} - -bool -IndexedDBParent::CheckWritePermission(const nsAString& aDatabaseName) -{ - // Write permission assumes read permission is granted as well. - MOZ_ASSERT(CheckReadPermission(aDatabaseName)); - - NS_NAMED_LITERAL_CSTRING(permission, PERMISSION_SUFFIX_WRITE); - return CheckPermissionInternal(aDatabaseName, permission); -} - -mozilla::ipc::IProtocol* -IndexedDBParent::CloneProtocol(Channel* aChannel, - mozilla::ipc::ProtocolCloneContext* aCtx) -{ - MOZ_ASSERT(mManagerContent != nullptr); - MOZ_ASSERT(mManagerTab == nullptr); - MOZ_ASSERT(!mDisconnected); - MOZ_ASSERT(IndexedDatabaseManager::Get()); - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - - ContentParent* contentParent = aCtx->GetContentParent(); - nsAutoPtr actor(contentParent->AllocPIndexedDBParent()); - if (!actor || !contentParent->RecvPIndexedDBConstructor(actor)) { - return nullptr; - } - return actor.forget(); -} - -bool -IndexedDBParent::CheckPermissionInternal(const nsAString& aDatabaseName, - const nsACString& aPermission) -{ - MOZ_ASSERT(!mASCIIOrigin.IsEmpty()); - MOZ_ASSERT(mManagerContent || mManagerTab); - - if (mASCIIOrigin.EqualsLiteral(CHROME_ORIGIN)) { - nsAutoCString fullPermission = - NS_LITERAL_CSTRING(PERMISSION_PREFIX) + - NS_ConvertUTF16toUTF8(aDatabaseName) + - aPermission; - - if ((mManagerContent && - !AssertAppProcessPermission(mManagerContent, fullPermission.get())) || - (mManagerTab && - !AssertAppProcessPermission(mManagerTab, fullPermission.get()))) { - return false; - } - } - - return true; -} - -void -IndexedDBParent::ActorDestroy(ActorDestroyReason aWhy) -{ - // Nothing really needs to be done here... -} - -bool -IndexedDBParent::RecvPIndexedDBDatabaseConstructor( - PIndexedDBDatabaseParent* aActor, - const nsString& aName, - const uint64_t& aVersion, - const PersistenceType& aPersistenceType) -{ - if (!CheckReadPermission(aName)) { - return false; - } - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mFactory) { - return true; - } - - nsRefPtr request; - nsresult rv = mFactory->OpenInternal(aName, aVersion, aPersistenceType, false, - getter_AddRefs(request)); - NS_ENSURE_SUCCESS(rv, false); - - IndexedDBDatabaseParent* actor = - static_cast(aActor); - - rv = actor->SetOpenRequest(request); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -bool -IndexedDBParent::RecvPIndexedDBDeleteDatabaseRequestConstructor( - PIndexedDBDeleteDatabaseRequestParent* aActor, - const nsString& aName, - const PersistenceType& aPersistenceType) -{ - if (!CheckWritePermission(aName)) { - return false; - } - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mFactory) { - return true; - } - - IndexedDBDeleteDatabaseRequestParent* actor = - static_cast(aActor); - - nsRefPtr request; - - nsresult rv = mFactory->OpenInternal(aName, 0, aPersistenceType, true, - getter_AddRefs(request)); - NS_ENSURE_SUCCESS(rv, false); - - rv = actor->SetOpenRequest(request); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -PIndexedDBDatabaseParent* -IndexedDBParent::AllocPIndexedDBDatabaseParent( - const nsString& aName, - const uint64_t& aVersion, - const PersistenceType& aPersistenceType) -{ - return new IndexedDBDatabaseParent(); -} - -bool -IndexedDBParent::DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBDeleteDatabaseRequestParent* -IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent( - const nsString& aName, - const PersistenceType& aPersistenceType) -{ - return new IndexedDBDeleteDatabaseRequestParent(mFactory); -} - -bool -IndexedDBParent::DeallocPIndexedDBDeleteDatabaseRequestParent( - PIndexedDBDeleteDatabaseRequestParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBDatabaseParent - ******************************************************************************/ - -IndexedDBDatabaseParent::IndexedDBDatabaseParent() -: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()) -{ - MOZ_COUNT_CTOR(IndexedDBDatabaseParent); -} - -IndexedDBDatabaseParent::~IndexedDBDatabaseParent() -{ - MOZ_COUNT_DTOR(IndexedDBDatabaseParent); -} - -nsresult -IndexedDBDatabaseParent::SetOpenRequest(IDBOpenDBRequest* aRequest) -{ - MOZ_ASSERT(aRequest); - MOZ_ASSERT(!mOpenRequest); - - nsresult rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(BLOCKED_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aRequest->EventTarget::AddEventListener(NS_LITERAL_STRING(UPGRADENEEDED_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - mOpenRequest = aRequest; - return NS_OK; -} - -nsresult -IndexedDBDatabaseParent::HandleEvent(nsIDOMEvent* aEvent) -{ - MOZ_ASSERT(aEvent); - - if (IsDisconnected()) { - // We're shutting down, ignore this event. - return NS_OK; - } - - nsString type; - nsresult rv = aEvent->GetType(type); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr target = aEvent->InternalDOMEvent()->GetTarget(); - - if (mDatabase && - SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, - mDatabase))) { - rv = HandleDatabaseEvent(aEvent, type); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - if (mOpenRequest && - SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, - mOpenRequest))) { - rv = HandleRequestEvent(aEvent, type); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - MOZ_CRASH("Unexpected message!"); -} - -void -IndexedDBDatabaseParent::Disconnect() -{ - if (mDatabase) { - mDatabase->DisconnectFromActorParent(); - } -} - -bool -IndexedDBDatabaseParent::CheckWritePermission(const nsAString& aDatabaseName) -{ - IndexedDBParent* manager = static_cast(Manager()); - MOZ_ASSERT(manager); - - return manager->CheckWritePermission(aDatabaseName); -} - -void -IndexedDBDatabaseParent::Invalidate() -{ - MOZ_ASSERT(mDatabase); - - if (!IsDisconnected()) { - mozilla::unused << SendInvalidate(); - } -} - -nsresult -IndexedDBDatabaseParent::HandleRequestEvent(nsIDOMEvent* aEvent, - const nsAString& aType) -{ - MOZ_ASSERT(mOpenRequest); - MOZ_ASSERT(!IsDisconnected()); - - nsresult rv; - - if (aType.EqualsLiteral(ERROR_EVT_STR)) { - nsRefPtr request; - mOpenRequest.swap(request); - - rv = request->GetErrorCode(); - MOZ_ASSERT(NS_FAILED(rv)); - - if (!SendError(rv)) { - return NS_ERROR_FAILURE; - } - - rv = aEvent->PreventDefault(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - if (aType.EqualsLiteral(BLOCKED_EVT_STR)) { - MOZ_ASSERT(!mDatabase); - - nsCOMPtr changeEvent = do_QueryInterface(aEvent); - NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); - - uint64_t oldVersion = changeEvent->OldVersion(); - - if (!SendBlocked(oldVersion)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - AutoSafeJSContext cx; - - ErrorResult error; - JS::Rooted result(cx); - mOpenRequest->GetResult(cx, &result, error); - ENSURE_SUCCESS(error, error.ErrorCode()); - - MOZ_ASSERT(!result.isPrimitive()); - - IDBDatabase *database; - rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database); - if (NS_FAILED(rv)) { - NS_WARNING("Didn't get the object we expected!"); - return rv; - } - - DatabaseInfo* dbInfo = database->Info(); - MOZ_ASSERT(dbInfo); - - nsAutoTArray objectStoreNames; - if (!dbInfo->GetObjectStoreNames(objectStoreNames)) { - MOZ_CRASH("This should never fail!"); - } - - InfallibleTArray objectStoreInfos; - if (!objectStoreNames.IsEmpty()) { - uint32_t length = objectStoreNames.Length(); - - objectStoreInfos.SetCapacity(length); - - for (uint32_t i = 0; i < length; i++) { - ObjectStoreInfo* osInfo = dbInfo->GetObjectStore(objectStoreNames[i]); - MOZ_ASSERT(osInfo); - - objectStoreInfos.AppendElement(*osInfo); - } - } - - if (aType.EqualsLiteral(SUCCESS_EVT_STR)) { - nsRefPtr request; - mOpenRequest.swap(request); - - EventTarget* target = static_cast(database); - -#ifdef DEBUG - { - nsresult rvDEBUG = - target->AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mEventListener, false); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rvDEBUG), "Failed to add error listener!"); - } -#endif - - NS_NAMED_LITERAL_STRING(versionChange, VERSIONCHANGE_EVT_STR); - rv = target->AddEventListener(versionChange, mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - if (!SendSuccess(*dbInfo, objectStoreInfos)) { - return NS_ERROR_FAILURE; - } - - MOZ_ASSERT(!mDatabase || mDatabase == database); - - if (!mDatabase) { - database->SetActor(this); - mDatabase = database; - } - - return NS_OK; - } - - if (aType.EqualsLiteral(UPGRADENEEDED_EVT_STR)) { - MOZ_ASSERT(!mDatabase); - - IDBTransaction* transaction = - AsyncConnectionHelper::GetCurrentTransaction(); - MOZ_ASSERT(transaction); - - if (!CheckWritePermission(database->Name())) { - // If we get here then the child process is either dead or in the process - // of being killed. Abort the transaction now to prevent any changes to - // the database. - ErrorResult rv; - transaction->Abort(rv); - if (rv.Failed()) { - NS_WARNING("Failed to abort transaction!"); - } - return NS_ERROR_FAILURE; - } - - nsCOMPtr changeEvent = do_QueryInterface(aEvent); - NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); - - uint64_t oldVersion = changeEvent->OldVersion(); - - nsAutoPtr actor( - new IndexedDBVersionChangeTransactionParent()); - - rv = actor->SetTransaction(transaction); - NS_ENSURE_SUCCESS(rv, rv); - - VersionChangeTransactionParams versionChangeParams; - versionChangeParams.dbInfo() = *dbInfo; - versionChangeParams.osInfo() = objectStoreInfos; - versionChangeParams.oldVersion() = oldVersion; - - if (!SendPIndexedDBTransactionConstructor(actor.forget(), - versionChangeParams)) { - return NS_ERROR_FAILURE; - } - - database->SetActor(this); - mDatabase = database; - - return NS_OK; - } - - MOZ_CRASH("Unexpected message type!"); -} - -nsresult -IndexedDBDatabaseParent::HandleDatabaseEvent(nsIDOMEvent* aEvent, - const nsAString& aType) -{ - MOZ_ASSERT(mDatabase); - MOZ_ASSERT(!aType.EqualsLiteral(ERROR_EVT_STR), - "Should never get error events in the parent process!"); - MOZ_ASSERT(!IsDisconnected()); - - if (aType.EqualsLiteral(VERSIONCHANGE_EVT_STR)) { - AutoSafeJSContext cx; - NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); - - nsCOMPtr changeEvent = do_QueryInterface(aEvent); - NS_ENSURE_TRUE(changeEvent, NS_ERROR_FAILURE); - - uint64_t oldVersion = changeEvent->OldVersion(); - - Nullable newVersionVal = changeEvent->GetNewVersion(); - - uint64_t newVersion; - if (newVersionVal.IsNull()) { - newVersion = 0; - } - else { - newVersion = newVersionVal.Value(); - } - - if (!SendVersionChange(oldVersion, newVersion)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - MOZ_CRASH("Unexpected message type!"); -} - -void -IndexedDBDatabaseParent::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mDatabase) { - mDatabase->SetActor(static_cast(nullptr)); - mDatabase->InvalidateInternal(/* aIsDead */ true); - } -} - -bool -IndexedDBDatabaseParent::RecvClose(const bool& aUnlinked) -{ - MOZ_ASSERT(mDatabase); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - mDatabase->CloseInternal(aUnlinked); - return true; -} - -bool -IndexedDBDatabaseParent::RecvPIndexedDBTransactionConstructor( - PIndexedDBTransactionParent* aActor, - const TransactionParams& aParams) -{ - MOZ_ASSERT(aParams.type() == - TransactionParams::TNormalTransactionParams); - MOZ_ASSERT(!mOpenRequest); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mDatabase) { - return true; - } - - IndexedDBTransactionParent* actor = - static_cast(aActor); - - const NormalTransactionParams& params = aParams.get_NormalTransactionParams(); - - if (params.mode() != IDBTransaction::READ_ONLY && - !CheckWritePermission(mDatabase->Name())) { - return false; - } - - if (mDatabase->IsClosed()) { - // If the window was navigated then we won't be able to do anything here. - return true; - } - - Sequence storesToOpen; - storesToOpen.AppendElements(params.names()); - - nsRefPtr transaction = - IDBTransaction::Create(mDatabase, storesToOpen, params.mode(), false); - NS_ENSURE_TRUE(transaction, false); - - nsresult rv = actor->SetTransaction(transaction); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -PIndexedDBTransactionParent* -IndexedDBDatabaseParent::AllocPIndexedDBTransactionParent( - const TransactionParams& aParams) -{ - MOZ_ASSERT(aParams.type() == - TransactionParams::TNormalTransactionParams); - return new IndexedDBTransactionParent(); -} - -bool -IndexedDBDatabaseParent::DeallocPIndexedDBTransactionParent( - PIndexedDBTransactionParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBTransactionParent - ******************************************************************************/ - -IndexedDBTransactionParent::IndexedDBTransactionParent() -: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()), - mArtificialRequestCount(false) -{ - MOZ_COUNT_CTOR(IndexedDBTransactionParent); -} - -IndexedDBTransactionParent::~IndexedDBTransactionParent() -{ - MOZ_COUNT_DTOR(IndexedDBTransactionParent); -} - -nsresult -IndexedDBTransactionParent::SetTransaction(IDBTransaction* aTransaction) -{ - MOZ_ASSERT(aTransaction); - MOZ_ASSERT(!mTransaction); - - EventTarget* target = static_cast(aTransaction); - - NS_NAMED_LITERAL_STRING(complete, COMPLETE_EVT_STR); - nsresult rv = target->AddEventListener(complete, mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = target->AddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - aTransaction->OnNewRequest(); - mArtificialRequestCount = true; - - aTransaction->SetActor(this); - - mTransaction = aTransaction; - return NS_OK; -} - -nsresult -IndexedDBTransactionParent::HandleEvent(nsIDOMEvent* aEvent) -{ - MOZ_ASSERT(aEvent); - - if (IsDisconnected()) { - // We're shutting down, ignore this event. - return NS_OK; - } - - nsString type; - nsresult rv = aEvent->GetType(type); - NS_ENSURE_SUCCESS(rv, rv); - - CompleteParams params; - - if (type.EqualsLiteral(COMPLETE_EVT_STR)) { - params = CompleteResult(); - } - else if (type.EqualsLiteral(ABORT_EVT_STR)) { -#ifdef DEBUG - { - nsCOMPtr target = aEvent->InternalDOMEvent()->GetTarget(); - MOZ_ASSERT(SameCOMIdentity(target, NS_ISUPPORTS_CAST(EventTarget*, - mTransaction))); - } -#endif - params = AbortResult(mTransaction->GetAbortCode()); - } - else { - NS_WARNING("Unknown message type!"); - return NS_ERROR_UNEXPECTED; - } - - if (!SendComplete(params)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -void -IndexedDBTransactionParent::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mTransaction) { - if (mArtificialRequestCount) { - // The transaction never completed and now the child side is dead. Abort - // here to be safe. - ErrorResult rv; - mTransaction->Abort(rv); - - mTransaction->OnRequestFinished(); -#ifdef DEBUG - mArtificialRequestCount = false; -#endif - } - mTransaction->SetActor(static_cast(nullptr)); - } -} - -bool -IndexedDBTransactionParent::RecvAbort(const nsresult& aAbortCode) -{ - MOZ_ASSERT(mTransaction); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - mTransaction->Abort(aAbortCode); - return true; -} - -bool -IndexedDBTransactionParent::RecvAllRequestsFinished() -{ - MOZ_ASSERT(mTransaction); - MOZ_ASSERT(mArtificialRequestCount); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - mTransaction->OnRequestFinished(); - mArtificialRequestCount = false; - - return true; -} - -bool -IndexedDBTransactionParent::RecvDeleteObjectStore(const nsString& aName) -{ - MOZ_CRASH("Should be overridden, don't call me!"); -} - -bool -IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor( - PIndexedDBObjectStoreParent* aActor, - const ObjectStoreConstructorParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mTransaction) { - return true; - } - - IndexedDBObjectStoreParent* actor = - static_cast(aActor); - - if (aParams.type() == - ObjectStoreConstructorParams::TGetObjectStoreParams) { - const GetObjectStoreParams& params = aParams.get_GetObjectStoreParams(); - const nsString& name = params.name(); - - nsRefPtr objectStore; - - { - AutoSetCurrentTransaction asct(mTransaction); - - ErrorResult rv; - objectStore = mTransaction->ObjectStore(name, rv); - ENSURE_SUCCESS(rv, false); - - actor->SetObjectStore(objectStore); - } - - objectStore->SetActor(actor); - return true; - } - - if (aParams.type() == - ObjectStoreConstructorParams::TCreateObjectStoreParams) { - MOZ_CRASH("Should be overridden, don't call me!"); - } - - MOZ_CRASH("Unknown param type!"); -} - -PIndexedDBObjectStoreParent* -IndexedDBTransactionParent::AllocPIndexedDBObjectStoreParent( - const ObjectStoreConstructorParams& aParams) -{ - return new IndexedDBObjectStoreParent(); -} - -bool -IndexedDBTransactionParent::DeallocPIndexedDBObjectStoreParent( - PIndexedDBObjectStoreParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBVersionChangeTransactionParent - ******************************************************************************/ - -IndexedDBVersionChangeTransactionParent:: - IndexedDBVersionChangeTransactionParent() -{ - MOZ_COUNT_CTOR(IndexedDBVersionChangeTransactionParent); -} - -IndexedDBVersionChangeTransactionParent:: - ~IndexedDBVersionChangeTransactionParent() -{ - MOZ_COUNT_DTOR(IndexedDBVersionChangeTransactionParent); -} - -bool -IndexedDBVersionChangeTransactionParent::RecvDeleteObjectStore( - const nsString& aName) -{ - MOZ_ASSERT(!mTransaction || - mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mTransaction) { - return true; - } - - if (mTransaction->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return true; - } - - IDBDatabase* db = mTransaction->Database(); - MOZ_ASSERT(db); - - ErrorResult rv; - - { - AutoSetCurrentTransaction asct(mTransaction); - db->DeleteObjectStore(aName, rv); - } - - ENSURE_SUCCESS(rv, false); - return true; -} - -bool -IndexedDBVersionChangeTransactionParent::RecvPIndexedDBObjectStoreConstructor( - PIndexedDBObjectStoreParent* aActor, - const ObjectStoreConstructorParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mTransaction) { - return true; - } - - if (mTransaction->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return true; - } - - IndexedDBObjectStoreParent* actor = - static_cast(aActor); - - if (aParams.type() == - ObjectStoreConstructorParams::TCreateObjectStoreParams) { - MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE); - - const CreateObjectStoreParams& params = - aParams.get_CreateObjectStoreParams(); - - const ObjectStoreInfoGuts& info = params.info(); - - IDBDatabase* db = mTransaction->Database(); - MOZ_ASSERT(db); - - nsRefPtr objectStore; - - ErrorResult rv; - - { - AutoSetCurrentTransaction asct(mTransaction); - - objectStore = db->CreateObjectStoreInternal(mTransaction, info, rv); - } - - ENSURE_SUCCESS(rv, false); - - actor->SetObjectStore(objectStore); - objectStore->SetActor(actor); - return true; - } - - return - IndexedDBTransactionParent::RecvPIndexedDBObjectStoreConstructor(aActor, - aParams); -} - -PIndexedDBObjectStoreParent* -IndexedDBVersionChangeTransactionParent::AllocPIndexedDBObjectStoreParent( - const ObjectStoreConstructorParams& aParams) -{ - if (aParams.type() == - ObjectStoreConstructorParams::TCreateObjectStoreParams || - mTransaction->GetMode() == IDBTransaction::VERSION_CHANGE) { - return new IndexedDBVersionChangeObjectStoreParent(); - } - - return IndexedDBTransactionParent::AllocPIndexedDBObjectStoreParent(aParams); -} - -/******************************************************************************* - * IndexedDBCursorParent - ******************************************************************************/ - -IndexedDBCursorParent::IndexedDBCursorParent(IDBCursor* aCursor) -: mCursor(aCursor) -{ - MOZ_COUNT_CTOR(IndexedDBCursorParent); - MOZ_ASSERT(aCursor); - aCursor->SetActor(this); -} - -IndexedDBCursorParent::~IndexedDBCursorParent() -{ - MOZ_COUNT_DTOR(IndexedDBCursorParent); -} - -bool -IndexedDBCursorParent::IsDisconnected() const -{ - MOZ_ASSERT(mCursor); - return mCursor->Transaction()->GetActorParent()->IsDisconnected(); -} - -void -IndexedDBCursorParent::ActorDestroy(ActorDestroyReason aWhy) -{ - MOZ_ASSERT(mCursor); - mCursor->SetActor(static_cast(nullptr)); -} - -bool -IndexedDBCursorParent::RecvPIndexedDBRequestConstructor( - PIndexedDBRequestParent* aActor, - const CursorRequestParams& aParams) -{ - MOZ_ASSERT(mCursor); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - IndexedDBCursorRequestParent* actor = - static_cast(aActor); - - if (mCursor->Transaction()->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - switch (aParams.type()) { - case CursorRequestParams::TContinueParams: - return actor->Continue(aParams.get_ContinueParams()); - - default: - MOZ_CRASH("Unknown type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -PIndexedDBRequestParent* -IndexedDBCursorParent::AllocPIndexedDBRequestParent( - const CursorRequestParams& aParams) -{ - MOZ_ASSERT(mCursor); - return new IndexedDBCursorRequestParent(mCursor, aParams.type()); -} - -bool -IndexedDBCursorParent::DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBObjectStoreParent - ******************************************************************************/ - -IndexedDBObjectStoreParent::IndexedDBObjectStoreParent() -{ - MOZ_COUNT_CTOR(IndexedDBObjectStoreParent); -} - -IndexedDBObjectStoreParent::~IndexedDBObjectStoreParent() -{ - MOZ_COUNT_DTOR(IndexedDBObjectStoreParent); -} - -void -IndexedDBObjectStoreParent::SetObjectStore(IDBObjectStore* aObjectStore) -{ - // Sadly can't assert aObjectStore here... - MOZ_ASSERT(!mObjectStore); - - mObjectStore = aObjectStore; -} - -void -IndexedDBObjectStoreParent::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mObjectStore) { - mObjectStore->SetActor(static_cast(nullptr)); - } -} - -bool -IndexedDBObjectStoreParent::RecvDeleteIndex(const nsString& aName) -{ - MOZ_CRASH("Should be overridden, don't call me!"); -} - -bool -IndexedDBObjectStoreParent::RecvPIndexedDBRequestConstructor( - PIndexedDBRequestParent* aActor, - const ObjectStoreRequestParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mObjectStore) { - return true; - } - - IndexedDBObjectStoreRequestParent* actor = - static_cast(aActor); - - if (mObjectStore->Transaction()->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - switch (aParams.type()) { - case ObjectStoreRequestParams::TGetParams: - return actor->Get(aParams.get_GetParams()); - - case ObjectStoreRequestParams::TGetAllParams: - return actor->GetAll(aParams.get_GetAllParams()); - - case ObjectStoreRequestParams::TGetAllKeysParams: - return actor->GetAllKeys(aParams.get_GetAllKeysParams()); - - case ObjectStoreRequestParams::TAddParams: - return actor->Add(aParams.get_AddParams()); - - case ObjectStoreRequestParams::TPutParams: - return actor->Put(aParams.get_PutParams()); - - case ObjectStoreRequestParams::TDeleteParams: - return actor->Delete(aParams.get_DeleteParams()); - - case ObjectStoreRequestParams::TClearParams: - return actor->Clear(aParams.get_ClearParams()); - - case ObjectStoreRequestParams::TCountParams: - return actor->Count(aParams.get_CountParams()); - - case ObjectStoreRequestParams::TOpenCursorParams: - return actor->OpenCursor(aParams.get_OpenCursorParams()); - - case ObjectStoreRequestParams::TOpenKeyCursorParams: - return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams()); - - default: - MOZ_CRASH("Unknown type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -bool -IndexedDBObjectStoreParent::RecvPIndexedDBIndexConstructor( - PIndexedDBIndexParent* aActor, - const IndexConstructorParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mObjectStore) { - return true; - } - - IndexedDBIndexParent* actor = static_cast(aActor); - - if (aParams.type() == IndexConstructorParams::TGetIndexParams) { - const GetIndexParams& params = aParams.get_GetIndexParams(); - const nsString& name = params.name(); - - nsRefPtr index; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - index = mObjectStore->Index(name, rv); - ENSURE_SUCCESS(rv, false); - - actor->SetIndex(index); - } - - index->SetActor(actor); - return true; - } - - if (aParams.type() == IndexConstructorParams::TCreateIndexParams) { - MOZ_CRASH("Should be overridden, don't call me!"); - } - - MOZ_CRASH("Unknown param type!"); -} - -PIndexedDBRequestParent* -IndexedDBObjectStoreParent::AllocPIndexedDBRequestParent( - const ObjectStoreRequestParams& aParams) -{ - return new IndexedDBObjectStoreRequestParent(mObjectStore, aParams.type()); -} - -bool -IndexedDBObjectStoreParent::DeallocPIndexedDBRequestParent( - PIndexedDBRequestParent* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBIndexParent* -IndexedDBObjectStoreParent::AllocPIndexedDBIndexParent( - const IndexConstructorParams& aParams) -{ - return new IndexedDBIndexParent(); -} - -bool -IndexedDBObjectStoreParent::DeallocPIndexedDBIndexParent( - PIndexedDBIndexParent* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBCursorParent* -IndexedDBObjectStoreParent::AllocPIndexedDBCursorParent( - const ObjectStoreCursorConstructorParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct a cursor!"); -} - -bool -IndexedDBObjectStoreParent::DeallocPIndexedDBCursorParent( - PIndexedDBCursorParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBVersionChangeObjectStoreParent - ******************************************************************************/ - -IndexedDBVersionChangeObjectStoreParent:: - IndexedDBVersionChangeObjectStoreParent() -{ - MOZ_COUNT_CTOR(IndexedDBVersionChangeObjectStoreParent); -} - -IndexedDBVersionChangeObjectStoreParent:: - ~IndexedDBVersionChangeObjectStoreParent() -{ - MOZ_COUNT_DTOR(IndexedDBVersionChangeObjectStoreParent); -} - -bool -IndexedDBVersionChangeObjectStoreParent::RecvDeleteIndex(const nsString& aName) -{ - MOZ_ASSERT(!mObjectStore || - mObjectStore->Transaction()->GetMode() == - IDBTransaction::VERSION_CHANGE); - - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mObjectStore) { - return true; - } - - if (mObjectStore->Transaction()->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return true; - } - - ErrorResult rv; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - mObjectStore->DeleteIndex(aName, rv); - } - - ENSURE_SUCCESS(rv, false); - return true; -} - -bool -IndexedDBVersionChangeObjectStoreParent::RecvPIndexedDBIndexConstructor( - PIndexedDBIndexParent* aActor, - const IndexConstructorParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mObjectStore) { - return true; - } - - if (mObjectStore->Transaction()->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return true; - } - - IndexedDBIndexParent* actor = static_cast(aActor); - - if (aParams.type() == IndexConstructorParams::TCreateIndexParams) { - MOZ_ASSERT(mObjectStore->Transaction()->GetMode() == - IDBTransaction::VERSION_CHANGE); - - const CreateIndexParams& params = aParams.get_CreateIndexParams(); - const IndexInfo& info = params.info(); - - nsRefPtr index; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - index = mObjectStore->CreateIndexInternal(info, rv); - ENSURE_SUCCESS(rv, false); - } - - actor->SetIndex(index); - index->SetActor(actor); - return true; - } - - return IndexedDBObjectStoreParent::RecvPIndexedDBIndexConstructor(aActor, - aParams); -} - -/******************************************************************************* - * IndexedDBIndexParent - ******************************************************************************/ - -IndexedDBIndexParent::IndexedDBIndexParent() -{ - MOZ_COUNT_CTOR(IndexedDBIndexParent); -} - -IndexedDBIndexParent::~IndexedDBIndexParent() -{ - MOZ_COUNT_DTOR(IndexedDBIndexParent); -} - -void -IndexedDBIndexParent::SetIndex(IDBIndex* aIndex) -{ - MOZ_ASSERT(aIndex); - MOZ_ASSERT(!mIndex); - - mIndex = aIndex; -} - -void -IndexedDBIndexParent::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mIndex) { - mIndex->SetActor(static_cast(nullptr)); - } -} - -bool -IndexedDBIndexParent::RecvPIndexedDBRequestConstructor( - PIndexedDBRequestParent* aActor, - const IndexRequestParams& aParams) -{ - if (IsDisconnected()) { - // We're shutting down, ignore this request. - return true; - } - - if (!mIndex) { - return true; - } - - IndexedDBIndexRequestParent* actor = - static_cast(aActor); - - if (mIndex->ObjectStore()->Transaction()->Database()->IsInvalidated()) { - // If we've invalidated this database in the parent then we should bail out - // now to avoid logic problems that could force-kill the child. - return actor->Send__delete__(actor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - } - - switch (aParams.type()) { - case IndexRequestParams::TGetParams: - return actor->Get(aParams.get_GetParams()); - - case IndexRequestParams::TGetKeyParams: - return actor->GetKey(aParams.get_GetKeyParams()); - - case IndexRequestParams::TGetAllParams: - return actor->GetAll(aParams.get_GetAllParams()); - - case IndexRequestParams::TGetAllKeysParams: - return actor->GetAllKeys(aParams.get_GetAllKeysParams()); - - case IndexRequestParams::TCountParams: - return actor->Count(aParams.get_CountParams()); - - case IndexRequestParams::TOpenCursorParams: - return actor->OpenCursor(aParams.get_OpenCursorParams()); - - case IndexRequestParams::TOpenKeyCursorParams: - return actor->OpenKeyCursor(aParams.get_OpenKeyCursorParams()); - - default: - MOZ_CRASH("Unknown type!"); - } - - MOZ_CRASH("Should never get here!"); -} - -PIndexedDBRequestParent* -IndexedDBIndexParent::AllocPIndexedDBRequestParent(const IndexRequestParams& aParams) -{ - return new IndexedDBIndexRequestParent(mIndex, aParams.type()); -} - -bool -IndexedDBIndexParent::DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) -{ - delete aActor; - return true; -} - -PIndexedDBCursorParent* -IndexedDBIndexParent::AllocPIndexedDBCursorParent( - const IndexCursorConstructorParams& aParams) -{ - MOZ_CRASH("Caller is supposed to manually construct a cursor!"); -} - -bool -IndexedDBIndexParent::DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) -{ - delete aActor; - return true; -} - -/******************************************************************************* - * IndexedDBRequestParentBase - ******************************************************************************/ - -IndexedDBRequestParentBase::IndexedDBRequestParentBase() -{ - MOZ_COUNT_CTOR(IndexedDBRequestParentBase); -} - -IndexedDBRequestParentBase::~IndexedDBRequestParentBase() -{ - MOZ_COUNT_DTOR(IndexedDBRequestParentBase); -} - -void -IndexedDBRequestParentBase::ActorDestroy(ActorDestroyReason aWhy) -{ - if (mRequest) { - mRequest->SetActor(nullptr); - } -} - -/******************************************************************************* - * IndexedDBObjectStoreRequestParent - ******************************************************************************/ - -IndexedDBObjectStoreRequestParent::IndexedDBObjectStoreRequestParent( - IDBObjectStore* aObjectStore, - RequestType aRequestType) -: mObjectStore(aObjectStore), mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBObjectStoreRequestParent); - // Sadly can't assert aObjectStore here... - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBObjectStoreRequestParent::~IndexedDBObjectStoreRequestParent() -{ - MOZ_COUNT_DTOR(IndexedDBObjectStoreRequestParent); -} - -void -IndexedDBObjectStoreRequestParent::ConvertBlobActors( - const InfallibleTArray& aActors, - nsTArray >& aBlobs) -{ - MOZ_ASSERT(aBlobs.IsEmpty()); - MOZ_ASSERT(mObjectStore); - - if (!aActors.IsEmpty()) { - // Walk the chain to get to ContentParent. - MOZ_ASSERT(mObjectStore->Transaction()->Database()->GetContentParent()); - - uint32_t length = aActors.Length(); - aBlobs.SetCapacity(length); - - for (uint32_t index = 0; index < length; index++) { - BlobParent* actor = static_cast(aActors[index]); - aBlobs.AppendElement(actor->GetBlob()); - } - } -} - -bool -IndexedDBObjectStoreRequestParent::IsDisconnected() -{ - MOZ_ASSERT(mObjectStore); - MOZ_ASSERT(mObjectStore->GetActorParent()); - return mObjectStore->GetActorParent()->IsDisconnected(); -} - -bool -IndexedDBObjectStoreRequestParent::Get(const GetParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); - MOZ_ASSERT(mObjectStore); - - nsRefPtr request; - - nsRefPtr keyRange = - IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); - MOZ_ASSERT(keyRange); - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->GetInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - - return true; -} - -bool -IndexedDBObjectStoreRequestParent::GetAll(const GetAllParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); - MOZ_ASSERT(mObjectStore); - - nsRefPtr request; - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->GetAllInternal(keyRange, aParams.limit(), rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::GetAllKeys(const GetAllKeysParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); - MOZ_ASSERT(mObjectStore); - - nsRefPtr request; - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->GetAllKeysInternal(keyRange, aParams.limit(), rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::Add(const AddParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TAddParams); - MOZ_ASSERT(mObjectStore); - - ipc::AddPutParams params = aParams.commonParams(); - - nsTArray > blobs; - ConvertBlobActors(params.blobsParent(), blobs); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - nsresult rv = - mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(), - params.indexUpdateInfos(), blobs, false, - getter_AddRefs(request)); - NS_ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::Put(const PutParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TPutParams); - MOZ_ASSERT(mObjectStore); - - ipc::AddPutParams params = aParams.commonParams(); - - nsTArray > blobs; - ConvertBlobActors(params.blobsParent(), blobs); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - nsresult rv = - mObjectStore->AddOrPutInternal(params.cloneInfo(), params.key(), - params.indexUpdateInfos(), blobs, true, - getter_AddRefs(request)); - NS_ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::Delete(const DeleteParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TDeleteParams); - MOZ_ASSERT(mObjectStore); - - nsRefPtr request; - - nsRefPtr keyRange = - IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); - MOZ_ASSERT(keyRange); - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->DeleteInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::Clear(const ClearParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TClearParams); - MOZ_ASSERT(mObjectStore); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->Clear(rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::Count(const CountParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); - MOZ_ASSERT(mObjectStore); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->CountInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::OpenCursor(const OpenCursorParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams); - MOZ_ASSERT(mObjectStore); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->OpenCursorInternal(keyRange, direction, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBObjectStoreRequestParent::OpenKeyCursor( - const OpenKeyCursorParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams); - MOZ_ASSERT(mObjectStore); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mObjectStore->Transaction()); - - ErrorResult rv; - request = mObjectStore->OpenKeyCursorInternal(keyRange, direction, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -/******************************************************************************* - * IndexedDBIndexRequestParent - ******************************************************************************/ - -IndexedDBIndexRequestParent::IndexedDBIndexRequestParent( - IDBIndex* aIndex, - RequestType aRequestType) -: mIndex(aIndex), mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBIndexRequestParent); - // Sadly can't assert aIndex here... - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBIndexRequestParent::~IndexedDBIndexRequestParent() -{ - MOZ_COUNT_DTOR(IndexedDBIndexRequestParent); -} - -bool -IndexedDBIndexRequestParent::IsDisconnected() -{ - MOZ_ASSERT(mIndex); - MOZ_ASSERT(mIndex->GetActorParent()); - return mIndex->GetActorParent()->IsDisconnected(); -} - -bool -IndexedDBIndexRequestParent::Get(const GetParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetParams); - MOZ_ASSERT(mIndex); - - nsRefPtr request; - - nsRefPtr keyRange = - IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); - MOZ_ASSERT(keyRange); - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->GetInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::GetKey(const GetKeyParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetKeyParams); - MOZ_ASSERT(mIndex); - - nsRefPtr request; - - nsRefPtr keyRange = - IDBKeyRange::FromSerializedKeyRange(aParams.keyRange()); - MOZ_ASSERT(keyRange); - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->GetKeyInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::GetAll(const GetAllParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams); - MOZ_ASSERT(mIndex); - - nsRefPtr request; - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->GetAllInternal(keyRange, aParams.limit(), rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::GetAllKeys(const GetAllKeysParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams); - MOZ_ASSERT(mIndex); - - nsRefPtr request; - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->GetAllKeysInternal(keyRange, aParams.limit(), rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::Count(const CountParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams); - MOZ_ASSERT(mIndex); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->CountInternal(keyRange, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::OpenCursor(const OpenCursorParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams); - MOZ_ASSERT(mIndex); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - nsresult rv = - mIndex->OpenCursorInternal(keyRange, direction, getter_AddRefs(request)); - NS_ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -bool -IndexedDBIndexRequestParent::OpenKeyCursor(const OpenKeyCursorParams& aParams) -{ - MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams); - MOZ_ASSERT(mIndex); - - const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange(); - - nsRefPtr keyRange; - - switch (keyRangeUnion.type()) { - case ipc::OptionalKeyRange::TKeyRange: - keyRange = - IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange()); - break; - - case ipc::OptionalKeyRange::Tvoid_t: - break; - - default: - MOZ_CRASH("Unknown param type!"); - } - - size_t direction = static_cast(aParams.direction()); - - nsRefPtr request; - - { - AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction()); - - ErrorResult rv; - request = mIndex->OpenKeyCursorInternal(keyRange, direction, rv); - ENSURE_SUCCESS(rv, false); - } - - request->SetActor(this); - mRequest.swap(request); - return true; -} - -/******************************************************************************* - * IndexedDBCursorRequestParent - ******************************************************************************/ - -IndexedDBCursorRequestParent::IndexedDBCursorRequestParent( - IDBCursor* aCursor, - RequestType aRequestType) -: mCursor(aCursor), mRequestType(aRequestType) -{ - MOZ_COUNT_CTOR(IndexedDBCursorRequestParent); - MOZ_ASSERT(aCursor); - MOZ_ASSERT(aRequestType > ParamsUnionType::T__None && - aRequestType <= ParamsUnionType::T__Last); -} - -IndexedDBCursorRequestParent::~IndexedDBCursorRequestParent() -{ - MOZ_COUNT_DTOR(IndexedDBCursorRequestParent); -} - -bool -IndexedDBCursorRequestParent::IsDisconnected() -{ - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mCursor->GetActorParent()); - return mCursor->GetActorParent()->IsDisconnected(); -} - -bool -IndexedDBCursorRequestParent::Continue(const ContinueParams& aParams) -{ - MOZ_ASSERT(mCursor); - MOZ_ASSERT(mRequestType == ParamsUnionType::TContinueParams); - - { - AutoSetCurrentTransaction asct(mCursor->Transaction()); - - ErrorResult rv; - mCursor->ContinueInternal(aParams.key(), aParams.count(), rv); - ENSURE_SUCCESS(rv, false); - } - - mRequest = mCursor->Request(); - MOZ_ASSERT(mRequest); - - mRequest->SetActor(this); - return true; -} - -/******************************************************************************* - * IndexedDBDeleteDatabaseRequestParent - ******************************************************************************/ - -IndexedDBDeleteDatabaseRequestParent::IndexedDBDeleteDatabaseRequestParent( - IDBFactory* aFactory) -: mEventListener(MOZ_THIS_IN_INITIALIZER_LIST()), mFactory(aFactory) -{ - MOZ_COUNT_CTOR(IndexedDBDeleteDatabaseRequestParent); - MOZ_ASSERT(aFactory); -} - -IndexedDBDeleteDatabaseRequestParent::~IndexedDBDeleteDatabaseRequestParent() -{ - MOZ_COUNT_DTOR(IndexedDBDeleteDatabaseRequestParent); -} - -nsresult -IndexedDBDeleteDatabaseRequestParent::HandleEvent(nsIDOMEvent* aEvent) -{ - MOZ_ASSERT(aEvent); - - if (IsDisconnected()) { - // We're shutting down, ignore this event. - return NS_OK; - } - - nsString type; - nsresult rv = aEvent->GetType(type); - NS_ENSURE_SUCCESS(rv, rv); - - if (type.EqualsASCII(BLOCKED_EVT_STR)) { - nsCOMPtr event = do_QueryInterface(aEvent); - MOZ_ASSERT(event); - - uint64_t currentVersion = event->OldVersion(); - - if (!SendBlocked(currentVersion)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - -#ifdef DEBUG - if (type.EqualsASCII(SUCCESS_EVT_STR)) { - MOZ_ASSERT(NS_SUCCEEDED(mOpenRequest->GetErrorCode())); - } - else { - MOZ_ASSERT(type.EqualsASCII(ERROR_EVT_STR)); - MOZ_ASSERT(NS_FAILED(mOpenRequest->GetErrorCode())); - } -#endif - - if (!Send__delete__(this, mOpenRequest->GetErrorCode())) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -void -IndexedDBDeleteDatabaseRequestParent::ActorDestroy(ActorDestroyReason aWhy) -{ - // Implement me! Bug 1005149 -} - -nsresult -IndexedDBDeleteDatabaseRequestParent::SetOpenRequest( - IDBOpenDBRequest* aOpenRequest) -{ - MOZ_ASSERT(aOpenRequest); - MOZ_ASSERT(!mOpenRequest); - - EventTarget* target = static_cast(aOpenRequest); - - nsresult rv = target->AddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = target->AddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = target->AddEventListener(NS_LITERAL_STRING(BLOCKED_EVT_STR), - mEventListener, false); - NS_ENSURE_SUCCESS(rv, rv); - - mOpenRequest = aOpenRequest; - return NS_OK; -} - -/******************************************************************************* - * WeakEventListener - ******************************************************************************/ - - NS_IMPL_ISUPPORTS(WeakEventListenerBase, nsIDOMEventListener) - - NS_IMETHODIMP - WeakEventListenerBase::HandleEvent(nsIDOMEvent* aEvent) -{ - MOZ_CRASH("This must be overridden!"); -} diff --git a/dom/indexedDB/ipc/IndexedDBParent.h b/dom/indexedDB/ipc/IndexedDBParent.h deleted file mode 100644 index 355e54d11007..000000000000 --- a/dom/indexedDB/ipc/IndexedDBParent.h +++ /dev/null @@ -1,880 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_ipc_indexeddbparent_h__ -#define mozilla_dom_indexeddb_ipc_indexeddbparent_h__ - -#include "mozilla/Attributes.h" -#include "mozilla/DebugOnly.h" - -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "mozilla/dom/indexedDB/PIndexedDBParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBCursorParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBDatabaseParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBDeleteDatabaseRequestParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBIndexParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBObjectStoreParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBRequestParent.h" -#include "mozilla/dom/indexedDB/PIndexedDBTransactionParent.h" - -#include "nsIDOMEventListener.h" - -namespace mozilla { -namespace dom { -class ContentParent; -class PBlobParent; -class TabParent; -} -} - -class nsIDOMBlob; -class nsIDOMEvent; - -BEGIN_INDEXEDDB_NAMESPACE - -class IDBCursor; -class IDBDatabase; -class IDBFactory; -class IDBIndex; -class IDBObjectStore; -class IDBOpenDBRequest; -class IDBTransaction; - -class IndexedDBCursorParent; -class IndexedDBDatabaseParent; -class IndexedDBDeleteDatabaseRequestParent; -class IndexedDBIndexParent; -class IndexedDBObjectStoreParent; -class IndexedDBTransactionParent; -class IndexedDBVersionChangeTransactionParent; -class IndexedDBVersionChangeObjectStoreParent; - -/******************************************************************************* - * AutoSetCurrentTransaction - ******************************************************************************/ - -class AutoSetCurrentTransaction -{ -public: - explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction); - ~AutoSetCurrentTransaction(); -}; - -/******************************************************************************* - * WeakEventListener - ******************************************************************************/ - -class WeakEventListenerBase : public nsIDOMEventListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMEVENTLISTENER - -protected: - WeakEventListenerBase() - { } - - virtual ~WeakEventListenerBase() - { } -}; - -template -class WeakEventListener : public WeakEventListenerBase -{ - T* mActor; - -public: - explicit WeakEventListener(T* aActor) - : mActor(aActor) - { } - - void - NoteDyingActor() - { - mActor = nullptr; - } - - NS_IMETHOD - HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE - { - return mActor ? mActor->HandleEvent(aEvent) : NS_OK; - } - -protected: - virtual ~WeakEventListener() - { } -}; - -template -class AutoWeakEventListener -{ - nsRefPtr > mEventListener; - -public: - explicit AutoWeakEventListener(T* aActor) - { - mEventListener = new WeakEventListener(aActor); - } - - ~AutoWeakEventListener() - { - mEventListener->NoteDyingActor(); - } - - template - operator U*() - { - return mEventListener; - } - - T* - operator ->() - { - return mEventListener; - } -}; - -/******************************************************************************* - * IndexedDBParent - ******************************************************************************/ - -class IndexedDBParent : private PIndexedDBParent -{ - friend class mozilla::dom::ContentParent; - friend class mozilla::dom::TabParent; - friend class IndexedDBDatabaseParent; - friend class IndexedDBDeleteDatabaseRequestParent; - - nsRefPtr mFactory; - nsCString mASCIIOrigin; - - ContentParent* mManagerContent; - TabParent* mManagerTab; - - bool mDisconnected; - -public: - explicit IndexedDBParent(ContentParent* aContentParent); - explicit IndexedDBParent(TabParent* aTabParent); - - virtual ~IndexedDBParent(); - - const nsCString& - GetASCIIOrigin() const - { - return mASCIIOrigin; - } - - void - Disconnect(); - - bool - IsDisconnected() const - { - return mDisconnected; - } - - ContentParent* - GetManagerContent() const - { - return mManagerContent; - } - - TabParent* - GetManagerTab() const - { - return mManagerTab; - } - - bool - CheckReadPermission(const nsAString& aDatabaseName); - - bool - CheckWritePermission(const nsAString& aDatabaseName); - - mozilla::ipc::IProtocol* - CloneProtocol(Channel* aChannel, - mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE; - -protected: - bool - CheckPermissionInternal(const nsAString& aDatabaseName, - const nsACString& aPermission); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBDatabaseConstructor(PIndexedDBDatabaseParent* aActor, - const nsString& aName, - const uint64_t& aVersion, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBDeleteDatabaseRequestConstructor( - PIndexedDBDeleteDatabaseRequestParent* aActor, - const nsString& aName, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual PIndexedDBDatabaseParent* - AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBDeleteDatabaseRequestParent* - AllocPIndexedDBDeleteDatabaseRequestParent( - const nsString& aName, - const PersistenceType& aPersistenceType) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBDeleteDatabaseRequestParent( - PIndexedDBDeleteDatabaseRequestParent* aActor) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBDatabaseParent - ******************************************************************************/ - -class IndexedDBDatabaseParent : private PIndexedDBDatabaseParent -{ - friend class IndexedDBParent; - friend class IndexedDBTransactionParent; - friend class IndexedDBVersionChangeTransactionParent; - - AutoWeakEventListener mEventListener; - - nsRefPtr mOpenRequest; - nsRefPtr mDatabase; - -public: - IndexedDBDatabaseParent(); - virtual ~IndexedDBDatabaseParent(); - - nsresult - SetOpenRequest(IDBOpenDBRequest* aRequest); - - nsresult - HandleEvent(nsIDOMEvent* aEvent); - - void - Disconnect(); - - bool - IsDisconnected() const - { - return static_cast(Manager())->IsDisconnected(); - } - - bool - CheckWritePermission(const nsAString& aDatabaseName); - - void - Invalidate(); - -protected: - nsresult - HandleRequestEvent(nsIDOMEvent* aEvent, const nsAString& aType); - - nsresult - HandleDatabaseEvent(nsIDOMEvent* aEvent, const nsAString& aType); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvClose(const bool& aUnlinked) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBTransactionConstructor(PIndexedDBTransactionParent* aActor, - const TransactionParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBTransactionParent* - AllocPIndexedDBTransactionParent(const TransactionParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBTransactionParent(PIndexedDBTransactionParent* aActor) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBTransactionParent - ******************************************************************************/ - -class IndexedDBTransactionParent : protected PIndexedDBTransactionParent -{ - friend class IndexedDBCursorParent; - friend class IndexedDBDatabaseParent; - friend class IndexedDBObjectStoreParent; - -protected: - AutoWeakEventListener mEventListener; - - nsRefPtr mTransaction; - - bool mArtificialRequestCount; - -public: - IndexedDBTransactionParent(); - virtual ~IndexedDBTransactionParent(); - - bool - IsDisconnected() const - { - return static_cast(Manager())->IsDisconnected(); - } - - nsresult - SetTransaction(IDBTransaction* aTransaction); - - IDBTransaction* - GetTransaction() const - { - return mTransaction; - } - - nsresult - HandleEvent(nsIDOMEvent* aEvent); - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvAbort(const nsresult& aAbortCode) MOZ_OVERRIDE; - - virtual bool - RecvAllRequestsFinished() MOZ_OVERRIDE; - - virtual bool - RecvDeleteObjectStore(const nsString& aName) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBObjectStoreConstructor( - PIndexedDBObjectStoreParent* aActor, - const ObjectStoreConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBObjectStoreParent* - AllocPIndexedDBObjectStoreParent(const ObjectStoreConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBObjectStoreParent(PIndexedDBObjectStoreParent* aActor) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBVersionChangeTransactionParent - ******************************************************************************/ - -class IndexedDBVersionChangeTransactionParent : - public IndexedDBTransactionParent -{ - friend class IndexedDBVersionChangeObjectStoreParent; - -public: - IndexedDBVersionChangeTransactionParent(); - virtual ~IndexedDBVersionChangeTransactionParent(); - - bool - IsDisconnected() const - { - return static_cast(Manager())->IsDisconnected(); - } - -protected: - virtual bool - RecvDeleteObjectStore(const nsString& aName) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBObjectStoreConstructor( - PIndexedDBObjectStoreParent* aActor, - const ObjectStoreConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBObjectStoreParent* - AllocPIndexedDBObjectStoreParent(const ObjectStoreConstructorParams& aParams) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBCursorParent - ******************************************************************************/ - -class IndexedDBCursorParent : private PIndexedDBCursorParent -{ - friend class IndexedDBIndexParent; - friend class IndexedDBObjectStoreParent; - - nsRefPtr mCursor; - -public: - IDBCursor* - GetCursor() const - { - return mCursor; - } - - bool - IsDisconnected() const; - -protected: - explicit IndexedDBCursorParent(IDBCursor* aCursor); - virtual ~IndexedDBCursorParent(); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, - const CursorRequestParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBRequestParent* - AllocPIndexedDBRequestParent(const CursorRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBObjectStoreParent - ******************************************************************************/ - -class IndexedDBObjectStoreParent : protected PIndexedDBObjectStoreParent -{ - friend class IndexedDBIndexParent; - friend class IndexedDBTransactionParent; - friend class IndexedDBVersionChangeTransactionParent; - - typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse; - -protected: - nsRefPtr mObjectStore; - -public: - IndexedDBObjectStoreParent(); - virtual ~IndexedDBObjectStoreParent(); - - void - SetObjectStore(IDBObjectStore* aObjectStore); - - IDBObjectStore* - GetObjectStore() const - { - return mObjectStore; - } - - bool - IsDisconnected() const - { - IndexedDBTransactionParent* manager = - static_cast(Manager()); - return manager->IsDisconnected(); - } - - // Ordinarily callers could just do this manually using - // PIndexedDBObjectStoreParent::SendPIndexedDBCursorConstructor but we're - // inheriting the abstract protocol class privately to prevent outside code - // from sending messages without checking the disconnected state. Therefore - // we need a helper method. - bool - OpenCursor(IDBCursor* aCursor, - const ObjectStoreCursorConstructorParams& aParams, - OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT - { - if (IsDisconnected()) { - return true; - } - - IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor); - - if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) { - return false; - } - - aResponse = cursorActor; - return true; - } - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, - const ObjectStoreRequestParams& aParams) - MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBIndexConstructor(PIndexedDBIndexParent* aActor, - const IndexConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBRequestParent* - AllocPIndexedDBRequestParent(const ObjectStoreRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBIndexParent* - AllocPIndexedDBIndexParent(const IndexConstructorParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBIndexParent(PIndexedDBIndexParent* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBCursorParent* - AllocPIndexedDBCursorParent(const ObjectStoreCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBVersionChangeObjectStoreParent - ******************************************************************************/ - -class IndexedDBVersionChangeObjectStoreParent : - public IndexedDBObjectStoreParent -{ - friend class IndexedDBVersionChangeTransactionParent; - -public: - IndexedDBVersionChangeObjectStoreParent(); - virtual ~IndexedDBVersionChangeObjectStoreParent(); - -protected: - bool - IsDisconnected() const - { - IndexedDBVersionChangeTransactionParent* manager = - static_cast(Manager()); - return manager->IsDisconnected(); - } - - virtual bool - RecvDeleteIndex(const nsString& aName) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBIndexConstructor(PIndexedDBIndexParent* aActor, - const IndexConstructorParams& aParams) - MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBIndexParent - ******************************************************************************/ - -class IndexedDBIndexParent : private PIndexedDBIndexParent -{ - friend class IndexedDBObjectStoreParent; - friend class IndexedDBVersionChangeObjectStoreParent; - - typedef mozilla::dom::indexedDB::ipc::OpenCursorResponse OpenCursorResponse; - - nsRefPtr mIndex; - -public: - IndexedDBIndexParent(); - virtual ~IndexedDBIndexParent(); - - void - SetIndex(IDBIndex* aObjectStore); - - IDBIndex* - GetIndex() const - { - return mIndex; - } - - // Ordinarily callers could just do this manually using - // PIndexedDBIndexParent::SendPIndexedDBCursorConstructor but we're - // inheriting the abstract protocol class privately to prevent outside code - // from sending messages without checking the disconnected state. Therefore - // we need a helper method. - bool - OpenCursor(IDBCursor* aCursor, const IndexCursorConstructorParams& aParams, - OpenCursorResponse& aResponse) NS_WARN_UNUSED_RESULT - { - if (IsDisconnected()) { - return true; - } - - IndexedDBCursorParent* cursorActor = new IndexedDBCursorParent(aCursor); - - if (!SendPIndexedDBCursorConstructor(cursorActor, aParams)) { - return false; - } - - aResponse = cursorActor; - return true; - } - - bool - IsDisconnected() const - { - IndexedDBObjectStoreParent* manager = - static_cast(Manager()); - return manager->IsDisconnected(); - } - -protected: - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - RecvPIndexedDBRequestConstructor(PIndexedDBRequestParent* aActor, - const IndexRequestParams& aParams) - MOZ_OVERRIDE; - - virtual PIndexedDBRequestParent* - AllocPIndexedDBRequestParent(const IndexRequestParams& aParams) MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBRequestParent(PIndexedDBRequestParent* aActor) MOZ_OVERRIDE; - - virtual PIndexedDBCursorParent* - AllocPIndexedDBCursorParent(const IndexCursorConstructorParams& aParams) - MOZ_OVERRIDE; - - virtual bool - DeallocPIndexedDBCursorParent(PIndexedDBCursorParent* aActor) MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBRequestParentBase - ******************************************************************************/ - -class IndexedDBRequestParentBase : public PIndexedDBRequestParent -{ -public: - bool - SendResponse(const ResponseValue& aResponse) NS_WARN_UNUSED_RESULT - { - if (IsDisconnected()) { - return true; - } - - return Send__delete__(this, aResponse); - } - -protected: - // Don't let anyone call this directly, instead go through SendResponse. - using PIndexedDBRequestParent::Send__delete__; - - typedef ipc::ResponseValue ResponseValue; - typedef PIndexedDBRequestParent::PBlobParent PBlobParent; - - nsRefPtr mRequest; - - IndexedDBRequestParentBase(); - virtual ~IndexedDBRequestParentBase(); - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - IsDisconnected() = 0; -}; - -/******************************************************************************* - * IndexedDBObjectStoreRequestParent - ******************************************************************************/ - -class IndexedDBObjectStoreRequestParent : public IndexedDBRequestParentBase -{ - friend class IndexedDBObjectStoreParent; - - nsRefPtr mObjectStore; - - typedef ipc::ObjectStoreRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - - typedef ipc::AddParams AddParams; - typedef ipc::PutParams PutParams; - typedef ipc::ClearParams ClearParams; - typedef ipc::DeleteParams DeleteParams; - typedef ipc::GetParams GetParams; - typedef ipc::GetAllParams GetAllParams; - typedef ipc::GetAllKeysParams GetAllKeysParams; - typedef ipc::CountParams CountParams; - typedef ipc::OpenCursorParams OpenCursorParams; - typedef ipc::OpenKeyCursorParams OpenKeyCursorParams; - -public: - IndexedDBObjectStoreRequestParent(IDBObjectStore* aObjectStore, - RequestType aRequestType); - virtual ~IndexedDBObjectStoreRequestParent(); - - bool - Get(const GetParams& aParams); - - bool - GetAll(const GetAllParams& aParams); - - bool - GetAllKeys(const GetAllKeysParams& aParams); - - bool - Add(const AddParams& aParams); - - bool - Put(const PutParams& aParams); - - bool - Delete(const DeleteParams& aParams); - - bool - Clear(const ClearParams& aParams); - - bool - Count(const CountParams& aParams); - - bool - OpenCursor(const OpenCursorParams& aParams); - - bool - OpenKeyCursor(const OpenKeyCursorParams& aParams); - -protected: - void - ConvertBlobActors(const InfallibleTArray& aActors, - nsTArray >& aBlobs); - -private: - virtual bool - IsDisconnected() MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBIndexRequestParent - ******************************************************************************/ - -class IndexedDBIndexRequestParent : public IndexedDBRequestParentBase -{ - friend class IndexedDBIndexParent; - - nsRefPtr mIndex; - - typedef ipc::IndexRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - - typedef ipc::GetKeyParams GetKeyParams; - typedef ipc::GetAllKeysParams GetAllKeysParams; - typedef ipc::OpenKeyCursorParams OpenKeyCursorParams; - typedef ipc::GetParams GetParams; - typedef ipc::GetAllParams GetAllParams; - typedef ipc::CountParams CountParams; - typedef ipc::OpenCursorParams OpenCursorParams; - -public: - IndexedDBIndexRequestParent(IDBIndex* aIndex, RequestType aRequestType); - virtual ~IndexedDBIndexRequestParent(); - - bool - Get(const GetParams& aParams); - - bool - GetKey(const GetKeyParams& aParams); - - bool - GetAll(const GetAllParams& aParams); - - bool - GetAllKeys(const GetAllKeysParams& aParams); - - bool - Count(const CountParams& aParams); - - bool - OpenCursor(const OpenCursorParams& aParams); - - bool - OpenKeyCursor(const OpenKeyCursorParams& aParams); - -private: - virtual bool - IsDisconnected() MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBCursorRequestParent - ******************************************************************************/ - -class IndexedDBCursorRequestParent : public IndexedDBRequestParentBase -{ - friend class IndexedDBCursorParent; - - nsRefPtr mCursor; - - typedef ipc::CursorRequestParams ParamsUnionType; - typedef ParamsUnionType::Type RequestType; - DebugOnly mRequestType; - - typedef ipc::ContinueParams ContinueParams; - -public: - IndexedDBCursorRequestParent(IDBCursor* aCursor, RequestType aRequestType); - virtual ~IndexedDBCursorRequestParent(); - - bool - Continue(const ContinueParams& aParams); - -private: - virtual bool - IsDisconnected() MOZ_OVERRIDE; -}; - -/******************************************************************************* - * IndexedDBDeleteDatabaseRequestParent - ******************************************************************************/ - -class IndexedDBDeleteDatabaseRequestParent : - private PIndexedDBDeleteDatabaseRequestParent -{ - friend class IndexedDBParent; - - AutoWeakEventListener mEventListener; - - nsRefPtr mFactory; - nsRefPtr mOpenRequest; - -public: - nsresult - HandleEvent(nsIDOMEvent* aEvent); - -protected: - explicit IndexedDBDeleteDatabaseRequestParent(IDBFactory* aFactory); - virtual ~IndexedDBDeleteDatabaseRequestParent(); - - virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - nsresult - SetOpenRequest(IDBOpenDBRequest* aOpenRequest); - - bool - IsDisconnected() const - { - return static_cast(Manager())->IsDisconnected(); - } -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_ipc_indexeddbparent_h__ diff --git a/dom/indexedDB/ipc/Makefile.in b/dom/indexedDB/ipc/Makefile.in deleted file mode 100644 index a5a459b7f249..000000000000 --- a/dom/indexedDB/ipc/Makefile.in +++ /dev/null @@ -1,14 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -include $(topsrcdir)/config/rules.mk - -xpcshell_tests = unit - -# Copy all the normal xpcshell tests from the regular unit directory. -copy-xpcshell-tests: - $(call install_cmd,$(wildcard $(topsrcdir)/dom/indexedDB/test/unit/test_*.js) \ - $(testxpcobjdir)/$(relativesrcdir)/$(xpcshell_tests)) - -libs-xpcshell-tests: copy-xpcshell-tests diff --git a/dom/indexedDB/ipc/PIndexedDB.ipdl b/dom/indexedDB/ipc/PIndexedDB.ipdl deleted file mode 100644 index 4677685709ad..000000000000 --- a/dom/indexedDB/ipc/PIndexedDB.ipdl +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBrowser; -include protocol PContent; -include protocol PIndexedDBDatabase; -include protocol PIndexedDBDeleteDatabaseRequest; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using mozilla::dom::quota::PersistenceType from "mozilla/dom/quota/PersistenceType.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -protocol PIndexedDB -{ - manager PBrowser or PContent; - - manages PIndexedDBDatabase; - manages PIndexedDBDeleteDatabaseRequest; - -parent: - __delete__(); - - PIndexedDBDatabase(nsString name, uint64_t version, - PersistenceType persistenceType); - - PIndexedDBDeleteDatabaseRequest(nsString name, - PersistenceType persistenceType); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBCursor.ipdl b/dom/indexedDB/ipc/PIndexedDBCursor.ipdl deleted file mode 100644 index 1daa93417355..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBCursor.ipdl +++ /dev/null @@ -1,49 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PIndexedDBIndex; -include protocol PIndexedDBObjectStore; -include protocol PIndexedDBRequest; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; -using mozilla::dom::indexedDB::IDBCursor::Direction from "mozilla/dom/indexedDB/IDBCursor.h"; - -using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct ContinueParams -{ - Key key; - uint32_t count; -}; - -union CursorRequestParams -{ - ContinueParams; -}; - -} // namespace ipc - -protocol PIndexedDBCursor -{ - manager PIndexedDBObjectStore or PIndexedDBIndex; - - manages PIndexedDBRequest; - -parent: - __delete__(); - - PIndexedDBRequest(CursorRequestParams params); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl b/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl deleted file mode 100644 index f765687337cb..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBDatabase.ipdl +++ /dev/null @@ -1,69 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PIndexedDB; -include protocol PIndexedDBTransaction; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::dom::indexedDB::DatabaseInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; -using struct mozilla::dom::indexedDB::ObjectStoreInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; -using mozilla::dom::indexedDB::IDBTransaction::Mode from "mozilla/dom/indexedDB/IDBTransaction.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct NormalTransactionParams -{ - nsString[] names; - Mode mode; -}; - -struct VersionChangeTransactionParams -{ - DatabaseInfoGuts dbInfo; - ObjectStoreInfoGuts[] osInfo; - uint64_t oldVersion; -}; - -union TransactionParams -{ - NormalTransactionParams; - VersionChangeTransactionParams; -}; - -} // namespace ipc - -protocol PIndexedDBDatabase -{ - manager PIndexedDB; - - manages PIndexedDBTransaction; - -parent: - __delete__(); - - Close(bool unlinked); - -child: - Success(DatabaseInfoGuts dbInfo, ObjectStoreInfoGuts[] osInfo); - - Error(nsresult rv); - - Blocked(uint64_t oldVersion); - - VersionChange(uint64_t oldVersion, uint64_t newVersion); - - Invalidate(); - -both: - PIndexedDBTransaction(TransactionParams params); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl deleted file mode 100644 index 3b402fa5440e..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl +++ /dev/null @@ -1,64 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBlob; -include protocol PIndexedDBCursor; -include protocol PIndexedDBObjectStore; -include protocol PIndexedDBRequest; - -include IndexedDBParams; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct GetKeyParams -{ - KeyRange keyRange; -}; - -union IndexRequestParams -{ - GetParams; - GetKeyParams; - GetAllParams; - GetAllKeysParams; - CountParams; - OpenCursorParams; - OpenKeyCursorParams; -}; - -struct IndexCursorConstructorParams -{ - PIndexedDBRequest request; - Direction direction; - Key key; - Key objectKey; - OptionalStructuredCloneReadInfo optionalCloneInfo; - PBlob[] blobs; -}; - -} // namespace ipc - -protocol PIndexedDBIndex -{ - manager PIndexedDBObjectStore; - - manages PIndexedDBCursor; - manages PIndexedDBRequest; - -parent: - __delete__(); - - PIndexedDBRequest(IndexRequestParams params); - -child: - PIndexedDBCursor(IndexCursorConstructorParams params); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl deleted file mode 100644 index 8026db4086cd..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl +++ /dev/null @@ -1,113 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBlob; -include protocol PIndexedDBCursor; -include protocol PIndexedDBIndex; -include protocol PIndexedDBRequest; -include protocol PIndexedDBTransaction; - -include IndexedDBParams; - -using struct mozilla::dom::indexedDB::IndexInfo from "mozilla/dom/indexedDB/DatabaseInfo.h"; -using struct mozilla::dom::indexedDB::IndexUpdateInfo from "mozilla/dom/indexedDB/DatabaseInfo.h"; -using struct mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct AddPutParams -{ - SerializedStructuredCloneWriteInfo cloneInfo; - Key key; - IndexUpdateInfo[] indexUpdateInfos; - PBlob[] blobs; -}; - -struct AddParams -{ - AddPutParams commonParams; -}; - -struct PutParams -{ - AddPutParams commonParams; -}; - -struct DeleteParams -{ - KeyRange keyRange; -}; - -struct ClearParams -{ -}; - -union ObjectStoreRequestParams -{ - GetParams; - GetAllParams; - GetAllKeysParams; - AddParams; - PutParams; - DeleteParams; - ClearParams; - CountParams; - OpenCursorParams; - OpenKeyCursorParams; -}; - -struct CreateIndexParams -{ - IndexInfo info; -}; - -struct GetIndexParams -{ - nsString name; -}; - -union IndexConstructorParams -{ - CreateIndexParams; - GetIndexParams; -}; - -struct ObjectStoreCursorConstructorParams -{ - PIndexedDBRequest request; - Direction direction; - Key key; - OptionalStructuredCloneReadInfo optionalCloneInfo; - PBlob[] blobs; -}; - -} // namespace ipc - -protocol PIndexedDBObjectStore -{ - manager PIndexedDBTransaction; - - manages PIndexedDBCursor; - manages PIndexedDBIndex; - manages PIndexedDBRequest; - -parent: - __delete__(); - - PIndexedDBIndex(IndexConstructorParams params); - PIndexedDBRequest(ObjectStoreRequestParams params); - - DeleteIndex(nsString name); - -child: - PIndexedDBCursor(ObjectStoreCursorConstructorParams params); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl deleted file mode 100644 index 4c83fed55110..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl +++ /dev/null @@ -1,113 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBlob; -include protocol PIndexedDBCursor; -include protocol PIndexedDBIndex; -include protocol PIndexedDBObjectStore; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using class mozilla::dom::indexedDB::Key from "mozilla/dom/indexedDB/Key.h"; -using struct mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo from "mozilla/dom/indexedDB/IndexedDatabase.h"; - -using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct GetResponse -{ - SerializedStructuredCloneReadInfo cloneInfo; - PBlob[] blobs; -}; - -struct GetKeyResponse -{ - Key key; -}; - -struct BlobArray -{ - PBlob[] blobs; -}; - -struct GetAllResponse -{ - SerializedStructuredCloneReadInfo[] cloneInfos; - BlobArray[] blobs; -}; - -struct GetAllKeysResponse -{ - Key[] keys; -}; - -struct AddResponse -{ - Key key; -}; - -struct PutResponse -{ - Key key; -}; - -struct DeleteResponse -{ }; - -struct ClearResponse -{ }; - -struct CountResponse -{ - uint64_t count; -}; - -union OpenCursorResponse -{ - PIndexedDBCursor; - void_t; -}; - -struct ContinueResponse -{ - Key key; - Key objectKey; - SerializedStructuredCloneReadInfo cloneInfo; - PBlob[] blobs; -}; - -union ResponseValue -{ - nsresult; - GetResponse; - GetKeyResponse; - GetAllResponse; - GetAllKeysResponse; - AddResponse; - PutResponse; - DeleteResponse; - ClearResponse; - CountResponse; - OpenCursorResponse; - ContinueResponse; -}; - -} // namespace ipc - -protocol PIndexedDBRequest -{ - manager PIndexedDBObjectStore or PIndexedDBIndex or PIndexedDBCursor; - -child: - __delete__(ResponseValue response); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl b/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl deleted file mode 100644 index ff6d88a7b74f..000000000000 --- a/dom/indexedDB/ipc/PIndexedDBTransaction.ipdl +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PIndexedDBDatabase; -include protocol PIndexedDBObjectStore; - -include "mozilla/dom/indexedDB/SerializationHelpers.h"; - -using struct mozilla::dom::indexedDB::ObjectStoreInfoGuts from "mozilla/dom/indexedDB/DatabaseInfo.h"; - -namespace mozilla { -namespace dom { -namespace indexedDB { - -namespace ipc { - -struct CreateObjectStoreParams -{ - ObjectStoreInfoGuts info; -}; - -struct GetObjectStoreParams -{ - nsString name; -}; - -union ObjectStoreConstructorParams -{ - CreateObjectStoreParams; - GetObjectStoreParams; -}; - -struct CompleteResult -{ }; - -struct AbortResult -{ - nsresult errorCode; -}; - -union CompleteParams -{ - CompleteResult; - AbortResult; -}; - -} // namespace ipc - -protocol PIndexedDBTransaction -{ - manager PIndexedDBDatabase; - - manages PIndexedDBObjectStore; - -parent: - __delete__(); - - PIndexedDBObjectStore(ObjectStoreConstructorParams params); - - Abort(nsresult abortCode); - - AllRequestsFinished(); - - DeleteObjectStore(nsString name); - -child: - Complete(CompleteParams params); -}; - -} // namespace indexedDB -} // namespace dom -} // namespace mozilla diff --git a/dom/indexedDB/ipc/SerializationHelpers.h b/dom/indexedDB/ipc/SerializationHelpers.h deleted file mode 100644 index 3d6d4eb24aca..000000000000 --- a/dom/indexedDB/ipc/SerializationHelpers.h +++ /dev/null @@ -1,309 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_serializationhelpers_h__ -#define mozilla_dom_indexeddb_serializationhelpers_h__ - -#include "ipc/IPCMessageUtils.h" - -#include "mozilla/dom/indexedDB/DatabaseInfo.h" -#include "mozilla/dom/indexedDB/Key.h" -#include "mozilla/dom/indexedDB/KeyPath.h" -#include "mozilla/dom/indexedDB/IDBCursor.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" - -namespace IPC { - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::quota::PersistenceType, - mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT, - mozilla::dom::quota::PERSISTENCE_TYPE_INVALID> -{ }; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::Key paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mBuffer); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->mBuffer); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.mBuffer, aLog); - } -}; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::indexedDB::KeyPath::KeyPathType, - mozilla::dom::indexedDB::KeyPath::NONEXISTENT, - mozilla::dom::indexedDB::KeyPath::ENDGUARD> -{ }; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::KeyPath paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.mType); - WriteParam(aMsg, aParam.mStrings); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->mType) && - ReadParam(aMsg, aIter, &aResult->mStrings); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.mStrings, aLog); - } -}; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBCursor::Direction, - mozilla::dom::indexedDB::IDBCursor::NEXT, - mozilla::dom::indexedDB::IDBCursor::DIRECTION_INVALID> -{ }; - -template <> -struct ParamTraits : - public ContiguousEnumSerializer< - mozilla::dom::indexedDB::IDBTransaction::Mode, - mozilla::dom::indexedDB::IDBTransaction::READ_ONLY, - mozilla::dom::indexedDB::IDBTransaction::MODE_INVALID> -{ }; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::IndexInfo paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.name); - WriteParam(aMsg, aParam.id); - WriteParam(aMsg, aParam.keyPath); - WriteParam(aMsg, aParam.unique); - WriteParam(aMsg, aParam.multiEntry); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->name) && - ReadParam(aMsg, aIter, &aResult->id) && - ReadParam(aMsg, aIter, &aResult->keyPath) && - ReadParam(aMsg, aIter, &aResult->unique) && - ReadParam(aMsg, aIter, &aResult->multiEntry); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.name, aLog); - LogParam(aParam.id, aLog); - LogParam(aParam.keyPath, aLog); - LogParam(aParam.unique, aLog); - LogParam(aParam.multiEntry, aLog); - } -}; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::ObjectStoreInfoGuts paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.name); - WriteParam(aMsg, aParam.id); - WriteParam(aMsg, aParam.keyPath); - WriteParam(aMsg, aParam.autoIncrement); - WriteParam(aMsg, aParam.indexes); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->name) && - ReadParam(aMsg, aIter, &aResult->id) && - ReadParam(aMsg, aIter, &aResult->keyPath) && - ReadParam(aMsg, aIter, &aResult->autoIncrement) && - ReadParam(aMsg, aIter, &aResult->indexes); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.name, aLog); - LogParam(aParam.id, aLog); - LogParam(aParam.keyPath, aLog); - LogParam(aParam.autoIncrement, aLog); - LogParam(aParam.indexes, aLog); - } -}; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::DatabaseInfoGuts paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.name); - WriteParam(aMsg, aParam.group); - WriteParam(aMsg, aParam.origin); - WriteParam(aMsg, aParam.version); - WriteParam(aMsg, aParam.persistenceType); - WriteParam(aMsg, aParam.nextObjectStoreId); - WriteParam(aMsg, aParam.nextIndexId); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->name) && - ReadParam(aMsg, aIter, &aResult->group) && - ReadParam(aMsg, aIter, &aResult->origin) && - ReadParam(aMsg, aIter, &aResult->version) && - ReadParam(aMsg, aIter, &aResult->persistenceType) && - ReadParam(aMsg, aIter, &aResult->nextObjectStoreId) && - ReadParam(aMsg, aIter, &aResult->nextIndexId); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.name, aLog); - LogParam(aParam.group, aLog); - LogParam(aParam.origin, aLog); - LogParam(aParam.version, aLog); - LogParam(aParam.nextObjectStoreId, aLog); - LogParam(aParam.nextIndexId, aLog); - } -}; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::IndexUpdateInfo paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.indexId); - WriteParam(aMsg, aParam.indexUnique); - WriteParam(aMsg, aParam.value); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - return ReadParam(aMsg, aIter, &aResult->indexId) && - ReadParam(aMsg, aIter, &aResult->indexUnique) && - ReadParam(aMsg, aIter, &aResult->value); - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.indexId, aLog); - LogParam(aParam.indexUnique, aLog); - LogParam(aParam.value, aLog); - } -}; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.dataLength); - if (aParam.dataLength) { - aMsg->WriteBytes(aParam.data, aParam.dataLength); - } - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - if (!ReadParam(aMsg, aIter, &aResult->dataLength)) { - return false; - } - - if (aResult->dataLength) { - const char** buffer = - const_cast(reinterpret_cast(&aResult->data)); - if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength)) { - return false; - } - } else { - aResult->data = nullptr; - } - - return true; - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.dataLength, aLog); - } -}; - -template <> -struct ParamTraits -{ - typedef mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - WriteParam(aMsg, aParam.dataLength); - if (aParam.dataLength) { - aMsg->WriteBytes(aParam.data, aParam.dataLength); - } - WriteParam(aMsg, aParam.offsetToKeyProp); - } - - static bool Read(const Message* aMsg, void** aIter, paramType* aResult) - { - if (!ReadParam(aMsg, aIter, &aResult->dataLength)) { - return false; - } - - if (aResult->dataLength) { - const char** buffer = - const_cast(reinterpret_cast(&aResult->data)); - if (!aMsg->ReadBytes(aIter, buffer, aResult->dataLength)) { - return false; - } - } else { - aResult->data = nullptr; - } - - if (!ReadParam(aMsg, aIter, &aResult->offsetToKeyProp)) { - return false; - } - - return true; - } - - static void Log(const paramType& aParam, std::wstring* aLog) - { - LogParam(aParam.dataLength, aLog); - LogParam(aParam.offsetToKeyProp, aLog); - } -}; - -} // namespace IPC - -#endif // mozilla_dom_indexeddb_serializationhelpers_h__ diff --git a/dom/indexedDB/ipc/mochitest.ini b/dom/indexedDB/ipc/mochitest.ini deleted file mode 100644 index 81b48267ea25..000000000000 --- a/dom/indexedDB/ipc/mochitest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] - -[test_ipc.html] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #bug 783513 # b2g(nested ipc not working) b2g-debug(nested ipc not working) b2g-desktop(nested ipc not working) diff --git a/dom/indexedDB/ipc/moz.build b/dom/indexedDB/ipc/moz.build deleted file mode 100644 index fee6defb45ea..000000000000 --- a/dom/indexedDB/ipc/moz.build +++ /dev/null @@ -1,41 +0,0 @@ -# -*- 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/. - -EXPORTS.mozilla.dom.indexedDB += [ - 'SerializationHelpers.h', -] - -# Need to enable these tests sometime soon. -#XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] - -UNIFIED_SOURCES += [ - 'IndexedDBChild.cpp', - 'IndexedDBParent.cpp', -] - -IPDL_SOURCES += [ - 'IndexedDBParams.ipdlh', - 'PIndexedDB.ipdl', - 'PIndexedDBCursor.ipdl', - 'PIndexedDBDatabase.ipdl', - 'PIndexedDBDeleteDatabaseRequest.ipdl', - 'PIndexedDBIndex.ipdl', - 'PIndexedDBObjectStore.ipdl', - 'PIndexedDBRequest.ipdl', - 'PIndexedDBTransaction.ipdl', -] - -FAIL_ON_WARNINGS = True - -MOCHITEST_MANIFESTS += ['mochitest.ini'] - -include('/ipc/chromium/chromium-config.mozbuild') - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/dom/indexedDB', -] - diff --git a/dom/indexedDB/ipc/unit/head.js b/dom/indexedDB/ipc/unit/head.js deleted file mode 100644 index 8293b09c48fa..000000000000 --- a/dom/indexedDB/ipc/unit/head.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -const INDEXEDDB_UNIT_DIR = "../../test/unit/"; -const INDEXEDDB_HEAD_FILE = INDEXEDDB_UNIT_DIR + "head.js"; - -function run_test() { - // IndexedDB needs a profile. - do_get_profile(); - - let thisTest = _TEST_FILE.toString().replace(/\\/g, "/"); - thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1); - - _HEAD_FILES.push(do_get_file(INDEXEDDB_HEAD_FILE).path.replace(/\\/g, "/")); - run_test_in_child(INDEXEDDB_UNIT_DIR + thisTest); -} diff --git a/dom/indexedDB/ipc/unit/xpcshell.ini b/dom/indexedDB/ipc/unit/xpcshell.ini deleted file mode 100644 index 6aeedab2890a..000000000000 --- a/dom/indexedDB/ipc/unit/xpcshell.ini +++ /dev/null @@ -1,68 +0,0 @@ -[DEFAULT] -head = head.js -tail = - -# When adding files here please also update test/unit/xpcshell.ini! - -[test_add_put.js] -[test_add_twice_failure.js] -[test_advance.js] -[test_autoIncrement.js] -[test_autoIncrement_indexes.js] -[test_clear.js] -[test_complex_keyPaths.js] -[test_count.js] -[test_create_index.js] -[test_create_index_with_integer_keys.js] -[test_create_objectStore.js] -[test_cursor_mutation.js] -[test_cursor_update_updates_indexes.js] -[test_cursors.js] -[test_deleteDatabase.js] -[test_deleteDatabase_interactions.js] -[test_event_source.js] -[test_getAll.js] -[test_global_data.js] -[test_index_empty_keyPath.js] -[test_index_getAll.js] -[test_index_getAllObjects.js] -[test_index_object_cursors.js] -[test_index_update_delete.js] -[test_indexes.js] -[test_indexes_bad_values.js] -[test_key_requirements.js] -[test_keys.js] -[test_multientry.js] -[test_names_sorted.js] -[test_object_identity.js] -[test_objectCursors.js] -[test_objectStore_getAllKeys.js] -[test_objectStore_inline_autoincrement_key_added_on_put.js] -[test_objectStore_openKeyCursor.js] -[test_objectStore_remove_values.js] -[test_odd_result_order.js] -[test_open_empty_db.js] -[test_open_objectStore.js] -[test_optionalArguments.js] -[test_overlapping_transactions.js] -[test_put_get_values.js] -[test_put_get_values_autoIncrement.js] -[test_readonly_transactions.js] -[test_remove_index.js] -[test_remove_objectStore.js] -[test_request_readyState.js] -[test_setVersion.js] -[test_setVersion_abort.js] -[test_setVersion_events.js] -[test_setVersion_exclusion.js] -[test_success_events_after_abort.js] -[test_traffic_jam.js] -[test_transaction_abort.js] -[test_transaction_error.js] -[test_transaction_lifetimes.js] -[test_transaction_lifetimes_nested.js] -[test_transaction_ordering.js] -[test_unique_index_update.js] -[test_writer_starvation.js] - -# When adding files here please also update test/unit/xpcshell.ini! diff --git a/dom/indexedDB/moz.build b/dom/indexedDB/moz.build index 5c4071dd8970..ce1e78ef96a9 100644 --- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -4,12 +4,21 @@ # 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/. -DIRS += ['ipc'] TEST_DIRS += ['test/extensions'] +MOCHITEST_MANIFESTS += ['test/mochitest.ini'] + +BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] + +MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] + +XPCSHELL_TESTS_MANIFESTS += [ + 'test/unit/xpcshell-child-process.ini', + 'test/unit/xpcshell-parent-process.ini' +] + EXPORTS.mozilla.dom.indexedDB += [ - 'Client.h', - 'DatabaseInfo.h', + 'ActorsParent.h', 'FileInfo.h', 'FileManager.h', 'FileSnapshot.h', @@ -30,60 +39,61 @@ EXPORTS.mozilla.dom.indexedDB += [ 'IndexedDatabaseManager.h', 'Key.h', 'KeyPath.h', + 'SerializationHelpers.h', ] UNIFIED_SOURCES += [ - 'AsyncConnectionHelper.cpp', - 'CheckPermissionsHelper.cpp', - 'Client.cpp', - 'DatabaseInfo.cpp', + 'ActorsChild.cpp', 'FileInfo.cpp', - 'FileManager.cpp', 'FileSnapshot.cpp', + 'IDBCursor.cpp', 'IDBDatabase.cpp', 'IDBEvents.cpp', 'IDBFactory.cpp', 'IDBFileHandle.cpp', 'IDBFileRequest.cpp', + 'IDBIndex.cpp', 'IDBKeyRange.cpp', 'IDBMutableFile.cpp', + 'IDBObjectStore.cpp', 'IDBRequest.cpp', 'IDBTransaction.cpp', 'IDBWrapperCache.cpp', 'IndexedDatabaseManager.cpp', 'Key.cpp', 'KeyPath.cpp', - 'OpenDatabaseHelper.cpp', + 'PermissionRequestBase.cpp', + 'ReportInternalError.cpp', 'TransactionThreadPool.cpp', ] -# These files cannot be built in unified mode because of name collisions SOURCES += [ - 'IDBCursor.cpp', - 'IDBIndex.cpp', - 'IDBObjectStore.cpp', - 'ReportInternalError.cpp', + 'ActorsParent.cpp', # This file is huge. ] -FAIL_ON_WARNINGS = True +IPDL_SOURCES += [ + 'PBackgroundIDBCursor.ipdl', + 'PBackgroundIDBDatabase.ipdl', + 'PBackgroundIDBDatabaseFile.ipdl', + 'PBackgroundIDBFactory.ipdl', + 'PBackgroundIDBFactoryRequest.ipdl', + 'PBackgroundIDBRequest.ipdl', + 'PBackgroundIDBSharedTypes.ipdlh', + 'PBackgroundIDBTransaction.ipdl', + 'PBackgroundIDBVersionChangeTransaction.ipdl', + 'PIndexedDBPermissionRequest.ipdl', +] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' + LOCAL_INCLUDES += [ - '/caps', '/content/base/src', '/db/sqlite3/src', '/dom/base', '/dom/quota', '/dom/storage', + '/ipc/glue', '/xpcom/build', ] - -MOCHITEST_MANIFESTS += [ - 'test/mochitest.ini', - 'test/unit/mochitest.ini', -] -BROWSER_CHROME_MANIFESTS += ['test/browser.ini'] -MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] -XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] diff --git a/dom/indexedDB/test/browser.ini b/dom/indexedDB/test/browser.ini index 62a6254d1d5a..55ede42c0e7a 100644 --- a/dom/indexedDB/test/browser.ini +++ b/dom/indexedDB/test/browser.ini @@ -1,24 +1,28 @@ -[DEFAULT] -run-if = buildapp == "browser" -skip-if = e10s -support-files = - head.js - browser_forgetThisSiteAdd.html - browser_forgetThisSiteGet.html - browserHelpers.js - browser_permissionsPrompt.html - browser_quotaPrompt.html - browser_quotaPromptDatabases.html - browser_quotaPromptDelete.html - bug839193.js - bug839193.xul - -[browser_forgetThisSite.js] -[browser_permissionsPromptAllow.js] -[browser_permissionsPromptDeny.js] -[browser_perwindow_privateBrowsing.js] -[browser_quotaPromptAllow.js] -[browser_quotaPromptDeny.js] -[browser_quotaPromptDatabases.js] -[browser_quotaPromptDelete.js] -[browser_bug839193.js] +[DEFAULT] +run-if = buildapp == "browser" +skip-if = e10s +support-files = + head.js + browser_forgetThisSiteAdd.html + browser_forgetThisSiteGet.html + browserHelpers.js + browser_permissionsPrompt.html + browser_quotaPrompt.html + browser_quotaPromptDatabases.html + browser_quotaPromptDelete.html + bug839193.js + bug839193.xul + +[browser_forgetThisSite.js] +[browser_permissionsPromptAllow.js] +[browser_permissionsPromptDeny.js] +[browser_perwindow_privateBrowsing.js] +[browser_quotaPromptAllow.js] +skip-if = true # Quota handling disabled for now. +[browser_quotaPromptDeny.js] +skip-if = true # Quota handling disabled for now. +[browser_quotaPromptDatabases.js] +skip-if = true # Quota handling disabled for now. +[browser_quotaPromptDelete.js] +skip-if = true # Quota handling disabled for now. +[browser_bug839193.js] diff --git a/dom/indexedDB/test/file.js b/dom/indexedDB/test/file.js index b9b735c5d557..e8fc24a12891 100644 --- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -8,13 +8,6 @@ const DEFAULT_QUOTA = 50 * 1024 * 1024; var bufferCache = []; var utils = SpecialPowers.getDOMWindowUtils(window); -if (!SpecialPowers.isMainProcess()) { - window.runTest = function() { - todo(false, "Test disabled in child processes, for now"); - finishTest(); - } -} - function getBuffer(size) { let buffer = new ArrayBuffer(size); @@ -189,22 +182,17 @@ function grabFileUsageAndContinueHandler(usage, fileUsage) function getUsage(usageHandler) { - let comp = SpecialPowers.wrap(Components); - let quotaManager = comp.classes["@mozilla.org/dom/quota/manager;1"] - .getService(comp.interfaces.nsIQuotaManager); - - // We need to pass a JS callback to getUsageForURI. However, that callback - // takes an XPCOM URI object, which will cause us to throw when we wrap it - // for the content compartment. So we need to define the function in a - // privileged scope, which we do using a sandbox. - var sysPrin = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); - var sb = new SpecialPowers.Cu.Sandbox(sysPrin); - sb.usageHandler = usageHandler; - var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) { - usageHandler(usage, fileUsage); }).toSource(), sb); - - let uri = SpecialPowers.wrap(window).document.documentURIObject; - quotaManager.getUsageForURI(uri, cb); + let principal = SpecialPowers.wrap(document).nodePrincipal; + let appId, inBrowser; + if (principal.appId != Components.interfaces.nsIPrincipal.UNKNOWN_APP_ID && + principal.appId != Components.interfaces.nsIPrincipal.NO_APP_ID) { + appId = principal.appId; + inBrowser = principal.isInBrowserElement; + } + SpecialPowers.getStorageUsageForURI(window.document.documentURI, + usageHandler, + appId, + inBrowser); } function getFileId(file) @@ -212,6 +200,11 @@ function getFileId(file) return utils.getFileId(file); } +function getFilePath(file) +{ + return utils.getFilePath(file); +} + function hasFileInfo(name, id) { return utils.getFileReferences(name, id); diff --git a/dom/indexedDB/test/helpers.js b/dom/indexedDB/test/helpers.js index 0a6a5f4b8b56..1e2841dadc8f 100644 --- a/dom/indexedDB/test/helpers.js +++ b/dom/indexedDB/test/helpers.js @@ -34,42 +34,14 @@ function executeSoon(aFun) } function clearAllDatabases(callback) { - function runCallback() { - SimpleTest.executeSoon(function () { callback(); }); + let principal = SpecialPowers.wrap(document).nodePrincipal; + let appId, inBrowser; + if (principal.appId != Components.interfaces.nsIPrincipal.UNKNOWN_APP_ID && + principal.appId != Components.interfaces.nsIPrincipal.NO_APP_ID) { + appId = principal.appId; + inBrowser = principal.isInBrowserElement; } - - if (!SpecialPowers.isMainProcess()) { - runCallback(); - return; - } - - let comp = SpecialPowers.wrap(Components); - - let quotaManager = - comp.classes["@mozilla.org/dom/quota/manager;1"] - .getService(comp.interfaces.nsIQuotaManager); - - let uri = SpecialPowers.wrap(document).documentURIObject; - - // We need to pass a JS callback to getUsageForURI. However, that callback - // takes an XPCOM URI object, which will cause us to throw when we wrap it - // for the content compartment. So we need to define the function in a - // privileged scope, which we do using a sandbox. - var sysPrin = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(); - var sb = new SpecialPowers.Cu.Sandbox(sysPrin); - sb.ok = ok; - sb.runCallback = runCallback; - var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) { - if (usage) { - ok(false, - "getUsageForURI returned non-zero usage after clearing all " + - "storages!"); - } - runCallback(); - }).toSource(), sb); - - quotaManager.clearStoragesForURI(uri); - quotaManager.getUsageForURI(uri, cb); + SpecialPowers.clearStorageForURI(document.documentURI, callback, appId, inBrowser); } if (!window.runTest) { @@ -84,6 +56,7 @@ if (!window.runTest) { allowUnlimitedQuota(); } + enableTesting(); enableExperimental(); enableArchiveReader(); @@ -96,13 +69,13 @@ function finishTest() resetUnlimitedQuota(); resetExperimental(); resetArchiveReader(); + resetTesting(); SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", "free"); SimpleTest.executeSoon(function() { testGenerator.close(); - //clearAllDatabases(function() { SimpleTest.finish(); }); - SimpleTest.finish(); + clearAllDatabases(function() { SimpleTest.finish(); }); }); } @@ -224,11 +197,6 @@ function removePermission(type, url) SpecialPowers.removePermission(type, url); } -function setQuota(quota) -{ - SpecialPowers.setIntPref("dom.indexedDB.warningQuota", quota); -} - function allowUnlimitedQuota(url) { addPermission("indexedDB-unlimited", true, url); @@ -265,6 +233,16 @@ function resetExperimental() SpecialPowers.clearUserPref("dom.indexedDB.experimental"); } +function enableTesting() +{ + SpecialPowers.setBoolPref("dom.indexedDB.testing", true); +} + +function resetTesting() +{ + SpecialPowers.clearUserPref("dom.indexedDB.testing"); +} + function gc() { SpecialPowers.forceGC(); diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini index f8053fc50146..7701be78e71b 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -14,235 +14,346 @@ support-files = leaving_page_iframe.html third_party_iframe1.html third_party_iframe2.html + unit/test_add_put.js + unit/test_add_twice_failure.js + unit/test_advance.js + unit/test_autoIncrement.js + unit/test_autoIncrement_indexes.js + unit/test_blocked_order.js + unit/test_clear.js + unit/test_complex_keyPaths.js + unit/test_count.js + unit/test_create_index.js + unit/test_create_index_with_integer_keys.js + unit/test_create_objectStore.js + unit/test_cursor_mutation.js + unit/test_cursor_update_updates_indexes.js + unit/test_cursors.js + unit/test_deleteDatabase.js + unit/test_deleteDatabase_interactions.js + unit/test_event_source.js + unit/test_getAll.js + unit/test_globalObjects_ipc.js + unit/test_globalObjects_other.js + unit/test_globalObjects_xpc.js + unit/test_global_data.js + unit/test_index_empty_keyPath.js + unit/test_index_getAll.js + unit/test_index_getAllObjects.js + unit/test_index_object_cursors.js + unit/test_index_update_delete.js + unit/test_indexes.js + unit/test_indexes_bad_values.js + unit/test_indexes_funny_things.js + unit/test_invalid_version.js + unit/test_invalidate.js + unit/test_key_requirements.js + unit/test_keys.js + unit/test_lowDiskSpace.js + unit/test_multientry.js + unit/test_names_sorted.js + unit/test_objectCursors.js + unit/test_objectStore_getAllKeys.js + unit/test_objectStore_inline_autoincrement_key_added_on_put.js + unit/test_objectStore_openKeyCursor.js + unit/test_objectStore_remove_values.js + unit/test_object_identity.js + unit/test_odd_result_order.js + unit/test_open_empty_db.js + unit/test_open_for_principal.js + unit/test_open_objectStore.js + unit/test_optionalArguments.js + unit/test_overlapping_transactions.js + unit/test_persistenceType.js + unit/test_put_get_values.js + unit/test_put_get_values_autoIncrement.js + unit/test_readonly_transactions.js + unit/test_remove_index.js + unit/test_remove_objectStore.js + unit/test_request_readyState.js + unit/test_setVersion.js + unit/test_setVersion_abort.js + unit/test_setVersion_events.js + unit/test_setVersion_exclusion.js + unit/test_success_events_after_abort.js + unit/test_temporary_storage.js + unit/test_traffic_jam.js + unit/test_transaction_abort.js + unit/test_transaction_abort_hang.js + unit/test_transaction_duplicate_store_names.js + unit/test_transaction_error.js + unit/test_transaction_lifetimes.js + unit/test_transaction_lifetimes_nested.js + unit/test_transaction_ordering.js + unit/test_unique_index_update.js + unit/test_writer_starvation.js webapp_clearBrowserData.js webapp_clearBrowserData_appFrame.html webapp_clearBrowserData_browserFrame.html [test_add_put.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_add_twice_failure.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_advance.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_app_isolation_inproc.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage +# The app isolation tests are only supposed to run in the main process. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s [test_app_isolation_oop.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage +# The app isolation tests are only supposed to run in the main process. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s [test_autoIncrement.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_autoIncrement_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_bfcache.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_blob_archive.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_blob_simple.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_blob_worker_crash.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, bug 927889 still present -[test_clear.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_complex_keyPaths.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_count.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_create_index.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_create_index_with_integer_keys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_create_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_cursor_mutation.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_cursor_update_updates_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_cursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_deleteDatabase.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_deleteDatabase_interactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_error_events_abort_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_event_propagation.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT, bug 780855 #Bug 931116, b2g desktop specific, initial triage -[test_event_source.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_exceptions_in_events.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_array.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_cross_database_copying.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_os_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_put_get_object.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_put_get_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_quota.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_replace.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_resurrection_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_resurrection_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_sharing.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_file_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_filehandle_append_read_data.html] -skip-if = buildapp == 'b2g' -[test_filehandle_compat.html] -skip-if = buildapp == 'b2g' -[test_filehandle_getFile.html] -skip-if = buildapp == 'b2g' -[test_filehandle_lifetimes.html] -skip-if = buildapp == 'b2g' -[test_filehandle_lifetimes_nested.html] -skip-if = buildapp == 'b2g' -[test_filehandle_location.html] -skip-if = buildapp == 'b2g' -[test_filehandle_ordering.html] -skip-if = buildapp == 'b2g' -[test_filehandle_overlapping.html] -skip-if = buildapp == 'b2g' -[test_filehandle_progress_events.html] -skip-if = buildapp == 'b2g' # b2g(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-debug(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) b2g-desktop(All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:109) -[test_filehandle_quota.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_filehandle_readonly_exceptions.html] -skip-if = buildapp == 'b2g' -[test_filehandle_request_readyState.html] -skip-if = buildapp == 'b2g' -[test_filehandle_serialization.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_filehandle_store_snapshot.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_filehandle_stream_tracking.html] -skip-if = buildapp == 'b2g' -[test_filehandle_success_events_after_abort.html] -skip-if = buildapp == 'b2g' -[test_filehandle_truncate.html] -skip-if = buildapp == 'b2g' -[test_filehandle_workers.html] -skip-if = buildapp == 'b2g' -[test_filehandle_write_read_data.html] -skip-if = buildapp == 'b2g' -[test_getAll.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_getFileId.html] -[test_get_filehandle.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_globalObjects_content.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_global_data.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_index_empty_keyPath.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_index_getAll.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_index_getAllObjects.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_index_object_cursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_index_update_delete.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_indexes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_indexes_bad_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_indexes_funny_things.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_invalid_version.html] -[test_key_requirements.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_keys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_leaving_page.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_lowDiskSpace.html] -skip-if = buildapp == 'b2g' # b2g(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) b2g-debug(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) b2g-desktop(this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions) -[test_multientry.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_names_sorted.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_objectCursors.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_objectStore_getAllKeys.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_objectStore_inline_autoincrement_key_added_on_put.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_objectStore_openKeyCursor.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_objectStore_remove_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_object_identity.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_odd_result_order.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_open_empty_db.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_open_for_principal.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_open_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_optionalArguments.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_overlapping_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_persistenceType.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage -[test_put_get_values.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_put_get_values_autoIncrement.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_readonly_transactions.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_remove_index.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_remove_objectStore.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_request_readyState.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_setVersion.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_setVersion_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_setVersion_events.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_setVersion_exclusion.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_success_events_after_abort.html] -skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) -[test_third_party.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #TIMED_OUT #Bug 931116, b2g desktop specific, initial triage -[test_traffic_jam.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_abort.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_abort_hang.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_duplicate_store_names.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_error.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_lifetimes.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_lifetimes_nested.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_transaction_ordering.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_unique_index_update.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage -[test_webapp_clearBrowserData_inproc_inproc.html] -skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage -[test_webapp_clearBrowserData_inproc_oop.html] -skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage -[test_webapp_clearBrowserData_oop_inproc.html] -skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #No test app installed #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_blocked_order.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_bug937006.html] -skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_clear.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_complex_keyPaths.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_count.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_create_index.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_create_index_with_integer_keys.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_create_objectStore.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_cursor_mutation.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_cursor_update_updates_indexes.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_cursors.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_deleteDatabase.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_deleteDatabase_interactions.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_disabled_quota_prompt.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_error_events_abort_transactions.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_event_propagation.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_event_source.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_exceptions_in_events.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_array.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_cross_database_copying.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_delete.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_os_delete.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_put_get_object.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_put_get_values.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_quota.html] +# Quota handling disabled for now. +skip-if = true +# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_replace.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_resurrection_delete.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_resurrection_transaction_abort.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_sharing.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_file_transaction_abort.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_filehandle_append_read_data.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_compat.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_getFile.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_lifetimes.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_lifetimes_nested.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_location.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_ordering.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_overlapping.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_progress_events.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_quota.html] +# FileHandle is not supported in child processes. +# Quota handling disabled for now. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || true +[test_filehandle_readonly_exceptions.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_request_readyState.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_serialization.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_store_snapshot.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_stream_tracking.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_success_events_after_abort.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_truncate.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_workers.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_filehandle_write_read_data.html] +# FileHandle is not supported in child processes. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[test_getAll.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_get_filehandle.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_globalObjects_content.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_global_data.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_index_empty_keyPath.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_index_getAll.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_index_getAllObjects.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_index_object_cursors.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_index_update_delete.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_indexes.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_indexes_bad_values.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_indexes_funny_things.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_invalid_version.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_invalidate.html] +# disabled for the moment +skip-if = true +# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_key_requirements.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_keys.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_leaving_page.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_lowDiskSpace.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_multientry.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_names_sorted.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_objectCursors.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_objectStore_getAllKeys.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_objectStore_inline_autoincrement_key_added_on_put.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_objectStore_openKeyCursor.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_objectStore_remove_values.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_object_identity.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_odd_result_order.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_open_empty_db.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_open_for_principal.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_open_objectStore.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_optionalArguments.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_overlapping_transactions.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_persistenceType.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_put_get_values.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_put_get_values_autoIncrement.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_readonly_transactions.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_remove_index.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_remove_objectStore.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_request_readyState.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_setVersion.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_setVersion_abort.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_setVersion_events.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_setVersion_exclusion.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_success_events_after_abort.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_third_party.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_traffic_jam.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_abort.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_abort_hang.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_duplicate_store_names.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_error.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_lifetimes.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_lifetimes_nested.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_transaction_ordering.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_unique_index_update.html] +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_webapp_clearBrowserData_inproc_inproc.html] +# The clearBrowserData tests are only supposed to run in the main process. +# They currently time out on android. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' +[test_webapp_clearBrowserData_inproc_oop.html] +# The clearBrowserData tests are only supposed to run in the main process. +# They currently time out on android. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' +[test_webapp_clearBrowserData_oop_inproc.html] +# The clearBrowserData tests are only supposed to run in the main process. +# They currently time out on android. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android' diff --git a/dom/indexedDB/test/test_blob_simple.html b/dom/indexedDB/test/test_blob_simple.html index ccf753136b0c..9714788ec412 100644 --- a/dom/indexedDB/test/test_blob_simple.html +++ b/dom/indexedDB/test/test_blob_simple.html @@ -41,11 +41,14 @@ objectStore.add(data).onsuccess = grabEventAndContinueHandler; event = yield undefined; + info("Added blob to database once"); + let key = event.target.result; objectStore.add(data).onsuccess = grabEventAndContinueHandler; event = yield undefined; + info("Added blob to database twice"); info("Let's retrieve the blob again and verify the contents is the same."); @@ -53,6 +56,8 @@ objectStore.get(key).onsuccess = grabEventAndContinueHandler; event = yield undefined; + info("Got blob from database"); + let fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(event.target.result.blob); @@ -68,6 +73,8 @@ objectStore.get(key).onsuccess = grabEventAndContinueHandler; event = yield undefined; + info("Got blob from database"); + let blobURL = URL.createObjectURL(event.target.result.blob); let xhr = new XMLHttpRequest(); @@ -102,10 +109,12 @@ objectStore.openCursor().onsuccess = function(event) { let cursor = event.target.result; if (cursor) { + info("Got item from cursor"); cursorResults.push(cursor.value); cursor.continue(); } else { + info("Finished cursor"); continueToNextStep(); } }; @@ -127,6 +136,8 @@ index.get(INDEX_KEY).onsuccess = grabEventAndContinueHandler; event = yield undefined; + info("Got blob from database"); + fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(event.target.result.blob); @@ -153,10 +164,12 @@ index.openCursor().onsuccess = function(event) { let cursor = event.target.result; if (cursor) { + info("Got item from cursor"); cursorResults.push(cursor.value); cursor.continue(); } else { + info("Finished cursor"); continueToNextStep(); } }; @@ -226,11 +239,15 @@ event = yield undefined; let blobFromDB = event.target.result.blob; + info("Got blob from database"); + let txn = db.transaction("foo", "readwrite"); txn.objectStore("foo").put(event.target.result, key); txn.oncomplete = grabEventAndContinueHandler; event = yield undefined; + info("Stored blob back into database"); + let fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(blobFromDB); diff --git a/dom/indexedDB/test/test_blocked_order.html b/dom/indexedDB/test/test_blocked_order.html new file mode 100644 index 000000000000..9b82995a399a --- /dev/null +++ b/dom/indexedDB/test/test_blocked_order.html @@ -0,0 +1,18 @@ + + + + IndexedDB Test + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_disabled_quota_prompt.html b/dom/indexedDB/test/test_disabled_quota_prompt.html new file mode 100644 index 000000000000..7565a73a6f3e --- /dev/null +++ b/dom/indexedDB/test/test_disabled_quota_prompt.html @@ -0,0 +1,118 @@ + + + + Indexed Database Property Test + + + + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_file_cross_database_copying.html b/dom/indexedDB/test/test_file_cross_database_copying.html index 69bbd74e7c5b..7a66cb322e7f 100644 --- a/dom/indexedDB/test/test_file_cross_database_copying.html +++ b/dom/indexedDB/test/test_file_cross_database_copying.html @@ -62,8 +62,7 @@ continue; } - isnot(SpecialPowers.getMozFullPath(result), - SpecialPowers.getMozFullPath(refResult), "Different os files"); + isnot(getFilePath(result), getFilePath(refResult), "Different os files"); } for (let i = 1; i < databases.length; i++) { @@ -86,8 +85,7 @@ verifyBlob(result, refResult, 2); yield undefined; - isnot(SpecialPowers.getMozFullPath(result), - SpecialPowers.getMozFullPath(refResult), "Different os files"); + isnot(getFilePath(result), getFilePath(refResult), "Different os files"); } is(bufferCache.length, 2, "Correct length"); diff --git a/dom/indexedDB/test/test_file_os_delete.html b/dom/indexedDB/test/test_file_os_delete.html index f24c793f010f..6403f3f514ad 100644 --- a/dom/indexedDB/test/test_file_os_delete.html +++ b/dom/indexedDB/test/test_file_os_delete.html @@ -83,6 +83,13 @@ scheduleGC(); yield undefined; + // This isn't really necessary but in order to ensure that our files have + // been deleted we need to round-trip with the PBackground thread... + let request = indexedDB.deleteDatabase(name + "this can't exist"); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + yield undefined; + getUsage(grabFileUsageAndContinueHandler); let endUsage = yield undefined; diff --git a/dom/indexedDB/test/test_file_sharing.html b/dom/indexedDB/test/test_file_sharing.html index 1273f1f3073b..2cb3f47544d1 100644 --- a/dom/indexedDB/test/test_file_sharing.html +++ b/dom/indexedDB/test/test_file_sharing.html @@ -61,8 +61,7 @@ continue; } - is(SpecialPowers.getMozFullPath(result), - SpecialPowers.getMozFullPath(refResult), "The same os file"); + is(getFilePath(result), getFilePath(refResult), "The same os file"); } for (let i = 1; i < objectStoreInfo.length; i++) { @@ -85,8 +84,7 @@ verifyBlob(result, refResult, 1); yield undefined; - is(SpecialPowers.getMozFullPath(result), - SpecialPowers.getMozFullPath(refResult), "The same os file"); + is(getFilePath(result), getFilePath(refResult), "The same os file"); } is(bufferCache.length, 2, "Correct length"); diff --git a/dom/indexedDB/test/test_filehandle_compat.html b/dom/indexedDB/test/test_filehandle_compat.html index 6b5bc15abfa6..028ebd5f5449 100644 --- a/dom/indexedDB/test/test_filehandle_compat.html +++ b/dom/indexedDB/test/test_filehandle_compat.html @@ -40,7 +40,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_getFile.html b/dom/indexedDB/test/test_filehandle_getFile.html index eea32932bd38..46e012a59dcd 100644 --- a/dom/indexedDB/test/test_filehandle_getFile.html +++ b/dom/indexedDB/test/test_filehandle_getFile.html @@ -44,7 +44,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_lifetimes.html b/dom/indexedDB/test/test_filehandle_lifetimes.html index bb8ba8d7e9d8..9a24632a4284 100644 --- a/dom/indexedDB/test/test_filehandle_lifetimes.html +++ b/dom/indexedDB/test/test_filehandle_lifetimes.html @@ -48,7 +48,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html index 31b91fd7382d..2db35eebfdb7 100644 --- a/dom/indexedDB/test/test_filehandle_lifetimes_nested.html +++ b/dom/indexedDB/test/test_filehandle_lifetimes_nested.html @@ -60,7 +60,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_location.html b/dom/indexedDB/test/test_filehandle_location.html index 2b0682e49fd4..052c2ee52907 100644 --- a/dom/indexedDB/test/test_filehandle_location.html +++ b/dom/indexedDB/test/test_filehandle_location.html @@ -95,7 +95,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_ordering.html b/dom/indexedDB/test/test_filehandle_ordering.html index 33f5e262f6ed..71b181f61340 100644 --- a/dom/indexedDB/test/test_filehandle_ordering.html +++ b/dom/indexedDB/test/test_filehandle_ordering.html @@ -53,7 +53,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_overlapping.html b/dom/indexedDB/test/test_filehandle_overlapping.html index 75fca15f4cb5..6aaed5e376a9 100644 --- a/dom/indexedDB/test/test_filehandle_overlapping.html +++ b/dom/indexedDB/test/test_filehandle_overlapping.html @@ -64,7 +64,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html index 3bb4f5e810ea..d65656547b9f 100644 --- a/dom/indexedDB/test/test_filehandle_readonly_exceptions.html +++ b/dom/indexedDB/test/test_filehandle_readonly_exceptions.html @@ -72,7 +72,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_request_readyState.html b/dom/indexedDB/test/test_filehandle_request_readyState.html index 8fc19894a38c..ff28544669a4 100644 --- a/dom/indexedDB/test/test_filehandle_request_readyState.html +++ b/dom/indexedDB/test/test_filehandle_request_readyState.html @@ -60,7 +60,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html index 9fd902c6abc3..70b9acdbb691 100644 --- a/dom/indexedDB/test/test_filehandle_success_events_after_abort.html +++ b/dom/indexedDB/test/test_filehandle_success_events_after_abort.html @@ -65,7 +65,7 @@ yield undefined; } - + diff --git a/dom/indexedDB/test/test_invalidate.html b/dom/indexedDB/test/test_invalidate.html new file mode 100644 index 000000000000..45651953cb41 --- /dev/null +++ b/dom/indexedDB/test/test_invalidate.html @@ -0,0 +1,18 @@ + + + + IndexedDB Test + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_persistenceType.html b/dom/indexedDB/test/test_persistenceType.html index 6d9819abcedb..6b164c002170 100644 --- a/dom/indexedDB/test/test_persistenceType.html +++ b/dom/indexedDB/test/test_persistenceType.html @@ -29,10 +29,21 @@ let request = indexedDB.open(name, { version: version, storage: "persistent" }); + request.onerror = grabEventAndContinueHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "error", "Got error event"); + is(event.target, request, "Got correct target"); + is(event.target.error.name, "UnknownError", "Got correct error name"); + event.preventDefault(); + + request = indexedDB.open(name, { version: version }); request.onerror = errorHandler; request.onupgradeneeded = grabEventAndContinueHandler; request.onsuccess = grabEventAndContinueHandler; - let event = yield undefined; + event = yield undefined; is(event.type, "upgradeneeded", "Got correct event type"); diff --git a/dom/indexedDB/test/unit/test_blocked_order.js b/dom/indexedDB/test/unit/test_blocked_order.js new file mode 100644 index 000000000000..3e54ccece6ca --- /dev/null +++ b/dom/indexedDB/test/unit/test_blocked_order.js @@ -0,0 +1,150 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +let testGenerator = testSteps(); + +function testSteps() +{ + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + const databaseCount = 10; + + // Test 1: Make sure basic versionchange events work and that they don't + // trigger blocked events. + info("Opening " + databaseCount + " databases with version 1"); + + let databases = []; + + for (let i = 0; i < databaseCount; i++) { + let thisIndex = i; + + info("Opening database " + thisIndex); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "success", "Got success event"); + + let db = request.result; + is(db.version, 1, "Got version 1"); + + db.onversionchange = function(event) { + info("Closing database " + thisIndex); + db.close(); + + databases.splice(databases.indexOf(db), 1); + }; + + databases.push(db); + } + + is(databases.length, databaseCount, "Created all databases with version 1"); + + info("Opening database with version 2"); + + let request = indexedDB.open(databaseName, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + request.onblocked = function(event) { + ok(false, "Should not receive a blocked event"); + }; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + is(databases.length, 0, "All databases with version 1 were closed"); + + let db = request.result; + is(db.version, 2, "Got version 2"); + + info("Deleting database with version 2"); + db.close(); + + request = indexedDB.deleteDatabase(databaseName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + // Test 2: Make sure blocked events aren't delivered until all versionchange + // events have been delivered. + info("Opening " + databaseCount + " databases with version 1"); + + for (let i = 0; i < databaseCount; i++) { + let thisIndex = i; + + info("Opening database " + thisIndex); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onblocked = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + let event = yield undefined; + + is(event.type, "success", "Got success event"); + + let db = request.result; + is(db.version, 1, "Got version 1"); + + db.onversionchange = function(event) { + if (thisIndex == (databaseCount - 1)) { + info("Closing all databases with version 1"); + + for (let j = 0; j < databases.length; j++) { + databases[j].close(); + } + + databases = []; + info("Done closing all databases with version 1"); + } else { + info("Not closing database " + thisIndex); + } + }; + + databases.push(db); + } + + is(databases.length, databaseCount, "Created all databases with version 1"); + + info("Opening database with version 2"); + + request = indexedDB.open(databaseName, 2); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + request.onblocked = function(event) { + ok(false, "Should not receive a blocked event"); + }; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + is(databases.length, 0, "All databases with version 1 were closed"); + + db = request.result; + is(db.version, 2, "Got version 2"); + + info("Deleting database with version 2"); + db.close(); + + request = indexedDB.deleteDatabase(databaseName); + request.onerror = errorHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + is(event.type, "success", "Got success event"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_indexes.js b/dom/indexedDB/test/unit/test_indexes.js index baae78cb3f8d..3b9f73fc5884 100644 --- a/dom/indexedDB/test/unit/test_indexes.js +++ b/dom/indexedDB/test/unit/test_indexes.js @@ -97,7 +97,7 @@ function testSteps() is(found, true, "objectStore has our index"); let index = objectStore.index(indexData[i].name); is(index.name, indexData[i].name, "Correct name"); - is(index.storeName, objectStore.name, "Correct store name"); + is(index.objectStore.name, objectStore.name, "Correct store name"); is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); is(index.unique, indexData[i].options.unique ? true : false, "Correct unique value"); diff --git a/dom/indexedDB/test/unit/test_indexes_funny_things.js b/dom/indexedDB/test/unit/test_indexes_funny_things.js index a44f8eb18401..33386e079f97 100644 --- a/dom/indexedDB/test/unit/test_indexes_funny_things.js +++ b/dom/indexedDB/test/unit/test_indexes_funny_things.js @@ -93,7 +93,7 @@ function testSteps() is(found, true, "objectStore has our index"); let index = objectStore.index(indexData[i].name); is(index.name, indexData[i].name, "Correct name"); - is(index.storeName, objectStore.name, "Correct store name"); + is(index.objectStore.name, objectStore.name, "Correct store name"); is(index.keyPath, indexData[i].keyPath, "Correct keyPath"); is(index.unique, indexData[i].options.unique ? true : false, "Correct unique value"); diff --git a/dom/indexedDB/test/unit/test_invalidate.js b/dom/indexedDB/test/unit/test_invalidate.js new file mode 100644 index 000000000000..fe34bb015966 --- /dev/null +++ b/dom/indexedDB/test/unit/test_invalidate.js @@ -0,0 +1,82 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +let testGenerator = testSteps(); + +function testSteps() +{ + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + + let dbCount = 0; + + // Test invalidating during a versionchange transaction. + info("Opening database " + ++dbCount); + + let request = indexedDB.open(databaseName, dbCount); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + let event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database " + dbCount); + + request.onupgradeneeded = unexpectedSuccessHandler; + + let objStore = + request.result.createObjectStore("foo", { autoIncrement: true }); + objStore.createIndex("fooIndex", "fooIndex", { unique: true }); + objStore.put({ foo: 1 }); + objStore.get(1); + objStore.count(); + objStore.openCursor(); + objStore.delete(1); + + info("Invalidating database " + dbCount); + + clearAllDatabases(continueToNextStepSync); + + objStore = request.result.createObjectStore("bar"); + objStore.createIndex("barIndex", "barIndex", { multiEntry: true }); + objStore.put({ bar: 1, barIndex: [ 0, 1 ] }, 10); + objStore.get(10); + objStore.count(); + objStore.openCursor(); + objStore.delete(10); + + yield undefined; + + executeSoon(continueToNextStepSync); + yield undefined; + + // Test invalidating after the complete event of a versionchange transaction. + info("Opening database " + ++dbCount); + + request = indexedDB.open(databaseName, dbCount); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = unexpectedSuccessHandler; + event = yield undefined; + + is(event.type, "upgradeneeded", "Upgrading database " + dbCount); + + request.onupgradeneeded = unexpectedSuccessHandler; + + request.transaction.oncomplete = grabEventAndContinueHandler; + event = yield undefined; + + is(event.type, "complete", + "Got complete event for versionchange transaction on database " + dbCount); + + info("Invalidating database " + dbCount); + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + executeSoon(continueToNextStepSync); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_setVersion_events.js b/dom/indexedDB/test/unit/test_setVersion_events.js index 14dc55ff17df..7560d1fa2cba 100644 --- a/dom/indexedDB/test/unit/test_setVersion_events.js +++ b/dom/indexedDB/test/unit/test_setVersion_events.js @@ -15,12 +15,12 @@ function testSteps() // Sanity checks ok(request instanceof IDBRequest, "Request should be an IDBRequest"); ok(request instanceof IDBOpenDBRequest, "Request should be an IDBOpenDBRequest"); - //ok(request instanceof EventTarget, "Request should be an EventTarget"); + ok(request instanceof EventTarget, "Request should be an EventTarget"); is(request.source, null, "Request should have no source"); try { request.result; ok(false, "Getter should have thrown!"); - } catch (e if e.result == 0x80660006 /* NS_ERROR_DOM_INDEXEDDB_NOTALLOWED_ERR */) { + } catch (e if e.result == 0x8053000b /* NS_ERROR_DOM_INVALID_STATE_ERR */) { ok(true, "Getter threw the right exception"); } diff --git a/dom/indexedDB/test/unit/test_temporary_storage.js b/dom/indexedDB/test/unit/test_temporary_storage.js index 06443af431d0..5ba3609e467a 100644 --- a/dom/indexedDB/test/unit/test_temporary_storage.js +++ b/dom/indexedDB/test/unit/test_temporary_storage.js @@ -10,15 +10,15 @@ function testSteps() const name = this.window ? window.location.pathname : "Splendid Test"; const urls = [ - { url: "http://www.alpha.com", flags: [true, true, false, false] }, + { url: "http://www.alpha.com", flags: [true, true, true, true] }, { url: "http://www.beta.com", flags: [true, false, false, false] }, { url: "http://www.gamma.com", flags: [true, true, false, false] }, { url: "http://www.delta.com", flags: [true, true, false, false] }, { url: "http://www.epsilon.com", flags: [true, true, false, false] }, { url: "http://www2.alpha.com", flags: [true, true, false, false] }, { url: "http://www2.beta.com", flags: [true, true, false, false] }, - { url: "http://www2.gamma.com", flags: [true, true, true, false] }, - { url: "http://www2.delta.com", flags: [true, true, true, true] }, + { url: "http://www2.gamma.com", flags: [true, true, false, false] }, + { url: "http://www2.delta.com", flags: [true, true, true, false] }, { url: "http://www2.epsilon.com", flags: [true, true, true, true] }, { url: "http://joe.blog.alpha.com", flags: [true, true, true, true] }, { url: "http://joe.blog.beta.com", flags: [true, true, true, true] }, @@ -78,13 +78,14 @@ function testSteps() let handledIndex = 0; function usageHandler(usage, fileUsage) { - if (urls[handledIndex].flags[stageIndex - 1]) { - ok(usage > 0, "Correct usage"); + let data = urls[handledIndex++]; + if (data.flags[stageIndex - 1]) { + ok(usage > 0, "Non-zero usage for '" + data.url + "'"); } else { - ok(usage == 0, "Correct usage"); + ok(usage == 0, "Zero usage for '" + data.url + "'"); } - if (++handledIndex == urls.length) { + if (handledIndex == urls.length) { continueToNextStep(); } } @@ -112,10 +113,13 @@ function testSteps() setLimit(lastIndex * dbSize / 1024); quotaManager.clear(); - // Stage 1 + info("Stage 1"); + for (let i = 0; i < lastIndex; i++) { let data = urls[i]; + info("Opening database for " + data.url); + request = indexedDB.openForPrincipal(getPrincipal(data.url), name, { storage: "temporary" }); request.onerror = errorHandler; @@ -144,7 +148,8 @@ function testSteps() checkUsage(1); yield undefined; - // Stage 2 + info("Stage 2"); + for (let i = 1; i < urls.length; i++) { databases[i] = null; @@ -182,7 +187,8 @@ function testSteps() checkUsage(2); yield undefined; - // Stage 3 + info("Stage 3"); + setLimit(14 * dbSize / 1024); quotaManager.reset(); @@ -199,7 +205,8 @@ function testSteps() checkUsage(3); yield undefined; - // Stage 4 + info("Stage 4"); + let trans = db.transaction(["foo"], "readwrite"); let blob = Blob(["bar"]); @@ -214,7 +221,8 @@ function testSteps() checkUsage(4); yield undefined; - // Cleanup + info("Cleanup"); + setLimit(); quotaManager.reset(); diff --git a/dom/indexedDB/test/unit/xpcshell-child-process.ini b/dom/indexedDB/test/unit/xpcshell-child-process.ini new file mode 100644 index 000000000000..d9a7b7589c9e --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-child-process.ini @@ -0,0 +1,18 @@ +# 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/. + +[DEFAULT] +dupe-manifest = +head = xpcshell-head-child-process.js +tail = +support-files = + GlobalObjectsChild.js + GlobalObjectsComponent.js + GlobalObjectsComponent.manifest + GlobalObjectsModule.jsm + GlobalObjectsSandbox.js + xpcshell-head-parent-process.js + xpcshell-shared.ini + +[include:xpcshell-shared.ini] diff --git a/dom/indexedDB/test/unit/xpcshell-head-child-process.js b/dom/indexedDB/test/unit/xpcshell-head-child-process.js new file mode 100644 index 000000000000..2e704f8dc091 --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-head-child-process.js @@ -0,0 +1,27 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function run_test() { + const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; + + const INDEXEDDB_HEAD_FILE = "xpcshell-head-parent-process.js"; + const INDEXEDDB_PREF_EXPERIMENTAL = "dom.indexedDB.experimental"; + + // IndexedDB needs a profile. + do_get_profile(); + + let thisTest = _TEST_FILE.toString().replace(/\\/g, "/"); + thisTest = thisTest.substring(thisTest.lastIndexOf("/") + 1); + + _HEAD_FILES.push(do_get_file(INDEXEDDB_HEAD_FILE).path.replace(/\\/g, "/")); + + + let prefs = + Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService) + .getBranch(null); + prefs.setBoolPref(INDEXEDDB_PREF_EXPERIMENTAL, true); + + run_test_in_child(thisTest); +} diff --git a/dom/indexedDB/test/unit/head.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js similarity index 74% rename from dom/indexedDB/test/unit/head.js rename to dom/indexedDB/test/unit/xpcshell-head-parent-process.js index 1ec20b3463e0..cc1e05b0ada4 100644 --- a/dom/indexedDB/test/unit/head.js +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -8,17 +8,14 @@ const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; const DOMException = Ci.nsIDOMDOMException; function is(a, b, msg) { - dump("is(" + a + ", " + b + ", \"" + msg + "\")"); do_check_eq(a, b, Components.stack.caller); } function ok(cond, msg) { - dump("ok(" + cond + ", \"" + msg + "\")"); do_check_true(!!cond, Components.stack.caller); } function isnot(a, b, msg) { - dump("isnot(" + a + ", " + b + ", \"" + msg + "\")"); do_check_neq(a, b, Components.stack.caller); } @@ -27,7 +24,7 @@ function executeSoon(fun) { } function todo(condition, name, diag) { - dump("TODO: ", diag); + todo_check_true(condition, Components.stack.caller); } function info(name, message) { @@ -41,10 +38,13 @@ function run_test() { if (!this.runTest) { this.runTest = function() { - // XPCShell does not get a profile by default. - do_get_profile(); + if (SpecialPowers.isMainProcess()) { + // XPCShell does not get a profile by default. + do_get_profile(); - enableExperimental(); + enableTesting(); + enableExperimental(); + } Cu.importGlobalProperties(["indexedDB"]); @@ -55,9 +55,13 @@ if (!this.runTest) { function finishTest() { - resetExperimental(); - SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", - "free"); + if (SpecialPowers.isMainProcess()) { + resetExperimental(); + resetTesting(); + + SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher", + "free"); + } do_execute_soon(function(){ testGenerator.close(); @@ -79,7 +83,11 @@ function continueToNextStep() function errorHandler(event) { - dump("indexedDB error: " + event.target.error.name); + try { + dump("indexedDB error: " + event.target.error.name); + } catch(e) { + dump("indexedDB error: " + e); + } do_check_true(false); finishTest(); } @@ -158,11 +166,6 @@ function removePermission(permission, url) throw "removePermission"; } -function setQuota(quota) -{ - throw "setQuota"; -} - function allowIndexedDB(url) { throw "allowIndexedDB"; @@ -193,10 +196,20 @@ function resetExperimental() SpecialPowers.clearUserPref("dom.indexedDB.experimental"); } +function enableTesting() +{ + SpecialPowers.setBoolPref("dom.indexedDB.testing", true); +} + +function resetTesting() +{ + SpecialPowers.clearUserPref("dom.indexedDB.testing"); +} + function gc() { - Components.utils.forceGC(); - Components.utils.forceCC(); + Cu.forceGC(); + Cu.forceCC(); } function scheduleGC() @@ -217,6 +230,42 @@ function setTimeout(fun, timeout) { return timer; } +function clearAllDatabases(callback) { + if (!SpecialPowers.isMainProcess()) { + throw new Error("clearAllDatabases not implemented for child processes!"); + } + + let quotaManager = Cc["@mozilla.org/dom/quota/manager;1"] + .getService(Ci.nsIQuotaManager); + + const quotaPref = "dom.quotaManager.testing"; + + let oldPrefValue; + if (SpecialPowers._getPrefs().prefHasUserValue(quotaPref)) { + oldPrefValue = SpecialPowers.getBoolPref(quotaPref); + } + + SpecialPowers.setBoolPref(quotaPref, true); + + try { + quotaManager.clear(); + } catch(e) { + if (oldPrefValue !== undefined) { + SpecialPowers.setBoolPref(quotaPref, oldPrefValue); + } else { + SpecialPowers.clearUserPref(quotaPref); + } + throw e; + } + + let uri = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService) + .newURI("http://foo.com", null, null); + quotaManager.getUsageForURI(uri, function(usage, fileUsage) { + callback(); + }); +} + var SpecialPowers = { isMainProcess: function() { return Components.classes["@mozilla.org/xre/app-info;1"] diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini new file mode 100644 index 000000000000..cb7dc0e2eb96 --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -0,0 +1,26 @@ +# 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/. + +[DEFAULT] +dupe-manifest = +head = xpcshell-head-parent-process.js +tail = +support-files = + GlobalObjectsChild.js + GlobalObjectsComponent.js + GlobalObjectsComponent.manifest + GlobalObjectsModule.jsm + GlobalObjectsSandbox.js + xpcshell-shared.ini + +[include:xpcshell-shared.ini] + +[test_globalObjects_ipc.js] +[test_invalidate.js] +# disabled for the moment. +skip-if = true +[test_lowDiskSpace.js] +[test_temporary_storage.js] +# bug 951017: intermittent failure on Android x86 emulator +skip-if = os == "android" && processor == "x86" diff --git a/dom/indexedDB/test/unit/mochitest.ini b/dom/indexedDB/test/unit/xpcshell-shared.ini similarity index 88% rename from dom/indexedDB/test/unit/mochitest.ini rename to dom/indexedDB/test/unit/xpcshell-shared.ini index 2cfaf2f670e4..75ea49ade383 100644 --- a/dom/indexedDB/test/unit/mochitest.ini +++ b/dom/indexedDB/test/unit/xpcshell-shared.ini @@ -1,16 +1,20 @@ -[DEFAULT] +# 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/. [test_add_put.js] [test_add_twice_failure.js] [test_advance.js] [test_autoIncrement.js] [test_autoIncrement_indexes.js] +[test_blocked_order.js] [test_clear.js] [test_complex_keyPaths.js] [test_count.js] [test_create_index.js] [test_create_index_with_integer_keys.js] [test_create_objectStore.js] +[test_cursor_cycle.js] [test_cursor_mutation.js] [test_cursor_update_updates_indexes.js] [test_cursors.js] @@ -18,7 +22,6 @@ [test_deleteDatabase_interactions.js] [test_event_source.js] [test_getAll.js] -[test_globalObjects_ipc.js] [test_globalObjects_other.js] [test_globalObjects_xpc.js] [test_global_data.js] @@ -33,15 +36,14 @@ [test_invalid_version.js] [test_key_requirements.js] [test_keys.js] -[test_lowDiskSpace.js] [test_multientry.js] [test_names_sorted.js] +[test_object_identity.js] [test_objectCursors.js] [test_objectStore_getAllKeys.js] [test_objectStore_inline_autoincrement_key_added_on_put.js] [test_objectStore_openKeyCursor.js] [test_objectStore_remove_values.js] -[test_object_identity.js] [test_odd_result_order.js] [test_open_empty_db.js] [test_open_for_principal.js] @@ -60,7 +62,6 @@ [test_setVersion_events.js] [test_setVersion_exclusion.js] [test_success_events_after_abort.js] -[test_temporary_storage.js] [test_traffic_jam.js] [test_transaction_abort.js] [test_transaction_abort_hang.js] diff --git a/dom/indexedDB/test/unit/xpcshell.ini b/dom/indexedDB/test/unit/xpcshell.ini deleted file mode 100644 index 306bd42e58cf..000000000000 --- a/dom/indexedDB/test/unit/xpcshell.ini +++ /dev/null @@ -1,90 +0,0 @@ -[DEFAULT] -head = head.js -tail = -support-files = - GlobalObjectsChild.js - GlobalObjectsComponent.js - GlobalObjectsComponent.manifest - GlobalObjectsModule.jsm - GlobalObjectsSandbox.js - -# When adding files here please also update ipc/unit/xpcshell.ini! - -[test_add_put.js] -[test_add_twice_failure.js] -[test_advance.js] -[test_autoIncrement.js] -[test_autoIncrement_indexes.js] -[test_clear.js] -[test_complex_keyPaths.js] -[test_count.js] -[test_create_index.js] -[test_create_index_with_integer_keys.js] -[test_create_objectStore.js] -[test_cursor_cycle.js] -[test_cursor_mutation.js] -[test_cursor_update_updates_indexes.js] -[test_cursors.js] -[test_deleteDatabase.js] -[test_deleteDatabase_interactions.js] -[test_event_source.js] -[test_getAll.js] -[test_globalObjects_ipc.js] -# FIXME/bug 575918: out-of-process xpcshell is broken on OS X -skip-if = buildapp == 'mulet' || os == "mac" || os == "android" -[test_globalObjects_other.js] -[test_globalObjects_xpc.js] -[test_global_data.js] -[test_index_empty_keyPath.js] -[test_index_getAll.js] -[test_index_getAllObjects.js] -[test_index_object_cursors.js] -[test_index_update_delete.js] -[test_indexes.js] -[test_indexes_bad_values.js] -[test_indexes_funny_things.js] -[test_invalid_version.js] -[test_key_requirements.js] -[test_keys.js] -[test_lowDiskSpace.js] -[test_multientry.js] -[test_names_sorted.js] -[test_object_identity.js] -[test_objectCursors.js] -[test_objectStore_getAllKeys.js] -[test_objectStore_inline_autoincrement_key_added_on_put.js] -[test_objectStore_openKeyCursor.js] -[test_objectStore_remove_values.js] -[test_odd_result_order.js] -[test_open_empty_db.js] -[test_open_for_principal.js] -[test_open_objectStore.js] -[test_optionalArguments.js] -[test_overlapping_transactions.js] -[test_persistenceType.js] -[test_put_get_values.js] -[test_put_get_values_autoIncrement.js] -[test_readonly_transactions.js] -[test_remove_index.js] -[test_remove_objectStore.js] -[test_request_readyState.js] -[test_setVersion.js] -[test_setVersion_abort.js] -[test_setVersion_events.js] -[test_setVersion_exclusion.js] -[test_success_events_after_abort.js] -[test_temporary_storage.js] -# bug 951017: intermittent failure on Android x86 emulator -skip-if = os == "android" && processor == "x86" -[test_traffic_jam.js] -[test_transaction_abort.js] -[test_transaction_abort_hang.js] -[test_transaction_duplicate_store_names.js] -[test_transaction_error.js] -[test_transaction_lifetimes.js] -[test_transaction_lifetimes_nested.js] -[test_transaction_ordering.js] -[test_unique_index_update.js] -[test_writer_starvation.js] - -# When adding files here please also update ipc/unit/xpcshell.ini! diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index f7acd3f8dacd..53e064f010fa 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -51,7 +51,7 @@ interface nsITranslationNodeList; interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; -[scriptable, uuid(669095d8-1b96-472f-a48d-022adde26cfd)] +[scriptable, uuid(ed12c067-506b-4b10-9853-d4bba5c1bb6c)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1473,6 +1473,13 @@ interface nsIDOMWindowUtils : nsISupports { */ [implicit_jscontext] long long getFileId(in jsval aFile); + /** + * Get internal file path of the stored file or file handle. + * + * TODO: File handle objects are actually not supported at the moment. + */ + [implicit_jscontext] AString getFilePath(in jsval aFile); + /** * Get file ref count info for given database and file id. * diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index 20bf89e4c583..f972578eb3ca 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -2,8 +2,10 @@ * 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 "Blob.h" +#include "BlobChild.h" +#include "BlobParent.h" +#include "BackgroundParent.h" #include "ContentChild.h" #include "ContentParent.h" #include "FileDescriptorSetChild.h" @@ -16,8 +18,11 @@ #include "mozilla/dom/nsIContentChild.h" #include "mozilla/dom/PBlobStreamChild.h" #include "mozilla/dom/PBlobStreamParent.h" -#include "mozilla/dom/PFileDescriptorSetParent.h" +#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/PBackgroundParent.h" +#include "mozilla/ipc/PFileDescriptorSetParent.h" #include "nsCOMPtr.h" #include "nsDOMFile.h" #include "nsIDOMFile.h" @@ -27,8 +32,20 @@ #include "nsIRemoteBlob.h" #include "nsISeekableStream.h" #include "nsNetCID.h" -#include "nsProxyRelease.h" #include "nsThreadUtils.h" +#include "nsXULAppAPI.h" + +#ifdef DEBUG +#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread(). +#endif + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif #define PRIVATE_REMOTE_INPUT_STREAM_IID \ {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}} @@ -45,21 +62,138 @@ enum ActorType ParentActor }; -// Ensure that a nsCOMPtr/nsRefPtr is released on the main thread. +template +struct ConcreteManagerTypeTraits; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef ContentChild Type; +}; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef PBackgroundChild Type; +}; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef ContentParent Type; +}; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef PBackgroundParent Type; +}; + +template +void AssertCorrectThreadForManager(ManagerType* aManager); + +template <> +void AssertCorrectThreadForManager(nsIContentChild* aManager) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +template <> +void AssertCorrectThreadForManager(nsIContentParent* aManager) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); +} + +template <> +void AssertCorrectThreadForManager(PBackgroundChild* aManager) +{ +#ifdef DEBUG + if (aManager) { + PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread(); + MOZ_ASSERT(backgroundChild); + MOZ_ASSERT(backgroundChild == aManager); + } +#endif +} + +template <> +void AssertCorrectThreadForManager( + PBackgroundParent* aManager) +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + AssertIsOnBackgroundThread(); +} + +bool +EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget) +{ + if (!aEventTarget) { + return NS_IsMainThread(); + } + + bool current; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEventTarget->IsOnCurrentThread(¤t))); + + return current; +} + +// Ensure that a nsCOMPtr/nsRefPtr is released on the target thread. template