1 // Copyright (c) 2012 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_COMPONENT_LOADER_H_
6 #define CHROME_BROWSER_EXTENSIONS_COMPONENT_LOADER_H_
7 
8 #include <stddef.h>
9 
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include "base/callback_forward.h"
15 #include "base/files/file_path.h"
16 #include "base/gtest_prod_util.h"
17 #include "base/macros.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/memory/weak_ptr.h"
20 #include "base/optional.h"
21 #include "base/values.h"
22 #include "build/build_config.h"
23 #include "chrome/common/buildflags.h"
24 
25 class Profile;
26 
27 namespace extensions {
28 
29 class Extension;
30 class ExtensionSystem;
31 
32 // For registering, loading, and unloading component extensions.
33 class ComponentLoader {
34  public:
35   ComponentLoader(ExtensionSystem* extension_system, Profile* browser_context);
36   virtual ~ComponentLoader();
37 
registered_extensions_count()38   size_t registered_extensions_count() const {
39     return component_extensions_.size();
40   }
41 
42   // Creates and loads all registered component extensions.
43   void LoadAll();
44 
45   // Registers and possibly loads a component extension. If ExtensionService
46   // has been initialized, the extension is loaded; otherwise, the load is
47   // deferred until LoadAll is called. The ID of the added extension is
48   // returned.
49   //
50   // Component extension manifests must contain a "key" property with a unique
51   // public key, serialized in base64. You can create a suitable value with the
52   // following commands on a unixy system:
53   //
54   //   ssh-keygen -t rsa -b 1024 -N '' -f /tmp/key.pem
55   //   openssl rsa -pubout -outform DER < /tmp/key.pem 2>/dev/null | base64 -w 0
56   std::string Add(const base::StringPiece& manifest_contents,
57                   const base::FilePath& root_directory);
58 
59   // Convenience method for registering a component extension by resource id.
60   std::string Add(int manifest_resource_id,
61                   const base::FilePath& root_directory);
62 
63   // Loads a component extension from file system. Replaces previously added
64   // extension with the same ID.
65   std::string AddOrReplace(const base::FilePath& path);
66 
67   // Returns true if an extension with the specified id has been added.
68   bool Exists(const std::string& id) const;
69 
70   // Unloads a component extension and removes it from the list of component
71   // extensions to be loaded.
72   void Remove(const base::FilePath& root_directory);
73   void Remove(const std::string& id);
74 
75   // Call this during test setup to load component extensions that have
76   // background pages for testing, which could otherwise interfere with tests.
77   static void EnableBackgroundExtensionsForTesting();
78 
79   // Adds the default component extensions. If |skip_session_components|
80   // the loader will skip loading component extensions that weren't supposed to
81   // be loaded unless we are in signed user session (ChromeOS). For all other
82   // platforms this |skip_session_components| is expected to be unset.
83   void AddDefaultComponentExtensions(bool skip_session_components);
84 
85   // Similar to above but adds the default component extensions for kiosk mode.
86   void AddDefaultComponentExtensionsForKioskMode(bool skip_session_components);
87 
88   // Reloads a registered component extension.
89   void Reload(const std::string& extension_id);
90 
91   // Return ids of all registered extensions.
92   std::vector<std::string> GetRegisteredComponentExtensionsIds() const;
93 
94 #if defined(OS_CHROMEOS)
95   // Add a component extension from a specific directory. Assumes that the
96   // extension uses a different manifest file when this is a guest session
97   // and that the manifest file lives in |root_directory|. Calls |done_cb|
98   // on success, unless the component loader is shut down during loading.
99   void AddComponentFromDir(
100       const base::FilePath& root_directory,
101       const char* extension_id,
102       const base::Closure& done_cb);
103 
104   // Identical to above except allows for the caller to supply the name of the
105   // manifest file.
106   void AddComponentFromDirWithManifestFilename(
107       const base::FilePath& root_directory,
108       const char* extension_id,
109       const base::FilePath::CharType* manifest_file_name,
110       const base::FilePath::CharType* guest_manifest_file_name,
111       const base::Closure& done_cb);
112 
113   // Add a component extension from a specific directory. Assumes that the
114   // extension's manifest file lives in |root_directory| and its name is
115   // 'manifest.json'. |name_string| and |description_string| are used to
116   // localize component extension's name and description text exclusively.
117   void AddWithNameAndDescriptionFromDir(const base::FilePath& root_directory,
118                                         const char* extension_id,
119                                         const std::string& name_string,
120                                         const std::string& description_string);
121 
122   void AddChromeOsSpeechSynthesisExtensions();
123 #endif
124 
set_ignore_allowlist_for_testing(bool value)125   void set_ignore_allowlist_for_testing(bool value) {
126     ignore_allowlist_for_testing_ = value;
127   }
128 
129  private:
130   FRIEND_TEST_ALL_PREFIXES(ComponentLoaderTest, ParseManifest);
131 
132   // Information about a registered component extension.
133   struct ComponentExtensionInfo {
134     ComponentExtensionInfo(
135         std::unique_ptr<base::DictionaryValue> manifest_param,
136         const base::FilePath& root_directory);
137     ~ComponentExtensionInfo();
138 
139     ComponentExtensionInfo(ComponentExtensionInfo&& other);
140     ComponentExtensionInfo& operator=(ComponentExtensionInfo&& other);
141 
142     // The parsed contents of the extensions's manifest file.
143     std::unique_ptr<base::DictionaryValue> manifest;
144 
145     // Directory where the extension is stored.
146     base::FilePath root_directory;
147 
148     // The component extension's ID.
149     std::string extension_id;
150 
151    private:
152     DISALLOW_COPY_AND_ASSIGN(ComponentExtensionInfo);
153   };
154 
155   // Parses the given JSON manifest. Returns nullptr if it cannot be parsed or
156   // if the result is not a DictionaryValue.
157   std::unique_ptr<base::DictionaryValue> ParseManifest(
158       base::StringPiece manifest_contents) const;
159 
160   std::string Add(const base::StringPiece& manifest_contents,
161                   const base::FilePath& root_directory,
162                   bool skip_allowlist);
163   std::string Add(std::unique_ptr<base::DictionaryValue> parsed_manifest,
164                   const base::FilePath& root_directory,
165                   bool skip_allowlist);
166 
167   // Loads a registered component extension.
168   void Load(const ComponentExtensionInfo& info);
169 
170   void AddDefaultComponentExtensionsWithBackgroundPages(
171       bool skip_session_components);
172   void AddDefaultComponentExtensionsWithBackgroundPagesForKioskMode();
173 
174 #if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
175   void AddHangoutServicesExtension();
176 #endif  // BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
177 
178   void AddNetworkSpeechSynthesisExtension();
179 
180   void AddWithNameAndDescription(int manifest_resource_id,
181                                  const base::FilePath& root_directory,
182                                  const std::string& name_string,
183                                  const std::string& description_string);
184   void AddWebStoreApp();
185 
186 #if defined(OS_CHROMEOS)
187   void AddChromeApp();
188   void AddFileManagerExtension();
189   void AddVideoPlayerExtension();
190   void AddAudioPlayerExtension();
191   void AddGalleryExtension();
192   void AddImageLoaderExtension();
193   void AddKeyboardApp();
194   void AddChromeCameraApp();
195   void AddZipArchiverExtension();
196 #endif  // defined(OS_CHROMEOS)
197 
198   scoped_refptr<const Extension> CreateExtension(
199       const ComponentExtensionInfo& info, std::string* utf8_error);
200 
201   // Unloads |component| from the memory.
202   void UnloadComponent(ComponentExtensionInfo* component);
203 
204 #if defined(OS_CHROMEOS)
205   // Used as a reply callback by |AddComponentFromDir|.
206   // Called with a |root_directory| and parsed |manifest| and invokes
207   // |done_cb| after adding the extension.
208   void FinishAddComponentFromDir(
209       const base::FilePath& root_directory,
210       const char* extension_id,
211       const base::Optional<std::string>& name_string,
212       const base::Optional<std::string>& description_string,
213       const base::Closure& done_cb,
214       std::unique_ptr<base::DictionaryValue> manifest);
215 
216   // Finishes loading an extension tts engine.
217   void FinishLoadSpeechSynthesisExtension(const char* extension_id);
218 #endif
219 
220   Profile* profile_;
221 
222   ExtensionSystem* extension_system_;
223 
224   // List of registered component extensions (see Manifest::Location).
225   typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions;
226   RegisteredComponentExtensions component_extensions_;
227 
228   bool ignore_allowlist_for_testing_;
229 
230   base::WeakPtrFactory<ComponentLoader> weak_factory_{this};
231 
232   friend class TtsApiTest;
233 
234   DISALLOW_COPY_AND_ASSIGN(ComponentLoader);
235 };
236 
237 }  // namespace extensions
238 
239 #endif  // CHROME_BROWSER_EXTENSIONS_COMPONENT_LOADER_H_
240