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_SelectAll
, SelectAll
)
321 IPC_MESSAGE_HANDLER(AutomationMsg_Cut
, Cut
)
322 IPC_MESSAGE_HANDLER(AutomationMsg_Copy
, Copy
)
323 IPC_MESSAGE_HANDLER(AutomationMsg_Paste
, Paste
)
324 IPC_MESSAGE_HANDLER(AutomationMsg_ReloadAsync
, ReloadAsync
)
325 IPC_MESSAGE_HANDLER(AutomationMsg_StopAsync
, StopAsync
)
326 IPC_MESSAGE_HANDLER(AutomationMsg_SetPageFontSize
, OnSetPageFontSize
)
327 IPC_MESSAGE_HANDLER(AutomationMsg_JavaScriptStressTestControl
,
328 JavaScriptStressTestControl
)
329 IPC_MESSAGE_HANDLER(AutomationMsg_BeginTracing
, BeginTracing
)
330 IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_EndTracing
, EndTracing
)
331 IPC_MESSAGE_UNHANDLED(handled
= false)
332 IPC_END_MESSAGE_MAP_EX()
334 OnUnhandledMessage(message
);
335 if (!deserialize_success
)
336 OnMessageDeserializationFailure();
340 void AutomationProvider::OnUnhandledMessage(const IPC::Message
& message
) {
341 // We should not hang here. Print a message to indicate what's going on,
342 // and disconnect the channel to notify the caller about the error
343 // in a way it can't ignore, and make any further attempts to send
344 // messages fail fast.
345 LOG(ERROR
) << "AutomationProvider received a message it can't handle. "
346 << "Message type: " << message
.type()
347 << ", routing ID: " << message
.routing_id() << ". "
348 << "Please make sure that you use switches::kTestingChannelID "
349 << "for test code (TestingAutomationProvider), and "
350 << "switches::kAutomationClientChannelID for everything else "
351 << "(like ChromeFrame). Closing the automation channel.";
355 void AutomationProvider::OnMessageDeserializationFailure() {
356 LOG(ERROR
) << "Failed to deserialize IPC message. "
357 << "Closing the automation channel.";
361 void AutomationProvider::HandleUnused(const IPC::Message
& message
, int handle
) {
362 if (window_tracker_
->ContainsHandle(handle
)) {
363 window_tracker_
->Remove(window_tracker_
->GetResource(handle
));
367 bool AutomationProvider::ReinitializeChannel() {
368 base::ThreadRestrictions::ScopedAllowIO allow_io
;
370 // Make sure any old channels are cleaned up before starting up a new one.
372 return InitializeChannel(channel_id_
);
375 void AutomationProvider::OnChannelError() {
376 if (reinitialize_on_channel_error_
) {
377 VLOG(1) << "AutomationProxy disconnected, resetting AutomationProvider.";
378 if (ReinitializeChannel())
380 VLOG(1) << "Error reinitializing AutomationProvider channel.";
382 VLOG(1) << "AutomationProxy went away, shutting down app.";
383 g_browser_process
->GetAutomationProviderList()->RemoveProvider(this);
386 bool AutomationProvider::Send(IPC::Message
* msg
) {
387 DCHECK(channel_
.get());
388 return channel_
->Send(msg
);
391 Browser
* AutomationProvider::FindAndActivateTab(
392 NavigationController
* controller
) {
393 content::WebContentsDelegate
* d
= controller
->GetWebContents()->GetDelegate();
395 d
->ActivateContents(controller
->GetWebContents());
396 return chrome::FindBrowserWithWebContents(controller
->GetWebContents());
399 void AutomationProvider::HandleFindRequest(
401 const AutomationMsg_Find_Params
& params
,
402 IPC::Message
* reply_message
) {
403 if (!tab_tracker_
->ContainsHandle(handle
)) {
404 AutomationMsg_Find::WriteReplyParams(reply_message
, -1, -1);
409 NavigationController
* nav
= tab_tracker_
->GetResource(handle
);
410 WebContents
* web_contents
= nav
->GetWebContents();
412 SendFindRequest(web_contents
,
414 params
.search_string
,
421 void AutomationProvider::SendFindRequest(
422 WebContents
* web_contents
,
424 const base::string16
& search_string
,
428 IPC::Message
* reply_message
) {
429 int request_id
= FindInPageNotificationObserver::kFindInPageRequestId
;
430 FindInPageNotificationObserver
* observer
=
431 new FindInPageNotificationObserver(this,
436 find_in_page_observer_
.reset(observer
);
438 FindTabHelper
* find_tab_helper
= FindTabHelper::FromWebContents(web_contents
);
440 find_tab_helper
->set_current_find_request_id(request_id
);
442 WebFindOptions options
;
443 options
.forward
= forward
;
444 options
.matchCase
= match_case
;
445 options
.findNext
= find_next
;
447 FindInPageNotificationObserver::kFindInPageRequestId
, search_string
,
451 WebContents
* AutomationProvider::GetWebContentsForHandle(
452 int handle
, NavigationController
** tab
) {
453 if (tab_tracker_
->ContainsHandle(handle
)) {
454 NavigationController
* nav_controller
= tab_tracker_
->GetResource(handle
);
456 *tab
= nav_controller
;
457 return nav_controller
->GetWebContents();
462 // Gets the current used encoding name of the page in the specified tab.
463 void AutomationProvider::OverrideEncoding(int tab_handle
,
464 const std::string
& encoding_name
,
467 if (tab_tracker_
->ContainsHandle(tab_handle
)) {
468 NavigationController
* nav
= tab_tracker_
->GetResource(tab_handle
);
471 Browser
* browser
= FindAndActivateTab(nav
);
473 // If the browser has UI, simulate what a user would do.
474 // Activate the tab and then click the encoding menu.
475 if (browser
&& chrome::IsCommandEnabled(browser
, IDC_ENCODING_MENU
)) {
476 int selected_encoding_id
=
477 CharacterEncoding::GetCommandIdByCanonicalEncodingName(encoding_name
);
478 if (selected_encoding_id
) {
479 browser
->OverrideEncoding(selected_encoding_id
);
483 // There is no UI, Chrome probably runs as Chrome-Frame mode.
484 // Try to get WebContents and call its SetOverrideEncoding method.
485 WebContents
* contents
= nav
->GetWebContents();
488 const std::string selected_encoding
=
489 CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding_name
);
490 if (selected_encoding
.empty())
492 contents
->SetOverrideEncoding(selected_encoding
);
497 void AutomationProvider::SelectAll(int tab_handle
) {
498 RenderViewHost
* view
= GetViewForTab(tab_handle
);
507 void AutomationProvider::Cut(int tab_handle
) {
508 RenderViewHost
* view
= GetViewForTab(tab_handle
);
517 void AutomationProvider::Copy(int tab_handle
) {
518 RenderViewHost
* view
= GetViewForTab(tab_handle
);
527 void AutomationProvider::Paste(int tab_handle
) {
528 RenderViewHost
* view
= GetViewForTab(tab_handle
);
537 void AutomationProvider::ReloadAsync(int tab_handle
) {
538 if (tab_tracker_
->ContainsHandle(tab_handle
)) {
539 NavigationController
* tab
= tab_tracker_
->GetResource(tab_handle
);
545 const bool check_for_repost
= true;
546 tab
->Reload(check_for_repost
);
550 void AutomationProvider::StopAsync(int tab_handle
) {
551 RenderViewHost
* view
= GetViewForTab(tab_handle
);
553 // We tolerate StopAsync being called even before a view has been created.
554 // So just log a warning instead of a NOTREACHED().
555 DLOG(WARNING
) << "StopAsync: no view for handle " << tab_handle
;
562 void AutomationProvider::OnSetPageFontSize(int tab_handle
,
564 AutomationPageFontSize automation_font_size
=
565 static_cast<AutomationPageFontSize
>(font_size
);
567 if (automation_font_size
< SMALLEST_FONT
||
568 automation_font_size
> LARGEST_FONT
) {
569 DLOG(ERROR
) << "Invalid font size specified : "
574 if (tab_tracker_
->ContainsHandle(tab_handle
)) {
575 NavigationController
* tab
= tab_tracker_
->GetResource(tab_handle
);
577 if (tab
&& tab
->GetWebContents()) {
578 DCHECK(tab
->GetWebContents()->GetBrowserContext() != NULL
);
579 Profile
* profile
= Profile::FromBrowserContext(
580 tab
->GetWebContents()->GetBrowserContext());
581 profile
->GetPrefs()->SetInteger(prefs::kWebKitDefaultFontSize
, font_size
);
586 void AutomationProvider::JavaScriptStressTestControl(int tab_handle
,
589 RenderViewHost
* view
= GetViewForTab(tab_handle
);
595 view
->Send(new ChromeViewMsg_JavaScriptStressTestControl(
596 view
->GetRoutingID(), cmd
, param
));
599 void AutomationProvider::BeginTracing(const std::string
& category_patterns
,
601 *success
= TracingController::GetInstance()->EnableRecording(
602 category_patterns
, TracingController::DEFAULT_OPTIONS
,
603 TracingController::EnableRecordingDoneCallback());
606 void AutomationProvider::EndTracing(IPC::Message
* reply_message
) {
608 if (!TracingController::GetInstance()->DisableRecording(
609 path
, base::Bind(&AutomationProvider::OnTraceDataCollected
, this,
611 // If failed to call EndTracingAsync, need to reply with failure now.
612 AutomationMsg_EndTracing::WriteReplyParams(reply_message
, path
, false);
615 // Otherwise defer EndTracing reply until TraceController calls us back.
618 void AutomationProvider::OnTraceDataCollected(IPC::Message
* reply_message
,
619 const base::FilePath
& path
) {
621 AutomationMsg_EndTracing::WriteReplyParams(reply_message
, path
, true);
626 RenderViewHost
* AutomationProvider::GetViewForTab(int tab_handle
) {
627 if (tab_tracker_
->ContainsHandle(tab_handle
)) {
628 NavigationController
* tab
= tab_tracker_
->GetResource(tab_handle
);
634 WebContents
* web_contents
= tab
->GetWebContents();
640 RenderViewHost
* view_host
= web_contents
->GetRenderViewHost();