1 // Copyright (c) 2012 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.
5 #include "chrome/browser/automation/automation_provider.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/files/file_path.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_string_value_serializer.h"
16 #include "base/json/json_writer.h"
17 #include "base/json/string_escape.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/synchronization/waitable_event.h"
26 #include "base/threading/thread.h"
27 #include "base/values.h"
28 #include "chrome/app/chrome_command_ids.h"
29 #include "chrome/browser/automation/automation_browser_tracker.h"
30 #include "chrome/browser/automation/automation_provider_list.h"
31 #include "chrome/browser/automation/automation_provider_observers.h"
32 #include "chrome/browser/automation/automation_tab_tracker.h"
33 #include "chrome/browser/automation/automation_window_tracker.h"
34 #include "chrome/browser/browser_process.h"
35 #include "chrome/browser/browsing_data/browsing_data_helper.h"
36 #include "chrome/browser/browsing_data/browsing_data_remover.h"
37 #include "chrome/browser/character_encoding.h"
38 #include "chrome/browser/content_settings/host_content_settings_map.h"
39 #include "chrome/browser/net/url_request_mock_util.h"
40 #include "chrome/browser/printing/print_job.h"
41 #include "chrome/browser/profiles/profile_manager.h"
42 #include "chrome/browser/ssl/ssl_blocking_page.h"
43 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
44 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
45 #include "chrome/browser/ui/browser_commands.h"
46 #include "chrome/browser/ui/browser_finder.h"
47 #include "chrome/browser/ui/browser_tabstrip.h"
48 #include "chrome/browser/ui/browser_window.h"
49 #include "chrome/browser/ui/find_bar/find_bar.h"
50 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
51 #include "chrome/browser/ui/find_bar/find_notification_details.h"
52 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
53 #include "chrome/browser/ui/login/login_prompt.h"
54 #include "chrome/browser/ui/omnibox/location_bar.h"
55 #include "chrome/common/automation_constants.h"
56 #include "chrome/common/automation_messages.h"
57 #include "chrome/common/chrome_constants.h"
58 #include "chrome/common/chrome_paths.h"
59 #include "chrome/common/chrome_switches.h"
60 #include "chrome/common/chrome_version_info.h"
61 #include "chrome/common/pref_names.h"
62 #include "chrome/common/render_messages.h"
63 #include "chrome/common/url_constants.h"
64 #include "content/public/browser/browser_thread.h"
65 #include "content/public/browser/download_item.h"
66 #include "content/public/browser/native_web_keyboard_event.h"
67 #include "content/public/browser/render_view_host.h"
68 #include "content/public/browser/tracing_controller.h"
69 #include "content/public/browser/web_contents.h"
70 #include "content/public/browser/web_contents_view.h"
71 #include "ipc/ipc_channel_proxy.h"
72 #include "net/proxy/proxy_config_service_fixed.h"
73 #include "net/proxy/proxy_service.h"
74 #include "net/url_request/url_request_context.h"
75 #include "net/url_request/url_request_context_getter.h"
76 #include "third_party/WebKit/public/web/WebFindOptions.h"
78 #if defined(OS_CHROMEOS)
79 #include "chromeos/chromeos_switches.h"
80 #include "chromeos/login/login_state.h"
81 #endif // defined(OS_CHROMEOS)
83 using blink::WebFindOptions;
85 using content::BrowserThread;
86 using content::DownloadItem;
87 using content::NavigationController;
88 using content::RenderViewHost;
89 using content::TracingController;
90 using content::WebContents;
92 AutomationProvider::AutomationProvider(Profile* profile)
95 reinitialize_on_channel_error_(
96 CommandLine::ForCurrentProcess()->HasSwitch(
97 switches::kAutomationReinitializeOnChannelError)),
98 use_initial_load_observers_(true),
100 initial_tab_loads_complete_(false),
101 login_webui_ready_(true) {
102 TRACE_EVENT_BEGIN_ETW("AutomationProvider::AutomationProvider", 0, "");
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
106 browser_tracker_.reset(new AutomationBrowserTracker(this));
107 tab_tracker_.reset(new AutomationTabTracker(this));
108 window_tracker_.reset(new AutomationWindowTracker(this));
109 metric_event_duration_observer_.reset(new MetricEventDurationObserver());
111 TRACE_EVENT_END_ETW("AutomationProvider::AutomationProvider", 0, "");
114 AutomationProvider::~AutomationProvider() {
119 void AutomationProvider::set_profile(Profile* profile) {
123 bool AutomationProvider::InitializeChannel(const std::string& channel_id) {
124 TRACE_EVENT_BEGIN_ETW("AutomationProvider::InitializeChannel", 0, "");
126 channel_id_ = channel_id;
127 std::string effective_channel_id = channel_id;
129 // If the channel_id starts with kNamedInterfacePrefix, create a named IPC
130 // server and listen on it, else connect as client to an existing IPC server
131 bool use_named_interface =
132 channel_id.find(automation::kNamedInterfacePrefix) == 0;
133 if (use_named_interface) {
134 effective_channel_id = channel_id.substr(
135 strlen(automation::kNamedInterfacePrefix));
136 if (effective_channel_id.length() <= 0)
139 reinitialize_on_channel_error_ = true;
142 channel_.reset(new IPC::ChannelProxy(
143 effective_channel_id,
144 GetChannelMode(use_named_interface),
146 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
148 #if defined(OS_CHROMEOS)
149 if (use_initial_load_observers_) {
150 // Wait for webui login to be ready.
151 // Observer will delete itself.
152 if (CommandLine::ForCurrentProcess()->HasSwitch(
153 chromeos::switches::kLoginManager) &&
154 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
155 login_webui_ready_ = false;
156 new OOBEWebuiReadyObserver(this);
161 TRACE_EVENT_END_ETW("AutomationProvider::InitializeChannel", 0, "");
166 IPC::Channel::Mode AutomationProvider::GetChannelMode(
167 bool use_named_interface) {
168 if (use_named_interface)
169 return IPC::Channel::MODE_NAMED_SERVER;
171 return IPC::Channel::MODE_CLIENT;
174 std::string AutomationProvider::GetProtocolVersion() {
175 chrome::VersionInfo version_info;
176 return version_info.Version().c_str();
179 void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) {
180 VLOG(2) << "SetExpectedTabCount:" << expected_tabs;
181 if (expected_tabs == 0)
182 OnInitialTabLoadsComplete();
184 initial_load_observer_.reset(new InitialLoadObserver(expected_tabs, this));
187 void AutomationProvider::OnInitialTabLoadsComplete() {
188 initial_tab_loads_complete_ = true;
189 VLOG(2) << "OnInitialTabLoadsComplete";
190 SendInitialLoadMessage();
193 void AutomationProvider::OnOOBEWebuiReady() {
194 login_webui_ready_ = true;
195 VLOG(2) << "OnOOBEWebuiReady";
196 SendInitialLoadMessage();
199 void AutomationProvider::SendInitialLoadMessage() {
200 if (is_connected_ && initial_tab_loads_complete_ && login_webui_ready_) {
201 VLOG(2) << "Initial loads complete; sending initial loads message.";
202 Send(new AutomationMsg_InitialLoadsComplete());
206 void AutomationProvider::DisableInitialLoadObservers() {
207 use_initial_load_observers_ = false;
208 OnInitialTabLoadsComplete();
212 int AutomationProvider::GetIndexForNavigationController(
213 const NavigationController* controller, const Browser* parent) const {
215 return parent->tab_strip_model()->GetIndexOfWebContents(
216 controller->GetWebContents());
219 // TODO(phajdan.jr): move to TestingAutomationProvider.
220 base::DictionaryValue* AutomationProvider::GetDictionaryFromDownloadItem(
221 const DownloadItem* download, bool incognito) {
222 const char *download_state_string = NULL;
223 switch (download->GetState()) {
224 case DownloadItem::IN_PROGRESS:
225 download_state_string = "IN_PROGRESS";
227 case DownloadItem::CANCELLED:
228 download_state_string = "CANCELLED";
230 case DownloadItem::INTERRUPTED:
231 download_state_string = "INTERRUPTED";
233 case DownloadItem::COMPLETE:
234 download_state_string = "COMPLETE";
236 case DownloadItem::MAX_DOWNLOAD_STATE:
238 download_state_string = "UNKNOWN";
241 DCHECK(download_state_string);
242 if (!download_state_string)
243 download_state_string = "UNKNOWN";
245 const char* download_danger_type_string = NULL;
246 switch (download->GetDangerType()) {
247 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
248 download_danger_type_string = "NOT_DANGEROUS";
250 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
251 download_danger_type_string = "DANGEROUS_FILE";
253 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
254 download_danger_type_string = "DANGEROUS_URL";
256 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
257 download_danger_type_string = "DANGEROUS_CONTENT";
259 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
260 download_danger_type_string = "MAYBE_DANGEROUS_CONTENT";
262 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
263 download_danger_type_string = "UNCOMMON_CONTENT";
265 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
266 download_danger_type_string = "USER_VALIDATED";
268 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
269 download_danger_type_string = "DANGEROUS_HOST";
271 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
272 download_danger_type_string = "POTENTIALLY_UNWANTED";
274 case content::DOWNLOAD_DANGER_TYPE_MAX:
276 download_danger_type_string = "UNKNOWN";
279 DCHECK(download_danger_type_string);
280 if (!download_danger_type_string)
281 download_danger_type_string = "UNKNOWN";
283 base::DictionaryValue* dl_item_value = new base::DictionaryValue;
284 dl_item_value->SetInteger("id", static_cast<int>(download->GetId()));
285 dl_item_value->SetString("url", download->GetURL().spec());
286 dl_item_value->SetString("referrer_url", download->GetReferrerUrl().spec());
287 dl_item_value->SetString("file_name",
288 download->GetFileNameToReportUser().value());
289 dl_item_value->SetString("full_path",
290 download->GetTargetFilePath().value());
291 dl_item_value->SetBoolean("is_paused", download->IsPaused());
292 dl_item_value->SetBoolean("open_when_complete",
293 download->GetOpenWhenComplete());
294 dl_item_value->SetBoolean("is_temporary", download->IsTemporary());
295 dl_item_value->SetBoolean("is_otr", incognito);
296 dl_item_value->SetString("state", download_state_string);
297 dl_item_value->SetString("danger_type", download_danger_type_string);
298 dl_item_value->SetInteger("PercentComplete", download->PercentComplete());
300 return dl_item_value;
303 void AutomationProvider::OnChannelConnected(int pid) {
304 is_connected_ = true;
306 // Send a hello message with our current automation protocol version.
307 VLOG(2) << "Testing channel connected, sending hello message";
308 channel_->Send(new AutomationMsg_Hello(GetProtocolVersion()));
310 SendInitialLoadMessage();
313 bool AutomationProvider::OnMessageReceived(const IPC::Message& message) {
315 bool deserialize_success = true;
316 IPC_BEGIN_MESSAGE_MAP_EX(AutomationProvider, message, deserialize_success)
317 IPC_MESSAGE_HANDLER(AutomationMsg_HandleUnused, HandleUnused)
318 IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_Find, HandleFindRequest)
319 IPC_MESSAGE_HANDLER(AutomationMsg_OverrideEncoding, OverrideEncoding)
320 IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync, ReloadAsync)
321 IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync, StopAsync)
322 IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize, OnSetPageFontSize)
323 IPC_MESSAGE_HANDLER(AutomationMsg_JavaScriptStressTestControl,
324 JavaScriptStressTestControl)
325 IPC_MESSAGE_HANDLER(AutomationMsg_BeginTracing, BeginTracing)
326 IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EndTracing, EndTracing)
327 IPC_MESSAGE_UNHANDLED(handled = false)
328 IPC_END_MESSAGE_MAP_EX()
330 OnUnhandledMessage(message);
331 if (!deserialize_success)
332 OnMessageDeserializationFailure();
336 void AutomationProvider::OnUnhandledMessage(const IPC::Message& message) {
337 // We should not hang here. Print a message to indicate what's going on,
338 // and disconnect the channel to notify the caller about the error
339 // in a way it can't ignore, and make any further attempts to send
340 // messages fail fast.
341 LOG(ERROR) << "AutomationProvider received a message it can't handle. "
342 << "Message type: " << message.type()
343 << ", routing ID: " << message.routing_id() << ". "
344 << "Please make sure that you use switches::kTestingChannelID "
345 << "for test code (TestingAutomationProvider), and "
346 << "switches::kAutomationClientChannelID for everything else "
347 << "(like ChromeFrame). Closing the automation channel.";
351 void AutomationProvider::OnMessageDeserializationFailure() {
352 LOG(ERROR) << "Failed to deserialize IPC message. "
353 << "Closing the automation channel.";
357 void AutomationProvider::HandleUnused(const IPC::Message& message, int handle) {
358 if (window_tracker_->ContainsHandle(handle)) {
359 window_tracker_->Remove(window_tracker_->GetResource(handle));
363 bool AutomationProvider::ReinitializeChannel() {
364 base::ThreadRestrictions::ScopedAllowIO allow_io;
366 // Make sure any old channels are cleaned up before starting up a new one.
368 return InitializeChannel(channel_id_);
371 void AutomationProvider::OnChannelError() {
372 if (reinitialize_on_channel_error_) {
373 VLOG(1) << "AutomationProxy disconnected, resetting AutomationProvider.";
374 if (ReinitializeChannel())
376 VLOG(1) << "Error reinitializing AutomationProvider channel.";
378 VLOG(1) << "AutomationProxy went away, shutting down app.";
379 g_browser_process->GetAutomationProviderList()->RemoveProvider(this);
382 bool AutomationProvider::Send(IPC::Message* msg) {
383 DCHECK(channel_.get());
384 return channel_->Send(msg);
387 Browser* AutomationProvider::FindAndActivateTab(
388 NavigationController* controller) {
389 content::WebContentsDelegate* d = controller->GetWebContents()->GetDelegate();
391 d->ActivateContents(controller->GetWebContents());
392 return chrome::FindBrowserWithWebContents(controller->GetWebContents());
395 void AutomationProvider::HandleFindRequest(
397 const AutomationMsg_Find_Params& params,
398 IPC::Message* reply_message) {
399 if (!tab_tracker_->ContainsHandle(handle)) {
400 AutomationMsg_Find::WriteReplyParams(reply_message, -1, -1);
405 NavigationController* nav = tab_tracker_->GetResource(handle);
406 WebContents* web_contents = nav->GetWebContents();
408 SendFindRequest(web_contents,
410 params.search_string,
417 void AutomationProvider::SendFindRequest(
418 WebContents* web_contents,
420 const base::string16& search_string,
424 IPC::Message* reply_message) {
425 int request_id = FindInPageNotificationObserver::kFindInPageRequestId;
426 FindInPageNotificationObserver* observer =
427 new FindInPageNotificationObserver(this,
432 find_in_page_observer_.reset(observer);
434 FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents);
436 find_tab_helper->set_current_find_request_id(request_id);
438 WebFindOptions options;
439 options.forward = forward;
440 options.matchCase = match_case;
441 options.findNext = find_next;
443 FindInPageNotificationObserver::kFindInPageRequestId, search_string,
447 WebContents* AutomationProvider::GetWebContentsForHandle(
448 int handle, NavigationController** tab) {
449 if (tab_tracker_->ContainsHandle(handle)) {
450 NavigationController* nav_controller = tab_tracker_->GetResource(handle);
452 *tab = nav_controller;
453 return nav_controller->GetWebContents();
458 // Gets the current used encoding name of the page in the specified tab.
459 void AutomationProvider::OverrideEncoding(int tab_handle,
460 const std::string& encoding_name,
463 if (tab_tracker_->ContainsHandle(tab_handle)) {
464 NavigationController* nav = tab_tracker_->GetResource(tab_handle);
467 Browser* browser = FindAndActivateTab(nav);
469 // If the browser has UI, simulate what a user would do.
470 // Activate the tab and then click the encoding menu.
471 if (browser && chrome::IsCommandEnabled(browser, IDC_ENCODING_MENU)) {
472 int selected_encoding_id =
473 CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name);
474 if (selected_encoding_id) {
475 browser->OverrideEncoding(selected_encoding_id);
479 // There is no UI, Chrome probably runs as Chrome-Frame mode.
480 // Try to get WebContents and call its SetOverrideEncoding method.
481 WebContents* contents = nav->GetWebContents();
484 const std::string selected_encoding =
485 CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name);
486 if (selected_encoding.empty())
488 contents->SetOverrideEncoding(selected_encoding);
493 void AutomationProvider::ReloadAsync(int tab_handle) {
494 if (tab_tracker_->ContainsHandle(tab_handle)) {
495 NavigationController* tab = tab_tracker_->GetResource(tab_handle);
501 const bool check_for_repost = true;
502 tab->Reload(check_for_repost);
506 void AutomationProvider::StopAsync(int tab_handle) {
507 RenderViewHost* view = GetViewForTab(tab_handle);
509 // We tolerate StopAsync being called even before a view has been created.
510 // So just log a warning instead of a NOTREACHED().
511 DLOG(WARNING) << "StopAsync: no view for handle " << tab_handle;
518 void AutomationProvider::OnSetPageFontSize(int tab_handle,
520 AutomationPageFontSize automation_font_size =
521 static_cast<AutomationPageFontSize>(font_size);
523 if (automation_font_size < SMALLEST_FONT ||
524 automation_font_size > LARGEST_FONT) {
525 DLOG(ERROR) << "Invalid font size specified : "
530 if (tab_tracker_->ContainsHandle(tab_handle)) {
531 NavigationController* tab = tab_tracker_->GetResource(tab_handle);
533 if (tab && tab->GetWebContents()) {
534 DCHECK(tab->GetWebContents()->GetBrowserContext() != NULL);
535 Profile* profile = Profile::FromBrowserContext(
536 tab->GetWebContents()->GetBrowserContext());
537 profile->GetPrefs()->SetInteger(prefs::kWebKitDefaultFontSize, font_size);
542 void AutomationProvider::JavaScriptStressTestControl(int tab_handle,
545 RenderViewHost* view = GetViewForTab(tab_handle);
551 view->Send(new ChromeViewMsg_JavaScriptStressTestControl(
552 view->GetRoutingID(), cmd, param));
555 void AutomationProvider::BeginTracing(const std::string& category_patterns,
557 *success = TracingController::GetInstance()->EnableRecording(
558 category_patterns, TracingController::DEFAULT_OPTIONS,
559 TracingController::EnableRecordingDoneCallback());
562 void AutomationProvider::EndTracing(IPC::Message* reply_message) {
564 if (!TracingController::GetInstance()->DisableRecording(
565 path, base::Bind(&AutomationProvider::OnTraceDataCollected, this,
567 // If failed to call EndTracingAsync, need to reply with failure now.
568 AutomationMsg_EndTracing::WriteReplyParams(reply_message, path, false);
571 // Otherwise defer EndTracing reply until TraceController calls us back.
574 void AutomationProvider::OnTraceDataCollected(IPC::Message* reply_message,
575 const base::FilePath& path) {
577 AutomationMsg_EndTracing::WriteReplyParams(reply_message, path, true);
582 RenderViewHost* AutomationProvider::GetViewForTab(int tab_handle) {
583 if (tab_tracker_->ContainsHandle(tab_handle)) {
584 NavigationController* tab = tab_tracker_->GetResource(tab_handle);
590 WebContents* web_contents = tab->GetWebContents();
596 RenderViewHost* view_host = web_contents->GetRenderViewHost();