LCOV - code coverage report
Current view: top level - toolkit/components/extensions - WebExtensionPolicy.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 212 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 53 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "nsEscape.h"
      12             : #include "nsISubstitutingProtocolHandler.h"
      13             : #include "nsNetUtil.h"
      14             : #include "nsPrintfCString.h"
      15             : 
      16             : namespace mozilla {
      17             : namespace extensions {
      18             : 
      19             : using namespace dom;
      20             : 
      21             : static inline Result<Ok, nsresult>
      22             : WrapNSResult(PRStatus aRv)
      23             : {
      24             :   if (aRv != PR_SUCCESS) {
      25             :     return Err(NS_ERROR_FAILURE);
      26             :   }
      27             :   return Ok();
      28             : }
      29             : 
      30             : static inline Result<Ok, nsresult>
      31           0 : WrapNSResult(nsresult aRv)
      32             : {
      33           0 :   if (NS_FAILED(aRv)) {
      34           0 :     return Err(aRv);
      35             :   }
      36           0 :   return Ok();
      37             : }
      38             : 
      39             : #define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr))
      40             : 
      41             : static const char kProto[] = "moz-extension";
      42             : 
      43             : static const char kBackgroundPageHTMLStart[] = "<!DOCTYPE html>\n\
      44             : <html>\n\
      45             :   <head><meta charset=\"utf-8\"></head>\n\
      46             :   <body>";
      47             : 
      48             : static const char kBackgroundPageHTMLScript[] = "\n\
      49             :     <script type=\"text/javascript\" src=\"%s\"></script>";
      50             : 
      51             : static const char kBackgroundPageHTMLEnd[] = "\n\
      52             :   <body>\n\
      53             : </html>";
      54             : 
      55           0 : class EscapeHTML final : public nsAdoptingCString
      56             : {
      57             : public:
      58           0 :   explicit EscapeHTML(const nsACString& str)
      59           0 :     : nsAdoptingCString(nsEscapeHTML(str.BeginReading()))
      60           0 :   {}
      61             : };
      62             : 
      63             : 
      64             : static inline ExtensionPolicyService&
      65           0 : EPS()
      66             : {
      67           0 :   return ExtensionPolicyService::GetSingleton();
      68             : }
      69             : 
      70             : static nsISubstitutingProtocolHandler*
      71           0 : Proto()
      72             : {
      73           0 :   static nsCOMPtr<nsISubstitutingProtocolHandler> sHandler;
      74             : 
      75           0 :   if (MOZ_UNLIKELY(!sHandler)) {
      76           0 :     nsCOMPtr<nsIIOService> ios = do_GetIOService();
      77           0 :     MOZ_RELEASE_ASSERT(ios);
      78             : 
      79           0 :     nsCOMPtr<nsIProtocolHandler> handler;
      80           0 :     ios->GetProtocolHandler(kProto, getter_AddRefs(handler));
      81             : 
      82           0 :     sHandler = do_QueryInterface(handler);
      83           0 :     MOZ_RELEASE_ASSERT(sHandler);
      84             : 
      85           0 :     ClearOnShutdown(&sHandler);
      86             :   }
      87             : 
      88           0 :   return sHandler;
      89             : }
      90             : 
      91             : 
      92             : /*****************************************************************************
      93             :  * WebExtensionPolicy
      94             :  *****************************************************************************/
      95             : 
      96           0 : WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
      97             :                                        const WebExtensionInit& aInit,
      98           0 :                                        ErrorResult& aRv)
      99           0 :   : mId(NS_AtomizeMainThread(aInit.mId))
     100             :   , mHostname(aInit.mMozExtensionHostname)
     101             :   , mContentSecurityPolicy(aInit.mContentSecurityPolicy)
     102             :   , mLocalizeCallback(aInit.mLocalizeCallback)
     103           0 :   , mPermissions(new AtomSet(aInit.mPermissions))
     104           0 :   , mHostPermissions(aInit.mAllowedOrigins)
     105             : {
     106           0 :   mWebAccessiblePaths.AppendElements(aInit.mWebAccessibleResources);
     107             : 
     108           0 :   if (!aInit.mBackgroundScripts.IsNull()) {
     109           0 :     mBackgroundScripts.SetValue().AppendElements(aInit.mBackgroundScripts.Value());
     110             :   }
     111             : 
     112           0 :   if (mContentSecurityPolicy.IsVoid()) {
     113           0 :     EPS().DefaultCSP(mContentSecurityPolicy);
     114             :   }
     115             : 
     116           0 :   mContentScripts.SetCapacity(aInit.mContentScripts.Length());
     117           0 :   for (const auto& scriptInit : aInit.mContentScripts) {
     118             :     RefPtr<WebExtensionContentScript> contentScript =
     119           0 :       new WebExtensionContentScript(*this, scriptInit, aRv);
     120           0 :     if (aRv.Failed()) {
     121           0 :       return;
     122             :     }
     123           0 :     mContentScripts.AppendElement(Move(contentScript));
     124             :   }
     125             : 
     126           0 :   nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
     127           0 :   if (NS_FAILED(rv)) {
     128           0 :     aRv.Throw(rv);
     129             :   }
     130             : }
     131             : 
     132             : already_AddRefed<WebExtensionPolicy>
     133           0 : WebExtensionPolicy::Constructor(GlobalObject& aGlobal,
     134             :                                 const WebExtensionInit& aInit,
     135             :                                 ErrorResult& aRv)
     136             : {
     137           0 :   RefPtr<WebExtensionPolicy> policy = new WebExtensionPolicy(aGlobal, aInit, aRv);
     138           0 :   if (aRv.Failed()) {
     139           0 :     return nullptr;
     140             :   }
     141           0 :   return policy.forget();
     142             : }
     143             : 
     144             : 
     145             : /* static */ void
     146           0 : WebExtensionPolicy::GetActiveExtensions(dom::GlobalObject& aGlobal,
     147             :                                         nsTArray<RefPtr<WebExtensionPolicy>>& aResults)
     148             : {
     149           0 :   EPS().GetAll(aResults);
     150           0 : }
     151             : 
     152             : /* static */ already_AddRefed<WebExtensionPolicy>
     153           0 : WebExtensionPolicy::GetByID(dom::GlobalObject& aGlobal, const nsAString& aID)
     154             : {
     155           0 :   return do_AddRef(EPS().GetByID(aID));
     156             : }
     157             : 
     158             : /* static */ already_AddRefed<WebExtensionPolicy>
     159           0 : WebExtensionPolicy::GetByHostname(dom::GlobalObject& aGlobal, const nsACString& aHostname)
     160             : {
     161           0 :   return do_AddRef(EPS().GetByHost(aHostname));
     162             : }
     163             : 
     164             : /* static */ already_AddRefed<WebExtensionPolicy>
     165           0 : WebExtensionPolicy::GetByURI(dom::GlobalObject& aGlobal, nsIURI* aURI)
     166             : {
     167           0 :   return do_AddRef(EPS().GetByURL(aURI));
     168             : }
     169             : 
     170             : 
     171             : void
     172           0 : WebExtensionPolicy::SetActive(bool aActive, ErrorResult& aRv)
     173             : {
     174           0 :   if (aActive == mActive) {
     175           0 :     return;
     176             :   }
     177             : 
     178           0 :   bool ok = aActive ? Enable() : Disable();
     179             : 
     180           0 :   if (!ok) {
     181           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
     182             :   }
     183             : }
     184             : 
     185             : bool
     186           0 : WebExtensionPolicy::Enable()
     187             : {
     188           0 :   MOZ_ASSERT(!mActive);
     189             : 
     190           0 :   if (!EPS().RegisterExtension(*this)) {
     191           0 :     return false;
     192             :   }
     193             : 
     194           0 :   Unused << Proto()->SetSubstitution(MozExtensionHostname(), mBaseURI);
     195             : 
     196           0 :   mActive = true;
     197           0 :   return true;
     198             : }
     199             : 
     200             : bool
     201           0 : WebExtensionPolicy::Disable()
     202             : {
     203           0 :   MOZ_ASSERT(mActive);
     204           0 :   MOZ_ASSERT(EPS().GetByID(Id()) == this);
     205             : 
     206           0 :   if (!EPS().UnregisterExtension(*this)) {
     207           0 :     return false;
     208             :   }
     209             : 
     210           0 :   Unused << Proto()->SetSubstitution(MozExtensionHostname(), nullptr);
     211             : 
     212           0 :   mActive = false;
     213           0 :   return true;
     214             : }
     215             : 
     216             : void
     217           0 : WebExtensionPolicy::GetURL(const nsAString& aPath,
     218             :                            nsAString& aResult,
     219             :                            ErrorResult& aRv) const
     220             : {
     221           0 :   auto result = GetURL(aPath);
     222           0 :   if (result.isOk()) {
     223           0 :     aResult = result.unwrap();
     224             :   } else {
     225           0 :     aRv.Throw(result.unwrapErr());
     226             :   }
     227           0 : }
     228             : 
     229             : Result<nsString, nsresult>
     230           0 : WebExtensionPolicy::GetURL(const nsAString& aPath) const
     231             : {
     232           0 :   nsPrintfCString spec("%s://%s/", kProto, mHostname.get());
     233             : 
     234           0 :   nsCOMPtr<nsIURI> uri;
     235           0 :   NS_TRY(NS_NewURI(getter_AddRefs(uri), spec));
     236             : 
     237           0 :   NS_TRY(uri->Resolve(NS_ConvertUTF16toUTF8(aPath), spec));
     238             : 
     239           0 :   return NS_ConvertUTF8toUTF16(spec);
     240             : }
     241             : 
     242             : /* static */ bool
     243           0 : WebExtensionPolicy::IsExtensionProcess(GlobalObject& aGlobal)
     244             : {
     245           0 :   return EPS().IsExtensionProcess();
     246             : }
     247             : 
     248             : nsCString
     249           0 : WebExtensionPolicy::BackgroundPageHTML() const
     250             : {
     251           0 :   nsAutoCString result;
     252             : 
     253           0 :   if (mBackgroundScripts.IsNull()) {
     254           0 :     result.SetIsVoid(true);
     255           0 :     return result;
     256             :   }
     257             : 
     258           0 :   result.AppendLiteral(kBackgroundPageHTMLStart);
     259             : 
     260           0 :   for (auto& script : mBackgroundScripts.Value()) {
     261           0 :     EscapeHTML escaped{NS_ConvertUTF16toUTF8(script)};
     262             : 
     263           0 :     result.AppendPrintf(kBackgroundPageHTMLScript, escaped.get());
     264             :   }
     265             : 
     266           0 :   result.AppendLiteral(kBackgroundPageHTMLEnd);
     267           0 :   return result;
     268             : }
     269             : 
     270             : void
     271           0 : WebExtensionPolicy::Localize(const nsAString& aInput, nsString& aOutput) const
     272             : {
     273           0 :   mLocalizeCallback->Call(aInput, aOutput);
     274           0 : }
     275             : 
     276             : 
     277             : JSObject*
     278           0 : WebExtensionPolicy::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
     279             : {
     280           0 :   return WebExtensionPolicyBinding::Wrap(aCx, this, aGivenProto);
     281             : }
     282             : 
     283             : void
     284           0 : WebExtensionPolicy::GetContentScripts(nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) const
     285             : {
     286           0 :   aScripts.AppendElements(mContentScripts);
     287           0 : }
     288             : 
     289             : 
     290           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
     291             :                                       mLocalizeCallback,
     292             :                                       mHostPermissions,
     293             :                                       mWebAccessiblePaths,
     294             :                                       mContentScripts)
     295             : 
     296           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
     297           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     298           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     299           0 : NS_INTERFACE_MAP_END
     300             : 
     301           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
     302           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
     303             : 
     304             : 
     305             : /*****************************************************************************
     306             :  * WebExtensionContentScript
     307             :  *****************************************************************************/
     308             : 
     309             : /* static */ already_AddRefed<WebExtensionContentScript>
     310           0 : WebExtensionContentScript::Constructor(GlobalObject& aGlobal,
     311             :                                        WebExtensionPolicy& aExtension,
     312             :                                        const ContentScriptInit& aInit,
     313             :                                        ErrorResult& aRv)
     314             : {
     315           0 :   RefPtr<WebExtensionContentScript> script = new WebExtensionContentScript(aExtension, aInit, aRv);
     316           0 :   if (aRv.Failed()) {
     317           0 :     return nullptr;
     318             :   }
     319           0 :   return script.forget();
     320             : }
     321             : 
     322           0 : WebExtensionContentScript::WebExtensionContentScript(WebExtensionPolicy& aExtension,
     323             :                                                      const ContentScriptInit& aInit,
     324           0 :                                                      ErrorResult& aRv)
     325             :   : mExtension(&aExtension)
     326             :   , mMatches(aInit.mMatches)
     327             :   , mExcludeMatches(aInit.mExcludeMatches)
     328             :   , mCssPaths(aInit.mCssPaths)
     329             :   , mJsPaths(aInit.mJsPaths)
     330           0 :   , mRunAt(aInit.mRunAt)
     331           0 :   , mAllFrames(aInit.mAllFrames)
     332             :   , mFrameID(aInit.mFrameID)
     333           0 :   , mMatchAboutBlank(aInit.mMatchAboutBlank)
     334             : {
     335           0 :   if (!aInit.mIncludeGlobs.IsNull()) {
     336           0 :     mIncludeGlobs.SetValue().AppendElements(aInit.mIncludeGlobs.Value());
     337             :   }
     338             : 
     339           0 :   if (!aInit.mExcludeGlobs.IsNull()) {
     340           0 :     mExcludeGlobs.SetValue().AppendElements(aInit.mExcludeGlobs.Value());
     341             :   }
     342           0 : }
     343             : 
     344             : 
     345             : bool
     346           0 : WebExtensionContentScript::Matches(const DocInfo& aDoc) const
     347             : {
     348           0 :   if (!mFrameID.IsNull()) {
     349           0 :     if (aDoc.FrameID() != mFrameID.Value()) {
     350           0 :       return false;
     351             :     }
     352             :   } else {
     353           0 :     if (!mAllFrames && !aDoc.IsTopLevel()) {
     354           0 :       return false;
     355             :     }
     356             :   }
     357             : 
     358           0 :   if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
     359           0 :     return false;
     360             :   }
     361             : 
     362             :   // Top-level about:blank is a special case. We treat it as a match if
     363             :   // matchAboutBlank is true and it has the null principal. In all other
     364             :   // cases, we test the URL of the principal that it inherits.
     365           0 :   if (mMatchAboutBlank && aDoc.IsTopLevel() &&
     366           0 :       aDoc.URL().Spec().EqualsLiteral("about:blank") &&
     367           0 :       aDoc.Principal() && aDoc.Principal()->GetIsNullPrincipal()) {
     368           0 :     return true;
     369             :   }
     370             : 
     371             :   // With the exception of top-level about:blank documents with null
     372             :   // principals, we never match documents that have non-codebase principals,
     373             :   // including those with null principals or system principals.
     374           0 :   if (aDoc.Principal() && !aDoc.Principal()->GetIsCodebasePrincipal()) {
     375           0 :     return false;
     376             :   }
     377             : 
     378           0 :   return MatchesURI(aDoc.PrincipalURL());
     379             : }
     380             : 
     381             : bool
     382           0 : WebExtensionContentScript::MatchesURI(const URLInfo& aURL) const
     383             : {
     384           0 :   if (!mMatches->Matches(aURL)) {
     385           0 :     return false;
     386             :   }
     387             : 
     388           0 :   if (mExcludeMatches && mExcludeMatches->Matches(aURL)) {
     389           0 :     return false;
     390             :   }
     391             : 
     392           0 :   if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
     393           0 :     return false;
     394             :   }
     395             : 
     396           0 :   if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
     397           0 :     return false;
     398             :   }
     399             : 
     400           0 :   if (AddonManagerWebAPI::IsValidSite(aURL.URI())) {
     401           0 :     return false;
     402             :   }
     403             : 
     404           0 :   return true;
     405             : }
     406             : 
     407             : 
     408             : JSObject*
     409           0 : WebExtensionContentScript::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
     410             : {
     411           0 :   return WebExtensionContentScriptBinding::Wrap(aCx, this, aGivenProto);
     412             : }
     413             : 
     414             : 
     415           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionContentScript,
     416             :                                       mMatches, mExcludeMatches,
     417             :                                       mIncludeGlobs, mExcludeGlobs,
     418             :                                       mExtension)
     419             : 
     420           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionContentScript)
     421           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     422           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     423           0 : NS_INTERFACE_MAP_END
     424             : 
     425           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionContentScript)
     426           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionContentScript)
     427             : 
     428             : 
     429             : /*****************************************************************************
     430             :  * DocInfo
     431             :  *****************************************************************************/
     432             : 
     433           0 : DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
     434             :   : mURL(aURL)
     435           0 :   , mObj(AsVariant(aLoadInfo))
     436           0 : {}
     437             : 
     438           0 : DocInfo::DocInfo(nsPIDOMWindowOuter* aWindow)
     439             :   : mURL(aWindow->GetDocumentURI())
     440           0 :   , mObj(AsVariant(aWindow))
     441           0 : {}
     442             : 
     443             : bool
     444           0 : DocInfo::IsTopLevel() const
     445             : {
     446           0 :   if (mIsTopLevel.isNothing()) {
     447             :     struct Matcher
     448             :     {
     449           0 :       bool match(Window aWin) { return aWin->IsTopLevelWindow(); }
     450           0 :       bool match(LoadInfo aLoadInfo) { return aLoadInfo->GetIsTopLevelLoad(); }
     451             :     };
     452           0 :     mIsTopLevel.emplace(mObj.match(Matcher()));
     453             :   }
     454           0 :   return mIsTopLevel.ref();
     455             : }
     456             : 
     457             : uint64_t
     458           0 : DocInfo::FrameID() const
     459             : {
     460           0 :   if (mFrameID.isNothing()) {
     461           0 :     if (IsTopLevel()) {
     462           0 :       mFrameID.emplace(0);
     463             :     } else {
     464             :       struct Matcher
     465             :       {
     466           0 :         uint64_t match(Window aWin) { return aWin->WindowID(); }
     467           0 :         uint64_t match(LoadInfo aLoadInfo) { return aLoadInfo->GetOuterWindowID(); }
     468             :       };
     469           0 :       mFrameID.emplace(mObj.match(Matcher()));
     470             :     }
     471             :   }
     472           0 :   return mFrameID.ref();
     473             : }
     474             : 
     475             : nsIPrincipal*
     476           0 : DocInfo::Principal() const
     477             : {
     478           0 :   if (mPrincipal.isNothing()) {
     479             :     struct Matcher
     480             :     {
     481           0 :       explicit Matcher(const DocInfo& aThis) : mThis(aThis) {}
     482             :       const DocInfo& mThis;
     483             : 
     484           0 :       nsIPrincipal* match(Window aWin)
     485             :       {
     486           0 :         nsCOMPtr<nsIDocument> doc = aWin->GetDoc();
     487           0 :         return doc->NodePrincipal();
     488             :       }
     489           0 :       nsIPrincipal* match(LoadInfo aLoadInfo)
     490             :       {
     491           0 :         if (!(mThis.URL().InheritsPrincipal() || aLoadInfo->GetForceInheritPrincipal())) {
     492           0 :           return nullptr;
     493             :         }
     494           0 :         if (auto principal = aLoadInfo->PrincipalToInherit()) {
     495           0 :           return principal;
     496             :         }
     497           0 :         return aLoadInfo->TriggeringPrincipal();
     498             :       }
     499             :     };
     500           0 :     mPrincipal.emplace(mObj.match(Matcher(*this)));
     501             :   }
     502           0 :   return mPrincipal.ref();
     503             : }
     504             : 
     505             : const URLInfo&
     506           0 : DocInfo::PrincipalURL() const
     507             : {
     508           0 :   if (!URL().InheritsPrincipal() ||
     509           0 :       !(Principal() && Principal()->GetIsCodebasePrincipal())) {
     510           0 :     return URL();
     511             :   }
     512             : 
     513           0 :   if (mPrincipalURL.isNothing()) {
     514           0 :     nsIPrincipal* prin = Principal();
     515           0 :     nsCOMPtr<nsIURI> uri;
     516           0 :     if (NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
     517           0 :       MOZ_DIAGNOSTIC_ASSERT(uri);
     518           0 :       mPrincipalURL.emplace(uri);
     519             :     } else {
     520           0 :       mPrincipalURL.emplace(URL());
     521             :     }
     522             :   }
     523             : 
     524           0 :   return mPrincipalURL.ref();
     525             : }
     526             : 
     527             : } // namespace extensions
     528             : } // namespace mozilla

Generated by: LCOV version 1.13