1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/ExtensionPolicyService.h"
7 #include "mozilla/extensions/WebExtensionContentScript.h"
8 #include "mozilla/extensions/WebExtensionPolicy.h"
9 
10 #include "mozilla/AddonManagerWebAPI.h"
11 #include "mozilla/ResultExtensions.h"
12 #include "nsEscape.h"
13 #include "nsIDocShell.h"
14 #include "nsIObserver.h"
15 #include "nsISubstitutingProtocolHandler.h"
16 #include "nsNetUtil.h"
17 #include "nsPrintfCString.h"
18 
19 namespace mozilla {
20 namespace extensions {
21 
22 using namespace dom;
23 
24 static const char kProto[] = "moz-extension";
25 
26 static const char kBackgroundPageHTMLStart[] =
27     "<!DOCTYPE html>\n\
28 <html>\n\
29   <head><meta charset=\"utf-8\"></head>\n\
30   <body>";
31 
32 static const char kBackgroundPageHTMLScript[] =
33     "\n\
34     <script type=\"text/javascript\" src=\"%s\"></script>";
35 
36 static const char kBackgroundPageHTMLEnd[] =
37     "\n\
38   <body>\n\
39 </html>";
40 
41 static const char kRestrictedDomainPref[] =
42     "extensions.webextensions.restrictedDomains";
43 
EPS()44 static inline ExtensionPolicyService& EPS() {
45   return ExtensionPolicyService::GetSingleton();
46 }
47 
Proto()48 static nsISubstitutingProtocolHandler* Proto() {
49   static nsCOMPtr<nsISubstitutingProtocolHandler> sHandler;
50 
51   if (MOZ_UNLIKELY(!sHandler)) {
52     nsCOMPtr<nsIIOService> ios = do_GetIOService();
53     MOZ_RELEASE_ASSERT(ios);
54 
55     nsCOMPtr<nsIProtocolHandler> handler;
56     ios->GetProtocolHandler(kProto, getter_AddRefs(handler));
57 
58     sHandler = do_QueryInterface(handler);
59     MOZ_RELEASE_ASSERT(sHandler);
60 
61     ClearOnShutdown(&sHandler);
62   }
63 
64   return sHandler;
65 }
66 
67 /*****************************************************************************
68  * WebExtensionPolicy
69  *****************************************************************************/
70 
WebExtensionPolicy(GlobalObject & aGlobal,const WebExtensionInit & aInit,ErrorResult & aRv)71 WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
72                                        const WebExtensionInit& aInit,
73                                        ErrorResult& aRv)
74     : mId(NS_AtomizeMainThread(aInit.mId)),
75       mHostname(aInit.mMozExtensionHostname),
76       mName(aInit.mName),
77       mContentSecurityPolicy(aInit.mContentSecurityPolicy),
78       mLocalizeCallback(aInit.mLocalizeCallback),
79       mPermissions(new AtomSet(aInit.mPermissions)),
80       mHostPermissions(aInit.mAllowedOrigins) {
81   mWebAccessiblePaths.AppendElements(aInit.mWebAccessibleResources);
82 
83   if (!aInit.mBackgroundScripts.IsNull()) {
84     mBackgroundScripts.SetValue().AppendElements(
85         aInit.mBackgroundScripts.Value());
86   }
87 
88   if (mContentSecurityPolicy.IsVoid()) {
89     EPS().DefaultCSP(mContentSecurityPolicy);
90   }
91 
92   mContentScripts.SetCapacity(aInit.mContentScripts.Length());
93   for (const auto& scriptInit : aInit.mContentScripts) {
94     // The activeTab permission is only for dynamically injected scripts,
95     // it cannot be used for declarative content scripts.
96     if (scriptInit.mHasActiveTabPermission) {
97       aRv.Throw(NS_ERROR_INVALID_ARG);
98       return;
99     }
100 
101     RefPtr<WebExtensionContentScript> contentScript =
102         new WebExtensionContentScript(*this, scriptInit, aRv);
103     if (aRv.Failed()) {
104       return;
105     }
106     mContentScripts.AppendElement(Move(contentScript));
107   }
108 
109   nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
110   if (NS_FAILED(rv)) {
111     aRv.Throw(rv);
112   }
113 }
114 
Constructor(GlobalObject & aGlobal,const WebExtensionInit & aInit,ErrorResult & aRv)115 already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::Constructor(
116     GlobalObject& aGlobal, const WebExtensionInit& aInit, ErrorResult& aRv) {
117   RefPtr<WebExtensionPolicy> policy =
118       new WebExtensionPolicy(aGlobal, aInit, aRv);
119   if (aRv.Failed()) {
120     return nullptr;
121   }
122   return policy.forget();
123 }
124 
GetActiveExtensions(dom::GlobalObject & aGlobal,nsTArray<RefPtr<WebExtensionPolicy>> & aResults)125 /* static */ void WebExtensionPolicy::GetActiveExtensions(
126     dom::GlobalObject& aGlobal,
127     nsTArray<RefPtr<WebExtensionPolicy>>& aResults) {
128   EPS().GetAll(aResults);
129 }
130 
GetByID(dom::GlobalObject & aGlobal,const nsAString & aID)131 /* static */ already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::GetByID(
132     dom::GlobalObject& aGlobal, const nsAString& aID) {
133   return do_AddRef(EPS().GetByID(aID));
134 }
135 
136 /* static */ already_AddRefed<WebExtensionPolicy>
GetByHostname(dom::GlobalObject & aGlobal,const nsACString & aHostname)137 WebExtensionPolicy::GetByHostname(dom::GlobalObject& aGlobal,
138                                   const nsACString& aHostname) {
139   return do_AddRef(EPS().GetByHost(aHostname));
140 }
141 
GetByURI(dom::GlobalObject & aGlobal,nsIURI * aURI)142 /* static */ already_AddRefed<WebExtensionPolicy> WebExtensionPolicy::GetByURI(
143     dom::GlobalObject& aGlobal, nsIURI* aURI) {
144   return do_AddRef(EPS().GetByURL(aURI));
145 }
146 
SetActive(bool aActive,ErrorResult & aRv)147 void WebExtensionPolicy::SetActive(bool aActive, ErrorResult& aRv) {
148   if (aActive == mActive) {
149     return;
150   }
151 
152   bool ok = aActive ? Enable() : Disable();
153 
154   if (!ok) {
155     aRv.Throw(NS_ERROR_UNEXPECTED);
156   }
157 }
158 
Enable()159 bool WebExtensionPolicy::Enable() {
160   MOZ_ASSERT(!mActive);
161 
162   if (!EPS().RegisterExtension(*this)) {
163     return false;
164   }
165 
166   Unused << Proto()->SetSubstitution(MozExtensionHostname(), mBaseURI);
167 
168   mActive = true;
169   return true;
170 }
171 
Disable()172 bool WebExtensionPolicy::Disable() {
173   MOZ_ASSERT(mActive);
174   MOZ_ASSERT(EPS().GetByID(Id()) == this);
175 
176   if (!EPS().UnregisterExtension(*this)) {
177     return false;
178   }
179 
180   Unused << Proto()->SetSubstitution(MozExtensionHostname(), nullptr);
181 
182   mActive = false;
183   return true;
184 }
185 
GetURL(const nsAString & aPath,nsAString & aResult,ErrorResult & aRv) const186 void WebExtensionPolicy::GetURL(const nsAString& aPath, nsAString& aResult,
187                                 ErrorResult& aRv) const {
188   auto result = GetURL(aPath);
189   if (result.isOk()) {
190     aResult = result.unwrap();
191   } else {
192     aRv.Throw(result.unwrapErr());
193   }
194 }
195 
GetURL(const nsAString & aPath) const196 Result<nsString, nsresult> WebExtensionPolicy::GetURL(
197     const nsAString& aPath) const {
198   nsPrintfCString spec("%s://%s/", kProto, mHostname.get());
199 
200   nsCOMPtr<nsIURI> uri;
201   MOZ_TRY(NS_NewURI(getter_AddRefs(uri), spec));
202 
203   MOZ_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec));
204 
205   return NS_ConvertUTF8toUTF16(spec);
206 }
207 
RegisterContentScript(WebExtensionContentScript & script,ErrorResult & aRv)208 void WebExtensionPolicy::RegisterContentScript(
209     WebExtensionContentScript& script, ErrorResult& aRv) {
210   // Raise an "invalid argument" error if the script is not related to
211   // the expected extension or if it is already registered.
212   if (script.mExtension != this || mContentScripts.Contains(&script)) {
213     aRv.Throw(NS_ERROR_INVALID_ARG);
214     return;
215   }
216 
217   RefPtr<WebExtensionContentScript> newScript = &script;
218 
219   if (!mContentScripts.AppendElement(Move(newScript), fallible)) {
220     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
221     return;
222   }
223 
224   WebExtensionPolicyBinding::ClearCachedContentScriptsValue(this);
225 }
226 
UnregisterContentScript(const WebExtensionContentScript & script,ErrorResult & aRv)227 void WebExtensionPolicy::UnregisterContentScript(
228     const WebExtensionContentScript& script, ErrorResult& aRv) {
229   if (script.mExtension != this || !mContentScripts.RemoveElement(&script)) {
230     aRv.Throw(NS_ERROR_INVALID_ARG);
231     return;
232   }
233 
234   WebExtensionPolicyBinding::ClearCachedContentScriptsValue(this);
235 }
236 
UseRemoteWebExtensions(GlobalObject & aGlobal)237 /* static */ bool WebExtensionPolicy::UseRemoteWebExtensions(
238     GlobalObject& aGlobal) {
239   return EPS().UseRemoteExtensions();
240 }
241 
IsExtensionProcess(GlobalObject & aGlobal)242 /* static */ bool WebExtensionPolicy::IsExtensionProcess(
243     GlobalObject& aGlobal) {
244   return EPS().IsExtensionProcess();
245 }
246 
247 namespace {
248 /**
249  * Maintains a dynamically updated AtomSet based on the comma-separated
250  * values in the given string pref.
251  */
252 class AtomSetPref : public nsIObserver, public nsSupportsWeakReference {
253  public:
254   NS_DECL_ISUPPORTS
255   NS_DECL_NSIOBSERVER
256 
Create(const char * aPref)257   static already_AddRefed<AtomSetPref> Create(const char* aPref) {
258     RefPtr<AtomSetPref> self = new AtomSetPref(aPref);
259     Preferences::AddWeakObserver(self, aPref);
260     return self.forget();
261   }
262 
263   const AtomSet& Get() const;
264 
Contains(const nsAtom * aAtom) const265   bool Contains(const nsAtom* aAtom) const { return Get().Contains(aAtom); }
266 
267  protected:
268   virtual ~AtomSetPref() = default;
269 
AtomSetPref(const char * aPref)270   explicit AtomSetPref(const char* aPref) : mPref(aPref) {}
271 
272  private:
273   mutable RefPtr<AtomSet> mAtomSet;
274   const char* mPref;
275 };
276 
Get() const277 const AtomSet& AtomSetPref::Get() const {
278   if (!mAtomSet) {
279     nsAutoCString eltsString;
280     Unused << Preferences::GetCString(mPref, eltsString);
281 
282     AutoTArray<nsString, 32> elts;
283     for (const nsACString& elt : eltsString.Split(',')) {
284       elts.AppendElement(NS_ConvertUTF8toUTF16(elt));
285       elts.LastElement().StripWhitespace();
286     }
287     mAtomSet = new AtomSet(elts);
288   }
289 
290   return *mAtomSet;
291 }
292 
293 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)294 AtomSetPref::Observe(nsISupports* aSubject, const char* aTopic,
295                      const char16_t* aData) {
296   mAtomSet = nullptr;
297   return NS_OK;
298 }
299 
300 NS_IMPL_ISUPPORTS(AtomSetPref, nsIObserver, nsISupportsWeakReference)
301 };  // namespace
302 
IsRestrictedDoc(const DocInfo & aDoc)303 /* static */ bool WebExtensionPolicy::IsRestrictedDoc(const DocInfo& aDoc) {
304   // With the exception of top-level about:blank documents with null
305   // principals, we never match documents that have non-codebase principals,
306   // including those with null principals or system principals.
307   if (aDoc.Principal() && !aDoc.Principal()->GetIsCodebasePrincipal()) {
308     return true;
309   }
310 
311   return IsRestrictedURI(aDoc.PrincipalURL());
312 }
313 
IsRestrictedURI(const URLInfo & aURI)314 /* static */ bool WebExtensionPolicy::IsRestrictedURI(const URLInfo& aURI) {
315   static RefPtr<AtomSetPref> domains;
316   if (!domains) {
317     domains = AtomSetPref::Create(kRestrictedDomainPref);
318     ClearOnShutdown(&domains);
319   }
320 
321   if (domains->Contains(aURI.HostAtom())) {
322     return true;
323   }
324 
325   if (AddonManagerWebAPI::IsValidSite(aURI.URI())) {
326     return true;
327   }
328 
329   return false;
330 }
331 
BackgroundPageHTML() const332 nsCString WebExtensionPolicy::BackgroundPageHTML() const {
333   nsAutoCString result;
334 
335   if (mBackgroundScripts.IsNull()) {
336     result.SetIsVoid(true);
337     return result;
338   }
339 
340   result.AppendLiteral(kBackgroundPageHTMLStart);
341 
342   for (auto& script : mBackgroundScripts.Value()) {
343     nsCString escaped;
344     nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(script), escaped);
345 
346     result.AppendPrintf(kBackgroundPageHTMLScript, escaped.get());
347   }
348 
349   result.AppendLiteral(kBackgroundPageHTMLEnd);
350   return result;
351 }
352 
Localize(const nsAString & aInput,nsString & aOutput) const353 void WebExtensionPolicy::Localize(const nsAString& aInput,
354                                   nsString& aOutput) const {
355   mLocalizeCallback->Call(aInput, aOutput);
356 }
357 
WrapObject(JSContext * aCx,JS::HandleObject aGivenProto)358 JSObject* WebExtensionPolicy::WrapObject(JSContext* aCx,
359                                          JS::HandleObject aGivenProto) {
360   return WebExtensionPolicyBinding::Wrap(aCx, this, aGivenProto);
361 }
362 
GetContentScripts(nsTArray<RefPtr<WebExtensionContentScript>> & aScripts) const363 void WebExtensionPolicy::GetContentScripts(
364     nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) const {
365   aScripts.AppendElements(mContentScripts);
366 }
367 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy,mParent,mLocalizeCallback,mHostPermissions,mWebAccessiblePaths,mContentScripts)368 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
369                                       mLocalizeCallback, mHostPermissions,
370                                       mWebAccessiblePaths, mContentScripts)
371 
372 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
373   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
374   NS_INTERFACE_MAP_ENTRY(nsISupports)
375 NS_INTERFACE_MAP_END
376 
377 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
378 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
379 
380 /*****************************************************************************
381  * WebExtensionContentScript
382  *****************************************************************************/
383 
384 /* static */ already_AddRefed<WebExtensionContentScript>
385 WebExtensionContentScript::Constructor(GlobalObject& aGlobal,
386                                        WebExtensionPolicy& aExtension,
387                                        const ContentScriptInit& aInit,
388                                        ErrorResult& aRv) {
389   RefPtr<WebExtensionContentScript> script =
390       new WebExtensionContentScript(aExtension, aInit, aRv);
391   if (aRv.Failed()) {
392     return nullptr;
393   }
394   return script.forget();
395 }
396 
WebExtensionContentScript(WebExtensionPolicy & aExtension,const ContentScriptInit & aInit,ErrorResult & aRv)397 WebExtensionContentScript::WebExtensionContentScript(
398     WebExtensionPolicy& aExtension, const ContentScriptInit& aInit,
399     ErrorResult& aRv)
400     : mExtension(&aExtension),
401       mHasActiveTabPermission(aInit.mHasActiveTabPermission),
402       mRestricted(!aExtension.HasPermission(nsGkAtoms::mozillaAddons)),
403       mMatches(aInit.mMatches),
404       mExcludeMatches(aInit.mExcludeMatches),
405       mCssPaths(aInit.mCssPaths),
406       mJsPaths(aInit.mJsPaths),
407       mRunAt(aInit.mRunAt),
408       mAllFrames(aInit.mAllFrames),
409       mFrameID(aInit.mFrameID),
410       mMatchAboutBlank(aInit.mMatchAboutBlank) {
411   if (!aInit.mIncludeGlobs.IsNull()) {
412     mIncludeGlobs.SetValue().AppendElements(aInit.mIncludeGlobs.Value());
413   }
414 
415   if (!aInit.mExcludeGlobs.IsNull()) {
416     mExcludeGlobs.SetValue().AppendElements(aInit.mExcludeGlobs.Value());
417   }
418 }
419 
Matches(const DocInfo & aDoc) const420 bool WebExtensionContentScript::Matches(const DocInfo& aDoc) const {
421   if (!mFrameID.IsNull()) {
422     if (aDoc.FrameID() != mFrameID.Value()) {
423       return false;
424     }
425   } else {
426     if (!mAllFrames && !aDoc.IsTopLevel()) {
427       return false;
428     }
429   }
430 
431   if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
432     return false;
433   }
434 
435   // Top-level about:blank is a special case. We treat it as a match if
436   // matchAboutBlank is true and it has the null principal. In all other
437   // cases, we test the URL of the principal that it inherits.
438   if (mMatchAboutBlank && aDoc.IsTopLevel() &&
439       aDoc.URL().Spec().EqualsLiteral("about:blank") && aDoc.Principal() &&
440       aDoc.Principal()->GetIsNullPrincipal()) {
441     return true;
442   }
443 
444   if (mRestricted && mExtension->IsRestrictedDoc(aDoc)) {
445     return false;
446   }
447 
448   auto& urlinfo = aDoc.PrincipalURL();
449   if (mHasActiveTabPermission && aDoc.ShouldMatchActiveTabPermission() &&
450       MatchPattern::MatchesAllURLs(urlinfo)) {
451     return true;
452   }
453 
454   return MatchesURI(urlinfo);
455 }
456 
MatchesURI(const URLInfo & aURL) const457 bool WebExtensionContentScript::MatchesURI(const URLInfo& aURL) const {
458   if (!mMatches->Matches(aURL)) {
459     return false;
460   }
461 
462   if (mExcludeMatches && mExcludeMatches->Matches(aURL)) {
463     return false;
464   }
465 
466   if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
467     return false;
468   }
469 
470   if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
471     return false;
472   }
473 
474   if (mRestricted && mExtension->IsRestrictedURI(aURL)) {
475     return false;
476   }
477 
478   return true;
479 }
480 
WrapObject(JSContext * aCx,JS::HandleObject aGivenProto)481 JSObject* WebExtensionContentScript::WrapObject(JSContext* aCx,
482                                                 JS::HandleObject aGivenProto) {
483   return WebExtensionContentScriptBinding::Wrap(aCx, this, aGivenProto);
484 }
485 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionContentScript,mMatches,mExcludeMatches,mIncludeGlobs,mExcludeGlobs,mExtension)486 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionContentScript, mMatches,
487                                       mExcludeMatches, mIncludeGlobs,
488                                       mExcludeGlobs, mExtension)
489 
490 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionContentScript)
491   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
492   NS_INTERFACE_MAP_ENTRY(nsISupports)
493 NS_INTERFACE_MAP_END
494 
495 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionContentScript)
496 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionContentScript)
497 
498 /*****************************************************************************
499  * DocInfo
500  *****************************************************************************/
501 
502 DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
503     : mURL(aURL), mObj(AsVariant(aLoadInfo)) {}
504 
DocInfo(nsPIDOMWindowOuter * aWindow)505 DocInfo::DocInfo(nsPIDOMWindowOuter* aWindow)
506     : mURL(aWindow->GetDocumentURI()), mObj(AsVariant(aWindow)) {}
507 
IsTopLevel() const508 bool DocInfo::IsTopLevel() const {
509   if (mIsTopLevel.isNothing()) {
510     struct Matcher {
511       bool match(Window aWin) { return aWin->IsTopLevelWindow(); }
512       bool match(LoadInfo aLoadInfo) { return aLoadInfo->GetIsTopLevelLoad(); }
513     };
514     mIsTopLevel.emplace(mObj.match(Matcher()));
515   }
516   return mIsTopLevel.ref();
517 }
518 
WindowShouldMatchActiveTab(nsPIDOMWindowOuter * aWin)519 bool WindowShouldMatchActiveTab(nsPIDOMWindowOuter* aWin) {
520   if (aWin->IsTopLevelWindow()) {
521     return true;
522   }
523 
524   nsIDocShell* docshell = aWin->GetDocShell();
525   if (!docshell || docshell->GetCreatedDynamically()) {
526     return false;
527   }
528 
529   nsIDocument* doc = aWin->GetExtantDoc();
530   if (!doc) {
531     return false;
532   }
533 
534   nsIChannel* channel = doc->GetChannel();
535   if (!channel) {
536     return false;
537   }
538 
539   nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
540 
541   if (!loadInfo) {
542     return false;
543   }
544 
545   if (!loadInfo->GetOriginalFrameSrcLoad()) {
546     return false;
547   }
548 
549   nsCOMPtr<nsPIDOMWindowOuter> parent = aWin->GetParent();
550   MOZ_ASSERT(parent != nullptr);
551   return WindowShouldMatchActiveTab(parent);
552 }
553 
ShouldMatchActiveTabPermission() const554 bool DocInfo::ShouldMatchActiveTabPermission() const {
555   struct Matcher {
556     bool match(Window aWin) { return WindowShouldMatchActiveTab(aWin); }
557     bool match(LoadInfo aLoadInfo) { return false; }
558   };
559   return mObj.match(Matcher());
560 }
561 
FrameID() const562 uint64_t DocInfo::FrameID() const {
563   if (mFrameID.isNothing()) {
564     if (IsTopLevel()) {
565       mFrameID.emplace(0);
566     } else {
567       struct Matcher {
568         uint64_t match(Window aWin) { return aWin->WindowID(); }
569         uint64_t match(LoadInfo aLoadInfo) {
570           return aLoadInfo->GetOuterWindowID();
571         }
572       };
573       mFrameID.emplace(mObj.match(Matcher()));
574     }
575   }
576   return mFrameID.ref();
577 }
578 
Principal() const579 nsIPrincipal* DocInfo::Principal() const {
580   if (mPrincipal.isNothing()) {
581     struct Matcher {
582       explicit Matcher(const DocInfo& aThis) : mThis(aThis) {}
583       const DocInfo& mThis;
584 
585       nsIPrincipal* match(Window aWin) {
586         nsCOMPtr<nsIDocument> doc = aWin->GetDoc();
587         return doc->NodePrincipal();
588       }
589       nsIPrincipal* match(LoadInfo aLoadInfo) {
590         if (!(mThis.URL().InheritsPrincipal() ||
591               aLoadInfo->GetForceInheritPrincipal())) {
592           return nullptr;
593         }
594         if (auto principal = aLoadInfo->PrincipalToInherit()) {
595           return principal;
596         }
597         return aLoadInfo->TriggeringPrincipal();
598       }
599     };
600     mPrincipal.emplace(mObj.match(Matcher(*this)));
601   }
602   return mPrincipal.ref();
603 }
604 
PrincipalURL() const605 const URLInfo& DocInfo::PrincipalURL() const {
606   if (!(Principal() && Principal()->GetIsCodebasePrincipal())) {
607     return URL();
608   }
609 
610   if (mPrincipalURL.isNothing()) {
611     nsIPrincipal* prin = Principal();
612     nsCOMPtr<nsIURI> uri;
613     if (NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
614       MOZ_DIAGNOSTIC_ASSERT(uri);
615       mPrincipalURL.emplace(uri);
616     } else {
617       mPrincipalURL.emplace(URL());
618     }
619   }
620 
621   return mPrincipalURL.ref();
622 }
623 
624 }  // namespace extensions
625 }  // namespace mozilla
626