// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef EXTENSIONS_RENDERER_SCRIPT_CONTEXT_H_ #define EXTENSIONS_RENDERER_SCRIPT_CONTEXT_H_ #include #include #include #include #include "base/callback.h" #include "base/compiler_specific.h" #include "base/macros.h" #include "base/threading/thread_checker.h" #include "base/unguessable_token.h" #include "extensions/common/features/feature.h" #include "extensions/common/mojom/api_permission_id.mojom-shared.h" #include "extensions/common/permissions/api_permission_set.h" #include "extensions/common/script_constants.h" #include "extensions/renderer/module_system.h" #include "extensions/renderer/safe_builtins.h" #include "extensions/renderer/script_injection_callback.h" #include "url/gurl.h" #include "v8/include/v8.h" namespace blink { class WebDocumentLoader; class WebLocalFrame; } namespace content { class RenderFrame; } namespace extensions { enum class CheckAliasStatus; class Extension; // Extensions wrapper for a v8::Context. // // v8::Contexts can be constructed on any thread, and must only be accessed or // destroyed that thread. // // Note that ScriptContexts bound to worker threads will not have the full // functionality as those bound to the main RenderThread. class ScriptContext { public: using RunScriptExceptionHandler = base::OnceCallback; ScriptContext(const v8::Local& context, blink::WebLocalFrame* frame, const Extension* extension, Feature::Context context_type, const Extension* effective_extension, Feature::Context effective_context_type); ~ScriptContext(); // Returns whether |url| from any Extension in |extension_set| is sandboxed, // as declared in each Extension's manifest. // TODO(kalman): Delete this when crbug.com/466373 is fixed. // See comment in HasAccessOrThrowError. static bool IsSandboxedPage(const GURL& url); // Initializes |module_system| and associates it with this context. void SetModuleSystem(std::unique_ptr module_system); // Clears the WebLocalFrame for this contexts and invalidates the associated // ModuleSystem. void Invalidate(); // Registers |observer| to be run when this context is invalidated. Closures // are run immediately when Invalidate() is called, not in a message loop. void AddInvalidationObserver(base::OnceClosure observer); // Returns true if this context is still valid, false if it isn't. // A context becomes invalid via Invalidate(). bool is_valid() const { return is_valid_; } v8::Local v8_context() const { return v8::Local::New(isolate_, v8_context_); } const Extension* extension() const { return extension_.get(); } const Extension* effective_extension() const { return effective_extension_.get(); } blink::WebLocalFrame* web_frame() const { return web_frame_; } Feature::Context context_type() const { return context_type_; } Feature::Context effective_context_type() const { return effective_context_type_; } const base::UnguessableToken& context_id() const { return context_id_; } ModuleSystem* module_system() { return module_system_.get(); } SafeBuiltins* safe_builtins() { return &safe_builtins_; } const SafeBuiltins* safe_builtins() const { return &safe_builtins_; } // Returns the ID of the extension associated with this context, or empty // string if there is no such extension. const std::string& GetExtensionID() const; // Returns the RenderFrame associated with this context. Can return NULL if // the context is in the process of being destroyed. content::RenderFrame* GetRenderFrame() const; // Safely calls the v8::Function, respecting the page load deferrer and // possibly executing asynchronously. // Doesn't catch exceptions; callers must do that if they want. // USE THESE METHODS RATHER THAN v8::Function::Call WHEREVER POSSIBLE. void SafeCallFunction(const v8::Local& function, int argc, v8::Local argv[]); void SafeCallFunction(const v8::Local& function, int argc, v8::Local argv[], ScriptInjectionCallback::CompleteCallback callback); // Returns the availability of the API |api_name|. Feature::Availability GetAvailability(const std::string& api_name); // Returns the availability of the API |api_name|. // |check_alias| Whether API that has an alias that is available should be // considered available (even if the API itself is not available). Feature::Availability GetAvailability(const std::string& api_name, CheckAliasStatus check_alias); // Returns a string description of the type of context this is. std::string GetContextTypeDescription() const; // Returns a string description of the effective type of context this is. std::string GetEffectiveContextTypeDescription() const; v8::Isolate* isolate() const { return isolate_; } // Get the URL of this context's web frame. // // TODO(kalman): Remove this and replace with a GetOrigin() call which reads // of WebDocument::getSecurityOrigin(): // - The URL can change (e.g. pushState) but the origin cannot. Luckily it // appears as though callers don't make security decisions based on the // result of url() so it's not a problem... yet. // - Origin is the correct check to be making. // - It might let us remove the about:blank resolving? const GURL& url() const { return url_; } const GURL& service_worker_scope() const; int64_t service_worker_version_id() const { return service_worker_version_id_; } bool IsForServiceWorker() const; // Sets the URL of this ScriptContext. Usually this will automatically be set // on construction, unless this isn't constructed with enough information to // determine the URL (e.g. frame was null). // TODO(kalman): Make this a constructor parameter (as an origin). void set_url(const GURL& url) { url_ = url; } void set_service_worker_scope(const GURL& scope) { service_worker_scope_ = scope; } void set_service_worker_version_id(int64_t service_worker_version_id) { service_worker_version_id_ = service_worker_version_id; } // Returns whether the API |api| or any part of the API could be available in // this context without taking into account the context's extension. // |check_alias| Whether the API should be considered available if it has an // alias that is available. bool IsAnyFeatureAvailableToContext(const extensions::Feature& api, CheckAliasStatus check_alias); // Scope which maps a frame to a document loader. This is used by various // static methods below, which need to account for "just about to load" // document when retrieving URL. class ScopedFrameDocumentLoader { public: ScopedFrameDocumentLoader(blink::WebLocalFrame* frame, blink::WebDocumentLoader* document_loader); ~ScopedFrameDocumentLoader(); private: blink::WebLocalFrame* frame_; blink::WebDocumentLoader* document_loader_; DISALLOW_COPY_AND_ASSIGN(ScopedFrameDocumentLoader); }; // TODO(devlin): Move all these Get*URL*() methods out of here? While they are // vaguely ScriptContext related, there's enough here that they probably // warrant another class or utility file. // Utility to get the URL we will match against for a frame. If the frame has // committed, this is the commited URL. Otherwise it is the provisional URL. // The returned URL may be invalid. static GURL GetDocumentLoaderURLForFrame(const blink::WebLocalFrame* frame); // Similar to GetDocumentLoaderURLForFrame, but only returns the data source // URL if the frame's document url is empty and the frame has a security // origin that allows access to the data source url. // TODO(asargent/devlin) - there may be places that should switch to using // this instead of GetDocumentLoaderURLForFrame. static GURL GetAccessCheckedFrameURL(const blink::WebLocalFrame* frame); // Used to determine the "effective" URL in context classification, such as to // associate an about:blank frame in an extension context with its extension. // If |document_url| is an about: or data: URL, returns the URL of the first // frame without an about: or data: URL that matches the initiator origin. // This may not be the immediate parent. Returns |document_url| if it is not // an about: URL, if |match_about_blank| is false, or if a suitable parent // cannot be found. // Will not check parent contexts that cannot be accessed (as is the case // for sandboxed frames). static GURL GetEffectiveDocumentURLForContext(blink::WebLocalFrame* frame, const GURL& document_url, bool match_about_blank); // Used to determine the "effective" URL for extension script injection. // If |document_url| is an about: or data: URL, returns the URL of the first // frame without an about: or data: URL that matches the initiator origin. // This may not be the immediate parent. Returns |document_url| if it is not // an about: or data: URL, if |match_origin_as_fallback| is set to not match, // or if a suitable parent cannot be found. // Considers parent contexts that cannot be accessed (as is the case for // sandboxed frames). static GURL GetEffectiveDocumentURLForInjection( blink::WebLocalFrame* frame, const GURL& document_url, MatchOriginAsFallbackBehavior match_origin_as_fallback); // Grants a set of content capabilities to this context. void set_content_capabilities(APIPermissionSet capabilities) { content_capabilities_ = std::move(capabilities); } // Indicates if this context has an effective API permission either by being // a context for an extension which has that permission, or by being a web // context which has been granted the corresponding capability by an // extension. bool HasAPIPermission(mojom::APIPermissionID permission) const; // Throws an Error in this context's JavaScript context, if this context does // not have access to |name|. Returns true if this context has access (i.e. // no exception thrown), false if it does not (i.e. an exception was thrown). bool HasAccessOrThrowError(const std::string& name); // Returns a string representation of this ScriptContext, for debugging. std::string GetDebugString() const; // Gets the current stack trace as a multi-line string to be logged. std::string GetStackTraceAsString() const; // Runs |code|, labelling the script that gets created as |name| (the name is // used in the devtools and stack traces). |exception_handler| will be called // re-entrantly if an exception is thrown during the script's execution. v8::Local RunScript( v8::Local name, v8::Local code, RunScriptExceptionHandler exception_handler, v8::ScriptCompiler::NoCacheReason no_cache_reason = v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason); private: // DEPRECATED. v8::Local CallFunction(const v8::Local& function, int argc, v8::Local argv[]) const; // Whether this context is valid. bool is_valid_; // The v8 context the bindings are accessible to. v8::Global v8_context_; // The WebLocalFrame associated with this context. This can be NULL because // this object can outlive is destroyed asynchronously. blink::WebLocalFrame* web_frame_; // The extension associated with this context, or NULL if there is none. This // might be a hosted app in the case that this context is hosting a web URL. scoped_refptr extension_; // The type of context. Feature::Context context_type_; // The effective extension associated with this context, or NULL if there is // none. This is different from the above extension if this context is in an // about:blank iframe for example. scoped_refptr effective_extension_; // The type of context. Feature::Context effective_context_type_; // A globally-unique ID for the script context. base::UnguessableToken context_id_; // Owns and structures the JS that is injected to set up extension bindings. std::unique_ptr module_system_; // Contains safe copies of builtin objects like Function.prototype. SafeBuiltins safe_builtins_; // The set of capabilities granted to this context by extensions. APIPermissionSet content_capabilities_; // A list of base::OnceClosure instances as an observer interface for // invalidation. std::vector invalidate_observers_; v8::Isolate* isolate_; GURL url_; GURL service_worker_scope_; int64_t service_worker_version_id_; base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(ScriptContext); }; } // namespace extensions #endif // EXTENSIONS_RENDERER_SCRIPT_CONTEXT_H_