From 1f7bae230793f8cccd2b23d72f7524bea8249197 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 24 Dec 2014 21:30:04 -0800 Subject: [PATCH 01/16] Bug 1117267 - Uninline Debugger::isDebuggee() to fix -Wundefined-inline warning. r=shu --- js/src/vm/Debugger.cpp | 8 +++++++- js/src/vm/Debugger.h | 9 +++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index a3da29c78ce3..81e80e73f57c 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1385,7 +1385,6 @@ Debugger::onTrap(JSContext *cx, MutableHandleValue vp) if (!site || !site->hasBreakpoint(bp)) continue; - /* * There are two reasons we have to check whether dbg is enabled and * debugging scriptGlobal. @@ -1621,6 +1620,13 @@ Debugger::slowPathOnLogAllocationSite(JSContext *cx, HandleSavedFrame frame, int return true; } +bool +Debugger::isDebuggee(const JSCompartment *compartment) const +{ + MOZ_ASSERT(compartment); + return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); +} + bool Debugger::appendAllocationSite(JSContext *cx, HandleSavedFrame frame, int64_t when) { diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index 07b9b20f2ae1..ce3e2df562f8 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -22,13 +22,13 @@ #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" -typedef enum JSTrapStatus { +enum JSTrapStatus { JSTRAP_ERROR, JSTRAP_CONTINUE, JSTRAP_RETURN, JSTRAP_THROW, JSTRAP_LIMIT -} JSTrapStatus; +}; namespace js { @@ -234,10 +234,7 @@ class Debugger : private mozilla::LinkedListElement // Return true if the given compartment is a debuggee of this debugger, // false otherwise. - bool isDebuggee(const JSCompartment *compartment) const { - MOZ_ASSERT(compartment); - return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal()); - } + bool isDebuggee(const JSCompartment *compartment) const; private: HeapPtrNativeObject object; /* The Debugger object. Strong reference. */ From 5b6045b985206b5976e2ddc8031e9fa04cc4ddd5 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Sat, 3 Jan 2015 10:12:28 -0800 Subject: [PATCH 02/16] Bug 1112934 - Run tests that depend on nursery behavior under AutoLeaveZeal --- js/src/gc/GCRuntime.h | 1 + js/src/jsapi-tests/testGCHeapPostBarriers.cpp | 2 ++ js/src/jsapi-tests/tests.h | 24 +++++++++++++++++++ js/src/jsapi.cpp | 6 +++++ js/src/jsapi.h | 3 +++ js/src/jsgc.cpp | 7 ++++++ 6 files changed, 43 insertions(+) diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index cfb2b8eb7a12..0b9001c6122d 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -352,6 +352,7 @@ class GCRuntime #ifdef JS_GC_ZEAL const void *addressOfZealMode() { return &zealMode; } + void getZeal(uint8_t *zeal, uint32_t *frequency); void setZeal(uint8_t zeal, uint32_t frequency); bool parseAndSetZeal(const char *str); void setNextScheduled(uint32_t count); diff --git a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp index ef200a985346..61150eaa4726 100644 --- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp +++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp @@ -10,6 +10,8 @@ BEGIN_TEST(testGCHeapPostBarriers) { + AutoLeaveZeal nozeal(cx); + /* Sanity check - objects start in the nursery and then become tenured. */ JS_GC(cx->runtime()); JS::RootedObject obj(cx, NurseryObject()); diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 169fa1ad7246..72e69ea95a76 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -412,4 +412,28 @@ class TestJSPrincipals : public JSPrincipals } }; +#ifdef JS_GC_ZEAL +/* + * Temporarily disable the GC zeal setting. This is only useful in tests that + * need very explicit GC behavior and should not be used elsewhere. + */ +class AutoLeaveZeal +{ + JSContext *cx_; + uint8_t zeal_; + uint32_t frequency_; + + public: + AutoLeaveZeal(JSContext *cx) : cx_(cx) { + JS_GetGCZeal(cx_, &zeal_, &frequency_); + JS_SetGCZeal(cx_, 0, 0); + JS::PrepareForFullGC(JS_GetRuntime(cx_)); + JS::ShrinkingGC(JS_GetRuntime(cx_), JS::gcreason::DEBUG_GC); + } + ~AutoLeaveZeal() { + JS_SetGCZeal(cx_, zeal_, frequency_); + } +}; +#endif /* JS_GC_ZEAL */ + #endif /* jsapi_tests_tests_h */ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 295ec85329ac..23d313a1855f 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5725,6 +5725,12 @@ JS_AbortIfWrongThread(JSRuntime *rt) } #ifdef JS_GC_ZEAL +JS_PUBLIC_API(void) +JS_GetGCZeal(JSContext *cx, uint8_t *zeal, uint32_t *frequency) +{ + cx->runtime()->gc.getZeal(zeal, frequency); +} + JS_PUBLIC_API(void) JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 51d29020a76e..5f2db1c4ba1a 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5044,6 +5044,9 @@ JS_NewObjectForConstructor(JSContext *cx, const JSClass *clasp, const JS::CallAr #ifdef JS_GC_ZEAL #define JS_DEFAULT_ZEAL_FREQ 100 +extern JS_PUBLIC_API(void) +JS_GetGCZeal(JSContext *cx, uint8_t *zeal, uint32_t *frequency); + extern JS_PUBLIC_API(void) JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 773453d27ea3..beec59c69fb7 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1185,6 +1185,13 @@ GCRuntime::GCRuntime(JSRuntime *rt) : #ifdef JS_GC_ZEAL +void +GCRuntime::getZeal(uint8_t *zeal, uint32_t *frequency) +{ + *zeal = zealMode; + *frequency = zealFrequency; +} + const char *gc::ZealModeHelpText = " Specifies how zealous the garbage collector should be. Values for level:\n" " 0: Normal amount of collection\n" From f717f9af36583b30eb4445a1b94233a2eeded75c Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Jan 2015 17:18:16 -0500 Subject: [PATCH 03/16] Bug 1112934. Tell MGetDOMMember what its result type is so it can do a typed slot load. r=jandem --- js/src/jit/CodeGenerator.cpp | 20 ++++++++++++++++++-- js/src/jit/CodeGenerator.h | 3 ++- js/src/jit/LIR-Common.h | 23 ++++++++++++++++++++--- js/src/jit/LOpcodes.h | 3 ++- js/src/jit/Lowering.cpp | 16 +++++++++++++--- js/src/jit/MIR.h | 1 + 6 files changed, 56 insertions(+), 10 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 279b76fd7024..b29b3d9a35cd 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -9332,9 +9332,9 @@ CodeGenerator::visitGetDOMProperty(LGetDOMProperty *ins) } void -CodeGenerator::visitGetDOMMember(LGetDOMMember *ins) +CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV *ins) { - // It's simple to duplicate visitLoadFixedSlotV here than it is to try to + // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to // use an LLoadFixedSlotV or some subclass of it for this case: that would // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then // we'd have to duplicate a bunch of stuff we now get for free from @@ -9346,6 +9346,22 @@ CodeGenerator::visitGetDOMMember(LGetDOMMember *ins) masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), result); } +void +CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT *ins) +{ + // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to + // use an LLoadFixedSlotT or some subclass of it for this case: that would + // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then + // we'd have to duplicate a bunch of stuff we now get for free from + // MGetDOMProperty. + Register object = ToRegister(ins->object()); + size_t slot = ins->mir()->domMemberSlotIndex(); + AnyRegister result = ToAnyRegister(ins->getDef(0)); + MIRType type = ins->mir()->type(); + + masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)), type, result); +} + void CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 1d346fe3e2a3..e1a5d5be0132 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -305,7 +305,8 @@ class CodeGenerator : public CodeGeneratorSpecific void visitCallInstanceOf(LCallInstanceOf *ins); void visitProfilerStackOp(LProfilerStackOp *lir); void visitGetDOMProperty(LGetDOMProperty *lir); - void visitGetDOMMember(LGetDOMMember *lir); + void visitGetDOMMemberV(LGetDOMMemberV *lir); + void visitGetDOMMemberT(LGetDOMMemberT *lir); void visitSetDOMProperty(LSetDOMProperty *lir); void visitCallDOMNative(LCallDOMNative *lir); void visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index b73a9de8ffca..3420a8763296 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -1750,11 +1750,28 @@ class LGetDOMProperty : public LDOMPropertyInstructionHelper } }; -class LGetDOMMember : public LInstructionHelper +class LGetDOMMemberV : public LInstructionHelper { public: - LIR_HEADER(GetDOMMember); - explicit LGetDOMMember(const LAllocation &object) { + LIR_HEADER(GetDOMMemberV); + explicit LGetDOMMemberV(const LAllocation &object) { + setOperand(0, object); + } + + const LAllocation *object() { + return getOperand(0); + } + + MGetDOMMember *mir() const { + return mir_->toGetDOMMember(); + } +}; + +class LGetDOMMemberT : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(GetDOMMemberT); + explicit LGetDOMMemberT(const LAllocation &object) { setOperand(0, object); } diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 55bd732e8943..9b09b530db57 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -316,7 +316,8 @@ _(InterruptCheckImplicit) \ _(ProfilerStackOp) \ _(GetDOMProperty) \ - _(GetDOMMember) \ + _(GetDOMMemberV) \ + _(GetDOMMemberT) \ _(SetDOMProperty) \ _(CallDOMNative) \ _(IsCallable) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index d6f08c34aa16..f8bce867b79c 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3824,9 +3824,19 @@ LIRGenerator::visitGetDOMMember(MGetDOMMember *ins) // value can in fact change as a result of DOM setters and method calls. MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything, "Member gets had better not alias the world"); - LGetDOMMember *lir = - new(alloc()) LGetDOMMember(useRegisterAtStart(ins->object())); - defineBox(lir, ins); + + MDefinition *obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType_Object); + + MIRType type = ins->type(); + + if (type == MIRType_Value) { + LGetDOMMemberV *lir = new(alloc()) LGetDOMMemberV(useRegisterAtStart(obj)); + defineBox(lir, ins); + } else { + LGetDOMMemberT *lir = new(alloc()) LGetDOMMemberT(useRegisterForTypedLoad(obj, type)); + define(lir, ins); + } } void diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index b4c3e44b8b1e..1fde083aaba1 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10644,6 +10644,7 @@ class MGetDOMMember : public MGetDOMProperty explicit MGetDOMMember(const JSJitInfo *jitinfo) : MGetDOMProperty(jitinfo) { + setResultType(MIRTypeFromValueType(jitinfo->returnType())); } public: From 08a9eb67d53ee299d6a967e8f89415999f0c5c39 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Jan 2015 17:20:36 -0500 Subject: [PATCH 04/16] Bug 1114064. Support AliasNone DOM methods in ion compilation. r=efaust --- js/src/jit/MIR.cpp | 11 +++++++---- js/src/jit/MIR.h | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index f4ba5857e71f..3db3944ccbe2 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1013,11 +1013,10 @@ MCallDOMNative::getAliasSet() const { const JSJitInfo *jitInfo = getJitInfo(); - MOZ_ASSERT(jitInfo->aliasSet() != JSJitInfo::AliasNone); // If we don't know anything about the types of our arguments, we have to // assume that type-coercions can have side-effects, so we need to alias // everything. - if (jitInfo->aliasSet() != JSJitInfo::AliasDOMSets || !jitInfo->isTypedMethodJitInfo()) + if (jitInfo->aliasSet() == JSJitInfo::AliasEverything || !jitInfo->isTypedMethodJitInfo()) return AliasSet::Store(AliasSet::Any); uint32_t argIndex = 0; @@ -1048,8 +1047,12 @@ MCallDOMNative::getAliasSet() const } } - // We checked all the args, and they check out. So we only - // alias DOM mutations. + // We checked all the args, and they check out. So we only alias DOM + // mutations or alias nothing, depending on the alias set in the jitinfo. + if (jitInfo->aliasSet() == JSJitInfo::AliasNone) + return AliasSet::None(); + + MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets); return AliasSet::Load(AliasSet::DOMProperty); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 1fde083aaba1..4e06dddbc368 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3366,11 +3366,11 @@ class MCallDOMNative : public MCall : MCall(target, numActualArgs, false) { // If our jitinfo is not marked movable, that means that our C++ - // implementation is fallible or that we have no hope of ever doing the - // sort of argument analysis that would allow us to detemine that we're - // side-effect-free. In the latter case we wouldn't get DCEd no matter - // what, but for the former case we have to explicitly say that we can't - // be DCEd. + // implementation is fallible or that it never wants to be eliminated or + // coalesced or that we have no hope of ever doing the sort of argument + // analysis that would allow us to detemine that we're side-effect-free. + // In the latter case we wouldn't get DCEd no matter what, but for the + // former two cases we have to explicitly say that we can't be DCEd. if (!getJitInfo()->isMovable) setGuard(); } From b0a97fc2fab93979a0a68daf04e278da8a6712c6 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sat, 3 Jan 2015 23:46:51 -0500 Subject: [PATCH 05/16] Followup to terrence's patch on bug 1112934. AutoLeaveZeal only exists ifdef JS_GC_ZEAL. Needed so code compiles and we can reopen the CLOSED TREE. --- js/src/jsapi-tests/testGCHeapPostBarriers.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp index 61150eaa4726..5b9c765eec89 100644 --- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp +++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp @@ -10,7 +10,9 @@ BEGIN_TEST(testGCHeapPostBarriers) { +#ifdef JS_GC_ZEAL AutoLeaveZeal nozeal(cx); +#endif /* JS_GC_ZEAL */ /* Sanity check - objects start in the nursery and then become tenured. */ JS_GC(cx->runtime()); From fc28efa6e02a985ca0a885df0a88a97c21494b61 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Sun, 4 Jan 2015 08:25:29 +0100 Subject: [PATCH 06/16] Followup to terrence's patch on bug 1112934. Mark AutoLeaveZeal constructor explicit on a CLOSED TREE. --- js/src/jsapi-tests/tests.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 72e69ea95a76..d01113d93802 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -424,7 +424,7 @@ class AutoLeaveZeal uint32_t frequency_; public: - AutoLeaveZeal(JSContext *cx) : cx_(cx) { + explicit AutoLeaveZeal(JSContext *cx) : cx_(cx) { JS_GetGCZeal(cx_, &zeal_, &frequency_); JS_SetGCZeal(cx_, 0, 0); JS::PrepareForFullGC(JS_GetRuntime(cx_)); From d29adb7aca115cae79d679c0c76254b344825253 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:36:46 +0100 Subject: [PATCH 07/16] Bug 1018320 - RequestSync API - patch 1 - webIDL and basic logic, r=ehsan --- b2g/chrome/content/shell.js | 1 + b2g/installer/package-manifest.in | 3 + browser/installer/package-manifest.in | 4 + .../SystemMessagePermissionsChecker.jsm | 1 + dom/moz.build | 1 + dom/requestsync/RequestSync.manifest | 5 + dom/requestsync/RequestSyncManager.js | 80 +++ dom/requestsync/RequestSyncScheduler.js | 101 +++ dom/requestsync/RequestSyncService.jsm | 625 ++++++++++++++++++ dom/requestsync/moz.build | 17 + dom/requestsync/tests/common_app.js | 15 + dom/requestsync/tests/common_basic.js | 172 +++++ dom/requestsync/tests/file_app.sjs | 54 ++ .../tests/file_app.template.webapp | 6 + dom/requestsync/tests/file_basic_app.html | 62 ++ dom/requestsync/tests/file_interface.html | 21 + dom/requestsync/tests/mochitest.ini | 16 + dom/requestsync/tests/test_basic.html | 73 ++ dom/requestsync/tests/test_basic_app.html | 135 ++++ dom/requestsync/tests/test_minInterval.html | 65 ++ dom/requestsync/tests/test_wakeUp.html | 132 ++++ dom/requestsync/tests/test_webidl.html | 86 +++ .../mochitest/general/test_interfaces.html | 4 + dom/webidl/RequestSyncManager.webidl | 27 + dom/webidl/RequestSyncScheduler.webidl | 38 ++ dom/webidl/moz.build | 2 + mobile/android/installer/package-manifest.in | 3 + 27 files changed, 1749 insertions(+) create mode 100644 dom/requestsync/RequestSync.manifest create mode 100644 dom/requestsync/RequestSyncManager.js create mode 100644 dom/requestsync/RequestSyncScheduler.js create mode 100644 dom/requestsync/RequestSyncService.jsm create mode 100644 dom/requestsync/moz.build create mode 100644 dom/requestsync/tests/common_app.js create mode 100644 dom/requestsync/tests/common_basic.js create mode 100644 dom/requestsync/tests/file_app.sjs create mode 100644 dom/requestsync/tests/file_app.template.webapp create mode 100644 dom/requestsync/tests/file_basic_app.html create mode 100644 dom/requestsync/tests/file_interface.html create mode 100644 dom/requestsync/tests/mochitest.ini create mode 100644 dom/requestsync/tests/test_basic.html create mode 100644 dom/requestsync/tests/test_basic_app.html create mode 100644 dom/requestsync/tests/test_minInterval.html create mode 100644 dom/requestsync/tests/test_wakeUp.html create mode 100644 dom/requestsync/tests/test_webidl.html create mode 100644 dom/webidl/RequestSyncManager.webidl create mode 100644 dom/webidl/RequestSyncScheduler.webidl diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 96c7f5f3a495..6c67b80d14b1 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -15,6 +15,7 @@ Cu.import('resource://gre/modules/UserAgentOverrides.jsm'); Cu.import('resource://gre/modules/Keyboard.jsm'); Cu.import('resource://gre/modules/ErrorPage.jsm'); Cu.import('resource://gre/modules/AlertsHelper.jsm'); +Cu.import('resource://gre/modules/RequestSyncService.jsm'); #ifdef MOZ_WIDGET_GONK Cu.import('resource://gre/modules/NetworkStatsService.jsm'); Cu.import('resource://gre/modules/ResourceStatsService.jsm'); diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 9ff9063e6226..6ab59790e423 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -342,6 +342,9 @@ @BINPATH@/components/zipwriter.xpt ; JavaScript components +@BINPATH@/components/RequestSync.manifest +@BINPATH@/components/RequestSyncManager.js +@BINPATH@/components/RequestSyncScheduler.js @BINPATH@/components/ChromeNotifications.js @BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index a2f944bb616d..99dc6dd6bf5f 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -545,6 +545,10 @@ @RESPATH@/components/htmlMenuBuilder.js @RESPATH@/components/htmlMenuBuilder.manifest +@RESPATH@/components/RequestSync.manifest +@RESPATH@/components/RequestSyncManager.js +@RESPATH@/components/RequestSyncScheduler.js + @RESPATH@/components/PermissionSettings.js @RESPATH@/components/PermissionSettings.manifest @RESPATH@/components/ContactManager.js diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index eec8ba58909d..530c3b8143a1 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -84,6 +84,7 @@ this.SystemMessagePermissionsTable = { "push-register": { "push": [] }, + "request-sync": { }, "sms-delivery-success": { "sms": [] }, diff --git a/dom/moz.build b/dom/moz.build index 47bcc6dbb178..4b3545d73154 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -43,6 +43,7 @@ DIRS += [ 'base', 'activities', 'archivereader', + 'requestsync', 'bindings', 'battery', 'browser-element', diff --git a/dom/requestsync/RequestSync.manifest b/dom/requestsync/RequestSync.manifest new file mode 100644 index 000000000000..0f09724b6658 --- /dev/null +++ b/dom/requestsync/RequestSync.manifest @@ -0,0 +1,5 @@ +component {8ee5ab74-15c4-478f-9d32-67627b9f0f1a} RequestSyncScheduler.js +contract @mozilla.org/dom/request-sync-scheduler;1 {8ee5ab74-15c4-478f-9d32-67627b9f0f1a} + +component {e6f55080-e549-4e30-9d00-15f240fb763c} RequestSyncManager.js +contract @mozilla.org/dom/request-sync-manager;1 {e6f55080-e549-4e30-9d00-15f240fb763c} diff --git a/dom/requestsync/RequestSyncManager.js b/dom/requestsync/RequestSyncManager.js new file mode 100644 index 000000000000..8cb5f5ea060e --- /dev/null +++ b/dom/requestsync/RequestSyncManager.js @@ -0,0 +1,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/. */ + +'use strict'; + +function debug(s) { + //dump('DEBUG RequestSyncManager: ' + s + '\n'); +} + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); + +function RequestSyncManager() { + debug('created'); +} + +RequestSyncManager.prototype = { + __proto__: DOMRequestIpcHelper.prototype, + + classDescription: 'RequestSyncManager XPCOM Component', + classID: Components.ID('{e6f55080-e549-4e30-9d00-15f240fb763c}'), + contractID: '@mozilla.org/dom/request-sync-manager;1', + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, + Ci.nsIObserver, + Ci.nsIDOMGlobalPropertyInitializer]), + + _messages: [ "RequestSyncManager:Registrations:Return" ], + + init: function(aWindow) { + debug("init"); + + // DOMRequestIpcHelper.initHelper sets this._window + this.initDOMRequestHelper(aWindow, this._messages); + }, + + sendMessage: function(aMsg, aObj) { + let self = this; + return this.createPromise(function(aResolve, aReject) { + aObj.requestID = + self.getPromiseResolverId({ resolve: aResolve, reject: aReject }); + cpmm.sendAsyncMessage(aMsg, aObj, null, + self._window.document.nodePrincipal); + }); + }, + + registrations: function() { + debug('registrations'); + return this.sendMessage("RequestSyncManager:Registrations", {}); + }, + + receiveMessage: function(aMessage) { + debug('receiveMessage'); + + let req = this.getPromiseResolver(aMessage.data.requestID); + if (!req) { + return; + } + + if ('error' in aMessage.data) { + req.reject(Cu.cloneInto(aMessage.data.error, this._window)); + return; + } + + if ('results' in aMessage.data) { + req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); + return; + } + + req.resolve(); + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncManager]); diff --git a/dom/requestsync/RequestSyncScheduler.js b/dom/requestsync/RequestSyncScheduler.js new file mode 100644 index 000000000000..774636248939 --- /dev/null +++ b/dom/requestsync/RequestSyncScheduler.js @@ -0,0 +1,101 @@ +/* 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/. */ + +'use strict'; + +function debug(s) { + //dump('DEBUG RequestSyncScheduler: ' + s + '\n'); +} + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import('resource://gre/modules/DOMRequestHelper.jsm'); +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + +XPCOMUtils.defineLazyServiceGetter(this, 'cpmm', + '@mozilla.org/childprocessmessagemanager;1', + 'nsIMessageSender'); + +function RequestSyncScheduler() { + debug('created'); +} + +RequestSyncScheduler.prototype = { + __proto__: DOMRequestIpcHelper.prototype, + + classDescription: 'RequestSyncScheduler XPCOM Component', + classID: Components.ID('{8ee5ab74-15c4-478f-9d32-67627b9f0f1a}'), + contractID: '@mozilla.org/dom/request-sync-scheduler;1', + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, + Ci.nsIObserver, + Ci.nsIDOMGlobalPropertyInitializer]), + + _messages: [ 'RequestSync:Register:Return', + 'RequestSync:Unregister:Return', + 'RequestSync:Registrations:Return', + 'RequestSync:Registration:Return' ], + + init: function(aWindow) { + debug('init'); + + // DOMRequestIpcHelper.initHelper sets this._window + this.initDOMRequestHelper(aWindow, this._messages); + }, + + register: function(aTask, aParams) { + debug('register'); + return this.sendMessage('RequestSync:Register', + { task: aTask, params: aParams }); + }, + + unregister: function(aTask) { + debug('unregister'); + return this.sendMessage('RequestSync:Unregister', + { task: aTask }); + }, + + registrations: function() { + debug('registrations'); + return this.sendMessage('RequestSync:Registrations', {}); + }, + + registration: function(aTask) { + debug('registration'); + return this.sendMessage('RequestSync:Registration', + { task: aTask }); + }, + + sendMessage: function(aMsg, aObj) { + let self = this; + return this.createPromise(function(aResolve, aReject) { + aObj.requestID = + self.getPromiseResolverId({ resolve: aResolve, reject: aReject }); + cpmm.sendAsyncMessage(aMsg, aObj, null, + self._window.document.nodePrincipal); + }); + }, + + receiveMessage: function(aMessage) { + debug('receiveMessage'); + + let req = this.getPromiseResolver(aMessage.data.requestID); + if (!req) { + return; + } + + if ('error' in aMessage.data) { + req.reject(Cu.cloneInto(aMessage.data.error, this._window)); + return; + } + + if ('results' in aMessage.data) { + req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); + return; + } + + req.resolve(); + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncScheduler]); diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm new file mode 100644 index 000000000000..dc5e953c8744 --- /dev/null +++ b/dom/requestsync/RequestSyncService.jsm @@ -0,0 +1,625 @@ +/* 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/. */ + +'use strict' + +/* TODO: + - wifi +*/ + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +function debug(s) { + //dump('DEBUG RequestSyncService: ' + s + '\n'); +} + +const RSYNCDB_VERSION = 1; +const RSYNCDB_NAME = "requestSync"; +const RSYNC_MIN_INTERVAL = 100; + +Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.importGlobalProperties(["indexedDB"]); + + +XPCOMUtils.defineLazyServiceGetter(this, "appsService", + "@mozilla.org/AppsService;1", + "nsIAppsService"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsISyncMessageSender"); + +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", + "@mozilla.org/parentprocessmessagemanager;1", + "nsIMessageBroadcaster"); + +XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger", + "@mozilla.org/system-message-internal;1", + "nsISystemMessagesInternal"); + +XPCOMUtils.defineLazyServiceGetter(this, "secMan", + "@mozilla.org/scriptsecuritymanager;1", + "nsIScriptSecurityManager"); + +this.RequestSyncService = { + __proto__: IndexedDBHelper.prototype, + + children: [], + + _messages: [ "RequestSync:Register", "RequestSync:Unregister", + "RequestSync:Registrations", "RequestSync:Registration", + "RequestSyncManager:Registrations" ], + + _pendingOperation: false, + _pendingMessages: [], + + _registrations: {}, + + // Initialization of the RequestSyncService. + init: function() { + debug("init"); + + this._messages.forEach((function(msgName) { + ppmm.addMessageListener(msgName, this); + }).bind(this)); + + Services.obs.addObserver(this, 'xpcom-shutdown', false); + Services.obs.addObserver(this, 'webapps-clear-data', false); + + this.initDBHelper("requestSync", RSYNCDB_VERSION, [RSYNCDB_NAME]); + + // Loading all the data from the database into the _registrations map. + // Any incoming message will be stored and processed when the async + // operation is completed. + + let self = this; + this.dbTxn("readonly", function(aStore) { + aStore.openCursor().onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + self.addRegistration(cursor.value); + cursor.continue(); + } + } + }, + function() { + debug("initialization done"); + }, + function() { + dump("ERROR!! RequestSyncService - Failed to retrieve data from the database.\n"); + }); + }, + + // Shutdown the RequestSyncService. + shutdown: function() { + debug("shutdown"); + + this._messages.forEach((function(msgName) { + ppmm.removeMessageListener(msgName, this); + }).bind(this)); + + Services.obs.removeObserver(this, 'xpcom-shutdown'); + Services.obs.removeObserver(this, 'webapps-clear-data'); + + this.close(); + + // Removing all the registrations will delete the pending timers. + for (let key in this._registrations) { + for (let task in this._registrations[key]) { + this.removeRegistrationInternal(task, key); + } + } + }, + + observe: function(aSubject, aTopic, aData) { + debug("observe"); + + switch (aTopic) { + case 'xpcom-shutdown': + this.shutdown(); + break; + + case 'webapps-clear-data': + this.clearData(aSubject); + break; + + default: + debug("Wrong observer topic: " + aTopic); + break; + } + }, + + // When an app is uninstalled, we have to clean all its tasks. + clearData: function(aData) { + debug('clearData'); + + if (!aData) { + return; + } + + let params = + aData.QueryInterface(Ci.mozIApplicationClearPrivateDataParams); + if (!params) { + return; + } + + // At this point we don't have the origin, so we cannot create the full + // key. Using the partial one is enough to detect the uninstalled app. + var partialKey = params.appId + '|' + params.browserOnly + '|'; + var dbKeys = []; + + for (let key in this._registrations) { + if (key.indexOf(partialKey) != 0) { + continue; + } + + for (let task in this._registrations[key]) { + dbKeys = this._registrations[key][task].dbKey; + this.removeRegistrationInternal(task, key); + } + } + + if (dbKeys.length == 0) { + return; + } + + // Remove the tasks from the database. + this.dbTxn('readwrite', function(aStore) { + for (let i = 0; i < dbKeys.length; ++i) { + aStore.delete(dbKeys[i]); + } + }, + function() { + debug("ClearData completed"); + }, function() { + debug("ClearData failed"); + }); + }, + + // Creation of the schema for the database. + upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) { + debug('updateSchema'); + aDb.createObjectStore(RSYNCDB_NAME, { autoIncrement: true }); + }, + + // This method generates the key for the indexedDB object storage. + principalToKey: function(aPrincipal) { + return aPrincipal.appId + '|' + + aPrincipal.isInBrowserElement + '|' + + aPrincipal.origin; + }, + + // Add a task to the _registrations map and create the timer if it's needed. + addRegistration: function(aObj) { + debug('addRegistration'); + + let key = this.principalToKey(aObj.principal); + if (!(key in this._registrations)) { + this._registrations[key] = {}; + } + + this.scheduleTimer(aObj); + this._registrations[key][aObj.data.task] = aObj; + }, + + // Remove a task from the _registrations map and delete the timer if it's + // needed. It also checks if the principal is correct before doing the real + // operation. + removeRegistration: function(aTaskName, aKey, aPrincipal) { + debug('removeRegistration'); + + if (!(aKey in this._registrations) || + !(aTaskName in this._registrations[aKey])) { + return false; + } + + // Additional security check. + if (!aPrincipal.equals(this._registrations[aKey][aTaskName].principal)) { + return false; + } + + this.removeRegistrationInternal(aTaskName, aKey); + return true; + }, + + removeRegistrationInternal: function(aTaskName, aKey) { + debug('removeRegistrationInternal'); + + if (this._registrations[aKey][aTaskName].timer) { + this._registrations[aKey][aTaskName].timer.cancel(); + } + + delete this._registrations[aKey][aTaskName]; + + // Lets remove the key in case there are not tasks registered. + for (var key in this._registrations[aKey]) { + return; + } + delete this._registrations[aKey]; + }, + + // The communication from the exposed objects and the service is done using + // messages. This function receives and processes them. + receiveMessage: function(aMessage) { + debug("receiveMessage"); + + // We cannot process this request now. + if (this._pendingOperation) { + this._pendingMessages.push(aMessage); + return; + } + + // The principal is used to validate the message. + if (!aMessage.principal) { + return; + } + + let uri = Services.io.newURI(aMessage.principal.origin, null, null); + + let principal; + try { + principal = secMan.getAppCodebasePrincipal(uri, + aMessage.principal.appId, aMessage.principal.isInBrowserElement); + } catch(e) { + return; + } + + if (!principal) { + return; + } + + switch (aMessage.name) { + case "RequestSync:Register": + this.register(aMessage.target, aMessage.data, principal); + break; + + case "RequestSync:Unregister": + this.unregister(aMessage.target, aMessage.data, principal); + break; + + case "RequestSync:Registrations": + this.registrations(aMessage.target, aMessage.data, principal); + break; + + case "RequestSync:Registration": + this.registration(aMessage.target, aMessage.data, principal); + break; + + case "RequestSyncManager:Registrations": + this.managerRegistrations(aMessage.target, aMessage.data, principal); + break; + + default: + debug("Wrong message: " + aMessage.name); + break; + } + }, + + // Basic validation. + validateRegistrationParams: function(aParams) { + if (aParams === null) { + return false; + } + + // We must have a page. + if (!("wakeUpPage" in aParams) || + aParams.wakeUpPage.length == 0) { + return false; + } + + let minInterval = RSYNC_MIN_INTERVAL; + try { + minInterval = Services.prefs.getIntPref("dom.requestSync.minInterval"); + } catch(e) {} + + if (!("minInterval" in aParams) || + aParams.minInterval < minInterval) { + return false; + } + + return true; + }, + + // Registration of a new task. + register: function(aTarget, aData, aPrincipal) { + debug("register"); + + if (!this.validateRegistrationParams(aData.params)) { + aTarget.sendAsyncMessage("RequestSync:Register:Return", + { requestID: aData.requestID, + error: "ParamsError" } ); + return; + } + + let key = this.principalToKey(aPrincipal); + if (key in this._registrations && + aData.task in this._registrations[key]) { + // if this task already exists we overwrite it. + this.removeRegistrationInternal(aData.task, key); + } + + // This creates a RequestTaskFull object. + aData.params.task = aData.task; + aData.params.lastSync = 0; + aData.params.principal = aPrincipal; + + let dbKey = aData.task + "|" + + aPrincipal.appId + '|' + + aPrincipal.isInBrowserElement + '|' + + aPrincipal.origin; + + let data = { principal: aPrincipal, + dbKey: dbKey, + data: aData.params, + active: true, + timer: null }; + + let self = this; + this.dbTxn('readwrite', function(aStore) { + aStore.put(data, data.dbKey); + }, + function() { + self.addRegistration(data); + aTarget.sendAsyncMessage("RequestSync:Register:Return", + { requestID: aData.requestID }); + }, + function() { + aTarget.sendAsyncMessage("RequestSync:Register:Return", + { requestID: aData.requestID, + error: "IndexDBError" } ); + }); + }, + + // Unregister a task. + unregister: function(aTarget, aData, aPrincipal) { + debug("unregister"); + + let key = this.principalToKey(aPrincipal); + if (!(key in this._registrations) || + !(aData.task in this._registrations[key])) { + aTarget.sendAsyncMessage("RequestSync:Unregister:Return", + { requestID: aData.requestID, + error: "UnknownTaskError" }); + return; + } + + let dbKey = this._registrations[key][aData.task].dbKey; + this.removeRegistration(aData.task, key, aPrincipal); + + let self = this; + this.dbTxn('readwrite', function(aStore) { + aStore.delete(dbKey); + }, + function() { + aTarget.sendAsyncMessage("RequestSync:Unregister:Return", + { requestID: aData.requestID }); + }, + function() { + aTarget.sendAsyncMessage("RequestSync:Unregister:Return", + { requestID: aData.requestID, + error: "IndexDBError" } ); + }); + }, + + // Get the list of registered tasks for this principal. + registrations: function(aTarget, aData, aPrincipal) { + debug("registrations"); + + let results = []; + let key = this.principalToKey(aPrincipal); + if (key in this._registrations) { + for (let i in this._registrations[key]) { + results.push(this.createPartialTaskObject( + this._registrations[key][i].data)); + } + } + + aTarget.sendAsyncMessage("RequestSync:Registrations:Return", + { requestID: aData.requestID, + results: results }); + }, + + // Get a particular registered task for this principal. + registration: function(aTarget, aData, aPrincipal) { + debug("registration"); + + let results = null; + let key = this.principalToKey(aPrincipal); + if (key in this._registrations && + aData.task in this._registrations[key]) { + results = this.createPartialTaskObject( + this._registrations[key][aData.task].data); + } + + aTarget.sendAsyncMessage("RequestSync:Registration:Return", + { requestID: aData.requestID, + results: results }); + }, + + // Get the list of the registered tasks. + managerRegistrations: function(aTarget, aData, aPrincipal) { + debug("managerRegistrations"); + + let results = []; + for (var key in this._registrations) { + for (var task in this._registrations[key]) { + results.push( + this.createFullTaskObject(this._registrations[key][task].data)); + } + } + + aTarget.sendAsyncMessage("RequestSyncManager:Registrations:Return", + { requestID: aData.requestID, + results: results }); + }, + + // We cannot expose the full internal object to content but just a subset. + // This method creates this subset. + createPartialTaskObject: function(aObj) { + return { task: aObj.task, + lastSync: aObj.lastSync, + oneShot: aObj.oneShot, + minInterval: aObj.minInterval, + wakeUpPage: aObj.wakeUpPage, + wifiOnly: aObj.wifiOnly, + data: aObj.data }; + }, + + createFullTaskObject: function(aObj) { + let obj = this.createPartialTaskObject(aObj); + + obj.app = { manifestURL: '', + origin: aObj.principal.origin, + isInBrowserElement: aObj.principal.isInBrowserElement }; + + let app = appsService.getAppByLocalId(aObj.principal.appId); + if (app) { + obj.app.manifestURL = app.manifestURL; + } + + return obj; + }, + + // Creation of the timer for a particular task object. + scheduleTimer: function(aObj) { + debug("scheduleTimer"); + + // A registration can be already inactive if it was 1 shot. + if (aObj.active) { + aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + let self = this; + aObj.timer.initWithCallback(function() { self.timeout(aObj); }, + aObj.data.minInterval * 1000, + Ci.nsITimer.TYPE_ONE_SHOT); + } + }, + + timeout: function(aObj) { + debug("timeout"); + + let app = appsService.getAppByLocalId(aObj.principal.appId); + if (!app) { + dump("ERROR!! RequestSyncService - Failed to retrieve app data from a principal.\n"); + aObj.active = false; + this.updateObjectInDB(aObj); + return; + } + + let manifestURL = Services.io.newURI(app.manifestURL, null, null); + let pageURL = Services.io.newURI(aObj.data.wakeUpPage, null, aObj.principal.URI); + + // Maybe need to be rescheduled? + if (this.needRescheduling('request-sync', manifestURL, pageURL)) { + this.scheduleTimer(aObj); + return; + } + + aObj.timer = null; + + if (!manifestURL || !pageURL) { + dump("ERROR!! RequestSyncService - Failed to create URI for the page or the manifest\n"); + aObj.active = false; + this.updateObjectInDB(aObj); + return; + } + + // Sending the message. + systemMessenger.sendMessage('request-sync', + this.createPartialTaskObject(aObj.data), + pageURL, manifestURL); + + // One shot? Then this is not active. + aObj.active = !aObj.data.oneShot; + aObj.data.lastSync = new Date(); + + let self = this; + this.updateObjectInDB(aObj, function() { + // SchedulerTimer creates a timer and a nsITimer cannot be cloned. This + // is the reason why this operation has to be done after storing the aObj + // into IDB. + if (!aObj.data.oneShot) { + self.scheduleTimer(aObj); + } + }); + }, + + needRescheduling: function(aMessageName, aManifestURL, aPageURL) { + let hasPendingMessages = + cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages", + { type: aMessageName, + pageURL: aPageURL.spec, + manifestURL: aManifestURL.spec })[0]; + + debug("Pending messages: " + hasPendingMessages); + + if (hasPendingMessages) { + return true; + } + + // FIXME: other reasons? + + return false; + }, + + // Update the object into the database. + updateObjectInDB: function(aObj, aCb) { + debug("updateObjectInDB"); + + this.dbTxn('readwrite', function(aStore) { + aStore.put(aObj, aObj.dbKey); + }, + function() { + if (aCb) { + aCb(); + } + debug("UpdateObjectInDB completed"); + }, function() { + debug("UpdateObjectInDB failed"); + }); + }, + + pendingOperationStarted: function() { + debug('pendingOperationStarted'); + this._pendingOperation = true; + }, + + pendingOperationDone: function() { + debug('pendingOperationDone'); + + this._pendingOperation = false; + + // managing the pending messages now that the initialization is completed. + while (this._pendingMessages.length) { + this.receiveMessage(this._pendingMessages.shift()); + } + }, + + // This method creates a transaction and runs callbacks. Plus it manages the + // pending operations system. + dbTxn: function(aType, aCb, aSuccessCb, aErrorCb) { + debug('dbTxn'); + + this.pendingOperationStarted(); + + let self = this; + this.newTxn(aType, RSYNCDB_NAME, function(aTxn, aStore) { + aCb(aStore); + }, + function() { + self.pendingOperationDone(); + aSuccessCb(); + }, + function() { + self.pendingOperationDone(); + aErrorCb(); + }); + } +} + +RequestSyncService.init(); + +this.EXPORTED_SYMBOLS = [""]; diff --git a/dom/requestsync/moz.build b/dom/requestsync/moz.build new file mode 100644 index 000000000000..cf0954313cab --- /dev/null +++ b/dom/requestsync/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] + +EXTRA_COMPONENTS += [ + 'RequestSync.manifest', + 'RequestSyncManager.js', + 'RequestSyncScheduler.js', +] + +EXTRA_JS_MODULES += [ + 'RequestSyncService.jsm', +] diff --git a/dom/requestsync/tests/common_app.js b/dom/requestsync/tests/common_app.js new file mode 100644 index 000000000000..e2371ed0dc43 --- /dev/null +++ b/dom/requestsync/tests/common_app.js @@ -0,0 +1,15 @@ +function is(a, b, msg) { + alert((a === b ? 'OK' : 'KO') + ' ' + msg) +} + +function ok(a, msg) { + alert((a ? 'OK' : 'KO')+ ' ' + msg) +} + +function cbError() { + alert('KO error'); +} + +function finish() { + alert('DONE'); +} diff --git a/dom/requestsync/tests/common_basic.js b/dom/requestsync/tests/common_basic.js new file mode 100644 index 000000000000..3301c4135b67 --- /dev/null +++ b/dom/requestsync/tests/common_basic.js @@ -0,0 +1,172 @@ +function test_registerFailure() { + ok("sync" in navigator, "navigator.sync exists"); + + navigator.sync.register().then( + function() { + ok(false, "navigator.sync.register() throws without a task name"); + }, function() { + ok(true, "navigator.sync.register() throws without a task name"); + }) + + .then(function() { + return navigator.sync.register(42); + }).then(function() { + ok(false, "navigator.sync.register() throws without a string task name"); + }, function() { + ok(true, "navigator.sync.register() throws without a string task name"); + }) + + .then(function() { + return navigator.sync.register('foobar'); + }).then(function() { + ok(false, "navigator.sync.register() throws without a param dictionary"); + }, function() { + ok(true, "navigator.sync.register() throws without a param dictionary"); + }) + + .then(function() { + return navigator.sync.register('foobar', 42); + }).then(function() { + ok(false, "navigator.sync.register() throws without a real dictionary"); + }, function() { + ok(true, "navigator.sync.register() throws without a real dictionary"); + }) + + .then(function() { + return navigator.sync.register('foobar', {}); + }).then(function() { + ok(false, "navigator.sync.register() throws without a minInterval and wakeUpPage"); + }, function() { + ok(true, "navigator.sync.register() throws without a minInterval and wakeUpPage"); + }) + + .then(function() { + return navigator.sync.register('foobar', { minInterval: 100 }); + }).then(function() { + ok(false, "navigator.sync.register() throws without a wakeUpPage"); + }, function() { + ok(true, "navigator.sync.register() throws without a wakeUpPage"); + }) + + .then(function() { + return navigator.sync.register('foobar', { wakeUpPage: 100 }); + }).then(function() { + ok(false, "navigator.sync.register() throws without a minInterval"); + }, function() { + ok(true, "navigator.sync.register() throws without a minInterval"); + }) + + .then(function() { + runTests(); + }); +} + +function genericError() { + ok(false, "Some promise failed"); +} + +function test_register() { + navigator.sync.register('foobar', { minInterval: 5, wakeUpPage:'/' }).then( + function() { + ok(true, "navigator.sync.register() worked!"); + runTests(); + }, genericError); +} + +function test_unregister() { + navigator.sync.unregister('foobar').then( + function() { + ok(true, "navigator.sync.unregister() worked!"); + runTests(); + }, genericError); +} + +function test_unregisterDuplicate() { + navigator.sync.unregister('foobar').then( + genericError, + function(error) { + ok(true, "navigator.sync.unregister() should throw if the task doesn't exist."); + ok(error, "UnknownTaskError", "Duplicate unregistration error is correct"); + runTests(); + }); +} + +function test_registrationEmpty() { + navigator.sync.registration('bar').then( + function(results) { + is(results, null, "navigator.sync.registration() should return null."); + runTests(); + }, + genericError); +} + +function test_registration() { + navigator.sync.registration('foobar').then( + function(results) { + is(results.task, 'foobar', "navigator.sync.registration().task is correct"); + ok("lastSync" in results, "navigator.sync.registration().lastSync is correct"); + is(results.oneShot, true, "navigator.sync.registration().oneShot is correct"); + is(results.minInterval, 5, "navigator.sync.registration().minInterval is correct"); + ok("wakeUpPage" in results, "navigator.sync.registration().wakeUpPage is correct"); + ok("wifiOnly" in results, "navigator.sync.registration().wifiOnly is correct"); + ok("data" in results, "navigator.sync.registration().data is correct"); + ok(!("app" in results), "navigator.sync.registrations().app is correct"); + runTests(); + }, + genericError); +} + +function test_registrationsEmpty() { + navigator.sync.registrations().then( + function(results) { + is(results.length, 0, "navigator.sync.registrations() should return an empty array."); + runTests(); + }, + genericError); +} + +function test_registrations() { + navigator.sync.registrations().then( + function(results) { + is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); + is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct"); + ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct"); + is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct"); + is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct"); + ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct"); + ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct"); + ok("data" in results[0], "navigator.sync.registrations()[0].data is correct"); + ok(!("app" in results[0]), "navigator.sync.registrations()[0].app is correct"); + runTests(); + }, + genericError); +} + +function test_managerRegistrationsEmpty() { + navigator.syncManager.registrations().then( + function(results) { + is(results.length, 0, "navigator.syncManager.registrations() should return an empty array."); + runTests(); + }, + genericError); +} + +function test_managerRegistrations() { + navigator.syncManager.registrations().then( + function(results) { + is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); + is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct"); + ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct"); + is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct"); + is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct"); + ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct"); + ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct"); + ok("data" in results[0], "navigator.sync.registrations()[0].data is correct"); + ok("app" in results[0], "navigator.sync.registrations()[0].app is correct"); + ok("manifestURL" in results[0].app, "navigator.sync.registrations()[0].app.manifestURL is correct"); + is(results[0].app.origin, 'http://mochi.test:8888', "navigator.sync.registrations()[0].app.origin is correct"); + is(results[0].app.isInBrowserElement, false, "navigator.sync.registrations()[0].app.isInBrowserElement is correct"); + runTests(); + }, + genericError); +} diff --git a/dom/requestsync/tests/file_app.sjs b/dom/requestsync/tests/file_app.sjs new file mode 100644 index 000000000000..4aeedb25f64b --- /dev/null +++ b/dom/requestsync/tests/file_app.sjs @@ -0,0 +1,54 @@ +var gBasePath = "tests/dom/requestsync/tests/"; +var gTemplate = "file_app.template.webapp"; + +function handleRequest(request, response) { + var query = getQuery(request); + + var testToken = ''; + if ('testToken' in query) { + testToken = query.testToken; + } + + var template = gBasePath + gTemplate; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken)); +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let (str = {}) { + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + } + cis.close(); + return data; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} + diff --git a/dom/requestsync/tests/file_app.template.webapp b/dom/requestsync/tests/file_app.template.webapp new file mode 100644 index 000000000000..49a6158b9bcb --- /dev/null +++ b/dom/requestsync/tests/file_app.template.webapp @@ -0,0 +1,6 @@ +{ + "name": "Really Rapid Release (hosted)", + "description": "Updated even faster than Firefox, just to annoy slashdotters.", + "launch_path": "/tests/dom/requestsync/tests/TESTTOKEN", + "icons": { "128": "default_icon" } +} diff --git a/dom/requestsync/tests/file_basic_app.html b/dom/requestsync/tests/file_basic_app.html new file mode 100644 index 000000000000..28af6a481ee4 --- /dev/null +++ b/dom/requestsync/tests/file_basic_app.html @@ -0,0 +1,62 @@ + + + + + + + + +
+ + + diff --git a/dom/requestsync/tests/file_interface.html b/dom/requestsync/tests/file_interface.html new file mode 100644 index 000000000000..fbc40f2300b3 --- /dev/null +++ b/dom/requestsync/tests/file_interface.html @@ -0,0 +1,21 @@ + + + + + + + +
+ + + diff --git a/dom/requestsync/tests/mochitest.ini b/dom/requestsync/tests/mochitest.ini new file mode 100644 index 000000000000..b1071e0e76c1 --- /dev/null +++ b/dom/requestsync/tests/mochitest.ini @@ -0,0 +1,16 @@ +[DEFAULT] +skip-if = e10s +support-files = + file_app.template.webapp + file_app.sjs + file_basic_app.html + common_app.js + common_basic.js + +[test_webidl.html] +[test_minInterval.html] +[test_basic.html] +[test_basic_app.html] +run-if = buildapp != 'b2g' +[test_wakeUp.html] +run-if = buildapp == 'b2g' && toolkit == 'gonk' diff --git a/dom/requestsync/tests/test_basic.html b/dom/requestsync/tests/test_basic.html new file mode 100644 index 000000000000..ddab2a297a00 --- /dev/null +++ b/dom/requestsync/tests/test_basic.html @@ -0,0 +1,73 @@ + + + + + Test for RequestSync basic use + + + + + + + + diff --git a/dom/requestsync/tests/test_basic_app.html b/dom/requestsync/tests/test_basic_app.html new file mode 100644 index 000000000000..21437ae3eb22 --- /dev/null +++ b/dom/requestsync/tests/test_basic_app.html @@ -0,0 +1,135 @@ + + + + + Test for requestSync - basic operations in app + + + + + +
+ + + diff --git a/dom/requestsync/tests/test_minInterval.html b/dom/requestsync/tests/test_minInterval.html new file mode 100644 index 000000000000..1c877078cb04 --- /dev/null +++ b/dom/requestsync/tests/test_minInterval.html @@ -0,0 +1,65 @@ + + + + + Test for RequestSync minInterval pref + + + + + + + diff --git a/dom/requestsync/tests/test_wakeUp.html b/dom/requestsync/tests/test_wakeUp.html new file mode 100644 index 000000000000..f19c4f3a1503 --- /dev/null +++ b/dom/requestsync/tests/test_wakeUp.html @@ -0,0 +1,132 @@ + + + + + Test for requestSync - wakeUp + + + + + +
+ + + diff --git a/dom/requestsync/tests/test_webidl.html b/dom/requestsync/tests/test_webidl.html new file mode 100644 index 000000000000..46bed4590119 --- /dev/null +++ b/dom/requestsync/tests/test_webidl.html @@ -0,0 +1,86 @@ + + + + + Test for RequestSync interfaces + + + + + + + diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index b49076fa671c..809e0f301163 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -1209,6 +1209,10 @@ var interfaceNamesInGlobalScope = "SVGZoomAndPan", // IMPORTANT: Do not change this list without review from a DOM peer! "SVGZoomEvent", +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "RequestSyncScheduler", b2g: true, pref: "dom.requestSync.enabled" }, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "Telephony", b2g: true, pref: "dom.telephony.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/RequestSyncManager.webidl b/dom/webidl/RequestSyncManager.webidl new file mode 100644 index 000000000000..701e80b10f60 --- /dev/null +++ b/dom/webidl/RequestSyncManager.webidl @@ -0,0 +1,27 @@ +/* -*- 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/. + */ + +// Rappresentation of the app in the RequestTaskFull. +dictionary RequestTaskApp { + USVString origin; + USVString manifestURL; + boolean isInBrowserElement; +}; + +// Like a normal task, but with info about the app. +dictionary RequestTaskFull : RequestTask { + RequestTaskApp app; +}; + +[NavigatorProperty="syncManager", + AvailableIn=CertifiedApps, + Pref="dom.requestSync.enabled", + CheckPermissions="requestsync-manager", + JSImplementation="@mozilla.org/dom/request-sync-manager;1"] +// This interface will be used only by the B2G SystemApp +interface RequestSyncManager { + Promise> registrations(); +}; diff --git a/dom/webidl/RequestSyncScheduler.webidl b/dom/webidl/RequestSyncScheduler.webidl new file mode 100644 index 000000000000..3df115bff465 --- /dev/null +++ b/dom/webidl/RequestSyncScheduler.webidl @@ -0,0 +1,38 @@ +/* -*- 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/. + */ + +// This is the dictionary for the creation of a new task. +dictionary RequestTaskParams { + required USVString wakeUpPage; + boolean oneShot = true; + required long minInterval; // in seconds >= dom.requestSync.minInterval or 100 secs + boolean wifiOnly = true; + any data = null; +}; + + +// This is the dictionary you can have back from registration{s}(). +dictionary RequestTask : RequestTaskParams { + USVString task = ""; + + // Last synchonization date.. maybe it's useful to know. + DOMTimeStamp lastSync; +}; + +[NavigatorProperty="sync", + AvailableIn=CertifiedApps, + Pref="dom.requestSync.enabled", + JSImplementation="@mozilla.org/dom/request-sync-scheduler;1"] +interface RequestSyncScheduler { + + Promise register(USVString task, + optional RequestTaskParams params); + Promise unregister(USVString task); + + // Useful methods to get registrations + Promise> registrations(); + Promise registration(USVString task); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index c386d8693882..d62114d22454 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -341,6 +341,8 @@ WEBIDL_FILES = [ 'Range.webidl', 'Rect.webidl', 'Request.webidl', + 'RequestSyncManager.webidl', + 'RequestSyncScheduler.webidl', 'ResourceStats.webidl', 'ResourceStatsManager.webidl', 'Response.webidl', diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index ebaa9d373d85..4b557b0c3cf1 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -286,6 +286,9 @@ @BINPATH@/components/zipwriter.xpt ; JavaScript components +@BINPATH@/components/RequestSync.manifest +@BINPATH@/components/RequestSyncManager.js +@BINPATH@/components/RequestSyncScheduler.js @BINPATH@/components/ChromeNotifications.js @BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest From c01dc5fa144f53ff5678bee607db9f5e011fef51 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:36:53 +0100 Subject: [PATCH 08/16] Bug 1018320 - RequestSync API - patch 2 - Wifi Only, r=ehsan --- dom/requestsync/RequestSyncService.jsm | 100 ++++++++++++++++----- dom/requestsync/RequestSyncWifiService.cpp | 67 ++++++++++++++ dom/requestsync/RequestSyncWifiService.h | 43 +++++++++ dom/requestsync/moz.build | 13 +++ layout/build/nsLayoutStatics.cpp | 5 ++ 5 files changed, 204 insertions(+), 24 deletions(-) create mode 100644 dom/requestsync/RequestSyncWifiService.cpp create mode 100644 dom/requestsync/RequestSyncWifiService.h diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index dc5e953c8744..485b1faf452e 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -4,10 +4,6 @@ 'use strict' -/* TODO: - - wifi -*/ - const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; function debug(s) { @@ -58,6 +54,8 @@ this.RequestSyncService = { _registrations: {}, + _wifi: false, + // Initialization of the RequestSyncService. init: function() { debug("init"); @@ -68,6 +66,7 @@ this.RequestSyncService = { Services.obs.addObserver(this, 'xpcom-shutdown', false); Services.obs.addObserver(this, 'webapps-clear-data', false); + Services.obs.addObserver(this, 'wifi-state-changed', false); this.initDBHelper("requestSync", RSYNCDB_VERSION, [RSYNCDB_NAME]); @@ -103,15 +102,16 @@ this.RequestSyncService = { Services.obs.removeObserver(this, 'xpcom-shutdown'); Services.obs.removeObserver(this, 'webapps-clear-data'); + Services.obs.removeObserver(this, 'wifi-state-changed'); this.close(); // Removing all the registrations will delete the pending timers. - for (let key in this._registrations) { - for (let task in this._registrations[key]) { - this.removeRegistrationInternal(task, key); - } - } + let self = this; + this.forEachRegistration(function(aObj) { + let key = self.principalToKey(aObj.principal); + self.removeRegistrationInternal(aObj.data.task, key); + }); }, observe: function(aSubject, aTopic, aData) { @@ -123,8 +123,12 @@ this.RequestSyncService = { break; case 'webapps-clear-data': - this.clearData(aSubject); - break; + this.clearData(aSubject); + break; + + case 'wifi-state-changed': + this.wifiStateChanged(aSubject == 'enabled'); + break; default: debug("Wrong observer topic: " + aTopic); @@ -444,12 +448,10 @@ this.RequestSyncService = { debug("managerRegistrations"); let results = []; - for (var key in this._registrations) { - for (var task in this._registrations[key]) { - results.push( - this.createFullTaskObject(this._registrations[key][task].data)); - } - } + let self = this; + this.forEachRegistration(function(aObj) { + results.push(self.createFullTaskObject(aObj.data)); + }); aTarget.sendAsyncMessage("RequestSyncManager:Registrations:Return", { requestID: aData.requestID, @@ -488,14 +490,21 @@ this.RequestSyncService = { debug("scheduleTimer"); // A registration can be already inactive if it was 1 shot. - if (aObj.active) { - aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - let self = this; - aObj.timer.initWithCallback(function() { self.timeout(aObj); }, - aObj.data.minInterval * 1000, - Ci.nsITimer.TYPE_ONE_SHOT); + if (!aObj.active) { + return; } + + // WifiOnly check. + if (aObj.data.wifiOnly && !this._wifi) { + return; + } + + aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + let self = this; + aObj.timer.initWithCallback(function() { self.timeout(aObj); }, + aObj.data.minInterval * 1000, + Ci.nsITimer.TYPE_ONE_SHOT); }, timeout: function(aObj) { @@ -617,6 +626,49 @@ this.RequestSyncService = { self.pendingOperationDone(); aErrorCb(); }); + }, + + forEachRegistration: function(aCb) { + // This method is used also to remove registations from the map, so we have + // to make a new list and let _registations free to be used. + let list = []; + for (var key in this._registrations) { + for (var task in this._registrations[key]) { + list.push(this._registrations[key][task]); + } + } + + for (var i = 0; i < list.length; ++i) { + aCb(list[i]); + } + }, + + wifiStateChanged: function(aEnabled) { + debug("onWifiStateChanged"); + this._wifi = aEnabled; + + if (!this._wifi) { + // Disable all the wifiOnly tasks. + this.forEachRegistration(function(aObj) { + if (aObj.data.wifiOnly && aObj.timer) { + aObj.timer.cancel(); + aObj.timer = null; + } + }); + return; + } + + // Enable all the tasks. + let self = this; + this.forEachRegistration(function(aObj) { + if (aObj.active && !aObj.timer) { + if (!aObj.data.wifiOnly) { + dump("ERROR - Found a disabled task that is not wifiOnly."); + } + + self.scheduleTimer(aObj); + } + }); } } diff --git a/dom/requestsync/RequestSyncWifiService.cpp b/dom/requestsync/RequestSyncWifiService.cpp new file mode 100644 index 000000000000..0b7c9968a70e --- /dev/null +++ b/dom/requestsync/RequestSyncWifiService.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* 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 "RequestSyncWifiService.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "nsIObserverService.h" + +namespace mozilla { +namespace dom { + +using namespace hal; + +NS_IMPL_ISUPPORTS0(RequestSyncWifiService) + +namespace { + +StaticRefPtr sService; + +} // anonymous namespace + +/* static */ void +RequestSyncWifiService::Init() +{ + nsRefPtr service = GetInstance(); + if (!service) { + NS_WARNING("Failed to initialize RequestSyncWifiService."); + } +} + +/* static */ already_AddRefed +RequestSyncWifiService::GetInstance() +{ + if (!sService) { + sService = new RequestSyncWifiService(); + hal::RegisterNetworkObserver(sService); + ClearOnShutdown(&sService); + } + + nsRefPtr service = sService.get(); + return service.forget(); +} + +void +RequestSyncWifiService::Notify(const hal::NetworkInformation& aNetworkInfo) +{ + bool isWifi = aNetworkInfo.isWifi(); + if (isWifi == mIsWifi) { + return; + } + + mIsWifi = isWifi; + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "wifi-state-changed", + mIsWifi ? MOZ_UTF16("enabled") : + MOZ_UTF16("disabled")); + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/requestsync/RequestSyncWifiService.h b/dom/requestsync/RequestSyncWifiService.h new file mode 100644 index 000000000000..902104b0da36 --- /dev/null +++ b/dom/requestsync/RequestSyncWifiService.h @@ -0,0 +1,43 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* 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_RequestSyncWifiService_h +#define mozilla_dom_RequestSyncWifiService_h + +#include "mozilla/dom/network/Types.h" +#include "mozilla/Hal.h" +#include "nsIObserver.h" + +namespace mozilla { +namespace dom { + +class RequestSyncWifiService MOZ_FINAL : public nsISupports + , public NetworkObserver +{ +public: + NS_DECL_ISUPPORTS + + static void Init(); + + static already_AddRefed GetInstance(); + + void Notify(const hal::NetworkInformation& aNetworkInfo); + +private: + RequestSyncWifiService() + : mIsWifi(false) + {} + + ~RequestSyncWifiService() + {} + + bool mIsWifi; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_RequestSyncWifiService_h diff --git a/dom/requestsync/moz.build b/dom/requestsync/moz.build index cf0954313cab..481da0c886b0 100644 --- a/dom/requestsync/moz.build +++ b/dom/requestsync/moz.build @@ -6,6 +6,10 @@ MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +EXPORTS.mozilla.dom += [ + 'RequestSyncWifiService.h', +] + EXTRA_COMPONENTS += [ 'RequestSync.manifest', 'RequestSyncManager.js', @@ -15,3 +19,12 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'RequestSyncService.jsm', ] + +SOURCES += [ + 'RequestSyncWifiService.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FAIL_ON_WARNINGS = True +FINAL_LIBRARY = 'xul' diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 67c421559d38..d56828f83fbe 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -64,6 +64,7 @@ #include "ActiveLayerTracker.h" #include "CounterStyleManager.h" #include "FrameLayerBuilder.h" +#include "mozilla/dom/RequestSyncWifiService.h" #include "AudioChannelService.h" #include "mozilla/dom/DataStoreService.h" @@ -300,6 +301,10 @@ nsLayoutStatics::Initialize() IMEStateManager::Init(); +#ifdef MOZ_B2G + RequestSyncWifiService::Init(); +#endif + return NS_OK; } From fd2d4032688f9dc89f5cefc6ae380954392f2e11 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:36:58 +0100 Subject: [PATCH 09/16] Bug 1018320 - RequestSync API - patch 3 - a Promise return value from sendAsyncMessage, r=fabrice --- dom/messages/SystemMessageInternal.js | 94 +++++++++++++++++-- dom/messages/SystemMessageManager.js | 14 +-- .../interfaces/nsISystemMessagesInternal.idl | 7 +- 3 files changed, 97 insertions(+), 18 deletions(-) diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index f7e160ff61b6..b610943299c1 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -46,6 +46,7 @@ const kMessages =["SystemMessageManager:GetPendingMessages", "SystemMessageManager:Message:Return:OK", "SystemMessageManager:AskReadyToRegister", "SystemMessageManager:HandleMessagesDone", + "SystemMessageManager:HandleMessageDone", "child-process-shutdown"] function debug(aMsg) { @@ -84,6 +85,8 @@ function SystemMessageInternal() { this._configurators = {}; + this._pendingPromises = new Map(); + Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "webapps-registry-start", false); Services.obs.addObserver(this, "webapps-registry-ready", false); @@ -176,6 +179,14 @@ SystemMessageInternal.prototype = { }, sendMessage: function(aType, aMessage, aPageURI, aManifestURI, aExtra) { + return new Promise((resolve, reject) => { + this.sendMessageInternal(aType, aMessage, aPageURI, aManifestURI, aExtra, + aResolve, aReject); + }); + }, + + sendMessageInternal: function(aType, aMessage, aPageURI, aManifestURI, + aExtra, aResolvePromiseCb, aRejectPromiseCb) { // Buffer system messages until the webapps' registration is ready, // so that we can know the correct pages registered to be sent. if (!this._webappsRegistryReady) { @@ -184,15 +195,22 @@ SystemMessageInternal.prototype = { msg: aMessage, pageURI: aPageURI, manifestURI: aManifestURI, - extra: aExtra }); + extra: aExtra, + resolvePromiseCb: aResolvePromiseCb, + rejectPromiseCb: aRejectPromiseCb }); return; } // Give this message an ID so that we can identify the message and // clean it up from the pending message queue when apps receive it. let messageID = gUUIDGenerator.generateUUID().toString(); - let manifestURL = aManifestURI.spec; + + let pendingPromise = { resolvePromiseCb: aResolvePromiseCb, + rejectPromiseCb: aRejectPromiseCb, + manifestURL: manifestURL, + counter: 0 }; + let pageURLs = []; if (aPageURI) { pageURLs.push(aPageURI.spec); @@ -226,6 +244,9 @@ SystemMessageInternal.prototype = { return; } + // For each page we must receive a confirm. + ++pendingPromise.counter; + let page = this._findPage(aType, aPageURL, manifestURL); if (page) { // Queue this message in the corresponding pages. @@ -233,7 +254,12 @@ SystemMessageInternal.prototype = { this._openAppPage(page, aMessage, aExtra, result); } + }, this); + + if (pendingPromise.counter) { + this._pendingPromises.set(messageID, pendingPromise); + } }, broadcastMessage: function(aType, aMessage, aExtra) { @@ -393,7 +419,8 @@ SystemMessageInternal.prototype = { "SystemMessageManager:GetPendingMessages", "SystemMessageManager:HasPendingMessages", "SystemMessageManager:Message:Return:OK", - "SystemMessageManager:HandleMessagesDone"].indexOf(aMessage.name) != -1) { + "SystemMessageManager:HandleMessagesDone", + "SystemMessageManager:HandleMessageDone"].indexOf(aMessage.name) != -1) { if (!aMessage.target.assertContainApp(msg.manifestURL)) { debug("Got message from a child process containing illegal manifest URL."); return null; @@ -442,6 +469,8 @@ SystemMessageInternal.prototype = { manifestURL, true, null); + + this._rejectPendingPromises(manifestURL); } break; } @@ -453,6 +482,7 @@ SystemMessageInternal.prototype = { msg.manifestURL, false, msg.pageURL); + this._rejectPendingPromises(msg.manifestURL); break; } case "SystemMessageManager:GetPendingMessages": @@ -520,6 +550,21 @@ SystemMessageInternal.prototype = { } break; } + case "SystemMessageManager:HandleMessageDone": + { + debug("received SystemMessageManager:HandleMessageDone " + msg.type + + " with msgID " + msg.msgID + " for " + msg.pageURL + + " @ " + msg.manifestURL); + + // Maybe this should resolve a pending promise. + this._resolvePendingPromises(msg.msgID); + + // A page has finished handling some of its system messages, so we try + // to release the CPU wake lock we acquired on behalf of that page. + this._releaseCpuWakeLock(this._createKeyForPage(msg), 1); + break; + } + case "SystemMessageManager:HandleMessagesDone": { debug("received SystemMessageManager:HandleMessagesDone " + msg.type + @@ -547,6 +592,7 @@ SystemMessageInternal.prototype = { ppmm = null; this._pages = null; this._bufferedSysMsgs = null; + this._pendingPromises.clear(); break; case "webapps-registry-start": this._webappsRegistryReady = false; @@ -558,9 +604,10 @@ SystemMessageInternal.prototype = { this._bufferedSysMsgs.forEach(function(aSysMsg) { switch (aSysMsg.how) { case "send": - this.sendMessage( + this.sendMessageInternal( aSysMsg.type, aSysMsg.msg, - aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra); + aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra, + aSysMsg.resolvePromiseCb, aSysMsg.rejectPromiseCb); break; case "broadcast": this.broadcastMessage(aSysMsg.type, aSysMsg.msg, aSysMsg.extra); @@ -596,6 +643,9 @@ SystemMessageInternal.prototype = { " from registered pages due to app uninstallation."); } } + + this._rejectPendingPromises(manifestURL); + debug("Finish updating registered pages for an uninstalled app."); break; } @@ -692,9 +742,10 @@ SystemMessageInternal.prototype = { appPageIsRunning = true; // We need to acquire a CPU wake lock for that page and expect that - // we'll receive a "SystemMessageManager:HandleMessagesDone" message - // when the page finishes handling the system message. At that point, - // we'll release the lock we acquired. + // we'll receive a "SystemMessageManager:HandleMessagesDone" or a + // "SystemMessageManager:HandleMessageDone" message when the page + // finishes handling the system message. At that point, we'll release + // the lock we acquired. this._acquireCpuWakeLock(pageKey); // Multiple windows can share the same target (process), the content @@ -714,8 +765,9 @@ SystemMessageInternal.prototype = { // The app page isn't running and relies on the 'open-app' chrome event to // wake it up. We still need to acquire a CPU wake lock for that page and // expect that we will receive a "SystemMessageManager:HandleMessagesDone" - // message when the page finishes handling the system message with other - // pending messages. At that point, we'll release the lock we acquired. + // or a "SystemMessageManager:HandleMessageDone" message when the page + // finishes handling the system message with other pending messages. At + // that point, we'll release the lock we acquired. this._acquireCpuWakeLock(pageKey); return MSG_SENT_FAILURE_APP_NOT_RUNNING; } else { @@ -724,6 +776,28 @@ SystemMessageInternal.prototype = { }, + _resolvePendingPromises: function(aMessageID) { + if (!this._pendingPromises.has(aMessageID)) { + debug("Unknown pendingPromise messageID. This seems a bug!!"); + return; + } + + let obj = this._pendingPromises.get(aMessageID); + if (!--obj.counter) { + obj.resolvePromiseCb(); + this._pendingPromises.delete(aMessageID); + } + }, + + _rejectPendingPromises: function(aManifestURL) { + for (var [i, obj] of this._pendingPromises) { + if (obj.manifestURL == aManifestURL) { + obj.rejectPromiseCb(); + this._pendingPromises.delete(i); + } + } + }, + classID: Components.ID("{70589ca5-91ac-4b9e-b839-d6a88167d714}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesInternal, diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index 202f5bfd6b7c..538b9e7fa490 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -57,7 +57,7 @@ function SystemMessageManager() { SystemMessageManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, - _dispatchMessage: function(aType, aDispatcher, aMessage) { + _dispatchMessage: function(aType, aDispatcher, aMessage, aMessageID) { if (aDispatcher.isHandling) { // Queue up the incomming message if we're currently dispatching a // message; we'll send the message once we finish with the current one. @@ -66,7 +66,7 @@ SystemMessageManager.prototype = { // event loop from within a system message handler (e.g. via alert()), // and we can then try to send the page another message while it's // inside this nested event loop. - aDispatcher.messages.push(aMessage); + aDispatcher.messages.push({ message: aMessage, messageID: aMessageID }); return; } @@ -96,16 +96,17 @@ SystemMessageManager.prototype = { // We need to notify the parent one of the system messages has been handled, // so the parent can release the CPU wake lock it took on our behalf. - cpmm.sendAsyncMessage("SystemMessageManager:HandleMessagesDone", + cpmm.sendAsyncMessage("SystemMessageManager:HandleMessageDone", { type: aType, manifestURL: this._manifestURL, pageURL: this._pageURL, - handledCount: 1 }); + msgID: aMessageID }); aDispatcher.isHandling = false; if (aDispatcher.messages.length > 0) { - this._dispatchMessage(aType, aDispatcher, aDispatcher.messages.shift()); + let msg = aDispatcher.messages.shift(); + this._dispatchMessage(aType, aDispatcher, msg.message, msg.messageID); } else { // No more messages that need to be handled, we can notify the // ContentChild to release the CPU wake lock grabbed by the ContentParent @@ -236,8 +237,9 @@ SystemMessageManager.prototype = { } messages.forEach(function(aMsg) { - this._dispatchMessage(msg.type, dispatcher, aMsg); + this._dispatchMessage(msg.type, dispatcher, aMsg, msg.msgID); }, this); + } else { // Since no handlers are registered, we need to notify the parent as if // all the queued system messages have been handled (notice |handledCount: diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index cf3993f242a8..dbd48dfb9284 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -9,7 +9,7 @@ interface nsIDOMWindow; // Implemented by the contract id @mozilla.org/system-message-internal;1 -[scriptable, uuid(6296a314-2abf-4cd0-9097-5e81ee6832e2)] +[scriptable, uuid(54c8e274-decb-4258-9a24-4ebfcbf3d00a)] interface nsISystemMessagesInternal : nsISupports { /* @@ -22,8 +22,11 @@ interface nsISystemMessagesInternal : nsISupports * @param manifestURI The webapp's manifest URI. * @param extra Extra opaque information that will be passed around in the observer * notification to open the page. + * returns a Promise */ - void sendMessage(in DOMString type, in jsval message, in nsIURI pageURI, in nsIURI manifestURI, [optional] in jsval extra); + nsISupports sendMessage(in DOMString type, in jsval message, + in nsIURI pageURI, in nsIURI manifestURI, + [optional] in jsval extra); /* * Allow any internal user to broadcast a message of a given type. From db5503c1793c306b1e5baee07832bdfbd4dd81e2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:37:03 +0100 Subject: [PATCH 10/16] Bug 1018320 - RequestSync API - patch 4 - sendAsyncMessage used to schedule tasks, r=ehsan --- dom/messages/SystemMessageInternal.js | 2 +- dom/requestsync/RequestSyncService.jsm | 136 ++++++++++++++++++++----- 2 files changed, 112 insertions(+), 26 deletions(-) diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index b610943299c1..0f4b7feb79f9 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -179,7 +179,7 @@ SystemMessageInternal.prototype = { }, sendMessage: function(aType, aMessage, aPageURI, aManifestURI, aExtra) { - return new Promise((resolve, reject) => { + return new Promise((aResolve, aReject) => { this.sendMessageInternal(aType, aMessage, aPageURI, aManifestURI, aExtra, aResolve, aReject); }); diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index 485b1faf452e..03df16c71405 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -14,6 +14,8 @@ const RSYNCDB_VERSION = 1; const RSYNCDB_NAME = "requestSync"; const RSYNC_MIN_INTERVAL = 100; +const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes + Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -56,6 +58,9 @@ this.RequestSyncService = { _wifi: false, + _activeTask: null, + _queuedTasks: [], + // Initialization of the RequestSyncService. init: function() { debug("init"); @@ -232,10 +237,18 @@ this.RequestSyncService = { removeRegistrationInternal: function(aTaskName, aKey) { debug('removeRegistrationInternal'); - if (this._registrations[aKey][aTaskName].timer) { - this._registrations[aKey][aTaskName].timer.cancel(); + let obj = this._registrations[aKey][aTaskName]; + if (obj.timer) { + obj.timer.cancel(); } + // It can be that this task has been already schedulated. + this.removeTaskFromQueue(obj); + + // It can be that this object is already in scheduled, or in the queue of a + // iDB transacation. In order to avoid rescheduling it, we must disable it. + obj.active = false; + delete this._registrations[aKey][aTaskName]; // Lets remove the key in case there are not tasks registered. @@ -245,6 +258,13 @@ this.RequestSyncService = { delete this._registrations[aKey]; }, + removeTaskFromQueue: function(aObj) { + let pos = this._queuedTasks.indexOf(aObj); + if (pos != -1) { + this._queuedTasks.splice(pos, 1); + } + }, + // The communication from the exposed objects and the service is done using // messages. This function receives and processes them. receiveMessage: function(aMessage) { @@ -510,6 +530,15 @@ this.RequestSyncService = { timeout: function(aObj) { debug("timeout"); + if (this._activeTask) { + debug("queueing tasks"); + // We have an active task, let's queue this as next task. + if (this._queuedTasks.indexOf(aObj) == -1) { + this._queuedTasks.push(aObj); + } + return; + } + let app = appsService.getAppByLocalId(aObj.principal.appId); if (!app) { dump("ERROR!! RequestSyncService - Failed to retrieve app data from a principal.\n"); @@ -522,12 +551,13 @@ this.RequestSyncService = { let pageURL = Services.io.newURI(aObj.data.wakeUpPage, null, aObj.principal.URI); // Maybe need to be rescheduled? - if (this.needRescheduling('request-sync', manifestURL, pageURL)) { + if (this.hasPendingMessages('request-sync', manifestURL, pageURL)) { this.scheduleTimer(aObj); return; } aObj.timer = null; + this._activeTask = aObj; if (!manifestURL || !pageURL) { dump("ERROR!! RequestSyncService - Failed to create URI for the page or the manifest\n"); @@ -536,27 +566,86 @@ this.RequestSyncService = { return; } - // Sending the message. - systemMessenger.sendMessage('request-sync', - this.createPartialTaskObject(aObj.data), - pageURL, manifestURL); + // We don't want to run more than 1 task at the same time. We do this using + // the promise created by sendMessage(). But if the task takes more than + // RSYNC_OPERATION_TIMEOUT millisecs, we have to ignore the promise and + // continue processing other tasks. - // One shot? Then this is not active. - aObj.active = !aObj.data.oneShot; - aObj.data.lastSync = new Date(); + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + let done = false; let self = this; - this.updateObjectInDB(aObj, function() { - // SchedulerTimer creates a timer and a nsITimer cannot be cloned. This - // is the reason why this operation has to be done after storing the aObj - // into IDB. - if (!aObj.data.oneShot) { - self.scheduleTimer(aObj); + function taskCompleted() { + debug("promise or timeout for task calls taskCompleted"); + + if (!done) { + done = true; + self.operationCompleted(); } + + timer.cancel(); + timer = null; + } + + timer.initWithCallback(function() { + debug("Task is taking too much, let's ignore the promise."); + taskCompleted(); + }, RSYNC_OPERATION_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); + + // Sending the message. + let promise = + systemMessenger.sendMessage('request-sync', + this.createPartialTaskObject(aObj.data), + pageURL, manifestURL); + + promise.then(function() { + debug("promise resolved"); + taskCompleted(); + }, function() { + debug("promise rejected"); + taskCompleted(); }); }, - needRescheduling: function(aMessageName, aManifestURL, aPageURL) { + operationCompleted: function() { + debug("operationCompleted"); + + if (!this._activeTask) { + dump("ERROR!! RequestSyncService - OperationCompleted called without an active task\n"); + return; + } + + // One shot? Then this is not active. + this._activeTask.active = !this._activeTask.data.oneShot; + this._activeTask.data.lastSync = new Date(); + + let self = this; + this.updateObjectInDB(this._activeTask, function() { + // SchedulerTimer creates a timer and a nsITimer cannot be cloned. This + // is the reason why this operation has to be done after storing the task + // into IDB. + if (!self._activeTask.data.oneShot) { + self.scheduleTimer(self._activeTask); + } + + self.processNextTask(); + }); + }, + + processNextTask: function() { + debug("processNextTask"); + + this._activeTask = null; + + if (this._queuedTasks.length == 0) { + return; + } + + let task = this._queuedTasks.shift(); + this.timeout(task); + }, + + hasPendingMessages: function(aMessageName, aManifestURL, aPageURL) { let hasPendingMessages = cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages", { type: aMessageName, @@ -564,14 +653,7 @@ this.RequestSyncService = { manifestURL: aManifestURL.spec })[0]; debug("Pending messages: " + hasPendingMessages); - - if (hasPendingMessages) { - return true; - } - - // FIXME: other reasons? - - return false; + return hasPendingMessages; }, // Update the object into the database. @@ -649,10 +731,14 @@ this.RequestSyncService = { if (!this._wifi) { // Disable all the wifiOnly tasks. + let self = this; this.forEachRegistration(function(aObj) { if (aObj.data.wifiOnly && aObj.timer) { aObj.timer.cancel(); aObj.timer = null; + + // It can be that this task has been already schedulated. + self.removeTaskFromQueue(aObj); } }); return; From bc6fce69f953a344fac16b67a2971b4d2cbe7488 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:37:11 +0100 Subject: [PATCH 11/16] Bug 1018320 - RequestSync API - patch 5 - mozSetMessageHandlerPromise, r=fabrice --- dom/base/Navigator.cpp | 27 ++++ dom/base/Navigator.h | 2 + dom/messages/SystemMessageManager.js | 51 ++++++- .../nsIDOMNavigatorSystemMessages.idl | 7 +- dom/requestsync/RequestSyncService.jsm | 8 +- dom/requestsync/tests/mochitest.ini | 3 + dom/requestsync/tests/test_basic_app.html | 2 +- dom/requestsync/tests/test_promise.html | 56 +++++++ dom/requestsync/tests/test_promise_app.html | 138 ++++++++++++++++++ dom/requestsync/tests/test_wakeUp.html | 19 +++ dom/webidl/Navigator.webidl | 6 + 11 files changed, 308 insertions(+), 11 deletions(-) create mode 100644 dom/requestsync/tests/test_promise.html create mode 100644 dom/requestsync/tests/test_promise_app.html diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index bafb53ba05f8..81d842f25b9e 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1847,6 +1847,33 @@ Navigator::MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv) return result; } +void +Navigator::MozSetMessageHandlerPromise(Promise& aPromise, + ErrorResult& aRv) +{ + // The WebIDL binding is responsible for the pref check here. + aRv = EnsureMessagesManager(); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + bool result = false; + aRv = mMessagesManager->MozIsHandlingMessage(&result); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (!result) { + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return; + } + + aRv = mMessagesManager->MozSetMessageHandlerPromise(&aPromise); + if (NS_WARN_IF(aRv.Failed())) { + return; + } +} + void Navigator::MozSetMessageHandler(const nsAString& aType, systemMessageCallback* aCallback, diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 3852e49c9011..7d7daff15ad1 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -235,6 +235,8 @@ public: systemMessageCallback* aCallback, ErrorResult& aRv); bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv); + void MozSetMessageHandlerPromise(Promise& aPromise, ErrorResult& aRv); + #ifdef MOZ_B2G already_AddRefed GetMobileIdAssertion(const MobileIdOptions& options, ErrorResult& aRv); diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index 538b9e7fa490..b9740a52d14f 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -42,6 +42,10 @@ function SystemMessageManager() { // Flag to specify if this process has already registered the manifest URL. this._registerManifestURLReady = false; + // Used to know if the promise has to be accepted or not. + this._isHandling = false; + this._promise = null; + // Flag to determine this process is a parent or child process. let appInfo = Cc["@mozilla.org/xre/app-info;1"]; this._isParentProcess = @@ -71,6 +75,7 @@ SystemMessageManager.prototype = { } aDispatcher.isHandling = true; + this._isHandling = true; // We get a json blob, but in some cases we want another kind of object // to be dispatched. To do so, we check if we have a valid contract ID of @@ -94,15 +99,29 @@ SystemMessageManager.prototype = { .handleMessage(wrapped ? aMessage : Cu.cloneInto(aMessage, this._window)); - // We need to notify the parent one of the system messages has been handled, - // so the parent can release the CPU wake lock it took on our behalf. - cpmm.sendAsyncMessage("SystemMessageManager:HandleMessageDone", - { type: aType, - manifestURL: this._manifestURL, - pageURL: this._pageURL, - msgID: aMessageID }); - aDispatcher.isHandling = false; + this._isHandling = false; + + let self = this; + function sendResponse() { + // We need to notify the parent one of the system messages has been + // handled, so the parent can release the CPU wake lock it took on our + // behalf. + cpmm.sendAsyncMessage("SystemMessageManager:HandleMessageDone", + { type: aType, + manifestURL: self._manifestURL, + pageURL: self._pageURL, + msgID: aMessageID }); + } + + if (!this._promise) { + debug("No promise set, sending the response immediately"); + sendResponse(); + } else { + debug("Using the promise to postpone the response."); + this._promise.then(sendResponse, sendResponse); + this._promise = null; + } if (aDispatcher.messages.length > 0) { let msg = aDispatcher.messages.shift(); @@ -171,9 +190,25 @@ SystemMessageManager.prototype = { manifestURL: this._manifestURL })[0]; }, + mozIsHandlingMessage: function() { + debug("is handling message: " + this._isHandling); + return this._isHandling; + }, + + mozSetMessageHandlerPromise: function(aPromise) { + debug("setting a promise"); + + if (!this._isHandling) { + throw "Not in a handleMessage method"; + } + + this._promise = aPromise; + }, + uninit: function() { this._dispatchers = null; this._pendings = null; + this._promise = null; if (this._isParentProcess) { Services.obs.removeObserver(this, kSystemMessageInternalReady); diff --git a/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl b/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl index 855cfb0c8c70..4fc156c0e9dd 100644 --- a/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl +++ b/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl @@ -10,10 +10,15 @@ interface nsIDOMSystemMessageCallback : nsISupports void handleMessage(in jsval message); }; -[scriptable, uuid(091e90dd-0e8b-463d-8cdc-9225d3a6ff90)] +[scriptable, uuid(d04d3c11-26aa-46eb-a981-353af101f9cf)] interface nsIDOMNavigatorSystemMessages : nsISupports { void mozSetMessageHandler(in DOMString type, in nsIDOMSystemMessageCallback callback); boolean mozHasPendingMessage(in DOMString type); + + // the parameter is a promise object. + void mozSetMessageHandlerPromise(in nsISupports promise); + + bool mozIsHandlingMessage(); }; diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index 03df16c71405..dbd8d3422d1c 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -587,10 +587,16 @@ this.RequestSyncService = { timer = null; } + let timeout = RSYNC_OPERATION_TIMEOUT; + try { + let tmp = Services.prefs.getIntPref("dom.requestSync.maxTaskTimeout"); + timeout = tmp; + } catch(e) {} + timer.initWithCallback(function() { debug("Task is taking too much, let's ignore the promise."); taskCompleted(); - }, RSYNC_OPERATION_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); + }, timeout, Ci.nsITimer.TYPE_ONE_SHOT); // Sending the message. let promise = diff --git a/dom/requestsync/tests/mochitest.ini b/dom/requestsync/tests/mochitest.ini index b1071e0e76c1..a5fee3b10647 100644 --- a/dom/requestsync/tests/mochitest.ini +++ b/dom/requestsync/tests/mochitest.ini @@ -14,3 +14,6 @@ support-files = run-if = buildapp != 'b2g' [test_wakeUp.html] run-if = buildapp == 'b2g' && toolkit == 'gonk' +[test_promise.html] +[test_promise_app.html] +run-if = buildapp == 'b2g' && toolkit == 'gonk' diff --git a/dom/requestsync/tests/test_basic_app.html b/dom/requestsync/tests/test_basic_app.html index 21437ae3eb22..bb5baeb4f390 100644 --- a/dom/requestsync/tests/test_basic_app.html +++ b/dom/requestsync/tests/test_basic_app.html @@ -72,7 +72,7 @@ SpecialPowers.pushPermissions( [{ "type": "browser", "allow": 1, "context": document }, { "type": "embed-apps", "allow": 1, "context": document }, - {"type": "requestsync-manager", "allow": 1, "context": document }, + { "type": "requestsync-manager", "allow": 1, "context": document }, { "type": "webapps-manage", "allow": 1, "context": document }], runTests); }, diff --git a/dom/requestsync/tests/test_promise.html b/dom/requestsync/tests/test_promise.html new file mode 100644 index 000000000000..8277eadaa6df --- /dev/null +++ b/dom/requestsync/tests/test_promise.html @@ -0,0 +1,56 @@ + + + + + Test for requestSync - promise + + + + + +
+ + + diff --git a/dom/requestsync/tests/test_promise_app.html b/dom/requestsync/tests/test_promise_app.html new file mode 100644 index 000000000000..9039c8e0fba3 --- /dev/null +++ b/dom/requestsync/tests/test_promise_app.html @@ -0,0 +1,138 @@ + + + + + Test for requestSync - promise + + + + + +
+ + + diff --git a/dom/requestsync/tests/test_wakeUp.html b/dom/requestsync/tests/test_wakeUp.html index f19c4f3a1503..14d7ec6040ca 100644 --- a/dom/requestsync/tests/test_wakeUp.html +++ b/dom/requestsync/tests/test_wakeUp.html @@ -84,6 +84,22 @@ }, genericError); } + function test_unregister_oneShot() { + navigator.sync.unregister('oneShot').then( + function() { + ok(true, "navigator.sync.unregister() oneShot done"); + runTests(); + }, genericError); + } + + function test_unregister_multiShots() { + navigator.sync.unregister('multiShots').then( + function() { + ok(true, "navigator.sync.unregister() multiShots done"); + runTests(); + }, genericError); + } + function test_wait() { // nothing to do here. } @@ -113,6 +129,9 @@ test_register_multiShots, test_wait, + + test_unregister_oneShot, + test_unregister_multiShots, ]; function runTests() { diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 653facf8237b..64b67d5c434f 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -285,6 +285,12 @@ partial interface Navigator { void mozSetMessageHandler (DOMString type, systemMessageCallback? callback); [Throws, Pref="dom.sysmsg.enabled"] boolean mozHasPendingMessage (DOMString type); + + // This method can be called only from the systeMessageCallback function and + // it allows the page to set a promise to keep alive the app until the + // current operation is not fully completed. + [Throws, Pref="dom.sysmsg.enabled"] + void mozSetMessageHandlerPromise (Promise promise); }; #ifdef MOZ_B2G_RIL From 0ce46d31ce405a911a9e20022647ec7cd2bc8525 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:37:18 +0100 Subject: [PATCH 12/16] Bug 1018320 - RequestSync API - patch 6 - Manager API, r=ehsan --- dom/requestsync/RequestSyncApp.jsm | 48 +++++++++ dom/requestsync/RequestSyncManager.js | 46 +++++++- dom/requestsync/RequestSyncService.jsm | 91 ++++++++++++++-- dom/requestsync/RequestSyncTask.jsm | 101 ++++++++++++++++++ dom/requestsync/moz.build | 2 + dom/requestsync/tests/common_basic.js | 17 ++- dom/requestsync/tests/test_basic.html | 8 +- .../mochitest/general/test_interfaces.html | 4 + dom/webidl/RequestSyncManager.webidl | 43 ++++++-- dom/webidl/RequestSyncScheduler.webidl | 6 +- 10 files changed, 346 insertions(+), 20 deletions(-) create mode 100644 dom/requestsync/RequestSyncApp.jsm create mode 100644 dom/requestsync/RequestSyncTask.jsm diff --git a/dom/requestsync/RequestSyncApp.jsm b/dom/requestsync/RequestSyncApp.jsm new file mode 100644 index 000000000000..69ad349d5e53 --- /dev/null +++ b/dom/requestsync/RequestSyncApp.jsm @@ -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/. */ + +'use strict'; + +this.EXPORTED_SYMBOLS = ['RequestSyncApp']; + +function debug(s) { + //dump('DEBUG RequestSyncApp: ' + s + '\n'); +} + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + +this.RequestSyncApp = function(aData) { + debug('created'); + + let keys = [ 'origin', 'manifestURL', 'isInBrowserElement' ]; + for (let i = 0; i < keys.length; ++i) { + if (!(keys[i] in aData)) { + dump("ERROR - RequestSyncApp must receive a full app object: " + keys[i] + " missing."); + throw "ERROR!"; + } + + this["_" + keys[i]] = aData[keys[i]]; + } +} + +this.RequestSyncApp.prototype = { + classDescription: 'RequestSyncApp XPCOM Component', + classID: Components.ID('{5a0b64db-a2be-4f08-a6c5-8bf2e3ae0c57}'), + contractID: '@mozilla.org/dom/request-sync-manager;1', + QueryInterface: XPCOMUtils.generateQI([]), + + get origin() { + return this._origin; + }, + + get manifestURL() { + return this._manifestURL; + }, + + get isInBrowserElement() { + return this._isInBrowserElement; + } +}; diff --git a/dom/requestsync/RequestSyncManager.js b/dom/requestsync/RequestSyncManager.js index 8cb5f5ea060e..0e9e53b7ac43 100644 --- a/dom/requestsync/RequestSyncManager.js +++ b/dom/requestsync/RequestSyncManager.js @@ -12,6 +12,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/RequestSyncApp.jsm'); +Cu.import('resource://gre/modules/RequestSyncTask.jsm'); XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", @@ -31,7 +33,8 @@ RequestSyncManager.prototype = { Ci.nsIObserver, Ci.nsIDOMGlobalPropertyInitializer]), - _messages: [ "RequestSyncManager:Registrations:Return" ], + _messages: [ "RequestSyncManager:Registrations:Return", + "RequestSyncManager:SetPolicy:Return" ], init: function(aWindow) { debug("init"); @@ -55,6 +58,42 @@ RequestSyncManager.prototype = { return this.sendMessage("RequestSyncManager:Registrations", {}); }, + setPolicy: function(aTask, aOrigin, aManifestURL, aIsInBrowserElement, + aState, aOverwrittenMinInterval) { + debug('setPolicy'); + + return this.sendMessage("RequestSyncManager:SetPolicy", + { task: aTask, + origin: aOrigin, + manifestURL: aManifestURL, + isInBrowserElement: aIsInBrowserElement, + state: aState, + overwrittenMinInterval: aOverwrittenMinInterval }); + }, + + registrationsResult: function(aData) { + debug("registrationsResult"); + + let results = new this._window.Array(); + for (let i = 0; i < aData.length; ++i) { + if (!("app" in aData[i])) { + dump("ERROR - Serialization error in RequestSyncManager.\n"); + continue; + } + + let app = new RequestSyncApp(aData[i].app); + let exposedApp = + this._window.RequestSyncApp._create(this._window, app); + + let task = new RequestSyncTask(this, this._window, exposedApp, aData[i]); + let exposedTask = + this._window.RequestSyncTask._create(this._window, task); + + results.push(exposedTask); + } + return results; + }, + receiveMessage: function(aMessage) { debug('receiveMessage'); @@ -68,6 +107,11 @@ RequestSyncManager.prototype = { return; } + if (aMessage.name == 'RequestSyncManager:Registrations:Return') { + req.resolve(this.registrationsResult(aMessage.data.results)); + return; + } + if ('results' in aMessage.data) { req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); return; diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index dbd8d3422d1c..d1a705db4c4b 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -16,6 +16,10 @@ const RSYNC_MIN_INTERVAL = 100; const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes +const RSYNC_STATE_ENABLED = "enabled"; +const RSYNC_STATE_DISABLED = "disabled"; +const RSYNC_STATE_WIFIONLY = "wifiOnly"; + Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -47,9 +51,12 @@ this.RequestSyncService = { children: [], - _messages: [ "RequestSync:Register", "RequestSync:Unregister", - "RequestSync:Registrations", "RequestSync:Registration", - "RequestSyncManager:Registrations" ], + _messages: [ "RequestSync:Register", + "RequestSync:Unregister", + "RequestSync:Registrations", + "RequestSync:Registration", + "RequestSyncManager:Registrations", + "RequestSyncManager:SetPolicy" ], _pendingOperation: false, _pendingMessages: [], @@ -316,6 +323,10 @@ this.RequestSyncService = { this.managerRegistrations(aMessage.target, aMessage.data, principal); break; + case "RequestSyncManager:SetPolicy": + this.managerSetPolicy(aMessage.target, aMessage.data, principal); + break; + default: debug("Wrong message: " + aMessage.name); break; @@ -370,6 +381,13 @@ this.RequestSyncService = { aData.params.lastSync = 0; aData.params.principal = aPrincipal; + aData.params.state = RSYNC_STATE_ENABLED; + if (aData.params.wifiOnly) { + aData.params.state = RSYNC_STATE_WIFIONLY; + } + + aData.params.overwrittenMinInterval = 0; + let dbKey = aData.task + "|" + aPrincipal.appId + '|' + aPrincipal.isInBrowserElement + '|' + @@ -478,6 +496,51 @@ this.RequestSyncService = { results: results }); }, + // Set a policy to a task. + managerSetPolicy: function(aTarget, aData, aPrincipal) { + debug("managerSetPolicy"); + + let toSave = null; + let self = this; + this.forEachRegistration(function(aObj) { + if (aObj.principal.isInBrowserElement != aData.isInBrowserElement || + aObj.principal.origin != aData.origin) { + return; + } + + let app = appsService.getAppByLocalId(aObj.principal.appId); + if (app && app.manifestURL != aData.manifestURL || + (!app && aData.manifestURL != "")) { + return; + } + + if ("overwrittenMinInterval" in aData) { + aObj.data.overwrittenMinInterval = aData.overwrittenMinInterval; + } + + aObj.data.state = aData.state; + + if (toSave) { + dump("ERROR!! RequestSyncService - SetPolicy matches more than 1 task.\n"); + return; + } + + toSave = aObj; + }); + + if (!toSave) { + aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return", + { requestID: aData.requestID, error: "UnknownTaskError" }); + return; + } + + this.updateObjectInDB(toSave, function() { + self.scheduleTimer(toSave); + aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return", + { requestID: aData.requestID }); + }); + }, + // We cannot expose the full internal object to content but just a subset. // This method creates this subset. createPartialTaskObject: function(aObj) { @@ -502,6 +565,8 @@ this.RequestSyncService = { obj.app.manifestURL = app.manifestURL; } + obj.state = aObj.state; + obj.overwrittenMinInterval = aObj.overwrittenMinInterval; return obj; }, @@ -509,21 +574,35 @@ this.RequestSyncService = { scheduleTimer: function(aObj) { debug("scheduleTimer"); + if (aObj.timer) { + aObj.timer.cancel(); + aObj.timer = null; + } + // A registration can be already inactive if it was 1 shot. if (!aObj.active) { return; } + if (aObj.data.state == RSYNC_STATE_DISABLED) { + return; + } + // WifiOnly check. - if (aObj.data.wifiOnly && !this._wifi) { + if (aObj.data.state == RSYNC_STATE_WIFIONLY && !this._wifi) { return; } aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + let interval = aObj.data.minInterval; + if (aObj.data.overwrittenMinInterval > 0) { + interval = aObj.data.overwrittenMinInterval; + } + let self = this; aObj.timer.initWithCallback(function() { self.timeout(aObj); }, - aObj.data.minInterval * 1000, + interval * 1000, Ci.nsITimer.TYPE_ONE_SHOT); }, @@ -739,7 +818,7 @@ this.RequestSyncService = { // Disable all the wifiOnly tasks. let self = this; this.forEachRegistration(function(aObj) { - if (aObj.data.wifiOnly && aObj.timer) { + if (aObj.data.state == RSYNC_STATE_WIFIONLY && aObj.timer) { aObj.timer.cancel(); aObj.timer = null; diff --git a/dom/requestsync/RequestSyncTask.jsm b/dom/requestsync/RequestSyncTask.jsm new file mode 100644 index 000000000000..4360e3de3884 --- /dev/null +++ b/dom/requestsync/RequestSyncTask.jsm @@ -0,0 +1,101 @@ +/* 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/. */ + +'use strict'; + +this.EXPORTED_SYMBOLS = ['RequestSyncTask']; + +function debug(s) { + //dump('DEBUG RequestSyncTask: ' + s + '\n'); +} + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + +this.RequestSyncTask = function(aManager, aWindow, aApp, aData) { + debug('created'); + + this._manager = aManager; + this._window = aWindow; + this._app = aApp; + + let keys = [ 'task', 'lastSync', 'oneShot', 'minInterval', 'wakeUpPage', + 'wifiOnly', 'data', 'state', 'overwrittenMinInterval' ]; + for (let i = 0; i < keys.length; ++i) { + if (!(keys[i] in aData)) { + dump("ERROR - RequestSyncTask must receive a fully app object: " + keys[i] + " missing."); + throw "ERROR!"; + } + + this["_" + keys[i]] = aData[keys[i]]; + } +} + +this.RequestSyncTask.prototype = { + classDescription: 'RequestSyncTask XPCOM Component', + classID: Components.ID('{a1e1c9c6-ce42-49d4-b8b4-fbd686d8fdd9}'), + contractID: '@mozilla.org/dom/request-sync-manager;1', + QueryInterface: XPCOMUtils.generateQI([]), + + get app() { + return this._app; + }, + + get state() { + return this._state; + }, + + get overwrittenMinInterval() { + return this._overwrittenMinInterval; + }, + + get task() { + return this._task; + }, + + get lastSync() { + return this._lastSync; + }, + + get wakeUpPage() { + return this._wakeUpPage; + }, + + get oneShot() { + return this._oneShot; + }, + + get minInterval() { + return this._minInterval; + }, + + get wifiOnly() { + return this._wifiOnly; + }, + + get data() { + return this._data; + }, + + setPolicy: function(aState, aOverwrittenMinInterval) { + debug("setPolicy"); + let self = this; + + return new this._window.Promise(function(aResolve, aReject) { + let p = self._manager.setPolicy(self._task, self._app.origin, + self._app.manifestURL, + self._app.isInBrowserElement, + aState, + aOverwrittenMinInterval); + + // Set the new value only when the promise is resolved. + p.then(function() { + self._state = aState; + self._overwrittenMinInterval = aOverwrittenMinInterval; + aResolve(); + }, aReject); + }); + } +}; diff --git a/dom/requestsync/moz.build b/dom/requestsync/moz.build index 481da0c886b0..3dbdf4b3168e 100644 --- a/dom/requestsync/moz.build +++ b/dom/requestsync/moz.build @@ -17,7 +17,9 @@ EXTRA_COMPONENTS += [ ] EXTRA_JS_MODULES += [ + 'RequestSyncApp.jsm', 'RequestSyncService.jsm', + 'RequestSyncTask.jsm', ] SOURCES += [ diff --git a/dom/requestsync/tests/common_basic.js b/dom/requestsync/tests/common_basic.js index 3301c4135b67..bf0f4fd0aa84 100644 --- a/dom/requestsync/tests/common_basic.js +++ b/dom/requestsync/tests/common_basic.js @@ -151,7 +151,7 @@ function test_managerRegistrationsEmpty() { genericError); } -function test_managerRegistrations() { +function test_managerRegistrations(state, overwrittenMinInterval) { navigator.syncManager.registrations().then( function(results) { is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); @@ -166,7 +166,22 @@ function test_managerRegistrations() { ok("manifestURL" in results[0].app, "navigator.sync.registrations()[0].app.manifestURL is correct"); is(results[0].app.origin, 'http://mochi.test:8888', "navigator.sync.registrations()[0].app.origin is correct"); is(results[0].app.isInBrowserElement, false, "navigator.sync.registrations()[0].app.isInBrowserElement is correct"); + is(results[0].state, state, "navigator.sync.registrations()[0].state is correct"); + is(results[0].overwrittenMinInterval, overwrittenMinInterval, "navigator.sync.registrations()[0].overwrittenMinInterval is correct"); + ok("setPolicy" in results[0], "navigator.sync.registrations()[0].setPolicy is correct"); runTests(); }, genericError); } + +function test_managerSetPolicy(state, overwrittenMinInterval) { + navigator.syncManager.registrations().then( + function(results) { + results[0].setPolicy(state, overwrittenMinInterval).then( + function() { + ok(state, results[0].state, "State matches"); + ok(overwrittenMinInterval, results[0].overwrittenMinInterval, "OverwrittenMinInterval matches"); + runTests(); + }, genericError); + }).catch(genericError); +} diff --git a/dom/requestsync/tests/test_basic.html b/dom/requestsync/tests/test_basic.html index ddab2a297a00..e95974cad6ff 100644 --- a/dom/requestsync/tests/test_basic.html +++ b/dom/requestsync/tests/test_basic.html @@ -38,12 +38,18 @@ // overwrite the same registration. test_register, - test_managerRegistrations, + function() { test_managerRegistrations('wifiOnly', 0); }, test_registrations, test_registrationEmpty, test_registration, + function() { test_managerSetPolicy('disabled', 123); }, + function() { test_managerRegistrations('disabled', 123); }, + + function() { test_managerSetPolicy('enabled', 42); }, + function() { test_managerRegistrations('enabled', 42); }, + test_unregister, test_unregisterDuplicate, diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 809e0f301163..2586c34a1a88 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -1213,6 +1213,10 @@ var interfaceNamesInGlobalScope = {name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "RequestSyncScheduler", b2g: true, pref: "dom.requestSync.enabled" }, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "RequestSyncApp", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, +// IMPORTANT: Do not change this list without review from a DOM peer! + {name: "RequestSyncTask", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "Telephony", b2g: true, pref: "dom.telephony.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/RequestSyncManager.webidl b/dom/webidl/RequestSyncManager.webidl index 701e80b10f60..632081499580 100644 --- a/dom/webidl/RequestSyncManager.webidl +++ b/dom/webidl/RequestSyncManager.webidl @@ -4,16 +4,43 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -// Rappresentation of the app in the RequestTaskFull. -dictionary RequestTaskApp { - USVString origin; - USVString manifestURL; - boolean isInBrowserElement; +[AvailableIn=CertifiedApps, + Pref="dom.requestSync.enabled", + CheckPermissions="requestsync-manager", + JSImplementation="@mozilla.org/dom/request-sync-task-app;1"] +interface RequestSyncApp { + readonly attribute USVString origin; + readonly attribute USVString manifestURL; + readonly attribute boolean isInBrowserElement; }; +enum RequestSyncTaskPolicyState { "enabled", "disabled", "wifiOnly" }; + // Like a normal task, but with info about the app. -dictionary RequestTaskFull : RequestTask { - RequestTaskApp app; +[AvailableIn=CertifiedApps, + Pref="dom.requestSync.enabled", + CheckPermissions="requestsync-manager", + JSImplementation="@mozilla.org/dom/request-sync-task-manager;1"] +interface RequestSyncTask { + // This object describes the app that is owning the task. + readonly attribute RequestSyncApp app; + + // Using setPolicy it's possible to owerwrite the state and the minInterval. + readonly attribute RequestSyncTaskPolicyState state; + readonly attribute long overwrittenMinInterval; + + // These attributes are taken from the configuration of the task: + + readonly attribute USVString task; + readonly attribute DOMTimeStamp lastSync; + readonly attribute USVString wakeUpPage; + readonly attribute boolean oneShot; + readonly attribute long minInterval; + readonly attribute boolean wifiOnly; + readonly attribute any data; + + Promise setPolicy(RequestSyncTaskPolicyState aState, + optional long ovewrittenMinInterval); }; [NavigatorProperty="syncManager", @@ -23,5 +50,5 @@ dictionary RequestTaskFull : RequestTask { JSImplementation="@mozilla.org/dom/request-sync-manager;1"] // This interface will be used only by the B2G SystemApp interface RequestSyncManager { - Promise> registrations(); + Promise> registrations(); }; diff --git a/dom/webidl/RequestSyncScheduler.webidl b/dom/webidl/RequestSyncScheduler.webidl index 3df115bff465..1bd33ac12fdc 100644 --- a/dom/webidl/RequestSyncScheduler.webidl +++ b/dom/webidl/RequestSyncScheduler.webidl @@ -15,7 +15,7 @@ dictionary RequestTaskParams { // This is the dictionary you can have back from registration{s}(). -dictionary RequestTask : RequestTaskParams { +dictionary RequestTaskFull : RequestTaskParams { USVString task = ""; // Last synchonization date.. maybe it's useful to know. @@ -33,6 +33,6 @@ interface RequestSyncScheduler { Promise unregister(USVString task); // Useful methods to get registrations - Promise> registrations(); - Promise registration(USVString task); + Promise> registrations(); + Promise registration(USVString task); }; From cf6f008fcb30600a771506c25d0a6dfe239e0334 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Sun, 4 Jan 2015 10:37:24 +0100 Subject: [PATCH 13/16] Bug 1018320 - RequestSync API - patch 7 - reject promise when an exception is thrown, r=fabrice --- dom/messages/SystemMessageInternal.js | 31 ++++++++++++++++----- dom/messages/SystemMessageManager.js | 19 +++++++++---- dom/requestsync/RequestSyncService.jsm | 1 + dom/requestsync/tests/test_promise_app.html | 28 +++++++++++++------ 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index 0f4b7feb79f9..bde7b8926d98 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -470,7 +470,7 @@ SystemMessageInternal.prototype = { true, null); - this._rejectPendingPromises(manifestURL); + this._rejectPendingPromisesFromManifestURL(manifestURL); } break; } @@ -482,7 +482,7 @@ SystemMessageInternal.prototype = { msg.manifestURL, false, msg.pageURL); - this._rejectPendingPromises(msg.manifestURL); + this._rejectPendingPromisesFromManifestURL(msg.manifestURL); break; } case "SystemMessageManager:GetPendingMessages": @@ -554,10 +554,14 @@ SystemMessageInternal.prototype = { { debug("received SystemMessageManager:HandleMessageDone " + msg.type + " with msgID " + msg.msgID + " for " + msg.pageURL + - " @ " + msg.manifestURL); + " @ " + msg.manifestURL + " - promise rejected: " + msg.rejected); - // Maybe this should resolve a pending promise. - this._resolvePendingPromises(msg.msgID); + // Maybe this should resolve/reject a pending promise. + if (msg.rejected) { + this._rejectPendingPromises(msg.msgID); + } else { + this._resolvePendingPromises(msg.msgID); + } // A page has finished handling some of its system messages, so we try // to release the CPU wake lock we acquired on behalf of that page. @@ -644,7 +648,7 @@ SystemMessageInternal.prototype = { } } - this._rejectPendingPromises(manifestURL); + this._rejectPendingPromisesFromManifestURL(manifestURL); debug("Finish updating registered pages for an uninstalled app."); break; @@ -789,7 +793,20 @@ SystemMessageInternal.prototype = { } }, - _rejectPendingPromises: function(aManifestURL) { + _rejectPendingPromises: function(aMessageID) { + if (!this._pendingPromises.has(aMessageID)) { + debug("Unknown pendingPromise messageID. This seems a bug!!"); + return; + } + + let obj = this._pendingPromises.get(aMessageID); + if (!--obj.counter) { + obj.rejectPromiseCb(); + this._pendingPromises.delete(aMessageID); + } + }, + + _rejectPendingPromisesFromManifestURL: function(aManifestURL) { for (var [i, obj] of this._pendingPromises) { if (obj.manifestURL == aManifestURL) { obj.rejectPromiseCb(); diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index b9740a52d14f..b2864e757ff2 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -95,9 +95,14 @@ SystemMessageManager.prototype = { } } - aDispatcher.handler - .handleMessage(wrapped ? aMessage - : Cu.cloneInto(aMessage, this._window)); + let message = wrapped ? aMessage : Cu.cloneInto(aMessage, this._window); + + let rejectPromise = false; + try { + aDispatcher.handler.handleMessage(message); + } catch(e) { + rejectPromise = true; + } aDispatcher.isHandling = false; this._isHandling = false; @@ -111,7 +116,8 @@ SystemMessageManager.prototype = { { type: aType, manifestURL: self._manifestURL, pageURL: self._pageURL, - msgID: aMessageID }); + msgID: aMessageID, + rejected: rejectPromise }); } if (!this._promise) { @@ -119,7 +125,10 @@ SystemMessageManager.prototype = { sendResponse(); } else { debug("Using the promise to postpone the response."); - this._promise.then(sendResponse, sendResponse); + this._promise.then(sendResponse, function() { + rejectPromise = true; + sendResponse(); + }); this._promise = null; } diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index d1a705db4c4b..66770d5b620d 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -678,6 +678,7 @@ this.RequestSyncService = { }, timeout, Ci.nsITimer.TYPE_ONE_SHOT); // Sending the message. + debug("Sending message."); let promise = systemMessenger.sendMessage('request-sync', this.createPartialTaskObject(aObj.data), diff --git a/dom/requestsync/tests/test_promise_app.html b/dom/requestsync/tests/test_promise_app.html index 9039c8e0fba3..26c2a24e4f82 100644 --- a/dom/requestsync/tests/test_promise_app.html +++ b/dom/requestsync/tests/test_promise_app.html @@ -17,23 +17,35 @@ navigator.mozSetMessageHandler('request-sync', function(e) { if (e.task == 'foobar') { - ok(true, "foobar message received:" + foobarCounter); + ok(true, "foobar message received:" + ++foobarCounter); - if (++foobarCounter == 1) { + if (foobarCounter == 1) { // The first time we wait 2 seconds. info("Setting a promise object."); navigator.mozSetMessageHandlerPromise(new Promise(function(a, b) { + SimpleTest.requestFlakyTimeout("Just testing to make sure things work."); setTimeout(a, 2000); })); - } else { + } else if (foobarCounter == 2) { // The second time we don't reply at all. + info("Setting a promise object without resolving it."); navigator.mozSetMessageHandlerPromise(new Promise(function(a, b) {})); + } else if (foobarCounter == 3) { + info("Throwing an exception."); + // Now we throw an exception + SimpleTest.expectUncaughtException(); + throw "Booom!"; + } else { + info("Setting a promise object and reject it."); + navigator.mozSetMessageHandlerPromise(new Promise(function(a, b) { + setTimeout(b, 0); + })); } } else if (e.task == 'pending') { - ok(true, "pending message received: " + pendingCounter); - if (++pendingCounter == 2) { + ok(true, "pending message received: " + ++pendingCounter); + if (pendingCounter == 5) { runTests(); } } @@ -47,7 +59,7 @@ } function test_register_foobar() { - navigator.sync.register('foobar', { minInterval: 5, + navigator.sync.register('foobar', { minInterval: 2, oneShot: false, data: 42, wifiOnly: false, @@ -59,7 +71,7 @@ } function test_register_pending() { - navigator.sync.register('pending', { minInterval: 6, + navigator.sync.register('pending', { minInterval: 2, oneShot: false, data: 'hello world!', wifiOnly: false, @@ -94,7 +106,7 @@ function() { SpecialPowers.pushPrefEnv({"set": [["dom.requestSync.enabled", true], ["dom.requestSync.minInterval", 1], - ["dom.requestSync.maxTaskTimeout", 10000 /* 10 seconds */], + ["dom.requestSync.maxTaskTimeout", 5000 /* 5 seconds */], ["dom.ignore_webidl_scope_checks", true]]}, runTests); }, From 5f7eacef1ca2d30fa06331ace7a982f3e67ca24a Mon Sep 17 00:00:00 2001 From: "Jason Orendorff ext:(%2C%20Till%20Schneidereit%20%3Ctill%40tillschneidereit.net%3E)" Date: Mon, 29 Sep 2014 11:38:53 -0500 Subject: [PATCH 14/16] Bug 911142 - Make the "length" property of function objects configurable. r=Waldo. Thanks to Till Schneidereit for a bunch of test fixes. --- ...est_WeakMap.prototype-properties.html.json | 6 +- .../tests/for-of/string-iterator-surfaces.js | 2 +- js/src/jsfun.cpp | 27 ++++-- js/src/jsfun.h | 26 +++--- js/src/tests/ecma/Array/15.4.4.3-1.js | 2 - js/src/tests/ecma/Array/15.4.4.4-1.js | 10 --- js/src/tests/ecma/Array/15.4.4.4-2.js | 2 - js/src/tests/ecma/GlobalObject/15.1.2.2-1.js | 15 ---- js/src/tests/ecma/GlobalObject/15.1.2.3-1.js | 2 - js/src/tests/ecma/GlobalObject/15.1.2.4.js | 2 - js/src/tests/ecma/GlobalObject/15.1.2.5-1.js | 2 - js/src/tests/ecma/GlobalObject/15.1.2.6.js | 2 - js/src/tests/ecma/GlobalObject/15.1.2.7.js | 2 - js/src/tests/ecma/String/15.5.4.10-1.js | 2 - js/src/tests/ecma/String/15.5.4.11-1.js | 2 - js/src/tests/ecma/String/15.5.4.6-2.js | 2 - js/src/tests/ecma/String/15.5.4.7-2.js | 2 - js/src/tests/ecma/String/15.5.4.8-1.js | 2 - js/src/tests/ecma/String/15.5.4.9-1.js | 2 - js/src/tests/ecma/extensions/15.1.2.1-1.js | 1 - js/src/tests/ecma_5/Function/function-bind.js | 4 +- js/src/tests/ecma_5/Object/15.2.3.3-01.js | 4 +- .../ecma_5/Object/defineProperty-setup.js | 57 +++--------- js/src/tests/ecma_5/strict/15.3.5.1.js | 3 - js/src/tests/ecma_6/Function/browser.js | 0 .../Function/configurable-length-builtins.js | 21 +++++ .../ecma_6/Function/configurable-length.js | 86 +++++++++++++++++++ js/src/tests/ecma_6/Function/shell.js | 0 js/src/tests/jstests.list | 30 +++++++ .../WeakMap.prototype-properties.html.ini | 11 +++ 30 files changed, 207 insertions(+), 122 deletions(-) create mode 100644 js/src/tests/ecma_6/Function/browser.js create mode 100644 js/src/tests/ecma_6/Function/configurable-length-builtins.js create mode 100644 js/src/tests/ecma_6/Function/configurable-length.js create mode 100644 js/src/tests/ecma_6/Function/shell.js diff --git a/dom/imptests/failures/html/js/builtins/test_WeakMap.prototype-properties.html.json b/dom/imptests/failures/html/js/builtins/test_WeakMap.prototype-properties.html.json index 6479d5068e18..79755c8962e3 100644 --- a/dom/imptests/failures/html/js/builtins/test_WeakMap.prototype-properties.html.json +++ b/dom/imptests/failures/html/js/builtins/test_WeakMap.prototype-properties.html.json @@ -1,4 +1,8 @@ { + "WeakMap.prototype.clear.length": true, + "WeakMap.prototype.delete.length": true, "WeakMap.prototype.get.length": true, - "WeakMap.prototype.get: return undefined": true + "WeakMap.prototype.get: return undefined": true, + "WeakMap.prototype.has.length": true, + "WeakMap.prototype.set.length": true } diff --git a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js index a9bf2a69b2ba..c6c919758234 100644 --- a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js +++ b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js @@ -41,7 +41,7 @@ function assertBuiltinFunction(o, name, arity) { value: arity, writable: false, enumerable: false, - configurable: false, + configurable: true }); } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 90a80d11e669..6fab27a1163e 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -484,24 +484,41 @@ js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, bool *resolvedp) return true; } - if (JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().name)) { + bool isLength = JSID_IS_ATOM(id, cx->names().length); + if (isLength || JSID_IS_ATOM(id, cx->names().name)) { MOZ_ASSERT(!IsInternalFunctionObject(obj)); RootedValue v(cx); - if (JSID_IS_ATOM(id, cx->names().length)) { + uint32_t attrs; + if (isLength) { + // Since f.length is configurable, it could be resolved and then deleted: + // function f(x) {} + // assertEq(f.length, 1); + // delete f.length; + // Afterwards, asking for f.length again will cause this resolve + // hook to run again. Defining the property again the second + // time through would be a bug. + // assertEq(f.length, 0); // gets Function.prototype.length! + // We use the RESOLVED_LENGTH flag as a hack to prevent this bug. + if (fun->hasResolvedLength()) + return true; + if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) return false; uint16_t length = fun->hasScript() ? fun->nonLazyScript()->funLength() : fun->nargs() - fun->hasRest(); v.setInt32(length); + attrs = JSPROP_READONLY; } else { v.setString(fun->atom() == nullptr ? cx->runtime()->emptyString : fun->atom()); + attrs = JSPROP_READONLY | JSPROP_PERMANENT; } - if (!DefineNativeProperty(cx, fun, id, v, nullptr, nullptr, - JSPROP_PERMANENT | JSPROP_READONLY)) { + if (!DefineNativeProperty(cx, fun, id, v, nullptr, nullptr, attrs)) return false; - } + + if (isLength) + fun->setResolvedLength(); *resolvedp = true; return true; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 574ec86e48e2..a1c84bfc26b0 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -49,6 +49,7 @@ class JSFunction : public js::NativeObject ASMJS = 0x0800, /* function is an asm.js module or exported function */ INTERPRETED_LAZY = 0x1000, /* function is interpreted but doesn't have a script yet */ ARROW = 0x2000, /* ES6 '(args) => body' syntax */ + RESOLVED_LENGTH = 0x4000, /* f.length has been resolved (see js::fun_resolve). */ /* Derived Flags values for convenience: */ NATIVE_FUN = 0, @@ -128,13 +129,13 @@ class JSFunction : public js::NativeObject bool isSelfHostedBuiltin() const { return flags() & SELF_HOSTED; } bool isSelfHostedConstructor() const { return flags() & SELF_HOSTED_CTOR; } bool hasRest() const { return flags() & HAS_REST; } + bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; } + bool hasScript() const { return flags() & INTERPRETED; } - bool isInterpretedLazy() const { - return flags() & INTERPRETED_LAZY; - } - bool hasScript() const { - return flags() & INTERPRETED; - } + // Arrow functions store their lexical |this| in the first extended slot. + bool isArrow() const { return flags() & ARROW; } + + bool hasResolvedLength() const { return flags() & RESOLVED_LENGTH; } bool hasJITCode() const { if (!hasScript()) @@ -143,9 +144,6 @@ class JSFunction : public js::NativeObject return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript(); } - // Arrow functions store their lexical |this| in the first extended slot. - bool isArrow() const { return flags() & ARROW; } - /* Compound attributes: */ bool isBuiltin() const { return (isNative() && !isAsmJSNative()) || isSelfHostedBuiltin(); @@ -209,8 +207,16 @@ class JSFunction : public js::NativeObject flags_ |= ARROW; } + void setResolvedLength() { + flags_ |= RESOLVED_LENGTH; + } + JSAtom *atom() const { return hasGuessedAtom() ? nullptr : atom_.get(); } - js::PropertyName *name() const { return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName(); } + + js::PropertyName *name() const { + return hasGuessedAtom() || !atom_ ? nullptr : atom_->asPropertyName(); + } + void initAtom(JSAtom *atom) { atom_.init(atom); } JSAtom *displayAtom() const { diff --git a/js/src/tests/ecma/Array/15.4.4.3-1.js b/js/src/tests/ecma/Array/15.4.4.3-1.js index be0a99e03471..dd550e511ee1 100644 --- a/js/src/tests/ecma/Array/15.4.4.3-1.js +++ b/js/src/tests/ecma/Array/15.4.4.3-1.js @@ -30,8 +30,6 @@ writeHeaderToLog( SECTION + " Array.prototype.join()"); var ARR_PROTOTYPE = Array.prototype; new TestCase( SECTION, "Array.prototype.join.length", 1, Array.prototype.join.length ); -new TestCase( SECTION, "delete Array.prototype.join.length", false, delete Array.prototype.join.length ); -new TestCase( SECTION, "delete Array.prototype.join.length; Array.prototype.join.length", 1, eval("delete Array.prototype.join.length; Array.prototype.join.length") ); // case where array length is 0 diff --git a/js/src/tests/ecma/Array/15.4.4.4-1.js b/js/src/tests/ecma/Array/15.4.4.4-1.js index 71d8370ba013..da3e2ecdf0e8 100644 --- a/js/src/tests/ecma/Array/15.4.4.4-1.js +++ b/js/src/tests/ecma/Array/15.4.4.4-1.js @@ -68,16 +68,6 @@ new TestCase( SECTION, 0, Array.prototype.reverse.length ); -new TestCase( SECTION, - "delete Array.prototype.reverse.length", - false, - delete Array.prototype.reverse.length ); - -new TestCase( SECTION, - "delete Array.prototype.reverse.length; Array.prototype.reverse.length", - 0, - eval("delete Array.prototype.reverse.length; Array.prototype.reverse.length") ); - // length of array is 0 new TestCase( SECTION, "var A = new Array(); A.reverse(); A.length", diff --git a/js/src/tests/ecma/Array/15.4.4.4-2.js b/js/src/tests/ecma/Array/15.4.4.4-2.js index 8ffb2d6d0d78..60ad9049f77c 100644 --- a/js/src/tests/ecma/Array/15.4.4.4-2.js +++ b/js/src/tests/ecma/Array/15.4.4.4-2.js @@ -64,8 +64,6 @@ writeHeaderToLog( SECTION + " Array.prototype.reverse()"); var ARR_PROTOTYPE = Array.prototype; new TestCase( SECTION, "Array.prototype.reverse.length", 0, Array.prototype.reverse.length ); -new TestCase( SECTION, "delete Array.prototype.reverse.length", false, delete Array.prototype.reverse.length ); -new TestCase( SECTION, "delete Array.prototype.reverse.length; Array.prototype.reverse.length", 0, eval("delete Array.prototype.reverse.length; Array.prototype.reverse.length") ); // length of array is 0 new TestCase( SECTION, diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js b/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js index e7f097dac784..5e6a57f369f6 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js @@ -99,21 +99,6 @@ new TestCase( SECTION, "var PROPS=''; for ( var p in parseInt ) { PROPS += p; }; PROPS", "", eval("var PROPS=''; for ( var p in parseInt ) { PROPS += p; }; PROPS") ); -new TestCase( SECTION, - "delete parseInt.length", - false, - delete parseInt.length ); - -new TestCase( SECTION, - "delete parseInt.length; parseInt.length", - 2, - eval("delete parseInt.length; parseInt.length") ); - -new TestCase( SECTION, - "parseInt.length = null; parseInt.length", - 2, - eval("parseInt.length = null; parseInt.length") ); - new TestCase( SECTION, "parseInt()", NaN, diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.3-1.js b/js/src/tests/ecma/GlobalObject/15.1.2.3-1.js index 1963c10b0404..46fb82523fa4 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.3-1.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.3-1.js @@ -54,8 +54,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "parseFloat.length", 1, parseFloat.length ); new TestCase( SECTION, "parseFloat.length = null; parseFloat.length", 1, eval("parseFloat.length = null; parseFloat.length") ); -new TestCase( SECTION, "delete parseFloat.length", false, delete parseFloat.length ); -new TestCase( SECTION, "delete parseFloat.length; parseFloat.length", 1, eval("delete parseFloat.length; parseFloat.length") ); new TestCase( SECTION, "var MYPROPS=''; for ( var p in parseFloat ) { MYPROPS += p }; MYPROPS", "", eval("var MYPROPS=''; for ( var p in parseFloat ) { MYPROPS += p }; MYPROPS") ); new TestCase( SECTION, "parseFloat()", Number.NaN, parseFloat() ); diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.4.js b/js/src/tests/ecma/GlobalObject/15.1.2.4.js index 06050bf8754b..f74622edf214 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.4.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.4.js @@ -60,8 +60,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "escape.length", 1, escape.length ); new TestCase( SECTION, "escape.length = null; escape.length", 1, eval("escape.length = null; escape.length") ); -new TestCase( SECTION, "delete escape.length", false, delete escape.length ); -new TestCase( SECTION, "delete escape.length; escape.length", 1, eval("delete escape.length; escape.length") ); new TestCase( SECTION, "var MYPROPS=''; for ( var p in escape ) { MYPROPS+= p}; MYPROPS", "", eval("var MYPROPS=''; for ( var p in escape ) { MYPROPS+= p}; MYPROPS") ); new TestCase( SECTION, "escape()", "undefined", escape() ); diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.5-1.js b/js/src/tests/ecma/GlobalObject/15.1.2.5-1.js index 09fd97cf5ce5..3eccdfe88010 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.5-1.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.5-1.js @@ -58,8 +58,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "unescape.length", 1, unescape.length ); new TestCase( SECTION, "unescape.length = null; unescape.length", 1, eval("unescape.length=null; unescape.length") ); -new TestCase( SECTION, "delete unescape.length", false, delete unescape.length ); -new TestCase( SECTION, "delete unescape.length; unescape.length", 1, eval("delete unescape.length; unescape.length") ); new TestCase( SECTION, "var MYPROPS=''; for ( var p in unescape ) { MYPROPS+= p }; MYPROPS", "", eval("var MYPROPS=''; for ( var p in unescape ) { MYPROPS+= p }; MYPROPS") ); new TestCase( SECTION, "unescape()", "undefined", unescape() ); diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.6.js b/js/src/tests/ecma/GlobalObject/15.1.2.6.js index d46e198d9574..d54358a6d55d 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.6.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.6.js @@ -27,8 +27,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "isNaN.length", 1, isNaN.length ); new TestCase( SECTION, "var MYPROPS=''; for ( var p in isNaN ) { MYPROPS+= p }; MYPROPS", "", eval("var MYPROPS=''; for ( var p in isNaN ) { MYPROPS+= p }; MYPROPS") ); new TestCase( SECTION, "isNaN.length = null; isNaN.length", 1, eval("isNaN.length=null; isNaN.length") ); -new TestCase( SECTION, "delete isNaN.length", false, delete isNaN.length ); -new TestCase( SECTION, "delete isNaN.length; isNaN.length", 1, eval("delete isNaN.length; isNaN.length") ); // new TestCase( SECTION, "isNaN.__proto__", Function.prototype, isNaN.__proto__ ); diff --git a/js/src/tests/ecma/GlobalObject/15.1.2.7.js b/js/src/tests/ecma/GlobalObject/15.1.2.7.js index 05e5fbcbbe95..49797749b8e7 100644 --- a/js/src/tests/ecma/GlobalObject/15.1.2.7.js +++ b/js/src/tests/ecma/GlobalObject/15.1.2.7.js @@ -27,8 +27,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "isFinite.length", 1, isFinite.length ); new TestCase( SECTION, "isFinite.length = null; isFinite.length", 1, eval("isFinite.length=null; isFinite.length") ); -new TestCase( SECTION, "delete isFinite.length", false, delete isFinite.length ); -new TestCase( SECTION, "delete isFinite.length; isFinite.length", 1, eval("delete isFinite.length; isFinite.length") ); new TestCase( SECTION, "var MYPROPS=''; for ( p in isFinite ) { MYPROPS+= p }; MYPROPS", "", eval("var MYPROPS=''; for ( p in isFinite ) { MYPROPS += p }; MYPROPS") ); new TestCase( SECTION, "isFinite()", false, isFinite() ); diff --git a/js/src/tests/ecma/String/15.5.4.10-1.js b/js/src/tests/ecma/String/15.5.4.10-1.js index 5fc127ead7e2..90ad5f026476 100644 --- a/js/src/tests/ecma/String/15.5.4.10-1.js +++ b/js/src/tests/ecma/String/15.5.4.10-1.js @@ -52,8 +52,6 @@ var TITLE = "String.prototype.substring( start, end )"; writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "String.prototype.substring.length", 2, String.prototype.substring.length ); -new TestCase( SECTION, "delete String.prototype.substring.length", false, delete String.prototype.substring.length ); -new TestCase( SECTION, "delete String.prototype.substring.length; String.prototype.substring.length", 2, eval("delete String.prototype.substring.length; String.prototype.substring.length") ); // test cases for when substring is called with no arguments. diff --git a/js/src/tests/ecma/String/15.5.4.11-1.js b/js/src/tests/ecma/String/15.5.4.11-1.js index 94006a6a2afb..08b1fa68d680 100644 --- a/js/src/tests/ecma/String/15.5.4.11-1.js +++ b/js/src/tests/ecma/String/15.5.4.11-1.js @@ -33,8 +33,6 @@ var TITLE = "String.prototype.toLowerCase()"; writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "String.prototype.toLowerCase.length", 0, String.prototype.toLowerCase.length ); -new TestCase( SECTION, "delete String.prototype.toLowerCase.length", false, delete String.prototype.toLowerCase.length ); -new TestCase( SECTION, "delete String.prototype.toLowerCase.length; String.prototype.toLowerCase.length", 0, eval("delete String.prototype.toLowerCase.length; String.prototype.toLowerCase.length") ); // Basic Latin, Latin-1 Supplement, Latin Extended A for ( var i = 0; i <= 0x017f; i++ ) { diff --git a/js/src/tests/ecma/String/15.5.4.6-2.js b/js/src/tests/ecma/String/15.5.4.6-2.js index a760bf7dfe36..ee78b2b39f73 100644 --- a/js/src/tests/ecma/String/15.5.4.6-2.js +++ b/js/src/tests/ecma/String/15.5.4.6-2.js @@ -72,8 +72,6 @@ new TestCase( SECTION, new TestCase( SECTION, "String.prototype.indexOf.length", 1, String.prototype.indexOf.length ); new TestCase( SECTION, "String.prototype.indexOf.length = null; String.prototype.indexOf.length", 1, eval("String.prototype.indexOf.length = null; String.prototype.indexOf.length") ); -new TestCase( SECTION, "delete String.prototype.indexOf.length", false, delete String.prototype.indexOf.length ); -new TestCase( SECTION, "delete String.prototype.indexOf.length; String.prototype.indexOf.length", 1, eval("delete String.prototype.indexOf.length; String.prototype.indexOf.length") ); new TestCase( SECTION, "var s = new String(); s.indexOf()", diff --git a/js/src/tests/ecma/String/15.5.4.7-2.js b/js/src/tests/ecma/String/15.5.4.7-2.js index 96c97ec1c0c8..eda13ebd1947 100644 --- a/js/src/tests/ecma/String/15.5.4.7-2.js +++ b/js/src/tests/ecma/String/15.5.4.7-2.js @@ -53,8 +53,6 @@ writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "String.prototype.lastIndexOf.length", 1, String.prototype.lastIndexOf.length ); -new TestCase( SECTION, "delete String.prototype.lastIndexOf.length", false, delete String.prototype.lastIndexOf.length ); -new TestCase( SECTION, "delete String.prototype.lastIndexOf.length; String.prototype.lastIndexOf.length", 1, eval("delete String.prototype.lastIndexOf.length; String.prototype.lastIndexOf.length" ) ); new TestCase( SECTION, "var s = new String(''); s.lastIndexOf('', 0)", LastIndexOf("","",0), eval("var s = new String(''); s.lastIndexOf('', 0)") ); new TestCase( SECTION, "var s = new String(''); s.lastIndexOf('')", LastIndexOf("",""), eval("var s = new String(''); s.lastIndexOf('')") ); diff --git a/js/src/tests/ecma/String/15.5.4.8-1.js b/js/src/tests/ecma/String/15.5.4.8-1.js index 97b5df7182c5..344444ca4165 100644 --- a/js/src/tests/ecma/String/15.5.4.8-1.js +++ b/js/src/tests/ecma/String/15.5.4.8-1.js @@ -39,8 +39,6 @@ var TITLE = "String.prototype.split"; writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "String.prototype.split.length", 2, String.prototype.split.length ); -new TestCase( SECTION, "delete String.prototype.split.length", false, delete String.prototype.split.length ); -new TestCase( SECTION, "delete String.prototype.split.length; String.prototype.split.length", 2, eval("delete String.prototype.split.length; String.prototype.split.length") ); // test cases for when split is called with no arguments. diff --git a/js/src/tests/ecma/String/15.5.4.9-1.js b/js/src/tests/ecma/String/15.5.4.9-1.js index 9392e329a8ba..329600bf8a11 100644 --- a/js/src/tests/ecma/String/15.5.4.9-1.js +++ b/js/src/tests/ecma/String/15.5.4.9-1.js @@ -42,8 +42,6 @@ var TITLE = "String.prototype.substring( start )"; writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "String.prototype.substring.length", 2, String.prototype.substring.length ); -new TestCase( SECTION, "delete String.prototype.substring.length", false, delete String.prototype.substring.length ); -new TestCase( SECTION, "delete String.prototype.substring.length; String.prototype.substring.length", 2, eval("delete String.prototype.substring.length; String.prototype.substring.length") ); // test cases for when substring is called with no arguments. diff --git a/js/src/tests/ecma/extensions/15.1.2.1-1.js b/js/src/tests/ecma/extensions/15.1.2.1-1.js index d5aa8d3a9081..3119abd6d190 100644 --- a/js/src/tests/ecma/extensions/15.1.2.1-1.js +++ b/js/src/tests/ecma/extensions/15.1.2.1-1.js @@ -23,7 +23,6 @@ startTest(); writeHeaderToLog( SECTION + " "+ TITLE); new TestCase( SECTION, "eval.length", 1, eval.length ); -new TestCase( SECTION, "delete eval.length", false, delete eval.length ); new TestCase( SECTION, "var PROPS = ''; for ( p in eval ) { PROPS += p }; PROPS", "", eval("var PROPS = ''; for ( p in eval ) { PROPS += p }; PROPS") ); new TestCase( SECTION, "eval.length = null; eval.length", 1, eval( "eval.length = null; eval.length") ); // new TestCase( SECTION, "eval.__proto__", Function.prototype, eval.__proto__ ); diff --git a/js/src/tests/ecma_5/Function/function-bind.js b/js/src/tests/ecma_5/Function/function-bind.js index 96ce0c7d3e5a..a9333b0fb1d5 100644 --- a/js/src/tests/ecma_5/Function/function-bind.js +++ b/js/src/tests/ecma_5/Function/function-bind.js @@ -212,14 +212,14 @@ var len1Desc = assertEq(len1Desc.value, 3); assertEq(len1Desc.writable, false); assertEq(len1Desc.enumerable, false); -assertEq(len1Desc.configurable, false); +assertEq(len1Desc.configurable, true); var len2Desc = Object.getOwnPropertyDescriptor(function(a, b, c){}.bind(null, 2), "length"); assertEq(len2Desc.value, 2); assertEq(len2Desc.writable, false); assertEq(len2Desc.enumerable, false); -assertEq(len2Desc.configurable, false); +assertEq(len2Desc.configurable, true); /* diff --git a/js/src/tests/ecma_5/Object/15.2.3.3-01.js b/js/src/tests/ecma_5/Object/15.2.3.3-01.js index c9b9df39fb35..7675416d8be8 100644 --- a/js/src/tests/ecma_5/Object/15.2.3.3-01.js +++ b/js/src/tests/ecma_5/Object/15.2.3.3-01.js @@ -234,7 +234,7 @@ expected = value: 0, writable: false, enumerable: false, - configurable: false + configurable: true }; expectDescriptor(pd, expected); @@ -258,7 +258,7 @@ expected = value: 1, writable: false, enumerable: false, - configurable: false + configurable: true }; expectDescriptor(pd, expected); diff --git a/js/src/tests/ecma_5/Object/defineProperty-setup.js b/js/src/tests/ecma_5/Object/defineProperty-setup.js index 6994d1de443a..da6fe8f49681 100644 --- a/js/src/tests/ecma_5/Object/defineProperty-setup.js +++ b/js/src/tests/ecma_5/Object/defineProperty-setup.js @@ -520,8 +520,6 @@ function mapTestDescriptors(filter) var ALL_DESCRIPTORS = mapTestDescriptors(function(d) { return true; }); var VALID_DESCRIPTORS = mapTestDescriptors(isValidDescriptor); -var SKIP_FULL_FUNCTION_LENGTH_TESTS = true; - function TestRunner() { this._logLines = []; @@ -535,20 +533,11 @@ TestRunner.prototype = var self = this; function functionLengthTests() { - if (SKIP_FULL_FUNCTION_LENGTH_TESTS) - { - print("Skipping full tests for redefining Function.length for now " + - "because we don't support redefinition of properties with " + - "native getter or setter..."); - self._simpleFunctionLengthTests(); - } - else - { - self._simpleFunctionLengthTests(); - self._fullFunctionLengthTests(function() { }, 0); - self._fullFunctionLengthTests(function(one) { }, 1); - self._fullFunctionLengthTests(function(one, two) { }, 2); - } + self._fullFunctionLengthTests(() => Function("one", "/* body */"), 1); + self._fullFunctionLengthTests(() => function(one, two, three=null) { }, 2); + self._fullFunctionLengthTests(() => (one, two, ...etc) => 0, 2); + self._fullFunctionLengthTests(() => ({method(){}}.method), 0); + self._fullFunctionLengthTests(() => Object.getOwnPropertyDescriptor({set x(v){}}, "x").set, 1); } this._runTestSet(functionLengthTests, "Function length tests completed!"); @@ -737,39 +726,15 @@ TestRunner.prototype = if (errorCount > 0) throw errorCount + " errors detected, FAIL"; }, - _simpleFunctionLengthTests: function _simpleFunctionLengthTests(fun) + _fullFunctionLengthTests: function _fullFunctionLengthTests(funFactory, len) { - print("Running simple Function.length tests now.."); + print("Running Function.length (" + funFactory + ") tests now..."); - function expectThrowTypeError(o, p, desc) + for (var i = 0, sz = VALID_DESCRIPTORS.length; i < sz; i++) { - var err = "", passed = false; - try - { - Object.defineProperty(o, p, desc); - } - catch (e) - { - err = e; - passed = e instanceof TypeError; - } - assertEq(passed, true, fun + " didn't throw TypeError when called: " + err); + var desc = VALID_DESCRIPTORS[i]; + this._runSingleFunctionLengthTest(funFactory(), len, desc); } - - expectThrowTypeError(function a() { }, "length", { value: 1 }); - expectThrowTypeError(function a() { }, "length", { enumerable: true }); - expectThrowTypeError(function a() { }, "length", { configurable: true }); - expectThrowTypeError(function a() { }, "length", { writable: true }); - }, - _fullFunctionLengthTests: function _fullFunctionLengthTests(fun) - { - var len = fun.length; - print("Running Function.length (" + len + ") tests now..."); - - var desc; - var gen = new DescriptorState(); - while ((desc = gen.nextDescriptor())) - this._runSingleFunctionLengthTest(fun, len, desc); }, _log: function _log(v) { @@ -982,7 +947,7 @@ TestRunner.prototype = { value: len, enumerable: false, - configurable: false, + configurable: true, writable: false }); diff --git a/js/src/tests/ecma_5/strict/15.3.5.1.js b/js/src/tests/ecma_5/strict/15.3.5.1.js index 612faf5e81db..163928ee4177 100644 --- a/js/src/tests/ecma_5/strict/15.3.5.1.js +++ b/js/src/tests/ecma_5/strict/15.3.5.1.js @@ -12,8 +12,5 @@ function fn() { assertEq(testLenientAndStrict('var f = fn(); f.length = 1; f.length', returns(3), raisesException(TypeError)), true); -assertEq(testLenientAndStrict('var f = fn(); delete f.length', - returns(false), raisesException(TypeError)), - true); reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Function/browser.js b/js/src/tests/ecma_6/Function/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/Function/configurable-length-builtins.js b/js/src/tests/ecma_6/Function/configurable-length-builtins.js new file mode 100644 index 000000000000..f28eaae15763 --- /dev/null +++ b/js/src/tests/ecma_6/Function/configurable-length-builtins.js @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Deleting .length from a variety of builtin functions works as expected. +for (var fun of [Math.sin, Array.prototype.map, eval]) { + assertEq(delete fun.length, true); + assertEq(fun.hasOwnProperty("length"), false); + assertEq("length" in fun, true); // still inheriting Function.prototype.length + assertEq(fun.length, 0); + + // The inherited property is nonwritable, so assigning still fails + // (silently, in sloppy mode). + fun.length = Math.hypot; + assertEq(fun.length, 0); + + // It can be shadowed via defineProperty. + Object.defineProperty(fun, "length", {value: Math.hypot}); + assertEq(fun.length, Math.hypot); +} + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/ecma_6/Function/configurable-length.js b/js/src/tests/ecma_6/Function/configurable-length.js new file mode 100644 index 000000000000..127038fb56a5 --- /dev/null +++ b/js/src/tests/ecma_6/Function/configurable-length.js @@ -0,0 +1,86 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Very simple initial test that the "length" property of a function is +// configurable. More thorough tests follow. +var f = function (a1, a2, a3, a4) {}; +assertEq(delete f.length, true); +assertEq(f.hasOwnProperty("length"), false); +assertEq(f.length, 0); // inherited from Function.prototype.length +assertEq(delete Function.prototype.length, true); +assertEq(f.length, undefined); + + +// Now for the details. +// +// Many of these tests are poking at the "resolve hook" mechanism SM uses to +// lazily create this property, which is wonky and deserving of some extra +// skepticism. + +// We've deleted Function.prototype.length. Check that the resolve hook does +// not resurrect it. +assertEq("length" in Function.prototype, false); +Function.prototype.length = 7; +assertEq(Function.prototype.length, 7); +delete Function.prototype.length; +assertEq(Function.prototype.length, undefined); + +// OK, define Function.prototype.length back to its original state per spec, so +// the remaining tests can run in a more typical environment. +Object.defineProperty(Function.prototype, "length", {value: 0, configurable: true}); + +// Check the property descriptor of a function length property. +var g = function f(a1, a2, a3, a4, a5) {}; +var desc = Object.getOwnPropertyDescriptor(g, "length"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, false); +assertEq(desc.value, 5); + +// After deleting the length property, assigning to f.length fails because +// Function.prototype.length is non-writable. In strict mode it would throw. +delete g.length; +g.length = 12; +assertEq(g.hasOwnProperty("length"), false); +assertEq(g.length, 0); + +// After deleting both the length property and Function.prototype.length, +// assigning to f.length creates a new plain old data property. +delete Function.prototype.length; +g.length = 13; +var desc = Object.getOwnPropertyDescriptor(g, "length"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, true); +assertEq(desc.writable, true); +assertEq(desc.value, 13); + +// Deleting the .length of one instance of a FunctionDeclaration does not +// affect other instances. +function mkfun() { + function fun(a1, a2, a3, a4, a5) {} + return fun; +} +g = mkfun(); +var h = mkfun(); +delete h.length; +assertEq(g.length, 5); +assertEq(mkfun().length, 5); + +// Object.defineProperty on a brand-new function is sufficient to cause the +// LENGTH_RESOLVED flag to be set. +g = mkfun(); +Object.defineProperty(g, "length", {value: 0}); +assertEq(delete g.length, true); +assertEq(g.hasOwnProperty("length"), false); + +// Object.defineProperty on a brand-new function correctly merges new +// attributes with the builtin ones. +g = mkfun(); +Object.defineProperty(g, "length", { value: 42 }); +desc = Object.getOwnPropertyDescriptor(g, "length"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, false); +assertEq(desc.value, 42); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/ecma_6/Function/shell.js b/js/src/tests/ecma_6/Function/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 9479841d2501..26861ed683f4 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -4,6 +4,36 @@ skip-if(!this.hasOwnProperty("Intl")) include test262/intl402/jstests.list # Intl is not enabled in all builds skip script ecma_6/String/normalize-generateddata-input.js # input data for other test +################################################### +# Test262 tests skipped due to SpiderMonkey fixes # +################################################### + +# These tests assert that function.length is non-configurable. It was +# non-configurable in ES5, but ES6 changes this and we have implemented the +# newer standard (bug 911142). +skip script test262/intl402/ch13/13.1/13.1.1_L15.js +skip script test262/intl402/ch13/13.2/13.2.1_L15.js +skip script test262/intl402/ch13/13.3/13.3.1_L15.js +skip script test262/intl402/ch13/13.3/13.3.2_L15.js +skip script test262/intl402/ch13/13.3/13.3.3_L15.js +skip script test262/intl402/ch10/10.2/10.2.2_L15.js +skip script test262/intl402/ch10/10.3/10.3.2_1_a_L15.js +skip script test262/intl402/ch10/10.3/10.3.3_L15.js +skip script test262/intl402/ch10/10.3/10.3.2_L15.js +skip script test262/intl402/ch10/10.1/10.1_L15.js +skip script test262/intl402/ch11/11.2/11.2.2_L15.js +skip script test262/intl402/ch11/11.1/11.1_L15.js +skip script test262/intl402/ch11/11.3/11.3.3_L15.js +skip script test262/intl402/ch11/11.3/11.3.2_L15.js +skip script test262/intl402/ch11/11.3/11.3.2_1_a_L15.js +skip script test262/intl402/ch12/12.3/12.3.3_L15.js +skip script test262/intl402/ch12/12.3/12.3.2_L15.js +skip script test262/intl402/ch12/12.3/12.3.2_1_a_L15.js +skip script test262/intl402/ch12/12.1/12.1_L15.js +skip script test262/intl402/ch12/12.2/12.2.2_L15.js +skip script test262/ch13/13.2/13.2-15-1.js +skip script test262/ch11/11.4/11.4.1/11.4.1-5-a-28-s.js + ################################################## # Test262 tests skipped due to SpiderMonkey bugs # ################################################## diff --git a/testing/web-platform/meta/js/builtins/WeakMap.prototype-properties.html.ini b/testing/web-platform/meta/js/builtins/WeakMap.prototype-properties.html.ini index 5f6ed28b952b..f69bc9887502 100644 --- a/testing/web-platform/meta/js/builtins/WeakMap.prototype-properties.html.ini +++ b/testing/web-platform/meta/js/builtins/WeakMap.prototype-properties.html.ini @@ -1,8 +1,19 @@ [WeakMap.prototype-properties.html] type: testharness + [WeakMap.prototype.clear.length] + expected: FAIL + + [WeakMap.prototype.delete.length] + expected: FAIL + [WeakMap.prototype.get.length] expected: FAIL [WeakMap.prototype.get: return undefined] expected: FAIL + [WeakMap.prototype.has.length] + expected: FAIL + + [WeakMap.prototype.set.length] + expected: FAIL From c8acfd64a8d1be8fafef7191e2a6ad4eeb2e8d81 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Mon, 22 Dec 2014 11:03:05 -0500 Subject: [PATCH 15/16] bug 1113065 - spdy should not close session on imglib error r=bagder r=seth --- image/src/imgRequest.cpp | 2 +- netwerk/protocol/http/ASpdySession.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/image/src/imgRequest.cpp b/image/src/imgRequest.cpp index 5495f529ee5a..9298f1c9a254 100644 --- a/image/src/imgRequest.cpp +++ b/image/src/imgRequest.cpp @@ -931,7 +931,7 @@ imgRequest::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, // We allow multipart images to fail to initialize without cancelling the // load because subsequent images might be fine; thus only single part // images end up here. - this->Cancel(NS_ERROR_FAILURE); + this->Cancel(NS_IMAGELIB_ERROR_FAILURE); return NS_BINDING_ABORTED; } diff --git a/netwerk/protocol/http/ASpdySession.h b/netwerk/protocol/http/ASpdySession.h index d553f7f80ce0..c2807e4601ad 100644 --- a/netwerk/protocol/http/ASpdySession.h +++ b/netwerk/protocol/http/ASpdySession.h @@ -50,12 +50,26 @@ public: // scenarios. const static uint32_t kInitialRwin = 256 * 1024 * 1024; + // soft errors are errors that terminate a stream without terminating the + // connection. In general non-network errors are stream errors as well + // as network specific items like cancels. bool SoftStreamError(nsresult code) { if (NS_SUCCEEDED(code)) { return false; } + // this could go either way, but because there are network instances of + // it being a hard error we should consider it hard. + if (code == NS_ERROR_FAILURE) { + return false; + } + + if (NS_ERROR_GET_MODULE(code) != NS_ERROR_MODULE_NETWORK) { + return true; + } + + // these are network specific soft errors return (code == NS_BASE_STREAM_CLOSED || code == NS_BINDING_FAILED || code == NS_BINDING_ABORTED || code == NS_BINDING_REDIRECTED || code == NS_ERROR_INVALID_CONTENT_ENCODING || From 415bfb677611b219c3fbfd7b4e0c315194f81b15 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 4 Jan 2015 08:40:27 -0800 Subject: [PATCH 16/16] Backed out 7 changesets (bug 1018320) Backed out changeset 2ef1c26d77d3 (bug 1018320) Backed out changeset bce9ed290ddd (bug 1018320) Backed out changeset 8c01c134e40f (bug 1018320) Backed out changeset 46353577ef7a (bug 1018320) Backed out changeset edf5737d6e0e (bug 1018320) Backed out changeset c6fcdd1c681f (bug 1018320) Backed out changeset 5e26604cc6e0 (bug 1018320) --- b2g/chrome/content/shell.js | 1 - b2g/installer/package-manifest.in | 3 - browser/installer/package-manifest.in | 4 - dom/base/Navigator.cpp | 27 - dom/base/Navigator.h | 2 - dom/messages/SystemMessageInternal.js | 111 +-- dom/messages/SystemMessageManager.js | 74 +- .../SystemMessagePermissionsChecker.jsm | 1 - .../nsIDOMNavigatorSystemMessages.idl | 7 +- .../interfaces/nsISystemMessagesInternal.idl | 7 +- dom/moz.build | 1 - dom/requestsync/RequestSync.manifest | 5 - dom/requestsync/RequestSyncApp.jsm | 48 - dom/requestsync/RequestSyncManager.js | 124 --- dom/requestsync/RequestSyncScheduler.js | 101 --- dom/requestsync/RequestSyncService.jsm | 849 ------------------ dom/requestsync/RequestSyncTask.jsm | 101 --- dom/requestsync/RequestSyncWifiService.cpp | 67 -- dom/requestsync/RequestSyncWifiService.h | 43 - dom/requestsync/moz.build | 32 - dom/requestsync/tests/common_app.js | 15 - dom/requestsync/tests/common_basic.js | 187 ---- dom/requestsync/tests/file_app.sjs | 54 -- .../tests/file_app.template.webapp | 6 - dom/requestsync/tests/file_basic_app.html | 62 -- dom/requestsync/tests/file_interface.html | 21 - dom/requestsync/tests/mochitest.ini | 19 - dom/requestsync/tests/test_basic.html | 79 -- dom/requestsync/tests/test_basic_app.html | 135 --- dom/requestsync/tests/test_minInterval.html | 65 -- dom/requestsync/tests/test_promise.html | 56 -- dom/requestsync/tests/test_promise_app.html | 150 ---- dom/requestsync/tests/test_wakeUp.html | 151 ---- dom/requestsync/tests/test_webidl.html | 86 -- .../mochitest/general/test_interfaces.html | 8 - dom/webidl/Navigator.webidl | 6 - dom/webidl/RequestSyncManager.webidl | 54 -- dom/webidl/RequestSyncScheduler.webidl | 38 - dom/webidl/moz.build | 2 - layout/build/nsLayoutStatics.cpp | 5 - mobile/android/installer/package-manifest.in | 3 - 41 files changed, 27 insertions(+), 2783 deletions(-) delete mode 100644 dom/requestsync/RequestSync.manifest delete mode 100644 dom/requestsync/RequestSyncApp.jsm delete mode 100644 dom/requestsync/RequestSyncManager.js delete mode 100644 dom/requestsync/RequestSyncScheduler.js delete mode 100644 dom/requestsync/RequestSyncService.jsm delete mode 100644 dom/requestsync/RequestSyncTask.jsm delete mode 100644 dom/requestsync/RequestSyncWifiService.cpp delete mode 100644 dom/requestsync/RequestSyncWifiService.h delete mode 100644 dom/requestsync/moz.build delete mode 100644 dom/requestsync/tests/common_app.js delete mode 100644 dom/requestsync/tests/common_basic.js delete mode 100644 dom/requestsync/tests/file_app.sjs delete mode 100644 dom/requestsync/tests/file_app.template.webapp delete mode 100644 dom/requestsync/tests/file_basic_app.html delete mode 100644 dom/requestsync/tests/file_interface.html delete mode 100644 dom/requestsync/tests/mochitest.ini delete mode 100644 dom/requestsync/tests/test_basic.html delete mode 100644 dom/requestsync/tests/test_basic_app.html delete mode 100644 dom/requestsync/tests/test_minInterval.html delete mode 100644 dom/requestsync/tests/test_promise.html delete mode 100644 dom/requestsync/tests/test_promise_app.html delete mode 100644 dom/requestsync/tests/test_wakeUp.html delete mode 100644 dom/requestsync/tests/test_webidl.html delete mode 100644 dom/webidl/RequestSyncManager.webidl delete mode 100644 dom/webidl/RequestSyncScheduler.webidl diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 6c67b80d14b1..96c7f5f3a495 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -15,7 +15,6 @@ Cu.import('resource://gre/modules/UserAgentOverrides.jsm'); Cu.import('resource://gre/modules/Keyboard.jsm'); Cu.import('resource://gre/modules/ErrorPage.jsm'); Cu.import('resource://gre/modules/AlertsHelper.jsm'); -Cu.import('resource://gre/modules/RequestSyncService.jsm'); #ifdef MOZ_WIDGET_GONK Cu.import('resource://gre/modules/NetworkStatsService.jsm'); Cu.import('resource://gre/modules/ResourceStatsService.jsm'); diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 6ab59790e423..9ff9063e6226 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -342,9 +342,6 @@ @BINPATH@/components/zipwriter.xpt ; JavaScript components -@BINPATH@/components/RequestSync.manifest -@BINPATH@/components/RequestSyncManager.js -@BINPATH@/components/RequestSyncScheduler.js @BINPATH@/components/ChromeNotifications.js @BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 99dc6dd6bf5f..a2f944bb616d 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -545,10 +545,6 @@ @RESPATH@/components/htmlMenuBuilder.js @RESPATH@/components/htmlMenuBuilder.manifest -@RESPATH@/components/RequestSync.manifest -@RESPATH@/components/RequestSyncManager.js -@RESPATH@/components/RequestSyncScheduler.js - @RESPATH@/components/PermissionSettings.js @RESPATH@/components/PermissionSettings.manifest @RESPATH@/components/ContactManager.js diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 81d842f25b9e..bafb53ba05f8 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1847,33 +1847,6 @@ Navigator::MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv) return result; } -void -Navigator::MozSetMessageHandlerPromise(Promise& aPromise, - ErrorResult& aRv) -{ - // The WebIDL binding is responsible for the pref check here. - aRv = EnsureMessagesManager(); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - bool result = false; - aRv = mMessagesManager->MozIsHandlingMessage(&result); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - if (!result) { - aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); - return; - } - - aRv = mMessagesManager->MozSetMessageHandlerPromise(&aPromise); - if (NS_WARN_IF(aRv.Failed())) { - return; - } -} - void Navigator::MozSetMessageHandler(const nsAString& aType, systemMessageCallback* aCallback, diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 7d7daff15ad1..3852e49c9011 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -235,8 +235,6 @@ public: systemMessageCallback* aCallback, ErrorResult& aRv); bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv); - void MozSetMessageHandlerPromise(Promise& aPromise, ErrorResult& aRv); - #ifdef MOZ_B2G already_AddRefed GetMobileIdAssertion(const MobileIdOptions& options, ErrorResult& aRv); diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index bde7b8926d98..f7e160ff61b6 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -46,7 +46,6 @@ const kMessages =["SystemMessageManager:GetPendingMessages", "SystemMessageManager:Message:Return:OK", "SystemMessageManager:AskReadyToRegister", "SystemMessageManager:HandleMessagesDone", - "SystemMessageManager:HandleMessageDone", "child-process-shutdown"] function debug(aMsg) { @@ -85,8 +84,6 @@ function SystemMessageInternal() { this._configurators = {}; - this._pendingPromises = new Map(); - Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "webapps-registry-start", false); Services.obs.addObserver(this, "webapps-registry-ready", false); @@ -179,14 +176,6 @@ SystemMessageInternal.prototype = { }, sendMessage: function(aType, aMessage, aPageURI, aManifestURI, aExtra) { - return new Promise((aResolve, aReject) => { - this.sendMessageInternal(aType, aMessage, aPageURI, aManifestURI, aExtra, - aResolve, aReject); - }); - }, - - sendMessageInternal: function(aType, aMessage, aPageURI, aManifestURI, - aExtra, aResolvePromiseCb, aRejectPromiseCb) { // Buffer system messages until the webapps' registration is ready, // so that we can know the correct pages registered to be sent. if (!this._webappsRegistryReady) { @@ -195,22 +184,15 @@ SystemMessageInternal.prototype = { msg: aMessage, pageURI: aPageURI, manifestURI: aManifestURI, - extra: aExtra, - resolvePromiseCb: aResolvePromiseCb, - rejectPromiseCb: aRejectPromiseCb }); + extra: aExtra }); return; } // Give this message an ID so that we can identify the message and // clean it up from the pending message queue when apps receive it. let messageID = gUUIDGenerator.generateUUID().toString(); + let manifestURL = aManifestURI.spec; - - let pendingPromise = { resolvePromiseCb: aResolvePromiseCb, - rejectPromiseCb: aRejectPromiseCb, - manifestURL: manifestURL, - counter: 0 }; - let pageURLs = []; if (aPageURI) { pageURLs.push(aPageURI.spec); @@ -244,9 +226,6 @@ SystemMessageInternal.prototype = { return; } - // For each page we must receive a confirm. - ++pendingPromise.counter; - let page = this._findPage(aType, aPageURL, manifestURL); if (page) { // Queue this message in the corresponding pages. @@ -254,12 +233,7 @@ SystemMessageInternal.prototype = { this._openAppPage(page, aMessage, aExtra, result); } - }, this); - - if (pendingPromise.counter) { - this._pendingPromises.set(messageID, pendingPromise); - } }, broadcastMessage: function(aType, aMessage, aExtra) { @@ -419,8 +393,7 @@ SystemMessageInternal.prototype = { "SystemMessageManager:GetPendingMessages", "SystemMessageManager:HasPendingMessages", "SystemMessageManager:Message:Return:OK", - "SystemMessageManager:HandleMessagesDone", - "SystemMessageManager:HandleMessageDone"].indexOf(aMessage.name) != -1) { + "SystemMessageManager:HandleMessagesDone"].indexOf(aMessage.name) != -1) { if (!aMessage.target.assertContainApp(msg.manifestURL)) { debug("Got message from a child process containing illegal manifest URL."); return null; @@ -469,8 +442,6 @@ SystemMessageInternal.prototype = { manifestURL, true, null); - - this._rejectPendingPromisesFromManifestURL(manifestURL); } break; } @@ -482,7 +453,6 @@ SystemMessageInternal.prototype = { msg.manifestURL, false, msg.pageURL); - this._rejectPendingPromisesFromManifestURL(msg.manifestURL); break; } case "SystemMessageManager:GetPendingMessages": @@ -550,25 +520,6 @@ SystemMessageInternal.prototype = { } break; } - case "SystemMessageManager:HandleMessageDone": - { - debug("received SystemMessageManager:HandleMessageDone " + msg.type + - " with msgID " + msg.msgID + " for " + msg.pageURL + - " @ " + msg.manifestURL + " - promise rejected: " + msg.rejected); - - // Maybe this should resolve/reject a pending promise. - if (msg.rejected) { - this._rejectPendingPromises(msg.msgID); - } else { - this._resolvePendingPromises(msg.msgID); - } - - // A page has finished handling some of its system messages, so we try - // to release the CPU wake lock we acquired on behalf of that page. - this._releaseCpuWakeLock(this._createKeyForPage(msg), 1); - break; - } - case "SystemMessageManager:HandleMessagesDone": { debug("received SystemMessageManager:HandleMessagesDone " + msg.type + @@ -596,7 +547,6 @@ SystemMessageInternal.prototype = { ppmm = null; this._pages = null; this._bufferedSysMsgs = null; - this._pendingPromises.clear(); break; case "webapps-registry-start": this._webappsRegistryReady = false; @@ -608,10 +558,9 @@ SystemMessageInternal.prototype = { this._bufferedSysMsgs.forEach(function(aSysMsg) { switch (aSysMsg.how) { case "send": - this.sendMessageInternal( + this.sendMessage( aSysMsg.type, aSysMsg.msg, - aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra, - aSysMsg.resolvePromiseCb, aSysMsg.rejectPromiseCb); + aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra); break; case "broadcast": this.broadcastMessage(aSysMsg.type, aSysMsg.msg, aSysMsg.extra); @@ -647,9 +596,6 @@ SystemMessageInternal.prototype = { " from registered pages due to app uninstallation."); } } - - this._rejectPendingPromisesFromManifestURL(manifestURL); - debug("Finish updating registered pages for an uninstalled app."); break; } @@ -746,10 +692,9 @@ SystemMessageInternal.prototype = { appPageIsRunning = true; // We need to acquire a CPU wake lock for that page and expect that - // we'll receive a "SystemMessageManager:HandleMessagesDone" or a - // "SystemMessageManager:HandleMessageDone" message when the page - // finishes handling the system message. At that point, we'll release - // the lock we acquired. + // we'll receive a "SystemMessageManager:HandleMessagesDone" message + // when the page finishes handling the system message. At that point, + // we'll release the lock we acquired. this._acquireCpuWakeLock(pageKey); // Multiple windows can share the same target (process), the content @@ -769,9 +714,8 @@ SystemMessageInternal.prototype = { // The app page isn't running and relies on the 'open-app' chrome event to // wake it up. We still need to acquire a CPU wake lock for that page and // expect that we will receive a "SystemMessageManager:HandleMessagesDone" - // or a "SystemMessageManager:HandleMessageDone" message when the page - // finishes handling the system message with other pending messages. At - // that point, we'll release the lock we acquired. + // message when the page finishes handling the system message with other + // pending messages. At that point, we'll release the lock we acquired. this._acquireCpuWakeLock(pageKey); return MSG_SENT_FAILURE_APP_NOT_RUNNING; } else { @@ -780,41 +724,6 @@ SystemMessageInternal.prototype = { }, - _resolvePendingPromises: function(aMessageID) { - if (!this._pendingPromises.has(aMessageID)) { - debug("Unknown pendingPromise messageID. This seems a bug!!"); - return; - } - - let obj = this._pendingPromises.get(aMessageID); - if (!--obj.counter) { - obj.resolvePromiseCb(); - this._pendingPromises.delete(aMessageID); - } - }, - - _rejectPendingPromises: function(aMessageID) { - if (!this._pendingPromises.has(aMessageID)) { - debug("Unknown pendingPromise messageID. This seems a bug!!"); - return; - } - - let obj = this._pendingPromises.get(aMessageID); - if (!--obj.counter) { - obj.rejectPromiseCb(); - this._pendingPromises.delete(aMessageID); - } - }, - - _rejectPendingPromisesFromManifestURL: function(aManifestURL) { - for (var [i, obj] of this._pendingPromises) { - if (obj.manifestURL == aManifestURL) { - obj.rejectPromiseCb(); - this._pendingPromises.delete(i); - } - } - }, - classID: Components.ID("{70589ca5-91ac-4b9e-b839-d6a88167d714}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesInternal, diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index b2864e757ff2..202f5bfd6b7c 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -42,10 +42,6 @@ function SystemMessageManager() { // Flag to specify if this process has already registered the manifest URL. this._registerManifestURLReady = false; - // Used to know if the promise has to be accepted or not. - this._isHandling = false; - this._promise = null; - // Flag to determine this process is a parent or child process. let appInfo = Cc["@mozilla.org/xre/app-info;1"]; this._isParentProcess = @@ -61,7 +57,7 @@ function SystemMessageManager() { SystemMessageManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, - _dispatchMessage: function(aType, aDispatcher, aMessage, aMessageID) { + _dispatchMessage: function(aType, aDispatcher, aMessage) { if (aDispatcher.isHandling) { // Queue up the incomming message if we're currently dispatching a // message; we'll send the message once we finish with the current one. @@ -70,12 +66,11 @@ SystemMessageManager.prototype = { // event loop from within a system message handler (e.g. via alert()), // and we can then try to send the page another message while it's // inside this nested event loop. - aDispatcher.messages.push({ message: aMessage, messageID: aMessageID }); + aDispatcher.messages.push(aMessage); return; } aDispatcher.isHandling = true; - this._isHandling = true; // We get a json blob, but in some cases we want another kind of object // to be dispatched. To do so, we check if we have a valid contract ID of @@ -95,46 +90,22 @@ SystemMessageManager.prototype = { } } - let message = wrapped ? aMessage : Cu.cloneInto(aMessage, this._window); + aDispatcher.handler + .handleMessage(wrapped ? aMessage + : Cu.cloneInto(aMessage, this._window)); - let rejectPromise = false; - try { - aDispatcher.handler.handleMessage(message); - } catch(e) { - rejectPromise = true; - } + // We need to notify the parent one of the system messages has been handled, + // so the parent can release the CPU wake lock it took on our behalf. + cpmm.sendAsyncMessage("SystemMessageManager:HandleMessagesDone", + { type: aType, + manifestURL: this._manifestURL, + pageURL: this._pageURL, + handledCount: 1 }); aDispatcher.isHandling = false; - this._isHandling = false; - - let self = this; - function sendResponse() { - // We need to notify the parent one of the system messages has been - // handled, so the parent can release the CPU wake lock it took on our - // behalf. - cpmm.sendAsyncMessage("SystemMessageManager:HandleMessageDone", - { type: aType, - manifestURL: self._manifestURL, - pageURL: self._pageURL, - msgID: aMessageID, - rejected: rejectPromise }); - } - - if (!this._promise) { - debug("No promise set, sending the response immediately"); - sendResponse(); - } else { - debug("Using the promise to postpone the response."); - this._promise.then(sendResponse, function() { - rejectPromise = true; - sendResponse(); - }); - this._promise = null; - } if (aDispatcher.messages.length > 0) { - let msg = aDispatcher.messages.shift(); - this._dispatchMessage(aType, aDispatcher, msg.message, msg.messageID); + this._dispatchMessage(aType, aDispatcher, aDispatcher.messages.shift()); } else { // No more messages that need to be handled, we can notify the // ContentChild to release the CPU wake lock grabbed by the ContentParent @@ -199,25 +170,9 @@ SystemMessageManager.prototype = { manifestURL: this._manifestURL })[0]; }, - mozIsHandlingMessage: function() { - debug("is handling message: " + this._isHandling); - return this._isHandling; - }, - - mozSetMessageHandlerPromise: function(aPromise) { - debug("setting a promise"); - - if (!this._isHandling) { - throw "Not in a handleMessage method"; - } - - this._promise = aPromise; - }, - uninit: function() { this._dispatchers = null; this._pendings = null; - this._promise = null; if (this._isParentProcess) { Services.obs.removeObserver(this, kSystemMessageInternalReady); @@ -281,9 +236,8 @@ SystemMessageManager.prototype = { } messages.forEach(function(aMsg) { - this._dispatchMessage(msg.type, dispatcher, aMsg, msg.msgID); + this._dispatchMessage(msg.type, dispatcher, aMsg); }, this); - } else { // Since no handlers are registered, we need to notify the parent as if // all the queued system messages have been handled (notice |handledCount: diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index 530c3b8143a1..eec8ba58909d 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -84,7 +84,6 @@ this.SystemMessagePermissionsTable = { "push-register": { "push": [] }, - "request-sync": { }, "sms-delivery-success": { "sms": [] }, diff --git a/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl b/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl index 4fc156c0e9dd..855cfb0c8c70 100644 --- a/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl +++ b/dom/messages/interfaces/nsIDOMNavigatorSystemMessages.idl @@ -10,15 +10,10 @@ interface nsIDOMSystemMessageCallback : nsISupports void handleMessage(in jsval message); }; -[scriptable, uuid(d04d3c11-26aa-46eb-a981-353af101f9cf)] +[scriptable, uuid(091e90dd-0e8b-463d-8cdc-9225d3a6ff90)] interface nsIDOMNavigatorSystemMessages : nsISupports { void mozSetMessageHandler(in DOMString type, in nsIDOMSystemMessageCallback callback); boolean mozHasPendingMessage(in DOMString type); - - // the parameter is a promise object. - void mozSetMessageHandlerPromise(in nsISupports promise); - - bool mozIsHandlingMessage(); }; diff --git a/dom/messages/interfaces/nsISystemMessagesInternal.idl b/dom/messages/interfaces/nsISystemMessagesInternal.idl index dbd48dfb9284..cf3993f242a8 100644 --- a/dom/messages/interfaces/nsISystemMessagesInternal.idl +++ b/dom/messages/interfaces/nsISystemMessagesInternal.idl @@ -9,7 +9,7 @@ interface nsIDOMWindow; // Implemented by the contract id @mozilla.org/system-message-internal;1 -[scriptable, uuid(54c8e274-decb-4258-9a24-4ebfcbf3d00a)] +[scriptable, uuid(6296a314-2abf-4cd0-9097-5e81ee6832e2)] interface nsISystemMessagesInternal : nsISupports { /* @@ -22,11 +22,8 @@ interface nsISystemMessagesInternal : nsISupports * @param manifestURI The webapp's manifest URI. * @param extra Extra opaque information that will be passed around in the observer * notification to open the page. - * returns a Promise */ - nsISupports sendMessage(in DOMString type, in jsval message, - in nsIURI pageURI, in nsIURI manifestURI, - [optional] in jsval extra); + void sendMessage(in DOMString type, in jsval message, in nsIURI pageURI, in nsIURI manifestURI, [optional] in jsval extra); /* * Allow any internal user to broadcast a message of a given type. diff --git a/dom/moz.build b/dom/moz.build index 4b3545d73154..47bcc6dbb178 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -43,7 +43,6 @@ DIRS += [ 'base', 'activities', 'archivereader', - 'requestsync', 'bindings', 'battery', 'browser-element', diff --git a/dom/requestsync/RequestSync.manifest b/dom/requestsync/RequestSync.manifest deleted file mode 100644 index 0f09724b6658..000000000000 --- a/dom/requestsync/RequestSync.manifest +++ /dev/null @@ -1,5 +0,0 @@ -component {8ee5ab74-15c4-478f-9d32-67627b9f0f1a} RequestSyncScheduler.js -contract @mozilla.org/dom/request-sync-scheduler;1 {8ee5ab74-15c4-478f-9d32-67627b9f0f1a} - -component {e6f55080-e549-4e30-9d00-15f240fb763c} RequestSyncManager.js -contract @mozilla.org/dom/request-sync-manager;1 {e6f55080-e549-4e30-9d00-15f240fb763c} diff --git a/dom/requestsync/RequestSyncApp.jsm b/dom/requestsync/RequestSyncApp.jsm deleted file mode 100644 index 69ad349d5e53..000000000000 --- a/dom/requestsync/RequestSyncApp.jsm +++ /dev/null @@ -1,48 +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/. */ - -'use strict'; - -this.EXPORTED_SYMBOLS = ['RequestSyncApp']; - -function debug(s) { - //dump('DEBUG RequestSyncApp: ' + s + '\n'); -} - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); - -this.RequestSyncApp = function(aData) { - debug('created'); - - let keys = [ 'origin', 'manifestURL', 'isInBrowserElement' ]; - for (let i = 0; i < keys.length; ++i) { - if (!(keys[i] in aData)) { - dump("ERROR - RequestSyncApp must receive a full app object: " + keys[i] + " missing."); - throw "ERROR!"; - } - - this["_" + keys[i]] = aData[keys[i]]; - } -} - -this.RequestSyncApp.prototype = { - classDescription: 'RequestSyncApp XPCOM Component', - classID: Components.ID('{5a0b64db-a2be-4f08-a6c5-8bf2e3ae0c57}'), - contractID: '@mozilla.org/dom/request-sync-manager;1', - QueryInterface: XPCOMUtils.generateQI([]), - - get origin() { - return this._origin; - }, - - get manifestURL() { - return this._manifestURL; - }, - - get isInBrowserElement() { - return this._isInBrowserElement; - } -}; diff --git a/dom/requestsync/RequestSyncManager.js b/dom/requestsync/RequestSyncManager.js deleted file mode 100644 index 0e9e53b7ac43..000000000000 --- a/dom/requestsync/RequestSyncManager.js +++ /dev/null @@ -1,124 +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/. */ - -'use strict'; - -function debug(s) { - //dump('DEBUG RequestSyncManager: ' + s + '\n'); -} - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); -Cu.import('resource://gre/modules/RequestSyncApp.jsm'); -Cu.import('resource://gre/modules/RequestSyncTask.jsm'); - -XPCOMUtils.defineLazyServiceGetter(this, "cpmm", - "@mozilla.org/childprocessmessagemanager;1", - "nsIMessageSender"); - -function RequestSyncManager() { - debug('created'); -} - -RequestSyncManager.prototype = { - __proto__: DOMRequestIpcHelper.prototype, - - classDescription: 'RequestSyncManager XPCOM Component', - classID: Components.ID('{e6f55080-e549-4e30-9d00-15f240fb763c}'), - contractID: '@mozilla.org/dom/request-sync-manager;1', - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, - Ci.nsIObserver, - Ci.nsIDOMGlobalPropertyInitializer]), - - _messages: [ "RequestSyncManager:Registrations:Return", - "RequestSyncManager:SetPolicy:Return" ], - - init: function(aWindow) { - debug("init"); - - // DOMRequestIpcHelper.initHelper sets this._window - this.initDOMRequestHelper(aWindow, this._messages); - }, - - sendMessage: function(aMsg, aObj) { - let self = this; - return this.createPromise(function(aResolve, aReject) { - aObj.requestID = - self.getPromiseResolverId({ resolve: aResolve, reject: aReject }); - cpmm.sendAsyncMessage(aMsg, aObj, null, - self._window.document.nodePrincipal); - }); - }, - - registrations: function() { - debug('registrations'); - return this.sendMessage("RequestSyncManager:Registrations", {}); - }, - - setPolicy: function(aTask, aOrigin, aManifestURL, aIsInBrowserElement, - aState, aOverwrittenMinInterval) { - debug('setPolicy'); - - return this.sendMessage("RequestSyncManager:SetPolicy", - { task: aTask, - origin: aOrigin, - manifestURL: aManifestURL, - isInBrowserElement: aIsInBrowserElement, - state: aState, - overwrittenMinInterval: aOverwrittenMinInterval }); - }, - - registrationsResult: function(aData) { - debug("registrationsResult"); - - let results = new this._window.Array(); - for (let i = 0; i < aData.length; ++i) { - if (!("app" in aData[i])) { - dump("ERROR - Serialization error in RequestSyncManager.\n"); - continue; - } - - let app = new RequestSyncApp(aData[i].app); - let exposedApp = - this._window.RequestSyncApp._create(this._window, app); - - let task = new RequestSyncTask(this, this._window, exposedApp, aData[i]); - let exposedTask = - this._window.RequestSyncTask._create(this._window, task); - - results.push(exposedTask); - } - return results; - }, - - receiveMessage: function(aMessage) { - debug('receiveMessage'); - - let req = this.getPromiseResolver(aMessage.data.requestID); - if (!req) { - return; - } - - if ('error' in aMessage.data) { - req.reject(Cu.cloneInto(aMessage.data.error, this._window)); - return; - } - - if (aMessage.name == 'RequestSyncManager:Registrations:Return') { - req.resolve(this.registrationsResult(aMessage.data.results)); - return; - } - - if ('results' in aMessage.data) { - req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); - return; - } - - req.resolve(); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncManager]); diff --git a/dom/requestsync/RequestSyncScheduler.js b/dom/requestsync/RequestSyncScheduler.js deleted file mode 100644 index 774636248939..000000000000 --- a/dom/requestsync/RequestSyncScheduler.js +++ /dev/null @@ -1,101 +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/. */ - -'use strict'; - -function debug(s) { - //dump('DEBUG RequestSyncScheduler: ' + s + '\n'); -} - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import('resource://gre/modules/DOMRequestHelper.jsm'); -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); - -XPCOMUtils.defineLazyServiceGetter(this, 'cpmm', - '@mozilla.org/childprocessmessagemanager;1', - 'nsIMessageSender'); - -function RequestSyncScheduler() { - debug('created'); -} - -RequestSyncScheduler.prototype = { - __proto__: DOMRequestIpcHelper.prototype, - - classDescription: 'RequestSyncScheduler XPCOM Component', - classID: Components.ID('{8ee5ab74-15c4-478f-9d32-67627b9f0f1a}'), - contractID: '@mozilla.org/dom/request-sync-scheduler;1', - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, - Ci.nsIObserver, - Ci.nsIDOMGlobalPropertyInitializer]), - - _messages: [ 'RequestSync:Register:Return', - 'RequestSync:Unregister:Return', - 'RequestSync:Registrations:Return', - 'RequestSync:Registration:Return' ], - - init: function(aWindow) { - debug('init'); - - // DOMRequestIpcHelper.initHelper sets this._window - this.initDOMRequestHelper(aWindow, this._messages); - }, - - register: function(aTask, aParams) { - debug('register'); - return this.sendMessage('RequestSync:Register', - { task: aTask, params: aParams }); - }, - - unregister: function(aTask) { - debug('unregister'); - return this.sendMessage('RequestSync:Unregister', - { task: aTask }); - }, - - registrations: function() { - debug('registrations'); - return this.sendMessage('RequestSync:Registrations', {}); - }, - - registration: function(aTask) { - debug('registration'); - return this.sendMessage('RequestSync:Registration', - { task: aTask }); - }, - - sendMessage: function(aMsg, aObj) { - let self = this; - return this.createPromise(function(aResolve, aReject) { - aObj.requestID = - self.getPromiseResolverId({ resolve: aResolve, reject: aReject }); - cpmm.sendAsyncMessage(aMsg, aObj, null, - self._window.document.nodePrincipal); - }); - }, - - receiveMessage: function(aMessage) { - debug('receiveMessage'); - - let req = this.getPromiseResolver(aMessage.data.requestID); - if (!req) { - return; - } - - if ('error' in aMessage.data) { - req.reject(Cu.cloneInto(aMessage.data.error, this._window)); - return; - } - - if ('results' in aMessage.data) { - req.resolve(Cu.cloneInto(aMessage.data.results, this._window)); - return; - } - - req.resolve(); - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RequestSyncScheduler]); diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm deleted file mode 100644 index 66770d5b620d..000000000000 --- a/dom/requestsync/RequestSyncService.jsm +++ /dev/null @@ -1,849 +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/. */ - -'use strict' - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -function debug(s) { - //dump('DEBUG RequestSyncService: ' + s + '\n'); -} - -const RSYNCDB_VERSION = 1; -const RSYNCDB_NAME = "requestSync"; -const RSYNC_MIN_INTERVAL = 100; - -const RSYNC_OPERATION_TIMEOUT = 120000 // 2 minutes - -const RSYNC_STATE_ENABLED = "enabled"; -const RSYNC_STATE_DISABLED = "disabled"; -const RSYNC_STATE_WIFIONLY = "wifiOnly"; - -Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.importGlobalProperties(["indexedDB"]); - - -XPCOMUtils.defineLazyServiceGetter(this, "appsService", - "@mozilla.org/AppsService;1", - "nsIAppsService"); - -XPCOMUtils.defineLazyServiceGetter(this, "cpmm", - "@mozilla.org/childprocessmessagemanager;1", - "nsISyncMessageSender"); - -XPCOMUtils.defineLazyServiceGetter(this, "ppmm", - "@mozilla.org/parentprocessmessagemanager;1", - "nsIMessageBroadcaster"); - -XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger", - "@mozilla.org/system-message-internal;1", - "nsISystemMessagesInternal"); - -XPCOMUtils.defineLazyServiceGetter(this, "secMan", - "@mozilla.org/scriptsecuritymanager;1", - "nsIScriptSecurityManager"); - -this.RequestSyncService = { - __proto__: IndexedDBHelper.prototype, - - children: [], - - _messages: [ "RequestSync:Register", - "RequestSync:Unregister", - "RequestSync:Registrations", - "RequestSync:Registration", - "RequestSyncManager:Registrations", - "RequestSyncManager:SetPolicy" ], - - _pendingOperation: false, - _pendingMessages: [], - - _registrations: {}, - - _wifi: false, - - _activeTask: null, - _queuedTasks: [], - - // Initialization of the RequestSyncService. - init: function() { - debug("init"); - - this._messages.forEach((function(msgName) { - ppmm.addMessageListener(msgName, this); - }).bind(this)); - - Services.obs.addObserver(this, 'xpcom-shutdown', false); - Services.obs.addObserver(this, 'webapps-clear-data', false); - Services.obs.addObserver(this, 'wifi-state-changed', false); - - this.initDBHelper("requestSync", RSYNCDB_VERSION, [RSYNCDB_NAME]); - - // Loading all the data from the database into the _registrations map. - // Any incoming message will be stored and processed when the async - // operation is completed. - - let self = this; - this.dbTxn("readonly", function(aStore) { - aStore.openCursor().onsuccess = function(event) { - let cursor = event.target.result; - if (cursor) { - self.addRegistration(cursor.value); - cursor.continue(); - } - } - }, - function() { - debug("initialization done"); - }, - function() { - dump("ERROR!! RequestSyncService - Failed to retrieve data from the database.\n"); - }); - }, - - // Shutdown the RequestSyncService. - shutdown: function() { - debug("shutdown"); - - this._messages.forEach((function(msgName) { - ppmm.removeMessageListener(msgName, this); - }).bind(this)); - - Services.obs.removeObserver(this, 'xpcom-shutdown'); - Services.obs.removeObserver(this, 'webapps-clear-data'); - Services.obs.removeObserver(this, 'wifi-state-changed'); - - this.close(); - - // Removing all the registrations will delete the pending timers. - let self = this; - this.forEachRegistration(function(aObj) { - let key = self.principalToKey(aObj.principal); - self.removeRegistrationInternal(aObj.data.task, key); - }); - }, - - observe: function(aSubject, aTopic, aData) { - debug("observe"); - - switch (aTopic) { - case 'xpcom-shutdown': - this.shutdown(); - break; - - case 'webapps-clear-data': - this.clearData(aSubject); - break; - - case 'wifi-state-changed': - this.wifiStateChanged(aSubject == 'enabled'); - break; - - default: - debug("Wrong observer topic: " + aTopic); - break; - } - }, - - // When an app is uninstalled, we have to clean all its tasks. - clearData: function(aData) { - debug('clearData'); - - if (!aData) { - return; - } - - let params = - aData.QueryInterface(Ci.mozIApplicationClearPrivateDataParams); - if (!params) { - return; - } - - // At this point we don't have the origin, so we cannot create the full - // key. Using the partial one is enough to detect the uninstalled app. - var partialKey = params.appId + '|' + params.browserOnly + '|'; - var dbKeys = []; - - for (let key in this._registrations) { - if (key.indexOf(partialKey) != 0) { - continue; - } - - for (let task in this._registrations[key]) { - dbKeys = this._registrations[key][task].dbKey; - this.removeRegistrationInternal(task, key); - } - } - - if (dbKeys.length == 0) { - return; - } - - // Remove the tasks from the database. - this.dbTxn('readwrite', function(aStore) { - for (let i = 0; i < dbKeys.length; ++i) { - aStore.delete(dbKeys[i]); - } - }, - function() { - debug("ClearData completed"); - }, function() { - debug("ClearData failed"); - }); - }, - - // Creation of the schema for the database. - upgradeSchema: function(aTransaction, aDb, aOldVersion, aNewVersion) { - debug('updateSchema'); - aDb.createObjectStore(RSYNCDB_NAME, { autoIncrement: true }); - }, - - // This method generates the key for the indexedDB object storage. - principalToKey: function(aPrincipal) { - return aPrincipal.appId + '|' + - aPrincipal.isInBrowserElement + '|' + - aPrincipal.origin; - }, - - // Add a task to the _registrations map and create the timer if it's needed. - addRegistration: function(aObj) { - debug('addRegistration'); - - let key = this.principalToKey(aObj.principal); - if (!(key in this._registrations)) { - this._registrations[key] = {}; - } - - this.scheduleTimer(aObj); - this._registrations[key][aObj.data.task] = aObj; - }, - - // Remove a task from the _registrations map and delete the timer if it's - // needed. It also checks if the principal is correct before doing the real - // operation. - removeRegistration: function(aTaskName, aKey, aPrincipal) { - debug('removeRegistration'); - - if (!(aKey in this._registrations) || - !(aTaskName in this._registrations[aKey])) { - return false; - } - - // Additional security check. - if (!aPrincipal.equals(this._registrations[aKey][aTaskName].principal)) { - return false; - } - - this.removeRegistrationInternal(aTaskName, aKey); - return true; - }, - - removeRegistrationInternal: function(aTaskName, aKey) { - debug('removeRegistrationInternal'); - - let obj = this._registrations[aKey][aTaskName]; - if (obj.timer) { - obj.timer.cancel(); - } - - // It can be that this task has been already schedulated. - this.removeTaskFromQueue(obj); - - // It can be that this object is already in scheduled, or in the queue of a - // iDB transacation. In order to avoid rescheduling it, we must disable it. - obj.active = false; - - delete this._registrations[aKey][aTaskName]; - - // Lets remove the key in case there are not tasks registered. - for (var key in this._registrations[aKey]) { - return; - } - delete this._registrations[aKey]; - }, - - removeTaskFromQueue: function(aObj) { - let pos = this._queuedTasks.indexOf(aObj); - if (pos != -1) { - this._queuedTasks.splice(pos, 1); - } - }, - - // The communication from the exposed objects and the service is done using - // messages. This function receives and processes them. - receiveMessage: function(aMessage) { - debug("receiveMessage"); - - // We cannot process this request now. - if (this._pendingOperation) { - this._pendingMessages.push(aMessage); - return; - } - - // The principal is used to validate the message. - if (!aMessage.principal) { - return; - } - - let uri = Services.io.newURI(aMessage.principal.origin, null, null); - - let principal; - try { - principal = secMan.getAppCodebasePrincipal(uri, - aMessage.principal.appId, aMessage.principal.isInBrowserElement); - } catch(e) { - return; - } - - if (!principal) { - return; - } - - switch (aMessage.name) { - case "RequestSync:Register": - this.register(aMessage.target, aMessage.data, principal); - break; - - case "RequestSync:Unregister": - this.unregister(aMessage.target, aMessage.data, principal); - break; - - case "RequestSync:Registrations": - this.registrations(aMessage.target, aMessage.data, principal); - break; - - case "RequestSync:Registration": - this.registration(aMessage.target, aMessage.data, principal); - break; - - case "RequestSyncManager:Registrations": - this.managerRegistrations(aMessage.target, aMessage.data, principal); - break; - - case "RequestSyncManager:SetPolicy": - this.managerSetPolicy(aMessage.target, aMessage.data, principal); - break; - - default: - debug("Wrong message: " + aMessage.name); - break; - } - }, - - // Basic validation. - validateRegistrationParams: function(aParams) { - if (aParams === null) { - return false; - } - - // We must have a page. - if (!("wakeUpPage" in aParams) || - aParams.wakeUpPage.length == 0) { - return false; - } - - let minInterval = RSYNC_MIN_INTERVAL; - try { - minInterval = Services.prefs.getIntPref("dom.requestSync.minInterval"); - } catch(e) {} - - if (!("minInterval" in aParams) || - aParams.minInterval < minInterval) { - return false; - } - - return true; - }, - - // Registration of a new task. - register: function(aTarget, aData, aPrincipal) { - debug("register"); - - if (!this.validateRegistrationParams(aData.params)) { - aTarget.sendAsyncMessage("RequestSync:Register:Return", - { requestID: aData.requestID, - error: "ParamsError" } ); - return; - } - - let key = this.principalToKey(aPrincipal); - if (key in this._registrations && - aData.task in this._registrations[key]) { - // if this task already exists we overwrite it. - this.removeRegistrationInternal(aData.task, key); - } - - // This creates a RequestTaskFull object. - aData.params.task = aData.task; - aData.params.lastSync = 0; - aData.params.principal = aPrincipal; - - aData.params.state = RSYNC_STATE_ENABLED; - if (aData.params.wifiOnly) { - aData.params.state = RSYNC_STATE_WIFIONLY; - } - - aData.params.overwrittenMinInterval = 0; - - let dbKey = aData.task + "|" + - aPrincipal.appId + '|' + - aPrincipal.isInBrowserElement + '|' + - aPrincipal.origin; - - let data = { principal: aPrincipal, - dbKey: dbKey, - data: aData.params, - active: true, - timer: null }; - - let self = this; - this.dbTxn('readwrite', function(aStore) { - aStore.put(data, data.dbKey); - }, - function() { - self.addRegistration(data); - aTarget.sendAsyncMessage("RequestSync:Register:Return", - { requestID: aData.requestID }); - }, - function() { - aTarget.sendAsyncMessage("RequestSync:Register:Return", - { requestID: aData.requestID, - error: "IndexDBError" } ); - }); - }, - - // Unregister a task. - unregister: function(aTarget, aData, aPrincipal) { - debug("unregister"); - - let key = this.principalToKey(aPrincipal); - if (!(key in this._registrations) || - !(aData.task in this._registrations[key])) { - aTarget.sendAsyncMessage("RequestSync:Unregister:Return", - { requestID: aData.requestID, - error: "UnknownTaskError" }); - return; - } - - let dbKey = this._registrations[key][aData.task].dbKey; - this.removeRegistration(aData.task, key, aPrincipal); - - let self = this; - this.dbTxn('readwrite', function(aStore) { - aStore.delete(dbKey); - }, - function() { - aTarget.sendAsyncMessage("RequestSync:Unregister:Return", - { requestID: aData.requestID }); - }, - function() { - aTarget.sendAsyncMessage("RequestSync:Unregister:Return", - { requestID: aData.requestID, - error: "IndexDBError" } ); - }); - }, - - // Get the list of registered tasks for this principal. - registrations: function(aTarget, aData, aPrincipal) { - debug("registrations"); - - let results = []; - let key = this.principalToKey(aPrincipal); - if (key in this._registrations) { - for (let i in this._registrations[key]) { - results.push(this.createPartialTaskObject( - this._registrations[key][i].data)); - } - } - - aTarget.sendAsyncMessage("RequestSync:Registrations:Return", - { requestID: aData.requestID, - results: results }); - }, - - // Get a particular registered task for this principal. - registration: function(aTarget, aData, aPrincipal) { - debug("registration"); - - let results = null; - let key = this.principalToKey(aPrincipal); - if (key in this._registrations && - aData.task in this._registrations[key]) { - results = this.createPartialTaskObject( - this._registrations[key][aData.task].data); - } - - aTarget.sendAsyncMessage("RequestSync:Registration:Return", - { requestID: aData.requestID, - results: results }); - }, - - // Get the list of the registered tasks. - managerRegistrations: function(aTarget, aData, aPrincipal) { - debug("managerRegistrations"); - - let results = []; - let self = this; - this.forEachRegistration(function(aObj) { - results.push(self.createFullTaskObject(aObj.data)); - }); - - aTarget.sendAsyncMessage("RequestSyncManager:Registrations:Return", - { requestID: aData.requestID, - results: results }); - }, - - // Set a policy to a task. - managerSetPolicy: function(aTarget, aData, aPrincipal) { - debug("managerSetPolicy"); - - let toSave = null; - let self = this; - this.forEachRegistration(function(aObj) { - if (aObj.principal.isInBrowserElement != aData.isInBrowserElement || - aObj.principal.origin != aData.origin) { - return; - } - - let app = appsService.getAppByLocalId(aObj.principal.appId); - if (app && app.manifestURL != aData.manifestURL || - (!app && aData.manifestURL != "")) { - return; - } - - if ("overwrittenMinInterval" in aData) { - aObj.data.overwrittenMinInterval = aData.overwrittenMinInterval; - } - - aObj.data.state = aData.state; - - if (toSave) { - dump("ERROR!! RequestSyncService - SetPolicy matches more than 1 task.\n"); - return; - } - - toSave = aObj; - }); - - if (!toSave) { - aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return", - { requestID: aData.requestID, error: "UnknownTaskError" }); - return; - } - - this.updateObjectInDB(toSave, function() { - self.scheduleTimer(toSave); - aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return", - { requestID: aData.requestID }); - }); - }, - - // We cannot expose the full internal object to content but just a subset. - // This method creates this subset. - createPartialTaskObject: function(aObj) { - return { task: aObj.task, - lastSync: aObj.lastSync, - oneShot: aObj.oneShot, - minInterval: aObj.minInterval, - wakeUpPage: aObj.wakeUpPage, - wifiOnly: aObj.wifiOnly, - data: aObj.data }; - }, - - createFullTaskObject: function(aObj) { - let obj = this.createPartialTaskObject(aObj); - - obj.app = { manifestURL: '', - origin: aObj.principal.origin, - isInBrowserElement: aObj.principal.isInBrowserElement }; - - let app = appsService.getAppByLocalId(aObj.principal.appId); - if (app) { - obj.app.manifestURL = app.manifestURL; - } - - obj.state = aObj.state; - obj.overwrittenMinInterval = aObj.overwrittenMinInterval; - return obj; - }, - - // Creation of the timer for a particular task object. - scheduleTimer: function(aObj) { - debug("scheduleTimer"); - - if (aObj.timer) { - aObj.timer.cancel(); - aObj.timer = null; - } - - // A registration can be already inactive if it was 1 shot. - if (!aObj.active) { - return; - } - - if (aObj.data.state == RSYNC_STATE_DISABLED) { - return; - } - - // WifiOnly check. - if (aObj.data.state == RSYNC_STATE_WIFIONLY && !this._wifi) { - return; - } - - aObj.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - let interval = aObj.data.minInterval; - if (aObj.data.overwrittenMinInterval > 0) { - interval = aObj.data.overwrittenMinInterval; - } - - let self = this; - aObj.timer.initWithCallback(function() { self.timeout(aObj); }, - interval * 1000, - Ci.nsITimer.TYPE_ONE_SHOT); - }, - - timeout: function(aObj) { - debug("timeout"); - - if (this._activeTask) { - debug("queueing tasks"); - // We have an active task, let's queue this as next task. - if (this._queuedTasks.indexOf(aObj) == -1) { - this._queuedTasks.push(aObj); - } - return; - } - - let app = appsService.getAppByLocalId(aObj.principal.appId); - if (!app) { - dump("ERROR!! RequestSyncService - Failed to retrieve app data from a principal.\n"); - aObj.active = false; - this.updateObjectInDB(aObj); - return; - } - - let manifestURL = Services.io.newURI(app.manifestURL, null, null); - let pageURL = Services.io.newURI(aObj.data.wakeUpPage, null, aObj.principal.URI); - - // Maybe need to be rescheduled? - if (this.hasPendingMessages('request-sync', manifestURL, pageURL)) { - this.scheduleTimer(aObj); - return; - } - - aObj.timer = null; - this._activeTask = aObj; - - if (!manifestURL || !pageURL) { - dump("ERROR!! RequestSyncService - Failed to create URI for the page or the manifest\n"); - aObj.active = false; - this.updateObjectInDB(aObj); - return; - } - - // We don't want to run more than 1 task at the same time. We do this using - // the promise created by sendMessage(). But if the task takes more than - // RSYNC_OPERATION_TIMEOUT millisecs, we have to ignore the promise and - // continue processing other tasks. - - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - let done = false; - let self = this; - function taskCompleted() { - debug("promise or timeout for task calls taskCompleted"); - - if (!done) { - done = true; - self.operationCompleted(); - } - - timer.cancel(); - timer = null; - } - - let timeout = RSYNC_OPERATION_TIMEOUT; - try { - let tmp = Services.prefs.getIntPref("dom.requestSync.maxTaskTimeout"); - timeout = tmp; - } catch(e) {} - - timer.initWithCallback(function() { - debug("Task is taking too much, let's ignore the promise."); - taskCompleted(); - }, timeout, Ci.nsITimer.TYPE_ONE_SHOT); - - // Sending the message. - debug("Sending message."); - let promise = - systemMessenger.sendMessage('request-sync', - this.createPartialTaskObject(aObj.data), - pageURL, manifestURL); - - promise.then(function() { - debug("promise resolved"); - taskCompleted(); - }, function() { - debug("promise rejected"); - taskCompleted(); - }); - }, - - operationCompleted: function() { - debug("operationCompleted"); - - if (!this._activeTask) { - dump("ERROR!! RequestSyncService - OperationCompleted called without an active task\n"); - return; - } - - // One shot? Then this is not active. - this._activeTask.active = !this._activeTask.data.oneShot; - this._activeTask.data.lastSync = new Date(); - - let self = this; - this.updateObjectInDB(this._activeTask, function() { - // SchedulerTimer creates a timer and a nsITimer cannot be cloned. This - // is the reason why this operation has to be done after storing the task - // into IDB. - if (!self._activeTask.data.oneShot) { - self.scheduleTimer(self._activeTask); - } - - self.processNextTask(); - }); - }, - - processNextTask: function() { - debug("processNextTask"); - - this._activeTask = null; - - if (this._queuedTasks.length == 0) { - return; - } - - let task = this._queuedTasks.shift(); - this.timeout(task); - }, - - hasPendingMessages: function(aMessageName, aManifestURL, aPageURL) { - let hasPendingMessages = - cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages", - { type: aMessageName, - pageURL: aPageURL.spec, - manifestURL: aManifestURL.spec })[0]; - - debug("Pending messages: " + hasPendingMessages); - return hasPendingMessages; - }, - - // Update the object into the database. - updateObjectInDB: function(aObj, aCb) { - debug("updateObjectInDB"); - - this.dbTxn('readwrite', function(aStore) { - aStore.put(aObj, aObj.dbKey); - }, - function() { - if (aCb) { - aCb(); - } - debug("UpdateObjectInDB completed"); - }, function() { - debug("UpdateObjectInDB failed"); - }); - }, - - pendingOperationStarted: function() { - debug('pendingOperationStarted'); - this._pendingOperation = true; - }, - - pendingOperationDone: function() { - debug('pendingOperationDone'); - - this._pendingOperation = false; - - // managing the pending messages now that the initialization is completed. - while (this._pendingMessages.length) { - this.receiveMessage(this._pendingMessages.shift()); - } - }, - - // This method creates a transaction and runs callbacks. Plus it manages the - // pending operations system. - dbTxn: function(aType, aCb, aSuccessCb, aErrorCb) { - debug('dbTxn'); - - this.pendingOperationStarted(); - - let self = this; - this.newTxn(aType, RSYNCDB_NAME, function(aTxn, aStore) { - aCb(aStore); - }, - function() { - self.pendingOperationDone(); - aSuccessCb(); - }, - function() { - self.pendingOperationDone(); - aErrorCb(); - }); - }, - - forEachRegistration: function(aCb) { - // This method is used also to remove registations from the map, so we have - // to make a new list and let _registations free to be used. - let list = []; - for (var key in this._registrations) { - for (var task in this._registrations[key]) { - list.push(this._registrations[key][task]); - } - } - - for (var i = 0; i < list.length; ++i) { - aCb(list[i]); - } - }, - - wifiStateChanged: function(aEnabled) { - debug("onWifiStateChanged"); - this._wifi = aEnabled; - - if (!this._wifi) { - // Disable all the wifiOnly tasks. - let self = this; - this.forEachRegistration(function(aObj) { - if (aObj.data.state == RSYNC_STATE_WIFIONLY && aObj.timer) { - aObj.timer.cancel(); - aObj.timer = null; - - // It can be that this task has been already schedulated. - self.removeTaskFromQueue(aObj); - } - }); - return; - } - - // Enable all the tasks. - let self = this; - this.forEachRegistration(function(aObj) { - if (aObj.active && !aObj.timer) { - if (!aObj.data.wifiOnly) { - dump("ERROR - Found a disabled task that is not wifiOnly."); - } - - self.scheduleTimer(aObj); - } - }); - } -} - -RequestSyncService.init(); - -this.EXPORTED_SYMBOLS = [""]; diff --git a/dom/requestsync/RequestSyncTask.jsm b/dom/requestsync/RequestSyncTask.jsm deleted file mode 100644 index 4360e3de3884..000000000000 --- a/dom/requestsync/RequestSyncTask.jsm +++ /dev/null @@ -1,101 +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/. */ - -'use strict'; - -this.EXPORTED_SYMBOLS = ['RequestSyncTask']; - -function debug(s) { - //dump('DEBUG RequestSyncTask: ' + s + '\n'); -} - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import('resource://gre/modules/XPCOMUtils.jsm'); - -this.RequestSyncTask = function(aManager, aWindow, aApp, aData) { - debug('created'); - - this._manager = aManager; - this._window = aWindow; - this._app = aApp; - - let keys = [ 'task', 'lastSync', 'oneShot', 'minInterval', 'wakeUpPage', - 'wifiOnly', 'data', 'state', 'overwrittenMinInterval' ]; - for (let i = 0; i < keys.length; ++i) { - if (!(keys[i] in aData)) { - dump("ERROR - RequestSyncTask must receive a fully app object: " + keys[i] + " missing."); - throw "ERROR!"; - } - - this["_" + keys[i]] = aData[keys[i]]; - } -} - -this.RequestSyncTask.prototype = { - classDescription: 'RequestSyncTask XPCOM Component', - classID: Components.ID('{a1e1c9c6-ce42-49d4-b8b4-fbd686d8fdd9}'), - contractID: '@mozilla.org/dom/request-sync-manager;1', - QueryInterface: XPCOMUtils.generateQI([]), - - get app() { - return this._app; - }, - - get state() { - return this._state; - }, - - get overwrittenMinInterval() { - return this._overwrittenMinInterval; - }, - - get task() { - return this._task; - }, - - get lastSync() { - return this._lastSync; - }, - - get wakeUpPage() { - return this._wakeUpPage; - }, - - get oneShot() { - return this._oneShot; - }, - - get minInterval() { - return this._minInterval; - }, - - get wifiOnly() { - return this._wifiOnly; - }, - - get data() { - return this._data; - }, - - setPolicy: function(aState, aOverwrittenMinInterval) { - debug("setPolicy"); - let self = this; - - return new this._window.Promise(function(aResolve, aReject) { - let p = self._manager.setPolicy(self._task, self._app.origin, - self._app.manifestURL, - self._app.isInBrowserElement, - aState, - aOverwrittenMinInterval); - - // Set the new value only when the promise is resolved. - p.then(function() { - self._state = aState; - self._overwrittenMinInterval = aOverwrittenMinInterval; - aResolve(); - }, aReject); - }); - } -}; diff --git a/dom/requestsync/RequestSyncWifiService.cpp b/dom/requestsync/RequestSyncWifiService.cpp deleted file mode 100644 index 0b7c9968a70e..000000000000 --- a/dom/requestsync/RequestSyncWifiService.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* 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 "RequestSyncWifiService.h" -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/Services.h" -#include "mozilla/StaticPtr.h" -#include "nsIObserverService.h" - -namespace mozilla { -namespace dom { - -using namespace hal; - -NS_IMPL_ISUPPORTS0(RequestSyncWifiService) - -namespace { - -StaticRefPtr sService; - -} // anonymous namespace - -/* static */ void -RequestSyncWifiService::Init() -{ - nsRefPtr service = GetInstance(); - if (!service) { - NS_WARNING("Failed to initialize RequestSyncWifiService."); - } -} - -/* static */ already_AddRefed -RequestSyncWifiService::GetInstance() -{ - if (!sService) { - sService = new RequestSyncWifiService(); - hal::RegisterNetworkObserver(sService); - ClearOnShutdown(&sService); - } - - nsRefPtr service = sService.get(); - return service.forget(); -} - -void -RequestSyncWifiService::Notify(const hal::NetworkInformation& aNetworkInfo) -{ - bool isWifi = aNetworkInfo.isWifi(); - if (isWifi == mIsWifi) { - return; - } - - mIsWifi = isWifi; - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nullptr, "wifi-state-changed", - mIsWifi ? MOZ_UTF16("enabled") : - MOZ_UTF16("disabled")); - } -} - -} // dom namespace -} // mozilla namespace diff --git a/dom/requestsync/RequestSyncWifiService.h b/dom/requestsync/RequestSyncWifiService.h deleted file mode 100644 index 902104b0da36..000000000000 --- a/dom/requestsync/RequestSyncWifiService.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* 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_RequestSyncWifiService_h -#define mozilla_dom_RequestSyncWifiService_h - -#include "mozilla/dom/network/Types.h" -#include "mozilla/Hal.h" -#include "nsIObserver.h" - -namespace mozilla { -namespace dom { - -class RequestSyncWifiService MOZ_FINAL : public nsISupports - , public NetworkObserver -{ -public: - NS_DECL_ISUPPORTS - - static void Init(); - - static already_AddRefed GetInstance(); - - void Notify(const hal::NetworkInformation& aNetworkInfo); - -private: - RequestSyncWifiService() - : mIsWifi(false) - {} - - ~RequestSyncWifiService() - {} - - bool mIsWifi; -}; - -} // dom namespace -} // mozilla namespace - -#endif // mozilla_dom_RequestSyncWifiService_h diff --git a/dom/requestsync/moz.build b/dom/requestsync/moz.build deleted file mode 100644 index 3dbdf4b3168e..000000000000 --- a/dom/requestsync/moz.build +++ /dev/null @@ -1,32 +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/. - -MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] - -EXPORTS.mozilla.dom += [ - 'RequestSyncWifiService.h', -] - -EXTRA_COMPONENTS += [ - 'RequestSync.manifest', - 'RequestSyncManager.js', - 'RequestSyncScheduler.js', -] - -EXTRA_JS_MODULES += [ - 'RequestSyncApp.jsm', - 'RequestSyncService.jsm', - 'RequestSyncTask.jsm', -] - -SOURCES += [ - 'RequestSyncWifiService.cpp', -] - -include('/ipc/chromium/chromium-config.mozbuild') - -FAIL_ON_WARNINGS = True -FINAL_LIBRARY = 'xul' diff --git a/dom/requestsync/tests/common_app.js b/dom/requestsync/tests/common_app.js deleted file mode 100644 index e2371ed0dc43..000000000000 --- a/dom/requestsync/tests/common_app.js +++ /dev/null @@ -1,15 +0,0 @@ -function is(a, b, msg) { - alert((a === b ? 'OK' : 'KO') + ' ' + msg) -} - -function ok(a, msg) { - alert((a ? 'OK' : 'KO')+ ' ' + msg) -} - -function cbError() { - alert('KO error'); -} - -function finish() { - alert('DONE'); -} diff --git a/dom/requestsync/tests/common_basic.js b/dom/requestsync/tests/common_basic.js deleted file mode 100644 index bf0f4fd0aa84..000000000000 --- a/dom/requestsync/tests/common_basic.js +++ /dev/null @@ -1,187 +0,0 @@ -function test_registerFailure() { - ok("sync" in navigator, "navigator.sync exists"); - - navigator.sync.register().then( - function() { - ok(false, "navigator.sync.register() throws without a task name"); - }, function() { - ok(true, "navigator.sync.register() throws without a task name"); - }) - - .then(function() { - return navigator.sync.register(42); - }).then(function() { - ok(false, "navigator.sync.register() throws without a string task name"); - }, function() { - ok(true, "navigator.sync.register() throws without a string task name"); - }) - - .then(function() { - return navigator.sync.register('foobar'); - }).then(function() { - ok(false, "navigator.sync.register() throws without a param dictionary"); - }, function() { - ok(true, "navigator.sync.register() throws without a param dictionary"); - }) - - .then(function() { - return navigator.sync.register('foobar', 42); - }).then(function() { - ok(false, "navigator.sync.register() throws without a real dictionary"); - }, function() { - ok(true, "navigator.sync.register() throws without a real dictionary"); - }) - - .then(function() { - return navigator.sync.register('foobar', {}); - }).then(function() { - ok(false, "navigator.sync.register() throws without a minInterval and wakeUpPage"); - }, function() { - ok(true, "navigator.sync.register() throws without a minInterval and wakeUpPage"); - }) - - .then(function() { - return navigator.sync.register('foobar', { minInterval: 100 }); - }).then(function() { - ok(false, "navigator.sync.register() throws without a wakeUpPage"); - }, function() { - ok(true, "navigator.sync.register() throws without a wakeUpPage"); - }) - - .then(function() { - return navigator.sync.register('foobar', { wakeUpPage: 100 }); - }).then(function() { - ok(false, "navigator.sync.register() throws without a minInterval"); - }, function() { - ok(true, "navigator.sync.register() throws without a minInterval"); - }) - - .then(function() { - runTests(); - }); -} - -function genericError() { - ok(false, "Some promise failed"); -} - -function test_register() { - navigator.sync.register('foobar', { minInterval: 5, wakeUpPage:'/' }).then( - function() { - ok(true, "navigator.sync.register() worked!"); - runTests(); - }, genericError); -} - -function test_unregister() { - navigator.sync.unregister('foobar').then( - function() { - ok(true, "navigator.sync.unregister() worked!"); - runTests(); - }, genericError); -} - -function test_unregisterDuplicate() { - navigator.sync.unregister('foobar').then( - genericError, - function(error) { - ok(true, "navigator.sync.unregister() should throw if the task doesn't exist."); - ok(error, "UnknownTaskError", "Duplicate unregistration error is correct"); - runTests(); - }); -} - -function test_registrationEmpty() { - navigator.sync.registration('bar').then( - function(results) { - is(results, null, "navigator.sync.registration() should return null."); - runTests(); - }, - genericError); -} - -function test_registration() { - navigator.sync.registration('foobar').then( - function(results) { - is(results.task, 'foobar', "navigator.sync.registration().task is correct"); - ok("lastSync" in results, "navigator.sync.registration().lastSync is correct"); - is(results.oneShot, true, "navigator.sync.registration().oneShot is correct"); - is(results.minInterval, 5, "navigator.sync.registration().minInterval is correct"); - ok("wakeUpPage" in results, "navigator.sync.registration().wakeUpPage is correct"); - ok("wifiOnly" in results, "navigator.sync.registration().wifiOnly is correct"); - ok("data" in results, "navigator.sync.registration().data is correct"); - ok(!("app" in results), "navigator.sync.registrations().app is correct"); - runTests(); - }, - genericError); -} - -function test_registrationsEmpty() { - navigator.sync.registrations().then( - function(results) { - is(results.length, 0, "navigator.sync.registrations() should return an empty array."); - runTests(); - }, - genericError); -} - -function test_registrations() { - navigator.sync.registrations().then( - function(results) { - is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); - is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct"); - ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct"); - is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct"); - is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct"); - ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct"); - ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct"); - ok("data" in results[0], "navigator.sync.registrations()[0].data is correct"); - ok(!("app" in results[0]), "navigator.sync.registrations()[0].app is correct"); - runTests(); - }, - genericError); -} - -function test_managerRegistrationsEmpty() { - navigator.syncManager.registrations().then( - function(results) { - is(results.length, 0, "navigator.syncManager.registrations() should return an empty array."); - runTests(); - }, - genericError); -} - -function test_managerRegistrations(state, overwrittenMinInterval) { - navigator.syncManager.registrations().then( - function(results) { - is(results.length, 1, "navigator.sync.registrations() should not return an empty array."); - is(results[0].task, 'foobar', "navigator.sync.registrations()[0].task is correct"); - ok("lastSync" in results[0], "navigator.sync.registrations()[0].lastSync is correct"); - is(results[0].oneShot, true, "navigator.sync.registrations()[0].oneShot is correct"); - is(results[0].minInterval, 5, "navigator.sync.registrations()[0].minInterval is correct"); - ok("wakeUpPage" in results[0], "navigator.sync.registration()[0].wakeUpPage is correct"); - ok("wifiOnly" in results[0], "navigator.sync.registrations()[0].wifiOnly is correct"); - ok("data" in results[0], "navigator.sync.registrations()[0].data is correct"); - ok("app" in results[0], "navigator.sync.registrations()[0].app is correct"); - ok("manifestURL" in results[0].app, "navigator.sync.registrations()[0].app.manifestURL is correct"); - is(results[0].app.origin, 'http://mochi.test:8888', "navigator.sync.registrations()[0].app.origin is correct"); - is(results[0].app.isInBrowserElement, false, "navigator.sync.registrations()[0].app.isInBrowserElement is correct"); - is(results[0].state, state, "navigator.sync.registrations()[0].state is correct"); - is(results[0].overwrittenMinInterval, overwrittenMinInterval, "navigator.sync.registrations()[0].overwrittenMinInterval is correct"); - ok("setPolicy" in results[0], "navigator.sync.registrations()[0].setPolicy is correct"); - runTests(); - }, - genericError); -} - -function test_managerSetPolicy(state, overwrittenMinInterval) { - navigator.syncManager.registrations().then( - function(results) { - results[0].setPolicy(state, overwrittenMinInterval).then( - function() { - ok(state, results[0].state, "State matches"); - ok(overwrittenMinInterval, results[0].overwrittenMinInterval, "OverwrittenMinInterval matches"); - runTests(); - }, genericError); - }).catch(genericError); -} diff --git a/dom/requestsync/tests/file_app.sjs b/dom/requestsync/tests/file_app.sjs deleted file mode 100644 index 4aeedb25f64b..000000000000 --- a/dom/requestsync/tests/file_app.sjs +++ /dev/null @@ -1,54 +0,0 @@ -var gBasePath = "tests/dom/requestsync/tests/"; -var gTemplate = "file_app.template.webapp"; - -function handleRequest(request, response) { - var query = getQuery(request); - - var testToken = ''; - if ('testToken' in query) { - testToken = query.testToken; - } - - var template = gBasePath + gTemplate; - response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); - response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken)); -} - -// Copy-pasted incantations. There ought to be a better way to synchronously read -// a file into a string, but I guess we're trying to discourage that. -function readTemplate(path) { - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("CurWorkD", Components.interfaces.nsILocalFile); - var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. - createInstance(Components.interfaces.nsIFileInputStream); - var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Components.interfaces.nsIConverterInputStream); - var split = path.split("/"); - for(var i = 0; i < split.length; ++i) { - file.append(split[i]); - } - fis.init(file, -1, -1, false); - cis.init(fis, "UTF-8", 0, 0); - - var data = ""; - let (str = {}) { - let read = 0; - do { - read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value - data += str.value; - } while (read != 0); - } - cis.close(); - return data; -} - -function getQuery(request) { - var query = {}; - request.queryString.split('&').forEach(function (val) { - var [name, value] = val.split('='); - query[name] = unescape(value); - }); - return query; -} - diff --git a/dom/requestsync/tests/file_app.template.webapp b/dom/requestsync/tests/file_app.template.webapp deleted file mode 100644 index 49a6158b9bcb..000000000000 --- a/dom/requestsync/tests/file_app.template.webapp +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Really Rapid Release (hosted)", - "description": "Updated even faster than Firefox, just to annoy slashdotters.", - "launch_path": "/tests/dom/requestsync/tests/TESTTOKEN", - "icons": { "128": "default_icon" } -} diff --git a/dom/requestsync/tests/file_basic_app.html b/dom/requestsync/tests/file_basic_app.html deleted file mode 100644 index 28af6a481ee4..000000000000 --- a/dom/requestsync/tests/file_basic_app.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - -
- - - diff --git a/dom/requestsync/tests/file_interface.html b/dom/requestsync/tests/file_interface.html deleted file mode 100644 index fbc40f2300b3..000000000000 --- a/dom/requestsync/tests/file_interface.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - -
- - - diff --git a/dom/requestsync/tests/mochitest.ini b/dom/requestsync/tests/mochitest.ini deleted file mode 100644 index a5fee3b10647..000000000000 --- a/dom/requestsync/tests/mochitest.ini +++ /dev/null @@ -1,19 +0,0 @@ -[DEFAULT] -skip-if = e10s -support-files = - file_app.template.webapp - file_app.sjs - file_basic_app.html - common_app.js - common_basic.js - -[test_webidl.html] -[test_minInterval.html] -[test_basic.html] -[test_basic_app.html] -run-if = buildapp != 'b2g' -[test_wakeUp.html] -run-if = buildapp == 'b2g' && toolkit == 'gonk' -[test_promise.html] -[test_promise_app.html] -run-if = buildapp == 'b2g' && toolkit == 'gonk' diff --git a/dom/requestsync/tests/test_basic.html b/dom/requestsync/tests/test_basic.html deleted file mode 100644 index e95974cad6ff..000000000000 --- a/dom/requestsync/tests/test_basic.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - Test for RequestSync basic use - - - - - - - - diff --git a/dom/requestsync/tests/test_basic_app.html b/dom/requestsync/tests/test_basic_app.html deleted file mode 100644 index bb5baeb4f390..000000000000 --- a/dom/requestsync/tests/test_basic_app.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - Test for requestSync - basic operations in app - - - - - -
- - - diff --git a/dom/requestsync/tests/test_minInterval.html b/dom/requestsync/tests/test_minInterval.html deleted file mode 100644 index 1c877078cb04..000000000000 --- a/dom/requestsync/tests/test_minInterval.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Test for RequestSync minInterval pref - - - - - - - diff --git a/dom/requestsync/tests/test_promise.html b/dom/requestsync/tests/test_promise.html deleted file mode 100644 index 8277eadaa6df..000000000000 --- a/dom/requestsync/tests/test_promise.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - Test for requestSync - promise - - - - - -
- - - diff --git a/dom/requestsync/tests/test_promise_app.html b/dom/requestsync/tests/test_promise_app.html deleted file mode 100644 index 26c2a24e4f82..000000000000 --- a/dom/requestsync/tests/test_promise_app.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - Test for requestSync - promise - - - - - -
- - - diff --git a/dom/requestsync/tests/test_wakeUp.html b/dom/requestsync/tests/test_wakeUp.html deleted file mode 100644 index 14d7ec6040ca..000000000000 --- a/dom/requestsync/tests/test_wakeUp.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Test for requestSync - wakeUp - - - - - -
- - - diff --git a/dom/requestsync/tests/test_webidl.html b/dom/requestsync/tests/test_webidl.html deleted file mode 100644 index 46bed4590119..000000000000 --- a/dom/requestsync/tests/test_webidl.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Test for RequestSync interfaces - - - - - - - diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 2586c34a1a88..b49076fa671c 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -1209,14 +1209,6 @@ var interfaceNamesInGlobalScope = "SVGZoomAndPan", // IMPORTANT: Do not change this list without review from a DOM peer! "SVGZoomEvent", -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "RequestSyncManager", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "RequestSyncScheduler", b2g: true, pref: "dom.requestSync.enabled" }, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "RequestSyncApp", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, -// IMPORTANT: Do not change this list without review from a DOM peer! - {name: "RequestSyncTask", b2g: true, pref: "dom.requestSync.enabled", premission: "requestsync-manager" }, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "Telephony", b2g: true, pref: "dom.telephony.enabled"}, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 64b67d5c434f..653facf8237b 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -285,12 +285,6 @@ partial interface Navigator { void mozSetMessageHandler (DOMString type, systemMessageCallback? callback); [Throws, Pref="dom.sysmsg.enabled"] boolean mozHasPendingMessage (DOMString type); - - // This method can be called only from the systeMessageCallback function and - // it allows the page to set a promise to keep alive the app until the - // current operation is not fully completed. - [Throws, Pref="dom.sysmsg.enabled"] - void mozSetMessageHandlerPromise (Promise promise); }; #ifdef MOZ_B2G_RIL diff --git a/dom/webidl/RequestSyncManager.webidl b/dom/webidl/RequestSyncManager.webidl deleted file mode 100644 index 632081499580..000000000000 --- a/dom/webidl/RequestSyncManager.webidl +++ /dev/null @@ -1,54 +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/. - */ - -[AvailableIn=CertifiedApps, - Pref="dom.requestSync.enabled", - CheckPermissions="requestsync-manager", - JSImplementation="@mozilla.org/dom/request-sync-task-app;1"] -interface RequestSyncApp { - readonly attribute USVString origin; - readonly attribute USVString manifestURL; - readonly attribute boolean isInBrowserElement; -}; - -enum RequestSyncTaskPolicyState { "enabled", "disabled", "wifiOnly" }; - -// Like a normal task, but with info about the app. -[AvailableIn=CertifiedApps, - Pref="dom.requestSync.enabled", - CheckPermissions="requestsync-manager", - JSImplementation="@mozilla.org/dom/request-sync-task-manager;1"] -interface RequestSyncTask { - // This object describes the app that is owning the task. - readonly attribute RequestSyncApp app; - - // Using setPolicy it's possible to owerwrite the state and the minInterval. - readonly attribute RequestSyncTaskPolicyState state; - readonly attribute long overwrittenMinInterval; - - // These attributes are taken from the configuration of the task: - - readonly attribute USVString task; - readonly attribute DOMTimeStamp lastSync; - readonly attribute USVString wakeUpPage; - readonly attribute boolean oneShot; - readonly attribute long minInterval; - readonly attribute boolean wifiOnly; - readonly attribute any data; - - Promise setPolicy(RequestSyncTaskPolicyState aState, - optional long ovewrittenMinInterval); -}; - -[NavigatorProperty="syncManager", - AvailableIn=CertifiedApps, - Pref="dom.requestSync.enabled", - CheckPermissions="requestsync-manager", - JSImplementation="@mozilla.org/dom/request-sync-manager;1"] -// This interface will be used only by the B2G SystemApp -interface RequestSyncManager { - Promise> registrations(); -}; diff --git a/dom/webidl/RequestSyncScheduler.webidl b/dom/webidl/RequestSyncScheduler.webidl deleted file mode 100644 index 1bd33ac12fdc..000000000000 --- a/dom/webidl/RequestSyncScheduler.webidl +++ /dev/null @@ -1,38 +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/. - */ - -// This is the dictionary for the creation of a new task. -dictionary RequestTaskParams { - required USVString wakeUpPage; - boolean oneShot = true; - required long minInterval; // in seconds >= dom.requestSync.minInterval or 100 secs - boolean wifiOnly = true; - any data = null; -}; - - -// This is the dictionary you can have back from registration{s}(). -dictionary RequestTaskFull : RequestTaskParams { - USVString task = ""; - - // Last synchonization date.. maybe it's useful to know. - DOMTimeStamp lastSync; -}; - -[NavigatorProperty="sync", - AvailableIn=CertifiedApps, - Pref="dom.requestSync.enabled", - JSImplementation="@mozilla.org/dom/request-sync-scheduler;1"] -interface RequestSyncScheduler { - - Promise register(USVString task, - optional RequestTaskParams params); - Promise unregister(USVString task); - - // Useful methods to get registrations - Promise> registrations(); - Promise registration(USVString task); -}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index d62114d22454..c386d8693882 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -341,8 +341,6 @@ WEBIDL_FILES = [ 'Range.webidl', 'Rect.webidl', 'Request.webidl', - 'RequestSyncManager.webidl', - 'RequestSyncScheduler.webidl', 'ResourceStats.webidl', 'ResourceStatsManager.webidl', 'Response.webidl', diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index d56828f83fbe..67c421559d38 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -64,7 +64,6 @@ #include "ActiveLayerTracker.h" #include "CounterStyleManager.h" #include "FrameLayerBuilder.h" -#include "mozilla/dom/RequestSyncWifiService.h" #include "AudioChannelService.h" #include "mozilla/dom/DataStoreService.h" @@ -301,10 +300,6 @@ nsLayoutStatics::Initialize() IMEStateManager::Init(); -#ifdef MOZ_B2G - RequestSyncWifiService::Init(); -#endif - return NS_OK; } diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 4b557b0c3cf1..ebaa9d373d85 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -286,9 +286,6 @@ @BINPATH@/components/zipwriter.xpt ; JavaScript components -@BINPATH@/components/RequestSync.manifest -@BINPATH@/components/RequestSyncManager.js -@BINPATH@/components/RequestSyncScheduler.js @BINPATH@/components/ChromeNotifications.js @BINPATH@/components/ChromeNotifications.manifest @BINPATH@/components/ConsoleAPI.manifest