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