mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- caps part only: Bug 1178533 - Add nsIInstallPackagedWebapp for registering permissions when navigating to signed packages r=bholley,fabrice,valentin (8174625aab)
- Bug 1299615 - Part 2: Skip ICU source directory in Clang build plugin when searching for implicit conversion constructors. r=Waldo, r=mystor (30a51b6602)
- Bug 1264827 - Part 1: Rename the existing code to make it clear it's checking for template args; r=mystor (2710d7e318)
- Bug 1264827 - Part 2: Add a static analysis to help check Rust wrapped C++ classes for members which are unsafe to memmove; r=mystor (8f053f59cf)
- Bug 1264827 - Part 3: Add the MOZ_NEEDS_MEMMOVABLE_MEMBERS annotation to MFBT; r=froydnj (937622926a)
- Bug 1229769 - We should be able to use DOM promises in the worker debugger;r=khuey (b3b32e9b94)
- Bug 1265035 - Make ~WorkerJSRuntime() handle Initialize() failure better. r=khuey. (67e67fab06)
- Bug 1256428 P1 Add ServiceWorkerJob2 base class. r=jdm (0bedbfa070)
- Bug 1256428 P2 Add ServiceWorkerJobQueue2 class. r=jdm (806e9b242e)
- Bug 1260933 - Part 1: For invalid easing values, print the invalid value. r=birtles (482541cfcb)
- Bug 1260933 - Part 2: For invalid duration values, print the invalid value. r=birtles (5b20918f77)
- Bug 1256428 P3 Add ServiceWorkerUpdateJob2. r=jdm (135e9bf05d)
- Bug 1256428 P4 Add ServiceWorkerRegisterJob2. r=jdm (e790f95b1d)
- Bug 1256428 P5 Add ServiceWorkerUnregisterJob2 class. r=jdm (cf63826b57)
- Bug 1220757 - Add report to console when service worker register fails due to mismatching scope path.r=bkelly (dbbf1a8515)
- Bug 1255621 - Ignore service workers previously registered in non-private windows. r=bkelly (593ebfc612)
- Bug 1241531 - Part 1: Only pop jobs from the queue when the correct job completes. r=ehsan (f8a1ea2fda)
- Bug 1241531 - Part 2: Move Cancel() to ServiceWorkerJob base class. r=ehsan (33ffccb8a4)
- Bug 1241531 - Part 3: Call Cancel() on all service worker jobs. r=ehsan (4cce06ab41)
- Bug 1241531 - Part 4: Make service worker unregister job respect cancelation. r=ehsan (1877cb3919)
- Bug 1261776 Use SafeElementAt() in service worker job queue. r=ehsan (c7a883a087)
- Bug 1238990 P1 ServiceWorkerManager should trigger automatic updates in current process. r=ehsan (c65bded060)
- Bug 1238990 P2 Try to ensure service worker jobs do not run during shutdown. r=ehsan (f816a012f2)
- Bug 1256428 P6 Use ServiceWorkerJobQueue2 and new job classes in ServiceWorkerManager. r=jdm (641af03802)
- Bug 1256428 P0 Fix unified build failures in dom/workers. r=jdm (33aaafd188)
- Bug 1261814: Use the presence of the content global, and not any random global, to determine whether to run the close handler. r=bz (8f182bf345)
- Bug 1256428 P7 Fix wpt update.https.html to expect TypeError per current spec. r=jdm (4c6cad6e0f)
- Bug 1256428 P8 Fix wpt unregister-then-register-new-script.https.html to new spec expectations matching blink's tests. r=jdm (d9191f7002)
- Bug 1226443 P5 Always use first scheduled update timer instead of rescheduling on new events. r=ehsan (7b1b31dcc0)
- Bug 1230341 Hold a strong ref in service worker NS_NewRunnableMethodWithArg uses. r=ehsan a=abillings (2b1d942ae4)
- Bug 1256428 P9 Remove now unused code from ServiceWorkerManager.cpp. r=jdm (7f97035007)
- Bug 1256428 P10 Remove ServiceWorkerRegistrationInfo::mUpdating flag. r=jdm (31fc686d5d)
- Bug 1256428 P11 Don't coalesce SW jobs after the existing job has already resolved its promise. r=jdm (1ce373f98b)
- Bug 1256428 P12 ServiceWorkerUnregisterJob2 should not use ServiceWorkerManager internals. r=jdm (1abe304c3c)
- Bug 1256428 P13 Remove unnecessary ServiceWorkerUnregsterJob2 stop. r=jdm (05d0717b7c)
- Bug 1256428 P14 Remove dead code in SeviceWorkerUpdateJob.cpp. r=jdm (7d1ac1112a)
- Bug 1256428 P15 Perform byte-for-byte comparison check after validating script URL. r=jdm (dc30ec75a9)
- Bug 1256428 P16 Fix some issues calling purgeCache() in ServiceWorkerUpdateJob.cpp. r=jdm (cffe93613a)
- Bug 1256428 P17 Rename service worker job classes to remove "2" suffix. r=jdm (f1d7a6aadf)
- Bug 1256428 P18 Add spec annotations and tweak asserts in ServiceWorkerUpdateJob. r=jdm (1a9c95a5bb)
- Bug 1256428 P19 Address ServiceWorkerUnregisterJob review feedback. r=jdm (2b8775a9ad)
- Bug 1260591 Move ServiceWorkerInfo and ServiceWorkerRegistrationInfo into separate files. r=jdm (2e31a3c002)
- Bug 1266857 Make Clients.claim() use observer document list instead of secondary hashtable. r=bz (2b318072f5)
- Bug 1261428: Migrate the useless setTimeout error message to the bindings infrastructure. r=bz (80d2503978)
- Bug 1263307 P1 Make ServiceWorkerRegistrationInfo::mScope const. r=jdm (b8b03bc594)
- Bug 1263307 P2 Make ServiceWorkerRegistrationInfo worker members private. r=jdm (16773a9134)
- Bug 1263307 P3 Move ServiceWorker update logic into central place in ServiceWorkerRegistrationInfo methods. r=jdm (68b288cbfb)
- Bug 1265761 Clients.matchAll() should treat http windows as secure if devtools are open and http testing is enabled. r=ehsan (100e16ca08)
- Bug 1265795 P1 Uncontrolled service workers when global is removed from document. r=bz (db069b0756)
- Bug 1265795 P2 Add a web-platform-test for the window navigation case. r=bz (6571257e5b)
- Bug 1265795 P3 Assert that controlled documents have an outer window. r=bz (fdc14dbf66)
- Bug 1265795 P4 Always call nsDocument::SetScriptGlobalObject(nullptr) from nsDocument::Destroy(). r=bz (8825c3dbd5)
- Bug 1254194: Add a validator for custom add-on content security policies. r=billm f=aswan (c557dd47ef)
- Bug 1254194: Allow iterating over and inspecting sources of parsed CSP directives. r=ckerschb (2d93cdda56)
- Bug 1142332 - Prevent calling CSP_EnumToKeyword with CSP_HASH. r=ckerschb (15a80ed62f)
- Bug 1236416 - Remove some misc toolkit content UI from Fennec r=margaret (01f7f81c93)
- Bug 1234403 - Part 1: Implement CSSPseudoElement.getAnimations. r=birtles (91ce2e1cae)
- Bug 1234403 - Part 2: Implement document.getAnimations. r=birtles (49afbacadb)
- Bug 1234403 - Part 3: Test for the CSSPseudoElement objects returned by effect.target. r=birtles (bf34dda38f)
- Bug 1234403 - Part 4: Test for the animation order returned by document.getAnimations(). r=birtles (fa8ec8e01f)
- Bug 1234403 - Part 5: Test for CSSPseudoElement.getAnimations. r=birtles (3ef598f2ba)
- Bug 1254418 - Part 1: Support generated-content element for Element.getAnimations. r=birtles (7ae806859a)
- Bug 1254418 - Part 2: Test getAnimations for generated-content elements. r=birtles (b562ec7478)
- Bug 1254761 - Part 1: Implement getAnimations({ subtree: true }). r=smaug (c5419ffec0)
- Bug 1254761 - Part 2: Removes extra whitespaces. r=birtles (2a98381928)
- Bug 1254761 - Part 3: Add tests for AnimationFilter. r=birtles (bdd9b39849)
- Bug 1254194: Apply a content security policy to all WebExtension documents. r=gabor (c3a9f32be8)
- Bug 1257246: Update the version of eslint that mach installs. r=gps (da0481d7e4)
- Bug 1229588: Add a taskcluster test for eslint. r=dustin (e6eff5caf2)
- Bug 1257246: Update lint test image to newer packages of eslint. r=ahal (bcfaf3b5d8)
- Bug 1263637 - Fix eslint 2 warnings for WebExtensions code. r=kmag (16537b22dc)
- Bug 1238177 - fix extension content needs to use the correct user context id origin attribute. r=sicking (834faa0f62)
This commit is contained in:
@@ -91,7 +91,12 @@ private:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
class NonMemMovableChecker : public MatchFinder::MatchCallback {
|
||||
class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
class NonMemMovableMemberChecker : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
@@ -125,7 +130,8 @@ private:
|
||||
ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
|
||||
NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
|
||||
NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
|
||||
NonMemMovableChecker nonMemMovableChecker;
|
||||
NonMemMovableTemplateArgChecker nonMemMovableTemplateArgChecker;
|
||||
NonMemMovableMemberChecker nonMemMovableMemberChecker;
|
||||
ExplicitImplicitChecker explicitImplicitChecker;
|
||||
NoAutoTypeChecker noAutoTypeChecker;
|
||||
NoExplicitMoveConstructorChecker noExplicitMoveConstructorChecker;
|
||||
@@ -201,7 +207,8 @@ bool isIgnoredPathForImplicitCtor(const Decl *decl) {
|
||||
begin->compare_lower(StringRef("harfbuzz")) == 0 ||
|
||||
begin->compare_lower(StringRef("hunspell")) == 0 ||
|
||||
begin->compare_lower(StringRef("scoped_ptr.h")) == 0 ||
|
||||
begin->compare_lower(StringRef("graphite2")) == 0) {
|
||||
begin->compare_lower(StringRef("graphite2")) == 0 ||
|
||||
begin->compare_lower(StringRef("icu")) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (begin->compare_lower(StringRef("chromium")) == 0) {
|
||||
@@ -710,10 +717,15 @@ AST_MATCHER(QualType, isNonMemMovable) {
|
||||
}
|
||||
|
||||
/// This matcher will select classes which require a memmovable template arg
|
||||
AST_MATCHER(CXXRecordDecl, needsMemMovable) {
|
||||
AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
|
||||
return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
|
||||
}
|
||||
|
||||
/// This matcher will select classes which require all members to be memmovable
|
||||
AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
|
||||
return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
|
||||
}
|
||||
|
||||
AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
|
||||
const CXXConstructorDecl *decl = Node.getCanonicalDecl();
|
||||
return
|
||||
@@ -1015,10 +1027,16 @@ DiagnosticsMatcher::DiagnosticsMatcher() {
|
||||
// Handle non-mem-movable template specializations
|
||||
astMatcher.addMatcher(
|
||||
classTemplateSpecializationDecl(
|
||||
allOf(needsMemMovable(),
|
||||
allOf(needsMemMovableTemplateArg(),
|
||||
hasAnyTemplateArgument(refersToType(isNonMemMovable()))))
|
||||
.bind("specialization"),
|
||||
&nonMemMovableChecker);
|
||||
&nonMemMovableTemplateArgChecker);
|
||||
|
||||
// Handle non-mem-movable members
|
||||
astMatcher.addMatcher(
|
||||
cxxRecordDecl(needsMemMovableMembers())
|
||||
.bind("decl"),
|
||||
&nonMemMovableMemberChecker);
|
||||
|
||||
astMatcher.addMatcher(constructorDecl(isInterestingImplicitCtor(),
|
||||
ofClass(allOf(isConcreteClass(),
|
||||
@@ -1385,7 +1403,7 @@ void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
|
||||
<< specialization;
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::NonMemMovableChecker::run(
|
||||
void DiagnosticsMatcher::NonMemMovableTemplateArgChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
@@ -1406,7 +1424,7 @@ void DiagnosticsMatcher::NonMemMovableChecker::run(
|
||||
specialization->getTemplateInstantiationArgs();
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
QualType argType = args[i].getAsType();
|
||||
if (NonMemMovable.hasEffectiveAnnotation(args[i].getAsType())) {
|
||||
if (NonMemMovable.hasEffectiveAnnotation(argType)) {
|
||||
Diag.Report(specialization->getLocation(), errorID) << specialization
|
||||
<< argType;
|
||||
// XXX It would be really nice if we could get the instantiation stack
|
||||
@@ -1424,6 +1442,29 @@ void DiagnosticsMatcher::NonMemMovableChecker::run(
|
||||
}
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::NonMemMovableMemberChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Error,
|
||||
"class %0 cannot have non-memmovable member %1 of type %2");
|
||||
|
||||
// Get the specialization
|
||||
const CXXRecordDecl* Decl =
|
||||
Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
|
||||
|
||||
// Report an error for every member which is non-memmovable
|
||||
for (const FieldDecl *Field : Decl->fields()) {
|
||||
QualType Type = Field->getType();
|
||||
if (NonMemMovable.hasEffectiveAnnotation(Type)) {
|
||||
Diag.Report(Field->getLocation(), errorID) << Decl
|
||||
<< Field
|
||||
<< Type;
|
||||
NonMemMovable.dumpAnnotationReason(Diag, Type, Decl->getLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::ExplicitImplicitChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
|
||||
#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
|
||||
#define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members")))
|
||||
|
||||
/*
|
||||
These are a bunch of structs with variable levels of memmovability.
|
||||
@@ -9,12 +10,12 @@ struct MOZ_NON_MEMMOVABLE NonMovable {};
|
||||
struct Movable {};
|
||||
|
||||
// Subclasses
|
||||
struct S_NonMovable : NonMovable {}; // expected-note 48 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}}
|
||||
struct S_NonMovable : NonMovable {}; // expected-note 51 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}}
|
||||
struct S_Movable : Movable {};
|
||||
|
||||
// Members
|
||||
struct W_NonMovable {
|
||||
NonMovable m; // expected-note 32 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}}
|
||||
NonMovable m; // expected-note 34 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}}
|
||||
};
|
||||
struct W_Movable {
|
||||
Movable m;
|
||||
@@ -22,17 +23,17 @@ struct W_Movable {
|
||||
|
||||
// Wrapped Subclasses
|
||||
struct WS_NonMovable {
|
||||
S_NonMovable m; // expected-note 32 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}}
|
||||
S_NonMovable m; // expected-note 34 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}}
|
||||
};
|
||||
struct WS_Movable {
|
||||
S_Movable m;
|
||||
};
|
||||
|
||||
// Combinations of the above
|
||||
struct SW_NonMovable : W_NonMovable {}; // expected-note 16 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}}
|
||||
struct SW_NonMovable : W_NonMovable {}; // expected-note 17 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}}
|
||||
struct SW_Movable : W_Movable {};
|
||||
|
||||
struct SWS_NonMovable : WS_NonMovable {}; // expected-note 16 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}}
|
||||
struct SWS_NonMovable : WS_NonMovable {}; // expected-note 17 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}}
|
||||
struct SWS_Movable : WS_Movable {};
|
||||
|
||||
// Basic templated wrapper
|
||||
@@ -810,3 +811,20 @@ void specialization() {
|
||||
Defaulted_Templated_NeedyTemplate7<S_SpecializedNonMovable> c7;
|
||||
W_Defaulted_Templated_NeedyTemplate8<S_SpecializedNonMovable> c8;
|
||||
}
|
||||
|
||||
class MOZ_NEEDS_MEMMOVABLE_MEMBERS NeedsMemMovableMembers {
|
||||
Movable m1;
|
||||
NonMovable m2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'm2' of type 'NonMovable'}}
|
||||
S_Movable sm1;
|
||||
S_NonMovable sm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'sm2' of type 'S_NonMovable'}}
|
||||
W_Movable wm1;
|
||||
W_NonMovable wm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wm2' of type 'W_NonMovable'}}
|
||||
SW_Movable swm1;
|
||||
SW_NonMovable swm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swm2' of type 'SW_NonMovable'}}
|
||||
WS_Movable wsm1;
|
||||
WS_NonMovable wsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wsm2' of type 'WS_NonMovable'}}
|
||||
SWS_Movable swsm1;
|
||||
SWS_NonMovable swsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swsm2' of type 'SWS_NonMovable'}}
|
||||
};
|
||||
|
||||
class NeedsMemMovableMembersDerived : public NeedsMemMovableMembers {};
|
||||
|
||||
@@ -454,6 +454,13 @@ BasePrincipal::GetAppId(uint32_t* aAppId)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::GetAddonId(nsAString& aAddonId)
|
||||
{
|
||||
aAddonId.Assign(mOriginAttributes.mAddonId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BasePrincipal::GetUserContextId(uint32_t* aUserContextId)
|
||||
{
|
||||
@@ -507,6 +514,28 @@ BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAt
|
||||
return codebase.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<BasePrincipal>
|
||||
BasePrincipal::CreateCodebasePrincipal(const nsACString& aOrigin)
|
||||
{
|
||||
MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("[")),
|
||||
"CreateCodebasePrincipal does not support System and Expanded principals");
|
||||
|
||||
MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":")),
|
||||
"CreateCodebasePrincipal does not support nsNullPrincipal");
|
||||
|
||||
nsAutoCString originNoSuffix;
|
||||
mozilla::OriginAttributes attrs;
|
||||
if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
}
|
||||
|
||||
bool
|
||||
BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
|
||||
{
|
||||
|
||||
@@ -157,6 +157,7 @@ public:
|
||||
NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
|
||||
NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
|
||||
NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
|
||||
NS_IMETHOD GetAddonId(nsAString& aAddonId) final;
|
||||
NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) final;
|
||||
NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
|
||||
NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
|
||||
@@ -168,6 +169,7 @@ public:
|
||||
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
|
||||
static already_AddRefed<BasePrincipal>
|
||||
CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs);
|
||||
static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(const nsACString& aOrigin);
|
||||
|
||||
const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
|
||||
uint32_t AppId() const { return mOriginAttributes.mAppId; }
|
||||
|
||||
@@ -14,6 +14,25 @@
|
||||
[scriptable,uuid(8a034ef9-9d14-4c5d-8319-06c1ab574baa)]
|
||||
interface nsIAddonPolicyService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns the base content security policy, which is applied to all
|
||||
* extension documents, in addition to any custom policies.
|
||||
*/
|
||||
readonly attribute AString baseCSP;
|
||||
|
||||
/**
|
||||
* Returns the default content security policy which applies to extension
|
||||
* documents which do not specify any custom policies.
|
||||
*/
|
||||
readonly attribute AString defaultCSP;
|
||||
|
||||
/**
|
||||
* Returns the content security policy which applies to documents belonging
|
||||
* to the extension with the given ID. This may be either a custom policy,
|
||||
* if one was supplied, or the default policy if one was not.
|
||||
*/
|
||||
AString getAddonCSP(in AString aAddonId);
|
||||
|
||||
/**
|
||||
* Returns true if unprivileged code associated with the given addon may load
|
||||
* data from |aURI|.
|
||||
@@ -30,3 +49,18 @@ interface nsIAddonPolicyService : nsISupports
|
||||
*/
|
||||
AString extensionURIToAddonId(in nsIURI aURI);
|
||||
};
|
||||
|
||||
/**
|
||||
* This interface exposes functionality related to add-on content policy
|
||||
* enforcement.
|
||||
*/
|
||||
[scriptable,uuid(7a4fe60b-9131-45f5-83f3-dc63b5d71a5d)]
|
||||
interface nsIAddonContentPolicy : nsISupports
|
||||
{
|
||||
/**
|
||||
* Checks a custom content security policy string, to ensure that it meets
|
||||
* minimum security requirements. Returns null for valid policies, or a
|
||||
* string describing the error for invalid policies.
|
||||
*/
|
||||
AString validateAddonCSP(in AString aPolicyString);
|
||||
};
|
||||
|
||||
@@ -286,6 +286,11 @@ interface nsIPrincipal : nsISerializable
|
||||
*/
|
||||
[infallible] readonly attribute unsigned long appId;
|
||||
|
||||
/**
|
||||
* Gets the ID of the add-on this principal belongs to.
|
||||
*/
|
||||
readonly attribute AString addonId;
|
||||
|
||||
/**
|
||||
* Gets the id of the user context this principal is inside. If this
|
||||
* principal is inside the default userContext, this returns
|
||||
|
||||
@@ -26,7 +26,7 @@ class DomainPolicyClone;
|
||||
[ptr] native JSObjectPtr(JSObject);
|
||||
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
|
||||
|
||||
[scriptable, uuid(73f92674-f59d-4c9b-a9b5-f7a3ae8ffa98)]
|
||||
[scriptable, uuid(b7ae2310-576e-11e5-a837-0800200c9a66)]
|
||||
interface nsIScriptSecurityManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -195,6 +195,13 @@ interface nsIScriptSecurityManager : nsISupports
|
||||
[implicit_jscontext]
|
||||
nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes);
|
||||
|
||||
/**
|
||||
* Returns a principal whose origin is the one we pass in.
|
||||
* See nsIPrincipal.idl for a description of origin attributes, and
|
||||
* ChromeUtils.webidl for a list of origin attributes and their defaults.
|
||||
*/
|
||||
nsIPrincipal createCodebasePrincipalFromOrigin(in ACString origin);
|
||||
|
||||
/**
|
||||
* Returns a unique nonce principal with |originAttributes|.
|
||||
* See nsIPrincipal.h for a description of origin attributes, and
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsXPCOMStrings.h"
|
||||
|
||||
// This should be probably defined on some other place... but I couldn't find it
|
||||
#define WEBAPPS_PERM_NAME "webapps-manage"
|
||||
@@ -1096,6 +1097,23 @@ nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, JS::Handle<JS::Va
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CreateCodebasePrincipalFromOrigin(const nsACString& aOrigin,
|
||||
nsIPrincipal** aPrincipal)
|
||||
{
|
||||
if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("["))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":"))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aOrigin);
|
||||
prin.forget(aPrincipal);
|
||||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CreateNullPrincipal(JS::Handle<JS::Value> aOriginAttributes,
|
||||
JSContext* aCx, nsIPrincipal** aPrincipal)
|
||||
|
||||
@@ -3,7 +3,6 @@ const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/BrowserUtils.jsm");
|
||||
var ssm = Services.scriptSecurityManager;
|
||||
function makeURI(uri) { return Services.io.newURI(uri, null, null); }
|
||||
|
||||
@@ -28,9 +27,9 @@ function checkOriginAttributes(prin, attrs, suffix) {
|
||||
do_check_eq(prin.originAttributes.inBrowser, attrs.inBrowser || false);
|
||||
do_check_eq(prin.originSuffix, suffix || '');
|
||||
if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
|
||||
do_check_true(BrowserUtils.principalFromOrigin(prin.origin).equals(prin));
|
||||
do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin));
|
||||
} else {
|
||||
checkThrows(() => BrowserUtils.principalFromOrigin(prin.origin));
|
||||
checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1230,12 +1230,14 @@ Animation::GetPresContext() const
|
||||
void
|
||||
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
||||
{
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
||||
DoFinishNotificationImmediately();
|
||||
} else if (!mFinishNotificationTask.IsPending()) {
|
||||
RefPtr<nsRunnableMethod<Animation>> runnable =
|
||||
NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
|
||||
Promise::DispatchToMicroTask(runnable);
|
||||
runtime->DispatchToMicroTask(runnable);
|
||||
mFinishNotificationTask = runnable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
#include "mozilla/dom/CSSPseudoElementBinding.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/AnimationComparator.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -49,10 +50,16 @@ CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
void
|
||||
CSSPseudoElement::GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal)
|
||||
CSSPseudoElement::GetAnimations(const AnimationFilter& filter,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal)
|
||||
{
|
||||
// Bug 1234403: Implement this API.
|
||||
NS_NOTREACHED("CSSPseudoElement::GetAnimations() is not implemented yet.");
|
||||
nsIDocument* doc = mParentElement->GetComposedDoc();
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Style);
|
||||
}
|
||||
|
||||
Element::GetAnimationsUnsorted(mParentElement, mPseudoType, aRetVal);
|
||||
aRetVal.Sort(AnimationPtrComparator<RefPtr<Animation>>());
|
||||
}
|
||||
|
||||
already_AddRefed<Animation>
|
||||
|
||||
@@ -57,7 +57,8 @@ public:
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
void GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal);
|
||||
void GetAnimations(const AnimationFilter& filter,
|
||||
nsTArray<RefPtr<Animation>>& aRetVal);
|
||||
already_AddRefed<Animation>
|
||||
Animate(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
|
||||
@@ -161,7 +161,7 @@ TimingParams::ParseEasing(const nsAString& aEasing,
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
|
||||
break;
|
||||
}
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>();
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_EASING_ERROR>(aEasing);
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ struct TimingParams
|
||||
NS_LITERAL_STRING("duration"));
|
||||
}
|
||||
} else if (!aDuration.GetAsString().EqualsLiteral("auto")) {
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>();
|
||||
aRv.ThrowTypeError<dom::MSG_INVALID_DURATION_ERROR>(
|
||||
aDuration.GetAsString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ support-files =
|
||||
# file_animate_xrays.html needs to go in mochitest.ini since it is served
|
||||
# over HTTP
|
||||
[chrome/test_animation_observers.html]
|
||||
[chrome/test_animation_properties.html]
|
||||
[chrome/test_animation_property_state.html]
|
||||
[chrome/test_animation_properties.html]
|
||||
[chrome/test_generated_content_getAnimations.html]
|
||||
[chrome/test_restyles.html]
|
||||
[chrome/test_running_on_compositor.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Test getAnimations() for generated-content elements</title>
|
||||
<script type="application/javascript" src="../testharness.js"></script>
|
||||
<script type="application/javascript" src="../testharnessreport.js"></script>
|
||||
<script type="application/javascript" src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
@keyframes anim2 { }
|
||||
.before::before {
|
||||
content: '';
|
||||
animation: anim 100s;
|
||||
}
|
||||
.after::after {
|
||||
content: '';
|
||||
animation: anim 100s, anim2 100s;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='root' class='before after'>
|
||||
<div class='before'></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
const {Cc, Ci, Cu} = SpecialPowers;
|
||||
|
||||
function getWalker(node) {
|
||||
var walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"].
|
||||
createInstance(Ci.inIDeepTreeWalker);
|
||||
walker.showAnonymousContent = true;
|
||||
walker.init(node.ownerDocument, Ci.nsIDOMNodeFilter.SHOW_ALL);
|
||||
walker.currentNode = node;
|
||||
return walker;
|
||||
}
|
||||
|
||||
test(function(t) {
|
||||
var root = document.getElementById('root');
|
||||
// Flush first to make sure the generated-content elements are ready
|
||||
// in the tree.
|
||||
flushComputedStyle(root);
|
||||
var before = getWalker(root).firstChild();
|
||||
var after = getWalker(root).lastChild();
|
||||
|
||||
// Sanity Checks
|
||||
assert_equals(document.getAnimations().length, 4,
|
||||
'All animations in this document');
|
||||
assert_equals(before.tagName, '_moz_generated_content_before',
|
||||
'First child is ::before element');
|
||||
assert_equals(after.tagName, '_moz_generated_content_after',
|
||||
'Last child is ::after element');
|
||||
|
||||
// Test Element.getAnimations() for generated-content elements
|
||||
assert_equals(before.getAnimations().length, 1,
|
||||
'Animations of ::before generated-content element');
|
||||
assert_equals(after.getAnimations().length, 2,
|
||||
'Animations of ::after generated-content element');
|
||||
}, 'Element.getAnimations() used on generated-content elements');
|
||||
|
||||
test(function(t) {
|
||||
var root = document.getElementById('root');
|
||||
flushComputedStyle(root);
|
||||
var walker = getWalker(root);
|
||||
|
||||
var animations = [];
|
||||
var element = walker.currentNode;
|
||||
while (element) {
|
||||
if (element.getAnimations) {
|
||||
animations = [...animations, ...element.getAnimations()];
|
||||
}
|
||||
element = walker.nextNode();
|
||||
}
|
||||
|
||||
assert_equals(animations.length, document.getAnimations().length,
|
||||
'The number of animations got by DeepTreeWalker and ' +
|
||||
'document.getAnimations() should be the same');
|
||||
}, 'Element.getAnimations() used by traversing DeepTreeWalker');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -14,10 +14,6 @@
|
||||
@keyframes animRight {
|
||||
to { right: 100px }
|
||||
}
|
||||
.with-before-animation::before {
|
||||
content: " ";
|
||||
animation: animLeft 100s;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
@@ -245,14 +241,35 @@ test(function(t) {
|
||||
}, 'CSS Animations cancelled and restarted via the API are returned');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { class: 'with-before-animation' });
|
||||
// FIXME: This should actually return the animation on the pseudo element
|
||||
// but we haven't implemented a PseudoElement interface to use as
|
||||
// animation's target (bug 1174575) so we simply don't return these animations
|
||||
// until we can support them properly.
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'CSS animations on pseudo elements are not returned');
|
||||
}, 'CSS Animations targetting pseudo-elements are not returned');
|
||||
addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
|
||||
'#parent::before': 'animation: animRight 10s;' });
|
||||
// create two divs with these arrangement:
|
||||
// parent
|
||||
// ::before,
|
||||
// ::after
|
||||
// |
|
||||
// child
|
||||
var parent = addDiv(t, { 'id': 'parent' });
|
||||
var child = addDiv(t);
|
||||
parent.appendChild(child);
|
||||
[parent, child].forEach((div) => {
|
||||
div.setAttribute('style', 'animation: animBottom 10s');
|
||||
});
|
||||
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 4,
|
||||
'CSS animations on both pseudo-elements and elements ' +
|
||||
'are returned');
|
||||
assert_equals(anims[0].effect.target, parent,
|
||||
'The animation targeting the parent element comes first');
|
||||
assert_equals(anims[1].effect.target.type, '::before',
|
||||
'The animation targeting the ::before element comes second');
|
||||
assert_equals(anims[2].effect.target.type, '::after',
|
||||
'The animation targeting the ::after element comes third');
|
||||
assert_equals(anims[3].effect.target, child,
|
||||
'The animation targeting the child element comes last');
|
||||
}, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
|
||||
'after sorting');
|
||||
|
||||
done();
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,40 @@ test(function(t) {
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.effect.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS animations have the correct Animation.target');
|
||||
}, 'Returned CSS animations have the correct effect target');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
|
||||
var div = addDiv(t, { class: 'after' });
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 2,
|
||||
'Got animations running on ::after pseudo element');
|
||||
assert_equals(anims[0].effect.target, anims[1].effect.target,
|
||||
'Both animations return the same target object');
|
||||
}, 'effect.target should return the same CSSPseudoElement object each time');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '.after::after': 'animation: anim 10s;' });
|
||||
var div = addDiv(t, { class: 'after' });
|
||||
var pseudoTarget = document.getAnimations()[0].effect.target;
|
||||
var effect = new KeyframeEffectReadOnly(pseudoTarget,
|
||||
{ background: ["blue", "red"] },
|
||||
3000);
|
||||
var newAnim = new Animation(effect, document.timeline);
|
||||
newAnim.play();
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 2,
|
||||
'Got animations running on ::after pseudo element');
|
||||
assert_not_equals(anims[0], newAnim,
|
||||
'The scriped-generated animation appears last');
|
||||
assert_equals(newAnim.effect.target, pseudoTarget,
|
||||
'The effect.target of the scripted-generated animation is ' +
|
||||
'the same as the one from the argument of ' +
|
||||
'KeyframeEffectReadOnly constructor');
|
||||
assert_equals(anims[0].effect.target, newAnim.effect.target,
|
||||
'Both animations return the same target object');
|
||||
}, 'effect.target from the script-generated animation should return the same ' +
|
||||
'CSSPseudoElement object as that from the CSS generated animation');
|
||||
|
||||
done();
|
||||
</script>
|
||||
|
||||
@@ -304,6 +304,147 @@ async_test(function(t) {
|
||||
}));
|
||||
}, 'getAnimations for CSS Animations follows animation-name order');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '#target::after': 'animation: anim1 10s;',
|
||||
'#target::before': 'animation: anim1 10s;' });
|
||||
var target = addDiv(t, { 'id': 'target' });
|
||||
target.style.animation = 'anim1 100s';
|
||||
|
||||
var animations = target.getAnimations({ subtree: false });
|
||||
assert_equals(animations.length, 1,
|
||||
'Should find only the element');
|
||||
assert_equals(animations[0].effect.target, target,
|
||||
'Effect target should be the element');
|
||||
}, 'Test AnimationFilter{ subtree: false } with single element');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '#target::after': 'animation: anim1 10s;',
|
||||
'#target::before': 'animation: anim1 10s;' });
|
||||
var target = addDiv(t, { 'id': 'target' });
|
||||
target.style.animation = 'anim1 100s';
|
||||
|
||||
var animations = target.getAnimations({ subtree: true });
|
||||
assert_equals(animations.length, 3,
|
||||
'getAnimations({ subtree: true }) ' +
|
||||
'should return animations on pseudo-elements');
|
||||
assert_equals(animations[0].effect.target, target,
|
||||
'The animation targeting the parent element ' +
|
||||
'should be returned first');
|
||||
assert_equals(animations[1].effect.target.type, '::before',
|
||||
'The animation targeting the ::before pseudo-element ' +
|
||||
'should be returned second');
|
||||
assert_equals(animations[2].effect.target.type, '::after',
|
||||
'The animation targeting the ::after pesudo-element ' +
|
||||
'should be returned last');
|
||||
}, 'Test AnimationFilter{ subtree: true } with single element');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '#parent::after': 'animation: anim1 10s;',
|
||||
'#parent::before': 'animation: anim1 10s;',
|
||||
'#child::after': 'animation: anim1 10s;',
|
||||
'#child::before': 'animation: anim1 10s;' });
|
||||
var parent = addDiv(t, { 'id': 'parent' });
|
||||
parent.style.animation = 'anim1 100s';
|
||||
var child = addDiv(t, { 'id': 'child' });
|
||||
child.style.animation = 'anim1 100s';
|
||||
parent.appendChild(child);
|
||||
|
||||
var animations = parent.getAnimations({ subtree: false });
|
||||
assert_equals(animations.length, 1,
|
||||
'Should find only the element even if it has a child');
|
||||
assert_equals(animations[0].effect.target, parent,
|
||||
'Effect target shuld be the element');
|
||||
}, 'Test AnimationFilter{ subtree: false } with element that has a child');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '#parent::after': 'animation: anim1 10s;',
|
||||
'#parent::before': 'animation: anim1 10s;',
|
||||
'#child::after': 'animation: anim1 10s;',
|
||||
'#child::before': 'animation: anim1 10s;' });
|
||||
var parent = addDiv(t, { 'id': 'parent' });
|
||||
var child = addDiv(t, { 'id': 'child' });
|
||||
parent.style.animation = 'anim1 100s';
|
||||
child.style.animation = 'anim1 100s';
|
||||
parent.appendChild(child);
|
||||
|
||||
var animations = parent.getAnimations({ subtree: true });
|
||||
assert_equals(animations.length, 6,
|
||||
'Should find all elements, pesudo-elements that parent has');
|
||||
|
||||
assert_equals(animations[0].effect.target, parent,
|
||||
'The animation targeting the parent element ' +
|
||||
'should be returned first');
|
||||
assert_equals(animations[1].effect.target.type, '::before',
|
||||
'The animation targeting the ::before pseudo-element ' +
|
||||
'should be returned second');
|
||||
assert_equals(animations[1].effect.target.parentElement, parent,
|
||||
'This ::before element should be child of parent element');
|
||||
assert_equals(animations[2].effect.target.type, '::after',
|
||||
'The animation targeting the ::after pesudo-element ' +
|
||||
'should be returned third');
|
||||
assert_equals(animations[2].effect.target.parentElement, parent,
|
||||
'This ::after element should be child of parent element');
|
||||
|
||||
assert_equals(animations[3].effect.target, child,
|
||||
'The animation targeting the child element ' +
|
||||
'should be returned fourth');
|
||||
assert_equals(animations[4].effect.target.type, '::before',
|
||||
'The animation targeting the ::before pseudo-element ' +
|
||||
'should be returned fifth');
|
||||
assert_equals(animations[4].effect.target.parentElement, child,
|
||||
'This ::before element should be child of child element');
|
||||
assert_equals(animations[5].effect.target.type, '::after',
|
||||
'The animation targeting the ::after pesudo-element ' +
|
||||
'should be returned last');
|
||||
assert_equals(animations[5].effect.target.parentElement, child,
|
||||
'This ::after element should be child of child element');
|
||||
}, 'Test AnimationFilter{ subtree: true } with element that has a child');
|
||||
|
||||
test(function(t) {
|
||||
var parent = addDiv(t, { 'id': 'parent' });
|
||||
var child1 = addDiv(t, { 'id': 'child1' });
|
||||
var grandchild1 = addDiv(t, { 'id': 'grandchild1' });
|
||||
var grandchild2 = addDiv(t, { 'id': 'grandchild2' });
|
||||
var child2 = addDiv(t, { 'id': 'child2' });
|
||||
|
||||
parent.style.animation = 'anim1 100s';
|
||||
child1.style.animation = 'anim1 100s';
|
||||
grandchild1.style.animation = 'anim1 100s';
|
||||
grandchild2.style.animation = 'anim1 100s';
|
||||
child2.style.animation = 'anim1 100s';
|
||||
|
||||
parent.appendChild(child1);
|
||||
child1.appendChild(grandchild1);
|
||||
child1.appendChild(grandchild2);
|
||||
parent.appendChild(child2);
|
||||
|
||||
var animations = parent.getAnimations({ subtree: true });
|
||||
assert_equals(
|
||||
parent.getAnimations({ subtree: true }).length, 5,
|
||||
'Should find all descendants of the element');
|
||||
|
||||
assert_equals(animations[0].effect.target, parent,
|
||||
'The animation targeting the parent element ' +
|
||||
'should be returned first');
|
||||
|
||||
assert_equals(animations[1].effect.target, child1,
|
||||
'The animation targeting the child1 element ' +
|
||||
'should be returned second');
|
||||
|
||||
assert_equals(animations[2].effect.target, grandchild1,
|
||||
'The animation targeting the grandchild1 element ' +
|
||||
'should be returned third');
|
||||
|
||||
assert_equals(animations[3].effect.target, grandchild2,
|
||||
'The animation targeting the grandchild2 element ' +
|
||||
'should be returned fourth');
|
||||
|
||||
assert_equals(animations[4].effect.target, child2,
|
||||
'The animation targeting the child2 element ' +
|
||||
'should be returned last');
|
||||
|
||||
}, 'Test AnimationFilter{ subtree: true } with element that has many descendant');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim1 { }
|
||||
@keyframes anim2 { }
|
||||
.before::before {
|
||||
animation: anim1 10s;
|
||||
}
|
||||
.after-with-mix-anims-trans::after {
|
||||
content: '';
|
||||
animation: anim1 10s, anim2 10s;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
transition: all 100s;
|
||||
}
|
||||
.after-change::after {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { class: 'before' });
|
||||
var pseudoTarget = document.getAnimations()[0].effect.target;
|
||||
assert_equals(pseudoTarget.getAnimations().length, 1,
|
||||
'Expected number of animations are returned');
|
||||
assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
|
||||
'CSS animation name matches');
|
||||
}, 'getAnimations returns CSSAnimation objects');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { class: 'after-with-mix-anims-trans' });
|
||||
// Trigger transitions
|
||||
flushComputedStyle(div);
|
||||
div.classList.add('after-change');
|
||||
|
||||
// Create additional animation on the pseudo-element from script
|
||||
var pseudoTarget = document.getAnimations()[0].effect.target;
|
||||
var effect = new KeyframeEffectReadOnly(pseudoTarget,
|
||||
{ background: ["blue", "red"] },
|
||||
3000);
|
||||
var newAnimation = new Animation(effect, document.timeline);
|
||||
newAnimation.id = 'scripted-anim';
|
||||
newAnimation.play();
|
||||
|
||||
// Check order - the script-generated animation should appear later
|
||||
var anims = pseudoTarget.getAnimations();
|
||||
assert_equals(anims.length, 5,
|
||||
'Got expected number of animations/trnasitions running on ' +
|
||||
'::after pseudo element');
|
||||
assert_equals(anims[0].transitionProperty, 'height',
|
||||
'1st animation is the 1st transition sorted by name');
|
||||
assert_equals(anims[1].transitionProperty, 'width',
|
||||
'2nd animation is the 2nd transition sorted by name ');
|
||||
assert_equals(anims[2].animationName, 'anim1',
|
||||
'3rd animation is the 1st animation in animation-name list');
|
||||
assert_equals(anims[3].animationName, 'anim2',
|
||||
'4rd animation is the 2nd animation in animation-name list');
|
||||
assert_equals(anims[4].id, 'scripted-anim',
|
||||
'Animation added by script appears last');
|
||||
}, 'getAnimations returns css transitions/animations, and script-generated ' +
|
||||
'animations in the expected order');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_pseudoElement-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
@@ -31,6 +31,49 @@ test(function(t) {
|
||||
'getAnimations returns no running CSS Transitions');
|
||||
}, 'getAnimations for CSS Transitions');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
|
||||
'transition: all 100s;',
|
||||
'.init::before': 'content: ""; width: 0px; ' +
|
||||
'transition: all 10s;',
|
||||
'.change::after': 'width: 100px;',
|
||||
'.change::before': 'width: 100px;' });
|
||||
// create two divs with these arrangement:
|
||||
// parent
|
||||
// ::before,
|
||||
// ::after
|
||||
// |
|
||||
// child
|
||||
var parent = addDiv(t);
|
||||
var child = addDiv(t);
|
||||
parent.appendChild(child);
|
||||
|
||||
parent.style.left = '0px';
|
||||
parent.style.transition = 'left 10s';
|
||||
parent.classList.add('init');
|
||||
child.style.left = '0px';
|
||||
child.style.transition = 'left 10s';
|
||||
flushComputedStyle(parent);
|
||||
|
||||
parent.style.left = '100px';
|
||||
parent.classList.add('change');
|
||||
child.style.left = '100px';
|
||||
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 4,
|
||||
'CSS transition on both pseudo-elements and elements ' +
|
||||
'are returned');
|
||||
assert_equals(anims[0].effect.target, parent,
|
||||
'The animation targeting the parent element comes first');
|
||||
assert_equals(anims[1].effect.target.type, '::before',
|
||||
'The animation targeting the ::before element comes second');
|
||||
assert_equals(anims[2].effect.target.type, '::after',
|
||||
'The animation targeting the ::after element comes third');
|
||||
assert_equals(anims[3].effect.target, child,
|
||||
'The animation targeting the child element comes last');
|
||||
}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
|
||||
'after sorting');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
@@ -18,6 +18,49 @@ test(function(t) {
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS transitions have the correct Animation.target');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
|
||||
'transition: all 10s;',
|
||||
'.change::after': 'width: 100px; height: 100px;' });
|
||||
var div = addDiv(t, { class: 'init' });
|
||||
flushComputedStyle(div);
|
||||
div.classList.add('change');
|
||||
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 2,
|
||||
'Got transitions running on ::after pseudo element');
|
||||
assert_equals(anims[0].effect.target, anims[1].effect.target,
|
||||
'Both transitions return the same target object');
|
||||
}, 'effect.target should return the same CSSPseudoElement object each time');
|
||||
|
||||
test(function(t) {
|
||||
addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
|
||||
'.change::after': 'width: 100px;' });
|
||||
var div = addDiv(t, { class: 'init' });
|
||||
flushComputedStyle(div);
|
||||
div.classList.add('change');
|
||||
var pseudoTarget = document.getAnimations()[0].effect.target;
|
||||
var effect = new KeyframeEffectReadOnly(pseudoTarget,
|
||||
{ background: ["blue", "red"] },
|
||||
3000);
|
||||
var newAnim = new Animation(effect, document.timeline);
|
||||
newAnim.play();
|
||||
|
||||
var anims = document.getAnimations();
|
||||
assert_equals(anims.length, 2,
|
||||
'Got animations running on ::after pseudo element');
|
||||
assert_not_equals(anims[0], newAnim,
|
||||
'The scriped-generated animation appears last');
|
||||
assert_equals(newAnim.effect.target, pseudoTarget,
|
||||
'The effect.target of the scripted-generated animation is ' +
|
||||
'the same as the one from the argument of ' +
|
||||
'KeyframeEffectReadOnly constructor');
|
||||
assert_equals(anims[0].effect.target, newAnim.effect.target,
|
||||
'Both the transition and the scripted-generated animation ' +
|
||||
'return the same target object');
|
||||
}, 'effect.target from the script-generated animation should return the same ' +
|
||||
'CSSPseudoElement object as that from the CSS generated transition');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
.init::before {
|
||||
content: '';
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
opacity: 0;
|
||||
transition: all 100s;
|
||||
}
|
||||
.change::before {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { class: 'init' });
|
||||
flushComputedStyle(div);
|
||||
div.classList.add('change');
|
||||
|
||||
// Sanity checks
|
||||
assert_equals(document.getAnimations().length, 3,
|
||||
'Got expected number of animations on document');
|
||||
var pseudoTarget = document.getAnimations()[0].effect.target;
|
||||
assert_class_string(pseudoTarget, 'CSSPseudoElement',
|
||||
'Got pseudo-element target');
|
||||
|
||||
// Check animations returned from the pseudo element are in correct order
|
||||
var anims = pseudoTarget.getAnimations();
|
||||
assert_equals(anims.length, 3,
|
||||
'Got expected number of animations on pseudo-element');
|
||||
assert_equals(anims[0].transitionProperty, 'height');
|
||||
assert_equals(anims[1].transitionProperty, 'opacity');
|
||||
assert_equals(anims[2].transitionProperty, 'width');
|
||||
}, 'getAnimations sorts simultaneous transitions by name');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_pseudoElement-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
@@ -17,6 +17,34 @@ function addDiv(t) {
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a style div to the document head.
|
||||
*
|
||||
* @param t The testharness.js Test object. If provided, this will be used
|
||||
* to register a cleanup callback to remove the style element
|
||||
* when the test finishes.
|
||||
*
|
||||
* @param rules A dictionary object with selector names and rules to set on
|
||||
* the style sheet.
|
||||
*/
|
||||
function addStyle(t, rules) {
|
||||
var extraStyle = document.createElement('style');
|
||||
document.head.appendChild(extraStyle);
|
||||
if (rules) {
|
||||
var sheet = extraStyle.sheet;
|
||||
for (var selector in rules) {
|
||||
sheet.insertRule(selector + '{' + rules[selector] + '}',
|
||||
sheet.cssRules.length);
|
||||
}
|
||||
}
|
||||
|
||||
if (t && typeof t.add_cleanup === 'function') {
|
||||
t.add_cleanup(function() {
|
||||
extraStyle.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise wrapper for requestAnimationFrame.
|
||||
*/
|
||||
|
||||
@@ -126,13 +126,6 @@ ChromeUtils::OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
|
||||
return pattern.Matches(attrs);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::CreateDefaultOriginAttributes(dom::GlobalObject& aGlobal,
|
||||
dom::OriginAttributesDictionary& aAttrs)
|
||||
{
|
||||
aAttrs = OriginAttributes();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::CreateOriginAttributesFromOrigin(dom::GlobalObject& aGlobal,
|
||||
const nsAString& aOrigin,
|
||||
@@ -149,7 +142,7 @@ ChromeUtils::CreateOriginAttributesFromOrigin(dom::GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::CreateOriginAttributesFromDict(dom::GlobalObject& aGlobal,
|
||||
ChromeUtils::FillNonDefaultOriginAttributes(dom::GlobalObject& aGlobal,
|
||||
const dom::OriginAttributesDictionary& aAttrs,
|
||||
dom::OriginAttributesDictionary& aNewAttrs)
|
||||
{
|
||||
|
||||
@@ -72,10 +72,6 @@ public:
|
||||
const dom::OriginAttributesDictionary& aAttrs,
|
||||
const dom::OriginAttributesPatternDictionary& aPattern);
|
||||
|
||||
static void
|
||||
CreateDefaultOriginAttributes(dom::GlobalObject& aGlobal,
|
||||
dom::OriginAttributesDictionary& aAttrs);
|
||||
|
||||
static void
|
||||
CreateOriginAttributesFromOrigin(dom::GlobalObject& aGlobal,
|
||||
const nsAString& aOrigin,
|
||||
@@ -83,7 +79,7 @@ public:
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void
|
||||
CreateOriginAttributesFromDict(dom::GlobalObject& aGlobal,
|
||||
FillNonDefaultOriginAttributes(dom::GlobalObject& aGlobal,
|
||||
const dom::OriginAttributesDictionary& aAttrs,
|
||||
dom::OriginAttributesDictionary& aNewAttrs);
|
||||
};
|
||||
|
||||
+51
-6
@@ -68,6 +68,7 @@
|
||||
#include "nsDocument.h"
|
||||
#include "nsAttrValueOrString.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsXULElement.h"
|
||||
#endif /* MOZ_XUL */
|
||||
@@ -3451,22 +3452,66 @@ Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
|
||||
Element::GetAnimations(const AnimationFilter& filter,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations)
|
||||
{
|
||||
nsIDocument* doc = GetComposedDoc();
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Style);
|
||||
}
|
||||
|
||||
GetAnimationsUnsorted(aAnimations);
|
||||
Element* elem = this;
|
||||
CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
|
||||
// For animations on generated-content elements, the animations are stored
|
||||
// on the parent element.
|
||||
nsIAtom* name = NodeInfo()->NameAtom();
|
||||
if (name == nsGkAtoms::mozgeneratedcontentbefore) {
|
||||
elem = GetParentElement();
|
||||
pseudoType = CSSPseudoElementType::before;
|
||||
} else if (name == nsGkAtoms::mozgeneratedcontentafter) {
|
||||
elem = GetParentElement();
|
||||
pseudoType = CSSPseudoElementType::after;
|
||||
}
|
||||
|
||||
if (!elem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filter.mSubtree ||
|
||||
pseudoType == CSSPseudoElementType::before ||
|
||||
pseudoType == CSSPseudoElementType::after) {
|
||||
GetAnimationsUnsorted(elem, pseudoType, aAnimations);
|
||||
} else {
|
||||
for (nsIContent* node = this;
|
||||
node;
|
||||
node = node->GetNextNode(this)) {
|
||||
if (!node->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
Element* element = node->AsElement();
|
||||
Element::GetAnimationsUnsorted(element, CSSPseudoElementType::NotPseudo,
|
||||
aAnimations);
|
||||
Element::GetAnimationsUnsorted(element, CSSPseudoElementType::before,
|
||||
aAnimations);
|
||||
Element::GetAnimationsUnsorted(element, CSSPseudoElementType::after,
|
||||
aAnimations);
|
||||
}
|
||||
}
|
||||
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations)
|
||||
/* static */ void
|
||||
Element::GetAnimationsUnsorted(Element* aElement,
|
||||
CSSPseudoElementType aPseudoType,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(this,
|
||||
CSSPseudoElementType::NotPseudo);
|
||||
MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo ||
|
||||
aPseudoType == CSSPseudoElementType::after ||
|
||||
aPseudoType == CSSPseudoElementType::before,
|
||||
"Unsupported pseudo type");
|
||||
MOZ_ASSERT(aElement, "Null element");
|
||||
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects) {
|
||||
return;
|
||||
}
|
||||
|
||||
+10
-5
@@ -55,6 +55,7 @@ class nsDocument;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct AnimationFilter;
|
||||
struct ScrollIntoViewOptions;
|
||||
struct ScrollToOptions;
|
||||
class ElementOrCSSPseudoElement;
|
||||
@@ -120,6 +121,7 @@ enum {
|
||||
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
|
||||
|
||||
namespace mozilla {
|
||||
enum class CSSPseudoElementType : uint8_t;
|
||||
class EventChainPostVisitor;
|
||||
class EventChainPreVisitor;
|
||||
class EventChainVisitor;
|
||||
@@ -180,7 +182,7 @@ public:
|
||||
* removing it from the document).
|
||||
*/
|
||||
void UpdateState(bool aNotify);
|
||||
|
||||
|
||||
/**
|
||||
* Method to update mState with link state information. This does not notify.
|
||||
*/
|
||||
@@ -340,7 +342,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Only call UpdateState if we need to notify, because we call
|
||||
* SetDirectionality for every element, and UpdateState is very very slow
|
||||
* for some elements.
|
||||
@@ -874,8 +876,11 @@ public:
|
||||
ErrorResult& aError);
|
||||
|
||||
// Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
|
||||
void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
void GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
void GetAnimations(const AnimationFilter& filter,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
static void GetAnimationsUnsorted(Element* aElement,
|
||||
CSSPseudoElementType aPseudoType,
|
||||
nsTArray<RefPtr<Animation>>& aAnimations);
|
||||
|
||||
NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
|
||||
virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
|
||||
@@ -1006,7 +1011,7 @@ public:
|
||||
* Return the CORS mode for a given string
|
||||
*/
|
||||
static CORSMode StringToCORSMode(const nsAString& aValue);
|
||||
|
||||
|
||||
/**
|
||||
* Return the CORS mode for a given nsAttrValue (which may be null,
|
||||
* but if not should have been parsed via ParseCORSValue).
|
||||
|
||||
+66
-40
@@ -31,6 +31,7 @@
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsContentList.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIBaseWindow.h"
|
||||
#include "mozilla/css/Loader.h"
|
||||
@@ -198,7 +199,9 @@
|
||||
#include "imgRequestProxy.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "nsIAddonPolicyService.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "mozilla/dom/AnimatableBinding.h"
|
||||
#include "mozilla/dom/AnonymousContent.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
@@ -1455,7 +1458,7 @@ nsIDocument::nsIDocument()
|
||||
{
|
||||
SetInDocument();
|
||||
|
||||
PR_INIT_CLIST(&mDOMMediaQueryLists);
|
||||
PR_INIT_CLIST(&mDOMMediaQueryLists);
|
||||
}
|
||||
|
||||
// NOTE! nsDocument::operator new() zeroes out all members, so don't
|
||||
@@ -2812,12 +2815,18 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is part of the Loop/Hello service
|
||||
bool applyLoopCSP = IsLoopDocument(aChannel);
|
||||
// Check if this is a document from a WebExtension.
|
||||
nsString addonId;
|
||||
principal->GetAddonId(addonId);
|
||||
bool applyAddonCSP = !addonId.IsEmpty();
|
||||
|
||||
// Check if this is part of the Loop/Hello service
|
||||
bool applyLoopCSP = IsLoopDocument(aChannel);
|
||||
|
||||
// If there's no CSP to apply, go ahead and return early
|
||||
if (!applyAppDefaultCSP &&
|
||||
!applyAppManifestCSP &&
|
||||
!applyAddonCSP &&
|
||||
!applyLoopCSP &&
|
||||
cspHeaderValue.IsEmpty() &&
|
||||
cspROHeaderValue.IsEmpty()) {
|
||||
@@ -2875,6 +2884,22 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
csp->AppendPolicy(appManifestCSP, false, false);
|
||||
}
|
||||
|
||||
// ----- if the doc is an addon, apply its CSP.
|
||||
if (applyAddonCSP) {
|
||||
nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
|
||||
|
||||
nsAutoString addonCSP;
|
||||
rv = aps->GetBaseCSP(addonCSP);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
csp->AppendPolicy(addonCSP, false, false);
|
||||
}
|
||||
|
||||
rv = aps->GetAddonCSP(addonId, addonCSP);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
csp->AppendPolicy(addonCSP, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- if the doc is part of Loop, apply the loop CSP
|
||||
if (applyLoopCSP) {
|
||||
nsAdoptingString loopCSP;
|
||||
@@ -3197,27 +3222,13 @@ nsDocument::Timeline()
|
||||
void
|
||||
nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
|
||||
{
|
||||
FlushPendingNotifications(Flush_Style);
|
||||
|
||||
// Bug 1174575: Until we implement a suitable PseudoElement interface we
|
||||
// don't have anything to return for the |target| attribute of
|
||||
// KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
|
||||
// Rather than return some half-baked version of these objects (e.g.
|
||||
// we a null effect attribute) we simply don't provide access to animations
|
||||
// whose effect refers to a pseudo-element until we can support them
|
||||
// properly.
|
||||
for (nsIContent* node = nsINode::GetFirstChild();
|
||||
node;
|
||||
node = node->GetNextNode(this)) {
|
||||
if (!node->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node->AsElement()->GetAnimationsUnsorted(aAnimations);
|
||||
Element* root = GetRootElement();
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort animations by priority
|
||||
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
|
||||
AnimationFilter filter;
|
||||
filter.mSubtree = true;
|
||||
root->GetAnimations(filter, aAnimations);
|
||||
}
|
||||
|
||||
/* Return true if the document is in the focused top-level window, and is an
|
||||
@@ -4645,6 +4656,26 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||
loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
|
||||
}
|
||||
}
|
||||
|
||||
using mozilla::dom::workers::ServiceWorkerManager;
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
ErrorResult error;
|
||||
if (swm->IsControlled(this, error)) {
|
||||
imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
|
||||
if (loader) {
|
||||
loader->ClearCacheForControlledDocument(this);
|
||||
}
|
||||
|
||||
// We may become controlled again if this document comes back out
|
||||
// of bfcache. Clear our state to allow that to happen. Only
|
||||
// clear this flag if we are actually controlled, though, so pages
|
||||
// that were force reloaded don't become controlled when they
|
||||
// come out of bfcache.
|
||||
mMaybeServiceWorkerControlled = false;
|
||||
}
|
||||
swm->MaybeStopControlling(this);
|
||||
}
|
||||
}
|
||||
|
||||
// BlockOnload() might be called before mScriptGlobalObject is set.
|
||||
@@ -4758,8 +4789,13 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (swm) {
|
||||
nsAutoString documentId;
|
||||
static_cast<nsDocShell*>(docShell.get())->GetInterceptedDocumentId(documentId);
|
||||
// If this document is being resurrected from the bfcache, then we may
|
||||
// already have a document ID. In that case reuse the same ID. Otherwise
|
||||
// get our document ID from the docshell.
|
||||
nsString documentId(GetId());
|
||||
if (documentId.IsEmpty()) {
|
||||
static_cast<nsDocShell*>(docShell.get())->GetInterceptedDocumentId(documentId);
|
||||
}
|
||||
|
||||
swm->MaybeStartControlling(this, documentId);
|
||||
mMaybeServiceWorkerControlled = true;
|
||||
@@ -8891,6 +8927,7 @@ nsDocument::Destroy()
|
||||
|
||||
mIsGoingAway = true;
|
||||
|
||||
SetScriptGlobalObject(nullptr);
|
||||
RemovedFromDocShell();
|
||||
|
||||
bool oldVal = mInUnlinkOrDeletion;
|
||||
@@ -8921,19 +8958,6 @@ nsDocument::RemovedFromDocShell()
|
||||
if (mRemovedFromDocShell)
|
||||
return;
|
||||
|
||||
using mozilla::dom::workers::ServiceWorkerManager;
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
ErrorResult error;
|
||||
if (swm->IsControlled(this, error)) {
|
||||
imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
|
||||
if (loader) {
|
||||
loader->ClearCacheForControlledDocument(this);
|
||||
}
|
||||
}
|
||||
swm->MaybeStopControlling(this);
|
||||
}
|
||||
|
||||
mRemovedFromDocShell = true;
|
||||
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
|
||||
|
||||
@@ -13127,7 +13151,7 @@ nsDocument::ReportUseCounters()
|
||||
for (int32_t c = 0;
|
||||
c < eUseCounter_Count; ++c) {
|
||||
UseCounter uc = static_cast<UseCounter>(c);
|
||||
|
||||
|
||||
Telemetry::ID id =
|
||||
static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter + uc * 2);
|
||||
bool value = GetUseCounter(uc);
|
||||
@@ -13270,7 +13294,9 @@ nsIDocument::GetOrCreateId(nsAString& aId)
|
||||
void
|
||||
nsIDocument::SetId(const nsAString& aId)
|
||||
{
|
||||
MOZ_ASSERT(mId.IsEmpty(), "Cannot set the document ID after we have one");
|
||||
// The ID should only be set one time, but we may get the same value
|
||||
// more than once if the document is controlled coming out of bfcache.
|
||||
MOZ_ASSERT_IF(mId != aId, mId.IsEmpty());
|
||||
mId = aId;
|
||||
}
|
||||
|
||||
|
||||
@@ -2844,6 +2844,11 @@ protected:
|
||||
FlushUserFontSet();
|
||||
}
|
||||
|
||||
const nsString& GetId() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
nsCString mReferrer;
|
||||
nsString mLastModified;
|
||||
|
||||
|
||||
@@ -92,5 +92,7 @@ MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for
|
||||
MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
|
||||
MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
|
||||
MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
|
||||
MSG_DEF(MSG_INVALID_DURATION_ERROR, 0, JSEXN_TYPEERR, "Invalid duration.")
|
||||
MSG_DEF(MSG_INVALID_EASING_ERROR, 0, JSEXN_TYPEERR, "Invalid easing.")
|
||||
MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0] because the registration has been {1} since the update was scheduled.")
|
||||
MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
|
||||
MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
|
||||
MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
|
||||
|
||||
@@ -21,6 +21,12 @@ interface nsIURI;
|
||||
|
||||
typedef unsigned short CSPDirective;
|
||||
|
||||
%{C++
|
||||
class nsCSPPolicy;
|
||||
%}
|
||||
|
||||
[ptr] native CSPPolicyPtr(const nsCSPPolicy);
|
||||
|
||||
[scriptable, builtinclass, uuid(b3c4c0ae-bd5e-4cad-87e0-8d210dbb3f9f)]
|
||||
interface nsIContentSecurityPolicy : nsISerializable
|
||||
{
|
||||
@@ -60,6 +66,13 @@ interface nsIContentSecurityPolicy : nsISerializable
|
||||
*/
|
||||
AString getPolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Accessor method for a read-only pointer the policy object at a given
|
||||
* index. Returns a null pointer if the index is larger than the current
|
||||
* policy count.
|
||||
*/
|
||||
[noscript,notxpcom,nostdcall] CSPPolicyPtr GetPolicy(in unsigned long index);
|
||||
|
||||
/**
|
||||
* Returns the number of policies attached to this CSP instance. Useful with
|
||||
* getPolicy().
|
||||
|
||||
@@ -190,6 +190,8 @@ InterceptionCanceledWithURL=Failed to load '%S'. A ServiceWorker canceled the lo
|
||||
InterceptionRejectedResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that rejected with '%2$S'.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "promise", "FetchEvent.respondWith()", or "Response". %1$S is a URL. %2$S is an error string.
|
||||
InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a promise to FetchEvent.respondWith() that resolved with non-Response value '%2$S'.
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Service-Worker-Allowed" or "HTTP". %1$S and %2$S are URLs.
|
||||
ServiceWorkerScopePathMismatch=Failed to register a ServiceWorker: The path of the provided scope '%1$S' is not under the max scope allowed '%2$S'. Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
|
||||
ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
|
||||
PatternAttributeCompileFailure=Unable to check <input pattern='%S'> because the pattern is not a valid regexp: %S
|
||||
# LOCALIZATION NOTE: Do not translate "postMessage" or DOMWindow. %S values are origins, like https://domain.com:port
|
||||
|
||||
@@ -2575,8 +2575,8 @@ public:
|
||||
// This is coming from a ServiceWorkerRegistrationWorkerThread.
|
||||
MOZ_ASSERT(registration);
|
||||
|
||||
if (!registration->mActiveWorker ||
|
||||
registration->mActiveWorker->ID() != mWorkerPrivate->ServiceWorkerID()) {
|
||||
if (!registration->GetActive() ||
|
||||
registration->GetActive()->ID() != mWorkerPrivate->ServiceWorkerID()) {
|
||||
mRv = NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
+76
-21
@@ -909,7 +909,11 @@ Promise::MaybeRejectWithNull()
|
||||
bool
|
||||
Promise::PerformMicroTaskCheckpoint()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
// On the main thread, we always use the main promise micro task queue.
|
||||
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
|
||||
runtime->GetPromiseMicroTaskQueue();
|
||||
|
||||
@@ -917,10 +921,7 @@ Promise::PerformMicroTaskCheckpoint()
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<AutoSafeJSContext> cx;
|
||||
if (NS_IsMainThread()) {
|
||||
cx.emplace();
|
||||
}
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
do {
|
||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front();
|
||||
@@ -932,15 +933,77 @@ Promise::PerformMicroTaskCheckpoint()
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
if (cx.isSome()) {
|
||||
JS_CheckForInterrupt(cx.ref());
|
||||
}
|
||||
JS_CheckForInterrupt(cx);
|
||||
runtime->AfterProcessMicrotask();
|
||||
} while (!microtaskQueue.empty());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Promise::PerformWorkerMicroTaskCheckpoint()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
for (;;) {
|
||||
// For a normal microtask checkpoint, we try to use the debugger microtask
|
||||
// queue first. If the debugger queue is empty, we use the normal microtask
|
||||
// queue instead.
|
||||
std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
|
||||
&runtime->GetDebuggerPromiseMicroTaskQueue();
|
||||
|
||||
if (microtaskQueue->empty()) {
|
||||
microtaskQueue = &runtime->GetPromiseMicroTaskQueue();
|
||||
if (microtaskQueue->empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front();
|
||||
MOZ_ASSERT(runnable);
|
||||
|
||||
// This function can re-enter, so we remove the element before calling.
|
||||
microtaskQueue->pop();
|
||||
nsresult rv = runnable->Run();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
runtime->AfterProcessMicrotask();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
for (;;) {
|
||||
// For a debugger microtask checkpoint, we always use the debugger microtask
|
||||
// queue.
|
||||
std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
|
||||
&runtime->GetDebuggerPromiseMicroTaskQueue();
|
||||
|
||||
if (microtaskQueue->empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front();
|
||||
MOZ_ASSERT(runnable);
|
||||
|
||||
// This function can re-enter, so we remove the element before calling.
|
||||
microtaskQueue->pop();
|
||||
nsresult rv = runnable->Run();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
runtime->AfterProcessMicrotask();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ bool
|
||||
@@ -2402,18 +2465,6 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback,
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/* static */ void
|
||||
Promise::DispatchToMicroTask(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
|
||||
runtime->GetPromiseMicroTaskQueue();
|
||||
|
||||
microtaskQueue.push(aRunnable);
|
||||
}
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
#if defined(DOM_PROMISE_DEPRECATED_REPORTING)
|
||||
void
|
||||
@@ -2504,6 +2555,8 @@ void
|
||||
Promise::ResolveInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
mResolvePending = true;
|
||||
|
||||
if (aValue.isObject()) {
|
||||
@@ -2545,7 +2598,7 @@ Promise::ResolveInternal(JSContext* aCx,
|
||||
new PromiseInit(nullptr, thenObj, mozilla::dom::GetIncumbentGlobal());
|
||||
RefPtr<PromiseResolveThenableJob> task =
|
||||
new PromiseResolveThenableJob(this, valueObj, thenCallback);
|
||||
DispatchToMicroTask(task);
|
||||
runtime->DispatchToMicroTask(task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2635,6 +2688,8 @@ Promise::MaybeSettle(JS::Handle<JS::Value> aValue,
|
||||
void
|
||||
Promise::TriggerPromiseReactions()
|
||||
{
|
||||
CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
|
||||
|
||||
nsTArray<RefPtr<PromiseCallback>> callbacks;
|
||||
callbacks.SwapElements(mState == Resolved ? mResolveCallbacks
|
||||
: mRejectCallbacks);
|
||||
@@ -2644,7 +2699,7 @@ Promise::TriggerPromiseReactions()
|
||||
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
|
||||
RefPtr<PromiseReactionJob> task =
|
||||
new PromiseReactionJob(this, callbacks[i], mResult);
|
||||
DispatchToMicroTask(task);
|
||||
runtime->DispatchToMicroTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,10 @@ public:
|
||||
// Returns true if at least one microtask was processed.
|
||||
static bool PerformMicroTaskCheckpoint();
|
||||
|
||||
static void PerformWorkerMicroTaskCheckpoint();
|
||||
|
||||
static void PerformWorkerDebuggerMicroTaskCheckpoint();
|
||||
|
||||
// WebIDL
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
@@ -288,10 +292,6 @@ public:
|
||||
uint64_t GetID();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Queue an async microtask to current main or worker thread.
|
||||
static void
|
||||
DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
enum JSCallbackSlots {
|
||||
SLOT_PROMISE = 0,
|
||||
|
||||
@@ -299,6 +299,15 @@ nsCSPContext::GetPolicy(uint32_t aIndex, nsAString& outStr)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSPPolicy*
|
||||
nsCSPContext::GetPolicy(uint32_t aIndex)
|
||||
{
|
||||
if (aIndex >= mPolicies.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
return mPolicies[aIndex];
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::GetPolicyCount(uint32_t *outPolicyCount)
|
||||
{
|
||||
|
||||
@@ -389,6 +389,12 @@ nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirect
|
||||
return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return aVisitor->visitSchemeSrc(*this);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPSchemeSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -597,6 +603,12 @@ nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return aVisitor->visitHostSrc(*this);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHostSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -674,6 +686,12 @@ nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce)
|
||||
return mKeyword == aKeyword;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return aVisitor->visitKeywordSrc(*this);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPKeywordSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -730,6 +748,12 @@ nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) c
|
||||
return mNonce.Equals(aHashOrNonce);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return aVisitor->visitNonceSrc(*this);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPNonceSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -787,6 +811,12 @@ nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) co
|
||||
return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return aVisitor->visitHashSrc(*this);
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPHashSrc::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -808,6 +838,12 @@ nsCSPReportURI::~nsCSPReportURI()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSPReportURI::toString(nsAString& outStr) const
|
||||
{
|
||||
@@ -1012,6 +1048,17 @@ nsCSPDirective::getReportURIs(nsTArray<nsString> &outReportURIs) const
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
|
||||
if (!mSrcs[i]->visit(aVisitor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nsCSPDirective::equals(CSPDirective aDirective) const
|
||||
{
|
||||
return (mDirective == aDirective);
|
||||
@@ -1320,3 +1367,14 @@ nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const
|
||||
{
|
||||
for (uint32_t i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->equals(aDir)) {
|
||||
return mDirectives[i]->visitSrcs(aVisitor);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -145,7 +145,11 @@ inline const char* CSP_EnumToKeyword(enum CSPKeyword aKey)
|
||||
static_assert((sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0]) ==
|
||||
static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)),
|
||||
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
|
||||
return CSPStrKeywords[static_cast<uint32_t>(aKey)];
|
||||
|
||||
if (static_cast<uint32_t>(aKey) < static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) {
|
||||
return CSPStrKeywords[static_cast<uint32_t>(aKey)];
|
||||
}
|
||||
return "error: invalid keyword in CSP_EnumToKeyword";
|
||||
}
|
||||
|
||||
inline CSPKeyword CSP_KeywordToEnum(const nsAString& aKey)
|
||||
@@ -177,6 +181,7 @@ bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
|
||||
bool CSP_IsQuotelessKeyword(const nsAString& aKey);
|
||||
CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
|
||||
|
||||
class nsCSPSrcVisitor;
|
||||
|
||||
/* =============== nsCSPSrc ================== */
|
||||
|
||||
@@ -188,6 +193,7 @@ class nsCSPBaseSrc {
|
||||
virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const;
|
||||
virtual bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
virtual bool visit(nsCSPSrcVisitor* aVisitor) const = 0;
|
||||
virtual void toString(nsAString& outStr) const = 0;
|
||||
};
|
||||
|
||||
@@ -200,8 +206,12 @@ class nsCSPSchemeSrc : public nsCSPBaseSrc {
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
inline void getScheme(nsAString& outStr) const
|
||||
{ outStr.Assign(mScheme); };
|
||||
|
||||
private:
|
||||
nsString mScheme;
|
||||
};
|
||||
@@ -215,12 +225,25 @@ class nsCSPHostSrc : public nsCSPBaseSrc {
|
||||
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
void setScheme(const nsAString& aScheme);
|
||||
void setPort(const nsAString& aPort);
|
||||
void appendPath(const nsAString &aPath);
|
||||
|
||||
inline void getScheme(nsAString& outStr) const
|
||||
{ outStr.Assign(mScheme); };
|
||||
|
||||
inline void getHost(nsAString& outStr) const
|
||||
{ outStr.Assign(mHost); };
|
||||
|
||||
inline void getPort(nsAString& outStr) const
|
||||
{ outStr.Assign(mPort); };
|
||||
|
||||
inline void getPath(nsAString& outStr) const
|
||||
{ outStr.Assign(mPath); };
|
||||
|
||||
private:
|
||||
nsString mScheme;
|
||||
nsString mHost;
|
||||
@@ -236,9 +259,13 @@ class nsCSPKeywordSrc : public nsCSPBaseSrc {
|
||||
virtual ~nsCSPKeywordSrc();
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
void invalidate();
|
||||
|
||||
inline CSPKeyword getKeyword() const
|
||||
{ return mKeyword; };
|
||||
|
||||
private:
|
||||
CSPKeyword mKeyword;
|
||||
// invalidate 'unsafe-inline' if nonce- or hash-source specified
|
||||
@@ -255,8 +282,12 @@ class nsCSPNonceSrc : public nsCSPBaseSrc {
|
||||
bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
|
||||
bool aReportOnly, bool aUpgradeInsecure) const;
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
inline void getNonce(nsAString& outStr) const
|
||||
{ outStr.Assign(mNonce); };
|
||||
|
||||
private:
|
||||
nsString mNonce;
|
||||
};
|
||||
@@ -270,6 +301,13 @@ class nsCSPHashSrc : public nsCSPBaseSrc {
|
||||
|
||||
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
|
||||
inline void getAlgorithm(nsAString& outStr) const
|
||||
{ outStr.Assign(mAlgorithm); };
|
||||
|
||||
inline void getHash(nsAString& outStr) const
|
||||
{ outStr.Assign(mHash); };
|
||||
|
||||
private:
|
||||
nsString mAlgorithm;
|
||||
@@ -283,12 +321,32 @@ class nsCSPReportURI : public nsCSPBaseSrc {
|
||||
explicit nsCSPReportURI(nsIURI* aURI);
|
||||
virtual ~nsCSPReportURI();
|
||||
|
||||
bool visit(nsCSPSrcVisitor* aVisitor) const;
|
||||
void toString(nsAString& outStr) const;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIURI> mReportURI;
|
||||
};
|
||||
|
||||
/* =============== nsCSPSrcVisitor ================== */
|
||||
|
||||
class nsCSPSrcVisitor {
|
||||
public:
|
||||
virtual bool visitSchemeSrc(const nsCSPSchemeSrc& src) = 0;
|
||||
|
||||
virtual bool visitHostSrc(const nsCSPHostSrc& src) = 0;
|
||||
|
||||
virtual bool visitKeywordSrc(const nsCSPKeywordSrc& src) = 0;
|
||||
|
||||
virtual bool visitNonceSrc(const nsCSPNonceSrc& src) = 0;
|
||||
|
||||
virtual bool visitHashSrc(const nsCSPHashSrc& src) = 0;
|
||||
|
||||
protected:
|
||||
explicit nsCSPSrcVisitor() {};
|
||||
virtual ~nsCSPSrcVisitor() {};
|
||||
};
|
||||
|
||||
/* =============== nsCSPDirective ============= */
|
||||
|
||||
class nsCSPDirective {
|
||||
@@ -314,6 +372,8 @@ class nsCSPDirective {
|
||||
|
||||
void getReportURIs(nsTArray<nsString> &outReportURIs) const;
|
||||
|
||||
bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;
|
||||
|
||||
private:
|
||||
CSPDirective mDirective;
|
||||
nsTArray<nsCSPBaseSrc*> mSrcs;
|
||||
@@ -476,6 +536,8 @@ class nsCSPPolicy {
|
||||
inline uint32_t getNumDirectives() const
|
||||
{ return mDirectives.Length(); }
|
||||
|
||||
bool visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const;
|
||||
|
||||
private:
|
||||
nsUpgradeInsecureDirective* mUpgradeInsecDir;
|
||||
nsTArray<nsCSPDirective*> mDirectives;
|
||||
|
||||
@@ -14,6 +14,10 @@ dictionary KeyframeAnimationOptions : KeyframeEffectOptions {
|
||||
DOMString id = "";
|
||||
};
|
||||
|
||||
dictionary AnimationFilter {
|
||||
boolean subtree = false;
|
||||
};
|
||||
|
||||
[NoInterfaceObject]
|
||||
interface Animatable {
|
||||
[Func="nsDocument::IsWebAnimationsEnabled", Throws]
|
||||
@@ -21,5 +25,5 @@ interface Animatable {
|
||||
optional (unrestricted double or KeyframeAnimationOptions)
|
||||
options);
|
||||
[Func="nsDocument::IsWebAnimationsEnabled"]
|
||||
sequence<Animation> getAnimations();
|
||||
sequence<Animation> getAnimations(optional AnimationFilter filter);
|
||||
};
|
||||
|
||||
@@ -29,16 +29,6 @@ interface ChromeUtils : ThreadSafeChromeUtils {
|
||||
originAttributesMatchPattern(optional OriginAttributesDictionary originAttrs,
|
||||
optional OriginAttributesPatternDictionary pattern);
|
||||
|
||||
/**
|
||||
* Returns an OriginAttributesDictionary with all default attributes added
|
||||
* and assigned default values.
|
||||
*
|
||||
* @returns An OriginAttributesDictionary populated with the
|
||||
* default attributes added and assigned default values.
|
||||
*/
|
||||
static OriginAttributesDictionary
|
||||
createDefaultOriginAttributes();
|
||||
|
||||
/**
|
||||
* Returns an OriginAttributesDictionary with values from the |origin| suffix
|
||||
* and unspecified attributes added and assigned default values.
|
||||
@@ -62,7 +52,7 @@ interface ChromeUtils : ThreadSafeChromeUtils {
|
||||
* default values.
|
||||
*/
|
||||
static OriginAttributesDictionary
|
||||
createOriginAttributesFromDict(optional OriginAttributesDictionary originAttrs);
|
||||
fillNonDefaultOriginAttributes(optional OriginAttributesDictionary originAttrs);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ callback AnyCallback = any (any value);
|
||||
// values work.
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
[Constructor(PromiseInit init),
|
||||
Exposed=(Window,Worker,System)]
|
||||
Exposed=(Window,Worker,WorkerDebugger,System)]
|
||||
// Need to escape "Promise" so it's treated as an identifier.
|
||||
interface _Promise {
|
||||
// Have to use "any" (or "object", but "any" is simpler) as the type to
|
||||
|
||||
@@ -880,8 +880,10 @@ public:
|
||||
|
||||
~WorkerJSRuntime()
|
||||
{
|
||||
JSRuntime* rt = Runtime();
|
||||
MOZ_ASSERT(rt);
|
||||
JSRuntime* rt = MaybeRuntime();
|
||||
if (!rt) {
|
||||
return; // Initialize() must have failed
|
||||
}
|
||||
|
||||
delete static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(rt));
|
||||
JS_SetRuntimePrivate(rt, nullptr);
|
||||
@@ -969,6 +971,34 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DispatchToMicroTask(nsIRunnable* aRunnable) override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
|
||||
|
||||
JSContext* cx = GetCurrentThreadJSContext();
|
||||
NS_ASSERTION(cx, "This should never be null!");
|
||||
|
||||
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
NS_ASSERTION(global, "This should never be null!");
|
||||
|
||||
// On worker threads, if the current global is the worker global, we use the
|
||||
// main promise micro task queue. Otherwise, the current global must be
|
||||
// either the debugger global or a debugger sandbox, and we use the debugger
|
||||
// promise micro task queue instead.
|
||||
if (IsWorkerGlobal(global)) {
|
||||
microTaskQueue = &mPromiseMicroTaskQueue;
|
||||
} else {
|
||||
MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global));
|
||||
|
||||
microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
|
||||
}
|
||||
|
||||
microTaskQueue->push(aRunnable);
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerInfo.h"
|
||||
|
||||
#include "ServiceWorkerScriptCache.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetCacheName(nsAString& aCacheName)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
aCacheName = mCacheName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult)
|
||||
{
|
||||
if (NS_WARN_IF(!aResult)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mServiceWorkerPrivate->GetDebugger(aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::AttachDebugger()
|
||||
{
|
||||
return mServiceWorkerPrivate->AttachDebugger();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::DetachDebugger()
|
||||
{
|
||||
return mServiceWorkerPrivate->DetachDebugger();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
|
||||
{
|
||||
MOZ_ASSERT(aWorker);
|
||||
#ifdef DEBUG
|
||||
nsAutoString workerURL;
|
||||
aWorker->GetScriptURL(workerURL);
|
||||
MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
|
||||
#endif
|
||||
MOZ_ASSERT(!mInstances.Contains(aWorker));
|
||||
|
||||
mInstances.AppendElement(aWorker);
|
||||
aWorker->SetState(State());
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker)
|
||||
{
|
||||
MOZ_ASSERT(aWorker);
|
||||
#ifdef DEBUG
|
||||
nsAutoString workerURL;
|
||||
aWorker->GetScriptURL(workerURL);
|
||||
MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
|
||||
#endif
|
||||
MOZ_ASSERT(mInstances.Contains(aWorker));
|
||||
|
||||
mInstances.RemoveElement(aWorker);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ChangeStateUpdater final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ChangeStateUpdater(const nsTArray<ServiceWorker*>& aInstances,
|
||||
ServiceWorkerState aState)
|
||||
: mState(aState)
|
||||
{
|
||||
for (size_t i = 0; i < aInstances.Length(); ++i) {
|
||||
mInstances.AppendElement(aInstances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
// We need to update the state of all instances atomically before notifying
|
||||
// them to make sure that the observed state for all instances inside
|
||||
// statechange event handlers is correct.
|
||||
for (size_t i = 0; i < mInstances.Length(); ++i) {
|
||||
mInstances[i]->SetState(mState);
|
||||
}
|
||||
for (size_t i = 0; i < mInstances.Length(); ++i) {
|
||||
mInstances[i]->DispatchStateChange(mState);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
AutoTArray<RefPtr<ServiceWorker>, 1> mInstances;
|
||||
ServiceWorkerState mState;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
#ifdef DEBUG
|
||||
// Any state can directly transition to redundant, but everything else is
|
||||
// ordered.
|
||||
if (aState != ServiceWorkerState::Redundant) {
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
|
||||
}
|
||||
// Activated can only go to redundant.
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
|
||||
#endif
|
||||
// Flush any pending functional events to the worker when it transitions to the
|
||||
// activated state.
|
||||
// TODO: Do we care that these events will race with the propagation of the
|
||||
// state change?
|
||||
if (aState == ServiceWorkerState::Activated && mState != aState) {
|
||||
mServiceWorkerPrivate->Activated();
|
||||
}
|
||||
mState = aState;
|
||||
nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, mState);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget()));
|
||||
if (mState == ServiceWorkerState::Redundant) {
|
||||
serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName);
|
||||
}
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCacheName(aCacheName)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
, mServiceWorkerID(GetNextID())
|
||||
, mServiceWorkerPrivate(new ServiceWorkerPrivate(this))
|
||||
, mSkipWaitingFlag(false)
|
||||
{
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
MOZ_ASSERT(!mScope.IsEmpty());
|
||||
MOZ_ASSERT(!mScriptSpec.IsEmpty());
|
||||
MOZ_ASSERT(!mCacheName.IsEmpty());
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::~ServiceWorkerInfo()
|
||||
{
|
||||
MOZ_ASSERT(mServiceWorkerPrivate);
|
||||
mServiceWorkerPrivate->NoteDeadServiceWorkerInfo();
|
||||
}
|
||||
|
||||
static uint64_t gServiceWorkerInfoCurrentID = 0;
|
||||
|
||||
uint64_t
|
||||
ServiceWorkerInfo::GetNextID() const
|
||||
{
|
||||
return ++gServiceWorkerInfoCurrentID;
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
RefPtr<ServiceWorker> ref;
|
||||
|
||||
for (uint32_t i = 0; i < mInstances.Length(); ++i) {
|
||||
MOZ_ASSERT(mInstances[i]);
|
||||
if (mInstances[i]->GetOwner() == aWindow) {
|
||||
ref = mInstances[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
ref = new ServiceWorker(aWindow, this);
|
||||
}
|
||||
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
@@ -0,0 +1,144 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerinfo_h
|
||||
#define mozilla_dom_workers_serviceworkerinfo_h
|
||||
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
class ServiceWorkerPrivate;
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo final : public nsIServiceWorkerInfo
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
const nsCString mScriptSpec;
|
||||
const nsString mCacheName;
|
||||
ServiceWorkerState mState;
|
||||
|
||||
// This id is shared with WorkerPrivate to match requests issued by service
|
||||
// workers to their corresponding serviceWorkerInfo.
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
// We hold rawptrs since the ServiceWorker constructor and destructor ensure
|
||||
// addition and removal.
|
||||
// There is a high chance of there being at least one ServiceWorker
|
||||
// associated with this all the time.
|
||||
AutoTArray<ServiceWorker*, 1> mInstances;
|
||||
|
||||
RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
|
||||
bool mSkipWaitingFlag;
|
||||
|
||||
~ServiceWorkerInfo();
|
||||
|
||||
// Generates a unique id for the service worker, with zero being treated as
|
||||
// invalid.
|
||||
uint64_t
|
||||
GetNextID() const;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERINFO
|
||||
|
||||
class ServiceWorkerPrivate*
|
||||
WorkerPrivate() const
|
||||
{
|
||||
MOZ_ASSERT(mServiceWorkerPrivate);
|
||||
return mServiceWorkerPrivate;
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetPrincipal() const
|
||||
{
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ScriptSpec() const
|
||||
{
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
Scope() const
|
||||
{
|
||||
return mScope;
|
||||
}
|
||||
|
||||
bool SkipWaitingFlag() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mSkipWaitingFlag;
|
||||
}
|
||||
|
||||
void SetSkipWaitingFlag()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mSkipWaitingFlag = true;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName);
|
||||
|
||||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
const nsString&
|
||||
CacheName() const
|
||||
{
|
||||
return mCacheName;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ID() const
|
||||
{
|
||||
return mServiceWorkerID;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateState(ServiceWorkerState aState);
|
||||
|
||||
// Only used to set initial state when loading from disk!
|
||||
void
|
||||
SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
void
|
||||
AppendWorker(ServiceWorker* aWorker);
|
||||
|
||||
void
|
||||
RemoveWorker(ServiceWorker* aWorker);
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
GetOrCreateInstance(nsPIDOMWindow* aWindow);
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerinfo_h
|
||||
@@ -0,0 +1,231 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
ServiceWorkerJob::Type
|
||||
ServiceWorkerJob::GetType() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
ServiceWorkerJob::State
|
||||
ServiceWorkerJob::GetState() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::Canceled() const
|
||||
{
|
||||
return mCanceled;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::ResultCallbacksInvoked() const
|
||||
{
|
||||
return mResultCallbacksInvoked;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
return mType == aJob->mType &&
|
||||
mScope.Equals(aJob->mScope) &&
|
||||
mScriptSpec.Equals(aJob->mScriptSpec) &&
|
||||
mPrincipal->Equals(aJob->mPrincipal);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::AppendResultCallback(Callback* aCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mState != State::Finished);
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(mFinalCallback != aCallback);
|
||||
MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
|
||||
MOZ_ASSERT(!mResultCallbacksInvoked);
|
||||
mResultCallbackList.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
MOZ_ASSERT(aJob->mState == State::Initial);
|
||||
|
||||
// Take the callbacks from the other job immediately to avoid the
|
||||
// any possibility of them existing on both jobs at once.
|
||||
nsTArray<RefPtr<Callback>> callbackList;
|
||||
callbackList.SwapElements(aJob->mResultCallbackList);
|
||||
|
||||
for (RefPtr<Callback>& callback : callbackList) {
|
||||
// Use AppendResultCallback() so that assertion checking is performed on
|
||||
// each callback.
|
||||
AppendResultCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Start(Callback* aFinalCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
|
||||
MOZ_ASSERT(aFinalCallback);
|
||||
MOZ_ASSERT(!mFinalCallback);
|
||||
MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
|
||||
mFinalCallback = aFinalCallback;
|
||||
|
||||
MOZ_ASSERT(mState == State::Initial);
|
||||
mState = State::Started;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ServiceWorkerJob::AsyncExecute);
|
||||
|
||||
// We may have to wait for the PBackground actor to be initialized
|
||||
// before proceeding. We should always be able to get a ServiceWorkerManager,
|
||||
// however, since Start() should not be called during shutdown.
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm->HasBackgroundActor()) {
|
||||
swm->AppendPendingOperation(runnable);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise start asynchronously. We should never run a job synchronously.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
NS_DispatchToMainThread(runnable.forget())));
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Cancel()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
ServiceWorkerJob::ServiceWorkerJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec)
|
||||
: mType(aType)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mState(State::Initial)
|
||||
, mCanceled(false)
|
||||
, mResultCallbacksInvoked(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
MOZ_ASSERT(!mScope.IsEmpty());
|
||||
// Some job types may have an empty script spec
|
||||
}
|
||||
|
||||
ServiceWorkerJob::~ServiceWorkerJob()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
// Jobs must finish or never be started. Destroying an actively running
|
||||
// job is an error.
|
||||
MOZ_ASSERT(mState != State::Started);
|
||||
MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mState == State::Started);
|
||||
|
||||
MOZ_ASSERT(!mResultCallbacksInvoked);
|
||||
mResultCallbacksInvoked = true;
|
||||
|
||||
nsTArray<RefPtr<Callback>> callbackList;
|
||||
callbackList.SwapElements(mResultCallbackList);
|
||||
|
||||
for (RefPtr<Callback>& callback : callbackList) {
|
||||
// The callback might consume an exception on the ErrorResult, so we need
|
||||
// to clone in order to maintain the error for the next callback.
|
||||
ErrorResult rv;
|
||||
aRv.CloneTo(rv);
|
||||
|
||||
callback->JobFinished(this, rv);
|
||||
|
||||
// The callback might not consume the error.
|
||||
rv.SuppressException();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
|
||||
{
|
||||
ErrorResult converted(aRv);
|
||||
InvokeResultCallbacks(converted);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Finish(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mState == State::Started);
|
||||
|
||||
// Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
|
||||
if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
|
||||
!aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
|
||||
!aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
|
||||
|
||||
// Remove the old error code so we can replace it with a TypeError.
|
||||
aRv.SuppressException();
|
||||
|
||||
NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
|
||||
NS_ConvertUTF8toUTF16 scope(mScope);
|
||||
|
||||
// Throw the type error with a generic error message.
|
||||
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
|
||||
}
|
||||
|
||||
// The final callback may drop the last ref to this object.
|
||||
RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
|
||||
|
||||
if (!mResultCallbacksInvoked) {
|
||||
InvokeResultCallbacks(aRv);
|
||||
}
|
||||
|
||||
mState = State::Finished;
|
||||
|
||||
mFinalCallback->JobFinished(this, aRv);
|
||||
mFinalCallback = nullptr;
|
||||
|
||||
// The callback might not consume the error.
|
||||
aRv.SuppressException();
|
||||
|
||||
// Async release this object to ensure that our caller methods complete
|
||||
// as well.
|
||||
NS_ReleaseOnMainThread(kungFuDeathGrip.forget(), true /* always proxy */);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Finish(nsresult aRv)
|
||||
{
|
||||
ErrorResult converted(aRv);
|
||||
Finish(converted);
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,155 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerjob_h
|
||||
#define mozilla_dom_workers_serviceworkerjob_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
// Implement this interface to receive notification when a job completes.
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
// Called once when the job completes. If the job is started, then this
|
||||
// will be called. If a job is never executed due to browser shutdown,
|
||||
// then this method will never be called. This method is always called
|
||||
// on the main thread asynchronously after Start() completes.
|
||||
virtual void JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
AddRef(void) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
Release(void) = 0;
|
||||
};
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Register,
|
||||
Update,
|
||||
Unregister
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
Initial,
|
||||
Started,
|
||||
Finished
|
||||
};
|
||||
|
||||
Type
|
||||
GetType() const;
|
||||
|
||||
State
|
||||
GetState() const;
|
||||
|
||||
// Determine if the job has been canceled. This does not change the
|
||||
// current State, but indicates that the job should progress to Finished
|
||||
// as soon as possible.
|
||||
bool
|
||||
Canceled() const;
|
||||
|
||||
// Determine if the result callbacks have already been called. This is
|
||||
// equivalent to the spec checked to see if the job promise has settled.
|
||||
bool
|
||||
ResultCallbacksInvoked() const;
|
||||
|
||||
bool
|
||||
IsEquivalentTo(ServiceWorkerJob* aJob) const;
|
||||
|
||||
// Add a callback that will be invoked when the job's result is available.
|
||||
// Some job types will invoke this before the job is actually finished.
|
||||
// If an early callback does not occur, then it will be called automatically
|
||||
// when Finish() is called. These callbacks will be invoked while the job
|
||||
// state is Started.
|
||||
void
|
||||
AppendResultCallback(Callback* aCallback);
|
||||
|
||||
// This takes ownership of any result callbacks associated with the given job
|
||||
// and then appends them to this job's callback list.
|
||||
void
|
||||
StealResultCallbacksFrom(ServiceWorkerJob* aJob);
|
||||
|
||||
// Start the job. All work will be performed asynchronously on
|
||||
// the main thread. The Finish() method must be called exactly
|
||||
// once after this point. A final callback must be provided. It
|
||||
// will be invoked after all other callbacks have been processed.
|
||||
void
|
||||
Start(Callback* aFinalCallback);
|
||||
|
||||
// Set an internal flag indicating that a started job should finish as
|
||||
// soon as possible.
|
||||
void
|
||||
Cancel();
|
||||
|
||||
protected:
|
||||
ServiceWorkerJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec);
|
||||
|
||||
virtual ~ServiceWorkerJob();
|
||||
|
||||
// Invoke the result callbacks immediately. The job must be in the
|
||||
// Started state. The callbacks are cleared after being invoked,
|
||||
// so subsequent method calls have no effect.
|
||||
void
|
||||
InvokeResultCallbacks(ErrorResult& aRv);
|
||||
|
||||
// Convenience method that converts to ErrorResult and calls real method.
|
||||
void
|
||||
InvokeResultCallbacks(nsresult aRv);
|
||||
|
||||
// Indicate that the job has completed. The must be called exactly
|
||||
// once after Start() has initiated job execution. It may not be
|
||||
// called until Start() has returned.
|
||||
void
|
||||
Finish(ErrorResult& aRv);
|
||||
|
||||
// Convenience method that converts to ErrorResult and calls real method.
|
||||
void
|
||||
Finish(nsresult aRv);
|
||||
|
||||
// Specific job types should define AsyncExecute to begin their work.
|
||||
// All errors and successes must result in Finish() being called.
|
||||
virtual void
|
||||
AsyncExecute() = 0;
|
||||
|
||||
const Type mType;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
const nsCString mScriptSpec;
|
||||
|
||||
private:
|
||||
RefPtr<Callback> mFinalCallback;
|
||||
nsTArray<RefPtr<Callback>> mResultCallbackList;
|
||||
State mState;
|
||||
bool mCanceled;
|
||||
bool mResultCallbacksInvoked;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJob)
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerjob_h
|
||||
@@ -0,0 +1,134 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerJobQueue.h"
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJobQueue::Callback final : public ServiceWorkerJob::Callback
|
||||
{
|
||||
RefPtr<ServiceWorkerJobQueue> mQueue;
|
||||
|
||||
~Callback()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Callback(ServiceWorkerJobQueue* aQueue)
|
||||
: mQueue(aQueue)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mQueue);
|
||||
}
|
||||
|
||||
virtual void
|
||||
JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mQueue->JobFinished(aJob);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue::Callback, override)
|
||||
};
|
||||
|
||||
ServiceWorkerJobQueue::~ServiceWorkerJobQueue()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mJobList.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::JobFinished(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
|
||||
// XXX There are some corner cases where jobs can double-complete. Until
|
||||
// we track all these down we do a non-fatal assert in debug builds and
|
||||
// a runtime check to verify the queue is in the correct state.
|
||||
NS_ASSERTION(!mJobList.IsEmpty(),
|
||||
"Job queue should contain the job that just completed.");
|
||||
NS_ASSERTION(mJobList.SafeElementAt(0, nullptr) == aJob,
|
||||
"Job queue should contain the job that just completed.");
|
||||
if (NS_WARN_IF(mJobList.SafeElementAt(0, nullptr) != aJob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mJobList.RemoveElementAt(0);
|
||||
|
||||
if (mJobList.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RunJob();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::RunJob()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mJobList.IsEmpty());
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Initial);
|
||||
|
||||
RefPtr<Callback> callback = new Callback(this);
|
||||
mJobList[0]->Start(callback);
|
||||
}
|
||||
|
||||
ServiceWorkerJobQueue::ServiceWorkerJobQueue()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::ScheduleJob(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
MOZ_ASSERT(!mJobList.Contains(aJob));
|
||||
|
||||
if (mJobList.IsEmpty()) {
|
||||
mJobList.AppendElement(aJob);
|
||||
RunJob();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started);
|
||||
|
||||
RefPtr<ServiceWorkerJob>& tailJob = mJobList[mJobList.Length() - 1];
|
||||
if (!tailJob->ResultCallbacksInvoked() && aJob->IsEquivalentTo(tailJob)) {
|
||||
tailJob->StealResultCallbacksFrom(aJob);
|
||||
return;
|
||||
}
|
||||
|
||||
mJobList.AppendElement(aJob);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::CancelAll()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
for (RefPtr<ServiceWorkerJob>& job : mJobList) {
|
||||
job->Cancel();
|
||||
}
|
||||
|
||||
// Remove jobs that are queued but not started since they should never
|
||||
// run after being canceled. This means throwing away all jobs except
|
||||
// for the job at the front of the list.
|
||||
if (!mJobList.IsEmpty()) {
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started);
|
||||
mJobList.TruncateLength(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerjobqueue_h
|
||||
#define mozilla_dom_workers_serviceworkerjobqueue_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJob;
|
||||
|
||||
class ServiceWorkerJobQueue final
|
||||
{
|
||||
class Callback;
|
||||
|
||||
nsTArray<RefPtr<ServiceWorkerJob>> mJobList;
|
||||
|
||||
~ServiceWorkerJobQueue();
|
||||
|
||||
void
|
||||
JobFinished(ServiceWorkerJob* aJob);
|
||||
|
||||
void
|
||||
RunJob();
|
||||
|
||||
public:
|
||||
ServiceWorkerJobQueue();
|
||||
|
||||
void
|
||||
ScheduleJob(ServiceWorkerJob* aJob);
|
||||
|
||||
void
|
||||
CancelAll();
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue)
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerjobqueue_h
|
||||
+269
-1757
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,10 @@
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
|
||||
#include "mozilla/dom/ServiceWorkerCommon.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
|
||||
#include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
@@ -44,132 +44,12 @@ class ServiceWorkerRegistrationListener;
|
||||
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
class ServiceWorkerClientInfo;
|
||||
class ServiceWorkerInfo;
|
||||
class ServiceWorkerJob;
|
||||
class ServiceWorkerJobQueue;
|
||||
class ServiceWorkerManagerChild;
|
||||
class ServiceWorkerPrivate;
|
||||
|
||||
class ServiceWorkerRegistrationInfo final
|
||||
: public nsIServiceWorkerRegistrationInfo
|
||||
{
|
||||
uint32_t mControlledDocumentsCounter;
|
||||
|
||||
enum
|
||||
{
|
||||
NoUpdate,
|
||||
NeedTimeCheckAndUpdate,
|
||||
NeedUpdate
|
||||
} mUpdateState;
|
||||
|
||||
uint64_t mLastUpdateCheckTime;
|
||||
|
||||
virtual ~ServiceWorkerRegistrationInfo();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
|
||||
|
||||
nsCString mScope;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
RefPtr<ServiceWorkerInfo> mActiveWorker;
|
||||
RefPtr<ServiceWorkerInfo> mWaitingWorker;
|
||||
RefPtr<ServiceWorkerInfo> mInstallingWorker;
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
|
||||
|
||||
// According to the spec, Soft Update shouldn't queue an update job
|
||||
// if the registration queue is not empty. Because our job queue
|
||||
// works slightly different, we use a flag to determine if the registration
|
||||
// is already updating.
|
||||
bool mUpdating;
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
// removed since documents may be controlled. It is marked as
|
||||
// pendingUninstall and when all controlling documents go away, removed.
|
||||
bool mPendingUninstall;
|
||||
|
||||
ServiceWorkerRegistrationInfo(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
Newest() const
|
||||
{
|
||||
RefPtr<ServiceWorkerInfo> newest;
|
||||
if (mInstallingWorker) {
|
||||
newest = mInstallingWorker;
|
||||
} else if (mWaitingWorker) {
|
||||
newest = mWaitingWorker;
|
||||
} else {
|
||||
newest = mActiveWorker;
|
||||
}
|
||||
|
||||
return newest.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
GetServiceWorkerInfoById(uint64_t aId);
|
||||
|
||||
void
|
||||
StartControllingADocument()
|
||||
{
|
||||
++mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
StopControllingADocument()
|
||||
{
|
||||
MOZ_ASSERT(mControlledDocumentsCounter);
|
||||
--mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
bool
|
||||
IsControllingDocuments() const
|
||||
{
|
||||
return mActiveWorker && mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
Clear();
|
||||
|
||||
void
|
||||
PurgeActiveWorker();
|
||||
|
||||
void
|
||||
TryToActivateAsync();
|
||||
|
||||
void
|
||||
TryToActivate();
|
||||
|
||||
void
|
||||
Activate();
|
||||
|
||||
void
|
||||
FinishActivate(bool aSuccess);
|
||||
|
||||
void
|
||||
RefreshLastUpdateCheckTime();
|
||||
|
||||
bool
|
||||
IsLastUpdateCheckTimeOverOneDay() const;
|
||||
|
||||
void
|
||||
NotifyListenersOnChange();
|
||||
|
||||
void
|
||||
MaybeScheduleTimeCheckAndUpdate();
|
||||
|
||||
void
|
||||
MaybeScheduleUpdate();
|
||||
|
||||
bool
|
||||
CheckAndClearIfUpdateNeeded();
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateFinishCallback
|
||||
{
|
||||
protected:
|
||||
@@ -186,126 +66,6 @@ public:
|
||||
void UpdateFailed(ErrorResult& aStatus) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo final : public nsIServiceWorkerInfo
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
const nsCString mScriptSpec;
|
||||
const nsString mCacheName;
|
||||
ServiceWorkerState mState;
|
||||
|
||||
// This id is shared with WorkerPrivate to match requests issued by service
|
||||
// workers to their corresponding serviceWorkerInfo.
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
// We hold rawptrs since the ServiceWorker constructor and destructor ensure
|
||||
// addition and removal.
|
||||
// There is a high chance of there being at least one ServiceWorker
|
||||
// associated with this all the time.
|
||||
AutoTArray<ServiceWorker*, 1> mInstances;
|
||||
|
||||
RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
|
||||
bool mSkipWaitingFlag;
|
||||
|
||||
~ServiceWorkerInfo();
|
||||
|
||||
// Generates a unique id for the service worker, with zero being treated as
|
||||
// invalid.
|
||||
uint64_t
|
||||
GetNextID() const;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERINFO
|
||||
|
||||
class ServiceWorkerPrivate*
|
||||
WorkerPrivate() const
|
||||
{
|
||||
MOZ_ASSERT(mServiceWorkerPrivate);
|
||||
return mServiceWorkerPrivate;
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetPrincipal() const
|
||||
{
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ScriptSpec() const
|
||||
{
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
Scope() const
|
||||
{
|
||||
return mScope;
|
||||
}
|
||||
|
||||
bool SkipWaitingFlag() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mSkipWaitingFlag;
|
||||
}
|
||||
|
||||
void SetSkipWaitingFlag()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mSkipWaitingFlag = true;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName);
|
||||
|
||||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
const nsString&
|
||||
CacheName() const
|
||||
{
|
||||
return mCacheName;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ID() const
|
||||
{
|
||||
return mServiceWorkerID;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateState(ServiceWorkerState aState);
|
||||
|
||||
// Only used to set initial state when loading from disk!
|
||||
void
|
||||
SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
void
|
||||
AppendWorker(ServiceWorker* aWorker);
|
||||
|
||||
void
|
||||
RemoveWorker(ServiceWorker* aWorker);
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
GetOrCreateInstance(nsPIDOMWindow* aWindow);
|
||||
};
|
||||
|
||||
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
|
||||
{ /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
|
||||
0xf4f8755a, \
|
||||
@@ -327,13 +87,10 @@ class ServiceWorkerManager final
|
||||
friend class GetReadyPromiseRunnable;
|
||||
friend class GetRegistrationsRunnable;
|
||||
friend class GetRegistrationRunnable;
|
||||
friend class ServiceWorkerJobQueue;
|
||||
friend class ServiceWorkerInstallJob;
|
||||
friend class ServiceWorkerRegisterJob;
|
||||
friend class ServiceWorkerJobBase;
|
||||
friend class ServiceWorkerScriptJobBase;
|
||||
friend class ServiceWorkerJob;
|
||||
friend class ServiceWorkerRegistrationInfo;
|
||||
friend class ServiceWorkerUnregisterJob;
|
||||
friend class ServiceWorkerUpdateJob;
|
||||
friend class UpdateTimerCallback;
|
||||
|
||||
public:
|
||||
@@ -349,9 +106,6 @@ public:
|
||||
|
||||
nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
|
||||
|
||||
// Set of all documents that may be controlled by a service worker.
|
||||
nsTHashtable<nsISupportsHashKey> mAllDocuments;
|
||||
|
||||
// Track all documents that have attempted to register a service worker for a
|
||||
// given scope.
|
||||
typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
|
||||
@@ -512,7 +266,7 @@ private:
|
||||
void
|
||||
Init();
|
||||
|
||||
ServiceWorkerJobQueue*
|
||||
already_AddRefed<ServiceWorkerJobQueue>
|
||||
GetOrCreateJobQueue(const nsACString& aOriginSuffix,
|
||||
const nsACString& aScope);
|
||||
|
||||
@@ -626,8 +380,6 @@ private:
|
||||
};
|
||||
|
||||
void AppendPendingOperation(nsIRunnable* aRunnable);
|
||||
void AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
|
||||
ServiceWorkerJob* aJob);
|
||||
|
||||
bool HasBackgroundActor() const
|
||||
{
|
||||
@@ -645,8 +397,7 @@ private:
|
||||
|
||||
RefPtr<ServiceWorkerManagerChild> mActor;
|
||||
|
||||
struct PendingOperation;
|
||||
nsTArray<PendingOperation> mPendingOperations;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mPendingOperations;
|
||||
|
||||
bool mShuttingDown;
|
||||
|
||||
@@ -676,6 +427,9 @@ private:
|
||||
|
||||
void
|
||||
UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
||||
|
||||
void
|
||||
MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerRegisterJob.h"
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerUpdateJob(Type::Register, aPrincipal, aScope, aScriptSpec,
|
||||
aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegisterJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (Canceled()) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
|
||||
if (registration) {
|
||||
// If we are resurrecting an uninstalling registration, then persist
|
||||
// it to disk again. We preemptively removed it earlier during
|
||||
// unregister so that closing the window by shutting down the browser
|
||||
// results in the registration being gone on restart.
|
||||
if (registration->mPendingUninstall) {
|
||||
swm->StoreRegistration(mPrincipal, registration);
|
||||
}
|
||||
registration->mPendingUninstall = false;
|
||||
RefPtr<ServiceWorkerInfo> newest = registration->Newest();
|
||||
if (newest && mScriptSpec.Equals(newest->ScriptSpec())) {
|
||||
SetRegistration(registration);
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
registration = swm->CreateNewRegistration(mScope, mPrincipal);
|
||||
}
|
||||
|
||||
SetRegistration(registration);
|
||||
Update();
|
||||
}
|
||||
|
||||
ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerregisterjob_h
|
||||
#define mozilla_dom_workers_serviceworkerregisterjob_h
|
||||
|
||||
#include "ServiceWorkerUpdateJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
// The register job. This implements the steps in the spec Register algorithm,
|
||||
// but then uses ServiceWorkerUpdateJob to implement the Update and Install
|
||||
// spec algorithms.
|
||||
class ServiceWorkerRegisterJob final : public ServiceWorkerUpdateJob
|
||||
{
|
||||
public:
|
||||
ServiceWorkerRegisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
private:
|
||||
// Implement the Register algorithm steps and then call the parent class
|
||||
// Update() to complete the job execution.
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
virtual ~ServiceWorkerRegisterJob();
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerregisterjob_h
|
||||
@@ -0,0 +1,450 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerRegistrationInfo.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::Clear()
|
||||
{
|
||||
if (mInstallingWorker) {
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mInstallingWorker = nullptr;
|
||||
// FIXME(nsm): Abort any inflight requests from installing worker.
|
||||
}
|
||||
|
||||
if (mWaitingWorker) {
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mWaitingWorker = nullptr;
|
||||
}
|
||||
|
||||
if (mActiveWorker) {
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mActiveWorker = nullptr;
|
||||
}
|
||||
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal)
|
||||
: mControlledDocumentsCounter(0)
|
||||
, mUpdateState(NoUpdate)
|
||||
, mLastUpdateCheckTime(0)
|
||||
, mScope(aScope)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mPendingUninstall(false)
|
||||
{}
|
||||
|
||||
ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
|
||||
{
|
||||
if (IsControllingDocuments()) {
|
||||
NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
NS_ADDREF(*aPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
CopyUTF8toUTF16(mScope, aScope);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<ServiceWorkerInfo> newest = Newest();
|
||||
if (newest) {
|
||||
CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mInstallingWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mWaitingWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mActiveWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
RefPtr<ServiceWorkerInfo> info = GetServiceWorkerInfoById(aID);
|
||||
// It is ok to return null for a missing service worker info.
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::AddListener(
|
||||
nsIServiceWorkerRegistrationInfoListener *aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!aListener || mListeners.Contains(aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mListeners.AppendElement(aListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::RemoveListener(
|
||||
nsIServiceWorkerRegistrationInfoListener *aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!aListener || !mListeners.Contains(aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mListeners.RemoveElement(aListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId)
|
||||
{
|
||||
RefPtr<ServiceWorkerInfo> serviceWorker;
|
||||
if (mInstallingWorker && mInstallingWorker->ID() == aId) {
|
||||
serviceWorker = mInstallingWorker;
|
||||
} else if (mWaitingWorker && mWaitingWorker->ID() == aId) {
|
||||
serviceWorker = mWaitingWorker;
|
||||
} else if (mActiveWorker && mActiveWorker->ID() == aId) {
|
||||
serviceWorker = mActiveWorker;
|
||||
}
|
||||
|
||||
return serviceWorker.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TryToActivateAsync()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this,
|
||||
&ServiceWorkerRegistrationInfo::TryToActivate);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
|
||||
}
|
||||
|
||||
/*
|
||||
* TryToActivate should not be called directly, use TryToACtivateAsync instead.
|
||||
*/
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TryToActivate()
|
||||
{
|
||||
if (!IsControllingDocuments() ||
|
||||
// Waiting worker will be removed if the registration is removed
|
||||
(mWaitingWorker && mWaitingWorker->SkipWaitingFlag())) {
|
||||
Activate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::Activate()
|
||||
{
|
||||
if (!mWaitingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionWaitingToActive();
|
||||
|
||||
// FIXME(nsm): Unlink appcache if there is one.
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->CheckPendingReadyPromises();
|
||||
|
||||
// "Queue a task to fire a simple event named controllerchange..."
|
||||
nsCOMPtr<nsIRunnable> controllerChangeRunnable =
|
||||
NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
|
||||
swm, &ServiceWorkerManager::FireControllerChange, this);
|
||||
NS_DispatchToMainThread(controllerChangeRunnable);
|
||||
|
||||
nsCOMPtr<nsIRunnable> failRunnable =
|
||||
NS_NewRunnableMethodWithArg<bool>(this,
|
||||
&ServiceWorkerRegistrationInfo::FinishActivate,
|
||||
false /* success */);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueActivateRunnable(handle);
|
||||
|
||||
ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"),
|
||||
callback, failRunnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
|
||||
{
|
||||
if (mPendingUninstall || !mActiveWorker ||
|
||||
mActiveWorker->State() != ServiceWorkerState::Activating) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Activation never fails, so aSuccess is ignored.
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Activated);
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->StoreRegistration(mPrincipal, this);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mLastUpdateCheckTime = PR_IntervalNow() / PR_MSEC_PER_SEC;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// For testing.
|
||||
if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t kSecondsPerDay = 86400;
|
||||
const uint64_t now = PR_IntervalNow() / PR_MSEC_PER_SEC;
|
||||
|
||||
if ((mLastUpdateCheckTime != 0) &&
|
||||
(now - mLastUpdateCheckTime > kSecondsPerDay)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::NotifyListenersOnChange(WhichServiceWorker aChangedWorkers)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aChangedWorkers & (WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER));
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(this, aChangedWorkers);
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
|
||||
for (size_t index = 0; index < listeners.Length(); ++index) {
|
||||
listeners[index]->OnChange();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// shutting down, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUpdateState == NoUpdate) {
|
||||
mUpdateState = NeedTimeCheckAndUpdate;
|
||||
}
|
||||
|
||||
swm->ScheduleUpdateTimer(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::MaybeScheduleUpdate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// shutting down, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
mUpdateState = NeedUpdate;
|
||||
|
||||
swm->ScheduleUpdateTimer(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
bool result = mUpdateState == NeedUpdate ||
|
||||
(mUpdateState == NeedTimeCheckAndUpdate &&
|
||||
IsLastUpdateCheckTimeOverOneDay());
|
||||
|
||||
mUpdateState = NoUpdate;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetInstalling() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mInstallingWorker;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetWaiting() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mWaitingWorker;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetActive() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mActiveWorker;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::ClearInstalling()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mInstallingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mInstallingWorker = nullptr;
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::SetInstalling(ServiceWorkerInfo* aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aServiceWorker);
|
||||
MOZ_ASSERT(!mInstallingWorker);
|
||||
MOZ_ASSERT(mWaitingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mActiveWorker != aServiceWorker);
|
||||
|
||||
mInstallingWorker = aServiceWorker;
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mInstallingWorker);
|
||||
|
||||
if (mWaitingWorker) {
|
||||
MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName());
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
mWaitingWorker = mInstallingWorker.forget();
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->StoreRegistration(mPrincipal, this);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::SetActive(ServiceWorkerInfo* aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aServiceWorker);
|
||||
|
||||
// TODO: Assert installing, waiting, and active are nullptr once the SWM
|
||||
// moves to the parent process. After that happens this code will
|
||||
// only run for browser initialization and not for cross-process
|
||||
// overrides.
|
||||
MOZ_ASSERT(mInstallingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mWaitingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mActiveWorker != aServiceWorker);
|
||||
|
||||
if (mActiveWorker) {
|
||||
MOZ_ASSERT(aServiceWorker->CacheName() != mActiveWorker->CacheName());
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
// The active worker is being overriden due to initial load or
|
||||
// another process activating a worker. Move straight to the
|
||||
// Activated state.
|
||||
mActiveWorker = aServiceWorker;
|
||||
mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
|
||||
NotifyListenersOnChange(WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mWaitingWorker);
|
||||
|
||||
if (mActiveWorker) {
|
||||
MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName());
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
// We are transitioning from waiting to active normally, so go to
|
||||
// the activating state.
|
||||
mActiveWorker = mWaitingWorker.forget();
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Activating);
|
||||
NotifyListenersOnChange(WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
@@ -0,0 +1,167 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerregistrationinfo_h
|
||||
#define mozilla_dom_workers_serviceworkerregistrationinfo_h
|
||||
|
||||
#include "mozilla/dom/workers/ServiceWorkerInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerRegistrationInfo final
|
||||
: public nsIServiceWorkerRegistrationInfo
|
||||
{
|
||||
uint32_t mControlledDocumentsCounter;
|
||||
|
||||
enum
|
||||
{
|
||||
NoUpdate,
|
||||
NeedTimeCheckAndUpdate,
|
||||
NeedUpdate
|
||||
} mUpdateState;
|
||||
|
||||
uint64_t mLastUpdateCheckTime;
|
||||
|
||||
RefPtr<ServiceWorkerInfo> mActiveWorker;
|
||||
RefPtr<ServiceWorkerInfo> mWaitingWorker;
|
||||
RefPtr<ServiceWorkerInfo> mInstallingWorker;
|
||||
|
||||
virtual ~ServiceWorkerRegistrationInfo();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
|
||||
|
||||
const nsCString mScope;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
// removed since documents may be controlled. It is marked as
|
||||
// pendingUninstall and when all controlling documents go away, removed.
|
||||
bool mPendingUninstall;
|
||||
|
||||
ServiceWorkerRegistrationInfo(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
Newest() const
|
||||
{
|
||||
RefPtr<ServiceWorkerInfo> newest;
|
||||
if (mInstallingWorker) {
|
||||
newest = mInstallingWorker;
|
||||
} else if (mWaitingWorker) {
|
||||
newest = mWaitingWorker;
|
||||
} else {
|
||||
newest = mActiveWorker;
|
||||
}
|
||||
|
||||
return newest.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
GetServiceWorkerInfoById(uint64_t aId);
|
||||
|
||||
void
|
||||
StartControllingADocument()
|
||||
{
|
||||
++mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
StopControllingADocument()
|
||||
{
|
||||
MOZ_ASSERT(mControlledDocumentsCounter);
|
||||
--mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
bool
|
||||
IsControllingDocuments() const
|
||||
{
|
||||
return mActiveWorker && mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
Clear();
|
||||
|
||||
void
|
||||
TryToActivateAsync();
|
||||
|
||||
void
|
||||
TryToActivate();
|
||||
|
||||
void
|
||||
Activate();
|
||||
|
||||
void
|
||||
FinishActivate(bool aSuccess);
|
||||
|
||||
void
|
||||
RefreshLastUpdateCheckTime();
|
||||
|
||||
bool
|
||||
IsLastUpdateCheckTimeOverOneDay() const;
|
||||
|
||||
void
|
||||
NotifyListenersOnChange(WhichServiceWorker aChangedWorkers);
|
||||
|
||||
void
|
||||
MaybeScheduleTimeCheckAndUpdate();
|
||||
|
||||
void
|
||||
MaybeScheduleUpdate();
|
||||
|
||||
bool
|
||||
CheckAndClearIfUpdateNeeded();
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetInstalling() const;
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetWaiting() const;
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetActive() const;
|
||||
|
||||
// Remove an existing installing worker, if present. The worker will
|
||||
// be transitioned to the Redundant state.
|
||||
void
|
||||
ClearInstalling();
|
||||
|
||||
// Set a new installing worker. This may only be called if there is no
|
||||
// existing installing worker. The worker is transitioned to the Installing
|
||||
// state.
|
||||
void
|
||||
SetInstalling(ServiceWorkerInfo* aServiceWorker);
|
||||
|
||||
// Transition the current installing worker to be the waiting worker. The
|
||||
// workers state is updated to Installed.
|
||||
void
|
||||
TransitionInstallingToWaiting();
|
||||
|
||||
// Override the current active worker. This is used during browser
|
||||
// initialization to load persisted workers. Its also used to propagate
|
||||
// active workers across child processes in e10s. This second use will
|
||||
// go away once the ServiceWorkerManager moves to the parent process.
|
||||
// The worker is transitioned to the Activated state.
|
||||
void
|
||||
SetActive(ServiceWorkerInfo* aServiceWorker);
|
||||
|
||||
// Transition the current waiting worker to be the new active worker. The
|
||||
// worker is updated to the Activating state.
|
||||
void
|
||||
TransitionWaitingToActive();
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerregistrationinfo_h
|
||||
@@ -0,0 +1,89 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerUnregisterJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
|
||||
ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
bool aSendToParent)
|
||||
: ServiceWorkerJob(Type::Unregister, aPrincipal, aScope, EmptyCString())
|
||||
, mResult(false)
|
||||
, mSendToParent(aSendToParent)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerUnregisterJob::GetResult() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mResult;
|
||||
}
|
||||
|
||||
ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUnregisterJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (Canceled()) {
|
||||
Finish(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 of the Unregister algorithm requires checking that the
|
||||
// client origin matches the scope's origin. We perform this in
|
||||
// registration->update() method directly since we don't have that
|
||||
// client information available here.
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
|
||||
// "Let registration be the result of running [[Get Registration]]
|
||||
// algorithm passing scope as the argument."
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
if (!registration) {
|
||||
// "If registration is null, then, resolve promise with false."
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note, we send the message to remove the registration from disk now even
|
||||
// though we may only set the mPendingUninstall flag below. This is
|
||||
// necessary to ensure the registration is removed if the controlled
|
||||
// clients are closed by shutting down the browser. If the registration
|
||||
// is resurrected by clearing mPendingUninstall then it should be saved
|
||||
// to disk again.
|
||||
if (mSendToParent && !registration->mPendingUninstall) {
|
||||
swm->MaybeSendUnregister(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
// "Set registration's uninstalling flag."
|
||||
registration->mPendingUninstall = true;
|
||||
|
||||
// "Resolve promise with true"
|
||||
mResult = true;
|
||||
InvokeResultCallbacks(NS_OK);
|
||||
|
||||
// "If no service worker client is using registration..."
|
||||
if (!registration->IsControllingDocuments()) {
|
||||
// "Invoke [[Clear Registration]]..."
|
||||
swm->RemoveRegistration(registration);
|
||||
}
|
||||
|
||||
Finish(NS_OK);
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerunregisterjob_h
|
||||
#define mozilla_dom_workers_serviceworkerunregisterjob_h
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
bool aSendToParent);
|
||||
|
||||
bool
|
||||
GetResult() const;
|
||||
|
||||
private:
|
||||
virtual ~ServiceWorkerUnregisterJob();
|
||||
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
bool mResult;
|
||||
bool mSendToParent;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerunregisterjob_h
|
||||
@@ -0,0 +1,485 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ServiceWorkerUpdateJob.h"
|
||||
|
||||
#include "ServiceWorkerScriptCache.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerUpdateJob::CompareCallback final : public serviceWorkerScriptCache::CompareCallback
|
||||
{
|
||||
RefPtr<ServiceWorkerUpdateJob> mJob;
|
||||
|
||||
~CompareCallback()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CompareCallback(ServiceWorkerUpdateJob* aJob)
|
||||
: mJob(aJob)
|
||||
{
|
||||
MOZ_ASSERT(mJob);
|
||||
}
|
||||
|
||||
virtual void
|
||||
ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope) override
|
||||
{
|
||||
mJob->ComparisonResult(aStatus, aInCacheAndEqual, aNewCacheName, aMaxScope);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateJob::CompareCallback, override)
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateJob::ContinueUpdateRunnable final : public LifeCycleEventCallback
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> mJob;
|
||||
bool mSuccess;
|
||||
|
||||
public:
|
||||
explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<ServiceWorkerUpdateJob>& aJob)
|
||||
: mJob(aJob)
|
||||
, mSuccess(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
SetResult(bool aResult) override
|
||||
{
|
||||
mSuccess = aResult;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mJob->ContinueUpdateAfterScriptEval(mSuccess);
|
||||
mJob = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateJob::ContinueInstallRunnable final : public LifeCycleEventCallback
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> mJob;
|
||||
bool mSuccess;
|
||||
|
||||
public:
|
||||
explicit ContinueInstallRunnable(const nsMainThreadPtrHandle<ServiceWorkerUpdateJob>& aJob)
|
||||
: mJob(aJob)
|
||||
, mSuccess(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
SetResult(bool aResult) override
|
||||
{
|
||||
mSuccess = aResult;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mJob->ContinueAfterInstallEvent(mSuccess);
|
||||
mJob = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerJob(Type::Update, aPrincipal, aScope, aScriptSpec)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
ServiceWorkerUpdateJob::GetRegistration() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<ServiceWorkerRegistrationInfo> ref = mRegistration;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerJob(aType, aPrincipal, aScope, aScriptSpec)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerUpdateJob::~ServiceWorkerUpdateJob()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::FailUpdateJob(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aRv.Failed());
|
||||
|
||||
// Cleanup after a failed installation. This essentially implements
|
||||
// step 12 of the Install algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
|
||||
//
|
||||
// The spec currently only runs this after an install event fails,
|
||||
// but we must handle many more internal errors. So we check for
|
||||
// cleanup on every non-successful exit.
|
||||
if (mRegistration) {
|
||||
if (mServiceWorker) {
|
||||
mServiceWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
|
||||
mServiceWorker->CacheName());
|
||||
}
|
||||
|
||||
mRegistration->ClearInstalling();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->MaybeRemoveRegistration(mRegistration);
|
||||
}
|
||||
}
|
||||
|
||||
mServiceWorker = nullptr;
|
||||
mRegistration = nullptr;
|
||||
|
||||
Finish(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::FailUpdateJob(nsresult aRv)
|
||||
{
|
||||
ErrorResult rv(aRv);
|
||||
FailUpdateJob(rv);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(GetType() == Type::Update);
|
||||
|
||||
if (Canceled()) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin step 1 of the Update algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#update-algorithm
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
|
||||
if (!registration || registration->mPendingUninstall) {
|
||||
ErrorResult rv;
|
||||
rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope),
|
||||
NS_LITERAL_STRING("uninstalled"));
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a Register job with a new script executed ahead of us in the job queue,
|
||||
// then our update for the old script no longer makes sense. Simply abort
|
||||
// in this case.
|
||||
RefPtr<ServiceWorkerInfo> newest = registration->Newest();
|
||||
if (newest && !mScriptSpec.Equals(newest->ScriptSpec())) {
|
||||
ErrorResult rv;
|
||||
rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope),
|
||||
NS_LITERAL_STRING("changed"));
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
SetRegistration(registration);
|
||||
Update();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::SetRegistration(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(!mRegistration);
|
||||
MOZ_ASSERT(aRegistration);
|
||||
mRegistration = aRegistration;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::Update()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!Canceled());
|
||||
|
||||
// SetRegistration() must be called before Update().
|
||||
MOZ_ASSERT(mRegistration);
|
||||
MOZ_ASSERT(!mRegistration->GetInstalling());
|
||||
|
||||
// Begin the script download and comparison steps starting at step 5
|
||||
// of the Update algorithm.
|
||||
|
||||
RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
|
||||
nsAutoString cacheName;
|
||||
|
||||
// If the script has not changed, we need to perform a byte-for-byte
|
||||
// comparison.
|
||||
if (workerInfo && workerInfo->ScriptSpec().Equals(mScriptSpec)) {
|
||||
cacheName = workerInfo->CacheName();
|
||||
}
|
||||
|
||||
RefPtr<CompareCallback> callback = new CompareCallback(this);
|
||||
|
||||
nsresult rv =
|
||||
serviceWorkerScriptCache::Compare(mRegistration, mPrincipal, cacheName,
|
||||
NS_ConvertUTF8toUTF16(mScriptSpec),
|
||||
callback, mLoadGroup);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (NS_WARN_IF(Canceled())) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle failure of the download or comparison. This is part of Update
|
||||
// step 5 as "If the algorithm asynchronously completes with null, then:".
|
||||
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
||||
FailUpdateJob(aStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// The spec validates the response before performing the byte-for-byte check.
|
||||
// Here we perform the comparison in another module and then validate the
|
||||
// script URL and scope. Make sure to do this validation before accepting
|
||||
// an byte-for-byte match since the service-worker-allowed header might have
|
||||
// changed since the last time it was installed.
|
||||
|
||||
// This is step 2 the "validate response" section of Update algorithm step 5.
|
||||
// Step 1 is performed in the serviceWorkerScriptCache code.
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), mScriptSpec);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> maxScopeURI;
|
||||
if (!aMaxScope.IsEmpty()) {
|
||||
rv = NS_NewURI(getter_AddRefs(maxScopeURI), aMaxScope,
|
||||
nullptr, scriptURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString defaultAllowedPrefix;
|
||||
rv = GetRequiredScopeStringPrefix(scriptURI, defaultAllowedPrefix,
|
||||
eUseDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString maxPrefix(defaultAllowedPrefix);
|
||||
if (maxScopeURI) {
|
||||
rv = GetRequiredScopeStringPrefix(maxScopeURI, maxPrefix, eUsePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringBeginsWith(mRegistration->mScope, maxPrefix)) {
|
||||
nsXPIDLString message;
|
||||
NS_ConvertUTF8toUTF16 reportScope(mRegistration->mScope);
|
||||
NS_ConvertUTF8toUTF16 reportMaxPrefix(maxPrefix);
|
||||
const char16_t* params[] = { reportScope.get(), reportMaxPrefix.get() };
|
||||
|
||||
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
"ServiceWorkerScopePathMismatch",
|
||||
params, message);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to format localized string");
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->ReportToAllClients(mScope,
|
||||
message,
|
||||
EmptyString(),
|
||||
EmptyString(), 0, 0,
|
||||
nsIScriptError::errorFlag);
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// The response has been validated, so now we can consider if its a
|
||||
// byte-for-byte match. This is step 6 of the Update algorithm.
|
||||
if (aInCacheAndEqual) {
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_UPDATED, 1);
|
||||
|
||||
// Begin step 7 of the Update algorithm to evaluate the new script.
|
||||
|
||||
MOZ_ASSERT(!mServiceWorker);
|
||||
mServiceWorker = new ServiceWorkerInfo(mRegistration->mPrincipal,
|
||||
mRegistration->mScope,
|
||||
mScriptSpec, aNewCacheName);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle);
|
||||
|
||||
ServiceWorkerPrivate* workerPrivate = mServiceWorker->WorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
rv = workerPrivate->CheckScriptEvaluation(callback);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (Canceled()) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 7.5 of the Update algorithm verifying that the script evaluated
|
||||
// successfully.
|
||||
|
||||
if (NS_WARN_IF(!aScriptEvaluationResult)) {
|
||||
ErrorResult error;
|
||||
|
||||
NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
|
||||
NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
|
||||
error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope);
|
||||
FailUpdateJob(error);
|
||||
return;
|
||||
}
|
||||
|
||||
Install();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::Install()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!Canceled());
|
||||
|
||||
MOZ_ASSERT(!mRegistration->GetInstalling());
|
||||
|
||||
// Begin step 2 of the Install algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
|
||||
|
||||
MOZ_ASSERT(mServiceWorker);
|
||||
mRegistration->SetInstalling(mServiceWorker);
|
||||
mServiceWorker = nullptr;
|
||||
|
||||
// Step 6 of the Install algorithm resolving the job promise.
|
||||
InvokeResultCallbacks(NS_OK);
|
||||
|
||||
// The job promise cannot be rejected after this point, but the job can
|
||||
// still fail; e.g. if the install event handler throws, etc.
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
|
||||
// fire the updatefound event
|
||||
nsCOMPtr<nsIRunnable> upr =
|
||||
NS_NewRunnableMethodWithArg<RefPtr<ServiceWorkerRegistrationInfo>>(
|
||||
swm,
|
||||
&ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
|
||||
mRegistration);
|
||||
NS_DispatchToMainThread(upr);
|
||||
|
||||
// Call ContinueAfterInstallEvent(false) on main thread if the SW
|
||||
// script fails to load.
|
||||
nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
|
||||
(this, &ServiceWorkerUpdateJob::ContinueAfterInstallEvent, false);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueInstallRunnable(handle);
|
||||
|
||||
// Send the install event to the worker thread
|
||||
ServiceWorkerPrivate* workerPrivate =
|
||||
mRegistration->GetInstalling()->WorkerPrivate();
|
||||
nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
|
||||
callback, failRunnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ContinueAfterInstallEvent(false /* aSuccess */);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ContinueAfterInstallEvent(bool aInstallEventSuccess)
|
||||
{
|
||||
if (Canceled()) {
|
||||
return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mRegistration->GetInstalling());
|
||||
|
||||
// Continue executing the Install algorithm at step 12.
|
||||
|
||||
// "If installFailed is true"
|
||||
if (NS_WARN_IF(!aInstallEventSuccess)) {
|
||||
// The installing worker is cleaned up by FailUpdateJob().
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mRegistration->TransitionInstallingToWaiting();
|
||||
|
||||
Finish(NS_OK);
|
||||
|
||||
// Step 20 calls for explicitly waiting for queued event tasks to fire. Instead,
|
||||
// we simply queue a runnable to execute Activate. This ensures the events are
|
||||
// flushed from the queue before proceeding.
|
||||
|
||||
// Step 22 of the Install algorithm. Activate is executed after the completion
|
||||
// of this job. The controlling client and skipWaiting checks are performed
|
||||
// in TryToActivate().
|
||||
mRegistration->TryToActivateAsync();
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,103 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_workers_serviceworkerupdatejob_h
|
||||
#define mozilla_dom_workers_serviceworkerupdatejob_h
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
// A job class that performs the Update and Install algorithms from the
|
||||
// service worker spec. This class is designed to be inherited and customized
|
||||
// as a different job type. This is necessary because the register job
|
||||
// performs largely the same operations as the update job, but has a few
|
||||
// different starting steps.
|
||||
class ServiceWorkerUpdateJob : public ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
// Construct an update job to be used only for updates.
|
||||
ServiceWorkerUpdateJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
GetRegistration() const;
|
||||
|
||||
protected:
|
||||
// Construct an update job that is overriden as another job type.
|
||||
ServiceWorkerUpdateJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
virtual ~ServiceWorkerUpdateJob();
|
||||
|
||||
// FailUpdateJob() must be called if an update job needs Finish() with
|
||||
// an error.
|
||||
void
|
||||
FailUpdateJob(ErrorResult& aRv);
|
||||
|
||||
void
|
||||
FailUpdateJob(nsresult aRv);
|
||||
|
||||
// The entry point when the update job is being used directly. Job
|
||||
// types overriding this class should override this method to
|
||||
// customize behavior.
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
// Set the registration to be operated on by Update() or to be immediately
|
||||
// returned as a result of the job. This must be called before Update().
|
||||
void
|
||||
SetRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
// Execute the bulk of the update job logic using the registration defined
|
||||
// by a previous SetRegistration() call. This can be called by the overriden
|
||||
// AsyncExecute() to complete the job. The Update() method will always call
|
||||
// Finish(). This method corresponds to the spec Update algorithm.
|
||||
void
|
||||
Update();
|
||||
|
||||
private:
|
||||
class CompareCallback;
|
||||
class ContinueUpdateRunnable;
|
||||
class ContinueInstallRunnable;
|
||||
|
||||
// Utility method called after a script is loaded and compared to
|
||||
// our current cached script.
|
||||
void
|
||||
ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope);
|
||||
|
||||
// Utility method called after evaluating the worker script.
|
||||
void
|
||||
ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult);
|
||||
|
||||
// Utility method corresponding to the spec Install algorithm.
|
||||
void
|
||||
Install();
|
||||
|
||||
// Utility method called after the install event is handled.
|
||||
void
|
||||
ContinueAfterInstallEvent(bool aInstallEventSuccess);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
RefPtr<ServiceWorkerInfo> mServiceWorker;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerupdatejob_h
|
||||
@@ -26,6 +26,8 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
|
||||
|
||||
@@ -103,14 +103,11 @@
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerDebuggerManager.h"
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerNavigator.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
#include "WorkerThread.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
// JS_MaybeGC will run once every second during normal execution.
|
||||
#define PERIODIC_GC_TIMER_DELAY_SEC 1
|
||||
|
||||
@@ -4539,6 +4536,9 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||
static_cast<nsIRunnable*>(runnable)->Run();
|
||||
runnable->Release();
|
||||
|
||||
// Flush the promise queue.
|
||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
||||
|
||||
if (debuggerRunnablesPending) {
|
||||
WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
@@ -5589,6 +5589,9 @@ WorkerPrivate::EnterDebuggerEventLoop()
|
||||
static_cast<nsIRunnable*>(runnable)->Run();
|
||||
runnable->Release();
|
||||
|
||||
// Flush the promise queue.
|
||||
Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
|
||||
|
||||
// Now *might* be a good time to GC. Let the JS engine make the decision.
|
||||
if (JS::CurrentGlobalOrNull(cx)) {
|
||||
JS_MaybeGC(cx);
|
||||
@@ -5707,7 +5710,7 @@ WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
|
||||
|
||||
// If the worker script never ran, or failed to compile, we don't need to do
|
||||
// anything else, except pretend that we ran the close handler.
|
||||
if (!JS::CurrentGlobalOrNull(aCx)) {
|
||||
if (!GlobalScope()) {
|
||||
mCloseHandlerStarted = true;
|
||||
mCloseHandlerFinished = true;
|
||||
return true;
|
||||
@@ -5903,8 +5906,10 @@ WorkerPrivate::SetTimeout(JSContext* aCx,
|
||||
newInfo->mTimeoutString = aStringHandler;
|
||||
}
|
||||
else {
|
||||
JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)",
|
||||
aIsInterval ? "setInterval" : "setTimeout");
|
||||
NS_NAMED_LITERAL_STRING(kSetInterval, "setInterval");
|
||||
NS_NAMED_LITERAL_STRING(kSetTimeout, "setTimeout");
|
||||
aRv.ThrowTypeError<MSG_USELESS_SETTIMEOUT>(aIsInterval ? kSetInterval
|
||||
: kSetTimeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6083,7 +6088,7 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
|
||||
|
||||
// Since we might be processing more timeouts, go ahead and flush
|
||||
// the promise queue now before we do that.
|
||||
Promise::PerformMicroTaskCheckpoint();
|
||||
Promise::PerformWorkerMicroTaskCheckpoint();
|
||||
|
||||
NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#include "Queue.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
class nsIChannel;
|
||||
class nsIDocument;
|
||||
class nsIEventTarget;
|
||||
|
||||
@@ -237,7 +237,7 @@ WorkerGlobalScope::SetTimeout(JSContext* aCx,
|
||||
}
|
||||
|
||||
int32_t
|
||||
WorkerGlobalScope::SetTimeout(JSContext* /* unused */,
|
||||
WorkerGlobalScope::SetTimeout(JSContext* aCx,
|
||||
const nsAString& aHandler,
|
||||
const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */,
|
||||
@@ -245,8 +245,8 @@ WorkerGlobalScope::SetTimeout(JSContext* /* unused */,
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
Sequence<JS::Value> dummy;
|
||||
return mWorkerPrivate->SetTimeout(GetCurrentThreadJSContext(), nullptr,
|
||||
aHandler, aTimeout, dummy, false, aRv);
|
||||
return mWorkerPrivate->SetTimeout(aCx, nullptr, aHandler, aTimeout, dummy,
|
||||
false, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -118,9 +118,8 @@ public:
|
||||
SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
|
||||
int32_t
|
||||
SetTimeout(JSContext* /* unused */, const nsAString& aHandler,
|
||||
const int32_t aTimeout, const Sequence<JS::Value>& /* unused */,
|
||||
ErrorResult& aRv);
|
||||
SetTimeout(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */, ErrorResult& aRv);
|
||||
void
|
||||
ClearTimeout(int32_t aHandle);
|
||||
int32_t
|
||||
|
||||
@@ -23,7 +23,9 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
EXPORTS.mozilla.dom.workers += [
|
||||
'RuntimeService.h',
|
||||
'ServiceWorkerInfo.h',
|
||||
'ServiceWorkerManager.h',
|
||||
'ServiceWorkerRegistrationInfo.h',
|
||||
'WorkerDebuggerManager.h',
|
||||
'Workers.h',
|
||||
]
|
||||
@@ -66,15 +68,22 @@ UNIFIED_SOURCES += [
|
||||
'ServiceWorkerClients.cpp',
|
||||
'ServiceWorkerContainer.cpp',
|
||||
'ServiceWorkerEvents.cpp',
|
||||
'ServiceWorkerInfo.cpp',
|
||||
'ServiceWorkerJob.cpp',
|
||||
'ServiceWorkerJobQueue.cpp',
|
||||
'ServiceWorkerManager.cpp',
|
||||
'ServiceWorkerManagerChild.cpp',
|
||||
'ServiceWorkerManagerParent.cpp',
|
||||
'ServiceWorkerManagerService.cpp',
|
||||
'ServiceWorkerMessageEvent.cpp',
|
||||
'ServiceWorkerPrivate.cpp',
|
||||
'ServiceWorkerRegisterJob.cpp',
|
||||
'ServiceWorkerRegistrar.cpp',
|
||||
'ServiceWorkerRegistration.cpp',
|
||||
'ServiceWorkerRegistrationInfo.cpp',
|
||||
'ServiceWorkerScriptCache.cpp',
|
||||
'ServiceWorkerUnregisterJob.cpp',
|
||||
'ServiceWorkerUpdateJob.cpp',
|
||||
'ServiceWorkerWindowClient.cpp',
|
||||
'SharedWorker.cpp',
|
||||
'URL.cpp',
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
var self = this;
|
||||
|
||||
self.onmessage = function (event) {
|
||||
if (event.data !== "resolve") {
|
||||
return;
|
||||
}
|
||||
// This then-handler should be executed inside the top-level event loop,
|
||||
// within the context of the debugger's global.
|
||||
Promise.resolve().then(function () {
|
||||
var dbg = new Debugger(global);
|
||||
dbg.onDebuggerStatement = function () {
|
||||
self.onmessage = function (event) {
|
||||
if (event.data !== "resume") {
|
||||
return;
|
||||
}
|
||||
// This then-handler should be executed inside the nested event loop,
|
||||
// within the context of the debugger's global.
|
||||
Promise.resolve().then(function () {
|
||||
postMessage("resumed");
|
||||
leaveEventLoop();
|
||||
});
|
||||
};
|
||||
postMessage("paused");
|
||||
enterEventLoop();
|
||||
};
|
||||
postMessage("resolved");
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
self.onmessage = function (event) {
|
||||
if (event.data !== "resolve") {
|
||||
return;
|
||||
}
|
||||
// This then-handler should be executed inside the top-level event loop,
|
||||
// within the context of the worker's global.
|
||||
Promise.resolve().then(function () {
|
||||
self.onmessage = function (event) {
|
||||
if (event.data !== "pause") {
|
||||
return;
|
||||
}
|
||||
// This then-handler should be executed inside the top-level event loop,
|
||||
// within the context of the worker's global. Because the debugger
|
||||
// statement here below enters a nested event loop, the then-handler
|
||||
// should not be executed until the debugger statement returns.
|
||||
Promise.resolve().then(function () {
|
||||
postMessage("resumed");
|
||||
});
|
||||
debugger;
|
||||
}
|
||||
postMessage("resolved");
|
||||
});
|
||||
};
|
||||
@@ -28,6 +28,8 @@ support-files =
|
||||
WorkerDebugger_frozen_iframe2.html
|
||||
WorkerDebugger_frozen_worker1.js
|
||||
WorkerDebugger_frozen_worker2.js
|
||||
WorkerDebugger_promise_debugger.js
|
||||
WorkerDebugger_promise_worker.js
|
||||
WorkerDebugger_sharedWorker.js
|
||||
WorkerDebugger_suspended_debugger.js
|
||||
WorkerDebugger_suspended_worker.js
|
||||
@@ -65,6 +67,7 @@ support-files =
|
||||
[test_WorkerDebuggerManager.xul]
|
||||
[test_WorkerDebugger_console.xul]
|
||||
[test_WorkerDebugger_frozen.xul]
|
||||
[test_WorkerDebugger_promise.xul]
|
||||
[test_WorkerDebugger_suspended.xul]
|
||||
[test_bug883784.xul]
|
||||
[test_chromeWorker.xul]
|
||||
|
||||
@@ -12,6 +12,8 @@ const Ci = Components.interfaces;
|
||||
var mainWindow;
|
||||
|
||||
var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
|
||||
var workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
|
||||
var workerURL = workerScope + "worker.js";
|
||||
|
||||
function testOnWindow(aIsPrivate, aCallback) {
|
||||
var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
|
||||
@@ -44,18 +46,34 @@ function setupWindow() {
|
||||
}
|
||||
|
||||
var wN;
|
||||
var registration;
|
||||
var wP;
|
||||
|
||||
function testPrivateWindow() {
|
||||
testOnWindow(true, function(aWin) {
|
||||
wP = aWin;
|
||||
ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows");
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
function doTests() {
|
||||
testOnWindow(false, function(aWin) {
|
||||
wN = aWin;
|
||||
ok("serviceWorker" in wN.content.navigator, "ServiceWorkers are available for normal windows");
|
||||
|
||||
testOnWindow(true, function(aWin) {
|
||||
wP = aWin;
|
||||
ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows");
|
||||
runTest();
|
||||
});
|
||||
wN.content.navigator.serviceWorker.register(workerURL,
|
||||
{ scope: workerScope })
|
||||
.then(function(aRegistration) {
|
||||
registration = aRegistration;
|
||||
ok(registration, "Registering a service worker in a normal window should succeed");
|
||||
|
||||
// Bug 1255621: We should be able to load a controlled document in a private window.
|
||||
testPrivateWindow();
|
||||
}, function(aError) {
|
||||
ok(false, "Error registering worker in normal window: " + aError);
|
||||
testPrivateWindow();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,12 +82,17 @@ var steps = [
|
||||
doTests
|
||||
];
|
||||
|
||||
function cleanup() {
|
||||
wN.close();
|
||||
wP.close();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
if (!steps.length) {
|
||||
wN.close();
|
||||
wP.close();
|
||||
registration.unregister().then(cleanup, cleanup);
|
||||
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,6 +103,7 @@ function runTest() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
["browser.startup.page", 0],
|
||||
["browser.startup.homepage_override.mstone", "ignore"],
|
||||
]}, runTest);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
+ http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<window title="Test for WorkerDebugger with DOM Promises"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="test();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
<script type="application/javascript" src="dom_worker_helper.js"/>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
const WORKER_URL = "WorkerDebugger_promise_worker.js";
|
||||
const DEBUGGER_URL = BASE_URL + "WorkerDebugger_promise_debugger.js";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
|
||||
let worker = new Worker(WORKER_URL);
|
||||
let dbg = yield promise;
|
||||
|
||||
info("Send a request to the worker. This should cause the worker " +
|
||||
"to send a response.");
|
||||
promise = waitForWorkerMessage(worker, "resolved");
|
||||
worker.postMessage("resolve");
|
||||
yield promise;
|
||||
|
||||
info("Send a request to the debugger. This should cause the debugger " +
|
||||
"to send a response.");
|
||||
promise = waitForDebuggerMessage(dbg, "resolved");
|
||||
dbg.postMessage("resolve");
|
||||
yield promise;
|
||||
|
||||
info("Send a request to the worker. This should cause the debugger " +
|
||||
"to enter a nested event loop.");
|
||||
promise = waitForDebuggerMessage(dbg, "paused");
|
||||
worker.postMessage("pause");
|
||||
yield promise;
|
||||
|
||||
info("Send a request to the debugger. This should cause the debugger " +
|
||||
"to leave the nested event loop.");
|
||||
promise = waitForMultiple([
|
||||
waitForDebuggerMessage(dbg, "resumed"),
|
||||
waitForWorkerMessage(worker, "resumed")
|
||||
]);
|
||||
dbg.postMessage("resume");
|
||||
yield promise;
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display:none;"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
<label id="test-result"/>
|
||||
</window>
|
||||
@@ -1594,6 +1594,10 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||
|
||||
XPCJSRuntime::~XPCJSRuntime()
|
||||
{
|
||||
// Elsewhere we abort immediately if XPCJSRuntime initialization fails.
|
||||
// Therefore the runtime must be non-null.
|
||||
MOZ_ASSERT(MaybeRuntime());
|
||||
|
||||
// This destructor runs before ~CycleCollectedJSRuntime, which does the
|
||||
// actual JS_DestroyRuntime() call. But destroying the runtime triggers
|
||||
// one final GC, which can call back into the runtime with various
|
||||
|
||||
@@ -1850,7 +1850,6 @@ nsDocumentViewer::SetDocumentInternal(nsIDocument* aDocument,
|
||||
}
|
||||
|
||||
if (mDocument->IsStaticDocument()) {
|
||||
mDocument->SetScriptGlobalObject(nullptr);
|
||||
mDocument->Destroy();
|
||||
}
|
||||
|
||||
@@ -4419,7 +4418,6 @@ nsDocumentViewer::OnDonePrinting()
|
||||
}
|
||||
} else if (mClosingWhilePrinting) {
|
||||
if (mDocument) {
|
||||
mDocument->SetScriptGlobalObject(nullptr);
|
||||
mDocument->Destroy();
|
||||
mDocument = nullptr;
|
||||
}
|
||||
|
||||
@@ -522,6 +522,9 @@
|
||||
* template arguments are required to be safe to move in memory using
|
||||
* memmove(). Passing MOZ_NON_MEMMOVABLE types to these templates is a
|
||||
* compile time error.
|
||||
* MOZ_NEEDS_MEMMOVABLE_MEMBERS: Applies to class declarations where each member
|
||||
* must be safe to move in memory using memmove(). MOZ_NON_MEMMOVABLE types
|
||||
* used in members of these classes are compile time errors.
|
||||
* MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS: Applies to template class
|
||||
* declarations where an instance of the template should be considered, for
|
||||
* static analysis purposes, to inherit any type annotations (such as
|
||||
@@ -555,6 +558,7 @@
|
||||
# define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type")))
|
||||
# define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
|
||||
# define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
|
||||
# define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members")))
|
||||
# define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS \
|
||||
__attribute__((annotate("moz_inherit_type_annotations_from_template_args")))
|
||||
# define MOZ_NON_AUTOABLE __attribute__((annotate("moz_non_autoable")))
|
||||
@@ -587,6 +591,7 @@
|
||||
# define MOZ_NEEDS_NO_VTABLE_TYPE /* nothing */
|
||||
# define MOZ_NON_MEMMOVABLE /* nothing */
|
||||
# define MOZ_NEEDS_MEMMOVABLE_TYPE /* nothing */
|
||||
# define MOZ_NEEDS_MEMMOVABLE_MEMBERS /* nothing */
|
||||
# define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS /* nothing */
|
||||
# define MOZ_NON_AUTOABLE /* nothing */
|
||||
#endif /* MOZ_CLANG_PLUGIN */
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
FROM node:4.2
|
||||
MAINTAINER Dave Townsend <dtownsend@oxymoronical.com>
|
||||
|
||||
RUN useradd -d /home/worker -s /bin/bash -m worker
|
||||
WORKDIR /home/worker
|
||||
|
||||
# install necessary npm packages
|
||||
RUN npm install -g taskcluster-vcs@2.3.12
|
||||
RUN npm install -g eslint@2.8.0
|
||||
RUN npm install -g eslint-plugin-html@1.4.0
|
||||
RUN npm install -g eslint-plugin-react@4.2.3
|
||||
|
||||
# Set variable normally configured at login, by the shells parent process, these
|
||||
# are taken from GNU su manual
|
||||
ENV HOME /home/worker
|
||||
ENV SHELL /bin/bash
|
||||
ENV USER worker
|
||||
ENV LOGNAME worker
|
||||
ENV HOSTNAME taskcluster-worker
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
# Set a default command useful for debugging
|
||||
CMD ["/bin/bash", "--login"]
|
||||
@@ -23,6 +23,7 @@ flags:
|
||||
- aries
|
||||
- aries-ota
|
||||
- aries-eng
|
||||
- eslint-gecko
|
||||
|
||||
tests:
|
||||
- cppunit
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# This is the "base" task which contains the common values all linting tests must
|
||||
# provide.
|
||||
---
|
||||
taskId: {{build_slugid}}
|
||||
|
||||
task:
|
||||
created: '{{now}}'
|
||||
deadline: '{{#from_now}}24 hours{{/from_now}}'
|
||||
metadata:
|
||||
source: http://todo.com/soon
|
||||
owner: mozilla-taskcluster-maintenance@mozilla.com
|
||||
|
||||
tags:
|
||||
createdForUser: {{owner}}
|
||||
|
||||
workerType: b2gtest
|
||||
provisionerId: aws-provisioner-v1
|
||||
schedulerId: task-graph-scheduler
|
||||
|
||||
routes:
|
||||
- 'index.gecko.v1.{{project}}.revision.linux.{{head_rev}}.{{build_name}}.{{build_type}}'
|
||||
- 'index.gecko.v1.{{project}}.latest.linux.{{build_name}}.{{build_type}}'
|
||||
scopes:
|
||||
# Nearly all of our build tasks use tc-vcs so just include the scope across
|
||||
# the board.
|
||||
- 'docker-worker:cache:tc-vcs'
|
||||
|
||||
payload:
|
||||
# Thirty minutes should be enough for lint checks
|
||||
maxRunTime: 1800
|
||||
|
||||
cache:
|
||||
tc-vcs: '/home/worker/.tc-vcs'
|
||||
|
||||
extra:
|
||||
build_product: '{{build_product}}'
|
||||
build_name: '{{build_name}}'
|
||||
build_type: '{{build_type}}'
|
||||
index:
|
||||
rank: {{pushlog_id}}
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
$inherits:
|
||||
from: 'tasks/lint.yml'
|
||||
|
||||
task:
|
||||
metadata:
|
||||
name: '[TC] - ESLint'
|
||||
description: 'ESLint test'
|
||||
|
||||
payload:
|
||||
image:
|
||||
type: 'task-image'
|
||||
path: 'public/image.tar'
|
||||
taskId: '{{#task_id_for_image}}lint{{/task_id_for_image}}'
|
||||
|
||||
command:
|
||||
- bash
|
||||
- -cx
|
||||
- >
|
||||
tc-vcs checkout ./gecko {{base_repository}} {{head_repository}} {{head_rev}} {{head_ref}} &&
|
||||
cd gecko &&
|
||||
npm link testing/eslint-plugin-mozilla &&
|
||||
eslint --plugin html --ext [.js,.jsm,.jsx,.xml,.html] -f tools/lint/eslint-formatter .
|
||||
extra:
|
||||
locations:
|
||||
build: null
|
||||
tests: null
|
||||
treeherder:
|
||||
machine:
|
||||
platform: lint
|
||||
groupSymbol: tc
|
||||
symbol: ES
|
||||
treeherderEnv:
|
||||
- production
|
||||
- staging
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/test-helpers.sub.js"></script>
|
||||
<script>
|
||||
var worker_url = 'resources/empty-worker.js';
|
||||
|
||||
async_test(function(t) {
|
||||
var scope = 'resources/scope/unregister-then-register-new-script-that-exists';
|
||||
var new_worker_url = worker_url + '?new';
|
||||
var iframe;
|
||||
var registration;
|
||||
var new_registration;
|
||||
|
||||
service_worker_unregister_and_register(t, worker_url, scope)
|
||||
.then(function(r) {
|
||||
registration = r;
|
||||
return wait_for_state(t, r.installing, 'activated');
|
||||
})
|
||||
.then(function() {
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
iframe = frame;
|
||||
return registration.unregister();
|
||||
})
|
||||
.then(function() {
|
||||
return navigator.serviceWorker.register(new_worker_url,
|
||||
{ scope: scope });
|
||||
})
|
||||
.then(function(r) {
|
||||
new_registration = r;
|
||||
assert_equals(registration.installing.scriptURL,
|
||||
normalizeURL(new_worker_url),
|
||||
'before activated registration.installing');
|
||||
assert_equals(registration.waiting, null,
|
||||
'before activated registration.waiting');
|
||||
assert_equals(registration.active.scriptURL, normalizeURL(worker_url),
|
||||
'before activated registration.active');
|
||||
assert_equals(new_registration.installing.scriptURL,
|
||||
normalizeURL(new_worker_url),
|
||||
'before activated new_registration.installing');
|
||||
assert_equals(new_registration.waiting, null,
|
||||
'before activated new_registration.waiting');
|
||||
assert_equals(new_registration.active.scriptURL,
|
||||
normalizeURL(worker_url),
|
||||
'before activated new_registration.active');
|
||||
iframe.remove();
|
||||
return wait_for_state(t, registration.installing, 'activated');
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(new_registration.installing, null,
|
||||
'after activated new_registration.installing');
|
||||
assert_equals(new_registration.waiting, null,
|
||||
'after activated new_registration.waiting');
|
||||
assert_equals(new_registration.active.scriptURL,
|
||||
normalizeURL(new_worker_url),
|
||||
'after activated new_registration.active');
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
assert_equals(
|
||||
frame.contentWindow.navigator.serviceWorker.controller.scriptURL,
|
||||
normalizeURL(new_worker_url),
|
||||
'the new worker should control a new document');
|
||||
frame.remove();
|
||||
return registration.unregister();
|
||||
})
|
||||
.then(function() {
|
||||
t.done();
|
||||
})
|
||||
.catch(unreached_rejection(t));
|
||||
}, 'Registering a new script URL while an unregistered registration is in use');
|
||||
|
||||
async_test(function(t) {
|
||||
var scope = 'resources/scope/unregister-then-register-new-script-that-404s';
|
||||
var iframe;
|
||||
var registration;
|
||||
|
||||
service_worker_unregister_and_register(t, worker_url, scope)
|
||||
.then(function(r) {
|
||||
registration = r;
|
||||
return wait_for_state(t, r.installing, 'activated');
|
||||
})
|
||||
.then(function() {
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
iframe = frame;
|
||||
return registration.unregister();
|
||||
})
|
||||
.then(function() {
|
||||
var promise = navigator.serviceWorker.register('this-will-404',
|
||||
{ scope: scope });
|
||||
iframe.remove();
|
||||
return promise;
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
assert_unreached('register should reject the promise');
|
||||
},
|
||||
function() {
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
|
||||
null,
|
||||
'document should not load with a controller');
|
||||
frame.remove();
|
||||
t.done();
|
||||
})
|
||||
.catch(unreached_rejection(t));
|
||||
}, 'Registering a new script URL that 404s does not resurrect an ' +
|
||||
'unregistered registration');
|
||||
|
||||
async_test(function(t) {
|
||||
var scope = 'resources/scope/unregister-then-register-reject-install-worker';
|
||||
var iframe;
|
||||
var registration;
|
||||
|
||||
service_worker_unregister_and_register(t, worker_url, scope)
|
||||
.then(function(r) {
|
||||
registration = r;
|
||||
return wait_for_state(t, r.installing, 'activated');
|
||||
})
|
||||
.then(function() {
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
iframe = frame;
|
||||
return registration.unregister();
|
||||
})
|
||||
.then(function() {
|
||||
var promise = navigator.serviceWorker.register(
|
||||
'resources/reject-install-worker.js', { scope: scope });
|
||||
iframe.remove();
|
||||
return promise;
|
||||
})
|
||||
.then(function(r) {
|
||||
registration = r;
|
||||
return wait_for_state(t, r.installing, 'redundant');
|
||||
})
|
||||
.then(function() {
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
|
||||
null,
|
||||
'document should not load with a controller');
|
||||
frame.remove();
|
||||
return registration.unregister();
|
||||
})
|
||||
.then(function() {
|
||||
t.done();
|
||||
})
|
||||
.catch(unreached_rejection(t));
|
||||
}, 'Registering a new script URL that fails to install does not resurrect ' +
|
||||
'an unregistered registration');
|
||||
</script>
|
||||
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html>
|
||||
<title>Service Worker: Registration update()</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/testharness-helpers.js"></script>
|
||||
<script src="resources/test-helpers.sub.js"></script>
|
||||
<script>
|
||||
promise_test(function(t) {
|
||||
var scope = 'resources/simple.txt';
|
||||
var worker_url = 'resources/update-worker.py';
|
||||
var expected_url = normalizeURL(worker_url);
|
||||
var registration;
|
||||
var iframe;
|
||||
return service_worker_unregister_and_register(t, worker_url, scope)
|
||||
.then(function(r) {
|
||||
registration = r;
|
||||
return wait_for_state(t, registration.installing, 'activated');
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(registration.installing, null,
|
||||
'installing should be null in the initial state.');
|
||||
assert_equals(registration.waiting, null,
|
||||
'waiting should be null in the initial state.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should exist in the initial state.');
|
||||
// A new worker (generated by update-worker.py) should be found.
|
||||
// The returned promise should resolve when a new worker script is
|
||||
// fetched and starts installing.
|
||||
return Promise.all([registration.update(),
|
||||
wait_for_update(t, registration)]);
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(registration.installing.scriptURL, expected_url,
|
||||
'new installing should be set after update resolves.');
|
||||
assert_equals(registration.waiting, null,
|
||||
'waiting should still be null after update resolves.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should still exist after update found.');
|
||||
return wait_for_state(t, registration.installing, 'installed');
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(registration.installing, null,
|
||||
'installing should be null after installing.');
|
||||
if (registration.waiting) {
|
||||
assert_equals(registration.waiting.scriptURL, expected_url,
|
||||
'waiting should be set after installing.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should still exist after installing.');
|
||||
return wait_for_state(t, registration.waiting, 'activated');
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(registration.installing, null,
|
||||
'installing should be null after activated.');
|
||||
assert_equals(registration.waiting, null,
|
||||
'waiting should be null after activated.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'new worker should be promoted to active.');
|
||||
})
|
||||
.then(function() {
|
||||
// A new worker(generated by update-worker.py) should be found.
|
||||
// The returned promise should reject as update-worker.py sets the
|
||||
// mimetype to a disallowed value for this attempt.
|
||||
return registration.update();
|
||||
})
|
||||
.then(
|
||||
function() { assert_unreached("update() should reject."); },
|
||||
function(e) {
|
||||
assert_throws('SecurityError', function() { throw e; },
|
||||
'Using a disallowed mimetype should make update() ' +
|
||||
'promise reject with a SecurityError.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should still exist after update failure.');
|
||||
|
||||
// A new worker(generated by update-worker.py) should be found.
|
||||
// The returned promise should reject as update-worker.py returns
|
||||
// a worker script with a syntax error.
|
||||
return registration.update();
|
||||
})
|
||||
.then(
|
||||
function() { assert_unreached("update() should reject."); },
|
||||
function(e) {
|
||||
assert_throws({name: 'TypeError'}, function () { throw e; },
|
||||
'A script syntax error should make update() ' +
|
||||
'promise reject with a TypeError.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should still exist after update failure.');
|
||||
|
||||
// A new worker(generated by update-worker.py) should be found.
|
||||
// The returned promise should not reject, even though
|
||||
// update-worker.py returns a worker script that throws in the
|
||||
// install event handler.
|
||||
return Promise.all([registration.update(),
|
||||
wait_for_update(t, registration)]);
|
||||
})
|
||||
.then(function() {
|
||||
assert_equals(registration.installing.scriptURL, expected_url,
|
||||
'new installing should be set after update resolves.');
|
||||
assert_equals(registration.waiting, null,
|
||||
'waiting should be null after activated.');
|
||||
assert_equals(registration.active.scriptURL, expected_url,
|
||||
'active should still exist after update found.');
|
||||
|
||||
// We need to hold a client alive so that unregister() below doesn't
|
||||
// remove the registration before update() has had a chance to look
|
||||
// at the pending uninstall flag.
|
||||
return with_iframe(scope);
|
||||
})
|
||||
.then(function(frame) {
|
||||
iframe = frame;
|
||||
|
||||
return assert_promise_rejects(
|
||||
Promise.all([registration.unregister(), registration.update()]),
|
||||
new TypeError(),
|
||||
'Calling update() while the uninstalling flag is set' +
|
||||
'should return a promise that rejects with an ' +
|
||||
'InvalidStateError.');
|
||||
})
|
||||
.then(function() {
|
||||
iframe.remove();
|
||||
return t.done();
|
||||
});
|
||||
}, 'Update a registration.');
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<title>Service Worker: Navigate a Window</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/get-host-info.sub.js"></script>
|
||||
<script src="resources/test-helpers.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
var host_info = get_host_info();
|
||||
var BASE_URL = host_info['HTTPS_ORIGIN'] + base_path();
|
||||
|
||||
function wait_for_message(msg) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.addEventListener('message', function onMsg(evt) {
|
||||
if (evt.data.type === msg) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function with_window(url) {
|
||||
var win = window.open(url);
|
||||
return wait_for_message('LOADED').then(_ => win);
|
||||
}
|
||||
|
||||
function navigate_window(win, url) {
|
||||
win.location = url;
|
||||
return wait_for_message('LOADED').then(_ => win);
|
||||
}
|
||||
|
||||
function go_back(win) {
|
||||
win.history.back();
|
||||
return wait_for_message('PAGESHOW').then(_ => win);
|
||||
}
|
||||
|
||||
function go_forward(win) {
|
||||
win.history.forward();
|
||||
return wait_for_message('PAGESHOW').then(_ => win);
|
||||
}
|
||||
|
||||
function get_clients(win, sw, opts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
win.navigator.serviceWorker.addEventListener('message', function onMsg(evt) {
|
||||
win.navigator.serviceWorker.removeEventListener('message', onMsg);
|
||||
if (evt.data.type === 'success') {
|
||||
resolve(evt.data.detail);
|
||||
} else {
|
||||
reject(evt.data.detail);
|
||||
}
|
||||
});
|
||||
sw.postMessage({ type: 'GET_CLIENTS', opts: (opts || {}) });
|
||||
});
|
||||
}
|
||||
|
||||
function validate_window(win, url, opts) {
|
||||
return win.navigator.serviceWorker.getRegistration(url)
|
||||
.then(reg => {
|
||||
// In order to compare service worker instances we need to
|
||||
// make sure the DOM object is owned by the same global; the
|
||||
// opened window in this case.
|
||||
assert_equals(win.navigator.serviceWorker.controller, reg.active,
|
||||
'window should be controlled by service worker');
|
||||
return get_clients(win, reg.active);
|
||||
})
|
||||
.then(resultList => {
|
||||
assert_equals(resultList.length, 1, 'there should only be one client');
|
||||
assert_equals(resultList[0].url, url,
|
||||
'client should be our opened window');
|
||||
assert_equals(resultList[0].frameType, 'auxiliary',
|
||||
'window.open() should create a client with an auxiliary frame type');
|
||||
return win;
|
||||
})
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var worker = BASE_URL + 'resources/navigate-window-worker.js';
|
||||
var scope = BASE_URL + 'resources/loaded.html?navigate-window';
|
||||
var url1 = scope + '&q=1';
|
||||
var url2 = scope + '&q=2';
|
||||
service_worker_unregister_and_register(t, worker, scope)
|
||||
.then(reg => wait_for_state(t, reg.installing, 'activated') )
|
||||
.then(___ => with_window(url1))
|
||||
.then(win => validate_window(win, url1, { includeUncontrolled: false }))
|
||||
.then(win => navigate_window(win, url2))
|
||||
.then(win => validate_window(win, url2, { includeUncontrolled: false }))
|
||||
.then(win => go_back(win))
|
||||
.then(win => validate_window(win, url1, { includeUncontrolled: false }))
|
||||
.then(win => go_forward(win))
|
||||
.then(win => validate_window(win, url2, { includeUncontrolled: false }))
|
||||
.then(win => win.close())
|
||||
.catch(unreached_rejection(t))
|
||||
.then(___ => service_worker_unregister_and_done(t, scope))
|
||||
}, 'Clients.matchAll() should not show an old window as controlled after ' +
|
||||
'it navigates.');
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
addEventListener('load', function() {
|
||||
opener.postMessage({ type: 'LOADED' }, '*');
|
||||
});
|
||||
|
||||
addEventListener('pageshow', function() {
|
||||
opener.postMessage({ type: 'PAGESHOW' }, '*');
|
||||
});
|
||||
</script>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
addEventListener('message', function(evt) {
|
||||
if (evt.data.type === 'GET_CLIENTS') {
|
||||
clients.matchAll(evt.data.opts).then(function(clientList) {
|
||||
var resultList = clientList.map(function(c) {
|
||||
return { url: c.url, frameType: c.frameType, id: c.id };
|
||||
});
|
||||
evt.source.postMessage({ type: 'success', detail: resultList });
|
||||
}).catch(function(err) {
|
||||
evt.source.postMessage({
|
||||
type: 'failure',
|
||||
detail: 'matchAll() rejected with "' + err + '"'
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
evt.source.postMessage({
|
||||
type: 'failure',
|
||||
detail: 'Unexpected message type "' + evt.data.type + '"'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
const cps = Cc["@mozilla.org/addons/content-policy;1"].getService(Ci.nsIAddonContentPolicy);
|
||||
|
||||
add_task(function* test_csp_validator() {
|
||||
let checkPolicy = (policy, expectedResult, message = null) => {
|
||||
do_print(`Checking policy: ${policy}`);
|
||||
|
||||
let result = cps.validateAddonCSP(policy);
|
||||
equal(result, expectedResult);
|
||||
};
|
||||
|
||||
checkPolicy("script-src 'self'; object-src 'self';",
|
||||
null);
|
||||
|
||||
let hash = "'sha256-NjZhMDQ1YjQ1MjEwMmM1OWQ4NDBlYzA5N2Q1OWQ5NDY3ZTEzYTNmMzRmNjQ5NGU1MzlmZmQzMmMxYmIzNWYxOCAgLQo='";
|
||||
|
||||
checkPolicy(`script-src 'self' https://com https://*.example.com moz-extension://09abcdef blob: filesystem: ${hash} 'unsafe-eval'; ` +
|
||||
`object-src 'self' https://com https://*.example.com moz-extension://09abcdef blob: filesystem: ${hash}`,
|
||||
null);
|
||||
|
||||
checkPolicy("",
|
||||
"Policy is missing a required 'script-src' directive");
|
||||
|
||||
checkPolicy("object-src 'none';",
|
||||
"Policy is missing a required 'script-src' directive");
|
||||
|
||||
|
||||
checkPolicy("default-src 'self'", null,
|
||||
"A valid default-src should count as a valid script-src or object-src");
|
||||
|
||||
checkPolicy("default-src 'self'; script-src 'self'", null,
|
||||
"A valid default-src should count as a valid script-src or object-src");
|
||||
|
||||
checkPolicy("default-src 'self'; object-src 'self'", null,
|
||||
"A valid default-src should count as a valid script-src or object-src");
|
||||
|
||||
|
||||
checkPolicy("default-src 'self'; script-src http://example.com",
|
||||
"'script-src' directive contains a forbidden http: protocol source",
|
||||
"A valid default-src should not allow an invalid script-src directive");
|
||||
|
||||
checkPolicy("default-src 'self'; object-src http://example.com",
|
||||
"'object-src' directive contains a forbidden http: protocol source",
|
||||
"A valid default-src should not allow an invalid object-src directive");
|
||||
|
||||
|
||||
checkPolicy("script-src 'self';",
|
||||
"Policy is missing a required 'object-src' directive");
|
||||
|
||||
checkPolicy("script-src 'none'; object-src 'none'",
|
||||
"'script-src' must include the source 'self'");
|
||||
|
||||
checkPolicy("script-src 'self'; object-src 'none';",
|
||||
null);
|
||||
|
||||
checkPolicy("script-src 'self' 'unsafe-inline'; object-src 'self';",
|
||||
"'script-src' directive contains a forbidden 'unsafe-inline' keyword");
|
||||
|
||||
|
||||
let directives = ["script-src", "object-src"];
|
||||
|
||||
for (let [directive, other] of [directives, directives.slice().reverse()]) {
|
||||
for (let src of ["https://*", "https://*.blogspot.com", "https://*"]) {
|
||||
checkPolicy(`${directive} 'self' ${src}; ${other} 'self';`,
|
||||
`https: wildcard sources in '${directive}' directives must include at least one non-generic sub-domain (e.g., *.example.com rather than *.com)`);
|
||||
}
|
||||
|
||||
checkPolicy(`${directive} 'self' https:; ${other} 'self';`,
|
||||
`https: protocol requires a host in '${directive}' directives`);
|
||||
|
||||
checkPolicy(`${directive} 'self' http://example.com; ${other} 'self';`,
|
||||
`'${directive}' directive contains a forbidden http: protocol source`);
|
||||
|
||||
for (let protocol of ["http", "ftp", "meh"]) {
|
||||
checkPolicy(`${directive} 'self' ${protocol}:; ${other} 'self';`,
|
||||
`'${directive}' directive contains a forbidden ${protocol}: protocol source`);
|
||||
}
|
||||
|
||||
checkPolicy(`${directive} 'self' 'nonce-01234'; ${other} 'self';`,
|
||||
`'${directive}' directive contains a forbidden 'nonce-*' keyword`);
|
||||
}
|
||||
});
|
||||
@@ -40,15 +40,19 @@ toolkit.jar:
|
||||
content/global/browser-content.js
|
||||
*+ content/global/buildconfig.html
|
||||
* content/global/contentAreaUtils.js
|
||||
#ifndef MOZ_FENNEC
|
||||
content/global/customizeToolbar.css
|
||||
* content/global/customizeToolbar.js
|
||||
content/global/customizeToolbar.xul
|
||||
#endif
|
||||
content/global/devicestorage.properties
|
||||
#ifndef MOZ_FENNEC
|
||||
content/global/editMenuOverlay.js
|
||||
*+ content/global/editMenuOverlay.xul
|
||||
content/global/finddialog.js
|
||||
*+ content/global/finddialog.xul
|
||||
content/global/findUtils.js
|
||||
#endif
|
||||
content/global/filepicker.properties
|
||||
*+ content/global/globalOverlay.js
|
||||
content/global/logopage.xhtml
|
||||
|
||||
@@ -18,6 +18,9 @@ if CONFIG['OS_TARGET'] == 'Android':
|
||||
if CONFIG['MOZ_ANDROID_CXX_STL'] == 'mozstlport':
|
||||
DEFINES['USE_STLPORT'] = True
|
||||
|
||||
if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
|
||||
DEFINES['MOZ_FENNEC'] = True
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('aboutTelemetry.*'):
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
- 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/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findbar.dtd" >
|
||||
%findBarDTD;
|
||||
]>
|
||||
|
||||
<bindings id="browserBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
csp.error.missing-directive = Policy is missing a required '%S' directive
|
||||
|
||||
csp.error.illegal-keyword = '%1$S' directive contains a forbidden %2$S keyword
|
||||
|
||||
csp.error.illegal-protocol = '%1$S' directive contains a forbidden %2$S: protocol source
|
||||
|
||||
csp.error.missing-host = %2$S: protocol requires a host in '%1$S' directives
|
||||
|
||||
csp.error.missing-source = '%1$S' must include the source %2$S
|
||||
|
||||
csp.error.illegal-host-wildcard = %2$S: wildcard sources in '%1$S' directives must include at least one non-generic sub-domain (e.g., *.example.com rather than *.com)
|
||||
@@ -31,8 +31,10 @@
|
||||
locale/@AB_CD@/global/console.dtd (%chrome/global/console.dtd)
|
||||
locale/@AB_CD@/global/console.properties (%chrome/global/console.properties)
|
||||
locale/@AB_CD@/global/contentAreaCommands.properties (%chrome/global/contentAreaCommands.properties)
|
||||
#ifndef MOZ_FENNEC
|
||||
locale/@AB_CD@/global/customizeToolbar.dtd (%chrome/global/customizeToolbar.dtd)
|
||||
locale/@AB_CD@/global/customizeToolbar.properties (%chrome/global/customizeToolbar.properties)
|
||||
#endif
|
||||
locale/@AB_CD@/global/datetimepicker.dtd (%chrome/global/datetimepicker.dtd)
|
||||
locale/@AB_CD@/global/dateFormat.properties (%chrome/global/dateFormat.properties)
|
||||
locale/@AB_CD@/global/devtools/csscoverage.properties (%chrome/global/devtools/csscoverage.properties)
|
||||
@@ -94,15 +96,20 @@
|
||||
locale/@AB_CD@/global/devtools/webide.properties (%chrome/global/devtools/webide.properties)
|
||||
#endif
|
||||
locale/@AB_CD@/global/dialogOverlay.dtd (%chrome/global/dialogOverlay.dtd)
|
||||
#ifndef MOZ_FENNEC
|
||||
locale/@AB_CD@/global/editMenuOverlay.dtd (%chrome/global/editMenuOverlay.dtd)
|
||||
#endif
|
||||
locale/@AB_CD@/global/extensions.properties (%chrome/global/extensions.properties)
|
||||
locale/@AB_CD@/global/fallbackMenubar.properties (%chrome/global/fallbackMenubar.properties)
|
||||
locale/@AB_CD@/global/filefield.properties (%chrome/global/filefield.properties)
|
||||
locale/@AB_CD@/global/filepicker.dtd (%chrome/global/filepicker.dtd)
|
||||
locale/@AB_CD@/global/filepicker.properties (%chrome/global/filepicker.properties)
|
||||
#ifndef MOZ_FENNEC
|
||||
locale/@AB_CD@/global/findbar.dtd (%chrome/global/findbar.dtd)
|
||||
locale/@AB_CD@/global/findbar.properties (%chrome/global/findbar.properties)
|
||||
locale/@AB_CD@/global/finddialog.dtd (%chrome/global/finddialog.dtd)
|
||||
locale/@AB_CD@/global/finddialog.properties (%chrome/global/finddialog.properties)
|
||||
#endif
|
||||
locale/@AB_CD@/global/globalKeys.dtd (%chrome/global/globalKeys.dtd)
|
||||
locale/@AB_CD@/global/headsUpDisplay.properties (%chrome/global/headsUpDisplay.properties)
|
||||
locale/@AB_CD@/global/intl.css (%chrome/global/intl.css)
|
||||
@@ -146,6 +153,7 @@
|
||||
locale/@AB_CD@/mozapps/downloads/downloads.dtd (%chrome/mozapps/downloads/downloads.dtd)
|
||||
locale/@AB_CD@/mozapps/downloads/downloads.properties (%chrome/mozapps/downloads/downloads.properties)
|
||||
locale/@AB_CD@/mozapps/extensions/extensions.dtd (%chrome/mozapps/extensions/extensions.dtd)
|
||||
#ifndef MOZ_FENNEC
|
||||
locale/@AB_CD@/mozapps/extensions/extensions.properties (%chrome/mozapps/extensions/extensions.properties)
|
||||
locale/@AB_CD@/mozapps/extensions/blocklist.dtd (%chrome/mozapps/extensions/blocklist.dtd)
|
||||
locale/@AB_CD@/mozapps/extensions/about.dtd (%chrome/mozapps/extensions/about.dtd)
|
||||
@@ -153,6 +161,7 @@
|
||||
locale/@AB_CD@/mozapps/extensions/update.properties (%chrome/mozapps/extensions/update.properties)
|
||||
locale/@AB_CD@/mozapps/extensions/newaddon.dtd (%chrome/mozapps/extensions/newaddon.dtd)
|
||||
locale/@AB_CD@/mozapps/extensions/newaddon.properties (%chrome/mozapps/extensions/newaddon.properties)
|
||||
#endif
|
||||
locale/@AB_CD@/mozapps/handling/handling.dtd (%chrome/mozapps/handling/handling.dtd)
|
||||
locale/@AB_CD@/mozapps/handling/handling.properties (%chrome/mozapps/handling/handling.properties)
|
||||
locale/@AB_CD@/mozapps/plugins/plugins.dtd (%chrome/mozapps/plugins/plugins.dtd)
|
||||
@@ -170,9 +179,11 @@
|
||||
% locale alerts @AB_CD@ %locale/@AB_CD@/alerts/
|
||||
locale/@AB_CD@/alerts/alert.dtd (%chrome/alerts/alert.dtd)
|
||||
locale/@AB_CD@/alerts/alert.properties (%chrome/alerts/alert.properties)
|
||||
#ifndef MOZ_FENNEC
|
||||
% locale cookie @AB_CD@ %locale/@AB_CD@/cookie/
|
||||
locale/@AB_CD@/cookie/cookieAcceptDialog.dtd (%chrome/cookie/cookieAcceptDialog.dtd)
|
||||
locale/@AB_CD@/cookie/cookieAcceptDialog.properties (%chrome/cookie/cookieAcceptDialog.properties)
|
||||
#endif
|
||||
% locale formautofill @AB_CD@ %locale/@AB_CD@/formautofill/
|
||||
locale/@AB_CD@/formautofill/requestAutocomplete.dtd (%chrome/formautofill/requestAutocomplete.dtd)
|
||||
% locale passwordmgr @AB_CD@ %locale/@AB_CD@/passwordmgr/
|
||||
|
||||
@@ -6,19 +6,30 @@
|
||||
|
||||
#include "AddonContentPolicy.h"
|
||||
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/* Enforces content policies for WebExtension scopes. Currently:
|
||||
*
|
||||
* - Prevents loading scripts with a non-default JavaScript version.
|
||||
* - Checks custom content security policies for sufficiently stringent
|
||||
* script-src and object-src directives.
|
||||
*/
|
||||
|
||||
#define VERSIONED_JS_BLOCKED_MESSAGE \
|
||||
@@ -34,7 +45,7 @@ AddonContentPolicy::~AddonContentPolicy()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(AddonContentPolicy, nsIContentPolicy)
|
||||
NS_IMPL_ISUPPORTS(AddonContentPolicy, nsIContentPolicy, nsIAddonContentPolicy)
|
||||
|
||||
static nsresult
|
||||
GetWindowIDFromContext(nsISupports* aContext, uint64_t *aResult)
|
||||
@@ -80,6 +91,9 @@ LogMessage(const nsAString &aMessage, nsIURI* aSourceURI, const nsAString &aSour
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Content policy enforcement:
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonContentPolicy::ShouldLoad(uint32_t aContentType,
|
||||
nsIURI* aContentLocation,
|
||||
@@ -143,3 +157,323 @@ AddonContentPolicy::ShouldProcess(uint32_t aContentType,
|
||||
*aShouldProcess = nsIContentPolicy::ACCEPT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// CSP Validation:
|
||||
|
||||
static const char* allowedSchemes[] = {
|
||||
"blob",
|
||||
"filesystem",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static const char* allowedHostSchemes[] = {
|
||||
"https",
|
||||
"moz-extension",
|
||||
nullptr
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a CSP directive to ensure that it is sufficiently stringent.
|
||||
* In particular, ensures that:
|
||||
*
|
||||
* - No remote sources are allowed other than from https: schemes
|
||||
*
|
||||
* - No remote sources specify host wildcards for generic domains
|
||||
* (*.blogspot.com, *.com, *)
|
||||
*
|
||||
* - All remote sources and local extension sources specify a host
|
||||
*
|
||||
* - No scheme sources are allowed other than blob:, filesystem:,
|
||||
* moz-extension:, and https:
|
||||
*
|
||||
* - No keyword sources are allowed other than 'none', 'self', 'unsafe-eval',
|
||||
* and hash sources.
|
||||
*/
|
||||
class CSPValidator final : public nsCSPSrcVisitor {
|
||||
public:
|
||||
CSPValidator(nsAString& aURL, CSPDirective aDirective, bool aDirectiveRequired = true) :
|
||||
mURL(aURL),
|
||||
mDirective(CSP_CSPDirectiveToString(aDirective)),
|
||||
mFoundSelf(false)
|
||||
{
|
||||
// Start with the default error message for a missing directive, since no
|
||||
// visitors will be called if the directive isn't present.
|
||||
if (aDirectiveRequired) {
|
||||
FormatError("csp.error.missing-directive");
|
||||
}
|
||||
}
|
||||
|
||||
// Visitors
|
||||
|
||||
bool visitSchemeSrc(const nsCSPSchemeSrc& src) override
|
||||
{
|
||||
nsAutoString scheme;
|
||||
src.getScheme(scheme);
|
||||
|
||||
if (SchemeInList(scheme, allowedHostSchemes)) {
|
||||
FormatError("csp.error.missing-host", scheme);
|
||||
return false;
|
||||
}
|
||||
if (!SchemeInList(scheme, allowedSchemes)) {
|
||||
FormatError("csp.error.illegal-protocol", scheme);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool visitHostSrc(const nsCSPHostSrc& src) override
|
||||
{
|
||||
nsAutoString scheme, host;
|
||||
|
||||
src.getScheme(scheme);
|
||||
src.getHost(host);
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("https")) {
|
||||
if (!HostIsAllowed(host)) {
|
||||
FormatError("csp.error.illegal-host-wildcard", scheme);
|
||||
return false;
|
||||
}
|
||||
} else if (scheme.LowerCaseEqualsLiteral("moz-extension")) {
|
||||
// The CSP parser silently converts 'self' keywords to the origin
|
||||
// URL, so we need to reconstruct the URL to see if it was present.
|
||||
if (!mFoundSelf) {
|
||||
nsAutoString url(MOZ_UTF16("moz-extension://"));
|
||||
url.Append(host);
|
||||
|
||||
mFoundSelf = url.Equals(mURL);
|
||||
}
|
||||
|
||||
if (host.IsEmpty() || host.EqualsLiteral("*")) {
|
||||
FormatError("csp.error.missing-host", scheme);
|
||||
return false;
|
||||
}
|
||||
} else if (!SchemeInList(scheme, allowedSchemes)) {
|
||||
FormatError("csp.error.illegal-protocol", scheme);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool visitKeywordSrc(const nsCSPKeywordSrc& src) override
|
||||
{
|
||||
switch (src.getKeyword()) {
|
||||
case CSP_NONE:
|
||||
case CSP_SELF:
|
||||
case CSP_UNSAFE_EVAL:
|
||||
return true;
|
||||
|
||||
default:
|
||||
NS_ConvertASCIItoUTF16 keyword(CSP_EnumToKeyword(src.getKeyword()));
|
||||
|
||||
FormatError("csp.error.illegal-keyword", keyword);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool visitNonceSrc(const nsCSPNonceSrc& src) override
|
||||
{
|
||||
FormatError("csp.error.illegal-keyword", NS_LITERAL_STRING("'nonce-*'"));
|
||||
return false;
|
||||
};
|
||||
|
||||
bool visitHashSrc(const nsCSPHashSrc& src) override
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
// Accessors
|
||||
|
||||
inline nsAString& GetError()
|
||||
{
|
||||
return mError;
|
||||
};
|
||||
|
||||
inline bool FoundSelf()
|
||||
{
|
||||
return mFoundSelf;
|
||||
};
|
||||
|
||||
|
||||
// Formatters
|
||||
|
||||
template <typename... T>
|
||||
inline void FormatError(const char* aName, const T ...aParams)
|
||||
{
|
||||
const char16_t* params[] = { mDirective.get(), aParams.get()... };
|
||||
FormatErrorParams(aName, params, MOZ_ARRAY_LENGTH(params));
|
||||
};
|
||||
|
||||
private:
|
||||
// Validators
|
||||
|
||||
bool HostIsAllowed(nsAString& host)
|
||||
{
|
||||
if (host.First() == '*') {
|
||||
if (host.EqualsLiteral("*") || host[1] != '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
host.Cut(0, 2);
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService =
|
||||
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
|
||||
if (!tldService) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 cHost(host);
|
||||
nsAutoCString publicSuffix;
|
||||
|
||||
nsresult rv = tldService->GetPublicSuffixFromHost(cHost, publicSuffix);
|
||||
|
||||
return NS_SUCCEEDED(rv) && !cHost.Equals(publicSuffix);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool SchemeInList(nsAString& scheme, const char** schemes)
|
||||
{
|
||||
for (; *schemes; schemes++) {
|
||||
if (scheme.LowerCaseEqualsASCII(*schemes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
// Formatters
|
||||
|
||||
already_AddRefed<nsIStringBundle>
|
||||
GetStringBundle()
|
||||
{
|
||||
nsCOMPtr<nsIStringBundleService> sbs =
|
||||
mozilla::services::GetStringBundleService();
|
||||
NS_ENSURE_TRUE(sbs, nullptr);
|
||||
|
||||
nsCOMPtr<nsIStringBundle> stringBundle;
|
||||
sbs->CreateBundle("chrome://global/locale/extensions.properties",
|
||||
getter_AddRefs(stringBundle));
|
||||
|
||||
return stringBundle.forget();
|
||||
};
|
||||
|
||||
void FormatErrorParams(const char* aName, const char16_t** aParams, int32_t aLength)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIStringBundle> stringBundle = GetStringBundle();
|
||||
|
||||
if (stringBundle) {
|
||||
NS_ConvertASCIItoUTF16 name(aName);
|
||||
|
||||
rv = stringBundle->FormatStringFromName(name.get(), aParams, aLength,
|
||||
getter_Copies(mError));
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mError.AssignLiteral("An unexpected error occurred");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Data members
|
||||
|
||||
nsAutoString mURL;
|
||||
NS_ConvertASCIItoUTF16 mDirective;
|
||||
nsXPIDLString mError;
|
||||
|
||||
bool mFoundSelf;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a custom content security policy string for use by an add-on.
|
||||
* In particular, ensures that:
|
||||
*
|
||||
* - Both object-src and script-src directives are present, and meet
|
||||
* the policies required by the CSPValidator class
|
||||
*
|
||||
* - The script-src directive includes the source 'self'
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
AddonContentPolicy::ValidateAddonCSP(const nsAString& aPolicyString,
|
||||
nsAString& aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Validate against a randomly-generated extension origin.
|
||||
// There is no add-on-specific behavior in the CSP code, beyond the ability
|
||||
// for add-ons to specify a custom policy, but the parser requires a valid
|
||||
// origin in order to operate correctly.
|
||||
nsAutoString url(MOZ_UTF16("moz-extension://"));
|
||||
{
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen = services::GetUUIDGenerator();
|
||||
NS_ENSURE_TRUE(uuidgen, NS_ERROR_FAILURE);
|
||||
|
||||
nsID id;
|
||||
rv = uuidgen->GenerateUUIDInPlace(&id);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
char idString[NSID_LENGTH];
|
||||
id.ToProvidedString(idString);
|
||||
|
||||
MOZ_RELEASE_ASSERT(idString[0] == '{' && idString[NSID_LENGTH - 2] == '}',
|
||||
"UUID generator did not return a valid UUID");
|
||||
|
||||
url.AppendASCII(idString + 1, NSID_LENGTH - 3);
|
||||
}
|
||||
|
||||
|
||||
RefPtr<BasePrincipal> principal =
|
||||
BasePrincipal::CreateCodebasePrincipal(NS_ConvertUTF16toUTF8(url));
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->EnsureCSP(nullptr, getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
csp->AppendPolicy(aPolicyString, false, false);
|
||||
|
||||
const nsCSPPolicy* policy = csp->GetPolicy(0);
|
||||
if (!policy) {
|
||||
CSPValidator validator(url, nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE);
|
||||
aResult.Assign(validator.GetError());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool haveValidDefaultSrc = false;
|
||||
{
|
||||
CSPDirective directive = nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive);
|
||||
|
||||
haveValidDefaultSrc = policy->visitDirectiveSrcs(directive, &validator);
|
||||
}
|
||||
|
||||
aResult.SetIsVoid(true);
|
||||
{
|
||||
CSPDirective directive = nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive, !haveValidDefaultSrc);
|
||||
|
||||
if (!policy->visitDirectiveSrcs(directive, &validator)) {
|
||||
aResult.Assign(validator.GetError());
|
||||
} else if (!validator.FoundSelf()) {
|
||||
validator.FormatError("csp.error.missing-source", NS_LITERAL_STRING("'self'"));
|
||||
aResult.Assign(validator.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
if (aResult.IsVoid()) {
|
||||
CSPDirective directive = nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive, !haveValidDefaultSrc);
|
||||
|
||||
if (!policy->visitDirectiveSrcs(directive, &validator)) {
|
||||
aResult.Assign(validator.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIAddonPolicyService.h"
|
||||
|
||||
class AddonContentPolicy : public nsIContentPolicy
|
||||
class AddonContentPolicy : public nsIContentPolicy,
|
||||
public nsIAddonContentPolicy
|
||||
{
|
||||
protected:
|
||||
virtual ~AddonContentPolicy();
|
||||
@@ -16,4 +18,5 @@ public:
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPOLICY
|
||||
NS_DECL_NSIADDONCONTENTPOLICY
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/* 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 path = require("path")
|
||||
|
||||
module.exports = function(results) {
|
||||
for (let file of results) {
|
||||
let filePath = path.relative(".", file.filePath);
|
||||
for (let message of file.messages) {
|
||||
let status = message.message;
|
||||
|
||||
if ("ruleId" in message) {
|
||||
status = `${status} (${message.ruleId})`;
|
||||
}
|
||||
|
||||
let severity = message.severity == 1 ? "TEST-UNEXPECTED-WARNING"
|
||||
: "TEST-UNEXPECTED-ERROR";
|
||||
console.log(`${severity} | ${filePath}:${message.line}:${message.column} | ${status}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -955,7 +955,7 @@ CycleCollectedJSRuntime::EnqueuePromiseJobCallback(JSContext* aCx,
|
||||
MOZ_ASSERT(Get() == self);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aCx, aJob);
|
||||
self->GetPromiseMicroTaskQueue().push(runnable);
|
||||
self->DispatchToMicroTask(runnable);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1140,6 +1140,13 @@ CycleCollectedJSRuntime::GetPromiseMicroTaskQueue()
|
||||
return mPromiseMicroTaskQueue;
|
||||
}
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>>&
|
||||
CycleCollectedJSRuntime::GetDebuggerPromiseMicroTaskQueue()
|
||||
{
|
||||
MOZ_ASSERT(mJSRuntime);
|
||||
return mDebuggerPromiseMicroTaskQueue;
|
||||
}
|
||||
|
||||
nsCycleCollectionParticipant*
|
||||
CycleCollectedJSRuntime::GCThingParticipant()
|
||||
{
|
||||
@@ -1371,10 +1378,11 @@ CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
|
||||
// Step 4.1: Execute microtasks.
|
||||
if (NS_IsMainThread()) {
|
||||
nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
|
||||
Promise::PerformMicroTaskCheckpoint();
|
||||
} else {
|
||||
Promise::PerformWorkerMicroTaskCheckpoint();
|
||||
}
|
||||
|
||||
Promise::PerformMicroTaskCheckpoint();
|
||||
|
||||
// Step 4.2 Execute any events that were waiting for a stable state.
|
||||
ProcessStableStateQueue();
|
||||
}
|
||||
@@ -1652,6 +1660,15 @@ CycleCollectedJSRuntime::PrepareWaitingZonesForGC()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::DispatchToMicroTask(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
mPromiseMicroTaskQueue.push(aRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::EnvironmentPreparer::invoke(JS::HandleObject scope,
|
||||
js::ScriptEnvironmentPreparer::Closure& closure)
|
||||
|
||||
@@ -158,6 +158,9 @@ protected:
|
||||
return true; // Don't block context creation.
|
||||
}
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
|
||||
std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
|
||||
|
||||
private:
|
||||
void
|
||||
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
|
||||
@@ -285,6 +288,7 @@ public:
|
||||
void SetPendingException(nsIException* aException);
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
|
||||
std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
|
||||
|
||||
nsCycleCollectionParticipant* GCThingParticipant();
|
||||
nsCycleCollectionParticipant* ZoneParticipant();
|
||||
@@ -317,6 +321,10 @@ public:
|
||||
return mJSRuntime;
|
||||
}
|
||||
|
||||
protected:
|
||||
JSRuntime* MaybeRuntime() const { return mJSRuntime; }
|
||||
|
||||
public:
|
||||
// nsThread entrypoints
|
||||
virtual void BeforeProcessTask(bool aMightBlock) { };
|
||||
virtual void AfterProcessTask(uint32_t aRecursionDepth);
|
||||
@@ -348,6 +356,9 @@ public:
|
||||
// full GC.
|
||||
void PrepareWaitingZonesForGC();
|
||||
|
||||
// Queue an async microtask to the current main or worker thread.
|
||||
virtual void DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||
|
||||
// Storage for watching rejected promises waiting for some client to
|
||||
// consume their rejection.
|
||||
// We store values as `nsISupports` to avoid adding compile-time dependencies
|
||||
@@ -378,8 +389,6 @@ private:
|
||||
nsCOMPtr<nsIException> mPendingException;
|
||||
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
|
||||
|
||||
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
|
||||
|
||||
struct RunInMetastableStateData
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
|
||||
Reference in New Issue
Block a user