From bc18942ac02542fe25ed1aaf7608fc83a1d3b4d6 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 26 Sep 2014 10:34:08 -0700 Subject: [PATCH 01/85] Bug 1067664, part 1 - Allow different leak thresholds for different types of processes. r=jmaher --- build/automationutils.py | 24 +++++++++++++++++++++--- layout/tools/reftest/runreftest.py | 14 ++++++++------ testing/mochitest/mochitest_options.py | 17 +++++++++++------ testing/mochitest/runtests.py | 2 +- testing/mochitest/runtestsb2g.py | 2 +- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index df8be4946ed..124046c8708 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -311,7 +311,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold): log.info("%s | leakcheck | %s %d bytes leaked (%s)" % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) -def processLeakLog(leakLogFile, leakThreshold = 0): +def processLeakLog(leakLogFile, leakThresholds): """Process the leak log, including separate leak logs created by child processes. @@ -326,14 +326,28 @@ def processLeakLog(leakLogFile, leakThreshold = 0): optional. All other file names are treated as being for default processes. + + leakThresholds should be a dict mapping process types to leak thresholds, + in bytes. If a process type is not present in the dict the threshold + will be 0. """ if not os.path.exists(leakLogFile): log.info("WARNING | leakcheck | refcount logging is off, so leaks can't be detected!") return - if leakThreshold != 0: - log.info("TEST-INFO | leakcheck | threshold set at %d bytes" % leakThreshold) + # This list is based on kGeckoProcessTypeString. ipdlunittest processes likely + # are not going to produce leak logs we will ever see. + knownProcessTypes = ["default", "plugin", "tab", "geckomediaplugin"] + + for processType in knownProcessTypes: + log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes" + % (processType, leakThresholds.get(processType, 0))) + + for processType in leakThresholds: + if not processType in knownProcessTypes: + log.info("TEST-UNEXPECTED-FAIL | leakcheck | Unknown process type %s in leakThresholds" + % processType) (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile) if leakFileBase[-4:] == ".log": @@ -350,6 +364,10 @@ def processLeakLog(leakLogFile, leakThreshold = 0): processType = m.group(1) else: processType = "default" + if not processType in knownProcessTypes: + log.info("TEST-UNEXPECTED-FAIL | leakcheck | Leak log with unknown process type %s" + % processType) + leakThreshold = leakThresholds.get(processType, 0) processSingleLeakFile(thisFile, processType, leakThreshold) def replaceBackSlashes(input): diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index 875348570fc..c36b218d5c8 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -344,7 +344,7 @@ class RefTest(object): # give the JS harness 30 seconds to deal # with its own timeouts timeout=options.timeout + 30.0) - processLeakLog(self.leakLogFile, options.leakThreshold) + processLeakLog(self.leakLogFile, options.leakThresholds) self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.") finally: self.cleanup(profileDir) @@ -394,12 +394,12 @@ class ReftestOptions(OptionParser): default = 5 * 60, # 5 minutes per bug 479518 help = "reftest will timeout in specified number of seconds. [default %default s].") self.add_option("--leak-threshold", - action = "store", type = "int", dest = "leakThreshold", + action = "store", type = "int", dest = "defaultLeakThreshold", default = 0, - help = "fail if the number of bytes leaked through " - "refcounted objects (or bytes in classes with " - "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater " - "than the given number") + help = "fail if the number of bytes leaked in default " + "processes through refcounted objects (or bytes " + "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " + "is greater than the given number") self.add_option("--utility-path", action = "store", type = "string", dest = "utilityPath", default = self.automation.DIST_BIN, @@ -511,6 +511,8 @@ class ReftestOptions(OptionParser): if options.debugger is not None: self.error("cannot specify a debugger with parallel tests") + options.leakThresholds = {"default": options.defaultLeakThreshold} + return options def main(): diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index 91d79d470d9..5a89dcff5e7 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -223,12 +223,12 @@ class MochitestOptions(optparse.OptionParser): [["--leak-threshold"], { "action": "store", "type": "int", - "dest": "leakThreshold", + "dest": "defaultLeakThreshold", "metavar": "THRESHOLD", - "help": "fail if the number of bytes leaked through " - "refcounted objects (or bytes in classes with " - "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater " - "than the given number", + "help": "fail if the number of bytes leaked in default " + "processes through refcounted objects (or bytes " + "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " + "is greater than the given number", "default": 0, }], [["--fatal-assertions"], @@ -608,6 +608,11 @@ class MochitestOptions(optparse.OptionParser): if not os.path.isfile(f): self.error('Missing binary %s required for --use-test-media-devices') + options.leakThresholds = { + "default": options.defaultLeakThreshold, + "tab": 10000, # See dependencies of bug 1051230. + } + return options @@ -765,7 +770,7 @@ class B2GOptions(MochitestOptions): defaults["testPath"] = "" defaults["extensionsToExclude"] = ["specialpowers"] # See dependencies of bug 1038943. - defaults["leakThreshold"] = 5180 + defaults["defaultLeakThreshold"] = 5180 self.set_defaults(**defaults) def verifyRemoteOptions(self, options): diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index b370bc8c33c..cf438b008d7 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1843,7 +1843,7 @@ class Mochitest(MochitestUtilsMixin): self.stopVMwareRecording(); self.stopServers() - processLeakLog(self.leak_report_file, options.leakThreshold) + processLeakLog(self.leak_report_file, options.leakThresholds) if self.nsprLogs: with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip: diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index b9010e6d7b1..5d32e09d7b6 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -202,7 +202,7 @@ class B2GMochitest(MochitestUtilsMixin): self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) - processLeakLog(local_leak_file.name, options.leakThreshold) + processLeakLog(local_leak_file.name, options.leakThresholds) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n"); status = -1 From 0f3591b1fe3514f5e9d61297f8807ffdad0344e5 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 26 Sep 2014 10:34:09 -0700 Subject: [PATCH 02/85] Bug 1067664, part 2 - Stop ignoring tab process leaks. r=jmaher --- build/automationutils.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index 124046c8708..eccb2d5e45f 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -286,14 +286,9 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold): # totalBytesLeaked was seen and is non-zero. if totalBytesLeaked > leakThreshold: - if processType == "tab": - # For now, ignore tab process leaks. See bug 1051230. - log.info("WARNING | leakcheck | ignoring leaks in tab process") - prefix = "WARNING" - else: - logAsWarning = True - # Fail the run if we're over the threshold (which defaults to 0) - prefix = "TEST-UNEXPECTED-FAIL" + logAsWarning = True + # Fail the run if we're over the threshold (which defaults to 0) + prefix = "TEST-UNEXPECTED-FAIL" else: prefix = "WARNING" # Create a comma delimited string of the first N leaked objects found, From 8b01964740ebcef1b43201802d1b78d1b7629017 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 26 Sep 2014 10:34:09 -0700 Subject: [PATCH 03/85] Bug 1068276, part 1 - Allow configuring which type of processes we complain about when there's no leak log. r=jmaher Then don't warn for missing logs from tab and geckomediaplugin processes, or default processes on B2G. --- build/automationutils.py | 12 +++++++++--- layout/tools/reftest/runreftest.py | 4 +++- testing/mochitest/mochitest_options.py | 10 ++++++++++ testing/mochitest/runtests.py | 2 +- testing/mochitest/runtestsb2g.py | 2 +- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index eccb2d5e45f..472f91865b1 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -196,7 +196,7 @@ def dumpLeakLog(leakLogFile, filter = False): # Simply copy the log. log.info(leakReport.rstrip("\n")) -def processSingleLeakFile(leakLogFileName, processType, leakThreshold): +def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMissingLeaks): """Process a single leak log. """ @@ -273,11 +273,16 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold): if crashedOnPurpose: log.info("TEST-INFO | leakcheck | %s deliberate crash and thus no leak log" % processString) + elif ignoreMissingLeaks: + log.info("TEST-INFO | leakcheck | %s ignoring missing output line for total leaks" + % processString) else: # TODO: This should be a TEST-UNEXPECTED-FAIL, but was changed to a warning # due to too many intermittent failures (see bug 831223). log.info("WARNING | leakcheck | %s missing output line for total leaks!" % processString) + log.info("TEST-INFO | leakcheck | missing output line from log file %s" + % leakLogFileName) return if totalBytesLeaked == 0: @@ -306,7 +311,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold): log.info("%s | leakcheck | %s %d bytes leaked (%s)" % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) -def processLeakLog(leakLogFile, leakThresholds): +def processLeakLog(leakLogFile, leakThresholds, ignoreMissingLeaks): """Process the leak log, including separate leak logs created by child processes. @@ -363,7 +368,8 @@ def processLeakLog(leakLogFile, leakThresholds): log.info("TEST-UNEXPECTED-FAIL | leakcheck | Leak log with unknown process type %s" % processType) leakThreshold = leakThresholds.get(processType, 0) - processSingleLeakFile(thisFile, processType, leakThreshold) + processSingleLeakFile(thisFile, processType, leakThreshold, + processType in ignoreMissingLeaks) def replaceBackSlashes(input): return input.replace('\\', '/') diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index c36b218d5c8..a9f22ffda41 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -344,7 +344,7 @@ class RefTest(object): # give the JS harness 30 seconds to deal # with its own timeouts timeout=options.timeout + 30.0) - processLeakLog(self.leakLogFile, options.leakThresholds) + processLeakLog(self.leakLogFile, options.leakThresholds, options.ignoreMissingLeaks) self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.") finally: self.cleanup(profileDir) @@ -513,6 +513,8 @@ class ReftestOptions(OptionParser): options.leakThresholds = {"default": options.defaultLeakThreshold} + options.ignoreMissingLeaks = [] + return options def main(): diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index 5a89dcff5e7..507c22b83ef 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -613,6 +613,10 @@ class MochitestOptions(optparse.OptionParser): "tab": 10000, # See dependencies of bug 1051230. } + # Bug 1051230 - Leak logging does not yet work for tab processes on desktop. + # Bug 1065098 - The geckomediaplugin process fails to produce a leak log for some reason. + options.ignoreMissingLeaks = ["tab", "geckomediaplugin"] + return options @@ -817,6 +821,12 @@ class B2GOptions(MochitestOptions): options.sslPort = tempSSL options.httpPort = tempPort + # Bug 1071866 - B2G Mochitests do not always produce a leak log. + options.ignoreMissingLeaks.append("default") + + # Bug 1070068 - Leak logging does not work for tab processes on B2G. + assert "tab" in options.ignoreMissingLeaks, "Ignore failures for tab processes on B2G" + return options def elf_arm(self, filename): diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index cf438b008d7..668af88fd19 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1843,7 +1843,7 @@ class Mochitest(MochitestUtilsMixin): self.stopVMwareRecording(); self.stopServers() - processLeakLog(self.leak_report_file, options.leakThresholds) + processLeakLog(self.leak_report_file, options.leakThresholds, options.ignoreMissingLeaks) if self.nsprLogs: with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip: diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index 5d32e09d7b6..ed0a2d99cb4 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -202,7 +202,7 @@ class B2GMochitest(MochitestUtilsMixin): self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) - processLeakLog(local_leak_file.name, options.leakThresholds) + processLeakLog(local_leak_file.name, options.leakThresholds, options.ignoreMissingLeaks) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n"); status = -1 From d50e32f0f9f27bfac3de9b32705757c190bb716b Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Fri, 26 Sep 2014 10:34:09 -0700 Subject: [PATCH 04/85] Bug 1068276, part 2 - Make unexpected failure to produce a leak log an error. r=jmaher --- build/automationutils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index 472f91865b1..46a909db5e4 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -277,9 +277,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis log.info("TEST-INFO | leakcheck | %s ignoring missing output line for total leaks" % processString) else: - # TODO: This should be a TEST-UNEXPECTED-FAIL, but was changed to a warning - # due to too many intermittent failures (see bug 831223). - log.info("WARNING | leakcheck | %s missing output line for total leaks!" + log.info("TEST-UNEXPECTED-FAIL | leakcheck | %s missing output line for total leaks!" % processString) log.info("TEST-INFO | leakcheck | missing output line from log file %s" % leakLogFileName) From 881e8c88d25e90ff619ea0890a94e914b0c4acdf Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 26 Sep 2014 19:47:59 +0200 Subject: [PATCH 05/85] Bug 1072911 - Scalar Replacement: Add missing MIRTypes. r=h4writer --- js/src/jit-test/tests/ion/bug1072911.js | 14 ++++++++++++++ js/src/jit/MIR.cpp | 1 + js/src/jit/MIR.h | 1 + 3 files changed, 16 insertions(+) create mode 100644 js/src/jit-test/tests/ion/bug1072911.js diff --git a/js/src/jit-test/tests/ion/bug1072911.js b/js/src/jit-test/tests/ion/bug1072911.js new file mode 100644 index 00000000000..44df7268f36 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1072911.js @@ -0,0 +1,14 @@ + +function X () {}; +function Y () {}; +function testCallProtoMethod() { + var a = [new X, new X, __proto__, new Y, new Y]; +} +testCallProtoMethod(); + +function testNot() { + var r; + for (var i = 0; i < 10; ++i) + r = []; +} +testNot(); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index d6e2e09f2f5..6c6ef3e03d2 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3178,6 +3178,7 @@ MCreateThisWithTemplate::canRecoverOnBailout() const MObjectState::MObjectState(MDefinition *obj) { // This instruction is only used as a summary for bailout paths. + setResultType(MIRType_Object); setRecoveredOnBailout(); JSObject *templateObject = nullptr; if (obj->isNewObject()) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index c814096a8bd..4c92d5dcf17 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2581,6 +2581,7 @@ class MArrayState : public MVariadicInstruction explicit MArrayState(MDefinition *arr) { // This instruction is only used as a summary for bailout paths. + setResultType(MIRType_Object); setRecoveredOnBailout(); numElements_ = arr->toNewArray()->count(); } From a4830da8c474278905d567561707655a2a4a1e62 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Fri, 26 Sep 2014 19:48:00 +0200 Subject: [PATCH 06/85] Bug 1007213 - Capture implicit dead branches caused by type barriers. r=sunfish --- js/src/jit-test/tests/ion/bug1007213.js | 21 +++++++++++++++++ js/src/jit/MIR.h | 23 ++++++++++--------- js/src/jit/MIRGraph.cpp | 30 +++++++++++++++++++++++++ js/src/jit/MIRGraph.h | 7 ++++++ js/src/jit/RangeAnalysis.cpp | 9 ++++---- js/src/jit/TypePolicy.cpp | 6 +++++ 6 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug1007213.js diff --git a/js/src/jit-test/tests/ion/bug1007213.js b/js/src/jit-test/tests/ion/bug1007213.js new file mode 100644 index 00000000000..6366e1e3f04 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1007213.js @@ -0,0 +1,21 @@ + +function getval(o) { + return obj.val +} +function f(x, o) { + var lhs = -(~x >>> 0) + var rhs = getval(o) + return (lhs - rhs >> 0) +} +function getObj(v) { + return { + val: v + } +} + +var obj = getObj(1) +assertEq(f(0, obj), 0) +assertEq(f(0, obj), 0) +obj = getObj('can has bug?') +obj = getObj(.5) +assertEq(f(0, obj), 1) diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4c92d5dcf17..1db38d1a941 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -76,16 +76,19 @@ MIRType MIRTypeFromValue(const js::Value &vp) * points. */ \ _(Unused) \ - /* Marks if an instruction has fewer uses than the original code. - * E.g. UCE can remove code. - * Every instruction where an use is/was removed from an instruction and - * as a result the number of operands doesn't equal the original code - * need to get marked as UseRemoved. This is important for truncation - * analysis to know, since if all original uses are still present, - * it can ignore resumepoints. - * Currently this is done for every pass after IonBuilder and before - * Truncate Doubles. So every time removeUse is called, UseRemoved needs - * to get set. + \ + /* When a branch is removed, the uses of multiple instructions are removed. + * The removal of branches is based on hypotheses. These hypotheses might + * fail, in which case we need to bailout from the current code. + * + * When we implement a destructive optimization, we need to consider the + * failing cases, and consider the fact that we might resume the execution + * into a branch which was removed from the compiler. As such, a + * destructive optimization need to take into acount removed branches. + * + * In order to let destructive optimizations know about removed branches, we + * have to annotate instructions with the UseRemoved flag. This flag + * annotates instruction which were used in removed branches. */ \ _(UseRemoved) \ \ diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 7b1a2cb21e3..6cd4ad8a614 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -997,6 +997,36 @@ MBasicBlock::discardPhiAt(MPhiIterator &at) return result; } +void +MBasicBlock::flagOperandsOfPrunedBranches(MInstruction *ins) +{ + // Find the previous resume point which would be used for bailing out. + MResumePoint *rp = nullptr; + for (MInstructionReverseIterator iter = rbegin(ins); iter != rend(); iter++) { + rp = iter->resumePoint(); + if (rp) + break; + } + + // If none, take the entry resume point. + if (!rp) + rp = entryResumePoint(); + + // The only blocks which do not have any entryResumePoint in Ion, are the + // SplitEdge blocks. SplitEdge blocks only have a Goto instruction before + // Range Analysis phase. In adjustInputs, we are manipulating instructions + // which have a TypePolicy. So, as a Goto has no operand and no type + // policy, the entry resume point should exists. + MOZ_ASSERT(rp); + + // Flag all operand as being potentially used. + while (rp) { + for (size_t i = 0, end = rp->numOperands(); i < end; i++) + rp->getOperand(i)->setUseRemovedUnchecked(); + rp = rp->caller(); + } +} + bool MBasicBlock::addPredecessor(TempAllocator &alloc, MBasicBlock *pred) { diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h index a911ffd8628..a57b32c2411 100644 --- a/js/src/jit/MIRGraph.h +++ b/js/src/jit/MIRGraph.h @@ -301,6 +301,13 @@ class MBasicBlock : public TempObject, public InlineListNode // Discards a phi instruction and updates predecessor successorWithPhis. MPhiIterator discardPhiAt(MPhiIterator &at); + // Some instruction which are guarding against some MIRType value, or + // against a type expectation should be considered as removing a potenatial + // branch where the guard does not hold. We need to register such + // instructions in order to do destructive optimizations correctly, such as + // Range Analysis. + void flagOperandsOfPrunedBranches(MInstruction *ins); + // Mark this block as having been removed from the graph. void markAsDead() { MOZ_ASSERT(kind_ != DEAD); diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index c0d3e4a5c89..12024d358df 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2499,11 +2499,10 @@ ComputeRequestedTruncateKind(MDefinition *candidate) MDefinition::TruncateKind kind = MDefinition::Truncate; for (MUseIterator use(candidate->usesBegin()); use != candidate->usesEnd(); use++) { if (!use->consumer()->isDefinition()) { - // We can only skip testing resume points, if all original uses are - // still present, or if the value does not need conversion. - // Otherwise a branch removed by UCE might rely on the non-truncated - // value, and any bailout with a truncated value might lead an - // incorrect value. + // Truncation is a destructive optimization, as such, we need to pay + // attention to removed branches and prevent optimization + // destructive optimizations if we have no alternative. (see + // UseRemoved flag) if (candidate->isUseRemoved() && needsConversion) kind = Min(kind, MDefinition::TruncateAfterBailouts); continue; diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 3ae62e60035..dc20958e503 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -273,6 +273,12 @@ TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) MUnbox *unbox = MUnbox::New(alloc, ins->getOperand(0), outputType, MUnbox::TypeBarrier); ins->block()->insertBefore(ins, unbox); + + // The TypeBarrier is equivalent to removing branches with unexpected + // types. The unexpected types would have changed Range Analysis + // predictions. As such, we need to prevent destructive optimizations. + ins->block()->flagOperandsOfPrunedBranches(unbox); + ins->replaceOperand(0, unbox); return true; } From 177cdd3fc808dcd75885726e9d7b5890350dd9ee Mon Sep 17 00:00:00 2001 From: Anuj Agarwal Date: Fri, 26 Sep 2014 10:53:47 -0700 Subject: [PATCH 07/85] Bug 1071100 - Moved nsRefPtr from nsAutoPtr.h to a new nsRefPtr.h r=froydnj --- xpcom/base/moz.build | 1 + xpcom/base/nsAutoPtr.h | 527 +--------------------------------------- xpcom/base/nsRefPtr.h | 533 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 538 insertions(+), 523 deletions(-) create mode 100644 xpcom/base/nsRefPtr.h diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index 5b6ce1634e6..8c49542465d 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -56,6 +56,7 @@ EXPORTS += [ 'nsISupportsBase.h', 'nsISupportsObsolete.h', 'nsObjCExceptions.h', + 'nsRefPtr.h', 'nsStackWalk.h', 'nsTraceRefcnt.h', 'nsWeakPtr.h', diff --git a/xpcom/base/nsAutoPtr.h b/xpcom/base/nsAutoPtr.h index 95114dc3fd5..a7d86b96d0d 100644 --- a/xpcom/base/nsAutoPtr.h +++ b/xpcom/base/nsAutoPtr.h @@ -4,10 +4,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsAutoPtr_h___ -#define nsAutoPtr_h___ +#ifndef nsAutoPtr_h +#define nsAutoPtr_h #include "nsCOMPtr.h" +#include "nsRefPtr.h" #include "nsCycleCollectionNoteChild.h" #include "mozilla/MemoryReporting.h" @@ -804,526 +805,6 @@ operator==(int aLhs, const nsAutoArrayPtr& aRhs) #endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO) - -/*****************************************************************************/ - -// template class nsRefPtrGetterAddRefs; - -template -class nsRefPtr -{ -private: - - void - assign_with_AddRef(T* aRawPtr) - { - if (aRawPtr) { - aRawPtr->AddRef(); - } - assign_assuming_AddRef(aRawPtr); - } - - void** - begin_assignment() - { - assign_assuming_AddRef(0); - return reinterpret_cast(&mRawPtr); - } - - void - assign_assuming_AddRef(T* aNewPtr) - { - T* oldPtr = mRawPtr; - mRawPtr = aNewPtr; - if (oldPtr) { - oldPtr->Release(); - } - } - -private: - T* mRawPtr; - -public: - typedef T element_type; - - ~nsRefPtr() - { - if (mRawPtr) { - mRawPtr->Release(); - } - } - - // Constructors - - nsRefPtr() - : mRawPtr(0) - // default constructor - { - } - - nsRefPtr(const nsRefPtr& aSmartPtr) - : mRawPtr(aSmartPtr.mRawPtr) - // copy-constructor - { - if (mRawPtr) { - mRawPtr->AddRef(); - } - } - - nsRefPtr(nsRefPtr&& aRefPtr) - : mRawPtr(aRefPtr.mRawPtr) - { - aRefPtr.mRawPtr = nullptr; - } - - // construct from a raw pointer (of the right type) - - MOZ_IMPLICIT nsRefPtr(T* aRawPtr) - : mRawPtr(aRawPtr) - { - if (mRawPtr) { - mRawPtr->AddRef(); - } - } - - template - nsRefPtr(already_AddRefed& aSmartPtr) - : mRawPtr(aSmartPtr.take()) - // construct from |already_AddRefed| - { - } - - template - nsRefPtr(already_AddRefed&& aSmartPtr) - : mRawPtr(aSmartPtr.take()) - // construct from |otherRefPtr.forget()| - { - } - - MOZ_IMPLICIT nsRefPtr(const nsCOMPtr_helper& aHelper) - { - void* newRawPtr; - if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { - newRawPtr = 0; - } - mRawPtr = static_cast(newRawPtr); - } - - // Assignment operators - - nsRefPtr& - operator=(const nsRefPtr& aRhs) - // copy assignment operator - { - assign_with_AddRef(aRhs.mRawPtr); - return *this; - } - - nsRefPtr& - operator=(T* aRhs) - // assign from a raw pointer (of the right type) - { - assign_with_AddRef(aRhs); - return *this; - } - - template - nsRefPtr& - operator=(already_AddRefed& aRhs) - // assign from |already_AddRefed| - { - assign_assuming_AddRef(aRhs.take()); - return *this; - } - - template - nsRefPtr& - operator=(already_AddRefed && aRhs) - // assign from |otherRefPtr.forget()| - { - assign_assuming_AddRef(aRhs.take()); - return *this; - } - - nsRefPtr& - operator=(const nsCOMPtr_helper& aHelper) - { - void* newRawPtr; - if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { - newRawPtr = 0; - } - assign_assuming_AddRef(static_cast(newRawPtr)); - return *this; - } - - nsRefPtr& - operator=(nsRefPtr && aRefPtr) - { - assign_assuming_AddRef(aRefPtr.mRawPtr); - aRefPtr.mRawPtr = nullptr; - return *this; - } - - // Other pointer operators - - void - swap(nsRefPtr& aRhs) - // ...exchange ownership with |aRhs|; can save a pair of refcount operations - { - T* temp = aRhs.mRawPtr; - aRhs.mRawPtr = mRawPtr; - mRawPtr = temp; - } - - void - swap(T*& aRhs) - // ...exchange ownership with |aRhs|; can save a pair of refcount operations - { - T* temp = aRhs; - aRhs = mRawPtr; - mRawPtr = temp; - } - - already_AddRefed - forget() - // return the value of mRawPtr and null out mRawPtr. Useful for - // already_AddRefed return values. - { - T* temp = 0; - swap(temp); - return already_AddRefed(temp); - } - - template - void - forget(I** aRhs) - // Set the target of aRhs to the value of mRawPtr and null out mRawPtr. - // Useful to avoid unnecessary AddRef/Release pairs with "out" - // parameters where aRhs bay be a T** or an I** where I is a base class - // of T. - { - NS_ASSERTION(aRhs, "Null pointer passed to forget!"); - *aRhs = mRawPtr; - mRawPtr = 0; - } - - T* - get() const - /* - Prefer the implicit conversion provided automatically by |operator T*() const|. - Use |get()| to resolve ambiguity or to get a castable pointer. - */ - { - return const_cast(mRawPtr); - } - - operator T*() const - /* - ...makes an |nsRefPtr| act like its underlying raw pointer type whenever it - is used in a context where a raw pointer is expected. It is this operator - that makes an |nsRefPtr| substitutable for a raw pointer. - - Prefer the implicit use of this operator to calling |get()|, except where - necessary to resolve ambiguity. - */ - { - return get(); - } - - T* - operator->() const - { - NS_PRECONDITION(mRawPtr != 0, - "You can't dereference a NULL nsRefPtr with operator->()."); - return get(); - } - - // This operator is needed for gcc <= 4.0.* and for Sun Studio; it - // causes internal compiler errors for some MSVC versions. (It's not - // clear to me whether it should be needed.) -#ifndef _MSC_VER - template - U& - operator->*(U V::* aMember) - { - NS_PRECONDITION(mRawPtr != 0, - "You can't dereference a NULL nsRefPtr with operator->*()."); - return get()->*aMember; - } -#endif - - nsRefPtr* - get_address() - // This is not intended to be used by clients. See |address_of| - // below. - { - return this; - } - - const nsRefPtr* - get_address() const - // This is not intended to be used by clients. See |address_of| - // below. - { - return this; - } - -public: - T& - operator*() const - { - NS_PRECONDITION(mRawPtr != 0, - "You can't dereference a NULL nsRefPtr with operator*()."); - return *get(); - } - - T** - StartAssignment() - { -#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT - return reinterpret_cast(begin_assignment()); -#else - assign_assuming_AddRef(0); - return reinterpret_cast(&mRawPtr); -#endif - } -}; - -template -inline void -ImplCycleCollectionUnlink(nsRefPtr& aField) -{ - aField = nullptr; -} - -template -inline void -ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - nsRefPtr& aField, - const char* aName, - uint32_t aFlags = 0) -{ - CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags); -} - -template -inline nsRefPtr* -address_of(nsRefPtr& aPtr) -{ - return aPtr.get_address(); -} - -template -inline const nsRefPtr* -address_of(const nsRefPtr& aPtr) -{ - return aPtr.get_address(); -} - -template -class nsRefPtrGetterAddRefs -/* - ... - - This class is designed to be used for anonymous temporary objects in the - argument list of calls that return COM interface pointers, e.g., - - nsRefPtr fooP; - ...->GetAddRefedPointer(getter_AddRefs(fooP)) - - DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead. - - When initialized with a |nsRefPtr|, as in the example above, it returns - a |void**|, a |T**|, or an |nsISupports**| as needed, that the - outer call (|GetAddRefedPointer| in this case) can fill in. - - This type should be a nested class inside |nsRefPtr|. -*/ -{ -public: - explicit - nsRefPtrGetterAddRefs(nsRefPtr& aSmartPtr) - : mTargetSmartPtr(aSmartPtr) - { - // nothing else to do - } - - operator void**() - { - return reinterpret_cast(mTargetSmartPtr.StartAssignment()); - } - - operator T**() - { - return mTargetSmartPtr.StartAssignment(); - } - - T*& - operator*() - { - return *(mTargetSmartPtr.StartAssignment()); - } - -private: - nsRefPtr& mTargetSmartPtr; -}; - -template -inline nsRefPtrGetterAddRefs -getter_AddRefs(nsRefPtr& aSmartPtr) -/* - Used around a |nsRefPtr| when - ...makes the class |nsRefPtrGetterAddRefs| invisible. -*/ -{ - return nsRefPtrGetterAddRefs(aSmartPtr); -} - - - -// Comparing two |nsRefPtr|s - -template -inline bool -operator==(const nsRefPtr& aLhs, const nsRefPtr& aRhs) -{ - return static_cast(aLhs.get()) == static_cast(aRhs.get()); -} - - -template -inline bool -operator!=(const nsRefPtr& aLhs, const nsRefPtr& aRhs) -{ - return static_cast(aLhs.get()) != static_cast(aRhs.get()); -} - - -// Comparing an |nsRefPtr| to a raw pointer - -template -inline bool -operator==(const nsRefPtr& aLhs, const U* aRhs) -{ - return static_cast(aLhs.get()) == static_cast(aRhs); -} - -template -inline bool -operator==(const U* aLhs, const nsRefPtr& aRhs) -{ - return static_cast(aLhs) == static_cast(aRhs.get()); -} - -template -inline bool -operator!=(const nsRefPtr& aLhs, const U* aRhs) -{ - return static_cast(aLhs.get()) != static_cast(aRhs); -} - -template -inline bool -operator!=(const U* aLhs, const nsRefPtr& aRhs) -{ - return static_cast(aLhs) != static_cast(aRhs.get()); -} - -template -inline bool -operator==(const nsRefPtr& aLhs, U* aRhs) -{ - return static_cast(aLhs.get()) == const_cast(aRhs); -} - -template -inline bool -operator==(U* aLhs, const nsRefPtr& aRhs) -{ - return const_cast(aLhs) == static_cast(aRhs.get()); -} - -template -inline bool -operator!=(const nsRefPtr& aLhs, U* aRhs) -{ - return static_cast(aLhs.get()) != const_cast(aRhs); -} - -template -inline bool -operator!=(U* aLhs, const nsRefPtr& aRhs) -{ - return const_cast(aLhs) != static_cast(aRhs.get()); -} - - - -// Comparing an |nsRefPtr| to |0| - -template -inline bool -operator==(const nsRefPtr& aLhs, NSCAP_Zero* aRhs) -// specifically to allow |smartPtr == 0| -{ - return static_cast(aLhs.get()) == reinterpret_cast(aRhs); -} - -template -inline bool -operator==(NSCAP_Zero* aLhs, const nsRefPtr& aRhs) -// specifically to allow |0 == smartPtr| -{ - return reinterpret_cast(aLhs) == static_cast(aRhs.get()); -} - -template -inline bool -operator!=(const nsRefPtr& aLhs, NSCAP_Zero* aRhs) -// specifically to allow |smartPtr != 0| -{ - return static_cast(aLhs.get()) != reinterpret_cast(aRhs); -} - -template -inline bool -operator!=(NSCAP_Zero* aLhs, const nsRefPtr& aRhs) -// specifically to allow |0 != smartPtr| -{ - return reinterpret_cast(aLhs) != static_cast(aRhs.get()); -} - - -#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO - -// We need to explicitly define comparison operators for `int' -// because the compiler is lame. - -template -inline bool -operator==(const nsRefPtr& aLhs, int aRhs) -// specifically to allow |smartPtr == 0| -{ - return static_cast(aLhs.get()) == reinterpret_cast(aRhs); -} - -template -inline bool -operator==(int aLhs, const nsRefPtr& aRhs) -// specifically to allow |0 == smartPtr| -{ - return reinterpret_cast(aLhs) == static_cast(aRhs.get()); -} - -#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO) - -template -inline nsresult -CallQueryInterface(nsRefPtr& aSourcePtr, DestinationType** aDestPtr) -{ - return CallQueryInterface(aSourcePtr.get(), aDestPtr); -} - /*****************************************************************************/ template @@ -1414,4 +895,4 @@ do_QueryObject(nsRefPtr& aRawPtr, nsresult* aErrorPtr) /*****************************************************************************/ -#endif // !defined(nsAutoPtr_h___) +#endif // !defined(nsAutoPtr_h) diff --git a/xpcom/base/nsRefPtr.h b/xpcom/base/nsRefPtr.h new file mode 100644 index 00000000000..379203659d2 --- /dev/null +++ b/xpcom/base/nsRefPtr.h @@ -0,0 +1,533 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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 nsRefPtr_h +#define nsRefPtr_h + +#include "nsCOMPtr.h" + +#include "nsCycleCollectionNoteChild.h" + +/*****************************************************************************/ + +// template class nsRefPtrGetterAddRefs; + +template +class nsRefPtr +{ +private: + void + assign_with_AddRef(T* aRawPtr) + { + if (aRawPtr) { + aRawPtr->AddRef(); + } + assign_assuming_AddRef(aRawPtr); + } + + void** + begin_assignment() + { + assign_assuming_AddRef(0); + return reinterpret_cast(&mRawPtr); + } + + void + assign_assuming_AddRef(T* aNewPtr) + { + T* oldPtr = mRawPtr; + mRawPtr = aNewPtr; + if (oldPtr) { + oldPtr->Release(); + } + } + +private: + T* mRawPtr; + +public: + typedef T element_type; + + ~nsRefPtr() + { + if (mRawPtr) { + mRawPtr->Release(); + } + } + + // Constructors + + nsRefPtr() + : mRawPtr(0) + // default constructor + { + } + + nsRefPtr(const nsRefPtr& aSmartPtr) + : mRawPtr(aSmartPtr.mRawPtr) + // copy-constructor + { + if (mRawPtr) { + mRawPtr->AddRef(); + } + } + + nsRefPtr(nsRefPtr&& aRefPtr) + : mRawPtr(aRefPtr.mRawPtr) + { + aRefPtr.mRawPtr = nullptr; + } + + // construct from a raw pointer (of the right type) + + MOZ_IMPLICIT nsRefPtr(T* aRawPtr) + : mRawPtr(aRawPtr) + { + if (mRawPtr) { + mRawPtr->AddRef(); + } + } + + template + nsRefPtr(already_AddRefed& aSmartPtr) + : mRawPtr(aSmartPtr.take()) + // construct from |already_AddRefed| + { + } + + template + nsRefPtr(already_AddRefed&& aSmartPtr) + : mRawPtr(aSmartPtr.take()) + // construct from |otherRefPtr.forget()| + { + } + + MOZ_IMPLICIT nsRefPtr(const nsCOMPtr_helper& aHelper) + { + void* newRawPtr; + if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { + newRawPtr = 0; + } + mRawPtr = static_cast(newRawPtr); + } + + // Assignment operators + + nsRefPtr& + operator=(const nsRefPtr& aRhs) + // copy assignment operator + { + assign_with_AddRef(aRhs.mRawPtr); + return *this; + } + + nsRefPtr& + operator=(T* aRhs) + // assign from a raw pointer (of the right type) + { + assign_with_AddRef(aRhs); + return *this; + } + + template + nsRefPtr& + operator=(already_AddRefed& aRhs) + // assign from |already_AddRefed| + { + assign_assuming_AddRef(aRhs.take()); + return *this; + } + + template + nsRefPtr& + operator=(already_AddRefed && aRhs) + // assign from |otherRefPtr.forget()| + { + assign_assuming_AddRef(aRhs.take()); + return *this; + } + + nsRefPtr& + operator=(const nsCOMPtr_helper& aHelper) + { + void* newRawPtr; + if (NS_FAILED(aHelper(NS_GET_TEMPLATE_IID(T), &newRawPtr))) { + newRawPtr = 0; + } + assign_assuming_AddRef(static_cast(newRawPtr)); + return *this; + } + + nsRefPtr& + operator=(nsRefPtr && aRefPtr) + { + assign_assuming_AddRef(aRefPtr.mRawPtr); + aRefPtr.mRawPtr = nullptr; + return *this; + } + + // Other pointer operators + + void + swap(nsRefPtr& aRhs) + // ...exchange ownership with |aRhs|; can save a pair of refcount operations + { + T* temp = aRhs.mRawPtr; + aRhs.mRawPtr = mRawPtr; + mRawPtr = temp; + } + + void + swap(T*& aRhs) + // ...exchange ownership with |aRhs|; can save a pair of refcount operations + { + T* temp = aRhs; + aRhs = mRawPtr; + mRawPtr = temp; + } + + already_AddRefed + forget() + // return the value of mRawPtr and null out mRawPtr. Useful for + // already_AddRefed return values. + { + T* temp = 0; + swap(temp); + return already_AddRefed(temp); + } + + template + void + forget(I** aRhs) + // Set the target of aRhs to the value of mRawPtr and null out mRawPtr. + // Useful to avoid unnecessary AddRef/Release pairs with "out" + // parameters where aRhs bay be a T** or an I** where I is a base class + // of T. + { + NS_ASSERTION(aRhs, "Null pointer passed to forget!"); + *aRhs = mRawPtr; + mRawPtr = 0; + } + + T* + get() const + /* + Prefer the implicit conversion provided automatically by |operator T*() const|. + Use |get()| to resolve ambiguity or to get a castable pointer. + */ + { + return const_cast(mRawPtr); + } + + operator T*() const + /* + ...makes an |nsRefPtr| act like its underlying raw pointer type whenever it + is used in a context where a raw pointer is expected. It is this operator + that makes an |nsRefPtr| substitutable for a raw pointer. + + Prefer the implicit use of this operator to calling |get()|, except where + necessary to resolve ambiguity. + */ + { + return get(); + } + + T* + operator->() const + { + NS_PRECONDITION(mRawPtr != 0, + "You can't dereference a NULL nsRefPtr with operator->()."); + return get(); + } + + // This operator is needed for gcc <= 4.0.* and for Sun Studio; it + // causes internal compiler errors for some MSVC versions. (It's not + // clear to me whether it should be needed.) +#ifndef _MSC_VER + template + U& + operator->*(U V::* aMember) + { + NS_PRECONDITION(mRawPtr != 0, + "You can't dereference a NULL nsRefPtr with operator->*()."); + return get()->*aMember; + } +#endif + + nsRefPtr* + get_address() + // This is not intended to be used by clients. See |address_of| + // below. + { + return this; + } + + const nsRefPtr* + get_address() const + // This is not intended to be used by clients. See |address_of| + // below. + { + return this; + } + +public: + T& + operator*() const + { + NS_PRECONDITION(mRawPtr != 0, + "You can't dereference a NULL nsRefPtr with operator*()."); + return *get(); + } + + T** + StartAssignment() + { +#ifndef NSCAP_FEATURE_INLINE_STARTASSIGNMENT + return reinterpret_cast(begin_assignment()); +#else + assign_assuming_AddRef(0); + return reinterpret_cast(&mRawPtr); +#endif + } +}; + +template +inline void +ImplCycleCollectionUnlink(nsRefPtr& aField) +{ + aField = nullptr; +} + +template +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + nsRefPtr& aField, + const char* aName, + uint32_t aFlags = 0) +{ + CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags); +} + +template +inline nsRefPtr* +address_of(nsRefPtr& aPtr) +{ + return aPtr.get_address(); +} + +template +inline const nsRefPtr* +address_of(const nsRefPtr& aPtr) +{ + return aPtr.get_address(); +} + +template +class nsRefPtrGetterAddRefs +/* + ... + + This class is designed to be used for anonymous temporary objects in the + argument list of calls that return COM interface pointers, e.g., + + nsRefPtr fooP; + ...->GetAddRefedPointer(getter_AddRefs(fooP)) + + DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead. + + When initialized with a |nsRefPtr|, as in the example above, it returns + a |void**|, a |T**|, or an |nsISupports**| as needed, that the + outer call (|GetAddRefedPointer| in this case) can fill in. + + This type should be a nested class inside |nsRefPtr|. +*/ +{ +public: + explicit + nsRefPtrGetterAddRefs(nsRefPtr& aSmartPtr) + : mTargetSmartPtr(aSmartPtr) + { + // nothing else to do + } + + operator void**() + { + return reinterpret_cast(mTargetSmartPtr.StartAssignment()); + } + + operator T**() + { + return mTargetSmartPtr.StartAssignment(); + } + + T*& + operator*() + { + return *(mTargetSmartPtr.StartAssignment()); + } + +private: + nsRefPtr& mTargetSmartPtr; +}; + +template +inline nsRefPtrGetterAddRefs +getter_AddRefs(nsRefPtr& aSmartPtr) +/* + Used around a |nsRefPtr| when + ...makes the class |nsRefPtrGetterAddRefs| invisible. +*/ +{ + return nsRefPtrGetterAddRefs(aSmartPtr); +} + + +// Comparing two |nsRefPtr|s + +template +inline bool +operator==(const nsRefPtr& aLhs, const nsRefPtr& aRhs) +{ + return static_cast(aLhs.get()) == static_cast(aRhs.get()); +} + + +template +inline bool +operator!=(const nsRefPtr& aLhs, const nsRefPtr& aRhs) +{ + return static_cast(aLhs.get()) != static_cast(aRhs.get()); +} + + +// Comparing an |nsRefPtr| to a raw pointer + +template +inline bool +operator==(const nsRefPtr& aLhs, const U* aRhs) +{ + return static_cast(aLhs.get()) == static_cast(aRhs); +} + +template +inline bool +operator==(const U* aLhs, const nsRefPtr& aRhs) +{ + return static_cast(aLhs) == static_cast(aRhs.get()); +} + +template +inline bool +operator!=(const nsRefPtr& aLhs, const U* aRhs) +{ + return static_cast(aLhs.get()) != static_cast(aRhs); +} + +template +inline bool +operator!=(const U* aLhs, const nsRefPtr& aRhs) +{ + return static_cast(aLhs) != static_cast(aRhs.get()); +} + +template +inline bool +operator==(const nsRefPtr& aLhs, U* aRhs) +{ + return static_cast(aLhs.get()) == const_cast(aRhs); +} + +template +inline bool +operator==(U* aLhs, const nsRefPtr& aRhs) +{ + return const_cast(aLhs) == static_cast(aRhs.get()); +} + +template +inline bool +operator!=(const nsRefPtr& aLhs, U* aRhs) +{ + return static_cast(aLhs.get()) != const_cast(aRhs); +} + +template +inline bool +operator!=(U* aLhs, const nsRefPtr& aRhs) +{ + return const_cast(aLhs) != static_cast(aRhs.get()); +} + + + +// Comparing an |nsRefPtr| to |0| + +template +inline bool +operator==(const nsRefPtr& aLhs, NSCAP_Zero* aRhs) +// specifically to allow |smartPtr == 0| +{ + return static_cast(aLhs.get()) == reinterpret_cast(aRhs); +} + +template +inline bool +operator==(NSCAP_Zero* aLhs, const nsRefPtr& aRhs) +// specifically to allow |0 == smartPtr| +{ + return reinterpret_cast(aLhs) == static_cast(aRhs.get()); +} + +template +inline bool +operator!=(const nsRefPtr& aLhs, NSCAP_Zero* aRhs) +// specifically to allow |smartPtr != 0| +{ + return static_cast(aLhs.get()) != reinterpret_cast(aRhs); +} + +template +inline bool +operator!=(NSCAP_Zero* aLhs, const nsRefPtr& aRhs) +// specifically to allow |0 != smartPtr| +{ + return reinterpret_cast(aLhs) != static_cast(aRhs.get()); +} + + +#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO + +// We need to explicitly define comparison operators for `int' +// because the compiler is lame. + +template +inline bool +operator==(const nsRefPtr& aLhs, int aRhs) +// specifically to allow |smartPtr == 0| +{ + return static_cast(aLhs.get()) == reinterpret_cast(aRhs); +} + +template +inline bool +operator==(int aLhs, const nsRefPtr& aRhs) +// specifically to allow |0 == smartPtr| +{ + return reinterpret_cast(aLhs) == static_cast(aRhs.get()); +} + +#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO) + +template +inline nsresult +CallQueryInterface(nsRefPtr& aSourcePtr, DestinationType** aDestPtr) +{ + return CallQueryInterface(aSourcePtr.get(), aDestPtr); +} + +/*****************************************************************************/ + +#endif // !defined(nsRefPtr_h) From 11cb81a983d3efb41bf5fc92e66ef568ec7cdabf Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Fri, 26 Sep 2014 14:11:17 -0400 Subject: [PATCH 08/85] Bug 1068961 - Reset clip rect for color layers. r=roc --- layout/base/FrameLayerBuilder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index daebe822b80..f67bb9d0ca2 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -2130,6 +2130,7 @@ ContainerState::PopPaintedLayerData() nsIntRect visibleRect = data->mVisibleRegion.GetBounds(); visibleRect.MoveBy(-GetTranslationForPaintedLayer(data->mLayer)); colorLayer->SetBounds(visibleRect); + colorLayer->SetClipRect(nullptr); layer = colorLayer; FLB_LOG_PAINTED_LAYER_DECISION(data, " Selected color layer=%p\n", layer.get()); From 032f97d837c35638c707f1c8cdadcd712a5eff14 Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Fri, 26 Sep 2014 14:13:17 -0400 Subject: [PATCH 09/85] Bug 1033066: Never let AudioSegments underflow mDuration and cause OOM allocation r=karlt --- content/media/AudioSegment.cpp | 3 ++- content/media/AudioSegment.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/content/media/AudioSegment.cpp b/content/media/AudioSegment.cpp index 642881d3a12..9ba1ae326c0 100644 --- a/content/media/AudioSegment.cpp +++ b/content/media/AudioSegment.cpp @@ -154,7 +154,8 @@ AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels // Offset in the buffer that will end up sent to the AudioStream, in samples. uint32_t offset = 0; - if (!GetDuration()) { + if (GetDuration() <= 0) { + MOZ_ASSERT(GetDuration() == 0); return; } diff --git a/content/media/AudioSegment.h b/content/media/AudioSegment.h index 2c40b908205..48dcf9101dd 100644 --- a/content/media/AudioSegment.h +++ b/content/media/AudioSegment.h @@ -86,8 +86,8 @@ struct AudioChunk { // Generic methods void SliceTo(TrackTicks aStart, TrackTicks aEnd) { - NS_ASSERTION(aStart >= 0 && aStart < aEnd && aEnd <= mDuration, - "Slice out of bounds"); + MOZ_ASSERT(aStart >= 0 && aStart < aEnd && aEnd <= mDuration, + "Slice out of bounds"); if (mBuffer) { MOZ_ASSERT(aStart < INT32_MAX, "Can't slice beyond 32-bit sample lengths"); for (uint32_t channel = 0; channel < mChannelData.Length(); ++channel) { From 8210496062d74a79c3ad29a5d32be743722a279c Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Fri, 26 Sep 2014 11:21:32 -0700 Subject: [PATCH 10/85] Bug 1067628 - Add mozconfig for win32 Mulet, r=mshal, DONTBUILD because NPOTB --- b2g/dev/config/mozconfigs/win32/mulet | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 b2g/dev/config/mozconfigs/win32/mulet diff --git a/b2g/dev/config/mozconfigs/win32/mulet b/b2g/dev/config/mozconfigs/win32/mulet new file mode 100644 index 00000000000..a5d1c79146c --- /dev/null +++ b/b2g/dev/config/mozconfigs/win32/mulet @@ -0,0 +1,3 @@ +. "$topsrcdir/browser/config/mozconfigs/win32/nightly" + +ac_add_options --enable-application=b2g/dev From 3e19f27830a374e5fe55e1216ff34f7c42ad1f9c Mon Sep 17 00:00:00 2001 From: James Willcox Date: Fri, 26 Sep 2014 13:31:22 -0500 Subject: [PATCH 11/85] Bug 1024614 - Guard against null resolver when flushing DNS cache r=sworkman --HG-- extra : histedit_source : 028f9b336a9da0ede7fec22afc4cabd00b86222f --- netwerk/dns/nsDNSService2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index bc375fb2a06..c3af43d957c 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -883,7 +883,7 @@ nsDNSService::Observe(nsISupports *subject, const char *topic, const char16_t *d if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) { nsAutoCString converted = NS_ConvertUTF16toUTF8(data); - if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) { + if (mResolver && !strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) { mResolver->FlushCache(); } return NS_OK; From 17a864b3d588594b7ba1909501454e6a5c486d2e Mon Sep 17 00:00:00 2001 From: James Willcox Date: Fri, 26 Sep 2014 13:31:38 -0500 Subject: [PATCH 12/85] Bug 1024614 - Send network link change events on Android r=blassey,mcmanus --HG-- extra : histedit_source : eb8916c6322cf8f858a63a9df252ffae041b275a --- mobile/android/base/GeckoConnectivityReceiver.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/android/base/GeckoConnectivityReceiver.java b/mobile/android/base/GeckoConnectivityReceiver.java index c2094bb6c4f..32a94a67bbb 100644 --- a/mobile/android/base/GeckoConnectivityReceiver.java +++ b/mobile/android/base/GeckoConnectivityReceiver.java @@ -20,6 +20,7 @@ public class GeckoConnectivityReceiver extends BroadcastReceiver { */ private static final String LINK_DATA_UP = "up"; private static final String LINK_DATA_DOWN = "down"; + private static final String LINK_DATA_CHANGED = "changed"; private static final String LINK_DATA_UNKNOWN = "unknown"; private static final String LOGTAG = "GeckoConnectivityReceiver"; @@ -82,6 +83,7 @@ public class GeckoConnectivityReceiver extends BroadcastReceiver { if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkLinkChangeEvent(status)); + GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkLinkChangeEvent(LINK_DATA_CHANGED)); } } } From e207fcc8fd40d72b1f52c09d08cd99c1e808486c Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Fri, 26 Sep 2014 15:02:35 -0400 Subject: [PATCH 13/85] Bug 1054581 - Have all compositor backends behave consistently when creating a zero-sized intermediate render target. r=jrmuizel --- gfx/layers/basic/BasicCompositor.cpp | 6 ++++++ gfx/layers/d3d11/CompositorD3D11.cpp | 12 ++++++++++++ gfx/layers/d3d9/CompositorD3D9.cpp | 12 ++++++++++++ gfx/layers/opengl/CompositorOGL.cpp | 12 ++++++++++++ 4 files changed, 42 insertions(+) diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index 06cb2792671..6075d57e424 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -85,6 +85,12 @@ void BasicCompositor::Destroy() TemporaryRef BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + RefPtr target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); if (!target) { diff --git a/gfx/layers/d3d11/CompositorD3D11.cpp b/gfx/layers/d3d11/CompositorD3D11.cpp index 60c7c2dc902..002c64367fc 100644 --- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -404,6 +404,12 @@ TemporaryRef CompositorD3D11::CreateRenderTarget(const gfx::IntRect& aRect, SurfaceInitMode aInit) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); @@ -430,6 +436,12 @@ CompositorD3D11::CreateRenderTargetFromSource(const gfx::IntRect &aRect, const CompositingRenderTarget* aSource, const gfx::IntPoint &aSourcePoint) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); diff --git a/gfx/layers/d3d9/CompositorD3D9.cpp b/gfx/layers/d3d9/CompositorD3D9.cpp index 96101a9117b..ad1647c0857 100644 --- a/gfx/layers/d3d9/CompositorD3D9.cpp +++ b/gfx/layers/d3d9/CompositorD3D9.cpp @@ -103,6 +103,12 @@ TemporaryRef CompositorD3D9::CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + if (!mDeviceManager) { return nullptr; } @@ -129,6 +135,12 @@ CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect, const CompositingRenderTarget *aSource, const gfx::IntPoint &aSourcePoint) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + if (!mDeviceManager) { return nullptr; } diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 92055e6d9e3..8202261bc62 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -606,6 +606,12 @@ CompositorOGL::PrepareViewport(const gfx::IntSize& aSize) TemporaryRef CompositorOGL::CreateRenderTarget(const IntRect &aRect, SurfaceInitMode aInit) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + GLuint tex = 0; GLuint fbo = 0; CreateFBOWithTexture(aRect, false, 0, &fbo, &tex); @@ -620,6 +626,12 @@ CompositorOGL::CreateRenderTargetFromSource(const IntRect &aRect, const CompositingRenderTarget *aSource, const IntPoint &aSourcePoint) { + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + GLuint tex = 0; GLuint fbo = 0; const CompositingRenderTargetOGL* sourceSurface From fa88132850e2209ec1a3f8cefd9b7d45c087212c Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Fri, 26 Sep 2014 15:05:14 -0400 Subject: [PATCH 14/85] Bug 1070722 - Use the imagelib high quality downscaler on OSX instead of the quartz one. r=jrmuizel --HG-- extra : rebase_source : 53c205cfc8af95be28d0878248b04e9872910651 --- gfx/2d/DrawTargetCG.cpp | 2 +- modules/libpref/init/all.js | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index c3c6d30a862..0c10a5a8441 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter) case Filter::POINT: return kCGInterpolationNone; case Filter::GOOD: - return kCGInterpolationDefault; + return kCGInterpolationLow; } } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 1490e6f3118..9bffe9b1b01 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3669,13 +3669,7 @@ pref("image.cache.timeweight", 500); // The default Accept header sent for images loaded over HTTP(S) pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5"); -// Whether we do high-quality image downscaling. OS X natively supports -// high-quality image scaling. -#ifdef XP_MACOSX -pref("image.high_quality_downscaling.enabled", false); -#else pref("image.high_quality_downscaling.enabled", true); -#endif // The minimum percent downscaling we'll use high-quality downscaling on, // interpreted as a floating-point number / 1000. From 5031ab0f15982a8535ca12cf11559a12061cd2b9 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 26 Sep 2014 15:45:45 -0400 Subject: [PATCH 15/85] Bug 1067018 - Always specify throw() for nothrow placement new/delete; r=glandium --- build/autoconf/android.m4 | 2 +- build/stlport/README.mozilla | 2 ++ build/stlport/overrides/new | 65 ++++++++++++++++++++++++++++++++++++ memory/mozalloc/mozalloc.h | 17 +++++++--- 4 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 build/stlport/overrides/new diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 index 1c0efae27c6..6d9ff0233f4 100644 --- a/build/autoconf/android.m4 +++ b/build/autoconf/android.m4 @@ -239,7 +239,7 @@ if test "$OS_TARGET" = "Android" -a -z "$gonkdir"; then AC_MSG_ERROR([Couldn't find path to gnu-libstdc++ in the android ndk]) fi else - STLPORT_CPPFLAGS="-isystem $_topsrcdir/build/stlport/stlport -isystem $android_ndk/sources/cxx-stl/system/include" + STLPORT_CPPFLAGS="-isystem $_topsrcdir/build/stlport/stlport -isystem $_topsrcdir/build/stlport/overrides -isystem $android_ndk/sources/cxx-stl/system/include" STLPORT_LIBS="$_objdir/build/stlport/libstlport_static.a -static-libstdc++" fi fi diff --git a/build/stlport/README.mozilla b/build/stlport/README.mozilla index fa7dc0552dd..dddf18c0371 100644 --- a/build/stlport/README.mozilla +++ b/build/stlport/README.mozilla @@ -1,6 +1,8 @@ This copy of STLport was taken from the Android NDK r8e. Android specific changes are listed in README.android. The libs/ directory containing prebuilt static libraries was removed. +The overrides/ directory contains Mozilla-specific overrides to the standard + C++ headers found in the NDK. The following patches are applied on top: - android-mozilla-config.patch: Adjusts Android-specific configuration diff --git a/build/stlport/overrides/new b/build/stlport/overrides/new new file mode 100644 index 00000000000..34feec8a676 --- /dev/null +++ b/build/stlport/overrides/new @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright (C) 2009 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * This header is taken from $ndk/sources/cxx-stl/system/include/new, + * and it fixes a bug in the NDK header where the nothrow versions of + * placement new and delete are not specified with 'throw()'. This bug + * causes GCC to not null-check the results from these functions. + */ +#ifndef __NEW__ +#define __NEW__ + +#include + +extern "C++" { + +namespace std { + struct nothrow_t {}; + extern const nothrow_t nothrow; +} + +void* operator new(std::size_t); +void* operator new[](std::size_t); +void operator delete(void*); +void operator delete[](void*); +void* operator new(std::size_t, const std::nothrow_t&) throw(); +void* operator new[](std::size_t, const std::nothrow_t&) throw(); +void operator delete(void*, const std::nothrow_t&) throw(); +void operator delete[](void*, const std::nothrow_t&) throw(); + +inline void* operator new(std::size_t, void* p) { return p; } +inline void* operator new[](std::size_t, void* p) { return p; } + +// these next two are not really required, since exceptions are off +inline void operator delete(void*, void*) { } +inline void operator delete[](void*, void*) { } + +} // extern C++ + +#endif // __NEW__ diff --git a/memory/mozalloc/mozalloc.h b/memory/mozalloc/mozalloc.h index 35d2b3634f4..5fe580f7714 100644 --- a/memory/mozalloc/mozalloc.h +++ b/memory/mozalloc/mozalloc.h @@ -180,13 +180,20 @@ MOZALLOC_EXPORT void* moz_valloc(size_t size) # define MOZALLOC_EXPORT_NEW #endif -#if defined(ANDROID) || defined(_MSC_VER) +#if defined(ANDROID) /* - * Android doesn't fully support exceptions, so its header - * has operators that don't specify throw() at all. Also include MSVC - * to suppress build warning spam (bug 578546). + * It's important to always specify 'throw()' in GCC because it's used to tell + * GCC that 'new' may return null. That makes GCC null-check the result before + * potentially initializing the memory to zero. + * Also, the Android minimalistic headers don't include std::bad_alloc. */ -#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS /**/ +#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw() +#define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS +#elif defined(_MSC_VER) +/* + * Suppress build warning spam (bug 578546). + */ +#define MOZALLOC_THROW_IF_HAS_EXCEPTIONS #define MOZALLOC_THROW_BAD_ALLOC_IF_HAS_EXCEPTIONS #else #define MOZALLOC_THROW_IF_HAS_EXCEPTIONS throw() From dde35410d0c1367e3cbc25043fac1320ee6b9ad8 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 26 Sep 2014 15:45:46 -0400 Subject: [PATCH 16/85] Bug 1073328 - Prevent using our own handler as system handler; r=snorp --- mobile/android/base/GeckoAppShell.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 10e7e32ea0a..5322cf5aa17 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -212,7 +212,9 @@ public class GeckoAppShell public static native void dispatchMemoryPressure(); public static void registerGlobalExceptionHandler() { - systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler(); + if (systemUncaughtHandler == null) { + systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler(); + } Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override From d4a650a82bf595843dc4ed507a19488f85681e96 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 26 Sep 2014 16:02:12 -0400 Subject: [PATCH 17/85] Backed out 4 changesets (bug 1068276, bug 1067664) for Android leaktest complaints. Backed out changeset 846faaa68219 (bug 1068276) Backed out changeset 35e71ffcf8e1 (bug 1068276) Backed out changeset 605f59a806fd (bug 1067664) Backed out changeset 2d5b7ea96b70 (bug 1067664) --HG-- extra : rebase_source : 7a90c78ba35f31acce7609d5d8375a593cb9825d --- build/automationutils.py | 49 +++++++++----------------- layout/tools/reftest/runreftest.py | 16 ++++----- testing/mochitest/mochitest_options.py | 27 ++++---------- testing/mochitest/runtests.py | 2 +- testing/mochitest/runtestsb2g.py | 2 +- 5 files changed, 30 insertions(+), 66 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index 46a909db5e4..df8be4946ed 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -196,7 +196,7 @@ def dumpLeakLog(leakLogFile, filter = False): # Simply copy the log. log.info(leakReport.rstrip("\n")) -def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMissingLeaks): +def processSingleLeakFile(leakLogFileName, processType, leakThreshold): """Process a single leak log. """ @@ -273,14 +273,11 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis if crashedOnPurpose: log.info("TEST-INFO | leakcheck | %s deliberate crash and thus no leak log" % processString) - elif ignoreMissingLeaks: - log.info("TEST-INFO | leakcheck | %s ignoring missing output line for total leaks" - % processString) else: - log.info("TEST-UNEXPECTED-FAIL | leakcheck | %s missing output line for total leaks!" + # TODO: This should be a TEST-UNEXPECTED-FAIL, but was changed to a warning + # due to too many intermittent failures (see bug 831223). + log.info("WARNING | leakcheck | %s missing output line for total leaks!" % processString) - log.info("TEST-INFO | leakcheck | missing output line from log file %s" - % leakLogFileName) return if totalBytesLeaked == 0: @@ -289,9 +286,14 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis # totalBytesLeaked was seen and is non-zero. if totalBytesLeaked > leakThreshold: - logAsWarning = True - # Fail the run if we're over the threshold (which defaults to 0) - prefix = "TEST-UNEXPECTED-FAIL" + if processType == "tab": + # For now, ignore tab process leaks. See bug 1051230. + log.info("WARNING | leakcheck | ignoring leaks in tab process") + prefix = "WARNING" + else: + logAsWarning = True + # Fail the run if we're over the threshold (which defaults to 0) + prefix = "TEST-UNEXPECTED-FAIL" else: prefix = "WARNING" # Create a comma delimited string of the first N leaked objects found, @@ -309,7 +311,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis log.info("%s | leakcheck | %s %d bytes leaked (%s)" % (prefix, processString, totalBytesLeaked, leakedObjectSummary)) -def processLeakLog(leakLogFile, leakThresholds, ignoreMissingLeaks): +def processLeakLog(leakLogFile, leakThreshold = 0): """Process the leak log, including separate leak logs created by child processes. @@ -324,28 +326,14 @@ def processLeakLog(leakLogFile, leakThresholds, ignoreMissingLeaks): optional. All other file names are treated as being for default processes. - - leakThresholds should be a dict mapping process types to leak thresholds, - in bytes. If a process type is not present in the dict the threshold - will be 0. """ if not os.path.exists(leakLogFile): log.info("WARNING | leakcheck | refcount logging is off, so leaks can't be detected!") return - # This list is based on kGeckoProcessTypeString. ipdlunittest processes likely - # are not going to produce leak logs we will ever see. - knownProcessTypes = ["default", "plugin", "tab", "geckomediaplugin"] - - for processType in knownProcessTypes: - log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes" - % (processType, leakThresholds.get(processType, 0))) - - for processType in leakThresholds: - if not processType in knownProcessTypes: - log.info("TEST-UNEXPECTED-FAIL | leakcheck | Unknown process type %s in leakThresholds" - % processType) + if leakThreshold != 0: + log.info("TEST-INFO | leakcheck | threshold set at %d bytes" % leakThreshold) (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile) if leakFileBase[-4:] == ".log": @@ -362,12 +350,7 @@ def processLeakLog(leakLogFile, leakThresholds, ignoreMissingLeaks): processType = m.group(1) else: processType = "default" - if not processType in knownProcessTypes: - log.info("TEST-UNEXPECTED-FAIL | leakcheck | Leak log with unknown process type %s" - % processType) - leakThreshold = leakThresholds.get(processType, 0) - processSingleLeakFile(thisFile, processType, leakThreshold, - processType in ignoreMissingLeaks) + processSingleLeakFile(thisFile, processType, leakThreshold) def replaceBackSlashes(input): return input.replace('\\', '/') diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index a9f22ffda41..875348570fc 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -344,7 +344,7 @@ class RefTest(object): # give the JS harness 30 seconds to deal # with its own timeouts timeout=options.timeout + 30.0) - processLeakLog(self.leakLogFile, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(self.leakLogFile, options.leakThreshold) self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.") finally: self.cleanup(profileDir) @@ -394,12 +394,12 @@ class ReftestOptions(OptionParser): default = 5 * 60, # 5 minutes per bug 479518 help = "reftest will timeout in specified number of seconds. [default %default s].") self.add_option("--leak-threshold", - action = "store", type = "int", dest = "defaultLeakThreshold", + action = "store", type = "int", dest = "leakThreshold", default = 0, - help = "fail if the number of bytes leaked in default " - "processes through refcounted objects (or bytes " - "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " - "is greater than the given number") + help = "fail if the number of bytes leaked through " + "refcounted objects (or bytes in classes with " + "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater " + "than the given number") self.add_option("--utility-path", action = "store", type = "string", dest = "utilityPath", default = self.automation.DIST_BIN, @@ -511,10 +511,6 @@ class ReftestOptions(OptionParser): if options.debugger is not None: self.error("cannot specify a debugger with parallel tests") - options.leakThresholds = {"default": options.defaultLeakThreshold} - - options.ignoreMissingLeaks = [] - return options def main(): diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index 507c22b83ef..91d79d470d9 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -223,12 +223,12 @@ class MochitestOptions(optparse.OptionParser): [["--leak-threshold"], { "action": "store", "type": "int", - "dest": "defaultLeakThreshold", + "dest": "leakThreshold", "metavar": "THRESHOLD", - "help": "fail if the number of bytes leaked in default " - "processes through refcounted objects (or bytes " - "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " - "is greater than the given number", + "help": "fail if the number of bytes leaked through " + "refcounted objects (or bytes in classes with " + "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater " + "than the given number", "default": 0, }], [["--fatal-assertions"], @@ -608,15 +608,6 @@ class MochitestOptions(optparse.OptionParser): if not os.path.isfile(f): self.error('Missing binary %s required for --use-test-media-devices') - options.leakThresholds = { - "default": options.defaultLeakThreshold, - "tab": 10000, # See dependencies of bug 1051230. - } - - # Bug 1051230 - Leak logging does not yet work for tab processes on desktop. - # Bug 1065098 - The geckomediaplugin process fails to produce a leak log for some reason. - options.ignoreMissingLeaks = ["tab", "geckomediaplugin"] - return options @@ -774,7 +765,7 @@ class B2GOptions(MochitestOptions): defaults["testPath"] = "" defaults["extensionsToExclude"] = ["specialpowers"] # See dependencies of bug 1038943. - defaults["defaultLeakThreshold"] = 5180 + defaults["leakThreshold"] = 5180 self.set_defaults(**defaults) def verifyRemoteOptions(self, options): @@ -821,12 +812,6 @@ class B2GOptions(MochitestOptions): options.sslPort = tempSSL options.httpPort = tempPort - # Bug 1071866 - B2G Mochitests do not always produce a leak log. - options.ignoreMissingLeaks.append("default") - - # Bug 1070068 - Leak logging does not work for tab processes on B2G. - assert "tab" in options.ignoreMissingLeaks, "Ignore failures for tab processes on B2G" - return options def elf_arm(self, filename): diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index 668af88fd19..b370bc8c33c 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -1843,7 +1843,7 @@ class Mochitest(MochitestUtilsMixin): self.stopVMwareRecording(); self.stopServers() - processLeakLog(self.leak_report_file, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(self.leak_report_file, options.leakThreshold) if self.nsprLogs: with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip: diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index ed0a2d99cb4..b9010e6d7b1 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -202,7 +202,7 @@ class B2GMochitest(MochitestUtilsMixin): self.app_ctx.dm.getFile(self.leak_report_file, local_leak_file.name) self.app_ctx.dm.removeFile(self.leak_report_file) - processLeakLog(local_leak_file.name, options.leakThresholds, options.ignoreMissingLeaks) + processLeakLog(local_leak_file.name, options.leakThreshold) except KeyboardInterrupt: self.log.info("runtests.py | Received keyboard interrupt.\n"); status = -1 From a3d8b8fbd331b7317672a62d7f448156a35a7b6b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 23 Sep 2014 17:32:57 -0700 Subject: [PATCH 18/85] Bug 1070955 - IonMonkey: GVN: Split phi operand removal from removePredecessor so that GVN can do this itself r=nbp --- js/src/jit/MIRGraph.cpp | 60 ++++++++++++++++++++--------------- js/src/jit/MIRGraph.h | 5 +++ js/src/jit/ValueNumbering.cpp | 37 +++++++++++---------- js/src/jit/ValueNumbering.h | 2 +- 4 files changed, 59 insertions(+), 45 deletions(-) diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 6cd4ad8a614..a92a47372fa 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -1298,6 +1298,16 @@ MBasicBlock::getSuccessorIndex(MBasicBlock *block) const MOZ_CRASH("Invalid successor"); } +size_t +MBasicBlock::getPredecessorIndex(MBasicBlock *block) const +{ + for (size_t i = 0, e = numPredecessors(); i < e; ++i) { + if (getPredecessor(i) == block) + return i; + } + MOZ_CRASH("Invalid predecessor"); +} + void MBasicBlock::replaceSuccessor(size_t pos, MBasicBlock *split) { @@ -1339,38 +1349,38 @@ MBasicBlock::clearDominatorInfo() } void -MBasicBlock::removePredecessor(MBasicBlock *pred) +MBasicBlock::removePredecessorWithoutPhiOperands(MBasicBlock *pred, size_t predIndex) { // If we're removing the last backedge, this is no longer a loop. if (isLoopHeader() && hasUniqueBackedge() && backedge() == pred) clearLoopHeader(); - for (size_t i = 0; i < numPredecessors(); i++) { - if (getPredecessor(i) != pred) - continue; - - // Adjust phis. Note that this can leave redundant phis - // behind. - if (!phisEmpty()) { - for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) - iter->removeOperand(i); - if (pred->successorWithPhis()) { - // Don't adjust successorWithPhis() if we haven't constructed - // this information yet. - JS_ASSERT(pred->positionInPhiSuccessor() == i); - pred->setSuccessorWithPhis(nullptr, 0); - for (size_t j = i+1; j < numPredecessors(); j++) - getPredecessor(j)->setSuccessorWithPhis(this, j - 1); - } - } - - // Remove from pred list. - MBasicBlock **ptr = predecessors_.begin() + i; - predecessors_.erase(ptr); - return; + // Adjust phis. Note that this can leave redundant phis behind. + // Don't adjust successorWithPhis() if we haven't constructed this + // information yet. + if (pred->successorWithPhis()) { + JS_ASSERT(pred->positionInPhiSuccessor() == predIndex); + pred->setSuccessorWithPhis(nullptr, 0); + for (size_t j = predIndex+1; j < numPredecessors(); j++) + getPredecessor(j)->setSuccessorWithPhis(this, j - 1); } - MOZ_CRASH("predecessor was not found"); + // Remove from pred list. + predecessors_.erase(predecessors_.begin() + predIndex); +} + +void +MBasicBlock::removePredecessor(MBasicBlock *pred) +{ + size_t predIndex = getPredecessorIndex(pred); + + // Remove the phi operands. + for (MPhiIterator iter(phisBegin()), end(phisEnd()); iter != end; ++iter) + iter->removeOperand(predIndex); + + // Now we can call the underlying function, which expects that phi + // operands have been removed. + removePredecessorWithoutPhiOperands(pred, predIndex); } void diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h index a57b32c2411..db128334ff1 100644 --- a/js/src/jit/MIRGraph.h +++ b/js/src/jit/MIRGraph.h @@ -244,6 +244,10 @@ class MBasicBlock : public TempObject, public InlineListNode // than two predecessors. void removePredecessor(MBasicBlock *pred); + // A version of removePredecessor which expects that phi operands to + // |pred| have already been removed. + void removePredecessorWithoutPhiOperands(MBasicBlock *pred, size_t predIndex); + // Resets all the dominator info so that it can be recomputed. void clearDominatorInfo(); @@ -565,6 +569,7 @@ class MBasicBlock : public TempObject, public InlineListNode size_t numSuccessors() const; MBasicBlock *getSuccessor(size_t index) const; size_t getSuccessorIndex(MBasicBlock *) const; + size_t getPredecessorIndex(MBasicBlock *) const; void setLoopDepth(uint32_t loopDepth) { loopDepth_ = loopDepth; diff --git a/js/src/jit/ValueNumbering.cpp b/js/src/jit/ValueNumbering.cpp index 1a2b3fc3dbc..82c6d822dd2 100644 --- a/js/src/jit/ValueNumbering.cpp +++ b/js/src/jit/ValueNumbering.cpp @@ -480,33 +480,28 @@ ValueNumberer::fixupOSROnlyLoop(MBasicBlock *block, MBasicBlock *backedge) // Remove the CFG edge between |pred| and |block|, after releasing the phi // operands on that edge and discarding any definitions consequently made dead. bool -ValueNumberer::removePredecessorAndDoDCE(MBasicBlock *block, MBasicBlock *pred) +ValueNumberer::removePredecessorAndDoDCE(MBasicBlock *block, MBasicBlock *pred, size_t predIndex) { MOZ_ASSERT(!block->isMarked(), "Block marked unreachable should have predecessors removed already"); // Before removing the predecessor edge, scan the phi operands for that edge // for dead code before they get removed. - if (!block->phisEmpty()) { - uint32_t index = pred->positionInPhiSuccessor(); - for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) { - MPhi *phi = *iter; - MOZ_ASSERT(!values_.has(phi), "Visited phi in block having predecessor removed"); + MOZ_ASSERT(nextDef_ == nullptr); + for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ) { + MPhi *phi = *iter++; + MOZ_ASSERT(!values_.has(phi), "Visited phi in block having predecessor removed"); - MDefinition *op = phi->getOperand(index); - if (op == phi) - continue; + MDefinition *op = phi->getOperand(predIndex); + phi->removeOperand(predIndex); - // Set the operand to the phi itself rather than just releasing it - // because removePredecessor expects to have something to release. - phi->replaceOperand(index, phi); - - if (!handleUseReleased(op, DontSetUseRemoved) || !processDeadDefs()) - return false; - } + nextDef_ = *iter; + if (!handleUseReleased(op, DontSetUseRemoved) || !processDeadDefs()) + return false; } + nextDef_ = nullptr; - block->removePredecessor(pred); + block->removePredecessorWithoutPhiOperands(pred, predIndex); return true; } @@ -551,7 +546,7 @@ ValueNumberer::removePredecessorAndCleanUp(MBasicBlock *block, MBasicBlock *pred } // Actually remove the CFG edge. - if (!removePredecessorAndDoDCE(block, pred)) + if (!removePredecessorAndDoDCE(block, pred, block->getPredecessorIndex(pred))) return false; // We've now edited the CFG; check to see if |block| became unreachable. @@ -573,7 +568,7 @@ ValueNumberer::removePredecessorAndCleanUp(MBasicBlock *block, MBasicBlock *pred if (block->isLoopHeader()) block->clearLoopHeader(); for (size_t i = 0, e = block->numPredecessors(); i < e; ++i) { - if (!removePredecessorAndDoDCE(block, block->getPredecessor(i))) + if (!removePredecessorAndDoDCE(block, block->getPredecessor(i), i)) return false; } @@ -586,6 +581,7 @@ ValueNumberer::removePredecessorAndCleanUp(MBasicBlock *block, MBasicBlock *pred if (!releaseResumePointOperands(outer) || !processDeadDefs()) return false; } + MOZ_ASSERT(nextDef_ == nullptr); for (MInstructionIterator iter(block->begin()), end(block->end()); iter != end; ) { MInstruction *ins = *iter++; nextDef_ = *iter; @@ -594,6 +590,7 @@ ValueNumberer::removePredecessorAndCleanUp(MBasicBlock *block, MBasicBlock *pred return false; } } + nextDef_ = nullptr; } else { #ifdef DEBUG MOZ_ASSERT(block->outerResumePoint() == nullptr, @@ -870,6 +867,7 @@ ValueNumberer::visitUnreachableBlock(MBasicBlock *block) // Discard any instructions with no uses. The remaining instructions will be // discarded when their last use is discarded. + MOZ_ASSERT(nextDef_ == nullptr); for (MDefinitionIterator iter(block); iter; ) { MDefinition *def = *iter++; if (def->hasUses()) @@ -894,6 +892,7 @@ ValueNumberer::visitBlock(MBasicBlock *block, const MBasicBlock *dominatorRoot) JitSpew(JitSpew_GVN, " Visiting block%u", block->id()); // Visit the definitions in the block top-down. + MOZ_ASSERT(nextDef_ == nullptr); for (MDefinitionIterator iter(block); iter; ) { MDefinition *def = *iter++; diff --git a/js/src/jit/ValueNumbering.h b/js/src/jit/ValueNumbering.h index 5fd841f2546..289400236e0 100644 --- a/js/src/jit/ValueNumbering.h +++ b/js/src/jit/ValueNumbering.h @@ -87,7 +87,7 @@ class ValueNumberer bool processDeadDefs(); bool fixupOSROnlyLoop(MBasicBlock *block, MBasicBlock *backedge); - bool removePredecessorAndDoDCE(MBasicBlock *block, MBasicBlock *pred); + bool removePredecessorAndDoDCE(MBasicBlock *block, MBasicBlock *pred, size_t predIndex); bool removePredecessorAndCleanUp(MBasicBlock *block, MBasicBlock *pred); MDefinition *simplified(MDefinition *def) const; From 64e89669a6c79038205c1c59630b7ca2fba59d68 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 26 Sep 2014 14:29:46 -0700 Subject: [PATCH 19/85] Backed out changeset b5705fbed44f (bug 1070722) for reftest orange on a CLOSED TREE --- gfx/2d/DrawTargetCG.cpp | 2 +- modules/libpref/init/all.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index 0c10a5a8441..c3c6d30a862 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -136,7 +136,7 @@ InterpolationQualityFromFilter(Filter aFilter) case Filter::POINT: return kCGInterpolationNone; case Filter::GOOD: - return kCGInterpolationLow; + return kCGInterpolationDefault; } } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 9bffe9b1b01..1490e6f3118 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3669,7 +3669,13 @@ pref("image.cache.timeweight", 500); // The default Accept header sent for images loaded over HTTP(S) pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5"); +// Whether we do high-quality image downscaling. OS X natively supports +// high-quality image scaling. +#ifdef XP_MACOSX +pref("image.high_quality_downscaling.enabled", false); +#else pref("image.high_quality_downscaling.enabled", true); +#endif // The minimum percent downscaling we'll use high-quality downscaling on, // interpreted as a floating-point number / 1000. From e7b2e4759ef6e1ab690482ee41b93307cd6ac9b6 Mon Sep 17 00:00:00 2001 From: "Nils Ohlmeier [:drno]" Date: Fri, 26 Sep 2014 14:36:03 -0700 Subject: [PATCH 20/85] Bug 1066775: fix end of ICE gathering expected r=spolk --- dom/media/tests/mochitest/templates.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dom/media/tests/mochitest/templates.js b/dom/media/tests/mochitest/templates.js index be034f445bd..050476ea671 100644 --- a/dom/media/tests/mochitest/templates.js +++ b/dom/media/tests/mochitest/templates.js @@ -398,8 +398,6 @@ var commandsPeerConnection = [ if (test.pcLocal.localRequiresTrickleIce) { ok(test.pcLocal._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); } - // a super slow TURN server might make this fail - ok(test.pcLocal.endOfTrickleIce, "Received end of ICE gathering candidate"); isnot(test.pcLocal._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); test.next(); } @@ -440,8 +438,6 @@ var commandsPeerConnection = [ if (test.pcRemote.localRequiresTrickleIce) { ok(test.pcRemote._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); } - // a super slow TURN server might make this fail - ok(test.pcRemote.endOfTrickleIce, "Received end of ICE gathering candidate"); isnot(test.pcRemote._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); test.next(); } @@ -999,8 +995,6 @@ var commandsDataChannel = [ if (test.pcLocal.localRequiresTrickleIce) { ok(test.pcLocal._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); } - // a super slow TURN server might make this fail - ok(test.pcLocal.endOfTrickleIce, "Received end of ICE gathering candidate"); isnot(test.pcLocal._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); test.next(); } @@ -1041,8 +1035,6 @@ var commandsDataChannel = [ if (test.pcRemote.localRequiresTrickleIce) { ok(test.pcRemote._local_ice_candidates.length > 0, "Received local trickle ICE candidates"); } - // a super slow TURN server might make this fail - ok(test.pcRemote.endOfTrickleIce, "Received end of ICE gathering candidate"); isnot(test.pcRemote._pc.iceGatheringState, GATH_NEW, "ICE gathering state is not 'new'"); test.next(); } From 132b4df9d0a90d4388cbd96cd971310c3f36aff8 Mon Sep 17 00:00:00 2001 From: James Long Date: Thu, 25 Sep 2014 23:04:50 -0400 Subject: [PATCH 21/85] Bug 1056409 - move the `sourceMapURL` property from Debugger.Script to Debugger.Source r=jorendorff commit a5e75ddf88dfc2fce84fb5889a63d5d805f2d1e1 Author: James Long patch --- js/src/doc/Debugger/Debugger.Script.md | 11 ----- js/src/doc/Debugger/Debugger.Source.md | 11 +++++ .../jit-test/tests/debug/Source-displayURL.js | 4 +- ...d.js => Source-sourceMapURL-deprecated.js} | 6 +-- ...sourceMapURL.js => Source-sourceMapURL.js} | 6 +-- js/src/vm/Debugger.cpp | 40 +++++++++---------- toolkit/devtools/server/actors/script.js | 6 +-- 7 files changed, 41 insertions(+), 43 deletions(-) rename js/src/jit-test/tests/debug/{Script-sourceMapURL-deprecated.js => Source-sourceMapURL-deprecated.js} (92%) rename js/src/jit-test/tests/debug/{Script-sourceMapURL.js => Source-sourceMapURL.js} (92%) diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md index e7e54be2f53..7f0f7537c08 100644 --- a/js/src/doc/Debugger/Debugger.Script.md +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -115,17 +115,6 @@ from its prototype: : This is `true` if this script's code is ECMAScript strict mode code, and `false` otherwise. -`sourceMapURL` -: If this script was produced by a minimizer or translated from some other - language, and we know the URL of a source map document relating - the source positions in this script to the corresponding source - positions in the original source, then this property's value is that - URL. Otherwise, this is `null`. - - (On the web, the translator may provide the source map URL in a - specially formatted comment in the JavaScript source code, or via a - header in the HTTP reply that carried the generated JavaScript.) - ## Function Properties of the Debugger.Script Prototype Object The functions described below may only be called with a `this` value diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md index 70e80ffd442..91e6a4ae31b 100644 --- a/js/src/doc/Debugger/Debugger.Source.md +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -74,6 +74,17 @@ from its prototype: `url` accessor on `Debugger.Source` instances for such sources should return `undefined`.) +`sourceMapURL` +: If this source was produced by a minimizer or translated from some other + language, and we know the URL of a source map document relating + the source positions in this source to the corresponding source + positions in the original source, then this property's value is that + URL. Otherwise, this is `null`. + + (On the web, the translator may provide the source map URL in a + specially formatted comment in the JavaScript source code, or via a + header in the HTTP reply that carried the generated JavaScript.) + `element` : The [`Debugger.Object`][object] instance referring to the DOM element to which this source code belongs, if any, or `undefined` if it belongs to no DOM diff --git a/js/src/jit-test/tests/debug/Source-displayURL.js b/js/src/jit-test/tests/debug/Source-displayURL.js index 51536d81388..a16a761860a 100644 --- a/js/src/jit-test/tests/debug/Source-displayURL.js +++ b/js/src/jit-test/tests/debug/Source-displayURL.js @@ -78,7 +78,7 @@ var capturedSourceMapURL; dbg.onNewScript = function (script) { capturedScript = script; capturedDisplayURL = script.source.displayURL; - capturedSourceMapURL = script.sourceMapURL; + capturedSourceMapURL = script.source.sourceMapURL; dbg.onNewScript = undefined; }; var fun = gw.makeDebuggeeValue(g.Function('//# sourceURL=munge.js\n//# sourceMappingURL=grunge.map\n')); @@ -87,5 +87,5 @@ assertEq(capturedScript, fun.script); assertEq(capturedDisplayURL, fun.script.source.displayURL); assertEq(capturedDisplayURL, 'munge.js'); -assertEq(capturedSourceMapURL, fun.script.sourceMapURL); +assertEq(capturedSourceMapURL, fun.script.source.sourceMapURL); assertEq(capturedSourceMapURL, 'grunge.map'); diff --git a/js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js b/js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js similarity index 92% rename from js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js rename to js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js index 7e9e59971f4..fb01ccb0a67 100644 --- a/js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js +++ b/js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js @@ -1,4 +1,4 @@ -// Script.prototype.sourceMapURL can be a string or null. +// Source.prototype.sourceMapURL can be a string or null. let g = newGlobal(); let dbg = new Debugger; @@ -6,7 +6,7 @@ let gw = dbg.addDebuggee(g); function getSourceMapURL() { let fw = gw.makeDebuggeeValue(g.f); - return fw.script.sourceMapURL; + return fw.script.source.sourceMapURL; } // Without a source map @@ -21,7 +21,7 @@ assertEq(getSourceMapURL(), 'file:///var/foo.js.map'); let fired = false; dbg.onDebuggerStatement = function (frame) { fired = true; - assertEq(frame.script.sourceMapURL, 'file:///var/bar.js.map'); + assertEq(frame.script.source.sourceMapURL, 'file:///var/bar.js.map'); }; g.evaluate('(function () { (function () { debugger; })(); })();', {sourceMapURL: 'file:///var/bar.js.map'}); diff --git a/js/src/jit-test/tests/debug/Script-sourceMapURL.js b/js/src/jit-test/tests/debug/Source-sourceMapURL.js similarity index 92% rename from js/src/jit-test/tests/debug/Script-sourceMapURL.js rename to js/src/jit-test/tests/debug/Source-sourceMapURL.js index 512b5205dc3..c14004e9431 100644 --- a/js/src/jit-test/tests/debug/Script-sourceMapURL.js +++ b/js/src/jit-test/tests/debug/Source-sourceMapURL.js @@ -1,4 +1,4 @@ -// Script.prototype.sourceMapURL can be a string or null. +// Source.prototype.sourceMapURL can be a string or null. let g = newGlobal(); let dbg = new Debugger; @@ -6,7 +6,7 @@ let gw = dbg.addDebuggee(g); function getSourceMapURL() { let fw = gw.makeDebuggeeValue(g.f); - return fw.script.sourceMapURL; + return fw.script.source.sourceMapURL; } // Without a source map @@ -21,7 +21,7 @@ assertEq(getSourceMapURL(), 'file:///var/foo.js.map'); let fired = false; dbg.onDebuggerStatement = function (frame) { fired = true; - assertEq(frame.script.sourceMapURL, 'file:///var/bar.js.map'); + assertEq(frame.script.source.sourceMapURL, 'file:///var/bar.js.map'); }; g.evaluate('(function () { (function () { debugger; })(); })();', {sourceMapURL: 'file:///var/bar.js.map'}); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2a3f8124f7f..d7f408e1469 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3204,26 +3204,6 @@ DebuggerScript_getStaticLevel(JSContext *cx, unsigned argc, Value *vp) return true; } -static bool -DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp) -{ - THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceMapURL)", args, obj, script); - - ScriptSource *source = script->scriptSource(); - JS_ASSERT(source); - - if (source->hasSourceMapURL()) { - JSString *str = JS_NewUCStringCopyZ(cx, source->sourceMapURL()); - if (!str) - return false; - args.rval().setString(str); - } else { - args.rval().setNull(); - } - - return true; -} - static bool DebuggerScript_getGlobal(JSContext *cx, unsigned argc, Value *vp) { @@ -3954,7 +3934,6 @@ static const JSPropertySpec DebuggerScript_properties[] = { JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0), JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0), JS_PSG("staticLevel", DebuggerScript_getStaticLevel, 0), - JS_PSG("sourceMapURL", DebuggerScript_getSourceMapUrl, 0), JS_PSG("global", DebuggerScript_getGlobal, 0), JS_PS_END }; @@ -4222,6 +4201,24 @@ DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp) return true; } +static bool +DebuggerSource_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp) +{ + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get sourceMapURL)", args, obj, sourceObject); + + ScriptSource *ss = sourceObject->source(); + if (ss->hasSourceMapURL()) { + JSString *str = JS_NewUCStringCopyZ(cx, ss->sourceMapURL()); + if (!str) + return false; + args.rval().setString(str); + } else { + args.rval().setNull(); + } + + return true; +} + static const JSPropertySpec DebuggerSource_properties[] = { JS_PSG("text", DebuggerSource_getText, 0), JS_PSG("url", DebuggerSource_getUrl, 0), @@ -4231,6 +4228,7 @@ static const JSPropertySpec DebuggerSource_properties[] = { JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0), JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0), JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0), + JS_PSG("sourceMapURL", DebuggerSource_getSourceMapUrl, 0), JS_PS_END }; diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index 586a4b6987f..1636f7049d3 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -4977,7 +4977,7 @@ ThreadSources.prototype = { * of an array of source actors for those. */ sourcesForScript: function (aScript) { - if (!this._useSourceMaps || !aScript.sourceMapURL) { + if (!this._useSourceMaps || !aScript.source.sourceMapURL) { return resolve([this._sourceForScript(aScript)].filter(isNotNull)); } @@ -5004,8 +5004,8 @@ ThreadSources.prototype = { * |aScript| must have a non-null sourceMapURL. */ sourceMap: function (aScript) { - dbg_assert(aScript.sourceMapURL, "Script should have a sourceMapURL"); - let sourceMapURL = this._normalize(aScript.sourceMapURL, aScript.url); + dbg_assert(aScript.source.sourceMapURL, "Script should have a sourceMapURL"); + let sourceMapURL = this._normalize(aScript.source.sourceMapURL, aScript.url); let map = this._fetchSourceMap(sourceMapURL, aScript.url) .then(aSourceMap => this.saveSourceMap(aSourceMap, aScript.url)); this._sourceMapsByGeneratedSource[aScript.url] = map; From 2176cd99b395e3740bb7e4f09b2266c7d1cc45b8 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Fri, 26 Sep 2014 17:36:39 -0400 Subject: [PATCH 22/85] Bug 1037892 - Implement changes to WebCrypto API from latest Editor's Draft r=bz,ttaubert * * * Add check for old structured clone format. --- dom/crypto/AesKeyAlgorithm.cpp | 82 --- dom/crypto/AesKeyAlgorithm.h | 38 - dom/crypto/BasicSymmetricKeyAlgorithm.h | 38 - dom/crypto/CryptoBuffer.cpp | 7 + dom/crypto/CryptoBuffer.h | 1 + dom/crypto/CryptoKey.cpp | 116 ++- dom/crypto/CryptoKey.h | 16 +- dom/crypto/CryptoKeyPair.cpp | 31 - dom/crypto/CryptoKeyPair.h | 63 -- dom/crypto/EcKeyAlgorithm.cpp | 43 -- dom/crypto/EcKeyAlgorithm.h | 48 -- dom/crypto/HmacKeyAlgorithm.cpp | 64 -- dom/crypto/HmacKeyAlgorithm.h | 72 -- dom/crypto/KeyAlgorithm.cpp | 116 --- dom/crypto/KeyAlgorithm.h | 80 --- dom/crypto/KeyAlgorithmProxy.cpp | 215 ++++++ dom/crypto/KeyAlgorithmProxy.h | 117 +++ dom/crypto/RsaHashedKeyAlgorithm.cpp | 80 --- dom/crypto/RsaHashedKeyAlgorithm.h | 53 -- dom/crypto/RsaKeyAlgorithm.cpp | 46 -- dom/crypto/RsaKeyAlgorithm.h | 63 -- dom/crypto/WebCryptoCommon.h | 42 +- dom/crypto/WebCryptoTask.cpp | 680 ++++++++---------- dom/crypto/moz.build | 17 +- dom/crypto/test/test_WebCrypto.html | 160 ++--- dom/crypto/test/test_WebCrypto_ECDH.html | 14 +- dom/crypto/test/test_WebCrypto_JWK.html | 2 - dom/crypto/test/test_WebCrypto_PBKDF2.html | 5 +- dom/crypto/test/test_WebCrypto_RSA_OAEP.html | 3 +- .../mochitest/general/test_interfaces.html | 2 - dom/webidl/KeyAlgorithm.webidl | 32 + dom/webidl/SubtleCrypto.webidl | 102 +-- dom/webidl/moz.build | 1 + 33 files changed, 887 insertions(+), 1562 deletions(-) delete mode 100644 dom/crypto/AesKeyAlgorithm.cpp delete mode 100644 dom/crypto/AesKeyAlgorithm.h delete mode 100644 dom/crypto/BasicSymmetricKeyAlgorithm.h delete mode 100644 dom/crypto/CryptoKeyPair.cpp delete mode 100644 dom/crypto/CryptoKeyPair.h delete mode 100644 dom/crypto/EcKeyAlgorithm.cpp delete mode 100644 dom/crypto/EcKeyAlgorithm.h delete mode 100644 dom/crypto/HmacKeyAlgorithm.cpp delete mode 100644 dom/crypto/HmacKeyAlgorithm.h delete mode 100644 dom/crypto/KeyAlgorithm.cpp delete mode 100644 dom/crypto/KeyAlgorithm.h create mode 100644 dom/crypto/KeyAlgorithmProxy.cpp create mode 100644 dom/crypto/KeyAlgorithmProxy.h delete mode 100644 dom/crypto/RsaHashedKeyAlgorithm.cpp delete mode 100644 dom/crypto/RsaHashedKeyAlgorithm.h delete mode 100644 dom/crypto/RsaKeyAlgorithm.cpp delete mode 100644 dom/crypto/RsaKeyAlgorithm.h create mode 100644 dom/webidl/KeyAlgorithm.webidl diff --git a/dom/crypto/AesKeyAlgorithm.cpp b/dom/crypto/AesKeyAlgorithm.cpp deleted file mode 100644 index 78d0ea99793..00000000000 --- a/dom/crypto/AesKeyAlgorithm.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/AesKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - -JSObject* -AesKeyAlgorithm::WrapObject(JSContext* aCx) -{ - return AesKeyAlgorithmBinding::Wrap(aCx, this); -} - -nsString -AesKeyAlgorithm::ToJwkAlg() const -{ - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { - switch (mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { - switch (mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { - switch (mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { - switch (mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW); - } - } - - return nsString(); -} - -bool -AesKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - return JS_WriteUint32Pair(aWriter, SCTAG_AESKEYALG, 0) && - JS_WriteUint32Pair(aWriter, mLength, 0) && - WriteString(aWriter, mName); -} - -KeyAlgorithm* -AesKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) -{ - uint32_t length, zero; - nsString name; - bool read = JS_ReadUint32Pair(aReader, &length, &zero) && - ReadString(aReader, name); - if (!read) { - return nullptr; - } - - return new AesKeyAlgorithm(aGlobal, name, length); -} - - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/AesKeyAlgorithm.h b/dom/crypto/AesKeyAlgorithm.h deleted file mode 100644 index 737576aa289..00000000000 --- a/dom/crypto/AesKeyAlgorithm.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_AesKeyAlgorithm_h -#define mozilla_dom_AesKeyAlgorithm_h - -#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm -{ -public: - AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) - : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength) - {} - - ~AesKeyAlgorithm() - {} - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - virtual nsString ToJwkAlg() const MOZ_OVERRIDE; - - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_AesKeyAlgorithm_h diff --git a/dom/crypto/BasicSymmetricKeyAlgorithm.h b/dom/crypto/BasicSymmetricKeyAlgorithm.h deleted file mode 100644 index afcdc5f2034..00000000000 --- a/dom/crypto/BasicSymmetricKeyAlgorithm.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_BasicSymmetricKeyAlgorithm_h -#define mozilla_dom_BasicSymmetricKeyAlgorithm_h - -#include "mozilla/dom/KeyAlgorithm.h" - -namespace mozilla { -namespace dom { - -class BasicSymmetricKeyAlgorithm : public KeyAlgorithm -{ -public: - BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) - : KeyAlgorithm(aGlobal, aName) - , mLength(aLength) - {} - - ~BasicSymmetricKeyAlgorithm() - {} - - uint16_t Length() const - { - return mLength; - } - -protected: - uint16_t mLength; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h diff --git a/dom/crypto/CryptoBuffer.cpp b/dom/crypto/CryptoBuffer.cpp index d719cf6a94d..73a5f81d68e 100644 --- a/dom/crypto/CryptoBuffer.cpp +++ b/dom/crypto/CryptoBuffer.cpp @@ -161,6 +161,13 @@ CryptoBuffer::ToSECItem() const return item; } +JSObject* +CryptoBuffer::ToUint8Array(JSContext* aCx) const +{ + return Uint8Array::Create(aCx, Length(), Elements()); +} + + // "BigInt" comes from the WebCrypto spec // ("unsigned long" isn't very "big", of course) // Likewise, the spec calls for big-endian ints diff --git a/dom/crypto/CryptoBuffer.h b/dom/crypto/CryptoBuffer.h index 6a9c4eb60b3..e5ec172c15d 100644 --- a/dom/crypto/CryptoBuffer.h +++ b/dom/crypto/CryptoBuffer.h @@ -40,6 +40,7 @@ public: nsresult FromJwkBase64(const nsString& aBase64); nsresult ToJwkBase64(nsString& aBase64); SECItem* ToSECItem() const; + JSObject* ToUint8Array(JSContext* aCx) const; bool GetBigIntValue(unsigned long& aRetVal); }; diff --git a/dom/crypto/CryptoKey.cpp b/dom/crypto/CryptoKey.cpp index 42ac26d5d86..3647ecb27b4 100644 --- a/dom/crypto/CryptoKey.cpp +++ b/dom/crypto/CryptoKey.cpp @@ -10,11 +10,12 @@ #include "mozilla/dom/CryptoKey.h" #include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/ToJSValue.h" namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal, mAlgorithm) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal) NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey) NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey) @@ -90,10 +91,35 @@ CryptoKey::Extractable() const return (mAttributes & EXTRACTABLE); } -KeyAlgorithm* -CryptoKey::Algorithm() const +void +CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle aRetVal, + ErrorResult& aRv) const { - return mAlgorithm; + bool converted = false; + JS::RootedValue val(cx); + switch (mAlgorithm.mType) { + case KeyAlgorithmProxy::AES: + converted = ToJSValue(cx, mAlgorithm.mAes, &val); + break; + case KeyAlgorithmProxy::HMAC: + converted = ToJSValue(cx, mAlgorithm.mHmac, &val); + break; + case KeyAlgorithmProxy::RSA: { + RootedDictionary rsa(cx); + mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa); + converted = ToJSValue(cx, rsa, &val); + break; + } + case KeyAlgorithmProxy::EC: + converted = ToJSValue(cx, mAlgorithm.mEc, &val); + break; + } + if (!converted) { + aRv.Throw(NS_ERROR_DOM_OPERATION_ERR); + return; + } + + aRetVal.set(&val.toObject()); } void @@ -125,6 +151,18 @@ CryptoKey::GetUsages(nsTArray& aRetVal) const } } +KeyAlgorithmProxy& +CryptoKey::Algorithm() +{ + return mAlgorithm; +} + +const KeyAlgorithmProxy& +CryptoKey::Algorithm() const +{ + return mAlgorithm; +} + CryptoKey::KeyType CryptoKey::GetKeyType() const { @@ -165,12 +203,6 @@ CryptoKey::SetExtractable(bool aExtractable) } } -void -CryptoKey::SetAlgorithm(KeyAlgorithm* aAlgorithm) -{ - mAlgorithm = aAlgorithm; -} - void CryptoKey::ClearUsages() { @@ -205,6 +237,12 @@ CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage) mAttributes |= aUsage; } +bool +CryptoKey::HasAnyUsage() +{ + return !!(mAttributes & USAGES_MASK); +} + bool CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage) { @@ -217,6 +255,25 @@ CryptoKey::HasUsageOtherThan(uint32_t aUsages) return !!(mAttributes & USAGES_MASK & ~aUsages); } +bool +CryptoKey::IsRecognizedUsage(const nsString& aUsage) +{ + KeyUsage dummy; + nsresult rv = StringToUsage(aUsage, dummy); + return NS_SUCCEEDED(rv); +} + +bool +CryptoKey::AllUsagesRecognized(const Sequence& aUsages) +{ + for (uint32_t i = 0; i < aUsages.Length(); ++i) { + if (!IsRecognizedUsage(aUsages[i])) { + return false; + } + } + return true; +} + void CryptoKey::SetSymKey(const CryptoBuffer& aSymKey) { mSymKey = aSymKey; @@ -441,14 +498,10 @@ SECKEYPrivateKey* CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { - if (!aJwk.mKty.WasPassed()) { - return nullptr; - } - CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; - if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) { + if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y, d; if (!aJwk.mCrv.WasPassed() || @@ -459,7 +512,7 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, } nsString namedCurve; - if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) { + if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } @@ -504,7 +557,7 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, PR_ARRAY_SIZE(keyTemplate)); } - if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { + if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e, d, p, q, dp, dq, qi; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || @@ -643,7 +696,7 @@ ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams, return false; } - aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_EC)); + aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC); return true; } @@ -674,7 +727,7 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey, return NS_ERROR_DOM_OPERATION_ERR; } - aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA)); + aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA); return NS_OK; } case ecKey: { @@ -716,11 +769,7 @@ SECKEYPublicKey* CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { - if (!aJwk.mKty.WasPassed()) { - return nullptr; - } - - if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { + if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || @@ -753,7 +802,7 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA); } - if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) { + if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y; if (!aJwk.mCrv.WasPassed() || @@ -777,7 +826,7 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, key->pkcs11ID = CK_INVALID_HANDLE; nsString namedCurve; - if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) { + if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } @@ -819,7 +868,7 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey, return NS_ERROR_DOM_OPERATION_ERR; } - aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA)); + aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA); return NS_OK; } case ecKey: @@ -857,11 +906,11 @@ CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker); } - return JS_WriteUint32Pair(aWriter, mAttributes, 0) && + return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) && WriteBuffer(aWriter, mSymKey) && WriteBuffer(aWriter, priv) && WriteBuffer(aWriter, pub) && - mAlgorithm->WriteStructuredClone(aWriter); + mAlgorithm.WriteStructuredClone(aWriter); } bool @@ -872,15 +921,15 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader) return false; } - uint32_t zero; + uint32_t version; CryptoBuffer sym, priv, pub; - nsRefPtr algorithm; - bool read = JS_ReadUint32Pair(aReader, &mAttributes, &zero) && + bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) && + (version == CRYPTOKEY_SC_VERSION) && ReadBuffer(aReader, sym) && ReadBuffer(aReader, priv) && ReadBuffer(aReader, pub) && - (algorithm = KeyAlgorithm::Create(mGlobal, aReader)); + mAlgorithm.ReadStructuredClone(aReader); if (!read) { return false; } @@ -894,7 +943,6 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader) if (pub.Length() > 0) { mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker); } - mAlgorithm = algorithm; // Ensure that what we've read is consistent // If the attributes indicate a key type, should have a key of that type diff --git a/dom/crypto/CryptoKey.h b/dom/crypto/CryptoKey.h index 44ae8195460..d1f6f71d2f1 100644 --- a/dom/crypto/CryptoKey.h +++ b/dom/crypto/CryptoKey.h @@ -14,11 +14,14 @@ #include "pk11pub.h" #include "keyhi.h" #include "ScopedNSSTypes.h" -#include "mozilla/dom/KeyAlgorithm.h" +#include "mozilla/ErrorResult.h" #include "mozilla/dom/CryptoBuffer.h" +#include "mozilla/dom/KeyAlgorithmProxy.h" #include "js/StructuredClone.h" #include "js/TypeDecls.h" +#define CRYPTOKEY_SC_VERSION 0x00000001 + class nsIGlobalObject; namespace mozilla { @@ -100,23 +103,28 @@ public: // WebIDL methods void GetType(nsString& aRetVal) const; bool Extractable() const; - KeyAlgorithm* Algorithm() const; + void GetAlgorithm(JSContext* cx, JS::MutableHandle aRetVal, + ErrorResult& aRv) const; void GetUsages(nsTArray& aRetVal) const; // The below methods are not exposed to JS, but C++ can use // them to manipulate the object + KeyAlgorithmProxy& Algorithm(); + const KeyAlgorithmProxy& Algorithm() const; KeyType GetKeyType() const; nsresult SetType(const nsString& aType); void SetType(KeyType aType); void SetExtractable(bool aExtractable); - void SetAlgorithm(KeyAlgorithm* aAlgorithm); void ClearUsages(); nsresult AddUsage(const nsString& aUsage); nsresult AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask); void AddUsage(KeyUsage aUsage); + bool HasAnyUsage(); bool HasUsage(KeyUsage aUsage); bool HasUsageOtherThan(uint32_t aUsages); + static bool IsRecognizedUsage(const nsString& aUsage); + static bool AllUsagesRecognized(const Sequence& aUsages); void SetSymKey(const CryptoBuffer& aSymKey); void SetPrivateKey(SECKEYPrivateKey* aPrivateKey); @@ -172,7 +180,7 @@ private: nsRefPtr mGlobal; uint32_t mAttributes; // see above - nsRefPtr mAlgorithm; + KeyAlgorithmProxy mAlgorithm; // Only one key handle should be set, according to the KeyType CryptoBuffer mSymKey; diff --git a/dom/crypto/CryptoKeyPair.cpp b/dom/crypto/CryptoKeyPair.cpp deleted file mode 100644 index 3a3bfc5fb10..00000000000 --- a/dom/crypto/CryptoKeyPair.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/CryptoKeyPair.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "nsContentUtils.h" - -namespace mozilla { -namespace dom { - - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKeyPair, mGlobal, mPublicKey, mPrivateKey) -NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKeyPair) -NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKeyPair) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKeyPair) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -JSObject* -CryptoKeyPair::WrapObject(JSContext* aCx) -{ - return CryptoKeyPairBinding::Wrap(aCx, this); -} - - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/CryptoKeyPair.h b/dom/crypto/CryptoKeyPair.h deleted file mode 100644 index 788a6873222..00000000000 --- a/dom/crypto/CryptoKeyPair.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_CryptoKeyPair_h -#define mozilla_dom_CryptoKeyPair_h - -#include "nsCycleCollectionParticipant.h" -#include "nsWrapperCache.h" -#include "nsIGlobalObject.h" -#include "mozilla/dom/CryptoKey.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class CryptoKeyPair MOZ_FINAL : public nsISupports, - public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CryptoKeyPair) - -public: - explicit CryptoKeyPair(nsIGlobalObject* aGlobal) - : mGlobal(aGlobal) - , mPublicKey(new CryptoKey(aGlobal)) - , mPrivateKey(new CryptoKey(aGlobal)) - { - SetIsDOMBinding(); - } - - nsIGlobalObject* GetParentObject() const - { - return mGlobal; - } - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - CryptoKey* PublicKey() const - { - return mPublicKey; - } - - CryptoKey* PrivateKey() const - { - return mPrivateKey; - } - -private: - ~CryptoKeyPair() {} - - nsRefPtr mGlobal; - nsRefPtr mPublicKey; - nsRefPtr mPrivateKey; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_CryptoKeyPair_h diff --git a/dom/crypto/EcKeyAlgorithm.cpp b/dom/crypto/EcKeyAlgorithm.cpp deleted file mode 100644 index 3dbb4d0009f..00000000000 --- a/dom/crypto/EcKeyAlgorithm.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/EcKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - -JSObject* -EcKeyAlgorithm::WrapObject(JSContext* aCx) -{ - return EcKeyAlgorithmBinding::Wrap(aCx, this); -} - -bool -EcKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - return JS_WriteUint32Pair(aWriter, SCTAG_ECKEYALG, 0) && - WriteString(aWriter, mNamedCurve) && - WriteString(aWriter, mName); -} - -KeyAlgorithm* -EcKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) -{ - nsString name; - nsString namedCurve; - bool read = ReadString(aReader, namedCurve) && - ReadString(aReader, name); - if (!read) { - return nullptr; - } - - return new EcKeyAlgorithm(aGlobal, name, namedCurve); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/EcKeyAlgorithm.h b/dom/crypto/EcKeyAlgorithm.h deleted file mode 100644 index f456e807b59..00000000000 --- a/dom/crypto/EcKeyAlgorithm.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_EcKeyAlgorithm_h -#define mozilla_dom_EcKeyAlgorithm_h - -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/KeyAlgorithm.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class EcKeyAlgorithm : public KeyAlgorithm -{ -public: - EcKeyAlgorithm(nsIGlobalObject* aGlobal, - const nsString& aName, - const nsString& aNamedCurve) - : KeyAlgorithm(aGlobal, aName) - , mNamedCurve(aNamedCurve) - {} - - ~EcKeyAlgorithm() - {} - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - void GetNamedCurve(nsString& aRetVal) const - { - aRetVal.Assign(mNamedCurve); - } - - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - -protected: - nsString mNamedCurve; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_EcKeyAlgorithm_h diff --git a/dom/crypto/HmacKeyAlgorithm.cpp b/dom/crypto/HmacKeyAlgorithm.cpp deleted file mode 100644 index 858287139f6..00000000000 --- a/dom/crypto/HmacKeyAlgorithm.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/HmacKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_INHERITED(HmacKeyAlgorithm, KeyAlgorithm, mHash) -NS_IMPL_ADDREF_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) -NS_IMPL_RELEASE_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HmacKeyAlgorithm) -NS_INTERFACE_MAP_END_INHERITING(KeyAlgorithm) - -JSObject* -HmacKeyAlgorithm::WrapObject(JSContext* aCx) -{ - return HmacKeyAlgorithmBinding::Wrap(aCx, this); -} - -nsString -HmacKeyAlgorithm::ToJwkAlg() const -{ - switch (mMechanism) { - case CKM_SHA_1_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS1); - case CKM_SHA256_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS256); - case CKM_SHA384_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS384); - case CKM_SHA512_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS512); - } - return nsString(); -} - -bool -HmacKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - nsString hashName; - mHash->GetName(hashName); - return JS_WriteUint32Pair(aWriter, SCTAG_HMACKEYALG, 0) && - JS_WriteUint32Pair(aWriter, mLength, 0) && - WriteString(aWriter, hashName) && - WriteString(aWriter, mName); -} - -KeyAlgorithm* -HmacKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) -{ - uint32_t length, zero; - nsString hash, name; - bool read = JS_ReadUint32Pair(aReader, &length, &zero) && - ReadString(aReader, hash) && - ReadString(aReader, name); - if (!read) { - return nullptr; - } - - return new HmacKeyAlgorithm(aGlobal, name, length, hash); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/HmacKeyAlgorithm.h b/dom/crypto/HmacKeyAlgorithm.h deleted file mode 100644 index 72b9c28cddd..00000000000 --- a/dom/crypto/HmacKeyAlgorithm.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_HmacKeyAlgorithm_h -#define mozilla_dom_HmacKeyAlgorithm_h - -#include "nsCycleCollectionParticipant.h" -#include "nsWrapperCache.h" -#include "nsAutoPtr.h" -#include "mozilla/dom/KeyAlgorithm.h" -#include "mozilla/dom/WebCryptoCommon.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class HmacKeyAlgorithm MOZ_FINAL : public KeyAlgorithm -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) - - HmacKeyAlgorithm(nsIGlobalObject* aGlobal, - const nsString& aName, - uint32_t aLength, - const nsString& aHash) - : KeyAlgorithm(aGlobal, aName) - , mHash(new KeyAlgorithm(aGlobal, aHash)) - , mLength(aLength) - { - switch (mHash->Mechanism()) { - case CKM_SHA_1: mMechanism = CKM_SHA_1_HMAC; break; - case CKM_SHA256: mMechanism = CKM_SHA256_HMAC; break; - case CKM_SHA384: mMechanism = CKM_SHA384_HMAC; break; - case CKM_SHA512: mMechanism = CKM_SHA512_HMAC; break; - default: mMechanism = UNKNOWN_CK_MECHANISM; break; - } - } - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - KeyAlgorithm* Hash() const - { - return mHash; - } - - uint32_t Length() const - { - return mLength; - } - - virtual nsString ToJwkAlg() const MOZ_OVERRIDE; - - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - -protected: - ~HmacKeyAlgorithm() - {} - - nsRefPtr mHash; - uint32_t mLength; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_HmacKeyAlgorithm_h diff --git a/dom/crypto/KeyAlgorithm.cpp b/dom/crypto/KeyAlgorithm.cpp deleted file mode 100644 index 13d0ffb818c..00000000000 --- a/dom/crypto/KeyAlgorithm.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/KeyAlgorithm.h" -#include "mozilla/dom/WebCryptoCommon.h" -#include "mozilla/dom/AesKeyAlgorithm.h" -#include "mozilla/dom/EcKeyAlgorithm.h" -#include "mozilla/dom/HmacKeyAlgorithm.h" -#include "mozilla/dom/RsaKeyAlgorithm.h" -#include "mozilla/dom/RsaHashedKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(KeyAlgorithm, mGlobal) -NS_IMPL_CYCLE_COLLECTING_ADDREF(KeyAlgorithm) -NS_IMPL_CYCLE_COLLECTING_RELEASE(KeyAlgorithm) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(KeyAlgorithm) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName) - : mGlobal(aGlobal) - , mName(aName) -{ - SetIsDOMBinding(); - - // Set mechanism based on algorithm name - mMechanism = MapAlgorithmNameToMechanism(aName); - - // HMAC not handled here, since it requires extra info -} - -KeyAlgorithm::~KeyAlgorithm() -{ -} - -JSObject* -KeyAlgorithm::WrapObject(JSContext* aCx) -{ - return KeyAlgorithmBinding::Wrap(aCx, this); -} - -nsString -KeyAlgorithm::ToJwkAlg() const -{ - return nsString(); -} - -void -KeyAlgorithm::GetName(nsString& aRetVal) const -{ - aRetVal.Assign(mName); -} - -bool -KeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - return WriteString(aWriter, mName); -} - -KeyAlgorithm* -KeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) -{ - uint32_t tag, zero; - bool read = JS_ReadUint32Pair( aReader, &tag, &zero ); - if (!read) { - return nullptr; - } - - KeyAlgorithm* algorithm = nullptr; - switch (tag) { - case SCTAG_KEYALG: { - nsString name; - read = ReadString(aReader, name); - if (!read) { - return nullptr; - } - algorithm = new KeyAlgorithm(aGlobal, name); - break; - } - case SCTAG_AESKEYALG: { - algorithm = AesKeyAlgorithm::Create(aGlobal, aReader); - break; - } - case SCTAG_ECKEYALG: { - algorithm = EcKeyAlgorithm::Create(aGlobal, aReader); - break; - } - case SCTAG_HMACKEYALG: { - algorithm = HmacKeyAlgorithm::Create(aGlobal, aReader); - break; - } - case SCTAG_RSAKEYALG: { - algorithm = RsaKeyAlgorithm::Create(aGlobal, aReader); - break; - } - case SCTAG_RSAHASHEDKEYALG: { - algorithm = RsaHashedKeyAlgorithm::Create(aGlobal, aReader); - break; - } - // No default, algorithm is already nullptr - } - - return algorithm; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/KeyAlgorithm.h b/dom/crypto/KeyAlgorithm.h deleted file mode 100644 index 32aaccbce80..00000000000 --- a/dom/crypto/KeyAlgorithm.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_KeyAlgorithm_h -#define mozilla_dom_KeyAlgorithm_h - -#include "nsCycleCollectionParticipant.h" -#include "nsIGlobalObject.h" -#include "nsWrapperCache.h" -#include "pk11pub.h" -#include "mozilla/dom/CryptoBuffer.h" -#include "js/StructuredClone.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class CryptoKey; -class KeyAlgorithm; - -enum KeyAlgorithmStructuredCloneTags { - SCTAG_KEYALG, - SCTAG_AESKEYALG, - SCTAG_ECKEYALG, - SCTAG_HMACKEYALG, - SCTAG_RSAKEYALG, - SCTAG_RSAHASHEDKEYALG -}; - -} - -namespace dom { - -class KeyAlgorithm : public nsISupports, - public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(KeyAlgorithm) - -public: - KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName); - - nsIGlobalObject* GetParentObject() const - { - return mGlobal; - } - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - void GetName(nsString& aRetVal) const; - - virtual nsString ToJwkAlg() const; - - // Structured clone support methods - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - - // Helper method to look up NSS methods - // Sub-classes should assign mMechanism on constructor - CK_MECHANISM_TYPE Mechanism() const { - return mMechanism; - } - -protected: - virtual ~KeyAlgorithm(); - - nsRefPtr mGlobal; - nsString mName; - CK_MECHANISM_TYPE mMechanism; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_KeyAlgorithm_h diff --git a/dom/crypto/KeyAlgorithmProxy.cpp b/dom/crypto/KeyAlgorithmProxy.cpp new file mode 100644 index 00000000000..6fcc943bdf2 --- /dev/null +++ b/dom/crypto/KeyAlgorithmProxy.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/KeyAlgorithmProxy.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + +bool +KeyAlgorithmProxy::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + if (!WriteString(aWriter, mName) || + !JS_WriteUint32Pair(aWriter, mType, KEY_ALGORITHM_SC_VERSION)) { + return false; + } + + switch (mType) { + case AES: + return JS_WriteUint32Pair(aWriter, mAes.mLength, 0); + case HMAC: + return JS_WriteUint32Pair(aWriter, mHmac.mLength, 0) && + WriteString(aWriter, mHmac.mHash.mName); + case RSA: { + return JS_WriteUint32Pair(aWriter, mRsa.mModulusLength, 0) && + WriteBuffer(aWriter, mRsa.mPublicExponent) && + WriteString(aWriter, mRsa.mHash.mName); + } + case EC: + return WriteString(aWriter, mEc.mNamedCurve); + } + + return false; +} + +bool +KeyAlgorithmProxy::ReadStructuredClone(JSStructuredCloneReader* aReader) +{ + uint32_t type, version, dummy; + if (!ReadString(aReader, mName) || + !JS_ReadUint32Pair(aReader, &type, &version)) { + return false; + } + + if (version != KEY_ALGORITHM_SC_VERSION) { + return false; + } + + mType = (KeyAlgorithmType) type; + switch (mType) { + case AES: { + uint32_t length; + if (!JS_ReadUint32Pair(aReader, &length, &dummy)) { + return false; + } + + mAes.mLength = length; + mAes.mName = mName; + return true; + } + case HMAC: { + if (!JS_ReadUint32Pair(aReader, &mHmac.mLength, &dummy) || + !ReadString(aReader, mHmac.mHash.mName)) { + return false; + } + + mHmac.mName = mName; + return true; + } + case RSA: { + uint32_t modulusLength; + nsString hashName; + if (!JS_ReadUint32Pair(aReader, &modulusLength, &dummy) || + !ReadBuffer(aReader, mRsa.mPublicExponent) || + !ReadString(aReader, mRsa.mHash.mName)) { + return false; + } + + mRsa.mModulusLength = modulusLength; + mRsa.mName = mName; + return true; + } + case EC: { + nsString namedCurve; + if (!ReadString(aReader, mEc.mNamedCurve)) { + return false; + } + + mEc.mName = mName; + return true; + } + } + + return false; +} + +CK_MECHANISM_TYPE +KeyAlgorithmProxy::Mechanism() const +{ + if (mType == HMAC) { + return GetMechanism(mHmac); + } + return MapAlgorithmNameToMechanism(mName); +} + +nsString +KeyAlgorithmProxy::JwkAlg() const +{ + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { + switch (mAes.mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { + switch (mAes.mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { + switch (mAes.mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { + switch (mAes.mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { + nsString hashName = mHmac.mHash.mName; + if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { + return NS_LITERAL_STRING(JWK_ALG_HS1); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { + return NS_LITERAL_STRING(JWK_ALG_HS256); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { + return NS_LITERAL_STRING(JWK_ALG_HS384); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + return NS_LITERAL_STRING(JWK_ALG_HS512); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { + nsString hashName = mRsa.mHash.mName; + if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { + return NS_LITERAL_STRING(JWK_ALG_RS1); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { + return NS_LITERAL_STRING(JWK_ALG_RS256); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { + return NS_LITERAL_STRING(JWK_ALG_RS384); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + return NS_LITERAL_STRING(JWK_ALG_RS512); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + nsString hashName = mRsa.mHash.mName; + if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { + return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { + return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { + return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_384); + } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512); + } + } + + return nsString(); +} + +CK_MECHANISM_TYPE +KeyAlgorithmProxy::GetMechanism(const KeyAlgorithm& aAlgorithm) +{ + // For everything but HMAC, the name determines the mechanism + // HMAC is handled by the specialization below + return MapAlgorithmNameToMechanism(aAlgorithm.mName); +} + +CK_MECHANISM_TYPE +KeyAlgorithmProxy::GetMechanism(const HmacKeyAlgorithm& aAlgorithm) +{ + // The use of HmacKeyAlgorithm doesn't completely prevent this + // method from being called with dictionaries that don't really + // represent HMAC key algorithms. + MOZ_ASSERT(aAlgorithm.mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)); + + CK_MECHANISM_TYPE hashMech; + hashMech = MapAlgorithmNameToMechanism(aAlgorithm.mHash.mName); + + switch (hashMech) { + case CKM_SHA_1: return CKM_SHA_1_HMAC; + case CKM_SHA256: return CKM_SHA256_HMAC; + case CKM_SHA384: return CKM_SHA384_HMAC; + case CKM_SHA512: return CKM_SHA512_HMAC; + } + return UNKNOWN_CK_MECHANISM; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/KeyAlgorithmProxy.h b/dom/crypto/KeyAlgorithmProxy.h new file mode 100644 index 00000000000..1d5f3eac5bf --- /dev/null +++ b/dom/crypto/KeyAlgorithmProxy.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_KeyAlgorithmProxy_h +#define mozilla_dom_KeyAlgorithmProxy_h + +#include "pk11pub.h" +#include "js/StructuredClone.h" +#include "mozilla/dom/KeyAlgorithmBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +#define KEY_ALGORITHM_SC_VERSION 0x00000001 + +namespace mozilla { +namespace dom { + +// A heap-safe variant of RsaHashedKeyAlgorithm +// The only difference is that it uses CryptoBuffer instead of Uint8Array +struct RsaHashedKeyAlgorithmStorage { + nsString mName; + KeyAlgorithm mHash; + uint16_t mModulusLength; + CryptoBuffer mPublicExponent; + + void + ToKeyAlgorithm(JSContext* aCx, RsaHashedKeyAlgorithm& aRsa) const + { + aRsa.mName = mName; + aRsa.mModulusLength = mModulusLength; + aRsa.mHash.mName = mHash.mName; + aRsa.mPublicExponent.Init(mPublicExponent.ToUint8Array(aCx)); + aRsa.mPublicExponent.ComputeLengthAndData(); + } +}; + +// This class encapuslates a KeyAlgorithm object, and adds several +// methods that make WebCrypto operations simpler. +struct KeyAlgorithmProxy +{ + enum KeyAlgorithmType { + AES, + HMAC, + RSA, + EC + }; + KeyAlgorithmType mType; + + // Plain is always populated with the algorithm name + // Others are only populated for the corresponding key type + nsString mName; + AesKeyAlgorithm mAes; + HmacKeyAlgorithm mHmac; + RsaHashedKeyAlgorithmStorage mRsa; + EcKeyAlgorithm mEc; + + // Structured clone + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + bool ReadStructuredClone(JSStructuredCloneReader* aReader); + + // Extract various forms of derived information + CK_MECHANISM_TYPE Mechanism() const; + nsString JwkAlg() const; + + // And in static form for calling on raw KeyAlgorithm dictionaries + static CK_MECHANISM_TYPE GetMechanism(const KeyAlgorithm& aAlgorithm); + static CK_MECHANISM_TYPE GetMechanism(const HmacKeyAlgorithm& aAlgorithm); + static nsString GetJwkAlg(const KeyAlgorithm& aAlgorithm); + + // Construction of the various algorithm types + void + MakeAes(const nsString& aName, uint32_t aLength) + { + mType = AES; + mName = aName; + mAes.mName = aName; + mAes.mLength = aLength; + } + + void + MakeHmac(uint32_t aLength, const nsString& aHashName) + { + mType = HMAC; + mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC); + mHmac.mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC); + mHmac.mLength = aLength; + mHmac.mHash.mName = aHashName; + } + + void + MakeRsa(const nsString& aName, uint32_t aModulusLength, + const CryptoBuffer& aPublicExponent, const nsString& aHashName) + { + mType = RSA; + mName = aName; + mRsa.mName = aName; + mRsa.mModulusLength = aModulusLength; + mRsa.mHash.mName = aHashName; + mRsa.mPublicExponent.Assign(aPublicExponent); + } + + void + MakeEc(const nsString& aName, const nsString& aNamedCurve) + { + mType = EC; + mName = aName; + mEc.mName = aName; + mEc.mNamedCurve = aNamedCurve; + } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_KeyAlgorithmProxy_h diff --git a/dom/crypto/RsaHashedKeyAlgorithm.cpp b/dom/crypto/RsaHashedKeyAlgorithm.cpp deleted file mode 100644 index 70623fcf22a..00000000000 --- a/dom/crypto/RsaHashedKeyAlgorithm.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/RsaHashedKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_CYCLE_COLLECTION_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm, mHash) -NS_IMPL_ADDREF_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) -NS_IMPL_RELEASE_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RsaHashedKeyAlgorithm) -NS_INTERFACE_MAP_END_INHERITING(RsaKeyAlgorithm) - -JSObject* -RsaHashedKeyAlgorithm::WrapObject(JSContext* aCx) -{ - return RsaHashedKeyAlgorithmBinding::Wrap(aCx, this); -} - -nsString -RsaHashedKeyAlgorithm::ToJwkAlg() const -{ - if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { - switch (mHash->Mechanism()) { - case CKM_SHA_1: return NS_LITERAL_STRING(JWK_ALG_RS1); - case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RS256); - case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RS384); - case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RS512); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { - switch(mHash->Mechanism()) { - case CKM_SHA_1: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP); - case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); - case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); - case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512); - } - } - - return nsString(); -} - -bool -RsaHashedKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - nsString hashName; - mHash->GetName(hashName); - return JS_WriteUint32Pair(aWriter, SCTAG_RSAHASHEDKEYALG, 0) && - JS_WriteUint32Pair(aWriter, mModulusLength, 0) && - WriteBuffer(aWriter, mPublicExponent) && - WriteString(aWriter, hashName) && - WriteString(aWriter, mName); -} - -KeyAlgorithm* -RsaHashedKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) { - uint32_t modulusLength, zero; - CryptoBuffer publicExponent; - nsString name, hash; - - bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) && - ReadBuffer(aReader, publicExponent) && - ReadString(aReader, hash) && - ReadString(aReader, name); - if (!read) { - return nullptr; - } - - return new RsaHashedKeyAlgorithm(aGlobal, name, modulusLength, publicExponent, hash); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/RsaHashedKeyAlgorithm.h b/dom/crypto/RsaHashedKeyAlgorithm.h deleted file mode 100644 index e8d546f74c9..00000000000 --- a/dom/crypto/RsaHashedKeyAlgorithm.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_RsaHashedKeyAlgorithm_h -#define mozilla_dom_RsaHashedKeyAlgorithm_h - -#include "nsAutoPtr.h" -#include "mozilla/dom/RsaKeyAlgorithm.h" - -namespace mozilla { -namespace dom { - -class RsaHashedKeyAlgorithm MOZ_FINAL : public RsaKeyAlgorithm -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) - - RsaHashedKeyAlgorithm(nsIGlobalObject* aGlobal, - const nsString& aName, - uint32_t aModulusLength, - const CryptoBuffer& aPublicExponent, - const nsString& aHashName) - : RsaKeyAlgorithm(aGlobal, aName, aModulusLength, aPublicExponent) - , mHash(new KeyAlgorithm(aGlobal, aHashName)) - {} - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - virtual nsString ToJwkAlg() const MOZ_OVERRIDE; - - KeyAlgorithm* Hash() const - { - return mHash; - } - - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - -private: - ~RsaHashedKeyAlgorithm() {} - - nsRefPtr mHash; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_RsaHashedKeyAlgorithm_h diff --git a/dom/crypto/RsaKeyAlgorithm.cpp b/dom/crypto/RsaKeyAlgorithm.cpp deleted file mode 100644 index 65f5ba4cd22..00000000000 --- a/dom/crypto/RsaKeyAlgorithm.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/RsaKeyAlgorithm.h" -#include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - -JSObject* -RsaKeyAlgorithm::WrapObject(JSContext* aCx) -{ - return RsaKeyAlgorithmBinding::Wrap(aCx, this); -} - -bool -RsaKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - return JS_WriteUint32Pair(aWriter, SCTAG_RSAKEYALG, 0) && - JS_WriteUint32Pair(aWriter, mModulusLength, 0) && - WriteBuffer(aWriter, mPublicExponent) && - WriteString(aWriter, mName); -} - -KeyAlgorithm* -RsaKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) -{ - uint32_t modulusLength, zero; - CryptoBuffer publicExponent; - nsString name; - bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) && - ReadBuffer(aReader, publicExponent) && - ReadString(aReader, name); - if (!read) { - return nullptr; - } - - return new RsaKeyAlgorithm(aGlobal, name, modulusLength, publicExponent); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/RsaKeyAlgorithm.h b/dom/crypto/RsaKeyAlgorithm.h deleted file mode 100644 index 9aa9e53782b..00000000000 --- a/dom/crypto/RsaKeyAlgorithm.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_RsaKeyAlgorithm_h -#define mozilla_dom_RsaKeyAlgorithm_h - -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/KeyAlgorithm.h" -#include "js/TypeDecls.h" - -namespace mozilla { -namespace dom { - -class RsaKeyAlgorithm : public KeyAlgorithm -{ -public: - RsaKeyAlgorithm(nsIGlobalObject* aGlobal, - const nsString& aName, - uint32_t aModulusLength, - const CryptoBuffer& aPublicExponent) - : KeyAlgorithm(aGlobal, aName) - , mModulusLength(aModulusLength) - , mPublicExponent(aPublicExponent) - {} - - ~RsaKeyAlgorithm() - {} - - virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; - - uint32_t ModulusLength() const - { - return mModulusLength; - } - - void GetPublicExponent(JSContext* cx, JS::MutableHandle aRetval, - ErrorResult& aError) const - { - TypedArrayCreator creator(mPublicExponent); - JSObject* retval = creator.Create(cx); - if (!retval) { - aError.Throw(NS_ERROR_OUT_OF_MEMORY); - } else { - aRetval.set(retval); - } - } - - virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; - static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - -protected: - uint32_t mModulusLength; - CryptoBuffer mPublicExponent; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_RsaKeyAlgorithm_h diff --git a/dom/crypto/WebCryptoCommon.h b/dom/crypto/WebCryptoCommon.h index 5a0e2c1e00a..43c6e47ead9 100644 --- a/dom/crypto/WebCryptoCommon.h +++ b/dom/crypto/WebCryptoCommon.h @@ -23,7 +23,6 @@ #define WEBCRYPTO_ALG_SHA512 "SHA-512" #define WEBCRYPTO_ALG_HMAC "HMAC" #define WEBCRYPTO_ALG_PBKDF2 "PBKDF2" -#define WEBCRYPTO_ALG_RSAES_PKCS1 "RSAES-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP" #define WEBCRYPTO_ALG_ECDH "ECDH" @@ -181,8 +180,6 @@ MapAlgorithmNameToMechanism(const nsString& aName) mechanism = CKM_SHA512; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { mechanism = CKM_PKCS5_PBKD2; - } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - mechanism = CKM_RSA_PKCS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { mechanism = CKM_RSA_PKCS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { @@ -194,14 +191,45 @@ MapAlgorithmNameToMechanism(const nsString& aName) return mechanism; } +#define NORMALIZED_EQUALS(aTest, aConst) \ + nsContentUtils::EqualsIgnoreASCIICase(aTest, NS_LITERAL_STRING(aConst)) + inline bool -NormalizeNamedCurveValue(const nsString& aNamedCurve, nsString& aDest) +NormalizeToken(const nsString& aName, nsString& aDest) { - if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P256)) { + // Algorithm names + if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CBC)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CBC); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CTR)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CTR); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_GCM)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_AES_GCM); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_KW)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_AES_KW); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA1)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_SHA1); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA256)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_SHA256); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA384)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_SHA384); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA512)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_SHA512); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_HMAC)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_HMAC); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP); + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) { + aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH); + // Named curve values + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256); - } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P384)) { + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384); - } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P521)) { + } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521); } else { return false; diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index accb5995452..f37442e2674 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -11,16 +11,9 @@ #include "jsapi.h" #include "mozilla/Telemetry.h" -#include "mozilla/dom/AesKeyAlgorithm.h" #include "mozilla/dom/CryptoBuffer.h" #include "mozilla/dom/CryptoKey.h" -#include "mozilla/dom/CryptoKeyPair.h" -#include "mozilla/dom/EcKeyAlgorithm.h" -#include "mozilla/dom/HmacKeyAlgorithm.h" -#include "mozilla/dom/KeyAlgorithm.h" -#include "mozilla/dom/RsaHashedKeyAlgorithm.h" -#include "mozilla/dom/RsaKeyAlgorithm.h" -#include "mozilla/dom/ToJSValue.h" +#include "mozilla/dom/KeyAlgorithmProxy.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/dom/WebCryptoTask.h" @@ -54,7 +47,7 @@ enum TelemetryAlgorithm { TA_AES_CFB = 2, TA_AES_CTR = 3, TA_AES_GCM = 4, - TA_RSAES_PKCS1 = 5, + TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed TA_RSA_OAEP = 6, // sign/verify TA_RSASSA_PKCS1 = 7, @@ -99,9 +92,7 @@ enum TelemetryAlgorithm { // Safety check for algorithms that use keys, suitable for constructors #define CHECK_KEY_ALGORITHM(keyAlg, algName) \ { \ - nsString keyAlgName; \ - keyAlg->GetName(keyAlgName); \ - if (!keyAlgName.EqualsLiteral(algName)) { \ + if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \ mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \ return; \ } \ @@ -137,42 +128,15 @@ GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName) JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject())); Algorithm alg; - if (!alg.Init(aCx, value) || !alg.mName.WasPassed()) { + if (!alg.Init(aCx, value)) { return NS_ERROR_DOM_SYNTAX_ERR; } - aName.Assign(alg.mName.Value()); + aName = alg.mName; } - // Normalize algorithm names. - if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CBC)) { - aName.AssignLiteral(WEBCRYPTO_ALG_AES_CBC); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CTR)) { - aName.AssignLiteral(WEBCRYPTO_ALG_AES_CTR); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_GCM)) { - aName.AssignLiteral(WEBCRYPTO_ALG_AES_GCM); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_KW)) { - aName.AssignLiteral(WEBCRYPTO_ALG_AES_KW); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA1)) { - aName.AssignLiteral(WEBCRYPTO_ALG_SHA1); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA256)) { - aName.AssignLiteral(WEBCRYPTO_ALG_SHA256); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA384)) { - aName.AssignLiteral(WEBCRYPTO_ALG_SHA384); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA512)) { - aName.AssignLiteral(WEBCRYPTO_ALG_SHA512); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_HMAC)) { - aName.AssignLiteral(WEBCRYPTO_ALG_HMAC); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_PBKDF2)) { - aName.AssignLiteral(WEBCRYPTO_ALG_PBKDF2); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSAES_PKCS1)) { - aName.AssignLiteral(WEBCRYPTO_ALG_RSAES_PKCS1); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSASSA_PKCS1)) { - aName.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSA_OAEP)) { - aName.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP); - } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_ECDH)) { - aName.AssignLiteral(WEBCRYPTO_ALG_ECDH); + if (!NormalizeToken(aName, aName)) { + return NS_ERROR_DOM_SYNTAX_ERR; } return NS_OK; @@ -230,17 +194,17 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { RootedDictionary params(aCx); - if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || - !params.mLength.WasPassed()) { + if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) { return NS_ERROR_DOM_SYNTAX_ERR; } - size_t length = params.mLength.Value(); - if (length != 128 && length != 192 && length != 256) { + if (params.mLength != 128 && + params.mLength != 192 && + params.mLength != 256) { return NS_ERROR_DOM_DATA_ERR; } - aLength = length; + aLength = params.mLength; return NS_OK; } @@ -248,8 +212,7 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, // determine key length as the block size of the given hash. if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); - if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || - !params.mHash.WasPassed()) { + if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -260,7 +223,7 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, } nsString hashName; - if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) { + if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -318,7 +281,6 @@ CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle aSrc) return false; } - // Implementation of WebCryptoTask methods void @@ -464,12 +426,12 @@ public: telemetryAlg = TA_AES_CBC; AesCbcParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv) || !params.mIv.WasPassed()) { + if (NS_FAILED(rv)) { mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value()) + ATTEMPT_BUFFER_INIT(mIv, params.mIv) if (mIv.Length() != 16) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; @@ -481,19 +443,18 @@ public: telemetryAlg = TA_AES_CTR; AesCtrParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv) || !params.mCounter.WasPassed() || - !params.mLength.WasPassed()) { + if (NS_FAILED(rv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mCounter.Value()) + ATTEMPT_BUFFER_INIT(mIv, params.mCounter) if (mIv.Length() != 16) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; } - mCounterLength = params.mLength.Value(); + mCounterLength = params.mLength; } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM); @@ -501,12 +462,12 @@ public: telemetryAlg = TA_AES_GCM; AesGcmParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv) || !params.mIv.WasPassed()) { + if (NS_FAILED(rv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value()) + ATTEMPT_BUFFER_INIT(mIv, params.mIv) if (params.mAdditionalData.WasPassed()) { ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value()) @@ -743,104 +704,6 @@ private: } }; -class RsaesPkcs1Task : public ReturnArrayBufferViewTask, - public DeferredData -{ -public: - RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm, - CryptoKey& aKey, bool aEncrypt) - : mPrivKey(aKey.GetPrivateKey()) - , mPubKey(aKey.GetPublicKey()) - , mEncrypt(aEncrypt) - { - Init(aCx, aAlgorithm, aKey, aEncrypt); - } - - RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm, - CryptoKey& aKey, const CryptoOperationData& aData, - bool aEncrypt) - : mPrivKey(aKey.GetPrivateKey()) - , mPubKey(aKey.GetPublicKey()) - , mEncrypt(aEncrypt) - { - Init(aCx, aAlgorithm, aKey, aEncrypt); - SetData(aData); - } - - void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, - CryptoKey& aKey, bool aEncrypt) - { - - Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1); - - CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSAES_PKCS1); - - if (mEncrypt) { - if (!mPubKey) { - mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; - return; - } - mStrength = SECKEY_PublicKeyStrength(mPubKey); - } else { - if (!mPrivKey) { - mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; - return; - } - mStrength = PK11_GetPrivateModulusLen(mPrivKey); - } - } - -private: - ScopedSECKEYPrivateKey mPrivKey; - ScopedSECKEYPublicKey mPubKey; - uint32_t mStrength; - bool mEncrypt; - - virtual nsresult BeforeCrypto() MOZ_OVERRIDE - { - if (!mDataIsSet) { - return NS_ERROR_DOM_OPERATION_ERR; - } - - // Verify that the data input is not too big - // (as required by PKCS#1 / RFC 3447, Section 7.2) - // http://tools.ietf.org/html/rfc3447#section-7.2 - if (mEncrypt && mData.Length() > mStrength - 11) { - return NS_ERROR_DOM_DATA_ERR; - } - - return NS_OK; - } - - virtual nsresult DoCrypto() MOZ_OVERRIDE - { - nsresult rv; - - // Ciphertext is an integer mod the modulus, so it will be - // no longer than mStrength octets - if (!mResult.SetLength(mStrength)) { - return NS_ERROR_DOM_UNKNOWN_ERR; - } - - if (mEncrypt) { - rv = MapSECStatus(PK11_PubEncryptPKCS1( - mPubKey.get(), mResult.Elements(), - mData.Elements(), mData.Length(), - nullptr)); - } else { - uint32_t outLen; - rv = MapSECStatus(PK11_PrivDecryptPKCS1( - mPrivKey.get(), mResult.Elements(), - &outLen, mResult.Length(), - mData.Elements(), mData.Length())); - mResult.SetLength(outLen); - } - - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR); - return NS_OK; - } -}; - class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData { @@ -896,8 +759,8 @@ public: return; } - if (params.mLabel.WasPassed() && !params.mLabel.Value().IsNull()) { - ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value().Value()); + if (params.mLabel.WasPassed()) { + ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value()); } } // Otherwise mLabel remains the empty octet string, as intended @@ -906,10 +769,7 @@ public: // static_cast is safe because we only get here if the algorithm name // is RSA-OAEP, and that only happens if we've constructed // an RsaHashedKeyAlgorithm. - // TODO: Add As* methods to KeyAlgorithm (Bug 1036734) - nsRefPtr rsaAlg = - static_cast(aKey.Algorithm()); - mHashMechanism = rsaAlg->Hash()->Mechanism(); + mHashMechanism = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash); switch (mHashMechanism) { case CKM_SHA_1: @@ -996,7 +856,7 @@ public: const CryptoOperationData& aSignature, const CryptoOperationData& aData, bool aSign) - : mMechanism(aKey.Algorithm()->Mechanism()) + : mMechanism(aKey.Algorithm().Mechanism()) , mSymKey(aKey.GetSymKey()) , mSign(aSign) { @@ -1120,10 +980,10 @@ public: // static_cast is safe because we only get here if the algorithm name // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed // an RsaHashedKeyAlgorithm - nsRefPtr rsaAlg = static_cast(aKey.Algorithm()); - nsRefPtr hashAlg = rsaAlg->Hash(); + CK_MECHANISM_TYPE mech; + mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash); - switch (hashAlg->Mechanism()) { + switch (mech) { case CKM_SHA_1: mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break; case CKM_SHA256: @@ -1320,7 +1180,7 @@ public: // Check 'alg' if (aJwk.mAlg.WasPassed() && - aJwk.mAlg.Value() != aKey->Algorithm()->ToJwkAlg()) { + aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) { return false; } @@ -1429,6 +1289,10 @@ public: } SetKeyData(aCx, aKeyData); + if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { + mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; + return; + } } void Init(JSContext* aCx, @@ -1445,11 +1309,11 @@ public: if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; @@ -1481,8 +1345,6 @@ public: // Construct an appropriate KeyAlorithm, // and verify that usages are appropriate - nsRefPtr algorithm; - nsIGlobalObject* global = mKey->GetParentObject(); uint32_t length = 8 * mKeyData.Length(); // bytes to bits if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || @@ -1501,17 +1363,17 @@ public: if ( (length != 128) && (length != 192) && (length != 256) ) { return NS_ERROR_DOM_DATA_ERR; } - algorithm = new AesKeyAlgorithm(global, mAlgName, length); + mKey->Algorithm().MakeAes(mAlgName, length); if (mDataIsJwk && mJwk.mUse.WasPassed() && !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) { return NS_ERROR_DOM_DATA_ERR; } } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { - if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) { + if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS)) { return NS_ERROR_DOM_DATA_ERR; } - algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length); + mKey->Algorithm().MakeAes(mAlgName, length); if (mDataIsJwk && mJwk.mUse.WasPassed()) { // There is not a 'use' value consistent with PBKDF @@ -1522,8 +1384,9 @@ public: return NS_ERROR_DOM_DATA_ERR; } - algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName); - if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) { + mKey->Algorithm().MakeHmac(length, mHashName); + + if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -1535,18 +1398,14 @@ public: return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } - mKey->SetAlgorithm(algorithm); mKey->SetSymKey(mKeyData); mKey->SetType(CryptoKey::SECRET); - mEarlyComplete = true; - return NS_OK; - } - nsresult AfterCrypto() MOZ_OVERRIDE - { if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; } + + mEarlyComplete = true; return NS_OK; } @@ -1576,6 +1435,10 @@ public: } SetKeyData(aCx, aKeyData); + if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { + mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; + return; + } } void Init(JSContext* aCx, @@ -1593,16 +1456,24 @@ public: mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { - mEarlyRv = NS_ERROR_DOM_DATA_ERR; - return; - } - - mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; } + + mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName); + if (NS_FAILED(mEarlyRv)) { + mEarlyRv = NS_ERROR_DOM_DATA_ERR; + return; + } + } + + // Check support for the algorithm and hash names + CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName); + CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName); + if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) { + mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; + return; } } @@ -1669,9 +1540,7 @@ private: virtual nsresult AfterCrypto() MOZ_OVERRIDE { // Check permissions for the requested operation - nsIGlobalObject* global = mKey->GetParentObject(); - if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || - mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { if ((mKey->GetKeyType() == CryptoKey::PUBLIC && mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) || (mKey->GetKeyType() == CryptoKey::PRIVATE && @@ -1687,23 +1556,9 @@ private: } } - // Construct an appropriate KeyAlgorithm - if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - mKey->SetAlgorithm(new RsaKeyAlgorithm(global, mAlgName, mModulusLength, mPublicExponent)); - } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || - mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { - nsRefPtr algorithm = - new RsaHashedKeyAlgorithm(global, mAlgName, - mModulusLength, mPublicExponent, mHashName); - if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) { - return NS_ERROR_DOM_SYNTAX_ERR; - } - - if (algorithm->Hash()->Mechanism() == UNKNOWN_CK_MECHANISM) { - return NS_ERROR_DOM_NOT_SUPPORTED_ERR; - } - mKey->SetAlgorithm(algorithm); - } + // Set an appropriate KeyAlgorithm + mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, + mPublicExponent, mHashName); if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; @@ -1793,7 +1648,7 @@ private: // Extract 'crv' parameter from JWKs. if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) { + if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } } @@ -1809,8 +1664,7 @@ private: return NS_ERROR_DOM_DATA_ERR; } - nsIGlobalObject* global = mKey->GetParentObject(); - mKey->SetAlgorithm(new EcKeyAlgorithm(global, mAlgName, mNamedCurve)); + mKey->Algorithm().MakeEc(mAlgName, mNamedCurve); if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; @@ -1830,13 +1684,8 @@ public: , mPublicKey(aKey.GetPublicKey()) , mKeyType(aKey.GetKeyType()) , mExtractable(aKey.Extractable()) - , mAlg(aKey.Algorithm()->ToJwkAlg()) + , mAlg(aKey.Algorithm().JwkAlg()) { - if (!aKey.Extractable()) { - mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; - return; - } - aKey.GetUsages(mKeyUsages); } @@ -1897,7 +1746,7 @@ private: return NS_ERROR_DOM_OPERATION_ERR; } mJwk.mK.Construct(k); - mJwk.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC)); + mJwk.mKty = NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC); } else if (mKeyType == CryptoKey::PUBLIC) { if (!mPublicKey) { return NS_ERROR_DOM_UNKNOWN_ERR; @@ -1975,7 +1824,6 @@ public: } // Construct an appropriate KeyAlorithm - nsRefPtr algorithm; uint32_t allowedUsages = 0; if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || @@ -1985,19 +1833,20 @@ public: if (NS_FAILED(mEarlyRv)) { return; } - algorithm = new AesKeyAlgorithm(global, algName, mLength); + mKey->Algorithm().MakeAes(algName, mLength); + allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT | CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY; } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; @@ -2014,7 +1863,7 @@ public: return; } - algorithm = new HmacKeyAlgorithm(global, algName, mLength, hashName); + mKey->Algorithm().MakeHmac(mLength, hashName); allowedUsages = CryptoKey::SIGN | CryptoKey::VERIFY; } else { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; @@ -2031,8 +1880,7 @@ public: } mLength = mLength >> 3; // bits to bytes - mMechanism = algorithm->Mechanism(); - mKey->SetAlgorithm(algorithm); + mMechanism = mKey->Algorithm().Mechanism(); // SetSymKey done in Resolve, after we've done the keygen } @@ -2089,7 +1937,8 @@ public: } // Create an empty key and set easy attributes - mKeyPair = new CryptoKeyPair(global); + mKeyPair.mPrivateKey = new CryptoKey(global); + mKeyPair.mPublicKey = new CryptoKey(global); // Extract algorithm name nsString algName; @@ -2100,63 +1949,36 @@ public: } // Construct an appropriate KeyAlorithm - KeyAlgorithm* algorithm; uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0; if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() || - !params.mPublicExponent.WasPassed() || - !params.mHash.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } // Pull relevant info - uint32_t modulusLength = params.mModulusLength.Value(); + uint32_t modulusLength = params.mModulusLength; CryptoBuffer publicExponent; - ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value()); + ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent); nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } // Create algorithm - algorithm = new RsaHashedKeyAlgorithm(global, algName, modulusLength, - publicExponent, hashName); - mKeyPair->PublicKey()->SetAlgorithm(algorithm); - mKeyPair->PrivateKey()->SetAlgorithm(algorithm); - mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; - - // Set up params struct - mRsaParams.keySizeInBits = modulusLength; - bool converted = publicExponent.GetBigIntValue(mRsaParams.pe); - if (!converted) { - mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; - return; - } - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - RootedDictionary params(aCx); - mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() || - !params.mPublicExponent.WasPassed()) { - mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; - return; - } - - // Pull relevant info - uint32_t modulusLength = params.mModulusLength.Value(); - CryptoBuffer publicExponent; - ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value()); - - // Create algorithm and note the mechanism - algorithm = new RsaKeyAlgorithm(global, algName, modulusLength, - publicExponent); - mKeyPair->PublicKey()->SetAlgorithm(algorithm); - mKeyPair->PrivateKey()->SetAlgorithm(algorithm); + mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(algName, + modulusLength, + publicExponent, + hashName); + mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(algName, + modulusLength, + publicExponent, + hashName); mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // Set up params struct @@ -2169,20 +1991,19 @@ public: } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - if (!NormalizeNamedCurveValue(params.mNamedCurve.Value(), mNamedCurve)) { + if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; return; } // Create algorithm. - algorithm = new EcKeyAlgorithm(global, algName, mNamedCurve); - mKeyPair->PublicKey()->SetAlgorithm(algorithm); - mKeyPair->PrivateKey()->SetAlgorithm(algorithm); + mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve); + mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve); mMechanism = CKM_EC_KEY_PAIR_GEN; } else { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; @@ -2193,8 +2014,7 @@ public: if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { privateAllowedUsages = CryptoKey::SIGN; publicAllowedUsages = CryptoKey::VERIFY; - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || - algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY; publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY; } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { @@ -2202,31 +2022,38 @@ public: publicAllowedUsages = 0; } - mKeyPair->PrivateKey()->SetExtractable(aExtractable); - mKeyPair->PrivateKey()->SetType(CryptoKey::PRIVATE); + mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable); + mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE); - mKeyPair->PublicKey()->SetExtractable(true); - mKeyPair->PublicKey()->SetType(CryptoKey::PUBLIC); + mKeyPair.mPublicKey.get()->SetExtractable(true); + mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC); - mKeyPair->PrivateKey()->ClearUsages(); - mKeyPair->PublicKey()->ClearUsages(); + mKeyPair.mPrivateKey.get()->ClearUsages(); + mKeyPair.mPublicKey.get()->ClearUsages(); for (uint32_t i=0; i < aKeyUsages.Length(); ++i) { - mEarlyRv = mKeyPair->PrivateKey()->AddUsageIntersecting(aKeyUsages[i], - privateAllowedUsages); + mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i], + privateAllowedUsages); if (NS_FAILED(mEarlyRv)) { return; } - mEarlyRv = mKeyPair->PublicKey()->AddUsageIntersecting(aKeyUsages[i], - publicAllowedUsages); + mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i], + publicAllowedUsages); if (NS_FAILED(mEarlyRv)) { return; } } + + // If no usages ended up being allowed, DataError + if (!mKeyPair.mPrivateKey.get()->HasAnyUsage() || + !mKeyPair.mPrivateKey.get()->HasAnyUsage()) { + mEarlyRv = NS_ERROR_DOM_DATA_ERR; + return; + } } private: - nsRefPtr mKeyPair; + CryptoKeyPair mKeyPair; CK_MECHANISM_TYPE mMechanism; PK11RSAGenParams mRsaParams; ScopedSECKEYPublicKey mPublicKey; @@ -2275,8 +2102,8 @@ private: return NS_ERROR_DOM_UNKNOWN_ERR; } - mKeyPair->PrivateKey()->SetPrivateKey(mPrivateKey); - mKeyPair->PublicKey()->SetPublicKey(mPublicKey); + mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey); + mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey); return NS_OK; } @@ -2284,11 +2111,6 @@ private: { mResultPromise->MaybeResolve(mKeyPair); } - - virtual void Cleanup() MOZ_OVERRIDE - { - mKeyPair = nullptr; - } }; class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask @@ -2326,8 +2148,7 @@ public: RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() || - !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } @@ -2340,7 +2161,7 @@ public: // Extract the hash algorithm. nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); if (NS_FAILED(mEarlyRv)) { return; } @@ -2357,9 +2178,9 @@ public: } } - ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value()) + ATTEMPT_BUFFER_INIT(mSalt, params.mSalt) mLength = aLength >> 3; // bits to bytes - mIterations = params.mIterations.Value(); + mIterations = params.mIterations; } private: @@ -2494,25 +2315,23 @@ public: // Retrieve the peer's public key. RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv) || !params.mPublic.WasPassed()) { + if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - CryptoKey* publicKey = params.mPublic.Value(); + CryptoKey* publicKey = params.mPublic; mPubKey = publicKey->GetPublicKey(); if (!mPubKey) { mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; return; } - nsRefPtr publicAlgorithm = publicKey->Algorithm(); - CHECK_KEY_ALGORITHM(publicAlgorithm, WEBCRYPTO_ALG_ECDH); + CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH); // Both keys must use the same named curve. - nsString curve1, curve2; - static_cast(aKey.Algorithm())->GetNamedCurve(curve1); - static_cast(publicAlgorithm.get())->GetNamedCurve(curve2); + nsString curve1 = aKey.Algorithm().mEc.mNamedCurve; + nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve; if (!curve1.Equals(curve2)) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; @@ -2645,35 +2464,48 @@ private: // Task creation methods for WebCryptoTask +// Note: We do not perform algorithm normalization as a monolithic process, +// as described in the spec. Instead: +// * Each method handles its slice of the supportedAlgorithms structure +// * Task constructors take care of: +// * Coercing the algorithm to the proper concrete type +// * Cloning subordinate data items +// * Cloning input data as needed +// +// Thus, support for different algorithms is determined by the if-statements +// below, rather than a data structure. +// +// This results in algorithm normalization coming after some other checks, +// and thus slightly more steps being done synchronously than the spec calls +// for. But none of these steps is especially time-consuming. + WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - const CryptoOperationData& aData, - bool aEncrypt) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + const CryptoOperationData& aData, + bool aEncrypt) { TelemetryMethod method = (aEncrypt)? TM_ENCRYPT : TM_DECRYPT; Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC, aKey.Extractable()); - nsString algName; - nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); - if (NS_FAILED(rv)) { - return new FailureTask(rv); - } - // Ensure key is usable for this operation if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) || (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) { return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } + nsString algName; + nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); + if (NS_FAILED(rv)) { + return new FailureTask(rv); + } + if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt); - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - return new RsaesPkcs1Task(aCx, aAlgorithm, aKey, aData, aEncrypt); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt); } @@ -2683,28 +2515,28 @@ WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - const CryptoOperationData& aSignature, - const CryptoOperationData& aData, - bool aSign) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + const CryptoOperationData& aSignature, + const CryptoOperationData& aData, + bool aSign) { TelemetryMethod method = (aSign)? TM_SIGN : TM_VERIFY; Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG, aKey.Extractable()); - nsString algName; - nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); - if (NS_FAILED(rv)) { - return new FailureTask(rv); - } - // Ensure key is usable for this operation if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) || (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) { return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } + nsString algName; + nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); + if (NS_FAILED(rv)) { + return new FailureTask(rv); + } + if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { @@ -2716,23 +2548,10 @@ WebCryptoTask::CreateSignVerifyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDigestTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - const CryptoOperationData& aData) + const ObjectOrString& aAlgorithm, + const CryptoOperationData& aData) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST); - return new DigestTask(aCx, aAlgorithm, aData); -} - -WebCryptoTask* -WebCryptoTask::CreateImportKeyTask(JSContext* aCx, - const nsAString& aFormat, - JS::Handle aKeyData, - const ObjectOrString& aAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) -{ - Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY); - Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable); nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); @@ -2740,6 +2559,48 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, return new FailureTask(rv); } + if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) || + algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) || + algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) || + algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { + return new DigestTask(aCx, aAlgorithm, aData); + } + + return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); +} + +WebCryptoTask* +WebCryptoTask::CreateImportKeyTask(JSContext* aCx, + const nsAString& aFormat, + JS::Handle aKeyData, + const ObjectOrString& aAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) +{ + Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY); + Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable); + + // Verify that the format is recognized + if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + + // Verify that aKeyUsages does not contain an unrecognized value + if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + + nsString algName; + nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); + if (NS_FAILED(rv)) { + return new FailureTask(rv); + } + + // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation. + // However, the spec should be updated to allow it. if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || @@ -2748,8 +2609,7 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || - algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); @@ -2763,22 +2623,58 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat, - CryptoKey& aKey) + CryptoKey& aKey) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY); - return new ExportKeyTask(aFormat, aKey); + // Verify that the format is recognized + if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + + // Verify that the key is extractable + if (!aKey.Extractable()) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + + // Verify that the algorithm supports export + // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation. + // However, the spec should be updated to allow it. + nsString algName = aKey.Algorithm().mName; + if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || + algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || + algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || + algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) || + algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) || + algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) || + algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || + algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) || + algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { + return new ExportKeyTask(aFormat, aKey); + } + + return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); } WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) + const ObjectOrString& aAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE, aExtractable); + // Verify that aKeyUsages does not contain an unrecognized value + // SPEC-BUG: Spec says that this should be InvalidAccessError, but that + // is inconsistent with other analogous points in the spec + if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2791,8 +2687,7 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) || algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) { return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages); - } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) || - algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) || algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) { return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages); @@ -2803,14 +2698,24 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aBaseKey, - const ObjectOrString& aDerivedKeyType, - bool aExtractable, - const Sequence& aKeyUsages) + const ObjectOrString& aAlgorithm, + CryptoKey& aBaseKey, + const ObjectOrString& aDerivedKeyType, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY); + // Ensure baseKey is usable for this operation + if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + + // Verify that aKeyUsages does not contain an unrecognized value + if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2834,12 +2739,17 @@ WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - uint32_t aLength) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + uint32_t aLength) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS); + // Ensure baseKey is usable for this operation + if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2859,18 +2769,31 @@ WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, - const nsAString& aFormat, - CryptoKey& aKey, - CryptoKey& aWrappingKey, - const ObjectOrString& aWrapAlgorithm) + const nsAString& aFormat, + CryptoKey& aKey, + CryptoKey& aWrappingKey, + const ObjectOrString& aWrapAlgorithm) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY); - // Ensure key is usable for this operation + // Verify that the format is recognized + if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && + !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + + // Ensure wrappingKey is usable for this operation if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) { return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } + // Ensure key is extractable + if (!aKey.Extractable()) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + nsString wrapAlgName; nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName); if (NS_FAILED(rv)) { @@ -2885,9 +2808,6 @@ WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { return new WrapKeyTask(aCx, aFormat, aKey, aWrappingKey, aWrapAlgorithm); - } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - return new WrapKeyTask(aCx, aFormat, aKey, - aWrappingKey, aWrapAlgorithm); } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new WrapKeyTask(aCx, aFormat, aKey, aWrappingKey, aWrapAlgorithm); @@ -2898,13 +2818,13 @@ WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, - const nsAString& aFormat, - const ArrayBufferViewOrArrayBuffer& aWrappedKey, - CryptoKey& aUnwrappingKey, - const ObjectOrString& aUnwrapAlgorithm, - const ObjectOrString& aUnwrappedKeyAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) + const nsAString& aFormat, + const ArrayBufferViewOrArrayBuffer& aWrappedKey, + CryptoKey& aUnwrappingKey, + const ObjectOrString& aUnwrapAlgorithm, + const ObjectOrString& aUnwrappedKeyAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY); @@ -2913,6 +2833,11 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } + // Verify that aKeyUsages does not contain an unrecognized value + if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { + return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); + } + nsString keyAlgName; nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName); if (NS_FAILED(rv)) { @@ -2928,8 +2853,7 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, importTask = new ImportSymmetricKeyTask(aCx, aFormat, aUnwrappedKeyAlgorithm, aExtractable, aKeyUsages); - } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) || - keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) { importTask = new ImportRsaKeyTask(aCx, aFormat, aUnwrappedKeyAlgorithm, @@ -2953,10 +2877,6 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, return new UnwrapKeyTask(aCx, aWrappedKey, aUnwrappingKey, aUnwrapAlgorithm, importTask); - } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { - return new UnwrapKeyTask(aCx, aWrappedKey, - aUnwrappingKey, aUnwrapAlgorithm, - importTask); } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new UnwrapKeyTask(aCx, aWrappedKey, aUnwrappingKey, aUnwrapAlgorithm, diff --git a/dom/crypto/moz.build b/dom/crypto/moz.build index 08c7227f17d..b4f1fc3180a 100644 --- a/dom/crypto/moz.build +++ b/dom/crypto/moz.build @@ -5,30 +5,17 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - 'AesKeyAlgorithm.h', - 'BasicSymmetricKeyAlgorithm.h', 'CryptoBuffer.h', 'CryptoKey.h', - 'CryptoKeyPair.h', - 'EcKeyAlgorithm.h', - 'HmacKeyAlgorithm.h', - 'KeyAlgorithm.h', - 'RsaHashedKeyAlgorithm.h', - 'RsaKeyAlgorithm.h', + 'KeyAlgorithmProxy.h', 'WebCryptoCommon.h', 'WebCryptoTask.h', ] UNIFIED_SOURCES += [ - 'AesKeyAlgorithm.cpp', 'CryptoBuffer.cpp', 'CryptoKey.cpp', - 'CryptoKeyPair.cpp', - 'EcKeyAlgorithm.cpp', - 'HmacKeyAlgorithm.cpp', - 'KeyAlgorithm.cpp', - 'RsaHashedKeyAlgorithm.cpp', - 'RsaKeyAlgorithm.cpp', + 'KeyAlgorithmProxy.cpp', 'WebCryptoTask.cpp', ] diff --git a/dom/crypto/test/test_WebCrypto.html b/dom/crypto/test/test_WebCrypto.html index 2f868e8a8e3..9768a3a73bb 100644 --- a/dom/crypto/test/test_WebCrypto.html +++ b/dom/crypto/test/test_WebCrypto.html @@ -71,7 +71,6 @@ TestArray.addTest( function doExport(x) { if (!hasKeyFields(x)) { - window.result = x; throw "Invalid key; missing field(s)"; } else if ((x.algorithm.name != alg) || (x.algorithm.length != 8 * tv.raw.length) || @@ -85,7 +84,7 @@ TestArray.addTest( } crypto.subtle.importKey("raw", tv.raw, alg, true, ["encrypt"]) - .then(doExport, error(that)) + .then(doExport) .then( memcmp_complete(that, tv.raw), error(that) @@ -153,7 +152,7 @@ TestArray.addTest( } crypto.subtle.importKey("pkcs8", tv.pkcs8, alg, true, ["sign"]) - .then(doExport, error(that)) + .then(doExport) .then( memcmp_complete(that, tv.pkcs8), error(that) @@ -178,24 +177,27 @@ TestArray.addTest( "Import / export round-trip with 'spki'", function() { var that = this; - var alg = "RSAES-PKCS1-v1_5"; + var alg = { + name: "RSASSA-PKCS1-v1_5", + hash: "SHA-256" + }; function doExport(x) { if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; - } else if ((x.algorithm.name != alg) || + } else if ((x.algorithm.name != alg.name) || (x.algorithm.modulusLength != 1024) || (x.algorithm.publicExponent.byteLength != 3) || (x.type != "public") || (!x.extractable) || (x.usages.length != 1) || - (x.usages[0] != 'encrypt')){ + (x.usages[0] != 'verify')){ throw "Invalid key: incorrect key data"; } return crypto.subtle.exportKey("spki", x); } - crypto.subtle.importKey("spki", tv.spki, alg, true, ["encrypt"]) + crypto.subtle.importKey("spki", tv.spki, alg, true, ["verify"]) .then(doExport, error(that)) .then( memcmp_complete(that, tv.spki), @@ -209,7 +211,10 @@ TestArray.addTest( "Import failure with format 'spki'", function() { var that = this; - var alg = "RSAES-PKCS1-v1_5"; + var alg = { + name: "RSASSA-PKCS1-v1_5", + hash: "SHA-256" + }; crypto.subtle.importKey("spki", tv.negative_spki, alg, true, ["encrypt"]) .then(error(that), complete(that)); @@ -382,11 +387,12 @@ TestArray.addTest( function() { var that = this; var alg = { - name: "RSAES-PKCS1-v1_5", + name: "RSASSA-PKCS1-v1_5", + hash: "SHA-256", modulusLength: 1024, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then( + crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then( complete(that, function(x) { return exists(x.publicKey) && (x.publicKey.algorithm.name == alg.name) && @@ -394,14 +400,14 @@ TestArray.addTest( (x.publicKey.type == "public") && x.publicKey.extractable && (x.publicKey.usages.length == 1) && - (x.publicKey.usages[0] == "encrypt") && + (x.publicKey.usages[0] == "verify") && exists(x.privateKey) && (x.privateKey.algorithm.name == alg.name) && (x.privateKey.algorithm.modulusLength == alg.modulusLength) && (x.privateKey.type == "private") && !x.privateKey.extractable && (x.privateKey.usages.length == 1) && - (x.privateKey.usages[0] == "decrypt"); + (x.privateKey.usages[0] == "sign"); }), error(that) ); @@ -414,12 +420,13 @@ TestArray.addTest( function() { var that = this; var alg = { - name: "RSAES-PKCS1-v1_5", + name: "RSASSA-PKCS1-v1_5", + hash: "SHA-256", modulusLength: 2299, // NSS does not like this key length publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - crypto.subtle.generateKey(alg, false, ["encrypt"]) + crypto.subtle.generateKey(alg, false, ["sign"]) .then( error(that), complete(that) ); } ); @@ -455,7 +462,6 @@ TestArray.addTest( var that = this; function doEncrypt(x) { - console.log(x); return crypto.subtle.encrypt( { name: "AES-CBC", iv: tv.aes_cbc_enc.iv }, x, tv.aes_cbc_enc.data); @@ -477,7 +483,6 @@ TestArray.addTest( var that = this; function encrypt(x, iv) { - console.log(x); return crypto.subtle.encrypt( { name: "AES-CBC", iv: iv }, x, tv.aes_cbc_enc.data); @@ -822,58 +827,6 @@ TestArray.addTest( } ); -// ----------------------------------------------------------------------------- -TestArray.addTest( - "RSAES-PKCS#1 encrypt/decrypt round-trip", - function () { - var that = this; - var privKey, pubKey; - var alg = {name:"RSAES-PKCS1-v1_5"}; - - var privKey, pubKey, data, ct, pt; - function setPriv(x) { privKey = x; } - function setPub(x) { pubKey = x; } - function doEncrypt() { - return crypto.subtle.encrypt(alg.name, pubKey, tv.rsaes.data); - } - function doDecrypt(x) { - return crypto.subtle.decrypt(alg.name, privKey, x); - } - - function fail() { error(that); } - - Promise.all([ - crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt']) - .then(setPriv, error(that)), - crypto.subtle.importKey("spki", tv.rsaes.spki, alg, false, ['encrypt']) - .then(setPub, error(that)) - ]).then(doEncrypt, error(that)) - .then(doDecrypt, error(that)) - .then( - memcmp_complete(that, tv.rsaes.data), - error(that) - ); - } -); - -// ----------------------------------------------------------------------------- -TestArray.addTest( - "RSAES-PKCS#1 decryption known answer", - function () { - var that = this; - var alg = {name:"RSAES-PKCS1-v1_5"}; - - function doDecrypt(x) { - return crypto.subtle.decrypt(alg.name, x, tv.rsaes.result); - } - function fail() { error(that); } - - crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt']) - .then( doDecrypt, fail ) - .then( memcmp_complete(that, tv.rsaes.data), fail ); - } -); - // ----------------------------------------------------------------------------- TestArray.addTest( "RSASSA/SHA-1 signature", @@ -882,15 +835,12 @@ TestArray.addTest( var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" }; function doSign(x) { - console.log("sign"); - console.log(x); return crypto.subtle.sign(alg.name, x, tv.rsassa.data); } - function fail() { console.log("fail"); error(that); } crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign']) - .then( doSign, fail ) - .then( memcmp_complete(that, tv.rsassa.sig1), fail ); + .then( doSign ) + .then( memcmp_complete(that, tv.rsassa.sig1), error(that) ); } ); @@ -904,13 +854,12 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig1, tv.rsassa.data); } - function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify, fail ) + .then( doVerify ) .then( complete(that, function(x) { return x; }), - fail + error(that) ); } ); @@ -925,13 +874,12 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data); } - function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify, fail ) + .then( doVerify ) .then( complete(that, function(x) { return !x; }), - fail + error(that) ); } ); @@ -946,11 +894,10 @@ TestArray.addTest( function doSign(x) { return crypto.subtle.sign(alg.name, x, tv.rsassa.data); } - function fail(x) { console.log(x); error(that); } crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign']) - .then( doSign, fail ) - .then( memcmp_complete(that, tv.rsassa.sig256), fail ); + .then( doSign ) + .then( memcmp_complete(that, tv.rsassa.sig256), error(that) ); } ); @@ -964,13 +911,12 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig256, tv.rsassa.data); } - function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify, fail ) + .then( doVerify ) .then( complete(that, function(x) { return x; }), - fail + error(that) ); } ); @@ -984,17 +930,14 @@ TestArray.addTest( var use = ['sign', 'verify']; function doVerify(x) { - console.log("verifying") return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data); } - function fail(x) { console.log("failing"); error(that)(x); } - console.log("running") crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify, fail ) + .then( doVerify ) .then( complete(that, function(x) { return !x; }), - fail + error(that) ); } ); @@ -1175,20 +1118,18 @@ TestArray.addTest( true, ['sign', 'verify']); } - function temperr(x) { return function(y) { console.log("error in "+x); console.log(y); } } - Promise.all([ crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk, "AES-GCM", false, ['wrapKey','unwrapKey']) - .then(function(x) { console.log("wrapKey"); wrapKey = x; }), + .then(function(x) { wrapKey = x; }), crypto.subtle.generateKey(genAlg, true, ['sign', 'verify']) - .then(function(x) { console.log("originalKey"); originalKey = x; return x; }) + .then(function(x) { originalKey = x; return x; }) .then(doExport) .then(function(x) { originalKeyJwk = x; }) ]) - .then(doWrap, temperr("initial phase")) - .then(doUnwrap, temperr("wrap")) - .then(doExport, temperr("unwrap")) + .then(doWrap) + .then(doUnwrap) + .then(doExport) .then( complete(that, function(x) { return exists(x.k) && x.k == originalKeyJwk.k; @@ -1269,9 +1210,9 @@ TestArray.addTest( Promise.all([ crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key, "AES-KW", false, ['wrapKey','unwrapKey']) - .then(function(x) { console.log("wrapKey"); wrapKey = x; }), + .then(function(x) { wrapKey = x; }), crypto.subtle.generateKey(genAlg, true, ['sign']) - .then(function(x) { console.log("originalKey"); originalKey = x; return x; }) + .then(function(x) { originalKey = x; return x; }) .then(doExport) .then(function(x) { originalKeyJwk = x; }) ]) @@ -1332,7 +1273,7 @@ TestArray.addTest( modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - return crypto.subtle.generateKey(alg, false, ["encrypt"]); + return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]); } function doGenerateRsaSsaPkcs1Key() { @@ -1364,24 +1305,7 @@ TestArray.addTest( return crypto.subtle.generateKey(alg, false, ["sign"]).then(doSign); } - function doCheckRSAES() { - var alg = { - name: "RSAES-PKCS1-v1_5", - modulusLength: 1024, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]) - }; - - function doEncrypt(x) { - var alg = {name: "RSA-OAEP", hash: "SHA-1"}; - return crypto.subtle.encrypt(alg, x.publicKey, new Uint8Array()); - } - - return crypto.subtle.generateKey(alg, false, ["encrypt"]).then(doEncrypt); - } - - doCheckRSASSA().then(error(that), function () { - doCheckRSAES().then(error(that), complete(that)); - }); + doCheckRSASSA().then(error(that), complete(that)); } ); /*]]>*/ diff --git a/dom/crypto/test/test_WebCrypto_ECDH.html b/dom/crypto/test/test_WebCrypto_ECDH.html index 12793263ecd..04dc0c00e1d 100644 --- a/dom/crypto/test/test_WebCrypto_ECDH.html +++ b/dom/crypto/test/test_WebCrypto_ECDH.html @@ -103,7 +103,7 @@ TestArray.addTest( modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - return crypto.subtle.generateKey(alg, false, ["encrypt"]) + return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]) } function doDerive() { @@ -336,12 +336,12 @@ TestArray.addTest( } Promise.all([ - crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"]) - .then(setPriv, error(that)), - crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"]) - .then(setPub, error(that)) - ]).then(doDerive, error(that)) - .then(doSignAndVerify, error(that)) + crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveKey"]) + .then(setPriv), + crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveKey"]) + .then(setPub) + ]).then(doDerive) + .then(doSignAndVerify) .then(complete(that), error(that)); } ); diff --git a/dom/crypto/test/test_WebCrypto_JWK.html b/dom/crypto/test/test_WebCrypto_JWK.html index 710c04a6844..39b41d85d00 100644 --- a/dom/crypto/test/test_WebCrypto_JWK.html +++ b/dom/crypto/test/test_WebCrypto_JWK.html @@ -192,8 +192,6 @@ TestArray.addTest( .then(doExport) .then( complete(that, function(x) { - window.jwk_priv = x; - console.log(JSON.stringify(x)); return hasBaseJwkFields(x) && hasFields(x, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']) && x.kty == 'RSA' && diff --git a/dom/crypto/test/test_WebCrypto_PBKDF2.html b/dom/crypto/test/test_WebCrypto_PBKDF2.html index 21dd1e86f31..f1cf66c7bf3 100644 --- a/dom/crypto/test/test_WebCrypto_PBKDF2.html +++ b/dom/crypto/test/test_WebCrypto_PBKDF2.html @@ -46,7 +46,6 @@ TestArray.addTest( var key = tv.pbkdf2_sha1.password; function doDerive(x) { - console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } @@ -61,7 +60,7 @@ TestArray.addTest( } function fail(x) { console.log("failing"); error(that)(x); } - crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]) + crypto.subtle.importKey("raw", key, alg, false, ["deriveBits"]) .then( doDerive, fail ) .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail ); } @@ -76,7 +75,6 @@ TestArray.addTest( var key = tv.pbkdf2_sha1.password; function doDerive(x) { - console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } @@ -161,7 +159,6 @@ TestArray.addTest( var key = tv.pbkdf2_sha256.password; function doDerive(x) { - console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } diff --git a/dom/crypto/test/test_WebCrypto_RSA_OAEP.html b/dom/crypto/test/test_WebCrypto_RSA_OAEP.html index 0d2c2ff7be3..082e8c0a604 100644 --- a/dom/crypto/test/test_WebCrypto_RSA_OAEP.html +++ b/dom/crypto/test/test_WebCrypto_RSA_OAEP.html @@ -120,12 +120,13 @@ TestArray.addTest( var privKey, pubKey; function setKey(x) { pubKey = x.publicKey; privKey = x.privateKey; } function doEncrypt(n) { + console.log("entered encrypt("+ n +")"); return function () { return crypto.subtle.encrypt(alg, pubKey, new Uint8Array(n)); } } - crypto.subtle.generateKey(alg, false, ['encrypt']) + crypto.subtle.generateKey(alg, false, ['encrypt', 'decrypt']) .then(setKey, error(that)) .then(doEncrypt(214), error(that)) .then(doEncrypt(215), error(that)) diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index f57aff30021..c2c9495d7ef 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -244,8 +244,6 @@ var interfaceNamesInGlobalScope = "Crypto", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "CryptoKey", pref: "dom.webcrypto.enabled"}, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "CryptoKeyPair", pref: "dom.webcrypto.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! "CSS", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/KeyAlgorithm.webidl b/dom/webidl/KeyAlgorithm.webidl new file mode 100644 index 00000000000..ca6706fbbec --- /dev/null +++ b/dom/webidl/KeyAlgorithm.webidl @@ -0,0 +1,32 @@ +/* -*- Mode: IDL; 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/. + * + * The origin of this IDL file is + * http://www.w3.org/TR/WebCryptoAPI/ + */ + +dictionary KeyAlgorithm { + required DOMString name; +}; + +dictionary AesKeyAlgorithm : KeyAlgorithm { + required unsigned short length; +}; + +dictionary EcKeyAlgorithm : KeyAlgorithm { + required DOMString namedCurve; +}; + +dictionary HmacKeyAlgorithm : KeyAlgorithm { + required KeyAlgorithm hash; + required unsigned long length; +}; + +dictionary RsaHashedKeyAlgorithm : KeyAlgorithm { + required unsigned short modulusLength; + required Uint8Array publicExponent; + required KeyAlgorithm hash; +}; + diff --git a/dom/webidl/SubtleCrypto.webidl b/dom/webidl/SubtleCrypto.webidl index 4e07329f6ea..a4e1b07950c 100644 --- a/dom/webidl/SubtleCrypto.webidl +++ b/dom/webidl/SubtleCrypto.webidl @@ -9,113 +9,74 @@ typedef DOMString KeyType; typedef DOMString KeyUsage; +typedef DOMString NamedCurve; typedef Uint8Array BigInteger; -/***** KeyAlgorithm interfaces *****/ - -[NoInterfaceObject] -interface KeyAlgorithm { - readonly attribute DOMString name; -}; - -[NoInterfaceObject] -interface AesKeyAlgorithm : KeyAlgorithm { - readonly attribute unsigned short length; -}; - -[NoInterfaceObject] -interface HmacKeyAlgorithm : KeyAlgorithm { - readonly attribute KeyAlgorithm hash; - readonly attribute unsigned long length; -}; - -[NoInterfaceObject] -interface RsaKeyAlgorithm : KeyAlgorithm { - readonly attribute unsigned long modulusLength; - [Throws] - readonly attribute BigInteger publicExponent; -}; - -[NoInterfaceObject] -interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm { - readonly attribute KeyAlgorithm hash; -}; - -[NoInterfaceObject] -interface EcKeyAlgorithm : KeyAlgorithm { - readonly attribute NamedCurve namedCurve; -}; - - /***** Algorithm dictionaries *****/ dictionary Algorithm { - DOMString name; + required DOMString name; }; dictionary AesCbcParams : Algorithm { - CryptoOperationData iv; + required CryptoOperationData iv; }; dictionary AesCtrParams : Algorithm { - CryptoOperationData counter; - [EnforceRange] octet length; + required CryptoOperationData counter; + [EnforceRange] required octet length; }; dictionary AesGcmParams : Algorithm { - CryptoOperationData iv; + required CryptoOperationData iv; CryptoOperationData additionalData; [EnforceRange] octet tagLength; }; dictionary HmacImportParams : Algorithm { - AlgorithmIdentifier hash; + required AlgorithmIdentifier hash; }; dictionary Pbkdf2Params : Algorithm { - CryptoOperationData salt; - [EnforceRange] unsigned long iterations; - AlgorithmIdentifier hash; + required CryptoOperationData salt; + [EnforceRange] required unsigned long iterations; + required AlgorithmIdentifier hash; }; dictionary RsaHashedImportParams { - AlgorithmIdentifier hash; + required AlgorithmIdentifier hash; }; dictionary AesKeyGenParams : Algorithm { - [EnforceRange] unsigned short length; + [EnforceRange] required unsigned short length; }; dictionary HmacKeyGenParams : Algorithm { - AlgorithmIdentifier hash; + required AlgorithmIdentifier hash; [EnforceRange] unsigned long length; }; -dictionary RsaKeyGenParams : Algorithm { - [EnforceRange] unsigned long modulusLength; - BigInteger publicExponent; -}; - -dictionary RsaHashedKeyGenParams : RsaKeyGenParams { - AlgorithmIdentifier hash; +dictionary RsaHashedKeyGenParams : Algorithm { + [EnforceRange] required unsigned long modulusLength; + required BigInteger publicExponent; + required AlgorithmIdentifier hash; }; dictionary RsaOaepParams : Algorithm { - CryptoOperationData? label; + CryptoOperationData label; }; dictionary DhKeyGenParams : Algorithm { - BigInteger prime; - BigInteger generator; + required BigInteger prime; + required BigInteger generator; }; -typedef DOMString NamedCurve; dictionary EcKeyGenParams : Algorithm { - NamedCurve namedCurve; + required NamedCurve namedCurve; }; dictionary AesDerivedKeyParams : Algorithm { - [EnforceRange] unsigned long length; + [EnforceRange] required unsigned long length; }; dictionary HmacDerivedKeyParams : HmacImportParams { @@ -123,7 +84,7 @@ dictionary HmacDerivedKeyParams : HmacImportParams { }; dictionary EcdhKeyDeriveParams : Algorithm { - CryptoKey public; + required CryptoKey public; }; @@ -131,14 +92,14 @@ dictionary EcdhKeyDeriveParams : Algorithm { dictionary RsaOtherPrimesInfo { // The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms - DOMString r; - DOMString d; - DOMString t; + required DOMString r; + required DOMString d; + required DOMString t; }; dictionary JsonWebKey { // The following fields are defined in Section 3.1 of JSON Web Key - DOMString kty; + required DOMString kty; DOMString use; sequence key_ops; DOMString alg; @@ -169,14 +130,13 @@ dictionary JsonWebKey { interface CryptoKey { readonly attribute KeyType type; readonly attribute boolean extractable; - readonly attribute KeyAlgorithm algorithm; + [Cached, Constant, Throws] readonly attribute object algorithm; [Cached, Constant, Frozen] readonly attribute sequence usages; }; -[Pref="dom.webcrypto.enabled"] -interface CryptoKeyPair { - readonly attribute CryptoKey publicKey; - readonly attribute CryptoKey privateKey; +dictionary CryptoKeyPair { + required CryptoKey publicKey; + required CryptoKey privateKey; }; typedef DOMString KeyFormat; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index b603b4f040d..cb77cd1d01c 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -238,6 +238,7 @@ WEBIDL_FILES = [ 'InterAppConnection.webidl', 'InterAppConnectionRequest.webidl', 'InterAppMessagePort.webidl', + 'KeyAlgorithm.webidl', 'KeyboardEvent.webidl', 'KeyEvent.webidl', 'LegacyQueryInterface.webidl', From f6d15ee24bd6b90369a1cfd80fe3c702fd26f4fb Mon Sep 17 00:00:00 2001 From: William Chen Date: Fri, 26 Sep 2014 15:07:40 -0700 Subject: [PATCH 23/85] Bug 1062578 - Call BindToTree/UnbindFromTree on shadow root children when host is bound/unbound from tree. r=smaug --- content/base/src/Element.cpp | 41 +++++++++++-- content/base/src/FragmentOrElement.cpp | 4 ++ content/base/src/nsGenericDOMDataNode.cpp | 13 ++-- content/base/src/nsINode.cpp | 10 ++- content/base/src/nsStyleLinkElement.cpp | 6 +- .../html/content/src/HTMLContentElement.cpp | 27 ++++---- .../html/content/src/HTMLShadowElement.cpp | 61 +++++++++++++------ content/html/content/src/HTMLStyleElement.cpp | 12 +++- .../mochitest/webcomponents/mochitest.ini | 1 + .../webcomponents/test_detached_style.html | 25 ++++++++ dom/xbl/nsXBLBinding.cpp | 18 +++--- dom/xbl/nsXBLBinding.h | 1 + .../meta/shadow-dom/styles/test-003.html.ini | 8 --- 13 files changed, 165 insertions(+), 62 deletions(-) create mode 100644 dom/tests/mochitest/webcomponents/test_detached_style.html delete mode 100644 testing/web-platform/meta/shadow-dom/styles/test-003.html.ini diff --git a/content/base/src/Element.cpp b/content/base/src/Element.cpp index 19761e5c9b6..6b7ba996550 100644 --- a/content/base/src/Element.cpp +++ b/content/base/src/Element.cpp @@ -834,6 +834,13 @@ Element::CreateShadowRoot(ErrorResult& aError) SetShadowRoot(shadowRoot); if (olderShadow) { olderShadow->SetYoungerShadow(shadowRoot); + + // Unbind children of older shadow root because they + // are no longer in the composed tree. + for (nsIContent* child = olderShadow->GetFirstChild(); child; + child = child->GetNextSibling()) { + child->UnbindFromTree(true, false); + } } // xblBinding takes ownership of docInfo. @@ -1384,6 +1391,18 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } } + // Call BindToTree on shadow root children. + ShadowRoot* shadowRoot = GetShadowRoot(); + if (shadowRoot) { + for (nsIContent* child = shadowRoot->GetFirstChild(); child; + child = child->GetNextSibling()) { + rv = child->BindToTree(nullptr, shadowRoot, + shadowRoot->GetBindingParent(), + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + } + } + // XXXbz script execution during binding can trigger some of these // postcondition asserts.... But we do want that, since things will // generally be quite broken when that happens. @@ -1462,10 +1481,13 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) SetParentIsContent(false); } ClearInDocument(); - UnsetFlags(NODE_IS_IN_SHADOW_TREE); - // Begin keeping track of our subtree root. - SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); + if (aNullParent || !mParent->IsInShadowTree()) { + UnsetFlags(NODE_IS_IN_SHADOW_TREE); + + // Begin keeping track of our subtree root. + SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); + } if (document) { // Notify XBL- & nsIAnonymousContentCreator-generated @@ -1514,7 +1536,9 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) if (clearBindingParent) { slots->mBindingParent = nullptr; } - slots->mContainingShadow = nullptr; + if (aNullParent || !mParent->IsInShadowTree()) { + slots->mContainingShadow = nullptr; + } } // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, @@ -1539,6 +1563,15 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent) } nsNodeUtils::ParentChainChanged(this); + + // Unbind children of shadow root. + ShadowRoot* shadowRoot = GetShadowRoot(); + if (shadowRoot) { + for (nsIContent* child = shadowRoot->GetFirstChild(); child; + child = child->GetNextSibling()) { + child->UnbindFromTree(true, false); + } + } } nsICSSDeclaration* diff --git a/content/base/src/FragmentOrElement.cpp b/content/base/src/FragmentOrElement.cpp index 8eb3d557595..b937c32286c 100644 --- a/content/base/src/FragmentOrElement.cpp +++ b/content/base/src/FragmentOrElement.cpp @@ -1393,6 +1393,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement) unbind the child nodes. } */ + // Clear flag here because unlinking slots will clear the + // containing shadow root pointer. + tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE); + // Unlink any DOM slots of interest. { nsDOMSlots *slots = tmp->GetExistingDOMSlots(); diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index 9d087d7b24b..4c7680bb389 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -582,15 +582,20 @@ nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent) SetParentIsContent(false); } ClearInDocument(); - UnsetFlags(NODE_IS_IN_SHADOW_TREE); - // Begin keeping track of our subtree root. - SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); + if (aNullParent || !mParent->IsInShadowTree()) { + UnsetFlags(NODE_IS_IN_SHADOW_TREE); + + // Begin keeping track of our subtree root. + SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); + } nsDataSlots *slots = GetExistingDataSlots(); if (slots) { slots->mBindingParent = nullptr; - slots->mContainingShadow = nullptr; + if (aNullParent || !mParent->IsInShadowTree()) { + slots->mContainingShadow = nullptr; + } } nsNodeUtils::ParentChainChanged(this); diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index 20f95ea6c56..bcdc6a1a148 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -392,7 +392,15 @@ nsINode::GetComposedDocInternal() const // Cross ShadowRoot boundary. ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); - return containingShadow->GetHost()->GetCrossShadowCurrentDoc(); + + nsIContent* poolHost = containingShadow->GetPoolHost(); + if (!poolHost) { + // This node is in an older shadow root that does not get projected into + // an insertion point, thus this node can not be in the composed document. + return nullptr; + } + + return poolHost->GetComposedDoc(); } #ifdef DEBUG diff --git a/content/base/src/nsStyleLinkElement.cpp b/content/base/src/nsStyleLinkElement.cpp index a0db9527e30..febbf1bf421 100644 --- a/content/base/src/nsStyleLinkElement.cpp +++ b/content/base/src/nsStyleLinkElement.cpp @@ -208,7 +208,8 @@ nsStyleLinkElement::UpdateStyleSheet(nsICSSLoaderObserver* aObserver, // We remove this stylesheet from the cache to load a new version. nsCOMPtr thisContent; CallQueryInterface(this, getter_AddRefs(thisContent)); - nsIDocument* doc = thisContent->GetCrossShadowCurrentDoc(); + nsCOMPtr doc = thisContent->IsInShadowTree() ? + thisContent->OwnerDoc() : thisContent->GetUncomposedDoc(); if (doc && doc->CSSLoader()->GetEnabled() && mStyleSheet && mStyleSheet->GetOriginalURI()) { doc->CSSLoader()->ObsoleteSheet(mStyleSheet->GetOriginalURI()); @@ -344,7 +345,8 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument, return NS_OK; } - nsCOMPtr doc = thisContent->GetCrossShadowCurrentDoc(); + nsCOMPtr doc = thisContent->IsInShadowTree() ? + thisContent->OwnerDoc() : thisContent->GetUncomposedDoc(); if (!doc || !doc->CSSLoader()->GetEnabled()) { return NS_OK; } diff --git a/content/html/content/src/HTMLContentElement.cpp b/content/html/content/src/HTMLContentElement.cpp index a797def6100..c09fa64c518 100644 --- a/content/html/content/src/HTMLContentElement.cpp +++ b/content/html/content/src/HTMLContentElement.cpp @@ -55,13 +55,15 @@ HTMLContentElement::BindToTree(nsIDocument* aDocument, nsIContent* aBindingParent, bool aCompileEventHandlers) { + nsRefPtr oldContainingShadow = GetContainingShadow(); + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow) { + if (containingShadow && !oldContainingShadow) { nsINode* parentNode = nsINode::GetParentNode(); while (parentNode && parentNode != containingShadow) { if (parentNode->IsElement() && @@ -85,23 +87,20 @@ HTMLContentElement::BindToTree(nsIDocument* aDocument, void HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent) { - if (mIsInsertionPoint) { - ShadowRoot* containingShadow = GetContainingShadow(); - // Make sure that containingShadow exists, it may have been nulled - // during unlinking in which case the ShadowRoot is going away. - if (containingShadow) { - containingShadow->RemoveInsertionPoint(this); + nsRefPtr oldContainingShadow = GetContainingShadow(); - // Remove all the matched nodes now that the - // insertion point is no longer an insertion point. - ClearMatchedNodes(); - containingShadow->SetInsertionPointChanged(); - } + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { + oldContainingShadow->RemoveInsertionPoint(this); + + // Remove all the matched nodes now that the + // insertion point is no longer an insertion point. + ClearMatchedNodes(); + oldContainingShadow->SetInsertionPointChanged(); mIsInsertionPoint = false; } - - nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } void diff --git a/content/html/content/src/HTMLShadowElement.cpp b/content/html/content/src/HTMLShadowElement.cpp index 41c4bae4d51..87f6accc8f5 100644 --- a/content/html/content/src/HTMLShadowElement.cpp +++ b/content/html/content/src/HTMLShadowElement.cpp @@ -111,13 +111,15 @@ HTMLShadowElement::BindToTree(nsIDocument* aDocument, nsIContent* aBindingParent, bool aCompileEventHandlers) { + nsRefPtr oldContainingShadow = GetContainingShadow(); + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow) { + if (containingShadow && !oldContainingShadow) { // Keep track of all descendant elements in tree order so // that when the current shadow insertion point is removed, the next // one can be found quickly. @@ -141,35 +143,58 @@ HTMLShadowElement::BindToTree(nsIDocument* aDocument, containingShadow->SetInsertionPointChanged(); } + if (mIsInsertionPoint && containingShadow) { + // Propagate BindToTree calls to projected shadow root children. + ShadowRoot* projectedShadow = containingShadow->GetOlderShadow(); + if (projectedShadow) { + for (nsIContent* child = projectedShadow->GetFirstChild(); child; + child = child->GetNextSibling()) { + rv = child->BindToTree(nullptr, projectedShadow, + projectedShadow->GetBindingParent(), + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + return NS_OK; } void HTMLShadowElement::UnbindFromTree(bool aDeep, bool aNullParent) { - if (mIsInsertionPoint) { - ShadowRoot* containingShadow = GetContainingShadow(); - // Make sure that containingShadow exists, it may have been nulled - // during unlinking in which case the ShadowRoot is going away. - if (containingShadow) { - nsTArray& shadowDescendants = - containingShadow->ShadowDescendants(); - shadowDescendants.RemoveElement(this); - containingShadow->SetShadowElement(nullptr); + nsRefPtr oldContainingShadow = GetContainingShadow(); - // Find the next shadow insertion point. - if (shadowDescendants.Length() > 0 && - !IsInFallbackContent(shadowDescendants[0])) { - containingShadow->SetShadowElement(shadowDescendants[0]); + if (mIsInsertionPoint && oldContainingShadow) { + // Propagate UnbindFromTree call to previous projected shadow + // root children. + ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadow(); + if (projectedShadow) { + for (nsIContent* child = projectedShadow->GetFirstChild(); child; + child = child->GetNextSibling()) { + child->UnbindFromTree(true, false); } - - containingShadow->SetInsertionPointChanged(); } - - mIsInsertionPoint = false; } nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) { + nsTArray& shadowDescendants = + oldContainingShadow->ShadowDescendants(); + shadowDescendants.RemoveElement(this); + oldContainingShadow->SetShadowElement(nullptr); + + // Find the next shadow insertion point. + if (shadowDescendants.Length() > 0 && + !IsInFallbackContent(shadowDescendants[0])) { + oldContainingShadow->SetShadowElement(shadowDescendants[0]); + } + + oldContainingShadow->SetInsertionPointChanged(); + + mIsInsertionPoint = false; + } } void diff --git a/content/html/content/src/HTMLStyleElement.cpp b/content/html/content/src/HTMLStyleElement.cpp index b3b60cf11e2..27e0bf4ccca 100644 --- a/content/html/content/src/HTMLStyleElement.cpp +++ b/content/html/content/src/HTMLStyleElement.cpp @@ -156,9 +156,19 @@ HTMLStyleElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent) { - nsCOMPtr oldDoc = GetCurrentDoc(); + nsCOMPtr oldDoc = GetUncomposedDoc(); + nsCOMPtr oldComposedDoc = GetComposedDoc(); ShadowRoot* oldShadow = GetContainingShadow(); + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + if (GetContainingShadow() && !oldComposedDoc) { + // The style is in a shadow tree and was already not + // in the composed document. Thus the sheet does not + // need to be updated. + return; + } + UpdateStyleSheetInternal(oldDoc, oldShadow); } diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini index 3c6c9749f2e..503190c9411 100644 --- a/dom/tests/mochitest/webcomponents/mochitest.ini +++ b/dom/tests/mochitest/webcomponents/mochitest.ini @@ -9,6 +9,7 @@ support-files = [test_dest_insertion_points.html] [test_dest_insertion_points_shadow.html] [test_fallback_dest_insertion_points.html] +[test_detached_style.html] [test_dynamic_content_element_matching.html] [test_document_register.html] [test_document_register_base_queue.html] diff --git a/dom/tests/mochitest/webcomponents/test_detached_style.html b/dom/tests/mochitest/webcomponents/test_detached_style.html new file mode 100644 index 00000000000..9c52e2d29e3 --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_detached_style.html @@ -0,0 +1,25 @@ + + + + + Test for creating style in shadow root of host not in document. + + + + +
+Bug 1062578 + + + diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 84da40cef29..bac5f9f5976 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -106,6 +106,7 @@ static const JSClass gPrototypeJSClass = { nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) : mMarkedForDeath(false) , mUsingContentXBLScope(false) + , mIsShadowRootBinding(false) , mPrototypeBinding(aBinding) { NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!"); @@ -117,6 +118,7 @@ nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding) nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding) : mMarkedForDeath(false), mUsingContentXBLScope(false), + mIsShadowRootBinding(true), mPrototypeBinding(aBinding), mContent(aShadowRoot) { @@ -127,7 +129,10 @@ nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBind nsXBLBinding::~nsXBLBinding(void) { - if (mContent) { + if (mContent && !mIsShadowRootBinding) { + // It is unnecessary to uninstall anonymous content in a shadow tree + // because the ShadowRoot itself is a DocumentFragment and does not + // need any additional cleanup. nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent); } nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo(); @@ -139,7 +144,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding) // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because // mPrototypeBinding is weak. - if (tmp->mContent) { + if (tmp->mContent && !tmp->mIsShadowRootBinding) { nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(), tmp->mContent); } @@ -234,13 +239,6 @@ void nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument, nsIContent* aAnonParent) { - if (aAnonParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { - // It is unnecessary to uninstall anonymous content in a shadow tree - // because the ShadowRoot itself is a DocumentFragment and does not - // need any additional cleanup. - return; - } - nsAutoScriptBlocker scriptBlocker; // Hold a strong ref while doing this, just in case. nsCOMPtr anonParent = aAnonParent; @@ -811,7 +809,7 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen // Update the anonymous content. // XXXbz why not only for style bindings? - if (mContent) { + if (mContent && !mIsShadowRootBinding) { nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent); } diff --git a/dom/xbl/nsXBLBinding.h b/dom/xbl/nsXBLBinding.h index 238575cb64a..37ebd42661f 100644 --- a/dom/xbl/nsXBLBinding.h +++ b/dom/xbl/nsXBLBinding.h @@ -162,6 +162,7 @@ protected: bool mMarkedForDeath; bool mUsingContentXBLScope; + bool mIsShadowRootBinding; nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo nsCOMPtr mContent; // Strong. Our anonymous content stays around with us. diff --git a/testing/web-platform/meta/shadow-dom/styles/test-003.html.ini b/testing/web-platform/meta/shadow-dom/styles/test-003.html.ini deleted file mode 100644 index cb38c1de662..00000000000 --- a/testing/web-platform/meta/shadow-dom/styles/test-003.html.ini +++ /dev/null @@ -1,8 +0,0 @@ -[test-003.html] - type: testharness - [A_06_00_03_T04] - expected: FAIL - - [A_06_00_03_T03] - expected: FAIL - From 5df89995a5365e2a494f7725126cb6acb4d52a92 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 26 Sep 2014 15:35:09 -0700 Subject: [PATCH 24/85] Bug 1073320: Document and test the effect of removing debuggees on breakpoints. r=sfink --- js/src/doc/Debugger/Debugger.Script.md | 9 ++- js/src/doc/Debugger/Debugger.md | 3 + js/src/jit-test/tests/debug/breakpoint-12.js | 78 ++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 js/src/jit-test/tests/debug/breakpoint-12.js diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md index 7f0f7537c08..80ac2fd009a 100644 --- a/js/src/doc/Debugger/Debugger.Script.md +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -204,9 +204,12 @@ methods of other kinds of objects. and in the compartment containing the handler function (typically the debugger's compartment). - The new breakpoint belongs to the [`Debugger`][debugger-object] instance to which this - script belongs; disabling the [`Debugger`][debugger-object] instance disables this - breakpoint. + The new breakpoint belongs to the [`Debugger`][debugger-object] instance to + which this script belongs. Disabling the [`Debugger`][debugger-object] + instance disables this breakpoint; and removing a global from the + [`Debugger`][debugger-object] instance's set of debuggees clears all the + breakpoints belonging to that [`Debugger`][debugger-object] instance in that + global's scripts. getBreakpoints([offset]) : Return an array containing the handler objects for all the breakpoints diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md index 0de4972b6ed..029d6ab659e 100644 --- a/js/src/doc/Debugger/Debugger.md +++ b/js/src/doc/Debugger/Debugger.md @@ -280,6 +280,9 @@ other kinds of objects. This method interprets global using the same rules that [`addDebuggee`][add] does. + Removing a global as a debuggee from this `Debugger` clears all breakpoints + that belong to that `Debugger` in that global. + `removeAllDebuggees()` : Remove all the global objects from this `Debugger` instance's set of debuggees. Return `undefined`. diff --git a/js/src/jit-test/tests/debug/breakpoint-12.js b/js/src/jit-test/tests/debug/breakpoint-12.js new file mode 100644 index 00000000000..54f48b7c64e --- /dev/null +++ b/js/src/jit-test/tests/debug/breakpoint-12.js @@ -0,0 +1,78 @@ +// Removing a global as a debuggee forgets all its breakpoints. + + +var dbgA = new Debugger; +var logA = ''; + +var dbgB = new Debugger; +var logB = ''; + +var g1 = newGlobal(); +g1.eval('function g1f() { print("Weltuntergang"); }'); +var DOAg1 = dbgA.addDebuggee(g1); +var DOAg1f = DOAg1.getOwnPropertyDescriptor('g1f').value; +DOAg1f.script.setBreakpoint(0, { hit: () => { logA += '1'; } }); + +var DOBg1 = dbgB.addDebuggee(g1); +var DOBg1f = DOBg1.getOwnPropertyDescriptor('g1f').value; +DOBg1f.script.setBreakpoint(0, { hit: () => { logB += '1'; } }); + + +var g2 = newGlobal(); +g2.eval('function g2f() { print("Mokushi"); }'); + +var DOAg2 = dbgA.addDebuggee(g2); +var DOAg2f = DOAg2.getOwnPropertyDescriptor('g2f').value; +DOAg2f.script.setBreakpoint(0, { hit: () => { logA += '2'; } }); + +var DOBg2 = dbgB.addDebuggee(g2); +var DOBg2f = DOBg2.getOwnPropertyDescriptor('g2f').value; +DOBg2f.script.setBreakpoint(0, { hit: () => { logB += '2'; } }); + +assertEq(logA, ''); +assertEq(logB, ''); +g1.g1f(); +g2.g2f(); +assertEq(logA, '12'); +assertEq(logB, '12'); +logA = logB = ''; + +// Removing a global as a debuggee should make its breakpoint not hit. +dbgA.removeDebuggee(g2); +dbgB.removeDebuggee(g1); +assertEq(logA, ''); +assertEq(logB, ''); +g1.g1f(); +g2.g2f(); +assertEq(logA, '1'); +assertEq(logB, '2'); +logA = logB = ''; + +// Adding the global back as a debuggee should not resurrect its breakpoints. +dbgA.addDebuggee(g2); +dbgB.addDebuggee(g1); +assertEq(logA, ''); +assertEq(logB, ''); +g1.g1f(); +g2.g2f(); +assertEq(logA, '1'); +assertEq(logB, '2'); +logA = logB = ''; + +// But, we can set them again, and it all works. +DOAg2f.script.setBreakpoint(0, { hit: () => { logA += '2'; } }); +assertEq(logA, ''); +assertEq(logB, ''); +g2.g2f(); +g1.g1f(); +assertEq(logA, '21'); +assertEq(logB, '2'); +logA = logB = ''; + +DOBg1f.script.setBreakpoint(0, { hit: () => { logB += '1'; } }); +assertEq(logA, ''); +assertEq(logB, ''); +g2.g2f(); +g1.g1f(); +assertEq(logA, '21'); +assertEq(logB, '21'); From ce80476811adaf649a26eecb0d95bbbd8910f7d7 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 26 Sep 2014 15:35:38 -0700 Subject: [PATCH 25/85] Backed out changeset 398bdeea30b0 (bug 1037892) for build bustage --- dom/crypto/AesKeyAlgorithm.cpp | 82 +++ dom/crypto/AesKeyAlgorithm.h | 38 + dom/crypto/BasicSymmetricKeyAlgorithm.h | 38 + dom/crypto/CryptoBuffer.cpp | 7 - dom/crypto/CryptoBuffer.h | 1 - dom/crypto/CryptoKey.cpp | 116 +-- dom/crypto/CryptoKey.h | 16 +- dom/crypto/CryptoKeyPair.cpp | 31 + dom/crypto/CryptoKeyPair.h | 63 ++ dom/crypto/EcKeyAlgorithm.cpp | 43 ++ dom/crypto/EcKeyAlgorithm.h | 48 ++ dom/crypto/HmacKeyAlgorithm.cpp | 64 ++ dom/crypto/HmacKeyAlgorithm.h | 72 ++ dom/crypto/KeyAlgorithm.cpp | 116 +++ dom/crypto/KeyAlgorithm.h | 80 +++ dom/crypto/KeyAlgorithmProxy.cpp | 215 ------ dom/crypto/KeyAlgorithmProxy.h | 117 ---- dom/crypto/RsaHashedKeyAlgorithm.cpp | 80 +++ dom/crypto/RsaHashedKeyAlgorithm.h | 53 ++ dom/crypto/RsaKeyAlgorithm.cpp | 46 ++ dom/crypto/RsaKeyAlgorithm.h | 63 ++ dom/crypto/WebCryptoCommon.h | 42 +- dom/crypto/WebCryptoTask.cpp | 658 ++++++++++-------- dom/crypto/moz.build | 17 +- dom/crypto/test/test_WebCrypto.html | 160 +++-- dom/crypto/test/test_WebCrypto_ECDH.html | 14 +- dom/crypto/test/test_WebCrypto_JWK.html | 2 + dom/crypto/test/test_WebCrypto_PBKDF2.html | 5 +- dom/crypto/test/test_WebCrypto_RSA_OAEP.html | 3 +- .../mochitest/general/test_interfaces.html | 2 + dom/webidl/KeyAlgorithm.webidl | 32 - dom/webidl/SubtleCrypto.webidl | 102 ++- dom/webidl/moz.build | 1 - 33 files changed, 1551 insertions(+), 876 deletions(-) create mode 100644 dom/crypto/AesKeyAlgorithm.cpp create mode 100644 dom/crypto/AesKeyAlgorithm.h create mode 100644 dom/crypto/BasicSymmetricKeyAlgorithm.h create mode 100644 dom/crypto/CryptoKeyPair.cpp create mode 100644 dom/crypto/CryptoKeyPair.h create mode 100644 dom/crypto/EcKeyAlgorithm.cpp create mode 100644 dom/crypto/EcKeyAlgorithm.h create mode 100644 dom/crypto/HmacKeyAlgorithm.cpp create mode 100644 dom/crypto/HmacKeyAlgorithm.h create mode 100644 dom/crypto/KeyAlgorithm.cpp create mode 100644 dom/crypto/KeyAlgorithm.h delete mode 100644 dom/crypto/KeyAlgorithmProxy.cpp delete mode 100644 dom/crypto/KeyAlgorithmProxy.h create mode 100644 dom/crypto/RsaHashedKeyAlgorithm.cpp create mode 100644 dom/crypto/RsaHashedKeyAlgorithm.h create mode 100644 dom/crypto/RsaKeyAlgorithm.cpp create mode 100644 dom/crypto/RsaKeyAlgorithm.h delete mode 100644 dom/webidl/KeyAlgorithm.webidl diff --git a/dom/crypto/AesKeyAlgorithm.cpp b/dom/crypto/AesKeyAlgorithm.cpp new file mode 100644 index 00000000000..78d0ea99793 --- /dev/null +++ b/dom/crypto/AesKeyAlgorithm.cpp @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/AesKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + +JSObject* +AesKeyAlgorithm::WrapObject(JSContext* aCx) +{ + return AesKeyAlgorithmBinding::Wrap(aCx, this); +} + +nsString +AesKeyAlgorithm::ToJwkAlg() const +{ + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { + switch (mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { + switch (mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { + switch (mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { + switch (mLength) { + case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW); + case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW); + case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW); + } + } + + return nsString(); +} + +bool +AesKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return JS_WriteUint32Pair(aWriter, SCTAG_AESKEYALG, 0) && + JS_WriteUint32Pair(aWriter, mLength, 0) && + WriteString(aWriter, mName); +} + +KeyAlgorithm* +AesKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) +{ + uint32_t length, zero; + nsString name; + bool read = JS_ReadUint32Pair(aReader, &length, &zero) && + ReadString(aReader, name); + if (!read) { + return nullptr; + } + + return new AesKeyAlgorithm(aGlobal, name, length); +} + + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/AesKeyAlgorithm.h b/dom/crypto/AesKeyAlgorithm.h new file mode 100644 index 00000000000..737576aa289 --- /dev/null +++ b/dom/crypto/AesKeyAlgorithm.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_AesKeyAlgorithm_h +#define mozilla_dom_AesKeyAlgorithm_h + +#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm +{ +public: + AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) + : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength) + {} + + ~AesKeyAlgorithm() + {} + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + virtual nsString ToJwkAlg() const MOZ_OVERRIDE; + + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_AesKeyAlgorithm_h diff --git a/dom/crypto/BasicSymmetricKeyAlgorithm.h b/dom/crypto/BasicSymmetricKeyAlgorithm.h new file mode 100644 index 00000000000..afcdc5f2034 --- /dev/null +++ b/dom/crypto/BasicSymmetricKeyAlgorithm.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_BasicSymmetricKeyAlgorithm_h +#define mozilla_dom_BasicSymmetricKeyAlgorithm_h + +#include "mozilla/dom/KeyAlgorithm.h" + +namespace mozilla { +namespace dom { + +class BasicSymmetricKeyAlgorithm : public KeyAlgorithm +{ +public: + BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength) + : KeyAlgorithm(aGlobal, aName) + , mLength(aLength) + {} + + ~BasicSymmetricKeyAlgorithm() + {} + + uint16_t Length() const + { + return mLength; + } + +protected: + uint16_t mLength; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h diff --git a/dom/crypto/CryptoBuffer.cpp b/dom/crypto/CryptoBuffer.cpp index 73a5f81d68e..d719cf6a94d 100644 --- a/dom/crypto/CryptoBuffer.cpp +++ b/dom/crypto/CryptoBuffer.cpp @@ -161,13 +161,6 @@ CryptoBuffer::ToSECItem() const return item; } -JSObject* -CryptoBuffer::ToUint8Array(JSContext* aCx) const -{ - return Uint8Array::Create(aCx, Length(), Elements()); -} - - // "BigInt" comes from the WebCrypto spec // ("unsigned long" isn't very "big", of course) // Likewise, the spec calls for big-endian ints diff --git a/dom/crypto/CryptoBuffer.h b/dom/crypto/CryptoBuffer.h index e5ec172c15d..6a9c4eb60b3 100644 --- a/dom/crypto/CryptoBuffer.h +++ b/dom/crypto/CryptoBuffer.h @@ -40,7 +40,6 @@ public: nsresult FromJwkBase64(const nsString& aBase64); nsresult ToJwkBase64(nsString& aBase64); SECItem* ToSECItem() const; - JSObject* ToUint8Array(JSContext* aCx) const; bool GetBigIntValue(unsigned long& aRetVal); }; diff --git a/dom/crypto/CryptoKey.cpp b/dom/crypto/CryptoKey.cpp index 3647ecb27b4..42ac26d5d86 100644 --- a/dom/crypto/CryptoKey.cpp +++ b/dom/crypto/CryptoKey.cpp @@ -10,12 +10,11 @@ #include "mozilla/dom/CryptoKey.h" #include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/dom/SubtleCryptoBinding.h" -#include "mozilla/dom/ToJSValue.h" namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal, mAlgorithm) NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey) NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey) @@ -91,35 +90,10 @@ CryptoKey::Extractable() const return (mAttributes & EXTRACTABLE); } -void -CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle aRetVal, - ErrorResult& aRv) const +KeyAlgorithm* +CryptoKey::Algorithm() const { - bool converted = false; - JS::RootedValue val(cx); - switch (mAlgorithm.mType) { - case KeyAlgorithmProxy::AES: - converted = ToJSValue(cx, mAlgorithm.mAes, &val); - break; - case KeyAlgorithmProxy::HMAC: - converted = ToJSValue(cx, mAlgorithm.mHmac, &val); - break; - case KeyAlgorithmProxy::RSA: { - RootedDictionary rsa(cx); - mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa); - converted = ToJSValue(cx, rsa, &val); - break; - } - case KeyAlgorithmProxy::EC: - converted = ToJSValue(cx, mAlgorithm.mEc, &val); - break; - } - if (!converted) { - aRv.Throw(NS_ERROR_DOM_OPERATION_ERR); - return; - } - - aRetVal.set(&val.toObject()); + return mAlgorithm; } void @@ -151,18 +125,6 @@ CryptoKey::GetUsages(nsTArray& aRetVal) const } } -KeyAlgorithmProxy& -CryptoKey::Algorithm() -{ - return mAlgorithm; -} - -const KeyAlgorithmProxy& -CryptoKey::Algorithm() const -{ - return mAlgorithm; -} - CryptoKey::KeyType CryptoKey::GetKeyType() const { @@ -203,6 +165,12 @@ CryptoKey::SetExtractable(bool aExtractable) } } +void +CryptoKey::SetAlgorithm(KeyAlgorithm* aAlgorithm) +{ + mAlgorithm = aAlgorithm; +} + void CryptoKey::ClearUsages() { @@ -237,12 +205,6 @@ CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage) mAttributes |= aUsage; } -bool -CryptoKey::HasAnyUsage() -{ - return !!(mAttributes & USAGES_MASK); -} - bool CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage) { @@ -255,25 +217,6 @@ CryptoKey::HasUsageOtherThan(uint32_t aUsages) return !!(mAttributes & USAGES_MASK & ~aUsages); } -bool -CryptoKey::IsRecognizedUsage(const nsString& aUsage) -{ - KeyUsage dummy; - nsresult rv = StringToUsage(aUsage, dummy); - return NS_SUCCEEDED(rv); -} - -bool -CryptoKey::AllUsagesRecognized(const Sequence& aUsages) -{ - for (uint32_t i = 0; i < aUsages.Length(); ++i) { - if (!IsRecognizedUsage(aUsages[i])) { - return false; - } - } - return true; -} - void CryptoKey::SetSymKey(const CryptoBuffer& aSymKey) { mSymKey = aSymKey; @@ -498,10 +441,14 @@ SECKEYPrivateKey* CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { + if (!aJwk.mKty.WasPassed()) { + return nullptr; + } + CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; - if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { + if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y, d; if (!aJwk.mCrv.WasPassed() || @@ -512,7 +459,7 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, } nsString namedCurve; - if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { + if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } @@ -557,7 +504,7 @@ CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, PR_ARRAY_SIZE(keyTemplate)); } - if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { + if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e, d, p, q, dp, dq, qi; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || @@ -696,7 +643,7 @@ ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams, return false; } - aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC); + aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_EC)); return true; } @@ -727,7 +674,7 @@ CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey, return NS_ERROR_DOM_OPERATION_ERR; } - aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA); + aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA)); return NS_OK; } case ecKey: { @@ -769,7 +716,11 @@ SECKEYPublicKey* CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { - if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) { + if (!aJwk.mKty.WasPassed()) { + return nullptr; + } + + if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { // Verify that all of the required parameters are present CryptoBuffer n, e; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || @@ -802,7 +753,7 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, return SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA); } - if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) { + if (aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_EC)) { // Verify that all of the required parameters are present CryptoBuffer x, y; if (!aJwk.mCrv.WasPassed() || @@ -826,7 +777,7 @@ CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk, key->pkcs11ID = CK_INVALID_HANDLE; nsString namedCurve; - if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) { + if (!NormalizeNamedCurveValue(aJwk.mCrv.Value(), namedCurve)) { return nullptr; } @@ -868,7 +819,7 @@ CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey, return NS_ERROR_DOM_OPERATION_ERR; } - aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA); + aRetVal.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_RSA)); return NS_OK; } case ecKey: @@ -906,11 +857,11 @@ CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const CryptoKey::PublicKeyToSpki(mPublicKey, pub, locker); } - return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) && + return JS_WriteUint32Pair(aWriter, mAttributes, 0) && WriteBuffer(aWriter, mSymKey) && WriteBuffer(aWriter, priv) && WriteBuffer(aWriter, pub) && - mAlgorithm.WriteStructuredClone(aWriter); + mAlgorithm->WriteStructuredClone(aWriter); } bool @@ -921,15 +872,15 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader) return false; } - uint32_t version; + uint32_t zero; CryptoBuffer sym, priv, pub; + nsRefPtr algorithm; - bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) && - (version == CRYPTOKEY_SC_VERSION) && + bool read = JS_ReadUint32Pair(aReader, &mAttributes, &zero) && ReadBuffer(aReader, sym) && ReadBuffer(aReader, priv) && ReadBuffer(aReader, pub) && - mAlgorithm.ReadStructuredClone(aReader); + (algorithm = KeyAlgorithm::Create(mGlobal, aReader)); if (!read) { return false; } @@ -943,6 +894,7 @@ CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader) if (pub.Length() > 0) { mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker); } + mAlgorithm = algorithm; // Ensure that what we've read is consistent // If the attributes indicate a key type, should have a key of that type diff --git a/dom/crypto/CryptoKey.h b/dom/crypto/CryptoKey.h index d1f6f71d2f1..44ae8195460 100644 --- a/dom/crypto/CryptoKey.h +++ b/dom/crypto/CryptoKey.h @@ -14,14 +14,11 @@ #include "pk11pub.h" #include "keyhi.h" #include "ScopedNSSTypes.h" -#include "mozilla/ErrorResult.h" +#include "mozilla/dom/KeyAlgorithm.h" #include "mozilla/dom/CryptoBuffer.h" -#include "mozilla/dom/KeyAlgorithmProxy.h" #include "js/StructuredClone.h" #include "js/TypeDecls.h" -#define CRYPTOKEY_SC_VERSION 0x00000001 - class nsIGlobalObject; namespace mozilla { @@ -103,28 +100,23 @@ public: // WebIDL methods void GetType(nsString& aRetVal) const; bool Extractable() const; - void GetAlgorithm(JSContext* cx, JS::MutableHandle aRetVal, - ErrorResult& aRv) const; + KeyAlgorithm* Algorithm() const; void GetUsages(nsTArray& aRetVal) const; // The below methods are not exposed to JS, but C++ can use // them to manipulate the object - KeyAlgorithmProxy& Algorithm(); - const KeyAlgorithmProxy& Algorithm() const; KeyType GetKeyType() const; nsresult SetType(const nsString& aType); void SetType(KeyType aType); void SetExtractable(bool aExtractable); + void SetAlgorithm(KeyAlgorithm* aAlgorithm); void ClearUsages(); nsresult AddUsage(const nsString& aUsage); nsresult AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask); void AddUsage(KeyUsage aUsage); - bool HasAnyUsage(); bool HasUsage(KeyUsage aUsage); bool HasUsageOtherThan(uint32_t aUsages); - static bool IsRecognizedUsage(const nsString& aUsage); - static bool AllUsagesRecognized(const Sequence& aUsages); void SetSymKey(const CryptoBuffer& aSymKey); void SetPrivateKey(SECKEYPrivateKey* aPrivateKey); @@ -180,7 +172,7 @@ private: nsRefPtr mGlobal; uint32_t mAttributes; // see above - KeyAlgorithmProxy mAlgorithm; + nsRefPtr mAlgorithm; // Only one key handle should be set, according to the KeyType CryptoBuffer mSymKey; diff --git a/dom/crypto/CryptoKeyPair.cpp b/dom/crypto/CryptoKeyPair.cpp new file mode 100644 index 00000000000..3a3bfc5fb10 --- /dev/null +++ b/dom/crypto/CryptoKeyPair.cpp @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/CryptoKeyPair.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKeyPair, mGlobal, mPublicKey, mPrivateKey) +NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKeyPair) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKeyPair) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKeyPair) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +CryptoKeyPair::WrapObject(JSContext* aCx) +{ + return CryptoKeyPairBinding::Wrap(aCx, this); +} + + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/CryptoKeyPair.h b/dom/crypto/CryptoKeyPair.h new file mode 100644 index 00000000000..788a6873222 --- /dev/null +++ b/dom/crypto/CryptoKeyPair.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_CryptoKeyPair_h +#define mozilla_dom_CryptoKeyPair_h + +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "nsIGlobalObject.h" +#include "mozilla/dom/CryptoKey.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class CryptoKeyPair MOZ_FINAL : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CryptoKeyPair) + +public: + explicit CryptoKeyPair(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) + , mPublicKey(new CryptoKey(aGlobal)) + , mPrivateKey(new CryptoKey(aGlobal)) + { + SetIsDOMBinding(); + } + + nsIGlobalObject* GetParentObject() const + { + return mGlobal; + } + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + CryptoKey* PublicKey() const + { + return mPublicKey; + } + + CryptoKey* PrivateKey() const + { + return mPrivateKey; + } + +private: + ~CryptoKeyPair() {} + + nsRefPtr mGlobal; + nsRefPtr mPublicKey; + nsRefPtr mPrivateKey; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CryptoKeyPair_h diff --git a/dom/crypto/EcKeyAlgorithm.cpp b/dom/crypto/EcKeyAlgorithm.cpp new file mode 100644 index 00000000000..3dbb4d0009f --- /dev/null +++ b/dom/crypto/EcKeyAlgorithm.cpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/EcKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + +JSObject* +EcKeyAlgorithm::WrapObject(JSContext* aCx) +{ + return EcKeyAlgorithmBinding::Wrap(aCx, this); +} + +bool +EcKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return JS_WriteUint32Pair(aWriter, SCTAG_ECKEYALG, 0) && + WriteString(aWriter, mNamedCurve) && + WriteString(aWriter, mName); +} + +KeyAlgorithm* +EcKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) +{ + nsString name; + nsString namedCurve; + bool read = ReadString(aReader, namedCurve) && + ReadString(aReader, name); + if (!read) { + return nullptr; + } + + return new EcKeyAlgorithm(aGlobal, name, namedCurve); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/EcKeyAlgorithm.h b/dom/crypto/EcKeyAlgorithm.h new file mode 100644 index 00000000000..f456e807b59 --- /dev/null +++ b/dom/crypto/EcKeyAlgorithm.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_EcKeyAlgorithm_h +#define mozilla_dom_EcKeyAlgorithm_h + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/KeyAlgorithm.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class EcKeyAlgorithm : public KeyAlgorithm +{ +public: + EcKeyAlgorithm(nsIGlobalObject* aGlobal, + const nsString& aName, + const nsString& aNamedCurve) + : KeyAlgorithm(aGlobal, aName) + , mNamedCurve(aNamedCurve) + {} + + ~EcKeyAlgorithm() + {} + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + void GetNamedCurve(nsString& aRetVal) const + { + aRetVal.Assign(mNamedCurve); + } + + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); + +protected: + nsString mNamedCurve; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_EcKeyAlgorithm_h diff --git a/dom/crypto/HmacKeyAlgorithm.cpp b/dom/crypto/HmacKeyAlgorithm.cpp new file mode 100644 index 00000000000..858287139f6 --- /dev/null +++ b/dom/crypto/HmacKeyAlgorithm.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/HmacKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(HmacKeyAlgorithm, KeyAlgorithm, mHash) +NS_IMPL_ADDREF_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) +NS_IMPL_RELEASE_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HmacKeyAlgorithm) +NS_INTERFACE_MAP_END_INHERITING(KeyAlgorithm) + +JSObject* +HmacKeyAlgorithm::WrapObject(JSContext* aCx) +{ + return HmacKeyAlgorithmBinding::Wrap(aCx, this); +} + +nsString +HmacKeyAlgorithm::ToJwkAlg() const +{ + switch (mMechanism) { + case CKM_SHA_1_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS1); + case CKM_SHA256_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS256); + case CKM_SHA384_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS384); + case CKM_SHA512_HMAC: return NS_LITERAL_STRING(JWK_ALG_HS512); + } + return nsString(); +} + +bool +HmacKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + nsString hashName; + mHash->GetName(hashName); + return JS_WriteUint32Pair(aWriter, SCTAG_HMACKEYALG, 0) && + JS_WriteUint32Pair(aWriter, mLength, 0) && + WriteString(aWriter, hashName) && + WriteString(aWriter, mName); +} + +KeyAlgorithm* +HmacKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) +{ + uint32_t length, zero; + nsString hash, name; + bool read = JS_ReadUint32Pair(aReader, &length, &zero) && + ReadString(aReader, hash) && + ReadString(aReader, name); + if (!read) { + return nullptr; + } + + return new HmacKeyAlgorithm(aGlobal, name, length, hash); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/HmacKeyAlgorithm.h b/dom/crypto/HmacKeyAlgorithm.h new file mode 100644 index 00000000000..72b9c28cddd --- /dev/null +++ b/dom/crypto/HmacKeyAlgorithm.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_HmacKeyAlgorithm_h +#define mozilla_dom_HmacKeyAlgorithm_h + +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "nsAutoPtr.h" +#include "mozilla/dom/KeyAlgorithm.h" +#include "mozilla/dom/WebCryptoCommon.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class HmacKeyAlgorithm MOZ_FINAL : public KeyAlgorithm +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HmacKeyAlgorithm, KeyAlgorithm) + + HmacKeyAlgorithm(nsIGlobalObject* aGlobal, + const nsString& aName, + uint32_t aLength, + const nsString& aHash) + : KeyAlgorithm(aGlobal, aName) + , mHash(new KeyAlgorithm(aGlobal, aHash)) + , mLength(aLength) + { + switch (mHash->Mechanism()) { + case CKM_SHA_1: mMechanism = CKM_SHA_1_HMAC; break; + case CKM_SHA256: mMechanism = CKM_SHA256_HMAC; break; + case CKM_SHA384: mMechanism = CKM_SHA384_HMAC; break; + case CKM_SHA512: mMechanism = CKM_SHA512_HMAC; break; + default: mMechanism = UNKNOWN_CK_MECHANISM; break; + } + } + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + KeyAlgorithm* Hash() const + { + return mHash; + } + + uint32_t Length() const + { + return mLength; + } + + virtual nsString ToJwkAlg() const MOZ_OVERRIDE; + + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); + +protected: + ~HmacKeyAlgorithm() + {} + + nsRefPtr mHash; + uint32_t mLength; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_HmacKeyAlgorithm_h diff --git a/dom/crypto/KeyAlgorithm.cpp b/dom/crypto/KeyAlgorithm.cpp new file mode 100644 index 00000000000..13d0ffb818c --- /dev/null +++ b/dom/crypto/KeyAlgorithm.cpp @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/KeyAlgorithm.h" +#include "mozilla/dom/WebCryptoCommon.h" +#include "mozilla/dom/AesKeyAlgorithm.h" +#include "mozilla/dom/EcKeyAlgorithm.h" +#include "mozilla/dom/HmacKeyAlgorithm.h" +#include "mozilla/dom/RsaKeyAlgorithm.h" +#include "mozilla/dom/RsaHashedKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(KeyAlgorithm, mGlobal) +NS_IMPL_CYCLE_COLLECTING_ADDREF(KeyAlgorithm) +NS_IMPL_CYCLE_COLLECTING_RELEASE(KeyAlgorithm) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(KeyAlgorithm) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName) + : mGlobal(aGlobal) + , mName(aName) +{ + SetIsDOMBinding(); + + // Set mechanism based on algorithm name + mMechanism = MapAlgorithmNameToMechanism(aName); + + // HMAC not handled here, since it requires extra info +} + +KeyAlgorithm::~KeyAlgorithm() +{ +} + +JSObject* +KeyAlgorithm::WrapObject(JSContext* aCx) +{ + return KeyAlgorithmBinding::Wrap(aCx, this); +} + +nsString +KeyAlgorithm::ToJwkAlg() const +{ + return nsString(); +} + +void +KeyAlgorithm::GetName(nsString& aRetVal) const +{ + aRetVal.Assign(mName); +} + +bool +KeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return WriteString(aWriter, mName); +} + +KeyAlgorithm* +KeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) +{ + uint32_t tag, zero; + bool read = JS_ReadUint32Pair( aReader, &tag, &zero ); + if (!read) { + return nullptr; + } + + KeyAlgorithm* algorithm = nullptr; + switch (tag) { + case SCTAG_KEYALG: { + nsString name; + read = ReadString(aReader, name); + if (!read) { + return nullptr; + } + algorithm = new KeyAlgorithm(aGlobal, name); + break; + } + case SCTAG_AESKEYALG: { + algorithm = AesKeyAlgorithm::Create(aGlobal, aReader); + break; + } + case SCTAG_ECKEYALG: { + algorithm = EcKeyAlgorithm::Create(aGlobal, aReader); + break; + } + case SCTAG_HMACKEYALG: { + algorithm = HmacKeyAlgorithm::Create(aGlobal, aReader); + break; + } + case SCTAG_RSAKEYALG: { + algorithm = RsaKeyAlgorithm::Create(aGlobal, aReader); + break; + } + case SCTAG_RSAHASHEDKEYALG: { + algorithm = RsaHashedKeyAlgorithm::Create(aGlobal, aReader); + break; + } + // No default, algorithm is already nullptr + } + + return algorithm; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/KeyAlgorithm.h b/dom/crypto/KeyAlgorithm.h new file mode 100644 index 00000000000..32aaccbce80 --- /dev/null +++ b/dom/crypto/KeyAlgorithm.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_KeyAlgorithm_h +#define mozilla_dom_KeyAlgorithm_h + +#include "nsCycleCollectionParticipant.h" +#include "nsIGlobalObject.h" +#include "nsWrapperCache.h" +#include "pk11pub.h" +#include "mozilla/dom/CryptoBuffer.h" +#include "js/StructuredClone.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class CryptoKey; +class KeyAlgorithm; + +enum KeyAlgorithmStructuredCloneTags { + SCTAG_KEYALG, + SCTAG_AESKEYALG, + SCTAG_ECKEYALG, + SCTAG_HMACKEYALG, + SCTAG_RSAKEYALG, + SCTAG_RSAHASHEDKEYALG +}; + +} + +namespace dom { + +class KeyAlgorithm : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(KeyAlgorithm) + +public: + KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName); + + nsIGlobalObject* GetParentObject() const + { + return mGlobal; + } + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + void GetName(nsString& aRetVal) const; + + virtual nsString ToJwkAlg() const; + + // Structured clone support methods + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); + + // Helper method to look up NSS methods + // Sub-classes should assign mMechanism on constructor + CK_MECHANISM_TYPE Mechanism() const { + return mMechanism; + } + +protected: + virtual ~KeyAlgorithm(); + + nsRefPtr mGlobal; + nsString mName; + CK_MECHANISM_TYPE mMechanism; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_KeyAlgorithm_h diff --git a/dom/crypto/KeyAlgorithmProxy.cpp b/dom/crypto/KeyAlgorithmProxy.cpp deleted file mode 100644 index 6fcc943bdf2..00000000000 --- a/dom/crypto/KeyAlgorithmProxy.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/KeyAlgorithmProxy.h" -#include "mozilla/dom/WebCryptoCommon.h" - -namespace mozilla { -namespace dom { - -bool -KeyAlgorithmProxy::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const -{ - if (!WriteString(aWriter, mName) || - !JS_WriteUint32Pair(aWriter, mType, KEY_ALGORITHM_SC_VERSION)) { - return false; - } - - switch (mType) { - case AES: - return JS_WriteUint32Pair(aWriter, mAes.mLength, 0); - case HMAC: - return JS_WriteUint32Pair(aWriter, mHmac.mLength, 0) && - WriteString(aWriter, mHmac.mHash.mName); - case RSA: { - return JS_WriteUint32Pair(aWriter, mRsa.mModulusLength, 0) && - WriteBuffer(aWriter, mRsa.mPublicExponent) && - WriteString(aWriter, mRsa.mHash.mName); - } - case EC: - return WriteString(aWriter, mEc.mNamedCurve); - } - - return false; -} - -bool -KeyAlgorithmProxy::ReadStructuredClone(JSStructuredCloneReader* aReader) -{ - uint32_t type, version, dummy; - if (!ReadString(aReader, mName) || - !JS_ReadUint32Pair(aReader, &type, &version)) { - return false; - } - - if (version != KEY_ALGORITHM_SC_VERSION) { - return false; - } - - mType = (KeyAlgorithmType) type; - switch (mType) { - case AES: { - uint32_t length; - if (!JS_ReadUint32Pair(aReader, &length, &dummy)) { - return false; - } - - mAes.mLength = length; - mAes.mName = mName; - return true; - } - case HMAC: { - if (!JS_ReadUint32Pair(aReader, &mHmac.mLength, &dummy) || - !ReadString(aReader, mHmac.mHash.mName)) { - return false; - } - - mHmac.mName = mName; - return true; - } - case RSA: { - uint32_t modulusLength; - nsString hashName; - if (!JS_ReadUint32Pair(aReader, &modulusLength, &dummy) || - !ReadBuffer(aReader, mRsa.mPublicExponent) || - !ReadString(aReader, mRsa.mHash.mName)) { - return false; - } - - mRsa.mModulusLength = modulusLength; - mRsa.mName = mName; - return true; - } - case EC: { - nsString namedCurve; - if (!ReadString(aReader, mEc.mNamedCurve)) { - return false; - } - - mEc.mName = mName; - return true; - } - } - - return false; -} - -CK_MECHANISM_TYPE -KeyAlgorithmProxy::Mechanism() const -{ - if (mType == HMAC) { - return GetMechanism(mHmac); - } - return MapAlgorithmNameToMechanism(mName); -} - -nsString -KeyAlgorithmProxy::JwkAlg() const -{ - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { - switch (mAes.mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128CBC); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192CBC); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256CBC); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { - switch (mAes.mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128CTR); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192CTR); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256CTR); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { - switch (mAes.mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128GCM); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192GCM); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256GCM); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { - switch (mAes.mLength) { - case 128: return NS_LITERAL_STRING(JWK_ALG_A128KW); - case 192: return NS_LITERAL_STRING(JWK_ALG_A192KW); - case 256: return NS_LITERAL_STRING(JWK_ALG_A256KW); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { - nsString hashName = mHmac.mHash.mName; - if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { - return NS_LITERAL_STRING(JWK_ALG_HS1); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { - return NS_LITERAL_STRING(JWK_ALG_HS256); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { - return NS_LITERAL_STRING(JWK_ALG_HS384); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { - return NS_LITERAL_STRING(JWK_ALG_HS512); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { - nsString hashName = mRsa.mHash.mName; - if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { - return NS_LITERAL_STRING(JWK_ALG_RS1); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { - return NS_LITERAL_STRING(JWK_ALG_RS256); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { - return NS_LITERAL_STRING(JWK_ALG_RS384); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { - return NS_LITERAL_STRING(JWK_ALG_RS512); - } - } - - if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { - nsString hashName = mRsa.mHash.mName; - if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { - return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { - return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { - return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_384); - } else if (hashName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { - return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512); - } - } - - return nsString(); -} - -CK_MECHANISM_TYPE -KeyAlgorithmProxy::GetMechanism(const KeyAlgorithm& aAlgorithm) -{ - // For everything but HMAC, the name determines the mechanism - // HMAC is handled by the specialization below - return MapAlgorithmNameToMechanism(aAlgorithm.mName); -} - -CK_MECHANISM_TYPE -KeyAlgorithmProxy::GetMechanism(const HmacKeyAlgorithm& aAlgorithm) -{ - // The use of HmacKeyAlgorithm doesn't completely prevent this - // method from being called with dictionaries that don't really - // represent HMAC key algorithms. - MOZ_ASSERT(aAlgorithm.mName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)); - - CK_MECHANISM_TYPE hashMech; - hashMech = MapAlgorithmNameToMechanism(aAlgorithm.mHash.mName); - - switch (hashMech) { - case CKM_SHA_1: return CKM_SHA_1_HMAC; - case CKM_SHA256: return CKM_SHA256_HMAC; - case CKM_SHA384: return CKM_SHA384_HMAC; - case CKM_SHA512: return CKM_SHA512_HMAC; - } - return UNKNOWN_CK_MECHANISM; -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/crypto/KeyAlgorithmProxy.h b/dom/crypto/KeyAlgorithmProxy.h deleted file mode 100644 index 1d5f3eac5bf..00000000000 --- a/dom/crypto/KeyAlgorithmProxy.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et cindent: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_KeyAlgorithmProxy_h -#define mozilla_dom_KeyAlgorithmProxy_h - -#include "pk11pub.h" -#include "js/StructuredClone.h" -#include "mozilla/dom/KeyAlgorithmBinding.h" -#include "mozilla/dom/WebCryptoCommon.h" - -#define KEY_ALGORITHM_SC_VERSION 0x00000001 - -namespace mozilla { -namespace dom { - -// A heap-safe variant of RsaHashedKeyAlgorithm -// The only difference is that it uses CryptoBuffer instead of Uint8Array -struct RsaHashedKeyAlgorithmStorage { - nsString mName; - KeyAlgorithm mHash; - uint16_t mModulusLength; - CryptoBuffer mPublicExponent; - - void - ToKeyAlgorithm(JSContext* aCx, RsaHashedKeyAlgorithm& aRsa) const - { - aRsa.mName = mName; - aRsa.mModulusLength = mModulusLength; - aRsa.mHash.mName = mHash.mName; - aRsa.mPublicExponent.Init(mPublicExponent.ToUint8Array(aCx)); - aRsa.mPublicExponent.ComputeLengthAndData(); - } -}; - -// This class encapuslates a KeyAlgorithm object, and adds several -// methods that make WebCrypto operations simpler. -struct KeyAlgorithmProxy -{ - enum KeyAlgorithmType { - AES, - HMAC, - RSA, - EC - }; - KeyAlgorithmType mType; - - // Plain is always populated with the algorithm name - // Others are only populated for the corresponding key type - nsString mName; - AesKeyAlgorithm mAes; - HmacKeyAlgorithm mHmac; - RsaHashedKeyAlgorithmStorage mRsa; - EcKeyAlgorithm mEc; - - // Structured clone - bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const; - bool ReadStructuredClone(JSStructuredCloneReader* aReader); - - // Extract various forms of derived information - CK_MECHANISM_TYPE Mechanism() const; - nsString JwkAlg() const; - - // And in static form for calling on raw KeyAlgorithm dictionaries - static CK_MECHANISM_TYPE GetMechanism(const KeyAlgorithm& aAlgorithm); - static CK_MECHANISM_TYPE GetMechanism(const HmacKeyAlgorithm& aAlgorithm); - static nsString GetJwkAlg(const KeyAlgorithm& aAlgorithm); - - // Construction of the various algorithm types - void - MakeAes(const nsString& aName, uint32_t aLength) - { - mType = AES; - mName = aName; - mAes.mName = aName; - mAes.mLength = aLength; - } - - void - MakeHmac(uint32_t aLength, const nsString& aHashName) - { - mType = HMAC; - mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC); - mHmac.mName = NS_LITERAL_STRING(WEBCRYPTO_ALG_HMAC); - mHmac.mLength = aLength; - mHmac.mHash.mName = aHashName; - } - - void - MakeRsa(const nsString& aName, uint32_t aModulusLength, - const CryptoBuffer& aPublicExponent, const nsString& aHashName) - { - mType = RSA; - mName = aName; - mRsa.mName = aName; - mRsa.mModulusLength = aModulusLength; - mRsa.mHash.mName = aHashName; - mRsa.mPublicExponent.Assign(aPublicExponent); - } - - void - MakeEc(const nsString& aName, const nsString& aNamedCurve) - { - mType = EC; - mName = aName; - mEc.mName = aName; - mEc.mNamedCurve = aNamedCurve; - } -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_KeyAlgorithmProxy_h diff --git a/dom/crypto/RsaHashedKeyAlgorithm.cpp b/dom/crypto/RsaHashedKeyAlgorithm.cpp new file mode 100644 index 00000000000..70623fcf22a --- /dev/null +++ b/dom/crypto/RsaHashedKeyAlgorithm.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/RsaHashedKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm, mHash) +NS_IMPL_ADDREF_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) +NS_IMPL_RELEASE_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RsaHashedKeyAlgorithm) +NS_INTERFACE_MAP_END_INHERITING(RsaKeyAlgorithm) + +JSObject* +RsaHashedKeyAlgorithm::WrapObject(JSContext* aCx) +{ + return RsaHashedKeyAlgorithmBinding::Wrap(aCx, this); +} + +nsString +RsaHashedKeyAlgorithm::ToJwkAlg() const +{ + if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { + switch (mHash->Mechanism()) { + case CKM_SHA_1: return NS_LITERAL_STRING(JWK_ALG_RS1); + case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RS256); + case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RS384); + case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RS512); + } + } + + if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + switch(mHash->Mechanism()) { + case CKM_SHA_1: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP); + case CKM_SHA256: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); + case CKM_SHA384: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_256); + case CKM_SHA512: return NS_LITERAL_STRING(JWK_ALG_RSA_OAEP_512); + } + } + + return nsString(); +} + +bool +RsaHashedKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + nsString hashName; + mHash->GetName(hashName); + return JS_WriteUint32Pair(aWriter, SCTAG_RSAHASHEDKEYALG, 0) && + JS_WriteUint32Pair(aWriter, mModulusLength, 0) && + WriteBuffer(aWriter, mPublicExponent) && + WriteString(aWriter, hashName) && + WriteString(aWriter, mName); +} + +KeyAlgorithm* +RsaHashedKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) { + uint32_t modulusLength, zero; + CryptoBuffer publicExponent; + nsString name, hash; + + bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) && + ReadBuffer(aReader, publicExponent) && + ReadString(aReader, hash) && + ReadString(aReader, name); + if (!read) { + return nullptr; + } + + return new RsaHashedKeyAlgorithm(aGlobal, name, modulusLength, publicExponent, hash); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/RsaHashedKeyAlgorithm.h b/dom/crypto/RsaHashedKeyAlgorithm.h new file mode 100644 index 00000000000..e8d546f74c9 --- /dev/null +++ b/dom/crypto/RsaHashedKeyAlgorithm.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_RsaHashedKeyAlgorithm_h +#define mozilla_dom_RsaHashedKeyAlgorithm_h + +#include "nsAutoPtr.h" +#include "mozilla/dom/RsaKeyAlgorithm.h" + +namespace mozilla { +namespace dom { + +class RsaHashedKeyAlgorithm MOZ_FINAL : public RsaKeyAlgorithm +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RsaHashedKeyAlgorithm, RsaKeyAlgorithm) + + RsaHashedKeyAlgorithm(nsIGlobalObject* aGlobal, + const nsString& aName, + uint32_t aModulusLength, + const CryptoBuffer& aPublicExponent, + const nsString& aHashName) + : RsaKeyAlgorithm(aGlobal, aName, aModulusLength, aPublicExponent) + , mHash(new KeyAlgorithm(aGlobal, aHashName)) + {} + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + virtual nsString ToJwkAlg() const MOZ_OVERRIDE; + + KeyAlgorithm* Hash() const + { + return mHash; + } + + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); + +private: + ~RsaHashedKeyAlgorithm() {} + + nsRefPtr mHash; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_RsaHashedKeyAlgorithm_h diff --git a/dom/crypto/RsaKeyAlgorithm.cpp b/dom/crypto/RsaKeyAlgorithm.cpp new file mode 100644 index 00000000000..65f5ba4cd22 --- /dev/null +++ b/dom/crypto/RsaKeyAlgorithm.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/RsaKeyAlgorithm.h" +#include "mozilla/dom/SubtleCryptoBinding.h" +#include "mozilla/dom/WebCryptoCommon.h" + +namespace mozilla { +namespace dom { + +JSObject* +RsaKeyAlgorithm::WrapObject(JSContext* aCx) +{ + return RsaKeyAlgorithmBinding::Wrap(aCx, this); +} + +bool +RsaKeyAlgorithm::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const +{ + return JS_WriteUint32Pair(aWriter, SCTAG_RSAKEYALG, 0) && + JS_WriteUint32Pair(aWriter, mModulusLength, 0) && + WriteBuffer(aWriter, mPublicExponent) && + WriteString(aWriter, mName); +} + +KeyAlgorithm* +RsaKeyAlgorithm::Create(nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader) +{ + uint32_t modulusLength, zero; + CryptoBuffer publicExponent; + nsString name; + bool read = JS_ReadUint32Pair(aReader, &modulusLength, &zero) && + ReadBuffer(aReader, publicExponent) && + ReadString(aReader, name); + if (!read) { + return nullptr; + } + + return new RsaKeyAlgorithm(aGlobal, name, modulusLength, publicExponent); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/crypto/RsaKeyAlgorithm.h b/dom/crypto/RsaKeyAlgorithm.h new file mode 100644 index 00000000000..9aa9e53782b --- /dev/null +++ b/dom/crypto/RsaKeyAlgorithm.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_RsaKeyAlgorithm_h +#define mozilla_dom_RsaKeyAlgorithm_h + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/KeyAlgorithm.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace dom { + +class RsaKeyAlgorithm : public KeyAlgorithm +{ +public: + RsaKeyAlgorithm(nsIGlobalObject* aGlobal, + const nsString& aName, + uint32_t aModulusLength, + const CryptoBuffer& aPublicExponent) + : KeyAlgorithm(aGlobal, aName) + , mModulusLength(aModulusLength) + , mPublicExponent(aPublicExponent) + {} + + ~RsaKeyAlgorithm() + {} + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + uint32_t ModulusLength() const + { + return mModulusLength; + } + + void GetPublicExponent(JSContext* cx, JS::MutableHandle aRetval, + ErrorResult& aError) const + { + TypedArrayCreator creator(mPublicExponent); + JSObject* retval = creator.Create(cx); + if (!retval) { + aError.Throw(NS_ERROR_OUT_OF_MEMORY); + } else { + aRetval.set(retval); + } + } + + virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE; + static KeyAlgorithm* Create(nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader); + +protected: + uint32_t mModulusLength; + CryptoBuffer mPublicExponent; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_RsaKeyAlgorithm_h diff --git a/dom/crypto/WebCryptoCommon.h b/dom/crypto/WebCryptoCommon.h index 43c6e47ead9..5a0e2c1e00a 100644 --- a/dom/crypto/WebCryptoCommon.h +++ b/dom/crypto/WebCryptoCommon.h @@ -23,6 +23,7 @@ #define WEBCRYPTO_ALG_SHA512 "SHA-512" #define WEBCRYPTO_ALG_HMAC "HMAC" #define WEBCRYPTO_ALG_PBKDF2 "PBKDF2" +#define WEBCRYPTO_ALG_RSAES_PKCS1 "RSAES-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP" #define WEBCRYPTO_ALG_ECDH "ECDH" @@ -180,6 +181,8 @@ MapAlgorithmNameToMechanism(const nsString& aName) mechanism = CKM_SHA512; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { mechanism = CKM_PKCS5_PBKD2; + } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + mechanism = CKM_RSA_PKCS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { mechanism = CKM_RSA_PKCS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { @@ -191,45 +194,14 @@ MapAlgorithmNameToMechanism(const nsString& aName) return mechanism; } -#define NORMALIZED_EQUALS(aTest, aConst) \ - nsContentUtils::EqualsIgnoreASCIICase(aTest, NS_LITERAL_STRING(aConst)) - inline bool -NormalizeToken(const nsString& aName, nsString& aDest) +NormalizeNamedCurveValue(const nsString& aNamedCurve, nsString& aDest) { - // Algorithm names - if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CBC)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CBC); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CTR)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CTR); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_GCM)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_AES_GCM); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_KW)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_AES_KW); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA1)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_SHA1); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA256)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_SHA256); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA384)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_SHA384); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA512)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_SHA512); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_HMAC)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_HMAC); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) { - aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH); - // Named curve values - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) { + if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P256)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) { + } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P384)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384); - } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) { + } else if (aNamedCurve.EqualsIgnoreCase(WEBCRYPTO_NAMED_CURVE_P521)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521); } else { return false; diff --git a/dom/crypto/WebCryptoTask.cpp b/dom/crypto/WebCryptoTask.cpp index f37442e2674..accb5995452 100644 --- a/dom/crypto/WebCryptoTask.cpp +++ b/dom/crypto/WebCryptoTask.cpp @@ -11,9 +11,16 @@ #include "jsapi.h" #include "mozilla/Telemetry.h" +#include "mozilla/dom/AesKeyAlgorithm.h" #include "mozilla/dom/CryptoBuffer.h" #include "mozilla/dom/CryptoKey.h" -#include "mozilla/dom/KeyAlgorithmProxy.h" +#include "mozilla/dom/CryptoKeyPair.h" +#include "mozilla/dom/EcKeyAlgorithm.h" +#include "mozilla/dom/HmacKeyAlgorithm.h" +#include "mozilla/dom/KeyAlgorithm.h" +#include "mozilla/dom/RsaHashedKeyAlgorithm.h" +#include "mozilla/dom/RsaKeyAlgorithm.h" +#include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/TypedArray.h" #include "mozilla/dom/WebCryptoCommon.h" #include "mozilla/dom/WebCryptoTask.h" @@ -47,7 +54,7 @@ enum TelemetryAlgorithm { TA_AES_CFB = 2, TA_AES_CTR = 3, TA_AES_GCM = 4, - TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed + TA_RSAES_PKCS1 = 5, TA_RSA_OAEP = 6, // sign/verify TA_RSASSA_PKCS1 = 7, @@ -92,7 +99,9 @@ enum TelemetryAlgorithm { // Safety check for algorithms that use keys, suitable for constructors #define CHECK_KEY_ALGORITHM(keyAlg, algName) \ { \ - if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \ + nsString keyAlgName; \ + keyAlg->GetName(keyAlgName); \ + if (!keyAlgName.EqualsLiteral(algName)) { \ mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \ return; \ } \ @@ -128,15 +137,42 @@ GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName) JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject())); Algorithm alg; - if (!alg.Init(aCx, value)) { + if (!alg.Init(aCx, value) || !alg.mName.WasPassed()) { return NS_ERROR_DOM_SYNTAX_ERR; } - aName = alg.mName; + aName.Assign(alg.mName.Value()); } - if (!NormalizeToken(aName, aName)) { - return NS_ERROR_DOM_SYNTAX_ERR; + // Normalize algorithm names. + if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CBC)) { + aName.AssignLiteral(WEBCRYPTO_ALG_AES_CBC); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_CTR)) { + aName.AssignLiteral(WEBCRYPTO_ALG_AES_CTR); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_GCM)) { + aName.AssignLiteral(WEBCRYPTO_ALG_AES_GCM); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_AES_KW)) { + aName.AssignLiteral(WEBCRYPTO_ALG_AES_KW); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA1)) { + aName.AssignLiteral(WEBCRYPTO_ALG_SHA1); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA256)) { + aName.AssignLiteral(WEBCRYPTO_ALG_SHA256); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA384)) { + aName.AssignLiteral(WEBCRYPTO_ALG_SHA384); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_SHA512)) { + aName.AssignLiteral(WEBCRYPTO_ALG_SHA512); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_HMAC)) { + aName.AssignLiteral(WEBCRYPTO_ALG_HMAC); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_PBKDF2)) { + aName.AssignLiteral(WEBCRYPTO_ALG_PBKDF2); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSAES_PKCS1)) { + aName.AssignLiteral(WEBCRYPTO_ALG_RSAES_PKCS1); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSASSA_PKCS1)) { + aName.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_RSA_OAEP)) { + aName.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP); + } else if (aName.EqualsIgnoreCase(WEBCRYPTO_ALG_ECDH)) { + aName.AssignLiteral(WEBCRYPTO_ALG_ECDH); } return NS_OK; @@ -194,17 +230,17 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { RootedDictionary params(aCx); - if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) { + if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || + !params.mLength.WasPassed()) { return NS_ERROR_DOM_SYNTAX_ERR; } - if (params.mLength != 128 && - params.mLength != 192 && - params.mLength != 256) { + size_t length = params.mLength.Value(); + if (length != 128 && length != 192 && length != 256) { return NS_ERROR_DOM_DATA_ERR; } - aLength = params.mLength; + aLength = length; return NS_OK; } @@ -212,7 +248,8 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, // determine key length as the block size of the given hash. if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); - if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) { + if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) || + !params.mHash.WasPassed()) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -223,7 +260,7 @@ GetKeyLengthForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm, } nsString hashName; - if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) { + if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -281,6 +318,7 @@ CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle aSrc) return false; } + // Implementation of WebCryptoTask methods void @@ -426,12 +464,12 @@ public: telemetryAlg = TA_AES_CBC; AesCbcParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv) || !params.mIv.WasPassed()) { mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mIv) + ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value()) if (mIv.Length() != 16) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; @@ -443,18 +481,19 @@ public: telemetryAlg = TA_AES_CTR; AesCtrParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv) || !params.mCounter.WasPassed() || + !params.mLength.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mCounter) + ATTEMPT_BUFFER_INIT(mIv, params.mCounter.Value()) if (mIv.Length() != 16) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; } - mCounterLength = params.mLength; + mCounterLength = params.mLength.Value(); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM); @@ -462,12 +501,12 @@ public: telemetryAlg = TA_AES_GCM; AesGcmParams params; nsresult rv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv) || !params.mIv.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - ATTEMPT_BUFFER_INIT(mIv, params.mIv) + ATTEMPT_BUFFER_INIT(mIv, params.mIv.Value()) if (params.mAdditionalData.WasPassed()) { ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value()) @@ -704,6 +743,104 @@ private: } }; +class RsaesPkcs1Task : public ReturnArrayBufferViewTask, + public DeferredData +{ +public: + RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm, + CryptoKey& aKey, bool aEncrypt) + : mPrivKey(aKey.GetPrivateKey()) + , mPubKey(aKey.GetPublicKey()) + , mEncrypt(aEncrypt) + { + Init(aCx, aAlgorithm, aKey, aEncrypt); + } + + RsaesPkcs1Task(JSContext* aCx, const ObjectOrString& aAlgorithm, + CryptoKey& aKey, const CryptoOperationData& aData, + bool aEncrypt) + : mPrivKey(aKey.GetPrivateKey()) + , mPubKey(aKey.GetPublicKey()) + , mEncrypt(aEncrypt) + { + Init(aCx, aAlgorithm, aKey, aEncrypt); + SetData(aData); + } + + void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, + CryptoKey& aKey, bool aEncrypt) + { + + Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSAES_PKCS1); + + CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSAES_PKCS1); + + if (mEncrypt) { + if (!mPubKey) { + mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; + return; + } + mStrength = SECKEY_PublicKeyStrength(mPubKey); + } else { + if (!mPrivKey) { + mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; + return; + } + mStrength = PK11_GetPrivateModulusLen(mPrivKey); + } + } + +private: + ScopedSECKEYPrivateKey mPrivKey; + ScopedSECKEYPublicKey mPubKey; + uint32_t mStrength; + bool mEncrypt; + + virtual nsresult BeforeCrypto() MOZ_OVERRIDE + { + if (!mDataIsSet) { + return NS_ERROR_DOM_OPERATION_ERR; + } + + // Verify that the data input is not too big + // (as required by PKCS#1 / RFC 3447, Section 7.2) + // http://tools.ietf.org/html/rfc3447#section-7.2 + if (mEncrypt && mData.Length() > mStrength - 11) { + return NS_ERROR_DOM_DATA_ERR; + } + + return NS_OK; + } + + virtual nsresult DoCrypto() MOZ_OVERRIDE + { + nsresult rv; + + // Ciphertext is an integer mod the modulus, so it will be + // no longer than mStrength octets + if (!mResult.SetLength(mStrength)) { + return NS_ERROR_DOM_UNKNOWN_ERR; + } + + if (mEncrypt) { + rv = MapSECStatus(PK11_PubEncryptPKCS1( + mPubKey.get(), mResult.Elements(), + mData.Elements(), mData.Length(), + nullptr)); + } else { + uint32_t outLen; + rv = MapSECStatus(PK11_PrivDecryptPKCS1( + mPrivKey.get(), mResult.Elements(), + &outLen, mResult.Length(), + mData.Elements(), mData.Length())); + mResult.SetLength(outLen); + } + + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR); + return NS_OK; + } +}; + class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData { @@ -759,8 +896,8 @@ public: return; } - if (params.mLabel.WasPassed()) { - ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value()); + if (params.mLabel.WasPassed() && !params.mLabel.Value().IsNull()) { + ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value().Value()); } } // Otherwise mLabel remains the empty octet string, as intended @@ -769,7 +906,10 @@ public: // static_cast is safe because we only get here if the algorithm name // is RSA-OAEP, and that only happens if we've constructed // an RsaHashedKeyAlgorithm. - mHashMechanism = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash); + // TODO: Add As* methods to KeyAlgorithm (Bug 1036734) + nsRefPtr rsaAlg = + static_cast(aKey.Algorithm()); + mHashMechanism = rsaAlg->Hash()->Mechanism(); switch (mHashMechanism) { case CKM_SHA_1: @@ -856,7 +996,7 @@ public: const CryptoOperationData& aSignature, const CryptoOperationData& aData, bool aSign) - : mMechanism(aKey.Algorithm().Mechanism()) + : mMechanism(aKey.Algorithm()->Mechanism()) , mSymKey(aKey.GetSymKey()) , mSign(aSign) { @@ -980,10 +1120,10 @@ public: // static_cast is safe because we only get here if the algorithm name // is RSASSA-PKCS1-v1_5, and that only happens if we've constructed // an RsaHashedKeyAlgorithm - CK_MECHANISM_TYPE mech; - mech = KeyAlgorithmProxy::GetMechanism(aKey.Algorithm().mRsa.mHash); + nsRefPtr rsaAlg = static_cast(aKey.Algorithm()); + nsRefPtr hashAlg = rsaAlg->Hash(); - switch (mech) { + switch (hashAlg->Mechanism()) { case CKM_SHA_1: mOidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break; case CKM_SHA256: @@ -1180,7 +1320,7 @@ public: // Check 'alg' if (aJwk.mAlg.WasPassed() && - aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) { + aJwk.mAlg.Value() != aKey->Algorithm()->ToJwkAlg()) { return false; } @@ -1289,10 +1429,6 @@ public: } SetKeyData(aCx, aKeyData); - if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; - return; - } } void Init(JSContext* aCx, @@ -1309,11 +1445,11 @@ public: if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; @@ -1345,6 +1481,8 @@ public: // Construct an appropriate KeyAlorithm, // and verify that usages are appropriate + nsRefPtr algorithm; + nsIGlobalObject* global = mKey->GetParentObject(); uint32_t length = 8 * mKeyData.Length(); // bytes to bits if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || @@ -1363,17 +1501,17 @@ public: if ( (length != 128) && (length != 192) && (length != 256) ) { return NS_ERROR_DOM_DATA_ERR; } - mKey->Algorithm().MakeAes(mAlgName, length); + algorithm = new AesKeyAlgorithm(global, mAlgName, length); if (mDataIsJwk && mJwk.mUse.WasPassed() && !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) { return NS_ERROR_DOM_DATA_ERR; } } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { - if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS)) { + if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) { return NS_ERROR_DOM_DATA_ERR; } - mKey->Algorithm().MakeAes(mAlgName, length); + algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length); if (mDataIsJwk && mJwk.mUse.WasPassed()) { // There is not a 'use' value consistent with PBKDF @@ -1384,9 +1522,8 @@ public: return NS_ERROR_DOM_DATA_ERR; } - mKey->Algorithm().MakeHmac(length, mHashName); - - if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) { + algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName); + if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) { return NS_ERROR_DOM_SYNTAX_ERR; } @@ -1398,14 +1535,18 @@ public: return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } + mKey->SetAlgorithm(algorithm); mKey->SetSymKey(mKeyData); mKey->SetType(CryptoKey::SECRET); + mEarlyComplete = true; + return NS_OK; + } + nsresult AfterCrypto() MOZ_OVERRIDE + { if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; } - - mEarlyComplete = true; return NS_OK; } @@ -1435,10 +1576,6 @@ public: } SetKeyData(aCx, aKeyData); - if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; - return; - } } void Init(JSContext* aCx, @@ -1456,25 +1593,17 @@ public: mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; } - mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), mHashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; return; } } - - // Check support for the algorithm and hash names - CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName); - CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName); - if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) { - mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; - return; - } } private: @@ -1540,7 +1669,9 @@ private: virtual nsresult AfterCrypto() MOZ_OVERRIDE { // Check permissions for the requested operation - if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + nsIGlobalObject* global = mKey->GetParentObject(); + if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || + mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { if ((mKey->GetKeyType() == CryptoKey::PUBLIC && mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) || (mKey->GetKeyType() == CryptoKey::PRIVATE && @@ -1556,9 +1687,23 @@ private: } } - // Set an appropriate KeyAlgorithm - mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, - mPublicExponent, mHashName); + // Construct an appropriate KeyAlgorithm + if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + mKey->SetAlgorithm(new RsaKeyAlgorithm(global, mAlgName, mModulusLength, mPublicExponent)); + } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || + mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + nsRefPtr algorithm = + new RsaHashedKeyAlgorithm(global, mAlgName, + mModulusLength, mPublicExponent, mHashName); + if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + if (algorithm->Hash()->Mechanism() == UNKNOWN_CK_MECHANISM) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + mKey->SetAlgorithm(algorithm); + } if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; @@ -1648,7 +1793,7 @@ private: // Extract 'crv' parameter from JWKs. if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) { + if (!NormalizeNamedCurveValue(mJwk.mCrv.Value(), mNamedCurve)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } } @@ -1664,7 +1809,8 @@ private: return NS_ERROR_DOM_DATA_ERR; } - mKey->Algorithm().MakeEc(mAlgName, mNamedCurve); + nsIGlobalObject* global = mKey->GetParentObject(); + mKey->SetAlgorithm(new EcKeyAlgorithm(global, mAlgName, mNamedCurve)); if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) { return NS_ERROR_DOM_DATA_ERR; @@ -1684,8 +1830,13 @@ public: , mPublicKey(aKey.GetPublicKey()) , mKeyType(aKey.GetKeyType()) , mExtractable(aKey.Extractable()) - , mAlg(aKey.Algorithm().JwkAlg()) + , mAlg(aKey.Algorithm()->ToJwkAlg()) { + if (!aKey.Extractable()) { + mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; + return; + } + aKey.GetUsages(mKeyUsages); } @@ -1746,7 +1897,7 @@ private: return NS_ERROR_DOM_OPERATION_ERR; } mJwk.mK.Construct(k); - mJwk.mKty = NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC); + mJwk.mKty.Construct(NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC)); } else if (mKeyType == CryptoKey::PUBLIC) { if (!mPublicKey) { return NS_ERROR_DOM_UNKNOWN_ERR; @@ -1824,6 +1975,7 @@ public: } // Construct an appropriate KeyAlorithm + nsRefPtr algorithm; uint32_t allowedUsages = 0; if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || @@ -1833,20 +1985,19 @@ public: if (NS_FAILED(mEarlyRv)) { return; } - mKey->Algorithm().MakeAes(algName, mLength); - + algorithm = new AesKeyAlgorithm(global, algName, mLength); allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT | CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY; } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; @@ -1863,7 +2014,7 @@ public: return; } - mKey->Algorithm().MakeHmac(mLength, hashName); + algorithm = new HmacKeyAlgorithm(global, algName, mLength, hashName); allowedUsages = CryptoKey::SIGN | CryptoKey::VERIFY; } else { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; @@ -1880,7 +2031,8 @@ public: } mLength = mLength >> 3; // bits to bytes - mMechanism = mKey->Algorithm().Mechanism(); + mMechanism = algorithm->Mechanism(); + mKey->SetAlgorithm(algorithm); // SetSymKey done in Resolve, after we've done the keygen } @@ -1937,8 +2089,7 @@ public: } // Create an empty key and set easy attributes - mKeyPair.mPrivateKey = new CryptoKey(global); - mKeyPair.mPublicKey = new CryptoKey(global); + mKeyPair = new CryptoKeyPair(global); // Extract algorithm name nsString algName; @@ -1949,36 +2100,63 @@ public: } // Construct an appropriate KeyAlorithm + KeyAlgorithm* algorithm; uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0; if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() || + !params.mPublicExponent.WasPassed() || + !params.mHash.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } // Pull relevant info - uint32_t modulusLength = params.mModulusLength; + uint32_t modulusLength = params.mModulusLength.Value(); CryptoBuffer publicExponent; - ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent); + ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value()); nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); if (NS_FAILED(mEarlyRv)) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } // Create algorithm - mKeyPair.mPublicKey.get()->Algorithm().MakeRsa(algName, - modulusLength, - publicExponent, - hashName); - mKeyPair.mPrivateKey.get()->Algorithm().MakeRsa(algName, - modulusLength, - publicExponent, - hashName); + algorithm = new RsaHashedKeyAlgorithm(global, algName, modulusLength, + publicExponent, hashName); + mKeyPair->PublicKey()->SetAlgorithm(algorithm); + mKeyPair->PrivateKey()->SetAlgorithm(algorithm); + mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + + // Set up params struct + mRsaParams.keySizeInBits = modulusLength; + bool converted = publicExponent.GetBigIntValue(mRsaParams.pe); + if (!converted) { + mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; + return; + } + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + RootedDictionary params(aCx); + mEarlyRv = Coerce(aCx, params, aAlgorithm); + if (NS_FAILED(mEarlyRv) || !params.mModulusLength.WasPassed() || + !params.mPublicExponent.WasPassed()) { + mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; + return; + } + + // Pull relevant info + uint32_t modulusLength = params.mModulusLength.Value(); + CryptoBuffer publicExponent; + ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent.Value()); + + // Create algorithm and note the mechanism + algorithm = new RsaKeyAlgorithm(global, algName, modulusLength, + publicExponent); + mKeyPair->PublicKey()->SetAlgorithm(algorithm); + mKeyPair->PrivateKey()->SetAlgorithm(algorithm); mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // Set up params struct @@ -1991,19 +2169,20 @@ public: } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) { + if (!NormalizeNamedCurveValue(params.mNamedCurve.Value(), mNamedCurve)) { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; return; } // Create algorithm. - mKeyPair.mPublicKey.get()->Algorithm().MakeEc(algName, mNamedCurve); - mKeyPair.mPrivateKey.get()->Algorithm().MakeEc(algName, mNamedCurve); + algorithm = new EcKeyAlgorithm(global, algName, mNamedCurve); + mKeyPair->PublicKey()->SetAlgorithm(algorithm); + mKeyPair->PrivateKey()->SetAlgorithm(algorithm); mMechanism = CKM_EC_KEY_PAIR_GEN; } else { mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; @@ -2014,7 +2193,8 @@ public: if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { privateAllowedUsages = CryptoKey::SIGN; publicAllowedUsages = CryptoKey::VERIFY; - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || + algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY; publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY; } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { @@ -2022,38 +2202,31 @@ public: publicAllowedUsages = 0; } - mKeyPair.mPrivateKey.get()->SetExtractable(aExtractable); - mKeyPair.mPrivateKey.get()->SetType(CryptoKey::PRIVATE); + mKeyPair->PrivateKey()->SetExtractable(aExtractable); + mKeyPair->PrivateKey()->SetType(CryptoKey::PRIVATE); - mKeyPair.mPublicKey.get()->SetExtractable(true); - mKeyPair.mPublicKey.get()->SetType(CryptoKey::PUBLIC); + mKeyPair->PublicKey()->SetExtractable(true); + mKeyPair->PublicKey()->SetType(CryptoKey::PUBLIC); - mKeyPair.mPrivateKey.get()->ClearUsages(); - mKeyPair.mPublicKey.get()->ClearUsages(); + mKeyPair->PrivateKey()->ClearUsages(); + mKeyPair->PublicKey()->ClearUsages(); for (uint32_t i=0; i < aKeyUsages.Length(); ++i) { - mEarlyRv = mKeyPair.mPrivateKey.get()->AddUsageIntersecting(aKeyUsages[i], - privateAllowedUsages); + mEarlyRv = mKeyPair->PrivateKey()->AddUsageIntersecting(aKeyUsages[i], + privateAllowedUsages); if (NS_FAILED(mEarlyRv)) { return; } - mEarlyRv = mKeyPair.mPublicKey.get()->AddUsageIntersecting(aKeyUsages[i], - publicAllowedUsages); + mEarlyRv = mKeyPair->PublicKey()->AddUsageIntersecting(aKeyUsages[i], + publicAllowedUsages); if (NS_FAILED(mEarlyRv)) { return; } } - - // If no usages ended up being allowed, DataError - if (!mKeyPair.mPrivateKey.get()->HasAnyUsage() || - !mKeyPair.mPrivateKey.get()->HasAnyUsage()) { - mEarlyRv = NS_ERROR_DOM_DATA_ERR; - return; - } } private: - CryptoKeyPair mKeyPair; + nsRefPtr mKeyPair; CK_MECHANISM_TYPE mMechanism; PK11RSAGenParams mRsaParams; ScopedSECKEYPublicKey mPublicKey; @@ -2102,8 +2275,8 @@ private: return NS_ERROR_DOM_UNKNOWN_ERR; } - mKeyPair.mPrivateKey.get()->SetPrivateKey(mPrivateKey); - mKeyPair.mPublicKey.get()->SetPublicKey(mPublicKey); + mKeyPair->PrivateKey()->SetPrivateKey(mPrivateKey); + mKeyPair->PublicKey()->SetPublicKey(mPublicKey); return NS_OK; } @@ -2111,6 +2284,11 @@ private: { mResultPromise->MaybeResolve(mKeyPair); } + + virtual void Cleanup() MOZ_OVERRIDE + { + mKeyPair = nullptr; + } }; class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask @@ -2148,7 +2326,8 @@ public: RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() || + !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } @@ -2161,7 +2340,7 @@ public: // Extract the hash algorithm. nsString hashName; - mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName); + mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName); if (NS_FAILED(mEarlyRv)) { return; } @@ -2178,9 +2357,9 @@ public: } } - ATTEMPT_BUFFER_INIT(mSalt, params.mSalt) + ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value()) mLength = aLength >> 3; // bits to bytes - mIterations = params.mIterations; + mIterations = params.mIterations.Value(); } private: @@ -2315,23 +2494,25 @@ public: // Retrieve the peer's public key. RootedDictionary params(aCx); mEarlyRv = Coerce(aCx, params, aAlgorithm); - if (NS_FAILED(mEarlyRv)) { + if (NS_FAILED(mEarlyRv) || !params.mPublic.WasPassed()) { mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR; return; } - CryptoKey* publicKey = params.mPublic; + CryptoKey* publicKey = params.mPublic.Value(); mPubKey = publicKey->GetPublicKey(); if (!mPubKey) { mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; return; } - CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH); + nsRefPtr publicAlgorithm = publicKey->Algorithm(); + CHECK_KEY_ALGORITHM(publicAlgorithm, WEBCRYPTO_ALG_ECDH); // Both keys must use the same named curve. - nsString curve1 = aKey.Algorithm().mEc.mNamedCurve; - nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve; + nsString curve1, curve2; + static_cast(aKey.Algorithm())->GetNamedCurve(curve1); + static_cast(publicAlgorithm.get())->GetNamedCurve(curve2); if (!curve1.Equals(curve2)) { mEarlyRv = NS_ERROR_DOM_DATA_ERR; @@ -2464,48 +2645,35 @@ private: // Task creation methods for WebCryptoTask -// Note: We do not perform algorithm normalization as a monolithic process, -// as described in the spec. Instead: -// * Each method handles its slice of the supportedAlgorithms structure -// * Task constructors take care of: -// * Coercing the algorithm to the proper concrete type -// * Cloning subordinate data items -// * Cloning input data as needed -// -// Thus, support for different algorithms is determined by the if-statements -// below, rather than a data structure. -// -// This results in algorithm normalization coming after some other checks, -// and thus slightly more steps being done synchronously than the spec calls -// for. But none of these steps is especially time-consuming. - WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - const CryptoOperationData& aData, - bool aEncrypt) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + const CryptoOperationData& aData, + bool aEncrypt) { TelemetryMethod method = (aEncrypt)? TM_ENCRYPT : TM_DECRYPT; Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC, aKey.Extractable()); - // Ensure key is usable for this operation - if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) || - (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { return new FailureTask(rv); } + // Ensure key is usable for this operation + if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) || + (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt); + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + return new RsaesPkcs1Task(aCx, aAlgorithm, aKey, aData, aEncrypt); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt); } @@ -2515,28 +2683,28 @@ WebCryptoTask::CreateEncryptDecryptTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - const CryptoOperationData& aSignature, - const CryptoOperationData& aData, - bool aSign) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + const CryptoOperationData& aSignature, + const CryptoOperationData& aData, + bool aSign) { TelemetryMethod method = (aSign)? TM_SIGN : TM_VERIFY; Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG, aKey.Extractable()); - // Ensure key is usable for this operation - if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) || - (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { return new FailureTask(rv); } + // Ensure key is usable for this operation + if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) || + (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) { + return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); + } + if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign); } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { @@ -2548,59 +2716,30 @@ WebCryptoTask::CreateSignVerifyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDigestTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - const CryptoOperationData& aData) + const ObjectOrString& aAlgorithm, + const CryptoOperationData& aData) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST); - - nsString algName; - nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); - if (NS_FAILED(rv)) { - return new FailureTask(rv); - } - - if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) || - algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) || - algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) || - algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { - return new DigestTask(aCx, aAlgorithm, aData); - } - - return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return new DigestTask(aCx, aAlgorithm, aData); } WebCryptoTask* WebCryptoTask::CreateImportKeyTask(JSContext* aCx, - const nsAString& aFormat, - JS::Handle aKeyData, - const ObjectOrString& aAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) + const nsAString& aFormat, + JS::Handle aKeyData, + const ObjectOrString& aAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable); - // Verify that the format is recognized - if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - - // Verify that aKeyUsages does not contain an unrecognized value - if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { return new FailureTask(rv); } - // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation. - // However, the spec should be updated to allow it. if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || @@ -2609,7 +2748,8 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) { return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); - } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) || + algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm, aExtractable, aKeyUsages); @@ -2623,58 +2763,22 @@ WebCryptoTask::CreateImportKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat, - CryptoKey& aKey) + CryptoKey& aKey) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY); - // Verify that the format is recognized - if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - - // Verify that the key is extractable - if (!aKey.Extractable()) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - - // Verify that the algorithm supports export - // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation. - // However, the spec should be updated to allow it. - nsString algName = aKey.Algorithm().mName; - if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) || - algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) || - algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) || - algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) || - algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) || - algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) || - algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) || - algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) || - algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { - return new ExportKeyTask(aFormat, aKey); - } - - return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return new ExportKeyTask(aFormat, aKey); } WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) + const ObjectOrString& aAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY); Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE, aExtractable); - // Verify that aKeyUsages does not contain an unrecognized value - // SPEC-BUG: Spec says that this should be InvalidAccessError, but that - // is inconsistent with other analogous points in the spec - if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2687,7 +2791,8 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) || algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) { return new GenerateSymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages); - } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) || + algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) || algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) { return new GenerateAsymmetricKeyTask(aCx, aAlgorithm, aExtractable, aKeyUsages); @@ -2698,24 +2803,14 @@ WebCryptoTask::CreateGenerateKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aBaseKey, - const ObjectOrString& aDerivedKeyType, - bool aExtractable, - const Sequence& aKeyUsages) + const ObjectOrString& aAlgorithm, + CryptoKey& aBaseKey, + const ObjectOrString& aDerivedKeyType, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY); - // Ensure baseKey is usable for this operation - if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - - // Verify that aKeyUsages does not contain an unrecognized value - if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2739,17 +2834,12 @@ WebCryptoTask::CreateDeriveKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx, - const ObjectOrString& aAlgorithm, - CryptoKey& aKey, - uint32_t aLength) + const ObjectOrString& aAlgorithm, + CryptoKey& aKey, + uint32_t aLength) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS); - // Ensure baseKey is usable for this operation - if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - nsString algName; nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName); if (NS_FAILED(rv)) { @@ -2769,31 +2859,18 @@ WebCryptoTask::CreateDeriveBitsTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, - const nsAString& aFormat, - CryptoKey& aKey, - CryptoKey& aWrappingKey, - const ObjectOrString& aWrapAlgorithm) + const nsAString& aFormat, + CryptoKey& aKey, + CryptoKey& aWrappingKey, + const ObjectOrString& aWrapAlgorithm) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY); - // Verify that the format is recognized - if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) && - !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - - // Ensure wrappingKey is usable for this operation + // Ensure key is usable for this operation if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) { return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } - // Ensure key is extractable - if (!aKey.Extractable()) { - return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); - } - nsString wrapAlgName; nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName); if (NS_FAILED(rv)) { @@ -2808,6 +2885,9 @@ WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { return new WrapKeyTask(aCx, aFormat, aKey, aWrappingKey, aWrapAlgorithm); + } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + return new WrapKeyTask(aCx, aFormat, aKey, + aWrappingKey, aWrapAlgorithm); } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new WrapKeyTask(aCx, aFormat, aKey, aWrappingKey, aWrapAlgorithm); @@ -2818,13 +2898,13 @@ WebCryptoTask::CreateWrapKeyTask(JSContext* aCx, WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, - const nsAString& aFormat, - const ArrayBufferViewOrArrayBuffer& aWrappedKey, - CryptoKey& aUnwrappingKey, - const ObjectOrString& aUnwrapAlgorithm, - const ObjectOrString& aUnwrappedKeyAlgorithm, - bool aExtractable, - const Sequence& aKeyUsages) + const nsAString& aFormat, + const ArrayBufferViewOrArrayBuffer& aWrappedKey, + CryptoKey& aUnwrappingKey, + const ObjectOrString& aUnwrapAlgorithm, + const ObjectOrString& aUnwrappedKeyAlgorithm, + bool aExtractable, + const Sequence& aKeyUsages) { Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY); @@ -2833,11 +2913,6 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR); } - // Verify that aKeyUsages does not contain an unrecognized value - if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) { - return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR); - } - nsString keyAlgName; nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName); if (NS_FAILED(rv)) { @@ -2853,7 +2928,8 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, importTask = new ImportSymmetricKeyTask(aCx, aFormat, aUnwrappedKeyAlgorithm, aExtractable, aKeyUsages); - } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || + } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSAES_PKCS1) || + keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) || keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP)) { importTask = new ImportRsaKeyTask(aCx, aFormat, aUnwrappedKeyAlgorithm, @@ -2877,6 +2953,10 @@ WebCryptoTask::CreateUnwrapKeyTask(JSContext* aCx, return new UnwrapKeyTask(aCx, aWrappedKey, aUnwrappingKey, aUnwrapAlgorithm, importTask); + } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) { + return new UnwrapKeyTask(aCx, aWrappedKey, + aUnwrappingKey, aUnwrapAlgorithm, + importTask); } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { return new UnwrapKeyTask(aCx, aWrappedKey, aUnwrappingKey, aUnwrapAlgorithm, diff --git a/dom/crypto/moz.build b/dom/crypto/moz.build index b4f1fc3180a..08c7227f17d 100644 --- a/dom/crypto/moz.build +++ b/dom/crypto/moz.build @@ -5,17 +5,30 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ + 'AesKeyAlgorithm.h', + 'BasicSymmetricKeyAlgorithm.h', 'CryptoBuffer.h', 'CryptoKey.h', - 'KeyAlgorithmProxy.h', + 'CryptoKeyPair.h', + 'EcKeyAlgorithm.h', + 'HmacKeyAlgorithm.h', + 'KeyAlgorithm.h', + 'RsaHashedKeyAlgorithm.h', + 'RsaKeyAlgorithm.h', 'WebCryptoCommon.h', 'WebCryptoTask.h', ] UNIFIED_SOURCES += [ + 'AesKeyAlgorithm.cpp', 'CryptoBuffer.cpp', 'CryptoKey.cpp', - 'KeyAlgorithmProxy.cpp', + 'CryptoKeyPair.cpp', + 'EcKeyAlgorithm.cpp', + 'HmacKeyAlgorithm.cpp', + 'KeyAlgorithm.cpp', + 'RsaHashedKeyAlgorithm.cpp', + 'RsaKeyAlgorithm.cpp', 'WebCryptoTask.cpp', ] diff --git a/dom/crypto/test/test_WebCrypto.html b/dom/crypto/test/test_WebCrypto.html index 9768a3a73bb..2f868e8a8e3 100644 --- a/dom/crypto/test/test_WebCrypto.html +++ b/dom/crypto/test/test_WebCrypto.html @@ -71,6 +71,7 @@ TestArray.addTest( function doExport(x) { if (!hasKeyFields(x)) { + window.result = x; throw "Invalid key; missing field(s)"; } else if ((x.algorithm.name != alg) || (x.algorithm.length != 8 * tv.raw.length) || @@ -84,7 +85,7 @@ TestArray.addTest( } crypto.subtle.importKey("raw", tv.raw, alg, true, ["encrypt"]) - .then(doExport) + .then(doExport, error(that)) .then( memcmp_complete(that, tv.raw), error(that) @@ -152,7 +153,7 @@ TestArray.addTest( } crypto.subtle.importKey("pkcs8", tv.pkcs8, alg, true, ["sign"]) - .then(doExport) + .then(doExport, error(that)) .then( memcmp_complete(that, tv.pkcs8), error(that) @@ -177,27 +178,24 @@ TestArray.addTest( "Import / export round-trip with 'spki'", function() { var that = this; - var alg = { - name: "RSASSA-PKCS1-v1_5", - hash: "SHA-256" - }; + var alg = "RSAES-PKCS1-v1_5"; function doExport(x) { if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; - } else if ((x.algorithm.name != alg.name) || + } else if ((x.algorithm.name != alg) || (x.algorithm.modulusLength != 1024) || (x.algorithm.publicExponent.byteLength != 3) || (x.type != "public") || (!x.extractable) || (x.usages.length != 1) || - (x.usages[0] != 'verify')){ + (x.usages[0] != 'encrypt')){ throw "Invalid key: incorrect key data"; } return crypto.subtle.exportKey("spki", x); } - crypto.subtle.importKey("spki", tv.spki, alg, true, ["verify"]) + crypto.subtle.importKey("spki", tv.spki, alg, true, ["encrypt"]) .then(doExport, error(that)) .then( memcmp_complete(that, tv.spki), @@ -211,10 +209,7 @@ TestArray.addTest( "Import failure with format 'spki'", function() { var that = this; - var alg = { - name: "RSASSA-PKCS1-v1_5", - hash: "SHA-256" - }; + var alg = "RSAES-PKCS1-v1_5"; crypto.subtle.importKey("spki", tv.negative_spki, alg, true, ["encrypt"]) .then(error(that), complete(that)); @@ -387,12 +382,11 @@ TestArray.addTest( function() { var that = this; var alg = { - name: "RSASSA-PKCS1-v1_5", - hash: "SHA-256", + name: "RSAES-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - crypto.subtle.generateKey(alg, false, ["sign", "verify"]).then( + crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then( complete(that, function(x) { return exists(x.publicKey) && (x.publicKey.algorithm.name == alg.name) && @@ -400,14 +394,14 @@ TestArray.addTest( (x.publicKey.type == "public") && x.publicKey.extractable && (x.publicKey.usages.length == 1) && - (x.publicKey.usages[0] == "verify") && + (x.publicKey.usages[0] == "encrypt") && exists(x.privateKey) && (x.privateKey.algorithm.name == alg.name) && (x.privateKey.algorithm.modulusLength == alg.modulusLength) && (x.privateKey.type == "private") && !x.privateKey.extractable && (x.privateKey.usages.length == 1) && - (x.privateKey.usages[0] == "sign"); + (x.privateKey.usages[0] == "decrypt"); }), error(that) ); @@ -420,13 +414,12 @@ TestArray.addTest( function() { var that = this; var alg = { - name: "RSASSA-PKCS1-v1_5", - hash: "SHA-256", + name: "RSAES-PKCS1-v1_5", modulusLength: 2299, // NSS does not like this key length publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - crypto.subtle.generateKey(alg, false, ["sign"]) + crypto.subtle.generateKey(alg, false, ["encrypt"]) .then( error(that), complete(that) ); } ); @@ -462,6 +455,7 @@ TestArray.addTest( var that = this; function doEncrypt(x) { + console.log(x); return crypto.subtle.encrypt( { name: "AES-CBC", iv: tv.aes_cbc_enc.iv }, x, tv.aes_cbc_enc.data); @@ -483,6 +477,7 @@ TestArray.addTest( var that = this; function encrypt(x, iv) { + console.log(x); return crypto.subtle.encrypt( { name: "AES-CBC", iv: iv }, x, tv.aes_cbc_enc.data); @@ -827,6 +822,58 @@ TestArray.addTest( } ); +// ----------------------------------------------------------------------------- +TestArray.addTest( + "RSAES-PKCS#1 encrypt/decrypt round-trip", + function () { + var that = this; + var privKey, pubKey; + var alg = {name:"RSAES-PKCS1-v1_5"}; + + var privKey, pubKey, data, ct, pt; + function setPriv(x) { privKey = x; } + function setPub(x) { pubKey = x; } + function doEncrypt() { + return crypto.subtle.encrypt(alg.name, pubKey, tv.rsaes.data); + } + function doDecrypt(x) { + return crypto.subtle.decrypt(alg.name, privKey, x); + } + + function fail() { error(that); } + + Promise.all([ + crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt']) + .then(setPriv, error(that)), + crypto.subtle.importKey("spki", tv.rsaes.spki, alg, false, ['encrypt']) + .then(setPub, error(that)) + ]).then(doEncrypt, error(that)) + .then(doDecrypt, error(that)) + .then( + memcmp_complete(that, tv.rsaes.data), + error(that) + ); + } +); + +// ----------------------------------------------------------------------------- +TestArray.addTest( + "RSAES-PKCS#1 decryption known answer", + function () { + var that = this; + var alg = {name:"RSAES-PKCS1-v1_5"}; + + function doDecrypt(x) { + return crypto.subtle.decrypt(alg.name, x, tv.rsaes.result); + } + function fail() { error(that); } + + crypto.subtle.importKey("pkcs8", tv.rsaes.pkcs8, alg, false, ['decrypt']) + .then( doDecrypt, fail ) + .then( memcmp_complete(that, tv.rsaes.data), fail ); + } +); + // ----------------------------------------------------------------------------- TestArray.addTest( "RSASSA/SHA-1 signature", @@ -835,12 +882,15 @@ TestArray.addTest( var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-1" }; function doSign(x) { + console.log("sign"); + console.log(x); return crypto.subtle.sign(alg.name, x, tv.rsassa.data); } + function fail() { console.log("fail"); error(that); } crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign']) - .then( doSign ) - .then( memcmp_complete(that, tv.rsassa.sig1), error(that) ); + .then( doSign, fail ) + .then( memcmp_complete(that, tv.rsassa.sig1), fail ); } ); @@ -854,12 +904,13 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig1, tv.rsassa.data); } + function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify ) + .then( doVerify, fail ) .then( complete(that, function(x) { return x; }), - error(that) + fail ); } ); @@ -874,12 +925,13 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data); } + function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify ) + .then( doVerify, fail ) .then( complete(that, function(x) { return !x; }), - error(that) + fail ); } ); @@ -894,10 +946,11 @@ TestArray.addTest( function doSign(x) { return crypto.subtle.sign(alg.name, x, tv.rsassa.data); } + function fail(x) { console.log(x); error(that); } crypto.subtle.importKey("pkcs8", tv.rsassa.pkcs8, alg, false, ['sign']) - .then( doSign ) - .then( memcmp_complete(that, tv.rsassa.sig256), error(that) ); + .then( doSign, fail ) + .then( memcmp_complete(that, tv.rsassa.sig256), fail ); } ); @@ -911,12 +964,13 @@ TestArray.addTest( function doVerify(x) { return crypto.subtle.verify(alg.name, x, tv.rsassa.sig256, tv.rsassa.data); } + function fail(x) { error(that); } crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify ) + .then( doVerify, fail ) .then( complete(that, function(x) { return x; }), - error(that) + fail ); } ); @@ -930,14 +984,17 @@ TestArray.addTest( var use = ['sign', 'verify']; function doVerify(x) { + console.log("verifying") return crypto.subtle.verify(alg.name, x, tv.rsassa.sig_fail, tv.rsassa.data); } + function fail(x) { console.log("failing"); error(that)(x); } + console.log("running") crypto.subtle.importKey("spki", tv.rsassa.spki, alg, false, ['verify']) - .then( doVerify ) + .then( doVerify, fail ) .then( complete(that, function(x) { return !x; }), - error(that) + fail ); } ); @@ -1118,18 +1175,20 @@ TestArray.addTest( true, ['sign', 'verify']); } + function temperr(x) { return function(y) { console.log("error in "+x); console.log(y); } } + Promise.all([ crypto.subtle.importKey("jwk", tv.aes_gcm_enc.key_jwk, "AES-GCM", false, ['wrapKey','unwrapKey']) - .then(function(x) { wrapKey = x; }), + .then(function(x) { console.log("wrapKey"); wrapKey = x; }), crypto.subtle.generateKey(genAlg, true, ['sign', 'verify']) - .then(function(x) { originalKey = x; return x; }) + .then(function(x) { console.log("originalKey"); originalKey = x; return x; }) .then(doExport) .then(function(x) { originalKeyJwk = x; }) ]) - .then(doWrap) - .then(doUnwrap) - .then(doExport) + .then(doWrap, temperr("initial phase")) + .then(doUnwrap, temperr("wrap")) + .then(doExport, temperr("unwrap")) .then( complete(that, function(x) { return exists(x.k) && x.k == originalKeyJwk.k; @@ -1210,9 +1269,9 @@ TestArray.addTest( Promise.all([ crypto.subtle.importKey("jwk", tv.aes_kw.wrapping_key, "AES-KW", false, ['wrapKey','unwrapKey']) - .then(function(x) { wrapKey = x; }), + .then(function(x) { console.log("wrapKey"); wrapKey = x; }), crypto.subtle.generateKey(genAlg, true, ['sign']) - .then(function(x) { originalKey = x; return x; }) + .then(function(x) { console.log("originalKey"); originalKey = x; return x; }) .then(doExport) .then(function(x) { originalKeyJwk = x; }) ]) @@ -1273,7 +1332,7 @@ TestArray.addTest( modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]); + return crypto.subtle.generateKey(alg, false, ["encrypt"]); } function doGenerateRsaSsaPkcs1Key() { @@ -1305,7 +1364,24 @@ TestArray.addTest( return crypto.subtle.generateKey(alg, false, ["sign"]).then(doSign); } - doCheckRSASSA().then(error(that), complete(that)); + function doCheckRSAES() { + var alg = { + name: "RSAES-PKCS1-v1_5", + modulusLength: 1024, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]) + }; + + function doEncrypt(x) { + var alg = {name: "RSA-OAEP", hash: "SHA-1"}; + return crypto.subtle.encrypt(alg, x.publicKey, new Uint8Array()); + } + + return crypto.subtle.generateKey(alg, false, ["encrypt"]).then(doEncrypt); + } + + doCheckRSASSA().then(error(that), function () { + doCheckRSAES().then(error(that), complete(that)); + }); } ); /*]]>*/ diff --git a/dom/crypto/test/test_WebCrypto_ECDH.html b/dom/crypto/test/test_WebCrypto_ECDH.html index 04dc0c00e1d..12793263ecd 100644 --- a/dom/crypto/test/test_WebCrypto_ECDH.html +++ b/dom/crypto/test/test_WebCrypto_ECDH.html @@ -103,7 +103,7 @@ TestArray.addTest( modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]) }; - return crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]) + return crypto.subtle.generateKey(alg, false, ["encrypt"]) } function doDerive() { @@ -336,12 +336,12 @@ TestArray.addTest( } Promise.all([ - crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveKey"]) - .then(setPriv), - crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveKey"]) - .then(setPub) - ]).then(doDerive) - .then(doSignAndVerify) + crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_priv, alg, false, ["deriveBits"]) + .then(setPriv, error(that)), + crypto.subtle.importKey("jwk", tv.ecdh_p521.jwk_pub, alg, false, ["deriveBits"]) + .then(setPub, error(that)) + ]).then(doDerive, error(that)) + .then(doSignAndVerify, error(that)) .then(complete(that), error(that)); } ); diff --git a/dom/crypto/test/test_WebCrypto_JWK.html b/dom/crypto/test/test_WebCrypto_JWK.html index 39b41d85d00..710c04a6844 100644 --- a/dom/crypto/test/test_WebCrypto_JWK.html +++ b/dom/crypto/test/test_WebCrypto_JWK.html @@ -192,6 +192,8 @@ TestArray.addTest( .then(doExport) .then( complete(that, function(x) { + window.jwk_priv = x; + console.log(JSON.stringify(x)); return hasBaseJwkFields(x) && hasFields(x, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']) && x.kty == 'RSA' && diff --git a/dom/crypto/test/test_WebCrypto_PBKDF2.html b/dom/crypto/test/test_WebCrypto_PBKDF2.html index f1cf66c7bf3..21dd1e86f31 100644 --- a/dom/crypto/test/test_WebCrypto_PBKDF2.html +++ b/dom/crypto/test/test_WebCrypto_PBKDF2.html @@ -46,6 +46,7 @@ TestArray.addTest( var key = tv.pbkdf2_sha1.password; function doDerive(x) { + console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } @@ -60,7 +61,7 @@ TestArray.addTest( } function fail(x) { console.log("failing"); error(that)(x); } - crypto.subtle.importKey("raw", key, alg, false, ["deriveBits"]) + crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]) .then( doDerive, fail ) .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail ); } @@ -75,6 +76,7 @@ TestArray.addTest( var key = tv.pbkdf2_sha1.password; function doDerive(x) { + console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } @@ -159,6 +161,7 @@ TestArray.addTest( var key = tv.pbkdf2_sha256.password; function doDerive(x) { + console.log("deriving"); if (!hasKeyFields(x)) { throw "Invalid key; missing field(s)"; } diff --git a/dom/crypto/test/test_WebCrypto_RSA_OAEP.html b/dom/crypto/test/test_WebCrypto_RSA_OAEP.html index 082e8c0a604..0d2c2ff7be3 100644 --- a/dom/crypto/test/test_WebCrypto_RSA_OAEP.html +++ b/dom/crypto/test/test_WebCrypto_RSA_OAEP.html @@ -120,13 +120,12 @@ TestArray.addTest( var privKey, pubKey; function setKey(x) { pubKey = x.publicKey; privKey = x.privateKey; } function doEncrypt(n) { - console.log("entered encrypt("+ n +")"); return function () { return crypto.subtle.encrypt(alg, pubKey, new Uint8Array(n)); } } - crypto.subtle.generateKey(alg, false, ['encrypt', 'decrypt']) + crypto.subtle.generateKey(alg, false, ['encrypt']) .then(setKey, error(that)) .then(doEncrypt(214), error(that)) .then(doEncrypt(215), error(that)) diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index c2c9495d7ef..f57aff30021 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -244,6 +244,8 @@ var interfaceNamesInGlobalScope = "Crypto", // IMPORTANT: Do not change this list without review from a DOM peer! {name: "CryptoKey", pref: "dom.webcrypto.enabled"}, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "CryptoKeyPair", pref: "dom.webcrypto.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! "CSS", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/KeyAlgorithm.webidl b/dom/webidl/KeyAlgorithm.webidl deleted file mode 100644 index ca6706fbbec..00000000000 --- a/dom/webidl/KeyAlgorithm.webidl +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- Mode: IDL; 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/. - * - * The origin of this IDL file is - * http://www.w3.org/TR/WebCryptoAPI/ - */ - -dictionary KeyAlgorithm { - required DOMString name; -}; - -dictionary AesKeyAlgorithm : KeyAlgorithm { - required unsigned short length; -}; - -dictionary EcKeyAlgorithm : KeyAlgorithm { - required DOMString namedCurve; -}; - -dictionary HmacKeyAlgorithm : KeyAlgorithm { - required KeyAlgorithm hash; - required unsigned long length; -}; - -dictionary RsaHashedKeyAlgorithm : KeyAlgorithm { - required unsigned short modulusLength; - required Uint8Array publicExponent; - required KeyAlgorithm hash; -}; - diff --git a/dom/webidl/SubtleCrypto.webidl b/dom/webidl/SubtleCrypto.webidl index a4e1b07950c..4e07329f6ea 100644 --- a/dom/webidl/SubtleCrypto.webidl +++ b/dom/webidl/SubtleCrypto.webidl @@ -9,74 +9,113 @@ typedef DOMString KeyType; typedef DOMString KeyUsage; -typedef DOMString NamedCurve; typedef Uint8Array BigInteger; +/***** KeyAlgorithm interfaces *****/ + +[NoInterfaceObject] +interface KeyAlgorithm { + readonly attribute DOMString name; +}; + +[NoInterfaceObject] +interface AesKeyAlgorithm : KeyAlgorithm { + readonly attribute unsigned short length; +}; + +[NoInterfaceObject] +interface HmacKeyAlgorithm : KeyAlgorithm { + readonly attribute KeyAlgorithm hash; + readonly attribute unsigned long length; +}; + +[NoInterfaceObject] +interface RsaKeyAlgorithm : KeyAlgorithm { + readonly attribute unsigned long modulusLength; + [Throws] + readonly attribute BigInteger publicExponent; +}; + +[NoInterfaceObject] +interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm { + readonly attribute KeyAlgorithm hash; +}; + +[NoInterfaceObject] +interface EcKeyAlgorithm : KeyAlgorithm { + readonly attribute NamedCurve namedCurve; +}; + + /***** Algorithm dictionaries *****/ dictionary Algorithm { - required DOMString name; + DOMString name; }; dictionary AesCbcParams : Algorithm { - required CryptoOperationData iv; + CryptoOperationData iv; }; dictionary AesCtrParams : Algorithm { - required CryptoOperationData counter; - [EnforceRange] required octet length; + CryptoOperationData counter; + [EnforceRange] octet length; }; dictionary AesGcmParams : Algorithm { - required CryptoOperationData iv; + CryptoOperationData iv; CryptoOperationData additionalData; [EnforceRange] octet tagLength; }; dictionary HmacImportParams : Algorithm { - required AlgorithmIdentifier hash; + AlgorithmIdentifier hash; }; dictionary Pbkdf2Params : Algorithm { - required CryptoOperationData salt; - [EnforceRange] required unsigned long iterations; - required AlgorithmIdentifier hash; + CryptoOperationData salt; + [EnforceRange] unsigned long iterations; + AlgorithmIdentifier hash; }; dictionary RsaHashedImportParams { - required AlgorithmIdentifier hash; + AlgorithmIdentifier hash; }; dictionary AesKeyGenParams : Algorithm { - [EnforceRange] required unsigned short length; + [EnforceRange] unsigned short length; }; dictionary HmacKeyGenParams : Algorithm { - required AlgorithmIdentifier hash; + AlgorithmIdentifier hash; [EnforceRange] unsigned long length; }; -dictionary RsaHashedKeyGenParams : Algorithm { - [EnforceRange] required unsigned long modulusLength; - required BigInteger publicExponent; - required AlgorithmIdentifier hash; +dictionary RsaKeyGenParams : Algorithm { + [EnforceRange] unsigned long modulusLength; + BigInteger publicExponent; +}; + +dictionary RsaHashedKeyGenParams : RsaKeyGenParams { + AlgorithmIdentifier hash; }; dictionary RsaOaepParams : Algorithm { - CryptoOperationData label; + CryptoOperationData? label; }; dictionary DhKeyGenParams : Algorithm { - required BigInteger prime; - required BigInteger generator; + BigInteger prime; + BigInteger generator; }; +typedef DOMString NamedCurve; dictionary EcKeyGenParams : Algorithm { - required NamedCurve namedCurve; + NamedCurve namedCurve; }; dictionary AesDerivedKeyParams : Algorithm { - [EnforceRange] required unsigned long length; + [EnforceRange] unsigned long length; }; dictionary HmacDerivedKeyParams : HmacImportParams { @@ -84,7 +123,7 @@ dictionary HmacDerivedKeyParams : HmacImportParams { }; dictionary EcdhKeyDeriveParams : Algorithm { - required CryptoKey public; + CryptoKey public; }; @@ -92,14 +131,14 @@ dictionary EcdhKeyDeriveParams : Algorithm { dictionary RsaOtherPrimesInfo { // The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms - required DOMString r; - required DOMString d; - required DOMString t; + DOMString r; + DOMString d; + DOMString t; }; dictionary JsonWebKey { // The following fields are defined in Section 3.1 of JSON Web Key - required DOMString kty; + DOMString kty; DOMString use; sequence key_ops; DOMString alg; @@ -130,13 +169,14 @@ dictionary JsonWebKey { interface CryptoKey { readonly attribute KeyType type; readonly attribute boolean extractable; - [Cached, Constant, Throws] readonly attribute object algorithm; + readonly attribute KeyAlgorithm algorithm; [Cached, Constant, Frozen] readonly attribute sequence usages; }; -dictionary CryptoKeyPair { - required CryptoKey publicKey; - required CryptoKey privateKey; +[Pref="dom.webcrypto.enabled"] +interface CryptoKeyPair { + readonly attribute CryptoKey publicKey; + readonly attribute CryptoKey privateKey; }; typedef DOMString KeyFormat; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index cb77cd1d01c..b603b4f040d 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -238,7 +238,6 @@ WEBIDL_FILES = [ 'InterAppConnection.webidl', 'InterAppConnectionRequest.webidl', 'InterAppMessagePort.webidl', - 'KeyAlgorithm.webidl', 'KeyboardEvent.webidl', 'KeyEvent.webidl', 'LegacyQueryInterface.webidl', From 2452f6421259ee8b930df3e842b4eb9e08baf1b9 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 26 Sep 2014 15:40:41 -0700 Subject: [PATCH 26/85] Backed out changeset a8d4bd3746ec (bug 1056409) for m-oth failures on a CLOSED TREE --- js/src/doc/Debugger/Debugger.Script.md | 11 +++++ js/src/doc/Debugger/Debugger.Source.md | 11 ----- ...d.js => Script-sourceMapURL-deprecated.js} | 6 +-- ...sourceMapURL.js => Script-sourceMapURL.js} | 6 +-- .../jit-test/tests/debug/Source-displayURL.js | 4 +- js/src/vm/Debugger.cpp | 40 ++++++++++--------- toolkit/devtools/server/actors/script.js | 6 +-- 7 files changed, 43 insertions(+), 41 deletions(-) rename js/src/jit-test/tests/debug/{Source-sourceMapURL-deprecated.js => Script-sourceMapURL-deprecated.js} (92%) rename js/src/jit-test/tests/debug/{Source-sourceMapURL.js => Script-sourceMapURL.js} (92%) diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md index 80ac2fd009a..b0bd786c51f 100644 --- a/js/src/doc/Debugger/Debugger.Script.md +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -115,6 +115,17 @@ from its prototype: : This is `true` if this script's code is ECMAScript strict mode code, and `false` otherwise. +`sourceMapURL` +: If this script was produced by a minimizer or translated from some other + language, and we know the URL of a source map document relating + the source positions in this script to the corresponding source + positions in the original source, then this property's value is that + URL. Otherwise, this is `null`. + + (On the web, the translator may provide the source map URL in a + specially formatted comment in the JavaScript source code, or via a + header in the HTTP reply that carried the generated JavaScript.) + ## Function Properties of the Debugger.Script Prototype Object The functions described below may only be called with a `this` value diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md index 91e6a4ae31b..70e80ffd442 100644 --- a/js/src/doc/Debugger/Debugger.Source.md +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -74,17 +74,6 @@ from its prototype: `url` accessor on `Debugger.Source` instances for such sources should return `undefined`.) -`sourceMapURL` -: If this source was produced by a minimizer or translated from some other - language, and we know the URL of a source map document relating - the source positions in this source to the corresponding source - positions in the original source, then this property's value is that - URL. Otherwise, this is `null`. - - (On the web, the translator may provide the source map URL in a - specially formatted comment in the JavaScript source code, or via a - header in the HTTP reply that carried the generated JavaScript.) - `element` : The [`Debugger.Object`][object] instance referring to the DOM element to which this source code belongs, if any, or `undefined` if it belongs to no DOM diff --git a/js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js b/js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js similarity index 92% rename from js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js rename to js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js index fb01ccb0a67..7e9e59971f4 100644 --- a/js/src/jit-test/tests/debug/Source-sourceMapURL-deprecated.js +++ b/js/src/jit-test/tests/debug/Script-sourceMapURL-deprecated.js @@ -1,4 +1,4 @@ -// Source.prototype.sourceMapURL can be a string or null. +// Script.prototype.sourceMapURL can be a string or null. let g = newGlobal(); let dbg = new Debugger; @@ -6,7 +6,7 @@ let gw = dbg.addDebuggee(g); function getSourceMapURL() { let fw = gw.makeDebuggeeValue(g.f); - return fw.script.source.sourceMapURL; + return fw.script.sourceMapURL; } // Without a source map @@ -21,7 +21,7 @@ assertEq(getSourceMapURL(), 'file:///var/foo.js.map'); let fired = false; dbg.onDebuggerStatement = function (frame) { fired = true; - assertEq(frame.script.source.sourceMapURL, 'file:///var/bar.js.map'); + assertEq(frame.script.sourceMapURL, 'file:///var/bar.js.map'); }; g.evaluate('(function () { (function () { debugger; })(); })();', {sourceMapURL: 'file:///var/bar.js.map'}); diff --git a/js/src/jit-test/tests/debug/Source-sourceMapURL.js b/js/src/jit-test/tests/debug/Script-sourceMapURL.js similarity index 92% rename from js/src/jit-test/tests/debug/Source-sourceMapURL.js rename to js/src/jit-test/tests/debug/Script-sourceMapURL.js index c14004e9431..512b5205dc3 100644 --- a/js/src/jit-test/tests/debug/Source-sourceMapURL.js +++ b/js/src/jit-test/tests/debug/Script-sourceMapURL.js @@ -1,4 +1,4 @@ -// Source.prototype.sourceMapURL can be a string or null. +// Script.prototype.sourceMapURL can be a string or null. let g = newGlobal(); let dbg = new Debugger; @@ -6,7 +6,7 @@ let gw = dbg.addDebuggee(g); function getSourceMapURL() { let fw = gw.makeDebuggeeValue(g.f); - return fw.script.source.sourceMapURL; + return fw.script.sourceMapURL; } // Without a source map @@ -21,7 +21,7 @@ assertEq(getSourceMapURL(), 'file:///var/foo.js.map'); let fired = false; dbg.onDebuggerStatement = function (frame) { fired = true; - assertEq(frame.script.source.sourceMapURL, 'file:///var/bar.js.map'); + assertEq(frame.script.sourceMapURL, 'file:///var/bar.js.map'); }; g.evaluate('(function () { (function () { debugger; })(); })();', {sourceMapURL: 'file:///var/bar.js.map'}); diff --git a/js/src/jit-test/tests/debug/Source-displayURL.js b/js/src/jit-test/tests/debug/Source-displayURL.js index a16a761860a..51536d81388 100644 --- a/js/src/jit-test/tests/debug/Source-displayURL.js +++ b/js/src/jit-test/tests/debug/Source-displayURL.js @@ -78,7 +78,7 @@ var capturedSourceMapURL; dbg.onNewScript = function (script) { capturedScript = script; capturedDisplayURL = script.source.displayURL; - capturedSourceMapURL = script.source.sourceMapURL; + capturedSourceMapURL = script.sourceMapURL; dbg.onNewScript = undefined; }; var fun = gw.makeDebuggeeValue(g.Function('//# sourceURL=munge.js\n//# sourceMappingURL=grunge.map\n')); @@ -87,5 +87,5 @@ assertEq(capturedScript, fun.script); assertEq(capturedDisplayURL, fun.script.source.displayURL); assertEq(capturedDisplayURL, 'munge.js'); -assertEq(capturedSourceMapURL, fun.script.source.sourceMapURL); +assertEq(capturedSourceMapURL, fun.script.sourceMapURL); assertEq(capturedSourceMapURL, 'grunge.map'); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d7f408e1469..2a3f8124f7f 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3204,6 +3204,26 @@ DebuggerScript_getStaticLevel(JSContext *cx, unsigned argc, Value *vp) return true; } +static bool +DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp) +{ + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceMapURL)", args, obj, script); + + ScriptSource *source = script->scriptSource(); + JS_ASSERT(source); + + if (source->hasSourceMapURL()) { + JSString *str = JS_NewUCStringCopyZ(cx, source->sourceMapURL()); + if (!str) + return false; + args.rval().setString(str); + } else { + args.rval().setNull(); + } + + return true; +} + static bool DebuggerScript_getGlobal(JSContext *cx, unsigned argc, Value *vp) { @@ -3934,6 +3954,7 @@ static const JSPropertySpec DebuggerScript_properties[] = { JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0), JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0), JS_PSG("staticLevel", DebuggerScript_getStaticLevel, 0), + JS_PSG("sourceMapURL", DebuggerScript_getSourceMapUrl, 0), JS_PSG("global", DebuggerScript_getGlobal, 0), JS_PS_END }; @@ -4201,24 +4222,6 @@ DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp) return true; } -static bool -DebuggerSource_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp) -{ - THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get sourceMapURL)", args, obj, sourceObject); - - ScriptSource *ss = sourceObject->source(); - if (ss->hasSourceMapURL()) { - JSString *str = JS_NewUCStringCopyZ(cx, ss->sourceMapURL()); - if (!str) - return false; - args.rval().setString(str); - } else { - args.rval().setNull(); - } - - return true; -} - static const JSPropertySpec DebuggerSource_properties[] = { JS_PSG("text", DebuggerSource_getText, 0), JS_PSG("url", DebuggerSource_getUrl, 0), @@ -4228,7 +4231,6 @@ static const JSPropertySpec DebuggerSource_properties[] = { JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0), JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0), JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0), - JS_PSG("sourceMapURL", DebuggerSource_getSourceMapUrl, 0), JS_PS_END }; diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index 1636f7049d3..586a4b6987f 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -4977,7 +4977,7 @@ ThreadSources.prototype = { * of an array of source actors for those. */ sourcesForScript: function (aScript) { - if (!this._useSourceMaps || !aScript.source.sourceMapURL) { + if (!this._useSourceMaps || !aScript.sourceMapURL) { return resolve([this._sourceForScript(aScript)].filter(isNotNull)); } @@ -5004,8 +5004,8 @@ ThreadSources.prototype = { * |aScript| must have a non-null sourceMapURL. */ sourceMap: function (aScript) { - dbg_assert(aScript.source.sourceMapURL, "Script should have a sourceMapURL"); - let sourceMapURL = this._normalize(aScript.source.sourceMapURL, aScript.url); + dbg_assert(aScript.sourceMapURL, "Script should have a sourceMapURL"); + let sourceMapURL = this._normalize(aScript.sourceMapURL, aScript.url); let map = this._fetchSourceMap(sourceMapURL, aScript.url) .then(aSourceMap => this.saveSourceMap(aSourceMap, aScript.url)); this._sourceMapsByGeneratedSource[aScript.url] = map; From 141df104d6dc01119e82504ed177132cc8f3a985 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Fri, 26 Sep 2014 16:21:57 -0700 Subject: [PATCH 27/85] Bug 994190 - 'Modify main-thread IndexedDB to use PBackground', r=khuey. --- CLOBBER | 2 +- 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 +- .../test/test_ipc_messagemanager_blob.html | 53 +- 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 | 2352 +++ dom/indexedDB/ActorsChild.h | 620 + dom/indexedDB/ActorsParent.cpp | 16878 ++++++++++++++++ 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 | 1763 +- dom/indexedDB/IDBDatabase.h | 500 +- dom/indexedDB/IDBEvents.cpp | 114 +- dom/indexedDB/IDBEvents.h | 173 +- dom/indexedDB/IDBFactory.cpp | 1290 +- 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 | 2865 --- dom/indexedDB/OpenDatabaseHelper.h | 175 - dom/indexedDB/PBackgroundIDBCursor.ipdl | 90 + dom/indexedDB/PBackgroundIDBDatabase.ipdl | 66 + ...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 | 821 +- dom/indexedDB/TransactionThreadPool.h | 235 +- 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/test_ipc.html | 174 - dom/indexedDB/ipc/unit/head.js | 18 - dom/indexedDB/ipc/unit/xpcshell.ini | 68 - dom/indexedDB/moz.build | 60 +- dom/indexedDB/test/browser.ini | 52 +- dom/indexedDB/test/file.js | 39 +- dom/indexedDB/test/helpers.js | 62 +- dom/indexedDB/test/mochitest.ini | 546 +- 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 + .../test/test_message_manager_ipc.html | 347 + dom/indexedDB/test/test_persistenceType.html | 15 +- 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 | 3186 ++- dom/ipc/Blob.h | 200 - dom/ipc/BlobChild.h | 227 + dom/ipc/BlobParent.h | 256 + dom/ipc/ContentBridgeChild.cpp | 3 +- dom/ipc/ContentChild.cpp | 23 +- dom/ipc/ContentChild.h | 12 +- dom/ipc/ContentParent.cpp | 117 +- dom/ipc/ContentParent.h | 25 +- dom/ipc/DOMTypes.ipdlh | 43 +- dom/ipc/FilePickerParent.cpp | 2 +- dom/ipc/PBlob.ipdl | 16 +- dom/ipc/PBlobStream.ipdl | 2 +- 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 | 86 +- dom/ipc/nsIContentChild.h | 22 +- dom/ipc/nsIContentParent.cpp | 76 +- 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 | 158 +- 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 | 6 + ipc/glue/BackgroundChildImpl.cpp | 64 +- ipc/glue/BackgroundChildImpl.h | 39 +- ipc/glue/BackgroundImpl.cpp | 161 +- ipc/glue/BackgroundParent.h | 14 + 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 | 47 +- 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 | 50 +- 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/glue/nsID.h | 5 + xpcom/io/nsILocalFileWin.idl | 15 +- xpcom/io/nsLocalFileWin.cpp | 49 +- xpcom/io/nsLocalFileWin.h | 5 + xpcom/threads/LazyIdleThread.cpp | 4 + xpcom/threads/nsThread.cpp | 9 +- 239 files changed, 35170 insertions(+), 27810 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/test_ipc.html 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/test_message_manager_ipc.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/CLOBBER b/CLOBBER index d8f6dd7fb5e..b960740f365 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1065897: Updated moz.build requires CLOBBER +Bug 1069071: IPDL changes require CLOBBER diff --git a/content/base/public/nsDOMFile.h b/content/base/public/nsDOMFile.h index f4c91a827f0..c6e3416629c 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 33f5fc0733e..c8adb79a51b 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 7f238e44219..45bcf232e16 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 1ab34fe4a9c..132d9ddf9ee 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 84c5ce16ac4..ba2737153fc 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -154,7 +154,6 @@ #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" #include "mozilla/dom/EncodingUtils.h" -#include "mozilla/dom/quota/QuotaManager.h" #include "nsDOMNavigationTiming.h" #include "nsSMILAnimationController.h" @@ -8539,13 +8538,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 24244bdb449..07765a2e8b6 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/content/base/test/test_ipc_messagemanager_blob.html b/content/base/test/test_ipc_messagemanager_blob.html index 7c1e2baddd9..707d50a5bd5 100644 --- a/content/base/test/test_ipc_messagemanager_blob.html +++ b/content/base/test/test_ipc_messagemanager_blob.html @@ -40,6 +40,11 @@ } function runTests() { + function done() { + SpecialPowers.removePermission("browser", document); + SimpleTest.finish(); + } + ok("Browser prefs set."); let iframe = document.createElement("iframe"); @@ -50,6 +55,8 @@ iframe.addEventListener("mozbrowserloadend", function() { ok(true, "Got iframe load event."); + const blobString = "this is a great success!"; + const messages = [ "hi!", "", @@ -60,6 +67,8 @@ false, null, 0, + + // Make sure this one is always last. new Blob(["this ", "is ", "a ", "great ", "success!"], {"type" : "text\/plain"}), ]; @@ -67,15 +76,45 @@ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); mm.addMessageListener("test:ipcClonedMessage", function(message) { - // We need to wrap to access message.json, and unwrap to do the - // identity check. - is(SpecialPowers.unwrap(SpecialPowers.wrap(message).json), + let data = message.json; + + if (data instanceof Blob) { + is(receivedMessageIndex, messages.length - 1, "Blob is last"); + is (data.size, + messages[receivedMessageIndex].size, + "Correct blob size"); + is (data.type, + messages[receivedMessageIndex].type, + "Correct blob type"); + + let result1, result2; + + let reader1 = new FileReader(); + reader1.onload = function() { + result1 = reader1.result == blobString ? reader1.result : "bad1"; + if (result2) { + is(result1, result2, "Same results"); + done(); + } + }; + + let reader2 = new FileReader(); + reader2.onload = function() { + result2 = reader2.result == blobString ? reader2.result : "bad2"; + if (result1) { + is(result1, result2, "Same results"); + done(); + } + }; + + reader1.readAsText(data); + reader2.readAsText(messages[receivedMessageIndex]); + return; + } + + is(message.json, messages[receivedMessageIndex++], "Got correct round-tripped response"); - if (receivedMessageIndex == messages.length) { - SpecialPowers.removePermission("browser", document); - SimpleTest.finish(); - } }); mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();", false); diff --git a/dom/archivereader/ArchiveZipFile.cpp b/dom/archivereader/ArchiveZipFile.cpp index c7973729fae..816bdbf30f0 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 dfede39fca8..dcf74323268 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 9eeab72b619..ec4f0ba0545 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 8a3226cd14e..fac125801a4 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" @@ -1574,12 +1573,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) { @@ -10593,9 +10586,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. @@ -10642,8 +10637,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 57368d6a831..6a97df109f6 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 c8b7884d5d0..efd2749b336 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 91a9c6d0abf..09ff842f894 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 a0805ac67c1..fbfda2f294c 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 51d07d102bd..838e62941e3 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 19bccd4423e..26351aaea42 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 d7de3917883..d6e7bf6daa5 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 5c6038191df..9f5ac93e929 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 9482b9ca8ed..790b08cf0f2 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 cca723cd756..8d72b492a0d 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 91a9c6d0abf..09ff842f894 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 a0805ac67c1..fbfda2f294c 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 19bccd4423e..26351aaea42 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 d7de3917883..d6e7bf6daa5 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 56b9135c4ca..ee7c7b23334 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 c12dc7ef0ae..1078df678b8 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 580246bc54b..eef48ade293 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 349c4ceee2f..07132590485 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 5075f4ae8f7..3b426a4c309 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 7ca5e776543..8ccc281d19b 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 6fc578df27c..1d7c86ec4dd 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 124da638ca9..688c315416e 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 ff97c069cea..de847ec8c2f 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 a42e8a200dc..ed76866e9b6 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 518f024e4d6..f6e1102f408 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 a70b6be3aeb..d64b53b85de 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 50068bfe5d0..d5723122639 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 993a37913d2..9e63d783487 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 fc0c3cf0e1e..94d1207f84e 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 00000000000..e5d73b46e64 --- /dev/null +++ b/dom/indexedDB/ActorsChild.cpp @@ -0,0 +1,2352 @@ +/* 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: + explicit 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) + : BackgroundRequestChildBase(aOpenRequest) + , mFactory(aFactory) + , mRequestedVersion(aRequestedVersion) + , 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; + if (NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))) { + NS_WARNING("Failed to dispatch event!"); + } + + return true; +} + +/******************************************************************************* + * BackgroundDatabaseChild + ******************************************************************************/ + +BackgroundDatabaseChild::BackgroundDatabaseChild( + const DatabaseSpec& aSpec, + BackgroundFactoryRequestChild* aOpenRequestActor) + : mSpec(new DatabaseSpec(aSpec)) + , mOpenRequestActor(aOpenRequestActor) + , mDatabase(nullptr) +{ + // 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( + PBlobChild* aBlobChild) +{ + 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; + if (NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))) { + NS_WARNING("Failed to dispatch event!"); + } + + 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: + explicit 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 00000000000..20bd1316765 --- /dev/null +++ b/dom/indexedDB/ActorsChild.h @@ -0,0 +1,620 @@ +/* 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. + explicit 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: + explicit 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 bool mIsDeleteOp; + +public: + IDBOpenDBRequest* + GetOpenDBRequest() const; + +private: + // Only created by IDBFactory. + BackgroundFactoryRequestChild(IDBFactory* aFactory, + IDBOpenDBRequest* aOpenRequest, + bool aIsDeleteOp, + uint64_t aRequestedVersion); + + // 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 +{ + friend class BackgroundFactoryChild; + friend class BackgroundFactoryRequestChild; + friend class IDBDatabase; + + nsAutoPtr mSpec; + nsRefPtr mTemporaryStrongDatabase; + BackgroundFactoryRequestChild* mOpenRequestActor; + IDBDatabase* mDatabase; + +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(PBlobChild* aBlobChild) + 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(); + explicit 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. + explicit 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. + explicit 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: + explicit 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 00000000000..262e32a019d --- /dev/null +++ b/dom/indexedDB/ActorsParent.cpp @@ -0,0 +1,16878 @@ +/* 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 "CheckQuotaHelper.h" +#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 == 5, + "Need to update the major schema version."); + +// Major schema version. Bump for almost everything. +const uint32_t kMajorSchemaVersion = 17; + +// 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" + +#ifdef MOZ_CHILD_PERMISSIONS +const char kPermissionString[] = IDB_PREFIX; +#endif // MOZ_CHILD_PERMISSIONS + +const char kPermissionStringChromeBase[] = IDB_PREFIX "-chrome-"; +const char kPermissionStringChromeReadSuffix[] = "-read"; +const char kPermissionStringChromeWriteSuffix[] = "-write"; + +#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: + explicit 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); + } + + explicit 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 +UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection) +{ + // The only change between 16 and 17 was a different structured + // clone format, but it's backwards-compatible. + nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 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((17 << 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 if (schemaVersion == MakeSchemaVersion(16, 0)) { + rv = UpgradeSchemaFrom16_0To17_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: + explicit 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; + + const OptionalWindowId mOptionalWindowId; + + DebugOnly mActorDestroyed; + +public: + static already_AddRefed + Create(const OptionalWindowId& aOptionalWindowId); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory) + +private: + // Only constructed in Create(). + explicit 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 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(); + } + + 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(PBlobParent* aBlobParent) + 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; + + nsRefPtr mBlobImpl; + nsRefPtr mFileInfo; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile); + + FileInfo* + GetFileInfo() const + { + AssertIsOnBackgroundThread(); + + return mFileInfo; + } + + already_AddRefed + GetInputStream() const + { + AssertIsOnBackgroundThread(); + + nsCOMPtr inputStream; + if (mBlobImpl) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mBlobImpl->GetInternalStream(getter_AddRefs(inputStream)))); + } + + return inputStream.forget(); + } + + void + ClearInputStream() + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mBlobImpl); + + mBlobImpl = nullptr; + } + +private: + // Called when sending to the child. + explicit DatabaseFile(FileInfo* aFileInfo) + : mFileInfo(aFileInfo) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileInfo); + } + + // Called when receiving from the child. + DatabaseFile(DOMFileImpl* aBlobImpl, FileInfo* aFileInfo) + : mBlobImpl(aBlobImpl) + , mFileInfo(aFileInfo) + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aBlobImpl); + MOZ_ASSERT(aFileInfo); + } + + ~DatabaseFile() + { } + + virtual void + ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE + { + AssertIsOnBackgroundThread(); + + mBlobImpl = nullptr; + mFileInfo = nullptr; + } +}; + +class TransactionBase +{ + friend class Cursor; + + class CommitOp; + class UpdateRefcountFunction; + +public: + class AutoSavepoint; + class CachedStatement; + +protected: + typedef IDBTransaction::Mode Mode; + +private: + nsRefPtr mDatabase; + 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(); + } + + 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: + explicit 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 + + explicit 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. + explicit 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; + bool mEnforcingQuota; + 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) + , mEnforcingQuota(true) + , 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; + + MOZ_IMPLICIT 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: + explicit 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: + explicit 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: + explicit 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: + explicit 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: + explicit 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 NonMainThreadHackBlobImpl MOZ_FINAL + : public DOMFileImplFile +{ +public: + NonMainThreadHackBlobImpl(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(); + } + +private: + ~NonMainThreadHackBlobImpl() + { } +}; + +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; + + bool mShutdownRequested; + +public: + QuotaClient(); + + static QuotaClient* + GetInstance() + { + return sInstance; + } + + void + NoteBackgroundThread(nsIEventTarget* aBackgroundThread); + + 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: + + explicit 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(); + } + + 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 NonMainThreadHackBlobImpl(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; + +StaticRefPtr gTransactionThreadPool; + +#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: + explicit 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: + explicit 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) + : mOptionalWindowId(aOptionalWindowId) + , mActorDestroyed(false) +{ + AssertIsOnBackgroundThread(); +} + +Factory::~Factory() +{ + MOZ_ASSERT(mActorDestroyed); +} + +// static +already_AddRefed +Factory::Create(const OptionalWindowId& aOptionalWindowId) +{ + AssertIsOnBackgroundThread(); + + // If this is the first instance then we need to do some initialization. + if (!sFactoryInstanceCount) { + if (!gTransactionThreadPool) { + nsRefPtr threadPool = + TransactionThreadPool::Create(); + if (NS_WARN_IF(!threadPool)) { + return nullptr; + } + + gTransactionThreadPool = 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; + + // Clean up if there are no more instances. + if (!(--sFactoryInstanceCount)) { + 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) + , 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(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; + + if (!IsInvalidated()) { + Invalidate(); + } +} + +PBackgroundIDBDatabaseFileParent* +Database::AllocPBackgroundIDBDatabaseFileParent(PBlobParent* aBlobParent) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aBlobParent); + + nsRefPtr blobImpl = + static_cast(aBlobParent)->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsRefPtr actor; + + if (nsRefPtr fileInfo = blobImpl->GetFileInfo(mFileManager)) { + // This blob was previously shared with the child. + actor = new DatabaseFile(fileInfo); + } else { + // This is a blob we haven't seen before. + fileInfo = mFileManager->GetNewFileInfo(); + MOZ_ASSERT(fileInfo); + + actor = new DatabaseFile(blobImpl, fileInfo); + } + + 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); + + // Add a placeholder for this transaction immediately. + gTransactionThreadPool->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; +} + +/******************************************************************************* + * TransactionBase + ******************************************************************************/ + +TransactionBase::TransactionBase(Database* aDatabase, + Mode aMode) + : mDatabase(aDatabase) + , mTransactionId(gTransactionThreadPool->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); +} + +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)); + + gTransactionThreadPool->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; + } +} + +/******************************************************************************* + * 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; +} + +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 = gTransactionThreadPool.get(); + 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; + + nsRefPtr threadPool = gTransactionThreadPool.get(); + if (threadPool) { + threadPool->Shutdown(); + + gTransactionThreadPool = nullptr; + } + + } + + 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); + + nsRefPtr threadPool = gTransactionThreadPool.get(); + 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); + MOZ_ASSERT(mStoragePrivilege == mozilla::dom::quota::Chrome); + mEnforcingQuota = false; + } + + *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; + } + + MOZ_ASSERT(mStoragePrivilege != mozilla::dom::quota::Chrome); + } + + if (permission == PermissionRequestBase::kPermissionAllowed && + mEnforcingQuota) + { + // If we're running from a window then we should check the quota permission + // as well. + uint32_t quotaPermission = CheckQuotaHelper::GetQuotaPermission(principal); + if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { + mEnforcingQuota = false; + } + } + + *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, + mEnforcingQuota, + 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; + + // Intentionally empty. + nsTArray objectStoreNames; + + nsRefPtr versionChangeOp = new VersionChangeOp(this); + + gTransactionThreadPool->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); + } + + unused << + 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); + } +} + +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: + explicit 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: + explicit 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); + } + + unused << + 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(); + + gTransactionThreadPool->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); + + if (NS_FAILED(mTransaction->RollbackSavepoint())) { + NS_WARNING("Failed to rollback savepoint!"); + } + } +} + +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->ClearInputStream(); + } + } + + 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 00000000000..162f6fa09a9 --- /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 5ee6b272d1e..00000000000 --- 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 e39a654a1ae..00000000000 --- 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 b5b9c73ece1..00000000000 --- 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 ed6214ace67..00000000000 --- 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 6157ed0d260..00000000000 --- 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 d9d580b307d..00000000000 --- 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 fd7b6887e6e..00000000000 --- 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 3b9fa30b3a5..00000000000 --- 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 c177e5d31c2..b7ad44f9d2d 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 9272bd16d7c..83d78695036 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 c0fc7ea9452..00000000000 --- 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 83734a65320..a50546dd154 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 32f04c55f16..c1048c67c1f 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 c3aa2749dfe..5816d3ee15b 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 ec985566ff1..af9594773d1 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 3e0b46431bd..3d2f52299ef 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 7fb922b8d48..bd23e72f9d3 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -4,464 +4,508 @@ * 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/BackgroundChild.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() + explicit 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 +517,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 +530,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 +568,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 +602,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 +612,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 +685,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 +718,475 @@ 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 { + // Make sure that the input stream we get here is one that can actually be + // serialized to PBackground. + PBackgroundChild* backgroundManager = + mBackgroundActor->Manager()->Manager(); + MOZ_ASSERT(backgroundManager); + + auto* blobChild = + static_cast( + BackgroundChild::GetOrCreateActorForBlob(backgroundManager, aBlob)); + MOZ_ASSERT(blobChild); + + auto* dbFile = new DatabaseFile(this); + + actor = + mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(dbFile, + blobChild); + 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 +1195,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 91d8681ed34..ab0a041475b 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 a4c58bee3a6..cd9ce79cf1d 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 af942d286d7..c4844c7ee7e 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 a17ce5d1f86..a7a3ece57f6 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -6,538 +6,697 @@ #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: + explicit 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; + + switch (aParams.type()) { + case FactoryRequestParams::TDeleteDatabaseRequestParams: { + const DatabaseMetadata& metadata = + aParams.get_DeleteDatabaseRequestParams().commonParams().metadata(); + deleting = true; + requestedVersion = metadata.version(); + break; } + + case FactoryRequestParams::TOpenDatabaseRequestParams: { + const DatabaseMetadata& metadata = + aParams.get_OpenDatabaseRequestParams().commonParams().metadata(); + deleting = false; + requestedVersion = metadata.version(); + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + auto actor = + new BackgroundFactoryRequestChild(this, + aRequest, + deleting, + requestedVersion); + + 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 +734,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 284df811326..df3292dd78e 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 336aa2f007b..eb043e829a1 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 a2a3e154a59..1d0cda77033 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 806f4ec93e9..8b40e1fd16f 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 7c69077a01a..f7b2ed35489 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 85ade251091..ba307594d35 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 7b4f3f04077..038554f2dc6 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 bf28971cabc..53c3c00be64 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 554d64c40de..9170b071c68 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; + + explicit 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 0d164c8ffae..1ce9c869bac 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 e778a4bfdee..93e64abd0a9 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,65 @@ 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; + if (NS_FAILED(DispatchEvent(event, &ignored))) { + NS_WARNING("Failed to dispatch event!"); } } 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 +234,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 +248,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 +279,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 +367,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 +390,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 +451,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 +479,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 d628b5a7998..eda2d4e41cf 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 219dec7bf70..b210e7bda2f 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 db8149323e9..d07d5480a9c 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 964f2992ea0..8a7b46249b2 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 129cc0b3129..d77e9dd93e0 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 1afeab71b0d..aa583ea1f88 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 + MOZ_IMPLICIT 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 b856af67d55..1b632217e5e 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 3f4010d5576..fab61353d76 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 27bad25a874..4ce140274ef 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 bc1183f9165..5a847c51779 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 4ab780c225e..f29443722c4 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 e3ad5b3d289..9f440746372 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 bee0c2cad0e..abcd22c0022 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 1a07825d44e..00000000000 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ /dev/null @@ -1,2865 +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 == 5, - "Need to update the major schema version."); - -// Major schema version. Bump for almost everything. -const uint32_t kMajorSchemaVersion = 17; - -// 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; -} - -nsresult -UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection) -{ - // The only change between 16 and 17 was a different structured - // clone format, but it's backwards-compatible. - nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 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((17 << 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 if (schemaVersion == MakeSchemaVersion(16, 0)) { - rv = UpgradeSchemaFrom16_0To17_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 00a5a0fdd66..00000000000 --- 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 00000000000..3c755e903f2 --- /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 00000000000..ac5fa5b6722 --- /dev/null +++ b/dom/indexedDB/PBackgroundIDBDatabase.ipdl @@ -0,0 +1,66 @@ +/* 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; +}; + +protocol PBackgroundIDBDatabase +{ + manager PBackgroundIDBFactory; + + manages PBackgroundIDBDatabaseFile; + manages PBackgroundIDBTransaction; + manages PBackgroundIDBVersionChangeTransaction; + +parent: + DeleteMe(); + + Blocked(); + + Close(); + + PBackgroundIDBDatabaseFile(PBlob blob); + + 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 4e1f7048258..db66e773394 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 00000000000..1fac81f93b1 --- /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 00000000000..1e198fc96bb --- /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 00000000000..e63cfbd1656 --- /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 00000000000..d1e17690636 --- /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 00000000000..0db17629207 --- /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 00000000000..f0b475f4a4a --- /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 00000000000..4eecb2b1c0a --- /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 00000000000..9f7f38e8ba2 --- /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 00000000000..3ebf9b81f55 --- /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 46752955d4d..4afab8469c6 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 d02eedc3044..1441099ec7b 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 882c1469fd4..aa014a091a8 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 00000000000..45cb5b6fb78 --- /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 6a59f5b3d99..d0e68990e95 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,402 @@ 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(mShutdownRequested); + MOZ_ASSERT(mShutdownComplete); } -// static -TransactionThreadPool* -TransactionThreadPool::GetOrCreate() +#ifdef DEBUG + +void +TransactionThreadPool::AssertIsOnOwningThread() const { - if (!gThreadPool && !gShutdown) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - nsAutoPtr pool(new TransactionThreadPool()); + MOZ_ASSERT(mOwningThread); - nsresult rv = pool->Init(); - NS_ENSURE_SUCCESS(rv, nullptr); + bool current; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} - gThreadPool = pool.forget(); +#endif // DEBUG + +// static +already_AddRefed +TransactionThreadPool::Create() +{ + AssertIsOnBackgroundThread(); + + nsRefPtr threadPool = new TransactionThreadPool(); + threadPool->AssertIsOnOwningThread(); + + if (NS_WARN_IF(NS_FAILED(threadPool->Init()))) { + threadPool->CleanupAsync(); + return nullptr; } - return gThreadPool; + + return threadPool.forget(); } -// static -TransactionThreadPool* -TransactionThreadPool::Get() -{ - return gThreadPool; -} - -// static void TransactionThreadPool::Shutdown() { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(!mShutdownRequested); + MOZ_ASSERT(!mShutdownComplete); - gShutdown = true; + mShutdownRequested = true; - if (gThreadPool) { - if (NS_FAILED(gThreadPool->Cleanup())) { - NS_WARNING("Failed to shutdown thread pool!"); - } - delete gThreadPool; - gThreadPool = nullptr; + if (!mThreadPool) { + MOZ_ASSERT(!mTransactionsInProgress.Count()); + MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); + + mShutdownComplete = true; + return; } + + if (!mTransactionsInProgress.Count()) { + Cleanup(); + + MOZ_ASSERT(mShutdownComplete); + return; + } + + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + while (!mShutdownComplete) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } +} + +// 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(!mShutdownComplete); + MOZ_ASSERT(!mTransactionsInProgress.Count()); + + mShutdownRequested = true; + + if (!mThreadPool) { + MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); + + mShutdownComplete = true; + return; + } + + 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 +444,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 +468,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 +477,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 +523,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 +605,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 +616,7 @@ TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) } } - if (mode == IDBTransaction::READ_WRITE) { + if (aMode == IDBTransaction::READ_WRITE) { blockInfo->lastBlockingReads = transactionInfo; blockInfo->lastBlockingWrites.Clear(); } @@ -359,176 +632,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 +803,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 +820,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 +863,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 +888,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 +973,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 +998,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 4e90cc186fd..c08d9b6cbc1 100644 --- a/dom/indexedDB/TransactionThreadPool.h +++ b/dom/indexedDB/TransactionThreadPool.h @@ -7,145 +7,93 @@ #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 Shutdown(); + + 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 +101,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 5f2754cd9ce..00000000000 --- 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 a003a0402ac..00000000000 --- 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 d39e2cad985..00000000000 --- 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 0265d0327db..00000000000 --- 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 355e54d1100..00000000000 --- 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 a5a459b7f24..00000000000 --- 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 4677685709a..00000000000 --- 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 1daa9341735..00000000000 --- 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 f765687337c..00000000000 --- 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 3b402fa5440..00000000000 --- 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 8026db4086c..00000000000 --- 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 4c83fed5511..00000000000 --- 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 ff6d88a7b74..00000000000 --- 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 3d6d4eb24ac..00000000000 --- 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 81b48267ea2..00000000000 --- 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 fee6defb45e..00000000000 --- 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/test_ipc.html b/dom/indexedDB/ipc/test_ipc.html deleted file mode 100644 index 085ebef5435..00000000000 --- a/dom/indexedDB/ipc/test_ipc.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - Test for OOP IndexedDB - - - - - - - - diff --git a/dom/indexedDB/ipc/unit/head.js b/dom/indexedDB/ipc/unit/head.js deleted file mode 100644 index 8293b09c48f..00000000000 --- 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 6aeedab2890..00000000000 --- 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 5c4071dd897..d262a1f2695 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,63 @@ 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' + +FAIL_ON_WARNINGS = True + 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 62a6254d1d5..55ede42c0e7 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 b9b735c5d55..e8fc24a1289 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 0a6a5f4b8b5..1e2841dadc8 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 f8053fc5014..77aa4451628 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -14,235 +14,349 @@ 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_message_manager_ipc.html] +# This test is only supposed to run in the main process. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s +[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 854b90f0624..e7e4407197a 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"); + 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 00000000000..9b82995a399 --- /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 00000000000..b95dd24195e --- /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 69bbd74e7c5..7a66cb322e7 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 f24c793f010..6403f3f514a 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 1273f1f3073..2cb3f47544d 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 6b5bc15abfa..028ebd5f544 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 eea32932bd3..46e012a59dc 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 bb8ba8d7e9d..9a24632a428 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 31b91fd7382..2db35eebfdb 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 2b0682e49fd..052c2ee5290 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 33f5e262f6e..71b181f6134 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 75fca15f4cb..6aaed5e376a 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 3bb4f5e810e..d65656547b9 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 8fc19894a38..ff28544669a 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 9fd902c6abc..70b9acdbb69 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 00000000000..45651953cb4 --- /dev/null +++ b/dom/indexedDB/test/test_invalidate.html @@ -0,0 +1,18 @@ + + + + IndexedDB Test + + + + + + + + + + + diff --git a/dom/indexedDB/test/test_message_manager_ipc.html b/dom/indexedDB/test/test_message_manager_ipc.html new file mode 100644 index 00000000000..5a45b90a009 --- /dev/null +++ b/dom/indexedDB/test/test_message_manager_ipc.html @@ -0,0 +1,347 @@ + + + + Test for sending IndexedDB Blobs through MessageManager + + + + + + + diff --git a/dom/indexedDB/test/test_persistenceType.html b/dom/indexedDB/test/test_persistenceType.html index 89e8162f615..f7fa5a6289b 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"); @@ -103,6 +114,6 @@ - + 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 00000000000..3e54ccece6c --- /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 f1fd7b261a6..aaf536febe4 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 a44f8eb1840..33386e079f9 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 00000000000..fe34bb01596 --- /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 14dc55ff17d..7560d1fa2cb 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 cb8fd9090fd..671c8771f3f 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 00000000000..d9a7b7589c9 --- /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 00000000000..2e704f8dc09 --- /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 1ec20b3463e..cc1e05b0ada 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 00000000000..cb7dc0e2eb9 --- /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 2cfaf2f670e..75ea49ade38 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 306bd42e58c..00000000000 --- 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 fd2b9c16fa1..20ebcea4b78 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 20bf89e4c58..24b85aef8da 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -2,65 +2,236 @@ * 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" #include "jsapi.h" #include "mozilla/Assertions.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/DebugOnly.h" #include "mozilla/Monitor.h" +#include "mozilla/Mutex.h" #include "mozilla/unused.h" #include "mozilla/dom/nsIContentParent.h" #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 "nsCOMPtr.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/PBackgroundParent.h" +#include "mozilla/ipc/PFileDescriptorSetParent.h" +#include "nsDataHashtable.h" #include "nsDOMFile.h" +#include "nsHashKeys.h" +#include "nsID.h" #include "nsIDOMFile.h" #include "nsIInputStream.h" #include "nsIIPCSerializableInputStream.h" #include "nsIMultiplexInputStream.h" #include "nsIRemoteBlob.h" #include "nsISeekableStream.h" +#include "nsIUUIDGenerator.h" #include "nsNetCID.h" -#include "nsProxyRelease.h" +#include "nsServiceManagerUtils.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}} -using namespace mozilla; -using namespace mozilla::dom; +namespace mozilla { +namespace dom { + using namespace mozilla::ipc; namespace { -enum ActorType +const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1"; + +StaticRefPtr gUUIDGenerator; + +GeckoProcessType gProcessType = GeckoProcessType_Invalid; + +void +CommonStartup() { - ChildActor, - ParentActor + MOZ_ASSERT(NS_IsMainThread()); + + gProcessType = XRE_GetProcessType(); + MOZ_ASSERT(gProcessType != GeckoProcessType_Invalid); + + nsCOMPtr uuidGen = do_GetService(kUUIDGeneratorContractId); + MOZ_RELEASE_ASSERT(uuidGen); + + gUUIDGenerator = uuidGen; + ClearOnShutdown(&gUUIDGenerator); +} + +template +struct ConcreteManagerTypeTraits; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef ContentChild Type; }; -// Ensure that a nsCOMPtr/nsRefPtr is released on the main thread. +template <> +struct ConcreteManagerTypeTraits +{ + typedef PBackgroundChild Type; +}; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef ContentParent Type; +}; + +template <> +struct ConcreteManagerTypeTraits +{ + typedef PBackgroundParent Type; +}; + +void +AssertCorrectThreadForManager(nsIContentChild* aManager) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +void +AssertCorrectThreadForManager(nsIContentParent* aManager) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); +} + +void +AssertCorrectThreadForManager(PBackgroundChild* aManager) +{ +#ifdef DEBUG + if (aManager) { + PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread(); + MOZ_ASSERT(backgroundChild); + MOZ_ASSERT(backgroundChild == aManager); + } +#endif +} + +void +AssertCorrectThreadForManager(PBackgroundParent* aManager) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + AssertIsOnBackgroundThread(); +} + +intptr_t +ActorManagerProcessID(nsIContentParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return reinterpret_cast(aManager); +} + +intptr_t +ActorManagerProcessID(PBackgroundParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return BackgroundParent::GetRawContentParentForComparison(aManager); +} + +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