1 // Copyright (c) 2010 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_frame/chrome_frame_automation.h"
7 #include "base/callback.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/trace_event.h"
11 #include "base/file_util.h"
12 #include "base/file_version_info.h"
13 #include "base/lock.h"
14 #include "base/logging.h"
15 #include "base/path_service.h"
16 #include "base/process_util.h"
17 #include "base/singleton.h"
18 #include "base/string_util.h"
19 #include "base/sys_info.h"
20 #include "base/waitable_event.h"
21 #include "chrome/app/client_util.h"
22 #include "chrome/common/chrome_constants.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/test/automation/tab_proxy.h"
25 #include "chrome_frame/chrome_launcher_utils.h"
26 #include "chrome_frame/crash_reporting/crash_metrics.h"
27 #include "chrome_frame/custom_sync_call_context.h"
28 #include "chrome_frame/utils.h"
31 int64 kAutomationServerReasonableLaunchDelay
= 1000; // in milliseconds
33 int64 kAutomationServerReasonableLaunchDelay
= 1000 * 10;
36 int kDefaultSendUMADataInterval
= 20000; // in milliseconds.
38 static const wchar_t kUmaSendIntervalValue
[] = L
"UmaSendInterval";
40 // This lock ensures that histograms created by ChromeFrame are thread safe.
41 // The histograms created in ChromeFrame can be initialized on multiple
43 Lock g_ChromeFrameHistogramLock
;
45 class ChromeFrameAutomationProxyImpl::TabProxyNotificationMessageFilter
46 : public IPC::ChannelProxy::MessageFilter
{
48 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker
* tracker
)
52 void AddTabProxy(AutomationHandle tab_proxy
) {
53 tabs_list_
.push_back(tab_proxy
);
56 void RemoveTabProxy(AutomationHandle tab_proxy
) {
57 tabs_list_
.remove(tab_proxy
);
60 virtual bool OnMessageReceived(const IPC::Message
& message
) {
61 if (message
.is_reply())
65 if (!ChromeFrameDelegateImpl::IsTabMessage(message
, &tab_handle
))
68 // Get AddRef-ed pointer to corresponding TabProxy object
69 TabProxy
* tab
= static_cast<TabProxy
*>(tracker_
->GetResource(tab_handle
));
71 tab
->OnMessageReceived(message
);
74 DLOG(ERROR
) << "Failed to find TabProxy for tab:" << tab_handle
;
79 virtual void OnChannelError() {
80 std::list
<AutomationHandle
>::const_iterator iter
= tabs_list_
.begin();
81 for (; iter
!= tabs_list_
.end(); ++iter
) {
82 // Get AddRef-ed pointer to corresponding TabProxy object
83 TabProxy
* tab
= static_cast<TabProxy
*>(tracker_
->GetResource(*iter
));
85 tab
->OnChannelError();
92 AutomationHandleTracker
* tracker_
;
93 std::list
<AutomationHandle
> tabs_list_
;
96 class ChromeFrameAutomationProxyImpl::CFMsgDispatcher
97 : public SyncMessageReplyDispatcher
{
99 CFMsgDispatcher() : SyncMessageReplyDispatcher() {}
101 virtual bool HandleMessageType(const IPC::Message
& msg
,
102 SyncMessageCallContext
* context
) {
103 switch (context
->message_type()) {
104 case AutomationMsg_CreateExternalTab::ID
:
105 case AutomationMsg_ConnectExternalTab::ID
:
106 InvokeCallback
<CreateExternalTabContext
>(msg
, context
);
108 case AutomationMsg_NavigateExternalTabAtIndex::ID
:
109 case AutomationMsg_NavigateInExternalTab::ID
:
110 InvokeCallback
<BeginNavigateContext
>(msg
, context
);
112 case AutomationMsg_InstallExtension::ID
:
113 InvokeCallback
<InstallExtensionContext
>(msg
, context
);
115 case AutomationMsg_LoadExpandedExtension::ID
:
116 InvokeCallback
<InstallExtensionContext
>(msg
, context
);
118 case AutomationMsg_GetEnabledExtensions::ID
:
119 InvokeCallback
<GetEnabledExtensionsContext
>(msg
, context
);
128 ChromeFrameAutomationProxyImpl::ChromeFrameAutomationProxyImpl(
129 AutomationProxyCacheEntry
* entry
, int launch_timeout
)
130 : AutomationProxy(launch_timeout
), proxy_entry_(entry
) {
131 TRACE_EVENT_BEGIN("chromeframe.automationproxy", this, "");
133 sync_
= new CFMsgDispatcher();
134 message_filter_
= new TabProxyNotificationMessageFilter(tracker_
.get());
136 // Order of filters is not important.
137 channel_
->AddFilter(message_filter_
.get());
138 channel_
->AddFilter(sync_
.get());
141 ChromeFrameAutomationProxyImpl::~ChromeFrameAutomationProxyImpl() {
142 TRACE_EVENT_END("chromeframe.automationproxy", this, "");
145 void ChromeFrameAutomationProxyImpl::SendAsAsync(
146 IPC::SyncMessage
* msg
,
147 SyncMessageReplyDispatcher::SyncMessageCallContext
* context
, void* key
) {
148 sync_
->Push(msg
, context
, key
);
149 channel_
->ChannelProxy::Send(msg
);
152 void ChromeFrameAutomationProxyImpl::CancelAsync(void* key
) {
156 void ChromeFrameAutomationProxyImpl::OnChannelError() {
157 DLOG(ERROR
) << "Automation server died";
159 proxy_entry_
->OnChannelError();
165 scoped_refptr
<TabProxy
> ChromeFrameAutomationProxyImpl::CreateTabProxy(
167 DCHECK(tracker_
->GetResource(handle
) == NULL
);
168 TabProxy
* tab_proxy
= new TabProxy(this, tracker_
.get(), handle
);
169 if (tab_proxy
!= NULL
)
170 message_filter_
->AddTabProxy(handle
);
174 void ChromeFrameAutomationProxyImpl::ReleaseTabProxy(AutomationHandle handle
) {
175 message_filter_
->RemoveTabProxy(handle
);
178 struct LaunchTimeStats
{
181 launch_time_begin_
= base::Time::Now();
185 base::TimeDelta launch_time
= base::Time::Now() - launch_time_begin_
;
186 THREAD_SAFE_UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchTime",
188 const int64 launch_milliseconds
= launch_time
.InMilliseconds();
189 if (launch_milliseconds
> kAutomationServerReasonableLaunchDelay
) {
190 LOG(WARNING
) << "Automation server launch took longer than expected: " <<
191 launch_milliseconds
<< " ms.";
195 base::Time launch_time_begin_
;
201 DISABLE_RUNNABLE_METHOD_REFCOUNT(AutomationProxyCacheEntry
);
203 AutomationProxyCacheEntry::AutomationProxyCacheEntry(
204 ChromeFrameLaunchParams
* params
, LaunchDelegate
* delegate
)
205 : profile_name(params
->profile_name()),
206 launch_result_(AUTOMATION_LAUNCH_RESULT_INVALID
),
207 snapshots_(NULL
), uma_send_interval_(1) {
209 thread_
.reset(new base::Thread(WideToASCII(profile_name
).c_str()));
211 thread_
->message_loop()->PostTask(FROM_HERE
, NewRunnableMethod(this,
212 &AutomationProxyCacheEntry::CreateProxy
, params
, delegate
));
215 AutomationProxyCacheEntry::~AutomationProxyCacheEntry() {
216 DLOG(INFO
) << __FUNCTION__
<< profile_name
;
217 // Attempt to fix chrome_frame_tests crash seen at times on the IE6/IE7
218 // builders. It appears that there are cases when we can enter here when the
219 // AtExitManager is tearing down the global ProxyCache which causes a crash
220 // while tearing down the AutomationProxy object due to a NULL MessageLoop
221 // The AutomationProxy class uses the SyncChannel which assumes the existence
222 // of a MessageLoop instance.
223 // We leak the AutomationProxy pointer here to avoid a crash.
224 if (MessageLoop::current() == NULL
) {
229 void AutomationProxyCacheEntry::StartSendUmaInterval(
230 ChromeFrameHistogramSnapshots
* snapshots
, int send_interval
) {
233 snapshots_
= snapshots
;
234 uma_send_interval_
= send_interval
;
235 thread_
->message_loop()->PostDelayedTask(FROM_HERE
,
236 NewRunnableMethod(this, &AutomationProxyCacheEntry::SendUMAData
),
240 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams
* params
,
241 LaunchDelegate
* delegate
) {
242 DCHECK(IsSameThread(PlatformThread::CurrentId()));
245 DCHECK(proxy_
.get() == NULL
);
247 // We *must* create automationproxy in a thread that has message loop,
248 // since SyncChannel::Context construction registers event to be watched
249 // through ObjectWatcher which subscribes for the current thread message loop
250 // destruction notification.
252 // At same time we must destroy/stop the thread from another thread.
253 ChromeFrameAutomationProxyImpl
* proxy
=
254 new ChromeFrameAutomationProxyImpl(this, params
->launch_timeout());
257 scoped_ptr
<CommandLine
> command_line(
258 chrome_launcher::CreateLaunchCommandLine());
259 command_line
->AppendSwitchASCII(switches::kAutomationClientChannelID
,
260 proxy
->channel_id());
262 // Run Chrome in Chrome Frame mode. In practice, this modifies the paths
263 // and registry keys that Chrome looks in via the BrowserDistribution
265 command_line
->AppendSwitch(switches::kChromeFrame
);
267 // Chrome Frame never wants Chrome to start up with a First Run UI.
268 command_line
->AppendSwitch(switches::kNoFirstRun
);
270 command_line
->AppendSwitch(switches::kDisablePopupBlocking
);
272 // Disable the "Whoa! Chrome has crashed." dialog, because that isn't very
273 // useful for Chrome Frame users.
275 command_line
->AppendSwitch(switches::kNoErrorDialogs
);
278 // In headless mode runs like reliability test runs we want full crash dumps
280 if (IsHeadlessMode())
281 command_line
->AppendSwitch(switches::kFullMemoryCrashReport
);
283 DLOG(INFO
) << "Profile path: " << params
->profile_path().value();
284 command_line
->AppendSwitchPath(switches::kUserDataDir
,
285 params
->profile_path());
287 std::wstring
command_line_string(command_line
->command_line_string());
288 // If there are any extra arguments, append them to the command line.
289 if (!params
->extra_arguments().empty()) {
290 command_line_string
+= L
' ' + params
->extra_arguments();
293 automation_server_launch_start_time_
= base::TimeTicks::Now();
295 if (!base::LaunchApp(command_line_string
, false, false, NULL
)) {
296 // We have no code for launch failure.
297 launch_result_
= AUTOMATION_LAUNCH_RESULT_INVALID
;
299 // Launch timeout may happen if the new instance tries to communicate
300 // with an existing Chrome instance that is hung and displays msgbox
301 // asking to kill the previous one. This could be easily observed if the
302 // already running Chrome instance is running as high-integrity process
303 // (started with "Run as Administrator" or launched by another high
304 // integrity process) hence our medium-integrity process
305 // cannot SendMessage to it with request to activate itself.
307 // TODO(stoyan) AutomationProxy eats Hello message, hence installing
308 // message filter is pointless, we can leverage ObjectWatcher and use
309 // system thread pool to notify us when proxy->AppLaunch event is signaled.
310 LaunchTimeStats launch_stats
;
311 // Wait for the automation server launch result, then stash away the
312 // version string it reported.
313 launch_result_
= proxy
->WaitForAppLaunch();
316 base::TimeDelta delta
=
317 base::TimeTicks::Now() - automation_server_launch_start_time_
;
319 if (launch_result_
== AUTOMATION_SUCCESS
) {
320 THREAD_SAFE_UMA_HISTOGRAM_TIMES(
321 "ChromeFrame.AutomationServerLaunchSuccessTime", delta
);
323 THREAD_SAFE_UMA_HISTOGRAM_TIMES(
324 "ChromeFrame.AutomationServerLaunchFailedTime", delta
);
327 THREAD_SAFE_UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchResult",
330 AUTOMATION_CREATE_TAB_FAILED
,
331 AUTOMATION_CREATE_TAB_FAILED
+ 1);
334 TRACE_EVENT_END("chromeframe.createproxy", this, "");
336 // Finally set the proxy.
338 launch_delegates_
.push_back(delegate
);
340 delegate
->LaunchComplete(proxy_
.get(), launch_result_
);
343 void AutomationProxyCacheEntry::RemoveDelegate(LaunchDelegate
* delegate
,
344 base::WaitableEvent
* done
,
345 bool* was_last_delegate
) {
346 DCHECK(IsSameThread(PlatformThread::CurrentId()));
349 DCHECK(was_last_delegate
);
351 *was_last_delegate
= false;
353 LaunchDelegates::iterator it
= std::find(launch_delegates_
.begin(),
354 launch_delegates_
.end(), delegate
);
355 if (it
== launch_delegates_
.end()) {
358 if (launch_delegates_
.size() == 1) {
359 *was_last_delegate
= true;
364 // Take down the proxy since we no longer have any clients.
367 // Process pending notifications.
368 thread_
->message_loop()->RunAllPending();
370 // Be careful to remove from the list after running pending
371 // tasks. Otherwise the delegate being removed might miss out
372 // on pending notifications such as LaunchComplete.
373 launch_delegates_
.erase(it
);
379 void AutomationProxyCacheEntry::AddDelegate(LaunchDelegate
* delegate
) {
380 DCHECK(IsSameThread(PlatformThread::CurrentId()));
381 DCHECK(std::find(launch_delegates_
.begin(),
382 launch_delegates_
.end(),
383 delegate
) == launch_delegates_
.end())
384 << "Same delegate being added twice";
385 DCHECK(launch_result_
!= AUTOMATION_LAUNCH_RESULT_INVALID
);
387 launch_delegates_
.push_back(delegate
);
388 delegate
->LaunchComplete(proxy_
.get(), launch_result_
);
391 void AutomationProxyCacheEntry::OnChannelError() {
392 DCHECK(IsSameThread(PlatformThread::CurrentId()));
393 launch_result_
= AUTOMATION_SERVER_CRASHED
;
394 LaunchDelegates::const_iterator it
= launch_delegates_
.begin();
395 for (; it
!= launch_delegates_
.end(); ++it
) {
396 (*it
)->AutomationServerDied();
400 void AutomationProxyCacheEntry::SendUMAData() {
401 DCHECK(IsSameThread(PlatformThread::CurrentId()));
403 // IE uses the chrome frame provided UMA data uploading scheme. NPAPI
404 // continues to use Chrome to upload UMA data.
405 if (CrashMetricsReporter::GetInstance()->active()) {
410 NOTREACHED() << __FUNCTION__
<< " Invalid proxy entry";
412 ChromeFrameHistogramSnapshots::HistogramPickledList histograms
=
413 snapshots_
->GatherAllHistograms();
415 if (!histograms
.empty()) {
416 proxy_
->Send(new AutomationMsg_RecordHistograms(0, histograms
));
419 MessageLoop::current()->PostDelayedTask(FROM_HERE
,
420 NewRunnableMethod(this, &AutomationProxyCacheEntry::SendUMAData
),
426 DISABLE_RUNNABLE_METHOD_REFCOUNT(ProxyFactory
);
428 ProxyFactory::ProxyFactory()
429 : uma_send_interval_(0) {
430 uma_send_interval_
= GetConfigInt(kDefaultSendUMADataInterval
,
431 kUmaSendIntervalValue
);
434 ProxyFactory::~ProxyFactory() {
435 for (size_t i
= 0; i
< proxies_
.container().size(); ++i
) {
436 DWORD result
= proxies_
[i
]->WaitForThread(0);
437 if (WAIT_OBJECT_0
!= result
)
438 // TODO(stoyan): Don't leak proxies on exit.
439 DLOG(ERROR
) << "Proxies leaked on exit.";
443 void ProxyFactory::GetAutomationServer(
444 LaunchDelegate
* delegate
, ChromeFrameLaunchParams
* params
,
445 void** automation_server_id
) {
446 TRACE_EVENT_BEGIN("chromeframe.createproxy", this, "");
448 scoped_refptr
<AutomationProxyCacheEntry
> entry
;
449 // Find already existing launcher thread for given profile
450 AutoLock
lock(lock_
);
451 for (size_t i
= 0; i
< proxies_
.container().size(); ++i
) {
452 if (proxies_
[i
]->IsSameProfile(params
->profile_name())) {
459 DLOG(INFO
) << __FUNCTION__
<< " creating new proxy entry";
460 entry
= new AutomationProxyCacheEntry(params
, delegate
);
461 proxies_
.container().push_back(entry
);
463 // IE uses the chrome frame provided UMA data uploading scheme. NPAPI
464 // continues to use Chrome to upload UMA data.
465 if (!CrashMetricsReporter::GetInstance()->active()) {
466 entry
->StartSendUmaInterval(&chrome_frame_histograms_
,
469 } else if (delegate
) {
470 // Notify the new delegate of the launch status from the worker thread
471 // and add it to the list of delegates.
472 entry
->message_loop()->PostTask(FROM_HERE
, NewRunnableMethod(entry
.get(),
473 &AutomationProxyCacheEntry::AddDelegate
, delegate
));
476 DCHECK(automation_server_id
!= NULL
);
477 DCHECK(!entry
->IsSameThread(PlatformThread::CurrentId()));
479 *automation_server_id
= entry
;
482 bool ProxyFactory::ReleaseAutomationServer(void* server_id
,
483 LaunchDelegate
* delegate
) {
489 AutomationProxyCacheEntry
* entry
=
490 reinterpret_cast<AutomationProxyCacheEntry
*>(server_id
);
494 Vector::ContainerType::iterator it
= std::find(proxies_
.container().begin(),
495 proxies_
.container().end(),
497 DCHECK(it
!= proxies_
.container().end());
498 DCHECK(!entry
->IsSameThread(PlatformThread::CurrentId()));
503 // AddRef the entry object as we might need to take it out of the proxy
504 // stack and then uninitialize the entry.
507 bool last_delegate
= false;
509 base::WaitableEvent
done(true, false);
510 entry
->message_loop()->PostTask(FROM_HERE
, NewRunnableMethod(entry
,
511 &AutomationProxyCacheEntry::RemoveDelegate
, delegate
, &done
,
518 Vector::ContainerType::iterator it
= std::find(proxies_
.container().begin(),
519 proxies_
.container().end(),
521 proxies_
.container().erase(it
);
530 Singleton
<ProxyFactory
> g_proxy_factory
;
532 template <> struct RunnableMethodTraits
<ChromeFrameAutomationClient
> {
533 static void RetainCallee(ChromeFrameAutomationClient
* obj
) {}
534 static void ReleaseCallee(ChromeFrameAutomationClient
* obj
) {}
537 ChromeFrameAutomationClient::ChromeFrameAutomationClient()
538 : chrome_frame_delegate_(NULL
),
539 chrome_window_(NULL
),
541 parent_window_(NULL
),
542 automation_server_(NULL
),
543 automation_server_id_(NULL
),
545 init_state_(UNINITIALIZED
),
546 use_chrome_network_(false),
547 proxy_factory_(g_proxy_factory
.get()),
548 handle_top_level_requests_(false),
550 external_tab_cookie_(0),
552 url_fetcher_flags_(PluginUrlRequestManager::NOT_THREADSAFE
),
553 navigate_after_initialization_(false) {
556 ChromeFrameAutomationClient::~ChromeFrameAutomationClient() {
557 // Uninitialize must be called prior to the destructor
558 DCHECK(automation_server_
== NULL
);
561 bool ChromeFrameAutomationClient::Initialize(
562 ChromeFrameDelegate
* chrome_frame_delegate
,
563 ChromeFrameLaunchParams
* chrome_launch_params
) {
565 chrome_frame_delegate_
= chrome_frame_delegate
;
568 if (chrome_launch_params_
&& chrome_launch_params_
!= chrome_launch_params
) {
569 DCHECK_EQ(chrome_launch_params_
->url(), chrome_launch_params
->url());
570 DCHECK_EQ(chrome_launch_params_
->referrer(),
571 chrome_launch_params
->referrer());
575 chrome_launch_params_
= chrome_launch_params
;
577 ui_thread_id_
= PlatformThread::CurrentId();
579 // In debug mode give more time to work with a debugger.
580 if (IsDebuggerPresent()) {
581 // Don't use INFINITE (which is -1) or even MAXINT since we will convert
582 // from milliseconds to microseconds when stored in a base::TimeDelta,
583 // thus * 1000. An hour should be enough.
584 chrome_launch_params_
->set_launch_timeout(60 * 60 * 1000);
586 DCHECK_LT(chrome_launch_params_
->launch_timeout(),
588 chrome_launch_params_
->set_launch_timeout(
589 chrome_launch_params_
->launch_timeout() * 2);
593 // Create a window on the UI thread for marshaling messages back and forth
594 // from the IPC thread. This window cannot be a message only window as the
595 // external chrome tab window is created as a child of this window. This
596 // window is eventually reparented to the ActiveX/NPAPI plugin window.
597 if (!Create(GetDesktopWindow(), NULL
, NULL
,
598 WS_CHILDWINDOW
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
604 // Keep object in memory, while the window is alive.
605 // Corresponding Release is in OnFinalMessage();
608 // Mark our state as initializing. We'll reach initialized once
609 // InitializeComplete is called successfully.
610 init_state_
= INITIALIZING
;
612 if (chrome_launch_params_
->url().is_valid())
613 navigate_after_initialization_
= false;
615 proxy_factory_
->GetAutomationServer(static_cast<LaunchDelegate
*>(this),
616 chrome_launch_params_
, &automation_server_id_
);
621 void ChromeFrameAutomationClient::Uninitialize() {
622 if (init_state_
== UNINITIALIZED
) {
623 DLOG(WARNING
) << __FUNCTION__
<< ": Automation client not initialized";
627 init_state_
= UNINITIALIZING
;
629 // Called from client's FinalRelease() / destructor
630 // ChromeFrameAutomationClient may wait for the initialization (launch)
631 // to complete while Uninitialize is called.
632 // We either have to:
633 // 1) Make ReleaseAutomationServer blocking call (wait until thread exits)
634 // 2) Behave like a COM object i.e. increase module lock count.
635 // Otherwise the DLL may get unloaded while we have running threads.
636 // Unfortunately in NPAPI case we cannot increase module lock count, hence
637 // we stick with blocking/waiting
639 // Clean up any outstanding requests
640 url_fetcher_
->StopAllRequests();
645 tab_
->RemoveObserver(this);
646 if (automation_server_
)
647 automation_server_
->ReleaseTabProxy(tab_
->handle());
648 tab_
= NULL
; // scoped_refptr::Release
651 // Wait for the background thread to exit.
652 ReleaseAutomationServer();
654 // We must destroy the window, since if there are pending tasks
655 // window procedure may be invoked after DLL is unloaded.
656 // Unfortunately pending tasks are leaked.
657 if (::IsWindow(m_hWnd
))
660 DCHECK(navigate_after_initialization_
== false);
661 handle_top_level_requests_
= false;
663 chrome_frame_delegate_
= NULL
;
664 init_state_
= UNINITIALIZED
;
667 bool ChromeFrameAutomationClient::InitiateNavigation(const std::string
& url
,
668 const std::string
& referrer
, bool is_privileged
) {
672 GURL
parsed_url(url
);
673 // Catch invalid URLs early.
674 if (!parsed_url
.is_valid() ||
675 !IsValidUrlScheme(UTF8ToWide(url
), is_privileged
)) {
676 DLOG(ERROR
) << "Invalid URL passed to InitiateNavigation: " << url
677 << " is_privileged=" << is_privileged
;
681 if (!chrome_launch_params_
|| parsed_url
!= chrome_launch_params_
->url()) {
682 // Important: Since we will be using the referrer_ variable from a
683 // different thread, we need to force a new std::string buffer instance for
684 // the referrer_ GURL variable. Otherwise we can run into strangeness when
685 // the GURL is accessed and it could result in a bad URL that can cause the
686 // referrer to be dropped or something worse.
687 GURL
referrer_gurl(referrer
.c_str());
688 if (!chrome_launch_params_
) {
689 FilePath profile_path
;
690 chrome_launch_params_
= new ChromeFrameLaunchParams(parsed_url
,
691 referrer_gurl
, profile_path
, L
"", L
"", false, false);
693 chrome_launch_params_
->set_referrer(referrer_gurl
);
694 chrome_launch_params_
->set_url(parsed_url
);
697 navigate_after_initialization_
= false;
699 if (is_initialized()) {
702 navigate_after_initialization_
= true;
709 bool ChromeFrameAutomationClient::NavigateToIndex(int index
) {
710 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
711 if (!automation_server_
|| !tab_
.get() || !tab_
->is_valid()) {
715 DCHECK(::IsWindow(chrome_window_
));
717 IPC::SyncMessage
* msg
= new AutomationMsg_NavigateExternalTabAtIndex(
718 0, tab_
->handle(), index
, NULL
);
719 automation_server_
->SendAsAsync(msg
, new BeginNavigateContext(this),
724 bool ChromeFrameAutomationClient::ForwardMessageFromExternalHost(
725 const std::string
& message
, const std::string
& origin
,
726 const std::string
& target
) {
727 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
728 if (!is_initialized())
731 tab_
->HandleMessageFromExternalHost(message
, origin
, target
);
735 bool ChromeFrameAutomationClient::SetProxySettings(
736 const std::string
& json_encoded_proxy_settings
) {
737 if (!is_initialized())
739 automation_server_
->SendProxyConfig(json_encoded_proxy_settings
);
743 void ChromeFrameAutomationClient::BeginNavigate() {
744 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
745 if (!automation_server_
|| !tab_
.get()) {
746 DLOG(WARNING
) << "BeginNavigate - can't navigate.";
747 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR
,
748 chrome_launch_params_
->url().spec());
752 DCHECK(::IsWindow(chrome_window_
));
754 if (!tab_
->is_valid()) {
755 DLOG(WARNING
) << "BeginNavigate - tab isn't valid.";
759 IPC::SyncMessage
* msg
=
760 new AutomationMsg_NavigateInExternalTab(0, tab_
->handle(),
761 chrome_launch_params_
->url(), chrome_launch_params_
->referrer(),
763 automation_server_
->SendAsAsync(msg
, new BeginNavigateContext(this), this);
765 RECT client_rect
= {0};
766 chrome_frame_delegate_
->GetBounds(&client_rect
);
767 Resize(client_rect
.right
- client_rect
.left
,
768 client_rect
.bottom
- client_rect
.top
,
769 SWP_NOACTIVATE
| SWP_NOZORDER
);
772 void ChromeFrameAutomationClient::BeginNavigateCompleted(
773 AutomationMsg_NavigationResponseValues result
) {
774 if (result
== AUTOMATION_MSG_NAVIGATION_ERROR
)
775 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR
,
776 chrome_launch_params_
->url().spec());
779 void ChromeFrameAutomationClient::FindInPage(const std::wstring
& search_string
,
780 FindInPageDirection forward
,
781 FindInPageCase match_case
,
785 // What follows is quite similar to TabProxy::FindInPage() but uses
786 // the SyncMessageReplyDispatcher to avoid concerns about blocking
787 // synchronous messages.
788 AutomationMsg_Find_Params params
;
790 params
.search_string
= WideToUTF16Hack(search_string
);
791 params
.find_next
= find_next
;
792 params
.match_case
= (match_case
== CASE_SENSITIVE
);
793 params
.forward
= (forward
== FWD
);
795 IPC::SyncMessage
* msg
=
796 new AutomationMsg_Find(0, tab_
->handle(), params
, NULL
, NULL
);
797 automation_server_
->SendAsAsync(msg
, NULL
, this);
800 void ChromeFrameAutomationClient::InstallExtension(
801 const FilePath
& crx_path
,
803 if (automation_server_
== NULL
) {
804 InstallExtensionComplete(crx_path
,
806 AUTOMATION_MSG_EXTENSION_INSTALL_FAILED
);
810 InstallExtensionContext
* ctx
= new InstallExtensionContext(
811 this, crx_path
, user_data
);
813 IPC::SyncMessage
* msg
=
814 new AutomationMsg_InstallExtension(0, crx_path
, NULL
);
816 // The context will delete itself after it is called.
817 automation_server_
->SendAsAsync(msg
, ctx
, this);
820 void ChromeFrameAutomationClient::InstallExtensionComplete(
821 const FilePath
& crx_path
,
823 AutomationMsg_ExtensionResponseValues res
) {
824 DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_
);
826 if (chrome_frame_delegate_
) {
827 chrome_frame_delegate_
->OnExtensionInstalled(crx_path
, user_data
, res
);
831 void ChromeFrameAutomationClient::GetEnabledExtensions(void* user_data
) {
832 if (automation_server_
== NULL
) {
833 GetEnabledExtensionsComplete(user_data
, &std::vector
<FilePath
>());
837 GetEnabledExtensionsContext
* ctx
= new GetEnabledExtensionsContext(
840 IPC::SyncMessage
* msg
= new AutomationMsg_GetEnabledExtensions(
841 0, ctx
->extension_directories());
843 // The context will delete itself after it is called.
844 automation_server_
->SendAsAsync(msg
, ctx
, this);
847 void ChromeFrameAutomationClient::GetEnabledExtensionsComplete(
849 std::vector
<FilePath
>* extension_directories
) {
850 DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_
);
852 if (chrome_frame_delegate_
) {
853 chrome_frame_delegate_
->OnGetEnabledExtensionsComplete(
854 user_data
, *extension_directories
);
857 delete extension_directories
;
860 void ChromeFrameAutomationClient::OnChromeFrameHostMoved() {
861 // Use a local var to avoid the small possibility of getting the tab_
862 // member be cleared while we try to use it.
863 // Note that TabProxy is a RefCountedThreadSafe object, so we should be OK.
864 scoped_refptr
<TabProxy
> tab(tab_
);
865 // There also is a possibility that tab_ has not been set yet,
866 // so we still need to test for NULL.
867 if (tab
.get() != NULL
)
871 void ChromeFrameAutomationClient::LoadExpandedExtension(
872 const FilePath
& path
,
874 if (automation_server_
== NULL
) {
875 InstallExtensionComplete(path
,
877 AUTOMATION_MSG_EXTENSION_INSTALL_FAILED
);
881 InstallExtensionContext
* ctx
= new InstallExtensionContext(
882 this, path
, user_data
);
884 IPC::SyncMessage
* msg
=
885 new AutomationMsg_LoadExpandedExtension(0, path
, NULL
);
887 // The context will delete itself after it is called.
888 automation_server_
->SendAsAsync(msg
, ctx
, this);
891 void ChromeFrameAutomationClient::CreateExternalTab() {
892 AutomationLaunchResult launch_result
= AUTOMATION_SUCCESS
;
894 DCHECK(automation_server_
!= NULL
);
896 if (chrome_launch_params_
->url().is_valid()) {
897 navigate_after_initialization_
= false;
900 const IPC::ExternalTabSettings settings
= {
904 chrome_launch_params_
->incognito(),
905 !use_chrome_network_
,
906 handle_top_level_requests_
,
907 chrome_launch_params_
->url(),
908 chrome_launch_params_
->referrer(),
909 !chrome_launch_params_
->widget_mode() // Infobars disabled in widget mode.
912 THREAD_SAFE_UMA_HISTOGRAM_CUSTOM_COUNTS(
913 "ChromeFrame.HostNetworking", !use_chrome_network_
, 0, 1, 2);
915 THREAD_SAFE_UMA_HISTOGRAM_CUSTOM_COUNTS(
916 "ChromeFrame.HandleTopLevelRequests", handle_top_level_requests_
, 0, 1,
919 IPC::SyncMessage
* message
=
920 new AutomationMsg_CreateExternalTab(0, settings
, NULL
, NULL
, NULL
);
921 automation_server_
->SendAsAsync(message
, new CreateExternalTabContext(this),
925 AutomationLaunchResult
ChromeFrameAutomationClient::CreateExternalTabComplete(
926 HWND chrome_window
, HWND tab_window
, int tab_handle
) {
927 if (!automation_server_
) {
928 // If we receive this notification while shutting down, do nothing.
929 DLOG(ERROR
) << "CreateExternalTabComplete called when automation server "
931 return AUTOMATION_CREATE_TAB_FAILED
;
934 AutomationLaunchResult launch_result
= AUTOMATION_SUCCESS
;
935 if (tab_handle
== 0 || !::IsWindow(chrome_window
)) {
936 launch_result
= AUTOMATION_CREATE_TAB_FAILED
;
938 chrome_window_
= chrome_window
;
939 tab_window_
= tab_window
;
940 tab_
= automation_server_
->CreateTabProxy(tab_handle
);
941 tab_
->AddObserver(this);
942 tab_handle_
= tab_handle
;
944 return launch_result
;
947 void ChromeFrameAutomationClient::SetEnableExtensionAutomation(
948 const std::vector
<std::string
>& functions_enabled
) {
949 if (!is_initialized())
952 // We are doing initialization, so there is no need to reset extension
953 // automation, only to set it. Also, we want to avoid resetting extension
954 // automation that some other automation client has set up. Therefore only
955 // send the message if we are going to enable automation of some functions.
956 if (functions_enabled
.size() > 0) {
957 tab_
->SetEnableExtensionAutomation(functions_enabled
);
961 // Invoked in launch background thread.
962 void ChromeFrameAutomationClient::LaunchComplete(
963 ChromeFrameAutomationProxy
* proxy
,
964 AutomationLaunchResult result
) {
965 // If we're shutting down we don't keep a pointer to the automation server.
966 if (init_state_
!= UNINITIALIZING
) {
967 DCHECK(init_state_
== INITIALIZING
);
968 automation_server_
= proxy
;
970 DLOG(INFO
) << "Not storing automation server pointer due to shutting down";
973 if (result
== AUTOMATION_SUCCESS
) {
974 // NOTE: A potential problem here is that Uninitialize() may just have
975 // been called so we need to be careful and check the automation_server_
977 if (automation_server_
!= NULL
) {
978 // If we have a valid tab_handle here it means that we are attaching to
979 // an existing ExternalTabContainer instance, in which case we don't
980 // want to create an external tab instance in Chrome.
981 if (external_tab_cookie_
== 0) {
982 // Continue with Initialization - Create external tab
985 // Send a notification to Chrome that we are ready to connect to the
987 IPC::SyncMessage
* message
=
988 new AutomationMsg_ConnectExternalTab(0, external_tab_cookie_
, true,
989 m_hWnd
, NULL
, NULL
, NULL
);
990 automation_server_
->SendAsAsync(message
,
991 new CreateExternalTabContext(this),
993 DLOG(INFO
) << __FUNCTION__
<< ": sending CreateExternalTabComplete";
997 // Launch failed. Note, we cannot delete proxy here.
998 PostTask(FROM_HERE
, NewRunnableMethod(this,
999 &ChromeFrameAutomationClient::InitializeComplete
, result
));
1003 void ChromeFrameAutomationClient::AutomationServerDied() {
1004 // Make sure we notify our delegate.
1005 PostTask(FROM_HERE
, NewRunnableMethod(this,
1006 &ChromeFrameAutomationClient::InitializeComplete
,
1007 AUTOMATION_SERVER_CRASHED
));
1008 // Then uninitialize.
1009 PostTask(FROM_HERE
, NewRunnableMethod(this,
1010 &ChromeFrameAutomationClient::Uninitialize
));
1013 void ChromeFrameAutomationClient::InitializeComplete(
1014 AutomationLaunchResult result
) {
1015 DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_
);
1016 std::string version
= automation_server_
->server_version();
1018 if (result
!= AUTOMATION_SUCCESS
) {
1019 DLOG(WARNING
) << "InitializeComplete: failure " << result
;
1020 ReleaseAutomationServer();
1022 init_state_
= INITIALIZED
;
1024 // If the host already have a window, ask Chrome to re-parent.
1026 SetParentWindow(parent_window_
);
1028 // If host specified destination URL - navigate. Apparently we do not use
1029 // accelerator table.
1030 if (navigate_after_initialization_
) {
1031 navigate_after_initialization_
= false;
1036 if (chrome_frame_delegate_
) {
1037 if (result
== AUTOMATION_SUCCESS
) {
1038 chrome_frame_delegate_
->OnAutomationServerReady();
1040 chrome_frame_delegate_
->OnAutomationServerLaunchFailed(result
, version
);
1045 bool ChromeFrameAutomationClient::ProcessUrlRequestMessage(TabProxy
* tab
,
1046 const IPC::Message
& msg
, bool ui_thread
) {
1047 // Either directly call appropriate url_fetcher function
1048 // or postpone call to the UI thread.
1049 uint16 msg_type
= msg
.type();
1054 case AutomationMsg_RequestStart::ID
:
1055 if (ui_thread
|| (url_fetcher_flags_
&
1056 PluginUrlRequestManager::START_REQUEST_THREADSAFE
)) {
1057 AutomationMsg_RequestStart::Dispatch(&msg
, url_fetcher_
,
1058 &PluginUrlRequestManager::StartUrlRequest
);
1063 case AutomationMsg_RequestRead::ID
:
1064 if (ui_thread
|| (url_fetcher_flags_
&
1065 PluginUrlRequestManager::READ_REQUEST_THREADSAFE
)) {
1066 AutomationMsg_RequestRead::Dispatch(&msg
, url_fetcher_
,
1067 &PluginUrlRequestManager::ReadUrlRequest
);
1072 case AutomationMsg_RequestEnd::ID
:
1073 if (ui_thread
|| (url_fetcher_flags_
&
1074 PluginUrlRequestManager::STOP_REQUEST_THREADSAFE
)) {
1075 AutomationMsg_RequestEnd::Dispatch(&msg
, url_fetcher_
,
1076 &PluginUrlRequestManager::EndUrlRequest
);
1081 case AutomationMsg_DownloadRequestInHost::ID
:
1082 if (ui_thread
|| (url_fetcher_flags_
&
1083 PluginUrlRequestManager::DOWNLOAD_REQUEST_THREADSAFE
)) {
1084 AutomationMsg_DownloadRequestInHost::Dispatch(&msg
, url_fetcher_
,
1085 &PluginUrlRequestManager::DownloadUrlRequestInHost
);
1090 case AutomationMsg_GetCookiesFromHost::ID
:
1091 if (ui_thread
|| (url_fetcher_flags_
&
1092 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE
)) {
1093 AutomationMsg_GetCookiesFromHost::Dispatch(&msg
, url_fetcher_
,
1094 &PluginUrlRequestManager::GetCookiesFromHost
);
1099 case AutomationMsg_SetCookieAsync::ID
:
1100 if (ui_thread
|| (url_fetcher_flags_
&
1101 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE
)) {
1102 AutomationMsg_SetCookieAsync::Dispatch(&msg
, url_fetcher_
,
1103 &PluginUrlRequestManager::SetCookiesInHost
);
1109 PostTask(FROM_HERE
, NewRunnableMethod(this,
1110 &ChromeFrameAutomationClient::ProcessUrlRequestMessage
, tab
, msg
, true));
1114 // These are invoked in channel's background thread.
1115 // Cannot call any method of the activex/npapi here since they are STA
1117 // By default we marshal the IPC message to the main/GUI thread and from there
1118 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg).
1119 void ChromeFrameAutomationClient::OnMessageReceived(TabProxy
* tab
,
1120 const IPC::Message
& msg
) {
1121 DCHECK(tab
== tab_
.get());
1122 // Quickly process network related messages.
1123 if (url_fetcher_
&& ProcessUrlRequestMessage(tab
, msg
, false))
1126 // Early check to avoid needless marshaling
1127 if (chrome_frame_delegate_
== NULL
)
1130 PostTask(FROM_HERE
, NewRunnableMethod(this,
1131 &ChromeFrameAutomationClient::OnMessageReceivedUIThread
, msg
));
1134 void ChromeFrameAutomationClient::OnChannelError(TabProxy
* tab
) {
1135 DCHECK(tab
== tab_
.get());
1136 // Early check to avoid needless marshaling
1137 if (chrome_frame_delegate_
== NULL
)
1140 PostTask(FROM_HERE
, NewRunnableMethod(this,
1141 &ChromeFrameAutomationClient::OnChannelErrorUIThread
));
1144 void ChromeFrameAutomationClient::OnMessageReceivedUIThread(
1145 const IPC::Message
& msg
) {
1146 DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_
);
1147 // Forward to the delegate.
1148 if (chrome_frame_delegate_
)
1149 chrome_frame_delegate_
->OnMessageReceived(msg
);
1152 void ChromeFrameAutomationClient::OnChannelErrorUIThread() {
1153 DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_
);
1154 // Forward to the delegate.
1155 if (chrome_frame_delegate_
)
1156 chrome_frame_delegate_
->OnChannelError();
1159 void ChromeFrameAutomationClient::ReportNavigationError(
1160 AutomationMsg_NavigationResponseValues error_code
,
1161 const std::string
& url
) {
1162 if (!chrome_frame_delegate_
)
1165 if (ui_thread_id_
== PlatformThread::CurrentId()) {
1166 chrome_frame_delegate_
->OnLoadFailed(error_code
, url
);
1168 PostTask(FROM_HERE
, NewRunnableMethod(this,
1169 &ChromeFrameAutomationClient::ReportNavigationError
,
1174 void ChromeFrameAutomationClient::Resize(int width
, int height
,
1176 if (tab_
.get() && ::IsWindow(chrome_window())) {
1177 SetWindowPos(HWND_TOP
, 0, 0, width
, height
, flags
);
1178 tab_
->Reposition(chrome_window(), HWND_TOP
, 0, 0, width
, height
,
1183 void ChromeFrameAutomationClient::SetParentWindow(HWND parent_window
) {
1184 parent_window_
= parent_window
;
1185 // If we're done with the initialization step, go ahead
1186 if (is_initialized()) {
1187 if (parent_window
== NULL
) {
1188 // Hide and reparent the automation window. This window will get
1189 // reparented to the new ActiveX/Active document window when it gets
1191 ShowWindow(SW_HIDE
);
1192 SetParent(GetDesktopWindow());
1194 if (!::IsWindow(chrome_window())) {
1195 DLOG(WARNING
) << "Invalid Chrome Window handle in SetParentWindow";
1199 if (!SetParent(parent_window
)) {
1200 DLOG(WARNING
) << "Failed to set parent window for automation window. "
1206 RECT parent_client_rect
= {0};
1207 ::GetClientRect(parent_window
, &parent_client_rect
);
1208 int width
= parent_client_rect
.right
- parent_client_rect
.left
;
1209 int height
= parent_client_rect
.bottom
- parent_client_rect
.top
;
1211 Resize(width
, height
, SWP_SHOWWINDOW
| SWP_NOZORDER
);
1216 void ChromeFrameAutomationClient::ReleaseAutomationServer() {
1217 if (automation_server_id_
) {
1218 // Cache the server id and clear the automation_server_id_ before
1219 // calling ReleaseAutomationServer. The reason we do this is that
1220 // we must cancel pending messages before we release the automation server.
1221 // Furthermore, while ReleaseAutomationServer is running, we could get
1222 // a callback to LaunchComplete which could cause an external tab to be
1223 // created. Ideally the callbacks should be dropped.
1225 // Refactor the ChromeFrameAutomationProxy code to not depend on
1226 // AutomationProxy and simplify the whole mess.
1227 void* server_id
= automation_server_id_
;
1228 automation_server_id_
= NULL
;
1230 if (automation_server_
) {
1231 // Make sure to clean up any pending sync messages before we go away.
1232 automation_server_
->CancelAsync(this);
1235 proxy_factory_
->ReleaseAutomationServer(server_id
, this);
1236 automation_server_
= NULL
;
1238 // automation_server_ must not have been set to non NULL.
1239 // (if this regresses, start by looking at LaunchComplete()).
1240 DCHECK(automation_server_
== NULL
);
1242 DCHECK(automation_server_
== NULL
);
1246 void ChromeFrameAutomationClient::SendContextMenuCommandToChromeFrame(
1247 int selected_command
) {
1248 DCHECK(tab_
!= NULL
);
1249 tab_
->SendContextMenuCommand(selected_command
);
1252 std::wstring
ChromeFrameAutomationClient::GetVersion() const {
1253 static FileVersionInfo
* version_info
=
1254 FileVersionInfo::CreateFileVersionInfoForCurrentModule();
1256 std::wstring version
;
1258 version
= version_info
->product_version();
1263 void ChromeFrameAutomationClient::Print(HDC print_dc
,
1264 const RECT
& print_bounds
) {
1270 HDC window_dc
= ::GetDC(tab_window_
);
1272 BitBlt(print_dc
, print_bounds
.left
, print_bounds
.top
,
1273 print_bounds
.right
- print_bounds
.left
,
1274 print_bounds
.bottom
- print_bounds
.top
,
1275 window_dc
, print_bounds
.left
, print_bounds
.top
,
1278 ::ReleaseDC(tab_window_
, window_dc
);
1281 void ChromeFrameAutomationClient::PrintTab() {
1285 bool ChromeFrameAutomationClient::Reinitialize(
1286 ChromeFrameDelegate
* delegate
,
1287 PluginUrlRequestManager
* url_fetcher
) {
1289 // Clean up any outstanding requests
1290 url_fetcher_
->StopAllRequests();
1291 url_fetcher_
= NULL
;
1294 if (!tab_
.get() || !::IsWindow(chrome_window_
)) {
1296 DLOG(WARNING
) << "ChromeFrameAutomationClient instance reused "
1297 << "with invalid tab";
1306 chrome_frame_delegate_
= delegate
;
1307 DeleteAllPendingTasks();
1308 SetUrlFetcher(url_fetcher
);
1309 SetParentWindow(NULL
);
1313 void ChromeFrameAutomationClient::AttachExternalTab(
1314 uint64 external_tab_cookie
) {
1315 DCHECK_EQ(static_cast<TabProxy
*>(NULL
), tab_
.get());
1316 DCHECK_EQ(-1, tab_handle_
);
1318 external_tab_cookie_
= external_tab_cookie
;
1321 void ChromeFrameAutomationClient::BlockExternalTab(uint64 cookie
) {
1322 // The host does not want this tab to be shown (due popup blocker).
1323 IPC::SyncMessage
* message
=
1324 new AutomationMsg_ConnectExternalTab(0, cookie
, false, m_hWnd
,
1326 automation_server_
->SendAsAsync(message
, NULL
, this);
1329 void ChromeFrameAutomationClient::SetPageFontSize(
1330 enum AutomationPageFontSize font_size
) {
1331 if (font_size
< SMALLEST_FONT
||
1332 font_size
> LARGEST_FONT
) {
1333 NOTREACHED() << "Invalid font size specified : "
1338 automation_server_
->Send(
1339 new AutomationMsg_SetPageFontSize(0, tab_handle_
, font_size
));
1342 void ChromeFrameAutomationClient::RemoveBrowsingData(int remove_mask
) {
1343 automation_server_
->Send(
1344 new AutomationMsg_RemoveBrowsingData(0, remove_mask
));
1347 void ChromeFrameAutomationClient::RunUnloadHandlers(HWND notification_window
,
1348 int notification_message
) {
1349 if (automation_server_
) {
1350 automation_server_
->Send(
1351 new AutomationMsg_RunUnloadHandlers(0, tab_handle_
,
1352 notification_window
,
1353 notification_message
));
1355 // Post this message to ensure that the caller exits his message loop.
1356 ::PostMessage(notification_window
, notification_message
, 0, 0);
1360 void ChromeFrameAutomationClient::SetZoomLevel(PageZoom::Function zoom_level
) {
1361 if (automation_server_
) {
1362 automation_server_
->Send(new AutomationMsg_SetZoomLevel(0, tab_handle_
,
1367 //////////////////////////////////////////////////////////////////////////
1368 // PluginUrlRequestDelegate implementation.
1369 // Forward network related responses to Chrome.
1371 void ChromeFrameAutomationClient::OnResponseStarted(int request_id
,
1372 const char* mime_type
, const char* headers
, int size
,
1373 base::Time last_modified
, const std::string
& redirect_url
,
1374 int redirect_status
) {
1375 const IPC::AutomationURLResponse response
= {
1377 headers
? headers
: "",
1384 automation_server_
->Send(new AutomationMsg_RequestStarted(0,
1385 tab_
->handle(), request_id
, response
));
1388 void ChromeFrameAutomationClient::OnReadComplete(int request_id
,
1389 const std::string
& data
) {
1390 automation_server_
->Send(new AutomationMsg_RequestData(0, tab_
->handle(),
1394 void ChromeFrameAutomationClient::OnResponseEnd(int request_id
,
1395 const URLRequestStatus
& status
) {
1396 automation_server_
->Send(new AutomationMsg_RequestEnd(0, tab_
->handle(),
1397 request_id
, status
));
1400 void ChromeFrameAutomationClient::OnCookiesRetrieved(bool success
,
1401 const GURL
& url
, const std::string
& cookie_string
, int cookie_id
) {
1402 automation_server_
->Send(new AutomationMsg_GetCookiesHostResponse(0,
1403 tab_
->handle(), success
, url
, cookie_string
, cookie_id
));