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