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