Initialize all data members in HTTPResponseInfo's new ctor and remove the related...
[chromium-blink-merge.git] / chrome_frame / chrome_frame_automation.cc
blob216b7a9074cdc17db2cc53aea11a4380d498e62b
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"
30 #ifdef NDEBUG
31 int64 kAutomationServerReasonableLaunchDelay = 1000; // in milliseconds
32 #else
33 int64 kAutomationServerReasonableLaunchDelay = 1000 * 10;
34 #endif
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
42 // threads.
43 Lock g_ChromeFrameHistogramLock;
45 class ChromeFrameAutomationProxyImpl::TabProxyNotificationMessageFilter
46 : public IPC::ChannelProxy::MessageFilter {
47 public:
48 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker)
49 : tracker_(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())
62 return false;
64 int tab_handle = 0;
65 if (!ChromeFrameDelegateImpl::IsTabMessage(message, &tab_handle))
66 return false;
68 // Get AddRef-ed pointer to corresponding TabProxy object
69 TabProxy* tab = static_cast<TabProxy*>(tracker_->GetResource(tab_handle));
70 if (tab) {
71 tab->OnMessageReceived(message);
72 tab->Release();
73 } else {
74 DLOG(ERROR) << "Failed to find TabProxy for tab:" << tab_handle;
76 return true;
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));
84 if (tab) {
85 tab->OnChannelError();
86 tab->Release();
91 private:
92 AutomationHandleTracker* tracker_;
93 std::list<AutomationHandle> tabs_list_;
96 class ChromeFrameAutomationProxyImpl::CFMsgDispatcher
97 : public SyncMessageReplyDispatcher {
98 public:
99 CFMsgDispatcher() : SyncMessageReplyDispatcher() {}
100 protected:
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);
107 break;
108 case AutomationMsg_NavigateExternalTabAtIndex::ID:
109 case AutomationMsg_NavigateInExternalTab::ID:
110 InvokeCallback<BeginNavigateContext>(msg, context);
111 break;
112 case AutomationMsg_InstallExtension::ID:
113 InvokeCallback<InstallExtensionContext>(msg, context);
114 break;
115 case AutomationMsg_LoadExpandedExtension::ID:
116 InvokeCallback<InstallExtensionContext>(msg, context);
117 break;
118 case AutomationMsg_GetEnabledExtensions::ID:
119 InvokeCallback<GetEnabledExtensionsContext>(msg, context);
120 break;
121 default:
122 NOTREACHED();
124 return true;
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) {
153 sync_->Cancel(key);
156 void ChromeFrameAutomationProxyImpl::OnChannelError() {
157 DLOG(ERROR) << "Automation server died";
158 if (proxy_entry_) {
159 proxy_entry_->OnChannelError();
160 } else {
161 NOTREACHED();
165 scoped_refptr<TabProxy> ChromeFrameAutomationProxyImpl::CreateTabProxy(
166 int handle) {
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);
171 return tab_proxy;
174 void ChromeFrameAutomationProxyImpl::ReleaseTabProxy(AutomationHandle handle) {
175 message_filter_->RemoveTabProxy(handle);
178 struct LaunchTimeStats {
179 #ifndef NDEBUG
180 LaunchTimeStats() {
181 launch_time_begin_ = base::Time::Now();
184 void Dump() {
185 base::TimeDelta launch_time = base::Time::Now() - launch_time_begin_;
186 THREAD_SAFE_UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchTime",
187 launch_time);
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_;
196 #else
197 void Dump() {}
198 #endif
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) {
208 DCHECK(delegate);
209 thread_.reset(new base::Thread(WideToASCII(profile_name).c_str()));
210 thread_->Start();
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) {
225 proxy_.release();
229 void AutomationProxyCacheEntry::StartSendUmaInterval(
230 ChromeFrameHistogramSnapshots* snapshots, int send_interval) {
231 DCHECK(snapshots);
232 DCHECK(!snapshots_);
233 snapshots_ = snapshots;
234 uma_send_interval_ = send_interval;
235 thread_->message_loop()->PostDelayedTask(FROM_HERE,
236 NewRunnableMethod(this, &AutomationProxyCacheEntry::SendUMAData),
237 send_interval);
240 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params,
241 LaunchDelegate* delegate) {
242 DCHECK(IsSameThread(PlatformThread::CurrentId()));
243 DCHECK(delegate);
244 DCHECK(params);
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());
256 // Launch browser
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
264 // mechanism.
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.
274 #ifndef NDEBUG
275 command_line->AppendSwitch(switches::kNoErrorDialogs);
276 #endif
278 // In headless mode runs like reliability test runs we want full crash dumps
279 // from chrome.
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;
298 } else {
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();
314 launch_stats.Dump();
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);
322 } else {
323 THREAD_SAFE_UMA_HISTOGRAM_TIMES(
324 "ChromeFrame.AutomationServerLaunchFailedTime", delta);
327 THREAD_SAFE_UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchResult",
328 launch_result_,
329 AUTOMATION_SUCCESS,
330 AUTOMATION_CREATE_TAB_FAILED,
331 AUTOMATION_CREATE_TAB_FAILED + 1);
334 TRACE_EVENT_END("chromeframe.createproxy", this, "");
336 // Finally set the proxy.
337 proxy_.reset(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()));
347 DCHECK(delegate);
348 DCHECK(done);
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()) {
356 NOTREACHED();
357 } else {
358 if (launch_delegates_.size() == 1) {
359 *was_last_delegate = true;
361 if (snapshots_)
362 SendUMAData();
364 // Take down the proxy since we no longer have any clients.
365 proxy_.reset(NULL);
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);
376 done->Signal();
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()));
402 DCHECK(snapshots_);
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()) {
406 return;
409 if (!proxy_.get()) {
410 NOTREACHED() << __FUNCTION__ << " Invalid proxy entry";
411 } else {
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),
421 uma_send_interval_);
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())) {
453 entry = proxies_[i];
454 break;
458 if (entry == NULL) {
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_,
467 uma_send_interval_);
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) {
484 if (!server_id) {
485 NOTREACHED();
486 return false;
489 AutomationProxyCacheEntry* entry =
490 reinterpret_cast<AutomationProxyCacheEntry*>(server_id);
492 #ifndef NDEBUG
493 lock_.Acquire();
494 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
495 proxies_.container().end(),
496 entry);
497 DCHECK(it != proxies_.container().end());
498 DCHECK(!entry->IsSameThread(PlatformThread::CurrentId()));
500 lock_.Release();
501 #endif
503 // AddRef the entry object as we might need to take it out of the proxy
504 // stack and then uninitialize the entry.
505 entry->AddRef();
507 bool last_delegate = false;
508 if (delegate) {
509 base::WaitableEvent done(true, false);
510 entry->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(entry,
511 &AutomationProxyCacheEntry::RemoveDelegate, delegate, &done,
512 &last_delegate));
513 done.Wait();
516 if (last_delegate) {
517 lock_.Acquire();
518 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
519 proxies_.container().end(),
520 entry);
521 proxies_.container().erase(it);
522 lock_.Release();
525 entry->Release();
527 return true;
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),
540 tab_window_(NULL),
541 parent_window_(NULL),
542 automation_server_(NULL),
543 automation_server_id_(NULL),
544 ui_thread_id_(NULL),
545 init_state_(UNINITIALIZED),
546 use_chrome_network_(false),
547 proxy_factory_(g_proxy_factory.get()),
548 handle_top_level_requests_(false),
549 tab_handle_(-1),
550 external_tab_cookie_(0),
551 url_fetcher_(NULL),
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) {
564 DCHECK(!IsWindow());
565 chrome_frame_delegate_ = chrome_frame_delegate;
567 #ifndef NDEBUG
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());
573 #endif
575 chrome_launch_params_ = chrome_launch_params;
577 ui_thread_id_ = PlatformThread::CurrentId();
578 #ifndef NDEBUG
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);
585 } else {
586 DCHECK_LT(chrome_launch_params_->launch_timeout(),
587 MAXINT / 2000);
588 chrome_launch_params_->set_launch_timeout(
589 chrome_launch_params_->launch_timeout() * 2);
591 #endif // NDEBUG
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,
599 WS_EX_TOOLWINDOW)) {
600 NOTREACHED();
601 return false;
604 // Keep object in memory, while the window is alive.
605 // Corresponding Release is in OnFinalMessage();
606 AddRef();
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_);
618 return true;
621 void ChromeFrameAutomationClient::Uninitialize() {
622 if (init_state_ == UNINITIALIZED) {
623 DLOG(WARNING) << __FUNCTION__ << ": Automation client not initialized";
624 return;
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
638 if (url_fetcher_) {
639 // Clean up any outstanding requests
640 url_fetcher_->StopAllRequests();
641 url_fetcher_ = NULL;
644 if (tab_.get()) {
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))
658 DestroyWindow();
660 DCHECK(navigate_after_initialization_ == false);
661 handle_top_level_requests_ = false;
662 ui_thread_id_ = 0;
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) {
669 if (url.empty())
670 return false;
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;
678 return false;
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);
692 } else {
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()) {
700 BeginNavigate();
701 } else {
702 navigate_after_initialization_ = true;
706 return 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()) {
712 return false;
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),
720 this);
721 return true;
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())
729 return false;
731 tab_->HandleMessageFromExternalHost(message, origin, target);
732 return true;
735 bool ChromeFrameAutomationClient::SetProxySettings(
736 const std::string& json_encoded_proxy_settings) {
737 if (!is_initialized())
738 return false;
739 automation_server_->SendProxyConfig(json_encoded_proxy_settings);
740 return true;
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());
749 return;
752 DCHECK(::IsWindow(chrome_window_));
754 if (!tab_->is_valid()) {
755 DLOG(WARNING) << "BeginNavigate - tab isn't valid.";
756 return;
759 IPC::SyncMessage* msg =
760 new AutomationMsg_NavigateInExternalTab(0, tab_->handle(),
761 chrome_launch_params_->url(), chrome_launch_params_->referrer(),
762 NULL);
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,
782 bool find_next) {
783 DCHECK(tab_.get());
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;
789 params.unused = 0;
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,
802 void* user_data) {
803 if (automation_server_ == NULL) {
804 InstallExtensionComplete(crx_path,
805 user_data,
806 AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
807 return;
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,
822 void* user_data,
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>());
834 return;
837 GetEnabledExtensionsContext* ctx = new GetEnabledExtensionsContext(
838 this, user_data);
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(
848 void* user_data,
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)
868 tab->OnHostMoved();
871 void ChromeFrameAutomationClient::LoadExpandedExtension(
872 const FilePath& path,
873 void* user_data) {
874 if (automation_server_ == NULL) {
875 InstallExtensionComplete(path,
876 user_data,
877 AUTOMATION_MSG_EXTENSION_INSTALL_FAILED);
878 return;
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;
893 DCHECK(IsWindow());
894 DCHECK(automation_server_ != NULL);
896 if (chrome_launch_params_->url().is_valid()) {
897 navigate_after_initialization_ = false;
900 const IPC::ExternalTabSettings settings = {
901 m_hWnd,
902 gfx::Rect(),
903 WS_CHILD,
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),
922 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 "
930 << "was null!";
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;
937 } else {
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())
950 return;
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;
969 } else {
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_
976 // pointer.
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
983 CreateExternalTab();
984 } else {
985 // Send a notification to Chrome that we are ready to connect to the
986 // ExternalTab.
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),
992 this);
993 DLOG(INFO) << __FUNCTION__ << ": sending CreateExternalTabComplete";
996 } else {
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();
1021 } else {
1022 init_state_ = INITIALIZED;
1024 // If the host already have a window, ask Chrome to re-parent.
1025 if (parent_window_)
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;
1032 BeginNavigate();
1036 if (chrome_frame_delegate_) {
1037 if (result == AUTOMATION_SUCCESS) {
1038 chrome_frame_delegate_->OnAutomationServerReady();
1039 } else {
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();
1050 switch (msg_type) {
1051 default:
1052 return false;
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);
1059 return true;
1061 break;
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);
1068 return true;
1070 break;
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);
1077 return true;
1079 break;
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);
1086 return true;
1088 break;
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);
1095 return true;
1097 break;
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);
1104 return true;
1106 break;
1109 PostTask(FROM_HERE, NewRunnableMethod(this,
1110 &ChromeFrameAutomationClient::ProcessUrlRequestMessage, tab, msg, true));
1111 return true;
1114 // These are invoked in channel's background thread.
1115 // Cannot call any method of the activex/npapi here since they are STA
1116 // kind of beings.
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))
1124 return;
1126 // Early check to avoid needless marshaling
1127 if (chrome_frame_delegate_ == NULL)
1128 return;
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)
1138 return;
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_)
1163 return;
1165 if (ui_thread_id_ == PlatformThread::CurrentId()) {
1166 chrome_frame_delegate_->OnLoadFailed(error_code, url);
1167 } else {
1168 PostTask(FROM_HERE, NewRunnableMethod(this,
1169 &ChromeFrameAutomationClient::ReportNavigationError,
1170 error_code, url));
1174 void ChromeFrameAutomationClient::Resize(int width, int height,
1175 int flags) {
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,
1179 flags, m_hWnd);
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
1190 // created.
1191 ShowWindow(SW_HIDE);
1192 SetParent(GetDesktopWindow());
1193 } else {
1194 if (!::IsWindow(chrome_window())) {
1195 DLOG(WARNING) << "Invalid Chrome Window handle in SetParentWindow";
1196 return;
1199 if (!SetParent(parent_window)) {
1200 DLOG(WARNING) << "Failed to set parent window for automation window. "
1201 << "Error = "
1202 << GetLastError();
1203 return;
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.
1224 // TODO(ananta)
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);
1241 } else {
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;
1257 if (version_info)
1258 version = version_info->product_version();
1260 return version;
1263 void ChromeFrameAutomationClient::Print(HDC print_dc,
1264 const RECT& print_bounds) {
1265 if (!tab_window_) {
1266 NOTREACHED();
1267 return;
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,
1276 SRCCOPY);
1278 ::ReleaseDC(tab_window_, window_dc);
1281 void ChromeFrameAutomationClient::PrintTab() {
1282 tab_->PrintAsync();
1285 bool ChromeFrameAutomationClient::Reinitialize(
1286 ChromeFrameDelegate* delegate,
1287 PluginUrlRequestManager* url_fetcher) {
1288 if (url_fetcher_) {
1289 // Clean up any outstanding requests
1290 url_fetcher_->StopAllRequests();
1291 url_fetcher_ = NULL;
1294 if (!tab_.get() || !::IsWindow(chrome_window_)) {
1295 NOTREACHED();
1296 DLOG(WARNING) << "ChromeFrameAutomationClient instance reused "
1297 << "with invalid tab";
1298 return false;
1301 if (!delegate) {
1302 NOTREACHED();
1303 return false;
1306 chrome_frame_delegate_ = delegate;
1307 DeleteAllPendingTasks();
1308 SetUrlFetcher(url_fetcher);
1309 SetParentWindow(NULL);
1310 return true;
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,
1325 NULL, NULL, NULL);
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 : "
1334 << font_size;
1335 return;
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));
1354 } else {
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_,
1363 zoom_level));
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 = {
1376 mime_type,
1377 headers ? headers : "",
1378 size,
1379 last_modified,
1380 redirect_url,
1381 redirect_status
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(),
1391 request_id, data));
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));