1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LOADER_H_
6#define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LOADER_H_
7
8#include <map>
9#include <set>
10
11#include "base/compiler_specific.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/memory/weak_ptr.h"
14#include "base/scoped_observer.h"
15#include "content/public/browser/notification_observer.h"
16#include "content/public/browser/notification_registrar.h"
17#include "extensions/browser/extension_registry_observer.h"
18#include "extensions/common/extension.h"
19#include "extensions/common/extension_set.h"
20#include "extensions/common/user_script.h"
21
22namespace base {
23class SharedMemory;
24}
25
26namespace content {
27class BrowserContext;
28class RenderProcessHost;
29}
30
31class Profile;
32
33namespace extensions {
34
35class ContentVerifier;
36class ExtensionRegistry;
37
38typedef std::map<ExtensionId, ExtensionSet::ExtensionPathAndDefaultLocale>
39    ExtensionsInfo;
40
41// Manages one "logical unit" of user scripts in shared memory by constructing a
42// new shared memory region when the set of scripts changes. Also notifies
43// renderers of new shared memory region when new renderers appear, or when
44// script reloading completes. Script loading lives on the UI thread. Instances
45// of this class are embedded within classes with names ending in
46// UserScriptMaster. These "master" classes implement the strategy for which
47// scripts to load/unload on this logical unit of scripts.
48class UserScriptLoader : public content::NotificationObserver,
49                         public ExtensionRegistryObserver {
50 public:
51  // Parses the includes out of |script| and returns them in |includes|.
52  static bool ParseMetadataHeader(const base::StringPiece& script_text,
53                                  UserScript* script);
54
55  // A wrapper around the method to load user scripts, which is normally run on
56  // the file thread. Exposed only for tests.
57  static void LoadScriptsForTest(UserScriptList* user_scripts);
58
59  UserScriptLoader(Profile* profile,
60                   const ExtensionId& owner_extension_id,
61                   bool listen_for_extension_system_loaded);
62  virtual ~UserScriptLoader();
63
64  // Add |scripts| to the set of scripts managed by this loader.
65  void AddScripts(const std::set<UserScript>& scripts);
66
67  // Remove |scripts| from the set of scripts managed by this loader.
68  void RemoveScripts(const std::set<UserScript>& scripts);
69
70  // Clears the set of scripts managed by this loader.
71  void ClearScripts();
72
73  // Initiates procedure to start loading scripts on the file thread.
74  void StartLoad();
75
76  // Return true if we have any scripts ready.
77  bool scripts_ready() const { return shared_memory_.get() != NULL; }
78
79 private:
80  // content::NotificationObserver implementation.
81  virtual void Observe(int type,
82                       const content::NotificationSource& source,
83                       const content::NotificationDetails& details) OVERRIDE;
84
85  // ExtensionRegistryObserver implementation.
86  virtual void OnExtensionUnloaded(
87      content::BrowserContext* browser_context,
88      const Extension* extension,
89      UnloadedExtensionInfo::Reason reason) OVERRIDE;
90
91  // Initiates script load when we have been waiting for the extension system
92  // to be ready.
93  void OnExtensionSystemReady();
94
95  // Returns whether or not it is possible that calls to AddScripts(),
96  // RemoveScripts(), and/or ClearScripts() have caused any real change in the
97  // set of scripts to be loaded.
98  bool ScriptsMayHaveChanged() const;
99
100  // Attempt to initiate a load.
101  void AttemptLoad();
102
103  // Called once we have finished loading the scripts on the file thread.
104  void OnScriptsLoaded(scoped_ptr<UserScriptList> user_scripts,
105                       scoped_ptr<base::SharedMemory> shared_memory);
106
107  // Sends the renderer process a new set of user scripts. If
108  // |changed_extensions| is not empty, this signals that only the scripts from
109  // those extensions should be updated. Otherwise, all extensions will be
110  // updated.
111  void SendUpdate(content::RenderProcessHost* process,
112                  base::SharedMemory* shared_memory,
113                  const std::set<ExtensionId>& changed_extensions);
114
115  // Add to |changed_extensions_| those extensions referred to by |scripts|.
116  void ExpandChangedExtensions(const std::set<UserScript>& scripts);
117
118  // Update |extensions_info_| to contain info for each element of
119  // |changed_extensions_|.
120  void UpdateExtensionsInfo();
121
122  bool is_loading() const {
123    // Ownership of |user_scripts_| is passed to the file thread when loading.
124    return user_scripts_.get() == NULL;
125  }
126
127  // Manages our notification registrations.
128  content::NotificationRegistrar registrar_;
129
130  // Contains the scripts that were found the last time scripts were updated.
131  scoped_ptr<base::SharedMemory> shared_memory_;
132
133  // List of scripts from currently-installed extensions we should load.
134  scoped_ptr<UserScriptList> user_scripts_;
135
136  // Maps extension info needed for localization to an extension ID.
137  ExtensionsInfo extensions_info_;
138
139  // The mutually-exclusive sets of scripts that were added or removed since the
140  // last script load.
141  std::set<UserScript> added_scripts_;
142  std::set<UserScript> removed_scripts_;
143
144  // Indicates whether the the collection of scripts should be cleared before
145  // additions and removals on the next script load.
146  bool clear_scripts_;
147
148  // The IDs of the extensions which changed in the last update sent to the
149  // renderer.
150  ExtensionIdSet changed_extensions_;
151
152  // If the extensions service has finished loading its initial set of
153  // extensions.
154  bool extension_system_ready_;
155
156  // If list of user scripts is modified while we're loading it, we note
157  // that we're currently mid-load and then start over again once the load
158  // finishes.  This boolean tracks whether another load is pending.
159  bool pending_load_;
160
161  // Whether or not we are currently loading.
162  bool is_loading_;
163
164  // The profile for which the scripts managed here are installed.
165  Profile* profile_;
166
167  // ID of the extension that owns these scripts, if any. This is only set to a
168  // non-empty value for declarative user script shared memory regions.
169  ExtensionId owner_extension_id_;
170
171  ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
172      extension_registry_observer_;
173
174  base::WeakPtrFactory<UserScriptLoader> weak_factory_;
175
176  DISALLOW_COPY_AND_ASSIGN(UserScriptLoader);
177};
178
179}  // namespace extensions
180
181#endif  // CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_LOADER_H_
182