chrome_extension_message_filter.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// 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/renderer_host/chrome_extension_message_filter.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/files/file_path.h" 10#include "base/strings/utf_string_conversions.h" 11#include "chrome/browser/browser_process.h" 12#include "chrome/browser/chrome_notification_types.h" 13#include "chrome/browser/extensions/activity_log/activity_action_constants.h" 14#include "chrome/browser/extensions/activity_log/activity_actions.h" 15#include "chrome/browser/extensions/activity_log/activity_log.h" 16#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h" 17#include "chrome/browser/extensions/api/messaging/message_service.h" 18#include "chrome/browser/profiles/profile.h" 19#include "chrome/browser/profiles/profile_manager.h" 20#include "chrome/common/extensions/api/i18n/default_locale_handler.h" 21#include "chrome/common/extensions/chrome_extension_messages.h" 22#include "content/public/browser/notification_service.h" 23#include "content/public/browser/render_process_host.h" 24#include "extensions/browser/extension_system.h" 25#include "extensions/common/api/messaging/message.h" 26#include "extensions/common/constants.h" 27#include "extensions/common/extension_messages.h" 28#include "extensions/common/file_util.h" 29#include "extensions/common/message_bundle.h" 30 31using content::BrowserThread; 32using extensions::APIPermission; 33 34namespace { 35 36const uint32 kFilteredMessageClasses[] = { 37 ChromeExtensionMsgStart, 38 ExtensionMsgStart, 39}; 40 41// Logs an action to the extension activity log for the specified profile. Can 42// be called from any thread. 43void AddActionToExtensionActivityLog( 44 Profile* profile, 45 scoped_refptr<extensions::Action> action) { 46 // The ActivityLog can only be accessed from the main (UI) thread. If we're 47 // running on the wrong thread, re-dispatch from the main thread. 48 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 49 BrowserThread::PostTask( 50 BrowserThread::UI, FROM_HERE, 51 base::Bind(&AddActionToExtensionActivityLog, profile, action)); 52 } else { 53 if (!g_browser_process->profile_manager()->IsValidProfile(profile)) 54 return; 55 // If the action included a URL, check whether it is for an incognito 56 // profile. The check is performed here so that it can safely be done from 57 // the UI thread. 58 if (action->page_url().is_valid() || !action->page_title().empty()) 59 action->set_page_incognito(profile->IsOffTheRecord()); 60 extensions::ActivityLog* activity_log = 61 extensions::ActivityLog::GetInstance(profile); 62 activity_log->LogAction(action); 63 } 64} 65 66} // namespace 67 68ChromeExtensionMessageFilter::ChromeExtensionMessageFilter( 69 int render_process_id, 70 Profile* profile) 71 : BrowserMessageFilter(kFilteredMessageClasses, 72 arraysize(kFilteredMessageClasses)), 73 render_process_id_(render_process_id), 74 profile_(profile), 75 extension_info_map_( 76 extensions::ExtensionSystem::Get(profile)->info_map()) { 77 DCHECK_CURRENTLY_ON(BrowserThread::UI); 78 notification_registrar_.Add(this, 79 chrome::NOTIFICATION_PROFILE_DESTROYED, 80 content::Source<Profile>(profile)); 81} 82 83ChromeExtensionMessageFilter::~ChromeExtensionMessageFilter() { 84 DCHECK_CURRENTLY_ON(BrowserThread::UI); 85} 86 87bool ChromeExtensionMessageFilter::OnMessageReceived( 88 const IPC::Message& message) { 89 bool handled = true; 90 IPC_BEGIN_MESSAGE_MAP(ChromeExtensionMessageFilter, message) 91 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardRead, 92 OnCanTriggerClipboardRead) 93 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CanTriggerClipboardWrite, 94 OnCanTriggerClipboardWrite) 95 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToExtension, 96 OnOpenChannelToExtension) 97 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToTab, OnOpenChannelToTab) 98 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OpenChannelToNativeApp, 99 OnOpenChannelToNativeApp) 100 IPC_MESSAGE_HANDLER(ExtensionHostMsg_PostMessage, OnPostMessage) 101 IPC_MESSAGE_HANDLER_DELAY_REPLY(ExtensionHostMsg_GetMessageBundle, 102 OnGetExtMessageBundle) 103 IPC_MESSAGE_HANDLER(ExtensionHostMsg_CloseChannel, OnExtensionCloseChannel) 104 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddAPIActionToActivityLog, 105 OnAddAPIActionToExtensionActivityLog); 106 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddDOMActionToActivityLog, 107 OnAddDOMActionToExtensionActivityLog); 108 IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddEventToActivityLog, 109 OnAddEventToExtensionActivityLog); 110 IPC_MESSAGE_UNHANDLED(handled = false) 111 IPC_END_MESSAGE_MAP() 112 113 return handled; 114} 115 116void ChromeExtensionMessageFilter::OverrideThreadForMessage( 117 const IPC::Message& message, BrowserThread::ID* thread) { 118 switch (message.type()) { 119 case ExtensionHostMsg_PostMessage::ID: 120 case ExtensionHostMsg_CloseChannel::ID: 121 *thread = BrowserThread::UI; 122 break; 123 default: 124 break; 125 } 126} 127 128void ChromeExtensionMessageFilter::OnDestruct() const { 129 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 130 delete this; 131 } else { 132 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this); 133 } 134} 135 136void ChromeExtensionMessageFilter::OnCanTriggerClipboardRead( 137 const GURL& origin, bool* allowed) { 138 *allowed = extension_info_map_->SecurityOriginHasAPIPermission( 139 origin, render_process_id_, APIPermission::kClipboardRead); 140} 141 142void ChromeExtensionMessageFilter::OnCanTriggerClipboardWrite( 143 const GURL& origin, bool* allowed) { 144 // Since all extensions could historically write to the clipboard, preserve it 145 // for compatibility. 146 *allowed = (origin.SchemeIs(extensions::kExtensionScheme) || 147 extension_info_map_->SecurityOriginHasAPIPermission( 148 origin, render_process_id_, APIPermission::kClipboardWrite)); 149} 150 151void ChromeExtensionMessageFilter::OnOpenChannelToExtension( 152 int routing_id, 153 const ExtensionMsg_ExternalConnectionInfo& info, 154 const std::string& channel_name, 155 bool include_tls_channel_id, 156 int* port_id) { 157 int port2_id; 158 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); 159 160 BrowserThread::PostTask( 161 BrowserThread::UI, FROM_HERE, 162 base::Bind( 163 &ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread, 164 this, render_process_id_, routing_id, port2_id, info, 165 channel_name, include_tls_channel_id)); 166} 167 168void ChromeExtensionMessageFilter::OpenChannelToExtensionOnUIThread( 169 int source_process_id, int source_routing_id, 170 int receiver_port_id, 171 const ExtensionMsg_ExternalConnectionInfo& info, 172 const std::string& channel_name, 173 bool include_tls_channel_id) { 174 DCHECK_CURRENTLY_ON(BrowserThread::UI); 175 if (profile_) { 176 extensions::MessageService::Get(profile_) 177 ->OpenChannelToExtension(source_process_id, 178 source_routing_id, 179 receiver_port_id, 180 info.source_id, 181 info.target_id, 182 info.source_url, 183 channel_name, 184 include_tls_channel_id); 185 } 186} 187 188void ChromeExtensionMessageFilter::OnOpenChannelToNativeApp( 189 int routing_id, 190 const std::string& source_extension_id, 191 const std::string& native_app_name, 192 int* port_id) { 193 int port2_id; 194 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); 195 196 BrowserThread::PostTask( 197 BrowserThread::UI, FROM_HERE, 198 base::Bind( 199 &ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread, 200 this, routing_id, port2_id, source_extension_id, native_app_name)); 201} 202 203void ChromeExtensionMessageFilter::OpenChannelToNativeAppOnUIThread( 204 int source_routing_id, 205 int receiver_port_id, 206 const std::string& source_extension_id, 207 const std::string& native_app_name) { 208 DCHECK_CURRENTLY_ON(BrowserThread::UI); 209 if (profile_) { 210 extensions::MessageService::Get(profile_) 211 ->OpenChannelToNativeApp(render_process_id_, 212 source_routing_id, 213 receiver_port_id, 214 source_extension_id, 215 native_app_name); 216 } 217} 218 219void ChromeExtensionMessageFilter::OnOpenChannelToTab( 220 int routing_id, int tab_id, const std::string& extension_id, 221 const std::string& channel_name, int* port_id) { 222 int port2_id; 223 extensions::MessageService::AllocatePortIdPair(port_id, &port2_id); 224 225 BrowserThread::PostTask( 226 BrowserThread::UI, FROM_HERE, 227 base::Bind(&ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread, 228 this, render_process_id_, routing_id, port2_id, tab_id, 229 extension_id, channel_name)); 230} 231 232void ChromeExtensionMessageFilter::OpenChannelToTabOnUIThread( 233 int source_process_id, int source_routing_id, 234 int receiver_port_id, 235 int tab_id, 236 const std::string& extension_id, 237 const std::string& channel_name) { 238 DCHECK_CURRENTLY_ON(BrowserThread::UI); 239 if (profile_) { 240 extensions::MessageService::Get(profile_) 241 ->OpenChannelToTab(source_process_id, 242 source_routing_id, 243 receiver_port_id, 244 tab_id, 245 extension_id, 246 channel_name); 247 } 248} 249 250void ChromeExtensionMessageFilter::OnPostMessage( 251 int port_id, 252 const extensions::Message& message) { 253 extensions::MessageService::Get(profile_)->PostMessage(port_id, message); 254} 255 256void ChromeExtensionMessageFilter::OnGetExtMessageBundle( 257 const std::string& extension_id, IPC::Message* reply_msg) { 258 const extensions::Extension* extension = 259 extension_info_map_->extensions().GetByID(extension_id); 260 base::FilePath extension_path; 261 std::string default_locale; 262 if (extension) { 263 extension_path = extension->path(); 264 default_locale = extensions::LocaleInfo::GetDefaultLocale(extension); 265 } 266 267 BrowserThread::PostBlockingPoolTask( 268 FROM_HERE, 269 base::Bind( 270 &ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool, 271 this, extension_path, extension_id, default_locale, reply_msg)); 272} 273 274void ChromeExtensionMessageFilter::OnGetExtMessageBundleOnBlockingPool( 275 const base::FilePath& extension_path, 276 const std::string& extension_id, 277 const std::string& default_locale, 278 IPC::Message* reply_msg) { 279 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 280 281 scoped_ptr<extensions::MessageBundle::SubstitutionMap> dictionary_map( 282 extensions::file_util::LoadMessageBundleSubstitutionMap( 283 extension_path, extension_id, default_locale)); 284 285 ExtensionHostMsg_GetMessageBundle::WriteReplyParams(reply_msg, 286 *dictionary_map); 287 Send(reply_msg); 288} 289 290void ChromeExtensionMessageFilter::OnExtensionCloseChannel( 291 int port_id, 292 const std::string& error_message) { 293 if (!content::RenderProcessHost::FromID(render_process_id_)) 294 return; // To guard against crash in browser_tests shutdown. 295 296 extensions::MessageService* message_service = 297 extensions::MessageService::Get(profile_); 298 if (message_service) 299 message_service->CloseChannel(port_id, error_message); 300} 301 302void ChromeExtensionMessageFilter::OnAddAPIActionToExtensionActivityLog( 303 const std::string& extension_id, 304 const ExtensionHostMsg_APIActionOrEvent_Params& params) { 305 scoped_refptr<extensions::Action> action = new extensions::Action( 306 extension_id, base::Time::Now(), extensions::Action::ACTION_API_CALL, 307 params.api_call); 308 action->set_args(make_scoped_ptr(params.arguments.DeepCopy())); 309 if (!params.extra.empty()) { 310 action->mutable_other()->SetString( 311 activity_log_constants::kActionExtra, params.extra); 312 } 313 AddActionToExtensionActivityLog(profile_, action); 314} 315 316void ChromeExtensionMessageFilter::OnAddDOMActionToExtensionActivityLog( 317 const std::string& extension_id, 318 const ExtensionHostMsg_DOMAction_Params& params) { 319 scoped_refptr<extensions::Action> action = new extensions::Action( 320 extension_id, base::Time::Now(), extensions::Action::ACTION_DOM_ACCESS, 321 params.api_call); 322 action->set_args(make_scoped_ptr(params.arguments.DeepCopy())); 323 action->set_page_url(params.url); 324 action->set_page_title(base::UTF16ToUTF8(params.url_title)); 325 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, 326 params.call_type); 327 AddActionToExtensionActivityLog(profile_, action); 328} 329 330void ChromeExtensionMessageFilter::OnAddEventToExtensionActivityLog( 331 const std::string& extension_id, 332 const ExtensionHostMsg_APIActionOrEvent_Params& params) { 333 scoped_refptr<extensions::Action> action = new extensions::Action( 334 extension_id, base::Time::Now(), extensions::Action::ACTION_API_EVENT, 335 params.api_call); 336 action->set_args(make_scoped_ptr(params.arguments.DeepCopy())); 337 if (!params.extra.empty()) { 338 action->mutable_other()->SetString(activity_log_constants::kActionExtra, 339 params.extra); 340 } 341 AddActionToExtensionActivityLog(profile_, action); 342} 343 344void ChromeExtensionMessageFilter::Observe( 345 int type, 346 const content::NotificationSource& source, 347 const content::NotificationDetails& details) { 348 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type); 349 profile_ = NULL; 350} 351