1 // Copyright 2015 The Chromium Embedded Framework Authors.
2 // Portions copyright 2014 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "libcef/browser/extensions/extensions_browser_client.h"
7
8 #include <utility>
9
10 #include "libcef/browser/alloy/alloy_browser_host_impl.h"
11 #include "libcef/browser/browser_context.h"
12 #include "libcef/browser/extensions/component_extension_resource_manager.h"
13 #include "libcef/browser/extensions/extension_system.h"
14 #include "libcef/browser/extensions/extension_system_factory.h"
15 #include "libcef/browser/extensions/extension_web_contents_observer.h"
16 #include "libcef/browser/extensions/extensions_api_client.h"
17 #include "libcef/browser/extensions/extensions_browser_api_provider.h"
18 #include "libcef/browser/request_context_impl.h"
19
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/extensions/chrome_url_request_util.h"
22 #include "chrome/browser/extensions/event_router_forwarder.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/render_frame_host.h"
26 #include "extensions/browser/api/extensions_api_client.h"
27 #include "extensions/browser/api/mime_handler_private/mime_handler_private.h"
28 #include "extensions/browser/api/runtime/runtime_api_delegate.h"
29 #include "extensions/browser/app_sorting.h"
30 #include "extensions/browser/core_extensions_browser_api_provider.h"
31 #include "extensions/browser/event_router.h"
32 #include "extensions/browser/extension_host_delegate.h"
33 #include "extensions/browser/extensions_browser_interface_binders.h"
34 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
35 #include "extensions/browser/kiosk/kiosk_delegate.h"
36 #include "extensions/browser/url_request_util.h"
37 #include "extensions/common/api/mime_handler.mojom.h"
38 #include "extensions/common/constants.h"
39
40 using content::BrowserContext;
41 using content::BrowserThread;
42
43 namespace extensions {
44
45 namespace {
46
BindMimeHandlerService(content::RenderFrameHost * frame_host,mojo::PendingReceiver<extensions::mime_handler::MimeHandlerService> receiver)47 void BindMimeHandlerService(
48 content::RenderFrameHost* frame_host,
49 mojo::PendingReceiver<extensions::mime_handler::MimeHandlerService>
50 receiver) {
51 auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host);
52 if (!web_contents)
53 return;
54
55 auto* guest_view =
56 extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
57 if (!guest_view)
58 return;
59 extensions::MimeHandlerServiceImpl::Create(guest_view->GetStreamWeakPtr(),
60 std::move(receiver));
61 }
62
BindBeforeUnloadControl(content::RenderFrameHost * frame_host,mojo::PendingReceiver<extensions::mime_handler::BeforeUnloadControl> receiver)63 void BindBeforeUnloadControl(
64 content::RenderFrameHost* frame_host,
65 mojo::PendingReceiver<extensions::mime_handler::BeforeUnloadControl>
66 receiver) {
67 auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host);
68 if (!web_contents)
69 return;
70
71 auto* guest_view =
72 extensions::MimeHandlerViewGuest::FromWebContents(web_contents);
73 if (!guest_view)
74 return;
75 guest_view->FuseBeforeUnloadControl(std::move(receiver));
76 }
77
78 // Dummy KiosDelegate that always returns false
79 class CefKioskDelegate : public extensions::KioskDelegate {
80 public:
81 CefKioskDelegate() = default;
82 ~CefKioskDelegate() override = default;
83
84 // KioskDelegate overrides:
IsAutoLaunchedKioskApp(const ExtensionId & id) const85 bool IsAutoLaunchedKioskApp(const ExtensionId& id) const override {
86 return false;
87 }
88 };
89
90 } // namespace
91
CefExtensionsBrowserClient()92 CefExtensionsBrowserClient::CefExtensionsBrowserClient()
93 : api_client_(new CefExtensionsAPIClient),
94 resource_manager_(new CefComponentExtensionResourceManager) {
95 AddAPIProvider(std::make_unique<CoreExtensionsBrowserAPIProvider>());
96 AddAPIProvider(std::make_unique<CefExtensionsBrowserAPIProvider>());
97 }
98
~CefExtensionsBrowserClient()99 CefExtensionsBrowserClient::~CefExtensionsBrowserClient() {}
100
101 // static
Get()102 CefExtensionsBrowserClient* CefExtensionsBrowserClient::Get() {
103 return static_cast<CefExtensionsBrowserClient*>(
104 ExtensionsBrowserClient::Get());
105 }
106
IsShuttingDown()107 bool CefExtensionsBrowserClient::IsShuttingDown() {
108 return false;
109 }
110
AreExtensionsDisabled(const base::CommandLine & command_line,BrowserContext * context)111 bool CefExtensionsBrowserClient::AreExtensionsDisabled(
112 const base::CommandLine& command_line,
113 BrowserContext* context) {
114 return false;
115 }
116
IsValidContext(BrowserContext * context)117 bool CefExtensionsBrowserClient::IsValidContext(BrowserContext* context) {
118 return GetOriginalContext(context) != nullptr;
119 }
120
IsSameContext(BrowserContext * first,BrowserContext * second)121 bool CefExtensionsBrowserClient::IsSameContext(BrowserContext* first,
122 BrowserContext* second) {
123 // Returns true if |first| and |second| share the same underlying
124 // CefBrowserContext.
125 return GetOriginalContext(first) == GetOriginalContext(second);
126 }
127
HasOffTheRecordContext(BrowserContext * context)128 bool CefExtensionsBrowserClient::HasOffTheRecordContext(
129 BrowserContext* context) {
130 // CEF doesn't use incognito contexts.
131 return false;
132 }
133
GetOffTheRecordContext(BrowserContext * context)134 BrowserContext* CefExtensionsBrowserClient::GetOffTheRecordContext(
135 BrowserContext* context) {
136 return nullptr;
137 }
138
GetOriginalContext(BrowserContext * context)139 BrowserContext* CefExtensionsBrowserClient::GetOriginalContext(
140 BrowserContext* context) {
141 auto cef_browser_context = CefBrowserContext::FromBrowserContext(context);
142 if (cef_browser_context)
143 return cef_browser_context->AsBrowserContext();
144 return nullptr;
145 }
146
IsGuestSession(BrowserContext * context) const147 bool CefExtensionsBrowserClient::IsGuestSession(BrowserContext* context) const {
148 return false;
149 }
150
IsExtensionIncognitoEnabled(const std::string & extension_id,content::BrowserContext * context) const151 bool CefExtensionsBrowserClient::IsExtensionIncognitoEnabled(
152 const std::string& extension_id,
153 content::BrowserContext* context) const {
154 return false;
155 }
156
CanExtensionCrossIncognito(const Extension * extension,content::BrowserContext * context) const157 bool CefExtensionsBrowserClient::CanExtensionCrossIncognito(
158 const Extension* extension,
159 content::BrowserContext* context) const {
160 return false;
161 }
162
GetBundleResourcePath(const network::ResourceRequest & request,const base::FilePath & extension_resources_path,int * resource_id) const163 base::FilePath CefExtensionsBrowserClient::GetBundleResourcePath(
164 const network::ResourceRequest& request,
165 const base::FilePath& extension_resources_path,
166 int* resource_id) const {
167 return chrome_url_request_util::GetBundleResourcePath(
168 request, extension_resources_path, resource_id);
169 }
170
LoadResourceFromResourceBundle(const network::ResourceRequest & request,mojo::PendingReceiver<network::mojom::URLLoader> loader,const base::FilePath & resource_relative_path,const int resource_id,const std::string & content_security_policy,mojo::PendingRemote<network::mojom::URLLoaderClient> client,bool send_cors_header)171 void CefExtensionsBrowserClient::LoadResourceFromResourceBundle(
172 const network::ResourceRequest& request,
173 mojo::PendingReceiver<network::mojom::URLLoader> loader,
174 const base::FilePath& resource_relative_path,
175 const int resource_id,
176 const std::string& content_security_policy,
177 mojo::PendingRemote<network::mojom::URLLoaderClient> client,
178 bool send_cors_header) {
179 chrome_url_request_util::LoadResourceFromResourceBundle(
180 request, std::move(loader), resource_relative_path, resource_id,
181 content_security_policy, std::move(client), send_cors_header);
182 }
183
AllowCrossRendererResourceLoad(const network::ResourceRequest & request,network::mojom::RequestDestination destination,ui::PageTransition page_transition,int child_id,bool is_incognito,const Extension * extension,const ExtensionSet & extensions,const ProcessMap & process_map)184 bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
185 const network::ResourceRequest& request,
186 network::mojom::RequestDestination destination,
187 ui::PageTransition page_transition,
188 int child_id,
189 bool is_incognito,
190 const Extension* extension,
191 const ExtensionSet& extensions,
192 const ProcessMap& process_map) {
193 bool allowed = false;
194 if (url_request_util::AllowCrossRendererResourceLoad(
195 request, destination, page_transition, child_id, is_incognito,
196 extension, extensions, process_map, &allowed)) {
197 return allowed;
198 }
199
200 // Couldn't determine if resource is allowed. Block the load.
201 return false;
202 }
203
GetPrefServiceForContext(BrowserContext * context)204 PrefService* CefExtensionsBrowserClient::GetPrefServiceForContext(
205 BrowserContext* context) {
206 return CefBrowserContext::FromBrowserContext(context)
207 ->AsProfile()
208 ->GetPrefs();
209 }
210
GetEarlyExtensionPrefsObservers(content::BrowserContext * context,std::vector<EarlyExtensionPrefsObserver * > * observers) const211 void CefExtensionsBrowserClient::GetEarlyExtensionPrefsObservers(
212 content::BrowserContext* context,
213 std::vector<EarlyExtensionPrefsObserver*>* observers) const {}
214
GetProcessManagerDelegate() const215 ProcessManagerDelegate* CefExtensionsBrowserClient::GetProcessManagerDelegate()
216 const {
217 return nullptr;
218 }
219
220 std::unique_ptr<ExtensionHostDelegate>
CreateExtensionHostDelegate()221 CefExtensionsBrowserClient::CreateExtensionHostDelegate() {
222 // CEF does not use the ExtensionHost constructor that calls this method.
223 NOTREACHED();
224 return std::unique_ptr<ExtensionHostDelegate>();
225 }
226
CreateBackgroundExtensionHost(const Extension * extension,content::BrowserContext * browser_context,const GURL & url,ExtensionHost ** host)227 bool CefExtensionsBrowserClient::CreateBackgroundExtensionHost(
228 const Extension* extension,
229 content::BrowserContext* browser_context,
230 const GURL& url,
231 ExtensionHost** host) {
232 auto cef_browser_context =
233 CefBrowserContext::FromBrowserContext(browser_context);
234
235 // A CEF representation should always exist.
236 CefRefPtr<CefExtension> cef_extension =
237 cef_browser_context->GetExtension(extension->id());
238 DCHECK(cef_extension);
239 if (!cef_extension) {
240 // Cancel the background host creation.
241 return true;
242 }
243
244 // Always use the same request context that the extension was registered with.
245 // GetLoaderContext() will return NULL for internal extensions.
246 CefRefPtr<CefRequestContext> request_context =
247 cef_extension->GetLoaderContext();
248 if (!request_context) {
249 // Cancel the background host creation.
250 return true;
251 }
252
253 CefBrowserCreateParams create_params;
254 create_params.url = url.spec();
255 create_params.request_context = request_context;
256
257 CefRefPtr<CefExtensionHandler> handler = cef_extension->GetHandler();
258 if (handler.get() && handler->OnBeforeBackgroundBrowser(
259 cef_extension, create_params.url,
260 create_params.client, create_params.settings)) {
261 // Cancel the background host creation.
262 return true;
263 }
264
265 // This triggers creation of the background host.
266 create_params.extension = extension;
267 create_params.extension_host_type = mojom::ViewType::kExtensionBackgroundPage;
268
269 // Browser creation may fail under certain rare circumstances. Fail the
270 // background host creation in that case.
271 CefRefPtr<AlloyBrowserHostImpl> browser =
272 AlloyBrowserHostImpl::Create(create_params);
273 if (browser) {
274 *host = browser->GetExtensionHost();
275 DCHECK(*host);
276 }
277 return true;
278 }
279
DidVersionUpdate(BrowserContext * context)280 bool CefExtensionsBrowserClient::DidVersionUpdate(BrowserContext* context) {
281 // TODO(jamescook): We might want to tell extensions when app_shell updates.
282 return false;
283 }
284
PermitExternalProtocolHandler()285 void CefExtensionsBrowserClient::PermitExternalProtocolHandler() {}
286
IsInDemoMode()287 bool CefExtensionsBrowserClient::IsInDemoMode() {
288 return false;
289 }
290
IsScreensaverInDemoMode(const std::string & app_id)291 bool CefExtensionsBrowserClient::IsScreensaverInDemoMode(
292 const std::string& app_id) {
293 return false;
294 }
295
IsRunningInForcedAppMode()296 bool CefExtensionsBrowserClient::IsRunningInForcedAppMode() {
297 return false;
298 }
299
IsAppModeForcedForApp(const ExtensionId & extension_id)300 bool CefExtensionsBrowserClient::IsAppModeForcedForApp(
301 const ExtensionId& extension_id) {
302 return false;
303 }
304
IsLoggedInAsPublicAccount()305 bool CefExtensionsBrowserClient::IsLoggedInAsPublicAccount() {
306 return false;
307 }
308
309 ExtensionSystemProvider*
GetExtensionSystemFactory()310 CefExtensionsBrowserClient::GetExtensionSystemFactory() {
311 return CefExtensionSystemFactory::GetInstance();
312 }
313
RegisterBrowserInterfaceBindersForFrame(mojo::BinderMapWithContext<content::RenderFrameHost * > * map,content::RenderFrameHost * render_frame_host,const Extension * extension) const314 void CefExtensionsBrowserClient::RegisterBrowserInterfaceBindersForFrame(
315 mojo::BinderMapWithContext<content::RenderFrameHost*>* map,
316 content::RenderFrameHost* render_frame_host,
317 const Extension* extension) const {
318 PopulateExtensionFrameBinders(map, render_frame_host, extension);
319
320 map->Add<extensions::mime_handler::MimeHandlerService>(
321 base::BindRepeating(&BindMimeHandlerService));
322 map->Add<extensions::mime_handler::BeforeUnloadControl>(
323 base::BindRepeating(&BindBeforeUnloadControl));
324 }
325
326 std::unique_ptr<RuntimeAPIDelegate>
CreateRuntimeAPIDelegate(content::BrowserContext * context) const327 CefExtensionsBrowserClient::CreateRuntimeAPIDelegate(
328 content::BrowserContext* context) const {
329 // TODO(extensions): Implement to support Apps.
330 NOTREACHED();
331 return nullptr;
332 }
333
334 const ComponentExtensionResourceManager*
GetComponentExtensionResourceManager()335 CefExtensionsBrowserClient::GetComponentExtensionResourceManager() {
336 return resource_manager_.get();
337 }
338
BroadcastEventToRenderers(events::HistogramValue histogram_value,const std::string & event_name,std::unique_ptr<base::ListValue> args,bool dispatch_to_off_the_record_profiles)339 void CefExtensionsBrowserClient::BroadcastEventToRenderers(
340 events::HistogramValue histogram_value,
341 const std::string& event_name,
342 std::unique_ptr<base::ListValue> args,
343 bool dispatch_to_off_the_record_profiles) {
344 g_browser_process->extension_event_router_forwarder()
345 ->BroadcastEventToRenderers(histogram_value, event_name, std::move(args),
346 GURL(), dispatch_to_off_the_record_profiles);
347 }
348
GetExtensionCache()349 ExtensionCache* CefExtensionsBrowserClient::GetExtensionCache() {
350 // Only used by Chrome via ExtensionService.
351 NOTREACHED();
352 return nullptr;
353 }
354
IsBackgroundUpdateAllowed()355 bool CefExtensionsBrowserClient::IsBackgroundUpdateAllowed() {
356 return true;
357 }
358
IsMinBrowserVersionSupported(const std::string & min_version)359 bool CefExtensionsBrowserClient::IsMinBrowserVersionSupported(
360 const std::string& min_version) {
361 return true;
362 }
363
364 ExtensionWebContentsObserver*
GetExtensionWebContentsObserver(content::WebContents * web_contents)365 CefExtensionsBrowserClient::GetExtensionWebContentsObserver(
366 content::WebContents* web_contents) {
367 return CefExtensionWebContentsObserver::FromWebContents(web_contents);
368 }
369
GetKioskDelegate()370 KioskDelegate* CefExtensionsBrowserClient::GetKioskDelegate() {
371 if (!kiosk_delegate_)
372 kiosk_delegate_.reset(new CefKioskDelegate());
373 return kiosk_delegate_.get();
374 }
375
IsLockScreenContext(content::BrowserContext * context)376 bool CefExtensionsBrowserClient::IsLockScreenContext(
377 content::BrowserContext* context) {
378 return false;
379 }
380
GetApplicationLocale()381 std::string CefExtensionsBrowserClient::GetApplicationLocale() {
382 return g_browser_process->GetApplicationLocale();
383 }
384
385 } // namespace extensions
386