| // 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_BROWSER_USER_SCRIPT_LOADER_H_ |
| #define EXTENSIONS_BROWSER_USER_SCRIPT_LOADER_H_ |
| |
| #include <list> |
| #include <map> |
| #include <memory> |
| #include <set> |
| |
| #include "base/callback_forward.h" |
| #include "base/compiler_specific.h" |
| #include "base/macros.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "content/public/browser/render_process_host_creation_observer.h" |
| #include "extensions/common/mojom/host_id.mojom.h" |
| #include "extensions/common/user_script.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace base { |
| class ReadOnlySharedMemoryRegion; |
| } |
| |
| namespace content { |
| class BrowserContext; |
| class RenderProcessHost; |
| } |
| |
| namespace extensions { |
| |
| // Manages one "logical unit" of user scripts in shared memory by constructing a |
| // new shared memory region when the set of scripts changes. Also notifies |
| // renderers of new shared memory region when new renderers appear, or when |
| // script reloading completes. Script loading lives on the UI thread. Instances |
| // of this class are embedded within classes with names ending in |
| // UserScriptManager. These "manager" classes implement the strategy for which |
| // scripts to load/unload on this logical unit of scripts. |
| class UserScriptLoader : public content::RenderProcessHostCreationObserver { |
| public: |
| using LoadScriptsCallback = |
| base::OnceCallback<void(std::unique_ptr<UserScriptList>, |
| base::ReadOnlySharedMemoryRegion shared_memory)>; |
| |
| using ScriptsLoadedCallback = |
| base::OnceCallback<void(UserScriptLoader* loader, |
| const absl::optional<std::string>& error)>; |
| |
| class Observer { |
| public: |
| virtual void OnScriptsLoaded(UserScriptLoader* loader, |
| content::BrowserContext* browser_context) = 0; |
| virtual void OnUserScriptLoaderDestroyed(UserScriptLoader* loader) = 0; |
| }; |
| |
| // Parses the includes out of |script| and returns them in |includes|. |
| static bool ParseMetadataHeader(const base::StringPiece& script_text, |
| UserScript* script); |
| |
| UserScriptLoader(content::BrowserContext* browser_context, |
| const mojom::HostID& host_id); |
| ~UserScriptLoader() override; |
| |
| // Add |scripts| to the set of scripts managed by this loader. If provided, |
| // |callback| is called when |scripts| have been loaded. |
| void AddScripts(std::unique_ptr<UserScriptList> scripts, |
| ScriptsLoadedCallback callback); |
| |
| // Add |scripts| to the set of scripts managed by this loader. |
| // The fetch of the content of the script starts URL request |
| // to the associated render specified by |
| // |render_process_id, render_frame_id|. |
| // TODO(hanxi): The renderer information doesn't really belong in this base |
| // class, but it's not an easy fix. |
| virtual void AddScripts(std::unique_ptr<UserScriptList> scripts, |
| int render_process_id, |
| int render_frame_id, |
| ScriptsLoadedCallback callback); |
| |
| // Removes scripts with ids specified in |scripts| from the set of scripts |
| // managed by this loader and calls |callback| once these scripts have been |
| // removed, if specified. |
| // TODO(lazyboy): Likely we can make |scripts| a std::vector, but |
| // WebViewContentScriptManager makes this non-trivial. |
| void RemoveScripts(const std::set<std::string>& script_ids, |
| ScriptsLoadedCallback callback); |
| |
| // Returns true if the scripts for this loader's HostID have been loaded. |
| bool HasLoadedScripts() const; |
| |
| // Returns true if we have any scripts ready. |
| bool initial_load_complete() const { return shared_memory_.IsValid(); } |
| |
| const mojom::HostID& host_id() const { return host_id_; } |
| |
| // Pickle user scripts and return pointer to the shared memory. |
| static base::ReadOnlySharedMemoryRegion Serialize( |
| const extensions::UserScriptList& scripts); |
| |
| // Adds or removes observers. |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| // Manually attempts a load for this loader, and optionally adds a callback to |
| // |queued_load_callbacks_|, to be called when the next load has completed. |
| // Only used for tests which manually trigger loads. |
| void StartLoadForTesting(ScriptsLoadedCallback callback); |
| |
| protected: |
| // Allows the derived classes to have different ways to load user scripts. |
| // This may not be synchronous with the calls to Add/Remove/Clear scripts. |
| virtual void LoadScripts(std::unique_ptr<UserScriptList> user_scripts, |
| const std::set<std::string>& added_script_ids, |
| LoadScriptsCallback callback) = 0; |
| |
| // Sets the flag if the initial set of hosts has finished loading; if it's |
| // set to be true, calls AttempLoad() to bootstrap. |
| void SetReady(bool ready); |
| |
| content::BrowserContext* browser_context() const { return browser_context_; } |
| |
| private: |
| // content::RenderProcessHostCreationObserver: |
| void OnRenderProcessHostCreated( |
| content::RenderProcessHost* process_host) override; |
| |
| // Returns whether or not it is possible that calls to AddScripts(), |
| // RemoveScripts(), and/or ClearScripts() have caused any real change in the |
| // set of scripts to be loaded. |
| bool ScriptsMayHaveChanged() const; |
| |
| // Attempts to initiate a load. |callback| is added to |
| // |queued_load_callbacks_|, to be called when the next load completes. If no |
| // scripts will be changed then |callback| will be called immediately. |
| void AttemptLoad(ScriptsLoadedCallback callback); |
| |
| // Initiates procedure to start loading scripts on the file thread. |
| void StartLoad(); |
| |
| // Called once we have finished loading the scripts on the file thread. |
| void OnScriptsLoaded(std::unique_ptr<UserScriptList> user_scripts, |
| base::ReadOnlySharedMemoryRegion shared_memory); |
| |
| // Sends the renderer process a new set of user scripts for this |
| // UserScriptLoader's host. |
| void SendUpdate(content::RenderProcessHost* process, |
| const base::ReadOnlySharedMemoryRegion& shared_memory); |
| |
| bool is_loading() const { |
| // |loaded_scripts_| is reset when loading. |
| return loaded_scripts_.get() == nullptr; |
| } |
| |
| // Contains the scripts that were found the last time scripts were updated. |
| base::ReadOnlySharedMemoryRegion shared_memory_; |
| |
| // List of scripts that are currently loaded. This is null when a load is in |
| // progress. |
| std::unique_ptr<UserScriptList> loaded_scripts_; |
| |
| // The mutually-exclusive information about sets of scripts that were added or |
| // removed since the last script load. These maps are keyed by script ids. |
| // Note that we only need HostID information for removal. |
| std::map<std::string, std::unique_ptr<UserScript>> added_scripts_map_; |
| std::set<std::string> removed_script_ids_; |
| |
| // If the initial set of hosts has finished loading. |
| bool ready_; |
| |
| // If list of user scripts is modified while we're loading it, we note |
| // that we're currently mid-load and then start over again once the load |
| // finishes. This boolean tracks whether another load is pending. |
| bool queued_load_; |
| |
| // The browser_context for which the scripts managed here are installed. |
| content::BrowserContext* browser_context_; |
| |
| // ID of the host that owns these scripts, if any. This is only set to a |
| // non-empty value for declarative user script shared memory regions. |
| mojom::HostID host_id_; |
| |
| // The associated observers. |
| base::ObserverList<Observer>::Unchecked observers_; |
| |
| // A list of callbacks associated with script updates that are queued for the |
| // next script load (if one is already in progress). These callbacks are moved |
| // to |loading_callbacks_| once a new script load starts. |
| std::list<ScriptsLoadedCallback> queued_load_callbacks_; |
| |
| // A list of callbacks associated with script updates that will be applied in |
| // the current script load. These callbacks are called once scripts have |
| // finished loading and IPC messages to renderers have been sent. |
| std::list<ScriptsLoadedCallback> loading_callbacks_; |
| |
| base::WeakPtrFactory<UserScriptLoader> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(UserScriptLoader); |
| }; |
| |
| } // namespace extensions |
| |
| #endif // EXTENSIONS_BROWSER_USER_SCRIPT_LOADER_H_ |