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