• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "tests/shared/browser/extension_util.h"
6 
7 #include <algorithm>
8 #include <memory>
9 
10 #include "include/base/cef_callback.h"
11 #include "include/base/cef_cxx17_backports.h"
12 #include "include/cef_parser.h"
13 #include "include/cef_path_util.h"
14 #include "include/wrapper/cef_closure_task.h"
15 #include "tests/shared/browser/file_util.h"
16 #include "tests/shared/browser/resource_util.h"
17 
18 namespace client {
19 namespace extension_util {
20 
21 namespace {
22 
GetResourcesPath()23 std::string GetResourcesPath() {
24   CefString resources_dir;
25   if (CefGetPath(PK_DIR_RESOURCES, resources_dir) && !resources_dir.empty()) {
26     return resources_dir.ToString() + file_util::kPathSep;
27   }
28   return std::string();
29 }
30 
31 // Internal extension paths may be prefixed with PK_DIR_RESOURCES and always
32 // use forward slash as path separator.
GetInternalPath(const std::string & extension_path)33 std::string GetInternalPath(const std::string& extension_path) {
34   std::string resources_path_lower = GetResourcesPath();
35   std::string extension_path_lower = extension_path;
36 
37 #if defined(OS_WIN)
38   // Convert to lower-case, since Windows paths are case-insensitive.
39   std::transform(resources_path_lower.begin(), resources_path_lower.end(),
40                  resources_path_lower.begin(), ::tolower);
41   std::transform(extension_path_lower.begin(), extension_path_lower.end(),
42                  extension_path_lower.begin(), ::tolower);
43 #endif
44 
45   std::string internal_path;
46   if (!resources_path_lower.empty() &&
47       extension_path_lower.find(resources_path_lower) == 0U) {
48     internal_path = extension_path.substr(resources_path_lower.size());
49   } else {
50     internal_path = extension_path;
51   }
52 
53 #if defined(OS_WIN)
54   // Normalize path separators.
55   std::replace(internal_path.begin(), internal_path.end(), '\\', '/');
56 #endif
57 
58   return internal_path;
59 }
60 
61 using ManifestCallback =
62     base::OnceCallback<void(CefRefPtr<CefDictionaryValue> /*manifest*/)>;
63 
RunManifestCallback(ManifestCallback callback,CefRefPtr<CefDictionaryValue> manifest)64 void RunManifestCallback(ManifestCallback callback,
65                          CefRefPtr<CefDictionaryValue> manifest) {
66   if (!CefCurrentlyOn(TID_UI)) {
67     // Execute on the browser UI thread.
68     CefPostTask(TID_UI, base::BindOnce(std::move(callback), manifest));
69     return;
70   }
71   std::move(callback).Run(manifest);
72 }
73 
74 // Asynchronously reads the manifest and executes |callback| on the UI thread.
GetInternalManifest(const std::string & extension_path,ManifestCallback callback)75 void GetInternalManifest(const std::string& extension_path,
76                          ManifestCallback callback) {
77   if (!CefCurrentlyOn(TID_FILE_USER_BLOCKING)) {
78     // Execute on the browser FILE thread.
79     CefPostTask(TID_FILE_USER_BLOCKING,
80                 base::BindOnce(GetInternalManifest, extension_path,
81                                std::move(callback)));
82     return;
83   }
84 
85   const std::string& manifest_path = GetInternalExtensionResourcePath(
86       file_util::JoinPath(extension_path, "manifest.json"));
87   std::string manifest_contents;
88   if (!LoadBinaryResource(manifest_path.c_str(), manifest_contents) ||
89       manifest_contents.empty()) {
90     LOG(ERROR) << "Failed to load manifest from " << manifest_path;
91     RunManifestCallback(std::move(callback), nullptr);
92     return;
93   }
94 
95   CefString error_msg;
96   CefRefPtr<CefValue> value =
97       CefParseJSONAndReturnError(manifest_contents, JSON_PARSER_RFC, error_msg);
98   if (!value || value->GetType() != VTYPE_DICTIONARY) {
99     if (error_msg.empty())
100       error_msg = "Incorrectly formatted dictionary contents.";
101     LOG(ERROR) << "Failed to parse manifest from " << manifest_path << "; "
102                << error_msg.ToString();
103     RunManifestCallback(std::move(callback), nullptr);
104     return;
105   }
106 
107   RunManifestCallback(std::move(callback), value->GetDictionary());
108 }
109 
LoadExtensionWithManifest(CefRefPtr<CefRequestContext> request_context,const std::string & extension_path,CefRefPtr<CefExtensionHandler> handler,CefRefPtr<CefDictionaryValue> manifest)110 void LoadExtensionWithManifest(CefRefPtr<CefRequestContext> request_context,
111                                const std::string& extension_path,
112                                CefRefPtr<CefExtensionHandler> handler,
113                                CefRefPtr<CefDictionaryValue> manifest) {
114   CEF_REQUIRE_UI_THREAD();
115 
116   // Load the extension internally. Resource requests will be handled via
117   // AddInternalExtensionToResourceManager.
118   request_context->LoadExtension(extension_path, manifest, handler);
119 }
120 
121 }  // namespace
122 
IsInternalExtension(const std::string & extension_path)123 bool IsInternalExtension(const std::string& extension_path) {
124   // List of internally handled extensions.
125   static const char* extensions[] = {"set_page_color"};
126 
127   const std::string& internal_path = GetInternalPath(extension_path);
128   for (size_t i = 0; i < base::size(extensions); ++i) {
129     // Exact match or first directory component.
130     const std::string& extension = extensions[i];
131     if (internal_path == extension ||
132         internal_path.find(extension + '/') == 0) {
133       return true;
134     }
135   }
136 
137   return false;
138 }
139 
GetInternalExtensionResourcePath(const std::string & extension_path)140 std::string GetInternalExtensionResourcePath(
141     const std::string& extension_path) {
142   return "extensions/" + GetInternalPath(extension_path);
143 }
144 
GetExtensionResourcePath(const std::string & extension_path,bool * internal)145 std::string GetExtensionResourcePath(const std::string& extension_path,
146                                      bool* internal) {
147   const bool is_internal = IsInternalExtension(extension_path);
148   if (internal)
149     *internal = is_internal;
150   if (is_internal)
151     return GetInternalExtensionResourcePath(extension_path);
152   return extension_path;
153 }
154 
GetExtensionResourceContents(const std::string & extension_path,std::string & contents)155 bool GetExtensionResourceContents(const std::string& extension_path,
156                                   std::string& contents) {
157   CEF_REQUIRE_FILE_USER_BLOCKING_THREAD();
158 
159   if (IsInternalExtension(extension_path)) {
160     const std::string& contents_path =
161         GetInternalExtensionResourcePath(extension_path);
162     return LoadBinaryResource(contents_path.c_str(), contents);
163   }
164 
165   return file_util::ReadFileToString(extension_path, &contents);
166 }
167 
LoadExtension(CefRefPtr<CefRequestContext> request_context,const std::string & extension_path,CefRefPtr<CefExtensionHandler> handler)168 void LoadExtension(CefRefPtr<CefRequestContext> request_context,
169                    const std::string& extension_path,
170                    CefRefPtr<CefExtensionHandler> handler) {
171   if (!CefCurrentlyOn(TID_UI)) {
172     // Execute on the browser UI thread.
173     CefPostTask(TID_UI, base::BindOnce(LoadExtension, request_context,
174                                        extension_path, handler));
175     return;
176   }
177 
178   if (IsInternalExtension(extension_path)) {
179     // Read the extension manifest and load asynchronously.
180     GetInternalManifest(
181         extension_path,
182         base::BindOnce(LoadExtensionWithManifest, request_context,
183                        extension_path, handler));
184   } else {
185     // Load the extension from disk.
186     request_context->LoadExtension(extension_path, nullptr, handler);
187   }
188 }
189 
AddInternalExtensionToResourceManager(CefRefPtr<CefExtension> extension,CefRefPtr<CefResourceManager> resource_manager)190 void AddInternalExtensionToResourceManager(
191     CefRefPtr<CefExtension> extension,
192     CefRefPtr<CefResourceManager> resource_manager) {
193   DCHECK(IsInternalExtension(extension->GetPath()));
194 
195   if (!CefCurrentlyOn(TID_IO)) {
196     // Execute on the browser IO thread.
197     CefPostTask(TID_IO, base::BindOnce(AddInternalExtensionToResourceManager,
198                                        extension, resource_manager));
199     return;
200   }
201 
202   const std::string& origin = GetExtensionOrigin(extension->GetIdentifier());
203   const std::string& resource_path =
204       GetInternalExtensionResourcePath(extension->GetPath());
205 
206 // Add provider for bundled resource files.
207 #if defined(OS_WIN)
208   // Read resources from the binary.
209   resource_manager->AddProvider(
210       CreateBinaryResourceProvider(origin, resource_path), 50, std::string());
211 #elif defined(OS_POSIX)
212   // Read resources from a directory on disk.
213   std::string resource_dir;
214   if (GetResourceDir(resource_dir)) {
215     resource_dir += "/" + resource_path;
216     resource_manager->AddDirectoryProvider(origin, resource_dir, 50,
217                                            std::string());
218   }
219 #endif
220 }
221 
GetExtensionOrigin(const std::string & extension_id)222 std::string GetExtensionOrigin(const std::string& extension_id) {
223   return "chrome-extension://" + extension_id + "/";
224 }
225 
GetExtensionURL(CefRefPtr<CefExtension> extension)226 std::string GetExtensionURL(CefRefPtr<CefExtension> extension) {
227   CefRefPtr<CefDictionaryValue> browser_action =
228       extension->GetManifest()->GetDictionary("browser_action");
229   if (browser_action) {
230     const std::string& default_popup =
231         browser_action->GetString("default_popup");
232     if (!default_popup.empty())
233       return GetExtensionOrigin(extension->GetIdentifier()) + default_popup;
234   }
235 
236   return std::string();
237 }
238 
GetExtensionIconPath(CefRefPtr<CefExtension> extension,bool * internal)239 std::string GetExtensionIconPath(CefRefPtr<CefExtension> extension,
240                                  bool* internal) {
241   CefRefPtr<CefDictionaryValue> browser_action =
242       extension->GetManifest()->GetDictionary("browser_action");
243   if (browser_action) {
244     const std::string& default_icon = browser_action->GetString("default_icon");
245     if (!default_icon.empty()) {
246       return GetExtensionResourcePath(
247           file_util::JoinPath(extension->GetPath(), default_icon), internal);
248     }
249   }
250 
251   return std::string();
252 }
253 
254 }  // namespace extension_util
255 }  // namespace client
256