Add VisibleForTesting to content/public/android
[chromium-blink-merge.git] / chrome_frame / chrome_frame_automation.cc
blob8cf778bab6d578d216b4d8d4bd8981c4349d1b8b
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_frame/chrome_frame_automation.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h"
14 #include "base/file_version_info.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/path_service.h"
19 #include "base/process_util.h"
20 #include "base/string_util.h"
21 #include "base/synchronization/lock.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/sys_info.h"
24 #include "base/utf_string_conversions.h"
25 #include "chrome/app/client_util.h"
26 #include "chrome/common/automation_messages.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/test/automation/tab_proxy.h"
30 #include "chrome_frame/chrome_launcher_utils.h"
31 #include "chrome_frame/crash_reporting/crash_metrics.h"
32 #include "chrome_frame/custom_sync_call_context.h"
33 #include "chrome_frame/navigation_constraints.h"
34 #include "chrome_frame/simple_resource_loader.h"
35 #include "chrome_frame/utils.h"
36 #include "ui/base/ui_base_switches.h"
38 namespace {
40 #ifdef NDEBUG
41 int64 kAutomationServerReasonableLaunchDelay = 1000; // in milliseconds
42 #else
43 int64 kAutomationServerReasonableLaunchDelay = 1000 * 10;
44 #endif
46 const char kChromeShutdownDelaySeconds[] = "30";
47 const char kWithDelayFieldTrialName[] = "WithShutdownDelay";
48 const char kNoDelayFieldTrialName[] = "NoShutdownDelay";
50 } // namespace
52 class ChromeFrameAutomationProxyImpl::TabProxyNotificationMessageFilter
53 : public IPC::ChannelProxy::MessageFilter {
54 public:
55 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker)
56 : tracker_(tracker) {
59 void AddTabProxy(AutomationHandle tab_proxy) {
60 base::AutoLock lock(lock_);
61 tabs_list_.push_back(tab_proxy);
64 void RemoveTabProxy(AutomationHandle tab_proxy) {
65 base::AutoLock lock(lock_);
66 tabs_list_.remove(tab_proxy);
69 virtual bool OnMessageReceived(const IPC::Message& message) {
70 if (message.is_reply())
71 return false;
73 if (!ChromeFrameDelegateImpl::IsTabMessage(message))
74 return false;
76 // Get AddRef-ed pointer to corresponding TabProxy object
77 TabProxy* tab = static_cast<TabProxy*>(tracker_->GetResource(
78 message.routing_id()));
79 bool handled = false;
80 if (tab) {
81 handled = tab->OnMessageReceived(message);
82 tab->Release();
83 } else {
84 DLOG(ERROR) << "Failed to find TabProxy for tab:" << message.routing_id();
85 // To prevent subsequent crashes, we set handled to true in this case.
86 handled = true;
88 return handled;
91 virtual void OnChannelError() {
92 std::list<AutomationHandle>::const_iterator iter = tabs_list_.begin();
93 for (; iter != tabs_list_.end(); ++iter) {
94 // Get AddRef-ed pointer to corresponding TabProxy object
95 TabProxy* tab = static_cast<TabProxy*>(tracker_->GetResource(*iter));
96 if (tab) {
97 tab->OnChannelError();
98 tab->Release();
103 private:
104 AutomationHandleTracker* tracker_;
105 std::list<AutomationHandle> tabs_list_;
106 base::Lock lock_;
109 class ChromeFrameAutomationProxyImpl::CFMsgDispatcher
110 : public SyncMessageReplyDispatcher {
111 public:
112 CFMsgDispatcher() : SyncMessageReplyDispatcher() {}
113 protected:
114 virtual bool HandleMessageType(const IPC::Message& msg,
115 SyncMessageCallContext* context) {
116 switch (context->message_type()) {
117 case AutomationMsg_CreateExternalTab::ID:
118 case AutomationMsg_ConnectExternalTab::ID:
119 InvokeCallback<CreateExternalTabContext>(msg, context);
120 break;
121 case AutomationMsg_NavigateExternalTabAtIndex::ID:
122 case AutomationMsg_NavigateInExternalTab::ID:
123 InvokeCallback<BeginNavigateContext>(msg, context);
124 break;
125 case AutomationMsg_RunUnloadHandlers::ID:
126 InvokeCallback<UnloadContext>(msg, context);
127 break;
128 default:
129 NOTREACHED();
131 return true;
135 ChromeFrameAutomationProxyImpl::ChromeFrameAutomationProxyImpl(
136 AutomationProxyCacheEntry* entry,
137 std::string channel_id, base::TimeDelta launch_timeout)
138 : AutomationProxy(launch_timeout, false), proxy_entry_(entry) {
139 TRACE_EVENT_BEGIN_ETW("chromeframe.automationproxy", this, "");
141 InitializeChannel(channel_id, false);
143 sync_ = new CFMsgDispatcher();
144 message_filter_ = new TabProxyNotificationMessageFilter(tracker_.get());
146 // Order of filters is not important.
147 channel_->AddFilter(message_filter_.get());
148 channel_->AddFilter(sync_.get());
151 ChromeFrameAutomationProxyImpl::~ChromeFrameAutomationProxyImpl() {
152 TRACE_EVENT_END_ETW("chromeframe.automationproxy", this, "");
155 void ChromeFrameAutomationProxyImpl::SendAsAsync(
156 IPC::SyncMessage* msg,
157 SyncMessageReplyDispatcher::SyncMessageCallContext* context, void* key) {
158 sync_->Push(msg, context, key);
159 channel_->ChannelProxy::Send(msg);
162 void ChromeFrameAutomationProxyImpl::CancelAsync(void* key) {
163 sync_->Cancel(key);
166 void ChromeFrameAutomationProxyImpl::OnChannelError() {
167 DLOG(ERROR) << "Automation server died";
168 if (proxy_entry_) {
169 proxy_entry_->OnChannelError();
170 } else {
171 NOTREACHED();
175 scoped_refptr<TabProxy> ChromeFrameAutomationProxyImpl::CreateTabProxy(
176 int handle) {
177 DCHECK(tracker_->GetResource(handle) == NULL);
178 TabProxy* tab_proxy = new TabProxy(this, tracker_.get(), handle);
179 if (tab_proxy != NULL)
180 message_filter_->AddTabProxy(handle);
181 return tab_proxy;
184 void ChromeFrameAutomationProxyImpl::ReleaseTabProxy(AutomationHandle handle) {
185 message_filter_->RemoveTabProxy(handle);
188 struct LaunchTimeStats {
189 #ifndef NDEBUG
190 LaunchTimeStats() {
191 launch_time_begin_ = base::Time::Now();
194 void Dump() {
195 base::TimeDelta launch_time = base::Time::Now() - launch_time_begin_;
196 UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchTime", launch_time);
197 const int64 launch_milliseconds = launch_time.InMilliseconds();
198 if (launch_milliseconds > kAutomationServerReasonableLaunchDelay) {
199 LOG(WARNING) << "Automation server launch took longer than expected: " <<
200 launch_milliseconds << " ms.";
204 base::Time launch_time_begin_;
205 #else
206 void Dump() {}
207 #endif
210 AutomationProxyCacheEntry::AutomationProxyCacheEntry(
211 ChromeFrameLaunchParams* params, LaunchDelegate* delegate)
212 : profile_name(params->profile_name()),
213 launch_result_(AUTOMATION_LAUNCH_RESULT_INVALID) {
214 DCHECK(delegate);
215 thread_.reset(new base::Thread(WideToASCII(profile_name).c_str()));
216 thread_->Start();
217 // Use scoped_refptr so that the params will get released when the task
218 // has been run.
219 scoped_refptr<ChromeFrameLaunchParams> ref_params(params);
220 thread_->message_loop()->PostTask(
221 FROM_HERE, base::Bind(&AutomationProxyCacheEntry::CreateProxy,
222 base::Unretained(this), ref_params, delegate));
225 AutomationProxyCacheEntry::~AutomationProxyCacheEntry() {
226 DVLOG(1) << __FUNCTION__ << profile_name;
227 // Attempt to fix chrome_frame_tests crash seen at times on the IE6/IE7
228 // builders. It appears that there are cases when we can enter here when the
229 // AtExitManager is tearing down the global ProxyCache which causes a crash
230 // while tearing down the AutomationProxy object due to a NULL MessageLoop
231 // The AutomationProxy class uses the SyncChannel which assumes the existence
232 // of a MessageLoop instance.
233 // We leak the AutomationProxy pointer here to avoid a crash.
234 if (MessageLoop::current() == NULL) {
235 proxy_.release();
239 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params,
240 LaunchDelegate* delegate) {
241 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
242 DCHECK(delegate);
243 DCHECK(params);
244 DCHECK(proxy_.get() == NULL);
246 // We *must* create automationproxy in a thread that has message loop,
247 // since SyncChannel::Context construction registers event to be watched
248 // through ObjectWatcher which subscribes for the current thread message loop
249 // destruction notification.
251 // At same time we must destroy/stop the thread from another thread.
252 std::string channel_id = AutomationProxy::GenerateChannelID();
253 ChromeFrameAutomationProxyImpl* proxy =
254 new ChromeFrameAutomationProxyImpl(
255 this,
256 channel_id,
257 base::TimeDelta::FromMilliseconds(params->launch_timeout()));
259 // Ensure that the automation proxy actually respects our choice on whether
260 // or not to check the version.
261 proxy->set_perform_version_check(params->version_check());
263 // Launch browser
264 std::wstring command_line_string;
265 scoped_ptr<CommandLine> command_line;
266 if (chrome_launcher::CreateLaunchCommandLine(&command_line)) {
267 command_line->AppendSwitchASCII(switches::kAutomationClientChannelID,
268 channel_id);
270 // Run Chrome in Chrome Frame mode. In practice, this modifies the paths
271 // and registry keys that Chrome looks in via the BrowserDistribution
272 // mechanism.
273 command_line->AppendSwitch(switches::kChromeFrame);
275 // Chrome Frame never wants Chrome to start up with a First Run UI.
276 command_line->AppendSwitch(switches::kNoFirstRun);
278 // Chrome Frame never wants to run background extensions since they
279 // interfere with in-use updates.
280 command_line->AppendSwitch(switches::kDisableBackgroundMode);
282 command_line->AppendSwitch(switches::kDisablePopupBlocking);
284 #if defined(GOOGLE_CHROME_BUILD)
285 // Chrome Frame should use the native print dialog.
286 command_line->AppendSwitch(switches::kDisablePrintPreview);
287 #endif
289 // Disable the "Whoa! Chrome has crashed." dialog, because that isn't very
290 // useful for Chrome Frame users.
291 #ifndef NDEBUG
292 command_line->AppendSwitch(switches::kNoErrorDialogs);
293 #endif
295 // In headless mode runs like reliability test runs we want full crash dumps
296 // from chrome.
297 if (IsHeadlessMode())
298 command_line->AppendSwitch(switches::kFullMemoryCrashReport);
300 // In accessible mode automation tests expect renderer accessibility to be
301 // enabled in chrome.
302 if (IsAccessibleMode())
303 command_line->AppendSwitch(switches::kForceRendererAccessibility);
305 if (params->send_shutdown_delay_switch())
306 command_line->AppendSwitchASCII(switches::kChromeFrameShutdownDelay,
307 kChromeShutdownDelaySeconds);
309 DVLOG(1) << "Profile path: " << params->profile_path().value();
310 command_line->AppendSwitchPath(switches::kUserDataDir,
311 params->profile_path());
313 // Ensure that Chrome is running the specified version of chrome.dll.
314 command_line->AppendSwitchNative(switches::kChromeVersion,
315 GetCurrentModuleVersion());
317 if (!params->language().empty())
318 command_line->AppendSwitchNative(switches::kLang, params->language());
320 command_line_string = command_line->GetCommandLineString();
323 automation_server_launch_start_time_ = base::TimeTicks::Now();
325 if (command_line_string.empty() ||
326 !base::LaunchProcess(command_line_string, base::LaunchOptions(), NULL)) {
327 // We have no code for launch failure.
328 launch_result_ = AUTOMATION_LAUNCH_RESULT_INVALID;
329 } else {
330 // Launch timeout may happen if the new instance tries to communicate
331 // with an existing Chrome instance that is hung and displays msgbox
332 // asking to kill the previous one. This could be easily observed if the
333 // already running Chrome instance is running as high-integrity process
334 // (started with "Run as Administrator" or launched by another high
335 // integrity process) hence our medium-integrity process
336 // cannot SendMessage to it with request to activate itself.
338 // TODO(stoyan) AutomationProxy eats Hello message, hence installing
339 // message filter is pointless, we can leverage ObjectWatcher and use
340 // system thread pool to notify us when proxy->AppLaunch event is signaled.
341 LaunchTimeStats launch_stats;
342 // Wait for the automation server launch result, then stash away the
343 // version string it reported.
344 launch_result_ = proxy->WaitForAppLaunch();
345 launch_stats.Dump();
347 base::TimeDelta delta =
348 base::TimeTicks::Now() - automation_server_launch_start_time_;
350 if (launch_result_ == AUTOMATION_SUCCESS) {
351 UMA_HISTOGRAM_TIMES(
352 "ChromeFrame.AutomationServerLaunchSuccessTime", delta);
353 UMA_HISTOGRAM_TIMES(
354 base::FieldTrial::MakeName(
355 "ChromeFrame.AutomationServerLaunchSuccessTime",
356 "ChromeShutdownDelay"),
357 delta);
358 } else {
359 UMA_HISTOGRAM_TIMES(
360 "ChromeFrame.AutomationServerLaunchFailedTime", delta);
361 UMA_HISTOGRAM_TIMES(
362 base::FieldTrial::MakeName(
363 "ChromeFrame.AutomationServerLaunchFailedTime",
364 "ChromeShutdownDelay"),
365 delta);
368 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchResult",
369 launch_result_,
370 AUTOMATION_SUCCESS,
371 AUTOMATION_CREATE_TAB_FAILED,
372 AUTOMATION_CREATE_TAB_FAILED + 1);
375 TRACE_EVENT_END_ETW("chromeframe.createproxy", this, "");
377 // Finally set the proxy.
378 proxy_.reset(proxy);
379 launch_delegates_.push_back(delegate);
381 delegate->LaunchComplete(proxy_.get(), launch_result_);
384 void AutomationProxyCacheEntry::RemoveDelegate(LaunchDelegate* delegate,
385 base::WaitableEvent* done,
386 bool* was_last_delegate) {
387 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
388 DCHECK(delegate);
389 DCHECK(done);
390 DCHECK(was_last_delegate);
392 *was_last_delegate = false;
394 LaunchDelegates::iterator it = std::find(launch_delegates_.begin(),
395 launch_delegates_.end(), delegate);
396 if (it == launch_delegates_.end()) {
397 NOTREACHED();
398 } else {
399 if (launch_delegates_.size() == 1) {
400 *was_last_delegate = true;
402 // Process pending notifications.
403 thread_->message_loop()->RunUntilIdle();
405 // Take down the proxy since we no longer have any clients.
406 // Make sure we only do this once all pending messages have been cleared.
407 proxy_.reset(NULL);
409 // Be careful to remove from the list after running pending
410 // tasks. Otherwise the delegate being removed might miss out
411 // on pending notifications such as LaunchComplete.
412 launch_delegates_.erase(it);
415 done->Signal();
418 void AutomationProxyCacheEntry::AddDelegate(LaunchDelegate* delegate) {
419 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
420 DCHECK(std::find(launch_delegates_.begin(),
421 launch_delegates_.end(),
422 delegate) == launch_delegates_.end())
423 << "Same delegate being added twice";
424 DCHECK(launch_result_ != AUTOMATION_LAUNCH_RESULT_INVALID);
426 launch_delegates_.push_back(delegate);
427 delegate->LaunchComplete(proxy_.get(), launch_result_);
430 void AutomationProxyCacheEntry::OnChannelError() {
431 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
432 launch_result_ = AUTOMATION_SERVER_CRASHED;
433 LaunchDelegates::const_iterator it = launch_delegates_.begin();
434 for (; it != launch_delegates_.end(); ++it) {
435 (*it)->AutomationServerDied();
439 ProxyFactory::ProxyFactory() {
442 ProxyFactory::~ProxyFactory() {
443 for (size_t i = 0; i < proxies_.container().size(); ++i) {
444 DWORD result = proxies_[i]->WaitForThread(0);
445 if (WAIT_OBJECT_0 != result)
446 // TODO(stoyan): Don't leak proxies on exit.
447 DLOG(ERROR) << "Proxies leaked on exit.";
451 void ProxyFactory::GetAutomationServer(
452 LaunchDelegate* delegate, ChromeFrameLaunchParams* params,
453 void** automation_server_id) {
454 TRACE_EVENT_BEGIN_ETW("chromeframe.createproxy", this, "");
456 scoped_refptr<AutomationProxyCacheEntry> entry;
457 // Find already existing launcher thread for given profile
458 base::AutoLock lock(lock_);
459 for (size_t i = 0; i < proxies_.container().size(); ++i) {
460 if (proxies_[i]->IsSameProfile(params->profile_name())) {
461 entry = proxies_[i];
462 break;
466 if (entry == NULL) {
467 DVLOG(1) << __FUNCTION__ << " creating new proxy entry";
468 entry = new AutomationProxyCacheEntry(params, delegate);
469 proxies_.container().push_back(entry);
470 } else if (delegate) {
471 // Notify the new delegate of the launch status from the worker thread
472 // and add it to the list of delegates.
473 entry->message_loop()->PostTask(
474 FROM_HERE, base::Bind(&AutomationProxyCacheEntry::AddDelegate,
475 base::Unretained(entry.get()), delegate));
478 DCHECK(automation_server_id != NULL);
479 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId()));
481 *automation_server_id = entry;
484 bool ProxyFactory::ReleaseAutomationServer(void* server_id,
485 LaunchDelegate* delegate) {
486 if (!server_id) {
487 NOTREACHED();
488 return false;
491 AutomationProxyCacheEntry* entry =
492 reinterpret_cast<AutomationProxyCacheEntry*>(server_id);
494 #ifndef NDEBUG
495 lock_.Acquire();
496 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
497 proxies_.container().end(),
498 entry);
499 DCHECK(it != proxies_.container().end());
500 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId()));
502 lock_.Release();
503 #endif
505 // AddRef the entry object as we might need to take it out of the proxy
506 // stack and then uninitialize the entry.
507 entry->AddRef();
509 bool last_delegate = false;
510 if (delegate) {
511 base::WaitableEvent done(true, false);
512 entry->message_loop()->PostTask(
513 FROM_HERE,
514 base::Bind(&AutomationProxyCacheEntry::RemoveDelegate,
515 base::Unretained(entry), delegate, &done, &last_delegate));
516 done.Wait();
519 if (last_delegate) {
520 lock_.Acquire();
521 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
522 proxies_.container().end(),
523 entry);
524 if (it != proxies_.container().end()) {
525 proxies_.container().erase(it);
526 } else {
527 DLOG(ERROR) << "Proxy wasn't found. Proxy map is likely empty (size="
528 << proxies_.container().size() << ").";
531 lock_.Release();
534 entry->Release();
536 return true;
539 static base::LazyInstance<ProxyFactory>::Leaky
540 g_proxy_factory = LAZY_INSTANCE_INITIALIZER;
542 ChromeFrameAutomationClient::ChromeFrameAutomationClient()
543 : chrome_frame_delegate_(NULL),
544 chrome_window_(NULL),
545 tab_window_(NULL),
546 parent_window_(NULL),
547 automation_server_(NULL),
548 automation_server_id_(NULL),
549 ui_thread_id_(NULL),
550 init_state_(UNINITIALIZED),
551 use_chrome_network_(false),
552 proxy_factory_(g_proxy_factory.Pointer()),
553 handle_top_level_requests_(false),
554 tab_handle_(-1),
555 session_id_(-1),
556 external_tab_cookie_(0),
557 url_fetcher_(NULL),
558 url_fetcher_flags_(PluginUrlRequestManager::NOT_THREADSAFE),
559 navigate_after_initialization_(false),
560 route_all_top_level_navigations_(false),
561 send_shutdown_delay_switch_(true) {
562 InitializeFieldTrials();
565 ChromeFrameAutomationClient::~ChromeFrameAutomationClient() {
566 // Uninitialize must be called prior to the destructor
567 DCHECK(automation_server_ == NULL);
570 bool ChromeFrameAutomationClient::Initialize(
571 ChromeFrameDelegate* chrome_frame_delegate,
572 ChromeFrameLaunchParams* chrome_launch_params) {
573 DCHECK(!IsWindow());
574 chrome_frame_delegate_ = chrome_frame_delegate;
576 #ifndef NDEBUG
577 if (chrome_launch_params_ && chrome_launch_params_ != chrome_launch_params) {
578 DCHECK_EQ(chrome_launch_params_->url(), chrome_launch_params->url());
579 DCHECK_EQ(chrome_launch_params_->referrer(),
580 chrome_launch_params->referrer());
582 #endif
584 chrome_launch_params_ = chrome_launch_params;
586 ui_thread_id_ = base::PlatformThread::CurrentId();
587 #ifndef NDEBUG
588 // In debug mode give more time to work with a debugger.
589 if (IsDebuggerPresent()) {
590 // Don't use INFINITE (which is -1) or even MAXINT since we will convert
591 // from milliseconds to microseconds when stored in a base::TimeDelta,
592 // thus * 1000. An hour should be enough.
593 chrome_launch_params_->set_launch_timeout(60 * 60 * 1000);
594 } else {
595 DCHECK_LT(chrome_launch_params_->launch_timeout(),
596 MAXINT / 2000);
597 chrome_launch_params_->set_launch_timeout(
598 chrome_launch_params_->launch_timeout() * 2);
600 #endif // NDEBUG
602 // Create a window on the UI thread for marshaling messages back and forth
603 // from the IPC thread. This window cannot be a message only window as the
604 // external chrome tab window is created as a child of this window. This
605 // window is eventually reparented to the ActiveX plugin window.
606 if (!Create(GetDesktopWindow(), NULL, NULL,
607 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
608 WS_EX_TOOLWINDOW)) {
609 NOTREACHED();
610 return false;
613 // Keep object in memory, while the window is alive.
614 // Corresponding Release is in OnFinalMessage();
615 AddRef();
617 // Mark our state as initializing. We'll reach initialized once
618 // InitializeComplete is called successfully.
619 init_state_ = INITIALIZING;
621 HRESULT hr = S_OK;
623 if (chrome_launch_params_->url().is_valid())
624 navigate_after_initialization_ = false;
626 proxy_factory_->GetAutomationServer(static_cast<LaunchDelegate*>(this),
627 chrome_launch_params_, &automation_server_id_);
629 return true;
632 void ChromeFrameAutomationClient::Uninitialize() {
633 if (init_state_ == UNINITIALIZED) {
634 DLOG(WARNING) << __FUNCTION__ << ": Automation client not initialized";
635 return;
638 init_state_ = UNINITIALIZING;
640 // Called from client's FinalRelease() / destructor
641 if (url_fetcher_) {
642 // Clean up any outstanding requests
643 url_fetcher_->StopAllRequests();
644 url_fetcher_ = NULL;
647 if (tab_) {
648 tab_->RemoveObserver(this);
649 if (automation_server_)
650 automation_server_->ReleaseTabProxy(tab_->handle());
651 tab_ = NULL; // scoped_refptr::Release
654 // Wait for the automation proxy's worker thread to exit.
655 ReleaseAutomationServer();
657 // We must destroy the window, since if there are pending tasks
658 // window procedure may be invoked after DLL is unloaded.
659 // Unfortunately pending tasks are leaked.
660 if (::IsWindow(m_hWnd))
661 DestroyWindow();
663 // DCHECK(navigate_after_initialization_ == false);
664 handle_top_level_requests_ = false;
665 ui_thread_id_ = 0;
666 chrome_frame_delegate_ = NULL;
667 init_state_ = UNINITIALIZED;
670 bool ChromeFrameAutomationClient::InitiateNavigation(
671 const std::string& url,
672 const std::string& referrer,
673 NavigationConstraints* navigation_constraints) {
674 if (url.empty())
675 return false;
677 GURL parsed_url(url);
679 // Catch invalid URLs early.
680 // Can we allow this navigation to happen?
681 if (!CanNavigate(parsed_url, navigation_constraints)) {
682 DLOG(ERROR) << __FUNCTION__ << " Not allowing navigation to: " << url;
683 return false;
686 // If we are not yet initialized ignore attempts to navigate to the same url.
687 // Navigation attempts to the same URL could occur if the automation client
688 // was reused for a new active document instance.
689 if (!chrome_launch_params_ || is_initialized() ||
690 parsed_url != chrome_launch_params_->url()) {
691 // Important: Since we will be using the referrer_ variable from a
692 // different thread, we need to force a new std::string buffer instance for
693 // the referrer_ GURL variable. Otherwise we can run into strangeness when
694 // the GURL is accessed and it could result in a bad URL that can cause the
695 // referrer to be dropped or something worse.
696 GURL referrer_gurl(referrer.c_str());
697 if (!chrome_launch_params_) {
698 FilePath profile_path;
699 chrome_launch_params_ = new ChromeFrameLaunchParams(parsed_url,
700 referrer_gurl, profile_path, L"", SimpleResourceLoader::GetLanguage(),
701 false, false, route_all_top_level_navigations_,
702 send_shutdown_delay_switch_);
703 } else {
704 chrome_launch_params_->set_referrer(referrer_gurl);
705 chrome_launch_params_->set_url(parsed_url);
708 navigate_after_initialization_ = false;
710 if (is_initialized()) {
711 BeginNavigate();
712 } else {
713 navigate_after_initialization_ = true;
717 return true;
720 bool ChromeFrameAutomationClient::NavigateToIndex(int index) {
721 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
722 if (!automation_server_ || !tab_.get() || !tab_->is_valid()) {
723 return false;
726 DCHECK(::IsWindow(chrome_window_));
728 IPC::SyncMessage* msg = new AutomationMsg_NavigateExternalTabAtIndex(
729 tab_->handle(), index, NULL);
730 automation_server_->SendAsAsync(msg, new BeginNavigateContext(this),
731 this);
732 return true;
735 bool ChromeFrameAutomationClient::ForwardMessageFromExternalHost(
736 const std::string& message, const std::string& origin,
737 const std::string& target) {
738 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
739 if (!is_initialized())
740 return false;
742 tab_->HandleMessageFromExternalHost(message, origin, target);
743 return true;
746 bool ChromeFrameAutomationClient::SetProxySettings(
747 const std::string& json_encoded_proxy_settings) {
748 if (!is_initialized())
749 return false;
750 automation_server_->SendProxyConfig(json_encoded_proxy_settings);
751 return true;
754 void ChromeFrameAutomationClient::BeginNavigate() {
755 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
756 if (!automation_server_ || !tab_.get()) {
757 DLOG(WARNING) << "BeginNavigate - can't navigate.";
758 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR,
759 chrome_launch_params_->url().spec());
760 return;
763 DCHECK(::IsWindow(chrome_window_));
765 if (!tab_->is_valid()) {
766 DLOG(WARNING) << "BeginNavigate - tab isn't valid.";
767 return;
770 IPC::SyncMessage* msg =
771 new AutomationMsg_NavigateInExternalTab(tab_->handle(),
772 chrome_launch_params_->url(), chrome_launch_params_->referrer(),
773 NULL);
774 automation_server_->SendAsAsync(msg, new BeginNavigateContext(this), this);
776 RECT client_rect = {0};
777 chrome_frame_delegate_->GetBounds(&client_rect);
778 Resize(client_rect.right - client_rect.left,
779 client_rect.bottom - client_rect.top,
780 SWP_NOACTIVATE | SWP_NOZORDER);
783 void ChromeFrameAutomationClient::BeginNavigateCompleted(
784 AutomationMsg_NavigationResponseValues result) {
785 if (result == AUTOMATION_MSG_NAVIGATION_ERROR)
786 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR,
787 chrome_launch_params_->url().spec());
790 void ChromeFrameAutomationClient::FindInPage(const std::wstring& search_string,
791 FindInPageDirection forward,
792 FindInPageCase match_case,
793 bool find_next) {
794 // Note that we can be called by the find dialog after the tab has gone away.
795 if (!tab_)
796 return;
798 // What follows is quite similar to TabProxy::FindInPage() but uses
799 // the SyncMessageReplyDispatcher to avoid concerns about blocking
800 // synchronous messages.
801 AutomationMsg_Find_Params params;
802 params.search_string = WideToUTF16Hack(search_string);
803 params.find_next = find_next;
804 params.match_case = (match_case == CASE_SENSITIVE);
805 params.forward = (forward == FWD);
807 IPC::SyncMessage* msg =
808 new AutomationMsg_Find(tab_->handle(), params, NULL, NULL);
809 automation_server_->SendAsAsync(msg, NULL, this);
812 void ChromeFrameAutomationClient::OnChromeFrameHostMoved() {
813 // Use a local var to avoid the small possibility of getting the tab_
814 // member be cleared while we try to use it.
815 // Note that TabProxy is a RefCountedThreadSafe object, so we should be OK.
816 scoped_refptr<TabProxy> tab(tab_);
817 // There also is a possibility that tab_ has not been set yet,
818 // so we still need to test for NULL.
819 if (tab)
820 tab->OnHostMoved();
823 void ChromeFrameAutomationClient::CreateExternalTab() {
824 AutomationLaunchResult launch_result = AUTOMATION_SUCCESS;
825 DCHECK(IsWindow());
826 DCHECK(automation_server_ != NULL);
828 if (chrome_launch_params_->url().is_valid()) {
829 navigate_after_initialization_ = false;
832 ExternalTabSettings settings;
833 settings.parent = m_hWnd;
834 settings.style = WS_CHILD;
835 settings.is_incognito = chrome_launch_params_->incognito();
836 settings.load_requests_via_automation = !use_chrome_network_;
837 settings.handle_top_level_requests = handle_top_level_requests_;
838 settings.initial_url = chrome_launch_params_->url();
839 settings.referrer = chrome_launch_params_->referrer();
840 // Infobars disabled in widget mode.
841 settings.infobars_enabled = !chrome_launch_params_->widget_mode();
842 settings.route_all_top_level_navigations =
843 chrome_launch_params_->route_all_top_level_navigations();
845 UMA_HISTOGRAM_CUSTOM_COUNTS(
846 "ChromeFrame.HostNetworking", !use_chrome_network_, 1, 2, 3);
848 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.HandleTopLevelRequests",
849 handle_top_level_requests_, 1, 2, 3);
851 IPC::SyncMessage* message =
852 new AutomationMsg_CreateExternalTab(settings, NULL, NULL, 0, 0);
853 automation_server_->SendAsAsync(message, new CreateExternalTabContext(this),
854 this);
857 AutomationLaunchResult ChromeFrameAutomationClient::CreateExternalTabComplete(
858 HWND chrome_window, HWND tab_window, int tab_handle, int session_id) {
859 if (!automation_server_) {
860 // If we receive this notification while shutting down, do nothing.
861 DLOG(ERROR) << "CreateExternalTabComplete called when automation server "
862 << "was null!";
863 return AUTOMATION_CREATE_TAB_FAILED;
866 AutomationLaunchResult launch_result = AUTOMATION_SUCCESS;
867 if (tab_handle == 0 || !::IsWindow(chrome_window)) {
868 launch_result = AUTOMATION_CREATE_TAB_FAILED;
869 } else {
870 chrome_window_ = chrome_window;
871 tab_window_ = tab_window;
872 tab_ = automation_server_->CreateTabProxy(tab_handle);
873 tab_->AddObserver(this);
874 tab_handle_ = tab_handle;
875 session_id_ = session_id;
877 return launch_result;
880 // Invoked in the automation proxy's worker thread.
881 void ChromeFrameAutomationClient::LaunchComplete(
882 ChromeFrameAutomationProxy* proxy,
883 AutomationLaunchResult result) {
884 // If we're shutting down we don't keep a pointer to the automation server.
885 if (init_state_ != UNINITIALIZING) {
886 DCHECK(init_state_ == INITIALIZING);
887 automation_server_ = proxy;
888 } else {
889 DVLOG(1) << "Not storing automation server pointer due to shutting down";
892 if (result == AUTOMATION_SUCCESS) {
893 // NOTE: A potential problem here is that Uninitialize() may just have
894 // been called so we need to be careful and check the automation_server_
895 // pointer.
896 if (automation_server_ != NULL) {
897 // If we have a valid tab_handle here it means that we are attaching to
898 // an existing ExternalTabContainer instance, in which case we don't
899 // want to create an external tab instance in Chrome.
900 if (external_tab_cookie_ == 0) {
901 // Continue with Initialization - Create external tab
902 CreateExternalTab();
903 } else {
904 // Send a notification to Chrome that we are ready to connect to the
905 // ExternalTab.
906 IPC::SyncMessage* message =
907 new AutomationMsg_ConnectExternalTab(external_tab_cookie_, true,
908 m_hWnd, NULL, NULL, NULL, 0);
909 automation_server_->SendAsAsync(message,
910 new CreateExternalTabContext(this),
911 this);
912 DVLOG(1) << __FUNCTION__ << ": sending CreateExternalTabComplete";
915 } else {
916 // Launch failed. Note, we cannot delete proxy here.
917 PostTask(FROM_HERE,
918 base::Bind(&ChromeFrameAutomationClient::InitializeComplete,
919 base::Unretained(this), result));
923 // Invoked in the automation proxy's worker thread.
924 void ChromeFrameAutomationClient::AutomationServerDied() {
925 // Make sure we notify our delegate.
926 PostTask(
927 FROM_HERE, base::Bind(&ChromeFrameAutomationClient::InitializeComplete,
928 base::Unretained(this), AUTOMATION_SERVER_CRASHED));
929 // Then uninitialize.
930 PostTask(
931 FROM_HERE, base::Bind(&ChromeFrameAutomationClient::Uninitialize,
932 base::Unretained(this)));
935 void ChromeFrameAutomationClient::InitializeComplete(
936 AutomationLaunchResult result) {
937 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_);
938 if (result != AUTOMATION_SUCCESS) {
939 DLOG(WARNING) << "InitializeComplete: failure " << result;
940 } else {
941 init_state_ = INITIALIZED;
943 // If the host already have a window, ask Chrome to re-parent.
944 if (parent_window_)
945 SetParentWindow(parent_window_);
947 // If host specified destination URL - navigate. Apparently we do not use
948 // accelerator table.
949 if (navigate_after_initialization_) {
950 navigate_after_initialization_ = false;
951 BeginNavigate();
955 if (chrome_frame_delegate_) {
956 if (result == AUTOMATION_SUCCESS) {
957 chrome_frame_delegate_->OnAutomationServerReady();
958 } else {
959 std::string version;
960 if (automation_server_)
961 version = automation_server_->server_version();
962 chrome_frame_delegate_->OnAutomationServerLaunchFailed(result, version);
967 bool ChromeFrameAutomationClient::ProcessUrlRequestMessage(TabProxy* tab,
968 const IPC::Message& msg, bool ui_thread) {
969 // Either directly call appropriate url_fetcher function
970 // or postpone call to the UI thread.
971 uint16 msg_type = msg.type();
972 switch (msg_type) {
973 default:
974 return false;
976 case AutomationMsg_RequestStart::ID:
977 if (ui_thread || (url_fetcher_flags_ &
978 PluginUrlRequestManager::START_REQUEST_THREADSAFE)) {
979 AutomationMsg_RequestStart::Dispatch(&msg, url_fetcher_, this,
980 &PluginUrlRequestManager::StartUrlRequest);
981 return true;
983 break;
985 case AutomationMsg_RequestRead::ID:
986 if (ui_thread || (url_fetcher_flags_ &
987 PluginUrlRequestManager::READ_REQUEST_THREADSAFE)) {
988 AutomationMsg_RequestRead::Dispatch(&msg, url_fetcher_, this,
989 &PluginUrlRequestManager::ReadUrlRequest);
990 return true;
992 break;
994 case AutomationMsg_RequestEnd::ID:
995 if (ui_thread || (url_fetcher_flags_ &
996 PluginUrlRequestManager::STOP_REQUEST_THREADSAFE)) {
997 AutomationMsg_RequestEnd::Dispatch(&msg, url_fetcher_, this,
998 &PluginUrlRequestManager::EndUrlRequest);
999 return true;
1001 break;
1003 case AutomationMsg_DownloadRequestInHost::ID:
1004 if (ui_thread || (url_fetcher_flags_ &
1005 PluginUrlRequestManager::DOWNLOAD_REQUEST_THREADSAFE)) {
1006 AutomationMsg_DownloadRequestInHost::Dispatch(&msg, url_fetcher_, this,
1007 &PluginUrlRequestManager::DownloadUrlRequestInHost);
1008 return true;
1010 break;
1012 case AutomationMsg_GetCookiesFromHost::ID:
1013 if (ui_thread || (url_fetcher_flags_ &
1014 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE)) {
1015 AutomationMsg_GetCookiesFromHost::Dispatch(&msg, url_fetcher_, this,
1016 &PluginUrlRequestManager::GetCookiesFromHost);
1017 return true;
1019 break;
1021 case AutomationMsg_SetCookieAsync::ID:
1022 if (ui_thread || (url_fetcher_flags_ &
1023 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE)) {
1024 AutomationMsg_SetCookieAsync::Dispatch(&msg, url_fetcher_, this,
1025 &PluginUrlRequestManager::SetCookiesInHost);
1026 return true;
1028 break;
1031 PostTask(
1032 FROM_HERE,
1033 base::Bind(
1034 base::IgnoreResult(
1035 &ChromeFrameAutomationClient::ProcessUrlRequestMessage),
1036 base::Unretained(this), tab, msg, true));
1037 return true;
1040 void ChromeFrameAutomationClient::InitializeFieldTrials() {
1041 static base::FieldTrial* trial = NULL;
1042 if (!trial) {
1043 // Do one-time initialization of the field trial here.
1044 // TODO(robertshield): End the field trial before March 7th 2013.
1045 scoped_refptr<base::FieldTrial> new_trial =
1046 base::FieldTrialList::FactoryGetFieldTrial(
1047 "ChromeShutdownDelay", 1000, kWithDelayFieldTrialName,
1048 2013, 3, 7, NULL);
1050 // Be consistent for this client. Note that this will only have an effect
1051 // once the client id is persisted. See http://crbug.com/117188
1052 new_trial->UseOneTimeRandomization();
1054 new_trial->AppendGroup(kNoDelayFieldTrialName, 500); // 50% without.
1056 trial = new_trial.get();
1059 // Take action depending of which group we randomly land in.
1060 if (trial->group_name() == kWithDelayFieldTrialName)
1061 send_shutdown_delay_switch_ = true;
1062 else
1063 send_shutdown_delay_switch_ = false;
1067 // These are invoked in channel's background thread.
1068 // Cannot call any method of the activex here since it is a STA kind of being.
1069 // By default we marshal the IPC message to the main/GUI thread and from there
1070 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg).
1071 bool ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab,
1072 const IPC::Message& msg) {
1073 DCHECK(tab == tab_.get());
1074 // Quickly process network related messages.
1075 if (url_fetcher_ && ProcessUrlRequestMessage(tab, msg, false))
1076 return true;
1078 // Early check to avoid needless marshaling
1079 if (chrome_frame_delegate_ == NULL)
1080 return false;
1082 PostTask(FROM_HERE,
1083 base::Bind(&ChromeFrameAutomationClient::OnMessageReceivedUIThread,
1084 base::Unretained(this), msg));
1085 return true;
1088 void ChromeFrameAutomationClient::OnChannelError(TabProxy* tab) {
1089 DCHECK(tab == tab_.get());
1090 // Early check to avoid needless marshaling
1091 if (chrome_frame_delegate_ == NULL)
1092 return;
1094 PostTask(
1095 FROM_HERE,
1096 base::Bind(&ChromeFrameAutomationClient::OnChannelErrorUIThread,
1097 base::Unretained(this)));
1100 void ChromeFrameAutomationClient::OnMessageReceivedUIThread(
1101 const IPC::Message& msg) {
1102 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_);
1103 // Forward to the delegate.
1104 if (chrome_frame_delegate_)
1105 chrome_frame_delegate_->OnMessageReceived(msg);
1108 void ChromeFrameAutomationClient::OnChannelErrorUIThread() {
1109 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_);
1111 // Report a metric that something went wrong unexpectedly.
1112 CrashMetricsReporter::GetInstance()->IncrementMetric(
1113 CrashMetricsReporter::CHANNEL_ERROR_COUNT);
1115 // Forward to the delegate.
1116 if (chrome_frame_delegate_)
1117 chrome_frame_delegate_->OnChannelError();
1120 void ChromeFrameAutomationClient::ReportNavigationError(
1121 AutomationMsg_NavigationResponseValues error_code,
1122 const std::string& url) {
1123 if (!chrome_frame_delegate_)
1124 return;
1126 if (ui_thread_id_ == base::PlatformThread::CurrentId()) {
1127 chrome_frame_delegate_->OnLoadFailed(error_code, url);
1128 } else {
1129 PostTask(FROM_HERE,
1130 base::Bind(&ChromeFrameAutomationClient::ReportNavigationError,
1131 base::Unretained(this), error_code, url));
1135 void ChromeFrameAutomationClient::Resize(int width, int height,
1136 int flags) {
1137 if (tab_.get() && ::IsWindow(chrome_window())) {
1138 SetWindowPos(HWND_TOP, 0, 0, width, height, flags);
1139 tab_->Reposition(chrome_window(), HWND_TOP, 0, 0, width, height,
1140 flags, m_hWnd);
1144 void ChromeFrameAutomationClient::SetParentWindow(HWND parent_window) {
1145 parent_window_ = parent_window;
1146 // If we're done with the initialization step, go ahead
1147 if (is_initialized()) {
1148 if (parent_window == NULL) {
1149 // Hide and reparent the automation window. This window will get
1150 // reparented to the new ActiveX/Active document window when it gets
1151 // created.
1152 ShowWindow(SW_HIDE);
1153 SetParent(GetDesktopWindow());
1154 } else {
1155 if (!::IsWindow(chrome_window())) {
1156 DLOG(WARNING) << "Invalid Chrome Window handle in SetParentWindow";
1157 return;
1160 if (!SetParent(parent_window)) {
1161 DLOG(WARNING) << "Failed to set parent window for automation window. "
1162 << "Error = "
1163 << GetLastError();
1164 return;
1167 RECT parent_client_rect = {0};
1168 ::GetClientRect(parent_window, &parent_client_rect);
1169 int width = parent_client_rect.right - parent_client_rect.left;
1170 int height = parent_client_rect.bottom - parent_client_rect.top;
1172 Resize(width, height, SWP_SHOWWINDOW | SWP_NOZORDER);
1177 void ChromeFrameAutomationClient::ReleaseAutomationServer() {
1178 if (automation_server_id_) {
1179 // Cache the server id and clear the automation_server_id_ before
1180 // calling ReleaseAutomationServer. The reason we do this is that
1181 // we must cancel pending messages before we release the automation server.
1182 // Furthermore, while ReleaseAutomationServer is running, we could get
1183 // a callback to LaunchComplete which could cause an external tab to be
1184 // created. Ideally the callbacks should be dropped.
1185 // TODO(ananta)
1186 // Refactor the ChromeFrameAutomationProxy code to not depend on
1187 // AutomationProxy and simplify the whole mess.
1188 void* server_id = automation_server_id_;
1189 automation_server_id_ = NULL;
1191 if (automation_server_) {
1192 // Make sure to clean up any pending sync messages before we go away.
1193 automation_server_->CancelAsync(this);
1196 proxy_factory_->ReleaseAutomationServer(server_id, this);
1197 automation_server_ = NULL;
1199 // automation_server_ must not have been set to non NULL.
1200 // (if this regresses, start by looking at LaunchComplete()).
1201 DCHECK(automation_server_ == NULL);
1202 } else {
1203 DCHECK(automation_server_ == NULL);
1207 void ChromeFrameAutomationClient::SendContextMenuCommandToChromeFrame(
1208 int selected_command) {
1209 if (tab_)
1210 tab_->SendContextMenuCommand(selected_command);
1213 std::wstring ChromeFrameAutomationClient::GetVersion() const {
1214 return GetCurrentModuleVersion();
1217 void ChromeFrameAutomationClient::Print(HDC print_dc,
1218 const RECT& print_bounds) {
1219 if (!tab_window_) {
1220 NOTREACHED();
1221 return;
1224 HDC window_dc = ::GetDC(tab_window_);
1226 BitBlt(print_dc, print_bounds.left, print_bounds.top,
1227 print_bounds.right - print_bounds.left,
1228 print_bounds.bottom - print_bounds.top,
1229 window_dc, print_bounds.left, print_bounds.top,
1230 SRCCOPY);
1232 ::ReleaseDC(tab_window_, window_dc);
1235 void ChromeFrameAutomationClient::PrintTab() {
1236 if (tab_)
1237 tab_->PrintAsync();
1240 void ChromeFrameAutomationClient::AttachExternalTab(
1241 uint64 external_tab_cookie) {
1242 DCHECK_EQ(static_cast<TabProxy*>(NULL), tab_.get());
1243 DCHECK_EQ(-1, tab_handle_);
1245 external_tab_cookie_ = external_tab_cookie;
1248 void ChromeFrameAutomationClient::BlockExternalTab(uint64 cookie) {
1249 // The host does not want this tab to be shown (due popup blocker).
1250 IPC::SyncMessage* message =
1251 new AutomationMsg_ConnectExternalTab(cookie, false, m_hWnd,
1252 NULL, NULL, NULL, 0);
1253 automation_server_->SendAsAsync(message, NULL, this);
1256 void ChromeFrameAutomationClient::SetPageFontSize(
1257 enum AutomationPageFontSize font_size) {
1258 if (font_size < SMALLEST_FONT ||
1259 font_size > LARGEST_FONT) {
1260 NOTREACHED() << "Invalid font size specified : "
1261 << font_size;
1262 return;
1265 automation_server_->Send(
1266 new AutomationMsg_SetPageFontSize(tab_handle_, font_size));
1269 void ChromeFrameAutomationClient::RemoveBrowsingData(int remove_mask) {
1270 automation_server_->Send(new AutomationMsg_RemoveBrowsingData(remove_mask));
1273 void ChromeFrameAutomationClient::SetUrlFetcher(
1274 PluginUrlRequestManager* url_fetcher) {
1275 DCHECK(url_fetcher != NULL);
1276 url_fetcher_ = url_fetcher;
1277 url_fetcher_flags_ = url_fetcher->GetThreadSafeFlags();
1278 url_fetcher_->set_delegate(this);
1281 void ChromeFrameAutomationClient::SetZoomLevel(content::PageZoom zoom_level) {
1282 if (automation_server_) {
1283 automation_server_->Send(new AutomationMsg_SetZoomLevel(tab_handle_,
1284 zoom_level));
1288 void ChromeFrameAutomationClient::OnUnload(bool* should_unload) {
1289 *should_unload = true;
1290 if (automation_server_) {
1291 const DWORD kUnloadEventTimeout = 20000;
1293 IPC::SyncMessage* msg = new AutomationMsg_RunUnloadHandlers(tab_handle_,
1294 should_unload);
1295 base::WaitableEvent unload_call_finished(false, false);
1296 UnloadContext* unload_context = new UnloadContext(&unload_call_finished,
1297 should_unload);
1298 automation_server_->SendAsAsync(msg, unload_context, this);
1299 HANDLE done = unload_call_finished.handle();
1300 WaitWithMessageLoop(&done, 1, kUnloadEventTimeout);
1304 //////////////////////////////////////////////////////////////////////////
1305 // PluginUrlRequestDelegate implementation.
1306 // Forward network related responses to Chrome.
1308 void ChromeFrameAutomationClient::OnResponseStarted(
1309 int request_id, const char* mime_type, const char* headers, int size,
1310 base::Time last_modified, const std::string& redirect_url,
1311 int redirect_status, const net::HostPortPair& socket_address,
1312 uint64 upload_size) {
1313 AutomationURLResponse response;
1314 response.mime_type = mime_type;
1315 if (headers)
1316 response.headers = headers;
1317 response.content_length = size;
1318 response.last_modified = last_modified;
1319 response.redirect_url = redirect_url;
1320 response.redirect_status = redirect_status;
1321 response.socket_address = socket_address;
1322 response.upload_size = upload_size;
1324 automation_server_->Send(new AutomationMsg_RequestStarted(
1325 tab_->handle(), request_id, response));
1328 void ChromeFrameAutomationClient::OnReadComplete(int request_id,
1329 const std::string& data) {
1330 automation_server_->Send(new AutomationMsg_RequestData(
1331 tab_->handle(), request_id, data));
1334 void ChromeFrameAutomationClient::OnResponseEnd(
1335 int request_id,
1336 const net::URLRequestStatus& status) {
1337 automation_server_->Send(new AutomationMsg_RequestEnd(
1338 tab_->handle(), request_id, status));
1341 void ChromeFrameAutomationClient::OnCookiesRetrieved(bool success,
1342 const GURL& url, const std::string& cookie_string, int cookie_id) {
1343 automation_server_->Send(new AutomationMsg_GetCookiesHostResponse(
1344 tab_->handle(), success, url, cookie_string, cookie_id));