Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(522)

Side by Side Diff: chrome_frame/chrome_frame_automation.cc

Issue 126143005: Remove Chrome Frame code and resources. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sync to r244038 Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome_frame/chrome_frame_automation.h ('k') | chrome_frame/chrome_frame_delegate.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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:
49 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker)
50 : tracker_(tracker) {
51 }
52
53 void AddTabProxy(AutomationHandle tab_proxy) {
54 base::AutoLock lock(lock_);
55 tabs_list_.push_back(tab_proxy);
56 }
57
58 void RemoveTabProxy(AutomationHandle tab_proxy) {
59 base::AutoLock lock(lock_);
60 tabs_list_.remove(tab_proxy);
61 }
62
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
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:
106 CFMsgDispatcher() : SyncMessageReplyDispatcher() {}
107 protected:
108 virtual bool HandleMessageType(const IPC::Message& msg,
109 SyncMessageCallContext* context) {
110 return true;
111 }
112 };
113
114 ChromeFrameAutomationProxyImpl::ChromeFrameAutomationProxyImpl(
115 AutomationProxyCacheEntry* entry,
116 std::string channel_id, base::TimeDelta launch_timeout)
117 : AutomationProxy(launch_timeout, false), proxy_entry_(entry) {
118 TRACE_EVENT_BEGIN_ETW("chromeframe.automationproxy", this, "");
119
120 InitializeChannel(channel_id, false);
121
122 sync_ = new CFMsgDispatcher();
123 message_filter_ = new TabProxyNotificationMessageFilter(tracker_.get());
124
125 // Order of filters is not important.
126 channel_->AddFilter(message_filter_.get());
127 channel_->AddFilter(sync_.get());
128 }
129
130 ChromeFrameAutomationProxyImpl::~ChromeFrameAutomationProxyImpl() {
131 TRACE_EVENT_END_ETW("chromeframe.automationproxy", this, "");
132 }
133
134 void ChromeFrameAutomationProxyImpl::SendAsAsync(
135 IPC::SyncMessage* msg,
136 SyncMessageReplyDispatcher::SyncMessageCallContext* context, void* key) {
137 sync_->Push(msg, context, key);
138 channel_->ChannelProxy::Send(msg);
139 }
140
141 void ChromeFrameAutomationProxyImpl::CancelAsync(void* key) {
142 sync_->Cancel(key);
143 }
144
145 void ChromeFrameAutomationProxyImpl::OnChannelError() {
146 DLOG(ERROR) << "Automation server died";
147 if (proxy_entry_) {
148 proxy_entry_->OnChannelError();
149 } else {
150 NOTREACHED();
151 }
152 }
153
154 scoped_refptr<TabProxy> ChromeFrameAutomationProxyImpl::CreateTabProxy(
155 int handle) {
156 DCHECK(tracker_->GetResource(handle) == NULL);
157 TabProxy* tab_proxy = new TabProxy(this, tracker_.get(), handle);
158 if (tab_proxy != NULL)
159 message_filter_->AddTabProxy(handle);
160 return tab_proxy;
161 }
162
163 void ChromeFrameAutomationProxyImpl::ReleaseTabProxy(AutomationHandle handle) {
164 message_filter_->RemoveTabProxy(handle);
165 }
166
167 struct LaunchTimeStats {
168 #ifndef NDEBUG
169 LaunchTimeStats() {
170 launch_time_begin_ = base::Time::Now();
171 }
172
173 void Dump() {
174 base::TimeDelta launch_time = base::Time::Now() - launch_time_begin_;
175 UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchTime", launch_time);
176 const int64 launch_milliseconds = launch_time.InMilliseconds();
177 if (launch_milliseconds > kAutomationServerReasonableLaunchDelay) {
178 LOG(WARNING) << "Automation server launch took longer than expected: " <<
179 launch_milliseconds << " ms.";
180 }
181 }
182
183 base::Time launch_time_begin_;
184 #else
185 void Dump() {}
186 #endif
187 };
188
189 AutomationProxyCacheEntry::AutomationProxyCacheEntry(
190 ChromeFrameLaunchParams* params, LaunchDelegate* delegate)
191 : profile_name(params->profile_name()),
192 launch_result_(AUTOMATION_LAUNCH_RESULT_INVALID) {
193 DCHECK(delegate);
194 thread_.reset(new base::Thread(WideToASCII(profile_name).c_str()));
195 thread_->Start();
196 // Use scoped_refptr so that the params will get released when the task
197 // has been run.
198 scoped_refptr<ChromeFrameLaunchParams> ref_params(params);
199 thread_->message_loop()->PostTask(
200 FROM_HERE, base::Bind(&AutomationProxyCacheEntry::CreateProxy,
201 base::Unretained(this), ref_params, delegate));
202 }
203
204 AutomationProxyCacheEntry::~AutomationProxyCacheEntry() {
205 DVLOG(1) << __FUNCTION__ << profile_name;
206 // Attempt to fix chrome_frame_tests crash seen at times on the IE6/IE7
207 // builders. It appears that there are cases when we can enter here when the
208 // AtExitManager is tearing down the global ProxyCache which causes a crash
209 // while tearing down the AutomationProxy object due to a NULL MessageLoop
210 // The AutomationProxy class uses the SyncChannel which assumes the existence
211 // of a MessageLoop instance.
212 // We leak the AutomationProxy pointer here to avoid a crash.
213 if (base::MessageLoop::current() == NULL) {
214 proxy_.release();
215 }
216 }
217
218 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params,
219 LaunchDelegate* delegate) {
220 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
221 DCHECK(delegate);
222 DCHECK(params);
223 DCHECK(proxy_.get() == NULL);
224
225 // We *must* create automationproxy in a thread that has message loop,
226 // since SyncChannel::Context construction registers event to be watched
227 // through ObjectWatcher which subscribes for the current thread message loop
228 // destruction notification.
229
230 // At same time we must destroy/stop the thread from another thread.
231 std::string channel_id = AutomationProxy::GenerateChannelID();
232 ChromeFrameAutomationProxyImpl* proxy =
233 new ChromeFrameAutomationProxyImpl(
234 this,
235 channel_id,
236 base::TimeDelta::FromMilliseconds(params->launch_timeout()));
237
238 // Ensure that the automation proxy actually respects our choice on whether
239 // or not to check the version.
240 proxy->set_perform_version_check(params->version_check());
241
242 // Launch browser
243 std::wstring command_line_string;
244 scoped_ptr<CommandLine> command_line;
245 if (chrome_launcher::CreateLaunchCommandLine(&command_line)) {
246 command_line->AppendSwitchASCII(switches::kAutomationClientChannelID,
247 channel_id);
248
249 // Run Chrome in Chrome Frame mode. In practice, this modifies the paths
250 // and registry keys that Chrome looks in via the BrowserDistribution
251 // mechanism.
252 command_line->AppendSwitch(switches::kChromeFrame);
253
254 // Chrome Frame never wants Chrome to start up with a First Run UI.
255 command_line->AppendSwitch(switches::kNoFirstRun);
256
257 // Chrome Frame never wants to run background extensions since they
258 // interfere with in-use updates.
259 command_line->AppendSwitch(switches::kDisableBackgroundMode);
260
261 command_line->AppendSwitch(switches::kDisablePopupBlocking);
262
263 #if defined(GOOGLE_CHROME_BUILD)
264 // Chrome Frame should use the native print dialog.
265 command_line->AppendSwitch(switches::kDisablePrintPreview);
266 #endif
267
268 // Disable the "Whoa! Chrome has crashed." dialog, because that isn't very
269 // useful for Chrome Frame users.
270 #ifndef NDEBUG
271 command_line->AppendSwitch(switches::kNoErrorDialogs);
272 #endif
273
274 // In headless mode runs like reliability test runs we want full crash dumps
275 // from chrome.
276 if (IsHeadlessMode())
277 command_line->AppendSwitch(switches::kFullMemoryCrashReport);
278
279 // In accessible mode automation tests expect renderer accessibility to be
280 // enabled in chrome.
281 if (IsAccessibleMode())
282 command_line->AppendSwitch(switches::kForceRendererAccessibility);
283
284 DVLOG(1) << "Profile path: " << params->profile_path().value();
285 command_line->AppendSwitchPath(switches::kUserDataDir,
286 params->profile_path());
287
288 // Ensure that Chrome is running the specified version of chrome.dll.
289 command_line->AppendSwitchNative(switches::kChromeVersion,
290 GetCurrentModuleVersion());
291
292 if (!params->language().empty())
293 command_line->AppendSwitchNative(switches::kLang, params->language());
294
295 command_line_string = command_line->GetCommandLineString();
296 }
297
298 automation_server_launch_start_time_ = base::TimeTicks::Now();
299
300 if (command_line_string.empty() ||
301 !base::LaunchProcess(command_line_string, base::LaunchOptions(), NULL)) {
302 // We have no code for launch failure.
303 launch_result_ = AUTOMATION_LAUNCH_RESULT_INVALID;
304 } else {
305 // Launch timeout may happen if the new instance tries to communicate
306 // with an existing Chrome instance that is hung and displays msgbox
307 // asking to kill the previous one. This could be easily observed if the
308 // already running Chrome instance is running as high-integrity process
309 // (started with "Run as Administrator" or launched by another high
310 // integrity process) hence our medium-integrity process
311 // cannot SendMessage to it with request to activate itself.
312
313 // TODO(stoyan) AutomationProxy eats Hello message, hence installing
314 // message filter is pointless, we can leverage ObjectWatcher and use
315 // system thread pool to notify us when proxy->AppLaunch event is signaled.
316 LaunchTimeStats launch_stats;
317 // Wait for the automation server launch result, then stash away the
318 // version string it reported.
319 launch_result_ = proxy->WaitForAppLaunch();
320 launch_stats.Dump();
321
322 base::TimeDelta delta =
323 base::TimeTicks::Now() - automation_server_launch_start_time_;
324
325 if (launch_result_ == AUTOMATION_SUCCESS) {
326 UMA_HISTOGRAM_TIMES(
327 "ChromeFrame.AutomationServerLaunchSuccessTime", delta);
328 } else {
329 UMA_HISTOGRAM_TIMES(
330 "ChromeFrame.AutomationServerLaunchFailedTime", delta);
331 }
332
333 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchResult",
334 launch_result_,
335 AUTOMATION_SUCCESS,
336 AUTOMATION_CREATE_TAB_FAILED,
337 AUTOMATION_CREATE_TAB_FAILED + 1);
338 }
339
340 TRACE_EVENT_END_ETW("chromeframe.createproxy", this, "");
341
342 // Finally set the proxy.
343 proxy_.reset(proxy);
344 launch_delegates_.push_back(delegate);
345
346 delegate->LaunchComplete(proxy_.get(), launch_result_);
347 }
348
349 void AutomationProxyCacheEntry::RemoveDelegate(LaunchDelegate* delegate,
350 base::WaitableEvent* done,
351 bool* was_last_delegate) {
352 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
353 DCHECK(delegate);
354 DCHECK(done);
355 DCHECK(was_last_delegate);
356
357 *was_last_delegate = false;
358
359 LaunchDelegates::iterator it = std::find(launch_delegates_.begin(),
360 launch_delegates_.end(), delegate);
361 if (it == launch_delegates_.end()) {
362 NOTREACHED();
363 } else {
364 if (launch_delegates_.size() == 1) {
365 *was_last_delegate = true;
366
367 // Process pending notifications.
368 thread_->message_loop()->RunUntilIdle();
369
370 // Take down the proxy since we no longer have any clients.
371 // Make sure we only do this once all pending messages have been cleared.
372 proxy_.reset(NULL);
373 }
374 // Be careful to remove from the list after running pending
375 // tasks. Otherwise the delegate being removed might miss out
376 // on pending notifications such as LaunchComplete.
377 launch_delegates_.erase(it);
378 }
379
380 done->Signal();
381 }
382
383 void AutomationProxyCacheEntry::AddDelegate(LaunchDelegate* delegate) {
384 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
385 DCHECK(std::find(launch_delegates_.begin(),
386 launch_delegates_.end(),
387 delegate) == launch_delegates_.end())
388 << "Same delegate being added twice";
389 DCHECK(launch_result_ != AUTOMATION_LAUNCH_RESULT_INVALID);
390
391 launch_delegates_.push_back(delegate);
392 delegate->LaunchComplete(proxy_.get(), launch_result_);
393 }
394
395 void AutomationProxyCacheEntry::OnChannelError() {
396 DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
397 launch_result_ = AUTOMATION_SERVER_CRASHED;
398 LaunchDelegates::const_iterator it = launch_delegates_.begin();
399 for (; it != launch_delegates_.end(); ++it) {
400 (*it)->AutomationServerDied();
401 }
402 }
403
404 ProxyFactory::ProxyFactory() {
405 }
406
407 ProxyFactory::~ProxyFactory() {
408 for (size_t i = 0; i < proxies_.container().size(); ++i) {
409 DWORD result = proxies_[i]->WaitForThread(0);
410 if (WAIT_OBJECT_0 != result)
411 // TODO(stoyan): Don't leak proxies on exit.
412 DLOG(ERROR) << "Proxies leaked on exit.";
413 }
414 }
415
416 void ProxyFactory::GetAutomationServer(
417 LaunchDelegate* delegate, ChromeFrameLaunchParams* params,
418 void** automation_server_id) {
419 TRACE_EVENT_BEGIN_ETW("chromeframe.createproxy", this, "");
420
421 scoped_refptr<AutomationProxyCacheEntry> entry;
422 // Find already existing launcher thread for given profile
423 base::AutoLock lock(lock_);
424 for (size_t i = 0; i < proxies_.container().size(); ++i) {
425 if (proxies_[i]->IsSameProfile(params->profile_name())) {
426 entry = proxies_[i];
427 break;
428 }
429 }
430
431 if (entry == NULL) {
432 DVLOG(1) << __FUNCTION__ << " creating new proxy entry";
433 entry = new AutomationProxyCacheEntry(params, delegate);
434 proxies_.container().push_back(entry);
435 } else if (delegate) {
436 // Notify the new delegate of the launch status from the worker thread
437 // and add it to the list of delegates.
438 entry->message_loop()->PostTask(
439 FROM_HERE, base::Bind(&AutomationProxyCacheEntry::AddDelegate,
440 base::Unretained(entry.get()), delegate));
441 }
442
443 DCHECK(automation_server_id != NULL);
444 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId()));
445
446 *automation_server_id = entry;
447 }
448
449 bool ProxyFactory::ReleaseAutomationServer(void* server_id,
450 LaunchDelegate* delegate) {
451 if (!server_id) {
452 NOTREACHED();
453 return false;
454 }
455
456 AutomationProxyCacheEntry* entry =
457 reinterpret_cast<AutomationProxyCacheEntry*>(server_id);
458
459 #ifndef NDEBUG
460 lock_.Acquire();
461 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
462 proxies_.container().end(),
463 entry);
464 DCHECK(it != proxies_.container().end());
465 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId()));
466
467 lock_.Release();
468 #endif
469
470 // AddRef the entry object as we might need to take it out of the proxy
471 // stack and then uninitialize the entry.
472 entry->AddRef();
473
474 bool last_delegate = false;
475 if (delegate) {
476 base::WaitableEvent done(true, false);
477 entry->message_loop()->PostTask(
478 FROM_HERE,
479 base::Bind(&AutomationProxyCacheEntry::RemoveDelegate,
480 base::Unretained(entry), delegate, &done, &last_delegate));
481 done.Wait();
482 }
483
484 if (last_delegate) {
485 lock_.Acquire();
486 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
487 proxies_.container().end(),
488 entry);
489 if (it != proxies_.container().end()) {
490 proxies_.container().erase(it);
491 } else {
492 DLOG(ERROR) << "Proxy wasn't found. Proxy map is likely empty (size="
493 << proxies_.container().size() << ").";
494 }
495
496 lock_.Release();
497 }
498
499 entry->Release();
500
501 return true;
502 }
503
504 static base::LazyInstance<ProxyFactory>::Leaky
505 g_proxy_factory = LAZY_INSTANCE_INITIALIZER;
506
507 ChromeFrameAutomationClient::ChromeFrameAutomationClient()
508 : chrome_frame_delegate_(NULL),
509 chrome_window_(NULL),
510 tab_window_(NULL),
511 parent_window_(NULL),
512 automation_server_(NULL),
513 automation_server_id_(NULL),
514 ui_thread_id_(NULL),
515 init_state_(UNINITIALIZED),
516 use_chrome_network_(false),
517 proxy_factory_(g_proxy_factory.Pointer()),
518 handle_top_level_requests_(false),
519 tab_handle_(-1),
520 session_id_(-1),
521 url_fetcher_(NULL),
522 url_fetcher_flags_(PluginUrlRequestManager::NOT_THREADSAFE),
523 navigate_after_initialization_(false),
524 route_all_top_level_navigations_(false) {
525 }
526
527 ChromeFrameAutomationClient::~ChromeFrameAutomationClient() {
528 // Uninitialize must be called prior to the destructor
529 DCHECK(automation_server_ == NULL);
530 }
531
532 bool ChromeFrameAutomationClient::Initialize(
533 ChromeFrameDelegate* chrome_frame_delegate,
534 ChromeFrameLaunchParams* chrome_launch_params) {
535 DCHECK(!IsWindow());
536 chrome_frame_delegate_ = chrome_frame_delegate;
537
538 #ifndef NDEBUG
539 if (chrome_launch_params_ && chrome_launch_params_ != chrome_launch_params) {
540 DCHECK_EQ(chrome_launch_params_->url(), chrome_launch_params->url());
541 DCHECK_EQ(chrome_launch_params_->referrer(),
542 chrome_launch_params->referrer());
543 }
544 #endif
545
546 chrome_launch_params_ = chrome_launch_params;
547
548 ui_thread_id_ = base::PlatformThread::CurrentId();
549 #ifndef NDEBUG
550 // In debug mode give more time to work with a debugger.
551 if (IsDebuggerPresent()) {
552 // Don't use INFINITE (which is -1) or even MAXINT since we will convert
553 // from milliseconds to microseconds when stored in a base::TimeDelta,
554 // thus * 1000. An hour should be enough.
555 chrome_launch_params_->set_launch_timeout(60 * 60 * 1000);
556 } else {
557 DCHECK_LT(chrome_launch_params_->launch_timeout(),
558 MAXINT / 2000);
559 chrome_launch_params_->set_launch_timeout(
560 chrome_launch_params_->launch_timeout() * 2);
561 }
562 #endif // NDEBUG
563
564 // Create a window on the UI thread for marshaling messages back and forth
565 // from the IPC thread. This window cannot be a message only window as the
566 // external chrome tab window is created as a child of this window. This
567 // window is eventually reparented to the ActiveX plugin window.
568 if (!Create(GetDesktopWindow(), NULL, NULL,
569 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
570 WS_EX_TOOLWINDOW)) {
571 NOTREACHED();
572 return false;
573 }
574
575 // Keep object in memory, while the window is alive.
576 // Corresponding Release is in OnFinalMessage();
577 AddRef();
578
579 // Mark our state as initializing. We'll reach initialized once
580 // InitializeComplete is called successfully.
581 init_state_ = INITIALIZING;
582
583 HRESULT hr = S_OK;
584
585 if (chrome_launch_params_->url().is_valid())
586 navigate_after_initialization_ = false;
587
588 proxy_factory_->GetAutomationServer(static_cast<LaunchDelegate*>(this),
589 chrome_launch_params_, &automation_server_id_);
590
591 return true;
592 }
593
594 void ChromeFrameAutomationClient::Uninitialize() {
595 if (init_state_ == UNINITIALIZED) {
596 DLOG(WARNING) << __FUNCTION__ << ": Automation client not initialized";
597 return;
598 }
599
600 init_state_ = UNINITIALIZING;
601
602 // Called from client's FinalRelease() / destructor
603 if (url_fetcher_) {
604 url_fetcher_ = NULL;
605 }
606
607 if (tab_) {
608 tab_->RemoveObserver(this);
609 if (automation_server_)
610 automation_server_->ReleaseTabProxy(tab_->handle());
611 tab_ = NULL; // scoped_refptr::Release
612 }
613
614 // Wait for the automation proxy's worker thread to exit.
615 ReleaseAutomationServer();
616
617 // We must destroy the window, since if there are pending tasks
618 // window procedure may be invoked after DLL is unloaded.
619 // Unfortunately pending tasks are leaked.
620 if (::IsWindow(m_hWnd))
621 DestroyWindow();
622
623 // DCHECK(navigate_after_initialization_ == false);
624 handle_top_level_requests_ = false;
625 ui_thread_id_ = 0;
626 chrome_frame_delegate_ = NULL;
627 init_state_ = UNINITIALIZED;
628 }
629
630 bool ChromeFrameAutomationClient::InitiateNavigation(
631 const std::string& url,
632 const std::string& referrer,
633 NavigationConstraints* navigation_constraints) {
634 return true;
635 }
636
637 void ChromeFrameAutomationClient::BeginNavigateCompleted(
638 AutomationMsg_NavigationResponseValues result) {
639 if (result == AUTOMATION_MSG_NAVIGATION_ERROR)
640 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR,
641 chrome_launch_params_->url().spec());
642 }
643
644 void ChromeFrameAutomationClient::FindInPage(const std::wstring& search_string,
645 FindInPageDirection forward,
646 FindInPageCase match_case,
647 bool find_next) {
648 // Note that we can be called by the find dialog after the tab has gone away.
649 if (!tab_)
650 return;
651
652 // What follows is quite similar to TabProxy::FindInPage() but uses
653 // the SyncMessageReplyDispatcher to avoid concerns about blocking
654 // synchronous messages.
655 AutomationMsg_Find_Params params;
656 params.search_string = base::WideToUTF16Hack(search_string);
657 params.find_next = find_next;
658 params.match_case = (match_case == CASE_SENSITIVE);
659 params.forward = (forward == FWD);
660
661 IPC::SyncMessage* msg =
662 new AutomationMsg_Find(tab_->handle(), params, NULL, NULL);
663 automation_server_->SendAsAsync(msg, NULL, this);
664 }
665
666 // Invoked in the automation proxy's worker thread.
667 void ChromeFrameAutomationClient::LaunchComplete(
668 ChromeFrameAutomationProxy* proxy,
669 AutomationLaunchResult result) {
670 }
671
672 // Invoked in the automation proxy's worker thread.
673 void ChromeFrameAutomationClient::AutomationServerDied() {
674 // Then uninitialize.
675 PostTask(
676 FROM_HERE, base::Bind(&ChromeFrameAutomationClient::Uninitialize,
677 base::Unretained(this)));
678 }
679
680 // These are invoked in channel's background thread.
681 // Cannot call any method of the activex here since it is a STA kind of being.
682 // By default we marshal the IPC message to the main/GUI thread and from there
683 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg).
684 bool ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab,
685 const IPC::Message& msg) {
686 DCHECK(tab == tab_.get());
687
688 // Early check to avoid needless marshaling
689 if (chrome_frame_delegate_ == NULL)
690 return false;
691
692 PostTask(FROM_HERE,
693 base::Bind(&ChromeFrameAutomationClient::OnMessageReceivedUIThread,
694 base::Unretained(this), msg));
695 return true;
696 }
697
698 void ChromeFrameAutomationClient::OnChannelError(TabProxy* tab) {
699 DCHECK(tab == tab_.get());
700 // Early check to avoid needless marshaling
701 if (chrome_frame_delegate_ == NULL)
702 return;
703
704 PostTask(
705 FROM_HERE,
706 base::Bind(&ChromeFrameAutomationClient::OnChannelErrorUIThread,
707 base::Unretained(this)));
708 }
709
710 void ChromeFrameAutomationClient::OnMessageReceivedUIThread(
711 const IPC::Message& msg) {
712 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_);
713 // Forward to the delegate.
714 if (chrome_frame_delegate_)
715 chrome_frame_delegate_->OnMessageReceived(msg);
716 }
717
718 void ChromeFrameAutomationClient::OnChannelErrorUIThread() {
719 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_);
720
721 // Report a metric that something went wrong unexpectedly.
722 CrashMetricsReporter::GetInstance()->IncrementMetric(
723 CrashMetricsReporter::CHANNEL_ERROR_COUNT);
724
725 // Forward to the delegate.
726 if (chrome_frame_delegate_)
727 chrome_frame_delegate_->OnChannelError();
728 }
729
730 void ChromeFrameAutomationClient::ReportNavigationError(
731 AutomationMsg_NavigationResponseValues error_code,
732 const std::string& url) {
733 if (!chrome_frame_delegate_)
734 return;
735
736 if (ui_thread_id_ == base::PlatformThread::CurrentId()) {
737 chrome_frame_delegate_->OnLoadFailed(error_code, url);
738 } else {
739 PostTask(FROM_HERE,
740 base::Bind(&ChromeFrameAutomationClient::ReportNavigationError,
741 base::Unretained(this), error_code, url));
742 }
743 }
744
745 void ChromeFrameAutomationClient::SetParentWindow(HWND parent_window) {
746 parent_window_ = parent_window;
747 // If we're done with the initialization step, go ahead
748 if (is_initialized()) {
749 if (parent_window == NULL) {
750 // Hide and reparent the automation window. This window will get
751 // reparented to the new ActiveX/Active document window when it gets
752 // created.
753 ShowWindow(SW_HIDE);
754 SetParent(GetDesktopWindow());
755 } else {
756 if (!::IsWindow(chrome_window())) {
757 DLOG(WARNING) << "Invalid Chrome Window handle in SetParentWindow";
758 return;
759 }
760
761 if (!SetParent(parent_window)) {
762 DLOG(WARNING) << "Failed to set parent window for automation window. "
763 << "Error = "
764 << GetLastError();
765 return;
766 }
767
768 RECT parent_client_rect = {0};
769 ::GetClientRect(parent_window, &parent_client_rect);
770 int width = parent_client_rect.right - parent_client_rect.left;
771 int height = parent_client_rect.bottom - parent_client_rect.top;
772 }
773 }
774 }
775
776 void ChromeFrameAutomationClient::ReleaseAutomationServer() {
777 if (automation_server_id_) {
778 // Cache the server id and clear the automation_server_id_ before
779 // calling ReleaseAutomationServer. The reason we do this is that
780 // we must cancel pending messages before we release the automation server.
781 // Furthermore, while ReleaseAutomationServer is running, we could get
782 // a callback to LaunchComplete which could cause an external tab to be
783 // created. Ideally the callbacks should be dropped.
784 // TODO(ananta)
785 // Refactor the ChromeFrameAutomationProxy code to not depend on
786 // AutomationProxy and simplify the whole mess.
787 void* server_id = automation_server_id_;
788 automation_server_id_ = NULL;
789
790 if (automation_server_) {
791 // Make sure to clean up any pending sync messages before we go away.
792 automation_server_->CancelAsync(this);
793 }
794
795 proxy_factory_->ReleaseAutomationServer(server_id, this);
796 automation_server_ = NULL;
797
798 // automation_server_ must not have been set to non NULL.
799 // (if this regresses, start by looking at LaunchComplete()).
800 DCHECK(automation_server_ == NULL);
801 } else {
802 DCHECK(automation_server_ == NULL);
803 }
804 }
805
806 std::wstring ChromeFrameAutomationClient::GetVersion() const {
807 return GetCurrentModuleVersion();
808 }
809
810 void ChromeFrameAutomationClient::Print(HDC print_dc,
811 const RECT& print_bounds) {
812 if (!tab_window_) {
813 NOTREACHED();
814 return;
815 }
816
817 HDC window_dc = ::GetDC(tab_window_);
818
819 BitBlt(print_dc, print_bounds.left, print_bounds.top,
820 print_bounds.right - print_bounds.left,
821 print_bounds.bottom - print_bounds.top,
822 window_dc, print_bounds.left, print_bounds.top,
823 SRCCOPY);
824
825 ::ReleaseDC(tab_window_, window_dc);
826 }
827
828 void ChromeFrameAutomationClient::SetPageFontSize(
829 enum AutomationPageFontSize font_size) {
830 if (font_size < SMALLEST_FONT ||
831 font_size > LARGEST_FONT) {
832 NOTREACHED() << "Invalid font size specified : "
833 << font_size;
834 return;
835 }
836
837 automation_server_->Send(
838 new AutomationMsg_SetPageFontSize(tab_handle_, font_size));
839 }
840
841 void ChromeFrameAutomationClient::SetUrlFetcher(
842 PluginUrlRequestManager* url_fetcher) {
843 DCHECK(url_fetcher != NULL);
844 url_fetcher_ = url_fetcher;
845 url_fetcher_flags_ = url_fetcher->GetThreadSafeFlags();
846 url_fetcher_->set_delegate(this);
847 }
OLDNEW
« no previous file with comments | « chrome_frame/chrome_frame_automation.h ('k') | chrome_frame/chrome_frame_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698