blob: 78aa9d767869f923c748778b5a4c59cde034b8c2 [file] [log] [blame]
[email protected]64c820732012-01-05 20:50:341// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]bfdffe2b2009-04-24 22:05:352// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extension_function_dispatcher.h"
6
[email protected]745feedb2010-08-02 04:08:077#include <map>
8
[email protected]ffbec692012-02-26 20:26:429#include "base/json/json_string_value_serializer.h"
[email protected]5bc248a2012-04-04 23:38:1110#include "base/lazy_instance.h"
[email protected]3b63f8f42011-03-28 01:54:1511#include "base/memory/ref_counted.h"
[email protected]bfdffe2b2009-04-24 22:05:3512#include "base/process_util.h"
[email protected]bfdffe2b2009-04-24 22:05:3513#include "base/values.h"
[email protected]17d40f02010-07-01 01:18:0614#include "build/build_config.h"
[email protected]efd75992011-12-15 22:42:4215#include "chrome/browser/extensions/extension_activity_log.h"
[email protected]bfdffe2b2009-04-24 22:05:3516#include "chrome/browser/extensions/extension_function.h"
[email protected]ae33d322012-03-19 22:24:3517#include "chrome/browser/extensions/extension_function_registry.h"
[email protected]2f69b382011-02-19 00:34:2518#include "chrome/browser/extensions/extension_service.h"
[email protected]8f9d4eb2011-02-05 01:39:1019#include "chrome/browser/extensions/extension_web_ui.h"
[email protected]d13950e2009-12-04 01:43:0220#include "chrome/browser/extensions/extensions_quota_service.h"
[email protected]83820d42011-11-12 22:03:1121#include "chrome/browser/extensions/process_map.h"
[email protected]ed2b1002011-05-25 14:12:1022#include "chrome/browser/external_protocol/external_protocol_handler.h"
[email protected]8ecad5e2010-12-02 21:18:3323#include "chrome/browser/profiles/profile.h"
[email protected]c357acb42011-06-09 20:52:4224#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
[email protected]71b73f02011-04-06 15:57:2925#include "chrome/browser/ui/browser_list.h"
[email protected]00070c732011-04-09 15:31:3326#include "chrome/browser/ui/browser_window.h"
[email protected]83820d42011-11-12 22:03:1127#include "chrome/common/extensions/api/extension_api.h"
[email protected]44c49c92011-03-28 16:17:2328#include "chrome/common/extensions/extension_messages.h"
[email protected]615d88f2011-12-13 01:47:4429#include "chrome/common/extensions/extension_set.h"
[email protected]9c45b7182009-08-04 16:44:4330#include "chrome/common/url_constants.h"
[email protected]c333e792012-01-06 16:57:3931#include "content/public/browser/render_process_host.h"
[email protected]9c1662b2012-03-06 15:44:3332#include "content/public/browser/render_view_host.h"
[email protected]f82d57b52011-04-27 19:13:1733#include "ipc/ipc_message.h"
34#include "ipc/ipc_message_macros.h"
[email protected]615d88f2011-12-13 01:47:4435#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
[email protected]9c1662b2012-03-06 15:44:3336#include "third_party/skia/include/core/SkBitmap.h"
[email protected]ae33d322012-03-19 22:24:3537#include "webkit/glue/resource_type.h"
[email protected]61b55b62011-03-24 09:03:1038
[email protected]83820d42011-11-12 22:03:1139using extensions::ExtensionAPI;
[email protected]eaabba22012-03-07 15:02:1140using content::RenderViewHost;
[email protected]615d88f2011-12-13 01:47:4441using WebKit::WebSecurityOrigin;
[email protected]83820d42011-11-12 22:03:1142
[email protected]5bc248a2012-04-04 23:38:1143namespace {
44
[email protected]efd75992011-12-15 22:42:4245const char kAccessDenied[] = "access denied";
46const char kQuotaExceeded[] = "quota exceeded";
47
48void LogSuccess(const Extension* extension,
49 const ExtensionHostMsg_Request_Params& params) {
50 ExtensionActivityLog* extension_activity_log =
51 ExtensionActivityLog::GetInstance();
52 if (extension_activity_log->HasObservers(extension)) {
53 std::string call_signature = params.name + "(";
54 ListValue::const_iterator it = params.arguments.begin();
55 for (; it != params.arguments.end(); ++it) {
56 std::string arg;
57 JSONStringValueSerializer serializer(&arg);
58 if (serializer.SerializeAndOmitBinaryValues(**it)) {
59 if (it != params.arguments.begin())
60 call_signature += ", ";
61 call_signature += arg;
62 }
63 }
64 call_signature += ")";
65
66 extension_activity_log->Log(
67 extension,
68 ExtensionActivityLog::ACTIVITY_EXTENSION_API_CALL,
69 call_signature);
70 }
71}
72
73void LogFailure(const Extension* extension,
74 const std::string& func_name,
75 const char* reason) {
76 ExtensionActivityLog* extension_activity_log =
77 ExtensionActivityLog::GetInstance();
78 if (extension_activity_log->HasObservers(extension)) {
79 extension_activity_log->Log(
80 extension,
81 ExtensionActivityLog::ACTIVITY_EXTENSION_API_BLOCK,
82 func_name + ": " + reason);
83 }
84}
85
[email protected]5bc248a2012-04-04 23:38:1186// Separate copy of ExtensionAPI used for IO thread extension functions. We need
87// this because ExtensionAPI has mutable data. It should be possible to remove
88// this once all the extension APIs are updated to the feature system.
89struct Static {
90 Static()
91 : api(extensions::ExtensionAPI::CreateWithDefaultConfiguration()) {
92 }
93 scoped_ptr<extensions::ExtensionAPI> api;
94};
95base::LazyInstance<Static> g_global_io_data = LAZY_INSTANCE_INITIALIZER;
96
97} // namespace
98
99
[email protected]bfdffe2b2009-04-24 22:05:35100void ExtensionFunctionDispatcher::GetAllFunctionNames(
101 std::vector<std::string>* names) {
[email protected]ae33d322012-03-19 22:24:35102 ExtensionFunctionRegistry::GetInstance()->GetAllNames(names);
[email protected]bfdffe2b2009-04-24 22:05:35103}
104
[email protected]b83e4602009-05-15 22:58:33105bool ExtensionFunctionDispatcher::OverrideFunction(
106 const std::string& name, ExtensionFunctionFactory factory) {
[email protected]ae33d322012-03-19 22:24:35107 return ExtensionFunctionRegistry::GetInstance()->OverrideFunction(name,
108 factory);
[email protected]b83e4602009-05-15 22:58:33109}
110
111void ExtensionFunctionDispatcher::ResetFunctions() {
[email protected]ae33d322012-03-19 22:24:35112 ExtensionFunctionRegistry::GetInstance()->ResetFunctions();
[email protected]b83e4602009-05-15 22:58:33113}
114
[email protected]c357acb42011-06-09 20:52:42115// static
116void ExtensionFunctionDispatcher::DispatchOnIOThread(
[email protected]fd50e7b2011-11-03 09:20:25117 ExtensionInfoMap* extension_info_map,
[email protected]673514522011-07-13 18:17:18118 void* profile,
[email protected]c357acb42011-06-09 20:52:42119 int render_process_id,
120 base::WeakPtr<ChromeRenderMessageFilter> ipc_sender,
121 int routing_id,
122 const ExtensionHostMsg_Request_Params& params) {
123 const Extension* extension =
[email protected]83820d42011-11-12 22:03:11124 extension_info_map->extensions().GetByID(params.extension_id);
[email protected]c357acb42011-06-09 20:52:42125
[email protected]6f371442011-11-09 06:45:46126 scoped_refptr<ExtensionFunction> function(
127 CreateExtensionFunction(params, extension, render_process_id,
[email protected]5bc248a2012-04-04 23:38:11128 extension_info_map->process_map(),
129 g_global_io_data.Get().api.get(),
130 profile,
[email protected]6f371442011-11-09 06:45:46131 ipc_sender, routing_id));
[email protected]efd75992011-12-15 22:42:42132 if (!function) {
133 LogFailure(extension, params.name, kAccessDenied);
[email protected]c357acb42011-06-09 20:52:42134 return;
[email protected]efd75992011-12-15 22:42:42135 }
[email protected]c357acb42011-06-09 20:52:42136
137 IOThreadExtensionFunction* function_io =
138 function->AsIOThreadExtensionFunction();
139 if (!function_io) {
140 NOTREACHED();
141 return;
142 }
143 function_io->set_ipc_sender(ipc_sender, routing_id);
144 function_io->set_extension_info_map(extension_info_map);
145 function->set_include_incognito(
146 extension_info_map->IsIncognitoEnabled(extension->id()));
[email protected]fd50e7b2011-11-03 09:20:25147
[email protected]36296912012-03-20 11:08:49148 ExtensionsQuotaService* quota = extension_info_map->GetQuotaService();
[email protected]fd50e7b2011-11-03 09:20:25149 if (quota->Assess(extension->id(), function, &params.arguments,
150 base::TimeTicks::Now())) {
151 function->Run();
[email protected]efd75992011-12-15 22:42:42152 LogSuccess(extension, params);
[email protected]fd50e7b2011-11-03 09:20:25153 } else {
154 function->OnQuotaExceeded();
[email protected]efd75992011-12-15 22:42:42155 LogFailure(extension, params.name, kQuotaExceeded);
[email protected]fd50e7b2011-11-03 09:20:25156 }
[email protected]c357acb42011-06-09 20:52:42157}
158
[email protected]c5dbef02011-05-13 05:06:09159ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(Profile* profile,
160 Delegate* delegate)
161 : profile_(profile),
[email protected]55ce330712011-05-24 19:04:27162 delegate_(delegate) {
[email protected]bfdffe2b2009-04-24 22:05:35163}
164
[email protected]32dda362009-06-05 19:07:01165ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() {
[email protected]32dda362009-06-05 19:07:01166}
167
[email protected]0ec92032010-03-24 19:59:41168Browser* ExtensionFunctionDispatcher::GetCurrentBrowser(
[email protected]c5dbef02011-05-13 05:06:09169 RenderViewHost* render_view_host, bool include_incognito) {
[email protected]0ec92032010-03-24 19:59:41170 Browser* browser = delegate_->GetBrowser();
[email protected]7eecaed52009-05-07 21:44:12171
[email protected]bc535ee52010-08-31 18:40:32172 // If the delegate has an associated browser, that is always the right answer.
173 if (browser)
174 return browser;
[email protected]9c45b7182009-08-04 16:44:43175
[email protected]bc535ee52010-08-31 18:40:32176 // Otherwise, try to default to a reasonable browser. If |include_incognito|
177 // is true, we will also search browsers in the incognito version of this
178 // profile. Note that the profile may already be incognito, in which case
179 // we will search the incognito version only, regardless of the value of
180 // |include_incognito|.
[email protected]9b62ecf2011-07-27 20:23:08181 Profile* profile = Profile::FromBrowserContext(
[email protected]9f76c1e2012-03-05 15:15:58182 render_view_host->GetProcess()->GetBrowserContext());
[email protected]8ecca492012-01-19 17:29:42183 browser = BrowserList::FindAnyBrowser(profile, include_incognito);
[email protected]0ec92032010-03-24 19:59:41184
[email protected]0ec92032010-03-24 19:59:41185 // NOTE(rafaelw): This can return NULL in some circumstances. In particular,
[email protected]6d7a6042010-08-12 20:12:42186 // a background_page onload chrome.tabs api call can make it into here
187 // before the browser is sufficiently initialized to return here.
[email protected]0ec92032010-03-24 19:59:41188 // A similar situation may arise during shutdown.
189 // TODO(rafaelw): Delay creation of background_page until the browser
190 // is available. http://code.google.com/p/chromium/issues/detail?id=13284
191 return browser;
[email protected]b27257562009-11-16 23:28:26192}
193
[email protected]c5dbef02011-05-13 05:06:09194void ExtensionFunctionDispatcher::Dispatch(
195 const ExtensionHostMsg_Request_Params& params,
196 RenderViewHost* render_view_host) {
[email protected]c5dbef02011-05-13 05:06:09197 ExtensionService* service = profile()->GetExtensionService();
[email protected]6f371442011-11-09 06:45:46198 extensions::ProcessMap* process_map = service->process_map();
199 if (!service || !process_map)
[email protected]c5dbef02011-05-13 05:06:09200 return;
201
[email protected]615d88f2011-12-13 01:47:44202 const Extension* extension = service->extensions()->GetByID(
203 params.extension_id);
[email protected]c5dbef02011-05-13 05:06:09204 if (!extension)
[email protected]615d88f2011-12-13 01:47:44205 extension = service->extensions()->GetHostedAppByURL(ExtensionURLInfo(
206 WebSecurityOrigin::createFromString(params.source_origin),
207 params.source_url));
[email protected]c5dbef02011-05-13 05:06:09208
[email protected]8add5412011-10-01 21:02:14209 scoped_refptr<ExtensionFunction> function(
[email protected]6f371442011-11-09 06:45:46210 CreateExtensionFunction(params, extension,
[email protected]9f76c1e2012-03-05 15:15:58211 render_view_host->GetProcess()->GetID(),
[email protected]6f371442011-11-09 06:45:46212 *(service->process_map()),
[email protected]5bc248a2012-04-04 23:38:11213 extensions::ExtensionAPI::GetSharedInstance(),
[email protected]6f371442011-11-09 06:45:46214 profile(), render_view_host,
[email protected]9f76c1e2012-03-05 15:15:58215 render_view_host->GetRoutingID()));
[email protected]efd75992011-12-15 22:42:42216 if (!function) {
217 LogFailure(extension, params.name, kAccessDenied);
[email protected]f82d57b52011-04-27 19:13:17218 return;
[email protected]efd75992011-12-15 22:42:42219 }
[email protected]f82d57b52011-04-27 19:13:17220
[email protected]a2aef2e2011-05-26 22:48:12221 UIThreadExtensionFunction* function_ui =
222 function->AsUIThreadExtensionFunction();
223 if (!function_ui) {
224 NOTREACHED();
225 return;
226 }
227 function_ui->SetRenderViewHost(render_view_host);
228 function_ui->set_dispatcher(AsWeakPtr());
229 function_ui->set_profile(profile_);
[email protected]2a8f24e2010-11-03 21:37:05230 function->set_include_incognito(service->CanCrossIncognito(extension));
[email protected]cb0ce1e022010-03-10 19:54:41231
[email protected]d13950e2009-12-04 01:43:02232 ExtensionsQuotaService* quota = service->quota_service();
[email protected]c5dbef02011-05-13 05:06:09233 if (quota->Assess(extension->id(), function, &params.arguments,
[email protected]8b8e7c92010-08-19 18:05:56234 base::TimeTicks::Now())) {
[email protected]d070ec62010-07-27 21:28:26235 // See crbug.com/39178.
236 ExternalProtocolHandler::PermitLaunchUrl();
237
[email protected]d13950e2009-12-04 01:43:02238 function->Run();
[email protected]efd75992011-12-15 22:42:42239 LogSuccess(extension, params);
[email protected]d13950e2009-12-04 01:43:02240 } else {
[email protected]fd50e7b2011-11-03 09:20:25241 function->OnQuotaExceeded();
[email protected]efd75992011-12-15 22:42:42242 LogFailure(extension, params.name, kQuotaExceeded);
[email protected]d13950e2009-12-04 01:43:02243 }
[email protected]720ad1312012-02-27 23:07:36244
245 // We only adjust the keepalive count for UIThreadExtensionFunction for
246 // now, largely for simplicity's sake. This is OK because currently, only
247 // the webRequest API uses IOThreadExtensionFunction, and that API is not
248 // compatible with lazy background pages.
249 profile()->GetExtensionProcessManager()->IncrementLazyKeepaliveCount(
250 extension);
251}
252
253void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted(
254 const Extension* extension) {
255 profile()->GetExtensionProcessManager()->DecrementLazyKeepaliveCount(
256 extension);
[email protected]bfdffe2b2009-04-24 22:05:35257}
258
[email protected]c357acb42011-06-09 20:52:42259// static
260ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
261 const ExtensionHostMsg_Request_Params& params,
262 const Extension* extension,
[email protected]6f371442011-11-09 06:45:46263 int requesting_process_id,
264 const extensions::ProcessMap& process_map,
[email protected]5bc248a2012-04-04 23:38:11265 extensions::ExtensionAPI* api,
[email protected]673514522011-07-13 18:17:18266 void* profile,
[email protected]c357acb42011-06-09 20:52:42267 IPC::Message::Sender* ipc_sender,
268 int routing_id) {
[email protected]c357acb42011-06-09 20:52:42269 if (!extension) {
[email protected]6f371442011-11-09 06:45:46270 LOG(ERROR) << "Specified extension does not exist.";
271 SendAccessDenied(ipc_sender, routing_id, params.request_id);
272 return NULL;
273 }
274
[email protected]5bc248a2012-04-04 23:38:11275 if (api->IsPrivileged(params.name) &&
[email protected]83820d42011-11-12 22:03:11276 !process_map.Contains(extension->id(), requesting_process_id)) {
[email protected]6f371442011-11-09 06:45:46277 LOG(ERROR) << "Extension API called from incorrect process "
278 << requesting_process_id
279 << " from URL " << params.source_url.spec();
[email protected]c357acb42011-06-09 20:52:42280 SendAccessDenied(ipc_sender, routing_id, params.request_id);
281 return NULL;
282 }
283
[email protected]0d3e4a22011-06-23 19:02:52284 if (!extension->HasAPIPermission(params.name)) {
[email protected]c357acb42011-06-09 20:52:42285 LOG(ERROR) << "Extension " << extension->id() << " does not have "
286 << "permission to function: " << params.name;
287 SendAccessDenied(ipc_sender, routing_id, params.request_id);
288 return NULL;
289 }
290
291 ExtensionFunction* function =
[email protected]ae33d322012-03-19 22:24:35292 ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
[email protected]c357acb42011-06-09 20:52:42293 function->SetArgs(&params.arguments);
294 function->set_source_url(params.source_url);
295 function->set_request_id(params.request_id);
296 function->set_has_callback(params.has_callback);
297 function->set_user_gesture(params.user_gesture);
298 function->set_extension(extension);
[email protected]637bf322011-10-01 20:46:32299 function->set_profile_id(profile);
[email protected]c357acb42011-06-09 20:52:42300 return function;
301}
302
303// static
[email protected]c5dbef02011-05-13 05:06:09304void ExtensionFunctionDispatcher::SendAccessDenied(
[email protected]c357acb42011-06-09 20:52:42305 IPC::Message::Sender* ipc_sender, int routing_id, int request_id) {
[email protected]602542d2012-04-20 02:48:01306 ListValue empty_list;
[email protected]c357acb42011-06-09 20:52:42307 ipc_sender->Send(new ExtensionMsg_Response(
[email protected]602542d2012-04-20 02:48:01308 routing_id, request_id, false, empty_list,
[email protected]c5dbef02011-05-13 05:06:09309 "Access to extension API denied."));
[email protected]bfdffe2b2009-04-24 22:05:35310}