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

Side by Side Diff: chrome_frame/chrome_frame_automation.cc

Issue 218019: Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 3 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) 2009 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/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_util.h"
10 #include "base/file_version_info.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/process_util.h"
14 #include "base/singleton.h"
15 #include "base/string_util.h"
16 #include "base/sys_info.h"
17 #include "chrome/app/client_util.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/test/automation/tab_proxy.h"
22 #include "chrome_frame/chrome_launcher.h"
23 #include "chrome_frame/utils.h"
24 #include "chrome_frame/sync_msg_reply_dispatcher.h"
25
26 #ifdef NDEBUG
27 int64 kAutomationServerReasonableLaunchDelay = 1000; // in milliseconds
28 #else
29 int64 kAutomationServerReasonableLaunchDelay = 1000 * 10;
30 #endif
31
32 int kDefaultSendUMADataInterval = 20000; // in milliseconds.
33
34 static const wchar_t kUmaSendIntervalValue[] = L"UmaSendInterval";
35
36 class TabProxyNotificationMessageFilter
37 : public IPC::ChannelProxy::MessageFilter {
38 public:
39 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker)
40 : tracker_(tracker) {
41 }
42
43 virtual bool OnMessageReceived(const IPC::Message& message) {
44 if (message.is_reply())
45 return false;
46
47 int tab_handle = 0;
48 if (!ChromeFrameDelegateImpl::IsTabMessage(message, &tab_handle))
49 return false;
50
51 // Get AddRef-ed pointer to corresponding TabProxy object
52 TabProxy* tab = static_cast<TabProxy*>(tracker_->GetResource(tab_handle));
53 if (tab) {
54 tab->OnMessageReceived(message);
55 tab->Release();
56 }
57 return true;
58 }
59
60
61 private:
62 AutomationHandleTracker* tracker_;
63 };
64
65 class ChromeFrameAutomationProxyImpl::CFMsgDispatcher
66 : public SyncMessageReplyDispatcher {
67 public:
68 CFMsgDispatcher() : SyncMessageReplyDispatcher() {}
69 protected:
70 virtual bool HandleMessageType(const IPC::Message& msg,
71 const MessageSent& origin) {
72 switch (origin.type) {
73 case AutomationMsg_CreateExternalTab::ID:
74 case AutomationMsg_ConnectExternalTab::ID:
75 InvokeCallback<Tuple3<HWND, HWND, int> >(msg, origin);
76 break;
77 case AutomationMsg_NavigateInExternalTab::ID:
78 InvokeCallback<Tuple1<AutomationMsg_NavigationResponseValues> >(msg,
79 origin);
80 break;
81 default:
82 NOTREACHED();
83 }
84 return true;
85 }
86 };
87
88 ChromeFrameAutomationProxyImpl::ChromeFrameAutomationProxyImpl(
89 int launch_timeout)
90 : AutomationProxy(launch_timeout) {
91 sync_ = new CFMsgDispatcher();
92 // Order of filters is not important.
93 channel_->AddFilter(new TabProxyNotificationMessageFilter(tracker_.get()));
94 channel_->AddFilter(sync_.get());
95 }
96
97 ChromeFrameAutomationProxyImpl::~ChromeFrameAutomationProxyImpl() {
98 }
99
100 void ChromeFrameAutomationProxyImpl::SendAsAsync(IPC::SyncMessage* msg,
101 void* callback, void* key) {
102 sync_->Push(msg, callback, key);
103 channel_->ChannelProxy::Send(msg);
104 }
105
106 void ChromeFrameAutomationProxyImpl::CancelAsync(void* key) {
107 sync_->Cancel(key);
108 }
109
110 scoped_refptr<TabProxy> ChromeFrameAutomationProxyImpl::CreateTabProxy(
111 int handle) {
112 DCHECK(tracker_->GetResource(handle) == NULL);
113 return new TabProxy(this, tracker_.get(), handle);
114 }
115
116 struct LaunchTimeStats {
117 #ifndef NDEBUG
118 LaunchTimeStats() {
119 launch_time_begin_ = base::Time::Now();
120 }
121
122 void Dump() {
123 base::TimeDelta launch_time = base::Time::Now() - launch_time_begin_;
124 HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchTime", launch_time);
125 const int64 launch_milliseconds = launch_time.InMilliseconds();
126 if (launch_milliseconds > kAutomationServerReasonableLaunchDelay) {
127 LOG(WARNING) << "Automation server launch took longer than expected: " <<
128 launch_milliseconds << " ms.";
129 }
130 }
131
132 base::Time launch_time_begin_;
133 #else
134 void Dump() {}
135 #endif
136 };
137
138
139 ProxyFactory::ProxyCacheEntry::ProxyCacheEntry(const std::wstring& profile)
140 : proxy(NULL), profile_name(profile), ref_count(1),
141 launch_result(AutomationLaunchResult(-1)) {
142 thread.reset(new base::Thread(WideToASCII(profile_name).c_str()));
143 thread->Start();
144 }
145
146 template <> struct RunnableMethodTraits<ProxyFactory> {
147 static void RetainCallee(ProxyFactory* obj) {}
148 static void ReleaseCallee(ProxyFactory* obj) {}
149 };
150
151 ProxyFactory::ProxyFactory()
152 : uma_send_interval_(0) {
153 uma_send_interval_ = GetConfigInt(kDefaultSendUMADataInterval,
154 kUmaSendIntervalValue);
155 }
156
157 ProxyFactory::~ProxyFactory() {
158 DCHECK_EQ(proxies_.container().size(), 0);
159 }
160
161 void* ProxyFactory::GetAutomationServer(int launch_timeout,
162 const std::wstring& profile_name,
163 const std::wstring& extra_argument,
164 bool perform_version_check,
165 LaunchDelegate* delegate) {
166 ProxyCacheEntry* entry = NULL;
167 // Find already existing launcher thread for given profile
168 AutoLock lock(lock_);
169 for (size_t i = 0; i < proxies_.container().size(); ++i) {
170 if (!lstrcmpiW(proxies_[i]->profile_name.c_str(), profile_name.c_str())) {
171 entry = proxies_[i];
172 DCHECK(entry->thread.get() != NULL);
173 break;
174 }
175 }
176
177 if (entry == NULL) {
178 entry = new ProxyCacheEntry(profile_name);
179 proxies_.container().push_back(entry);
180 } else {
181 entry->ref_count++;
182 }
183
184
185 // Note we always queue request to the launch thread, even if we already
186 // have established proxy object. A simple lock around entry->proxy = proxy
187 // would allow calling LaunchDelegate directly from here if
188 // entry->proxy != NULL. Drawback is that callback may be invoked either in
189 // main thread or in background thread, which may confuse the client.
190 entry->thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
191 &ProxyFactory::CreateProxy, entry,
192 launch_timeout, extra_argument,
193 perform_version_check, delegate));
194
195 entry->thread->message_loop()->PostDelayedTask(FROM_HERE,
196 NewRunnableMethod(this, &ProxyFactory::SendUMAData, entry),
197 uma_send_interval_);
198
199 return entry;
200 }
201
202 void ProxyFactory::CreateProxy(ProxyFactory::ProxyCacheEntry* entry,
203 int launch_timeout,
204 const std::wstring& extra_chrome_arguments,
205 bool perform_version_check,
206 LaunchDelegate* delegate) {
207 DCHECK(entry->thread->thread_id() == PlatformThread::CurrentId());
208 if (entry->proxy) {
209 delegate->LaunchComplete(entry->proxy, entry->launch_result);
210 return;
211 }
212
213 // We *must* create automationproxy in a thread that has message loop,
214 // since SyncChannel::Context construction registers event to be watched
215 // through ObjectWatcher which subscribes for the current thread message loop
216 // destruction notification.
217
218 // At same time we must destroy/stop the thread from another thread.
219 ChromeFrameAutomationProxyImpl* proxy =
220 new ChromeFrameAutomationProxyImpl(launch_timeout);
221
222 // Launch browser
223 scoped_ptr<CommandLine> command_line(
224 chrome_launcher::CreateLaunchCommandLine());
225 command_line->AppendSwitchWithValue(switches::kAutomationClientChannelID,
226 ASCIIToWide(proxy->channel_id()));
227
228 // The metrics bug out because they attempt to use URLFetcher with a
229 // null URLRequestContext::default_request_context_. Turn them off for now.
230 // TODO(robertshield): Figure out why this is. It appears to have something
231 // to do with an improperly set up profile...
232 command_line->AppendSwitch(switches::kDisableMetrics);
233
234 // Chrome Frame never wants Chrome to start up with a First Run UI.
235 command_line->AppendSwitch(switches::kNoFirstRun);
236
237 // Place the profile directory in
238 // "<chrome_exe_path>\..\User Data\<profile-name>"
239 if (!entry->profile_name.empty()) {
240 std::wstring profile_path;
241 if (GetUserProfileBaseDirectory(&profile_path)) {
242 file_util::AppendToPath(&profile_path, entry->profile_name);
243 command_line->AppendSwitchWithValue(switches::kUserDataDir,
244 profile_path);
245 } else {
246 // Can't get the profile dir :-( We need one to work, so fail.
247 // We have no code for launch failure.
248 entry->launch_result = AutomationLaunchResult(-1);
249 }
250 }
251
252 std::wstring command_line_string(command_line->command_line_string());
253 // If there are any extra arguments, append them to the command line.
254 if (!extra_chrome_arguments.empty()) {
255 command_line_string += L' ' + extra_chrome_arguments;
256 }
257
258 automation_server_launch_start_time_ = base::TimeTicks::Now();
259
260 if (!base::LaunchApp(command_line_string, false, false, NULL)) {
261 // We have no code for launch failure.
262 entry->launch_result = AutomationLaunchResult(-1);
263 } else {
264 // Launch timeout may happen if the new instance tries to communicate
265 // with an existing Chrome instance that is hung and displays msgbox
266 // asking to kill the previous one. This could be easily observed if the
267 // already running Chrome instance is running as high-integrity process
268 // (started with "Run as Administrator" or launched by another high
269 // integrity process) hence our medium-integrity process
270 // cannot SendMessage to it with request to activate itself.
271
272 // TODO(stoyan) AutomationProxy eats Hello message, hence installing
273 // message filter is pointless, we can leverage ObjectWatcher and use
274 // system thread pool to notify us when proxy->AppLaunch event is signaled.
275 LaunchTimeStats launch_stats;
276 // Wait for the automation server launch result, then stash away the
277 // version string it reported.
278 entry->launch_result = proxy->WaitForAppLaunch();
279 launch_stats.Dump();
280
281 base::TimeDelta delta =
282 base::TimeTicks::Now() - automation_server_launch_start_time_;
283
284 if (entry->launch_result == AUTOMATION_SUCCESS) {
285 UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchSuccessTime",
286 delta);
287 } else {
288 UMA_HISTOGRAM_TIMES("ChromeFrame.AutomationServerLaunchFailedTime",
289 delta);
290 }
291
292 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.LaunchResult",
293 entry->launch_result,
294 AUTOMATION_SUCCESS,
295 AUTOMATION_CREATE_TAB_FAILED,
296 AUTOMATION_CREATE_TAB_FAILED + 1);
297 }
298
299 // Finally set the proxy.
300 entry->proxy = proxy;
301 delegate->LaunchComplete(proxy, entry->launch_result);
302 }
303
304 bool ProxyFactory::ReleaseAutomationServer(void* server_id) {
305 DLOG(INFO) << __FUNCTION__;
306
307 if (!server_id) {
308 NOTREACHED();
309 return false;
310 }
311
312 ProxyCacheEntry* entry = reinterpret_cast<ProxyCacheEntry*>(server_id);
313
314 lock_.Acquire();
315 Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
316 proxies_.container().end(),
317 entry);
318 DCHECK(it != proxies_.container().end());
319 DCHECK(entry->thread->thread_id() != PlatformThread::CurrentId());
320 if (--entry->ref_count == 0) {
321 proxies_.container().erase(it);
322 }
323
324 lock_.Release();
325
326 // Destroy it.
327 if (entry->ref_count == 0) {
328 entry->thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
329 &ProxyFactory::DestroyProxy, entry));
330 // Wait until thread exits
331 entry->thread.reset();
332 DCHECK(entry->proxy == NULL);
333 delete entry;
334 }
335
336 return true;
337 }
338
339 void ProxyFactory::DestroyProxy(ProxyCacheEntry* entry) {
340 DCHECK(entry->thread->thread_id() == PlatformThread::CurrentId());
341 // Send pending UMA data if any.
342 SendUMAData(entry);
343 delete entry->proxy;
344 entry->proxy = NULL;
345 }
346
347 Singleton<ProxyFactory> g_proxy_factory;
348
349 void ProxyFactory::SendUMAData(ProxyCacheEntry* proxy_entry) {
350 if (!proxy_entry) {
351 NOTREACHED() << __FUNCTION__ << " Invalid proxy entry";
352 return;
353 }
354
355 DCHECK(proxy_entry->thread->thread_id() == PlatformThread::CurrentId());
356
357 if (proxy_entry->proxy) {
358 ChromeFrameHistogramSnapshots::HistogramPickledList histograms =
359 chrome_frame_histograms_.GatherAllHistograms();
360
361 if (!histograms.empty()) {
362 proxy_entry->proxy->Send(
363 new AutomationMsg_RecordHistograms(0, histograms));
364 }
365 } else {
366 DLOG(INFO) << __FUNCTION__ << " No proxy available to service the request";
367 return;
368 }
369
370 MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(
371 this, &ProxyFactory::SendUMAData, proxy_entry), uma_send_interval_);
372 }
373
374 template <> struct RunnableMethodTraits<ChromeFrameAutomationClient> {
375 static void RetainCallee(ChromeFrameAutomationClient* obj) {}
376 static void ReleaseCallee(ChromeFrameAutomationClient* obj) {}
377 };
378
379 ChromeFrameAutomationClient::ChromeFrameAutomationClient()
380 : chrome_frame_delegate_(NULL),
381 chrome_window_(NULL),
382 tab_window_(NULL),
383 parent_window_(NULL),
384 automation_server_(NULL),
385 automation_server_id_(NULL),
386 ui_thread_id_(NULL),
387 incognito_(false),
388 init_state_(UNINITIALIZED),
389 use_chrome_network_(false),
390 proxy_factory_(g_proxy_factory.get()),
391 handle_top_level_requests_(false),
392 tab_handle_(-1),
393 external_tab_cookie_(NULL) {
394 }
395
396 ChromeFrameAutomationClient::~ChromeFrameAutomationClient() {
397 // Uninitialize must be called prior to the destructor
398 DCHECK(automation_server_ == NULL);
399 }
400
401 bool ChromeFrameAutomationClient::Initialize(
402 ChromeFrameDelegate* chrome_frame_delegate,
403 int automation_server_launch_timeout,
404 bool perform_version_check,
405 const std::wstring& profile_name,
406 const std::wstring& extra_chrome_arguments,
407 bool incognito) {
408 DCHECK(!IsWindow());
409 chrome_frame_delegate_ = chrome_frame_delegate;
410 incognito_ = incognito;
411 ui_thread_id_ = PlatformThread::CurrentId();
412
413 #ifndef NDEBUG
414 // In debug mode give more time to work with a debugger.
415 if (automation_server_launch_timeout != INFINITE)
416 automation_server_launch_timeout *= 2;
417 #endif // NDEBUG
418
419 // Create a window on the UI thread for marshaling messages back and forth
420 // from the IPC thread. This window cannot be a message only window as the
421 // external chrome tab window is created as a child of this window. This
422 // window is eventually reparented to the ActiveX/NPAPI plugin window.
423 if (!Create(GetDesktopWindow(), NULL, NULL,
424 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
425 WS_EX_TOOLWINDOW)) {
426 NOTREACHED();
427 return false;
428 }
429
430 // Mark our state as initializing. We'll reach initialized once
431 // InitializeComplete is called successfully.
432 init_state_ = INITIALIZING;
433
434 automation_server_id_ = proxy_factory_->GetAutomationServer(
435 automation_server_launch_timeout,
436 profile_name, extra_chrome_arguments, perform_version_check,
437 static_cast<ProxyFactory::LaunchDelegate*>(this));
438
439 return true;
440 }
441
442 void ChromeFrameAutomationClient::Uninitialize() {
443 DLOG(INFO) << __FUNCTION__;
444
445 init_state_ = UNINITIALIZING;
446
447 // Called from client's FinalRelease() / destructor
448 // ChromeFrameAutomationClient may wait for the initialization (launch)
449 // to complete while Uninitialize is called.
450 // We either have to:
451 // 1) Make ReleaseAutomationServer blocking call (wait until thread exits)
452 // 2) Behave like a COM object i.e. increase module lock count.
453 // Otherwise the DLL may get unloaded while we have running threads.
454 // Unfortunately in NPAPI case we cannot increase module lock count, hence
455 // we stick with blocking/waiting
456 if (tab_.get()) {
457 tab_->RemoveObserver(this);
458 tab_ = NULL; // scoped_refptr::Release
459 }
460
461 // Clean up any outstanding requests
462 CleanupRequests();
463
464 // Wait for the background thread to exit.
465 ReleaseAutomationServer();
466
467 // We must destroy the window, since if there are pending tasks
468 // window procedure may be invoked after DLL is unloaded.
469 // Unfortunately pending tasks are leaked.
470 if (m_hWnd)
471 DestroyWindow();
472
473 chrome_frame_delegate_ = NULL;
474 init_state_ = UNINITIALIZED;
475 }
476
477 bool ChromeFrameAutomationClient::InitiateNavigation(const std::string& url) {
478 if (url.empty())
479 return false;
480
481 url_ = GURL(url);
482
483 // Catch invalid URLs early.
484 if (!url_.is_valid()) {
485 DLOG(ERROR) << "Invalid URL passed to InitiateNavigation: " << url;
486 return false;
487 }
488
489 if (is_initialized()) {
490 BeginNavigate(GURL(url));
491 }
492
493 return true;
494 }
495
496 bool ChromeFrameAutomationClient::NavigateToIndex(int index) {
497 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
498 if (!automation_server_ || !tab_.get() || !tab_->is_valid()) {
499 return false;
500 }
501
502 DCHECK(::IsWindow(chrome_window_));
503
504 IPC::SyncMessage* msg = new AutomationMsg_NavigateExternalTabAtIndex(
505 0, tab_->handle(), index, NULL);
506 automation_server_->SendAsAsync(msg, NewCallback(this,
507 &ChromeFrameAutomationClient::BeginNavigateCompleted), this);
508 return true;
509 }
510
511 bool ChromeFrameAutomationClient::ForwardMessageFromExternalHost(
512 const std::string& message, const std::string& origin,
513 const std::string& target) {
514 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
515 if (!is_initialized())
516 return false;
517
518 tab_->HandleMessageFromExternalHost(message, origin, target);
519 return true;
520 }
521
522 bool ChromeFrameAutomationClient::SetProxySettings(
523 const std::string& json_encoded_proxy_settings) {
524 if (!is_initialized())
525 return false;
526 automation_server_->SendProxyConfig(json_encoded_proxy_settings);
527 return true;
528 }
529
530 void ChromeFrameAutomationClient::BeginNavigate(const GURL& url) {
531 // Could be NULL if we failed to launch Chrome in LaunchAutomationServer()
532 if (!automation_server_ || !tab_.get()) {
533 DLOG(WARNING) << "BeginNavigate - can't navigate.";
534 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR, url_.spec());
535 return;
536 }
537
538 DCHECK(::IsWindow(chrome_window_));
539
540 if (!tab_->is_valid()) {
541 DLOG(WARNING) << "BeginNavigate - tab isn't valid.";
542 return;
543 }
544
545 IPC::SyncMessage* msg =
546 new AutomationMsg_NavigateInExternalTab(0, tab_->handle(), url, NULL);
547 automation_server_->SendAsAsync(msg, NewCallback(this,
548 &ChromeFrameAutomationClient::BeginNavigateCompleted), this);
549
550 RECT client_rect = {0};
551 chrome_frame_delegate_->GetBounds(&client_rect);
552 Resize(client_rect.right - client_rect.left,
553 client_rect.bottom - client_rect.top,
554 SWP_NOACTIVATE | SWP_NOZORDER);
555 }
556
557 void ChromeFrameAutomationClient::BeginNavigateCompleted(
558 AutomationMsg_NavigationResponseValues result) {
559 if (result == AUTOMATION_MSG_NAVIGATION_ERROR)
560 ReportNavigationError(AUTOMATION_MSG_NAVIGATION_ERROR, url_.spec());
561 }
562
563 void ChromeFrameAutomationClient::FindInPage(const std::wstring& search_string,
564 FindInPageDirection forward,
565 FindInPageCase match_case,
566 bool find_next) {
567 DCHECK(tab_.get());
568
569 // What follows is quite similar to TabProxy::FindInPage() but uses
570 // the SyncMessageReplyDispatcher to avoid concerns about blocking
571 // synchronous messages.
572 AutomationMsg_Find_Params params;
573 params.unused = 0;
574 params.search_string = WideToUTF16Hack(search_string);
575 params.find_next = find_next;
576 params.match_case = (match_case == CASE_SENSITIVE);
577 params.forward = (forward == FWD);
578
579 IPC::SyncMessage* msg =
580 new AutomationMsg_Find(0, tab_->handle(), params, NULL, NULL);
581 automation_server_->SendAsAsync(msg, NULL, this);
582 }
583
584 void ChromeFrameAutomationClient::CreateExternalTab() {
585 AutomationLaunchResult launch_result = AUTOMATION_SUCCESS;
586 DCHECK(IsWindow());
587 DCHECK(automation_server_ != NULL);
588
589 const IPC::ExternalTabSettings settings = {
590 m_hWnd,
591 gfx::Rect(),
592 WS_CHILD,
593 incognito_,
594 !use_chrome_network_,
595 handle_top_level_requests_,
596 GURL(url_)
597 };
598
599 UMA_HISTOGRAM_CUSTOM_COUNTS(
600 "ChromeFrame.HostNetworking", !use_chrome_network_, 0, 1, 2);
601
602 UMA_HISTOGRAM_CUSTOM_COUNTS(
603 "ChromeFrame.HandleTopLevelRequests", handle_top_level_requests_, 0, 1,
604 2);
605
606 IPC::SyncMessage* message =
607 new AutomationMsg_CreateExternalTab(0, settings, NULL, NULL, NULL);
608 automation_server_->SendAsAsync(message, NewCallback(this,
609 &ChromeFrameAutomationClient::CreateExternalTabComplete), this);
610 }
611
612 void ChromeFrameAutomationClient::CreateExternalTabComplete(HWND chrome_window,
613 HWND tab_window, int tab_handle) {
614 if (!automation_server_) {
615 // If we receive this notification while shutting down, do nothing.
616 DLOG(ERROR) << "CreateExternalTabComplete called when automation server "
617 << "was null!";
618 return;
619 }
620
621 AutomationLaunchResult launch_result = AUTOMATION_SUCCESS;
622 if (tab_handle == 0 || !::IsWindow(chrome_window) ||
623 !::IsWindow(chrome_window)) {
624 launch_result = AUTOMATION_CREATE_TAB_FAILED;
625 } else {
626 chrome_window_ = chrome_window;
627 tab_window_ = tab_window;
628 tab_ = automation_server_->CreateTabProxy(tab_handle);
629 tab_->AddObserver(this);
630 tab_handle_ = tab_handle;
631 }
632
633 PostTask(FROM_HERE, NewRunnableMethod(this,
634 &ChromeFrameAutomationClient::InitializeComplete, launch_result));
635 }
636
637 void ChromeFrameAutomationClient::SetEnableExtensionAutomation(
638 bool enable_automation) {
639 if (!is_initialized())
640 return;
641
642 automation_server_->SetEnableExtensionAutomation(enable_automation);
643 }
644
645 // Invoked in launch background thread.
646 void ChromeFrameAutomationClient::LaunchComplete(
647 ChromeFrameAutomationProxy* proxy,
648 AutomationLaunchResult result) {
649 // If we're shutting down we don't keep a pointer to the automation server.
650 if (init_state_ != UNINITIALIZING) {
651 DCHECK(init_state_ == INITIALIZING);
652 automation_server_ = proxy;
653 } else {
654 DLOG(INFO) << "Not storing automation server pointer due to shutting down";
655 }
656
657 if (result == AUTOMATION_SUCCESS) {
658 // NOTE: A potential problem here is that Uninitialize() may just have
659 // been called so we need to be careful and check the automation_server_
660 // pointer.
661 if (automation_server_ != NULL) {
662 // If we have a valid tab_handle here it means that we are attaching to
663 // an existing ExternalTabContainer instance, in which case we don't
664 // want to create an external tab instance in Chrome.
665 if (external_tab_cookie_ == NULL) {
666 // Continue with Initialization - Create external tab
667 CreateExternalTab();
668 } else {
669 // Send a notification to Chrome that we are ready to connect to the
670 // ExternalTab.
671 IPC::SyncMessage* message =
672 new AutomationMsg_ConnectExternalTab(0, external_tab_cookie_, NULL,
673 NULL, NULL);
674 automation_server_->SendAsAsync(message, NewCallback(this,
675 &ChromeFrameAutomationClient::CreateExternalTabComplete), this);
676 }
677 }
678 } else {
679 // Launch failed. Note, we cannot delete proxy here.
680 PostTask(FROM_HERE, NewRunnableMethod(this,
681 &ChromeFrameAutomationClient::InitializeComplete, result));
682 }
683 }
684
685 void ChromeFrameAutomationClient::InitializeComplete(
686 AutomationLaunchResult result) {
687 DCHECK(PlatformThread::CurrentId() == ui_thread_id_);
688 std::string version = automation_server_->server_version();
689
690 if (result != AUTOMATION_SUCCESS) {
691 DLOG(WARNING) << "InitializeComplete: failure " << result;
692 ReleaseAutomationServer();
693 } else {
694 init_state_ = INITIALIZED;
695
696 // If the host already have a window, ask Chrome to re-parent.
697 if (parent_window_)
698 SetParentWindow(parent_window_);
699 }
700
701 if (chrome_frame_delegate_) {
702 if (result == AUTOMATION_SUCCESS) {
703 chrome_frame_delegate_->OnAutomationServerReady();
704 } else {
705 chrome_frame_delegate_->OnAutomationServerLaunchFailed(result, version);
706 }
707 }
708 }
709
710 // This is invoked in channel's background thread.
711 // Cannot call any method of the activex/npapi here since they are STA
712 // kind of beings.
713 // By default we marshal the IPC message to the main/GUI thread and from there
714 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg).
715 void ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab,
716 const IPC::Message& msg) {
717 DCHECK(tab == tab_.get());
718
719 // Early check to avoid needless marshaling
720 if (chrome_frame_delegate_ == NULL)
721 return;
722
723 CallDelegate(FROM_HERE, NewRunnableMethod(chrome_frame_delegate_,
724 &ChromeFrameDelegate::OnMessageReceived, msg));
725 }
726
727 void ChromeFrameAutomationClient::ReportNavigationError(
728 AutomationMsg_NavigationResponseValues error_code,
729 const std::string& url) {
730 CallDelegate(FROM_HERE, NewRunnableMethod(chrome_frame_delegate_,
731 &ChromeFrameDelegate::OnLoadFailed,
732 error_code,
733 url));
734 }
735
736 void ChromeFrameAutomationClient::Resize(int width, int height,
737 int flags) {
738 if (tab_.get() && ::IsWindow(chrome_window())) {
739 SetWindowPos(HWND_TOP, 0, 0, width, height, flags);
740 tab_->Reposition(chrome_window(), HWND_TOP, 0, 0, width, height,
741 flags, m_hWnd);
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 NOTREACHED();
763 DLOG(WARNING) << "Failed to set parent window for automation window. "
764 << "Error = "
765 << GetLastError();
766 return;
767 }
768
769 RECT parent_client_rect = {0};
770 ::GetClientRect(parent_window, &parent_client_rect);
771 int width = parent_client_rect.right - parent_client_rect.left;
772 int height = parent_client_rect.bottom - parent_client_rect.top;
773
774 Resize(width, height, SWP_SHOWWINDOW | SWP_NOZORDER);
775 }
776 }
777 }
778
779 void ChromeFrameAutomationClient::ReleaseAutomationServer() {
780 DLOG(INFO) << __FUNCTION__;
781 if (automation_server_id_) {
782 // Cache the server id and clear the automation_server_id_ before
783 // calling ReleaseAutomationServer. The reason we do this is that
784 // we must cancel pending messages before we release the automation server.
785 // Furthermore, while ReleaseAutomationServer is running, we could get
786 // a callback to LaunchComplete which is where we normally get our pointer
787 // to the automation server and there we check the server id for NULLness
788 // and do nothing if it is NULL.
789 void* server_id = automation_server_id_;
790 automation_server_id_ = NULL;
791
792 if (automation_server_) {
793 // Make sure to clean up any pending sync messages before we go away.
794 automation_server_->CancelAsync(this);
795 automation_server_ = NULL;
796 }
797
798 proxy_factory_->ReleaseAutomationServer(server_id);
799
800 // automation_server_ must not have been set to non NULL.
801 // (if this regresses, start by looking at LaunchComplete()).
802 DCHECK(automation_server_ == NULL);
803 } else {
804 DCHECK(automation_server_ == NULL);
805 }
806 }
807
808 void ChromeFrameAutomationClient::SendContextMenuCommandToChromeFrame(
809 int selected_command) {
810 DCHECK(tab_ != NULL);
811 tab_->SendContextMenuCommand(selected_command);
812 }
813
814 std::wstring ChromeFrameAutomationClient::GetVersion() const {
815 static FileVersionInfo* version_info =
816 FileVersionInfo::CreateFileVersionInfoForCurrentModule();
817
818 std::wstring version;
819 if (version_info)
820 version = version_info->product_version();
821
822 return version;
823 }
824
825 void ChromeFrameAutomationClient::CallDelegate(
826 const tracked_objects::Location& from_here, Task* delegate_task ) {
827 delegate_task->SetBirthPlace(from_here);
828 PostTask(FROM_HERE, NewRunnableMethod(this,
829 &ChromeFrameAutomationClient::CallDelegateImpl,
830 delegate_task));
831 }
832
833 void ChromeFrameAutomationClient::CallDelegateImpl(Task* delegate_task) {
834 if (chrome_frame_delegate_) {
835 // task's object should be == chrome_frame_delegate_
836 delegate_task->Run();
837 }
838
839 delete delegate_task;
840 }
841
842 void ChromeFrameAutomationClient::Print(HDC print_dc,
843 const RECT& print_bounds) {
844 if (!tab_window_) {
845 NOTREACHED();
846 return;
847 }
848
849 HDC window_dc = ::GetDC(tab_window_);
850
851 BitBlt(print_dc, print_bounds.left, print_bounds.top,
852 print_bounds.right - print_bounds.left,
853 print_bounds.bottom - print_bounds.top,
854 window_dc, print_bounds.left, print_bounds.top,
855 SRCCOPY);
856
857 ::ReleaseDC(tab_window_, window_dc);
858 }
859
860 void ChromeFrameAutomationClient::PrintTab() {
861 tab_->PrintAsync();
862 }
863
864 // IPC:Message::Sender implementation
865 bool ChromeFrameAutomationClient::Send(IPC::Message* msg) {
866 return automation_server_->Send(msg);
867 }
868
869 bool ChromeFrameAutomationClient::AddRequest(PluginUrlRequest* request) {
870 if (!request) {
871 NOTREACHED();
872 return false;
873 }
874
875 DCHECK(request_map_.end() == request_map_.find(request->id()));
876 request_map_[request->id()] = request;
877 return true;
878 }
879
880 bool ChromeFrameAutomationClient::ReadRequest(
881 int request_id, int bytes_to_read) {
882 bool result = false;
883 PluginUrlRequest* request = LookupRequest(request_id);
884 if (request)
885 result = request->Read(bytes_to_read);
886
887 return result;
888 }
889
890 void ChromeFrameAutomationClient::RemoveRequest(PluginUrlRequest* request) {
891 DCHECK(request_map_.end() != request_map_.find(request->id()));
892 request_map_.erase(request->id());
893 }
894
895 void ChromeFrameAutomationClient::RemoveRequest(
896 int request_id, int reason, bool abort) {
897 PluginUrlRequest* request = LookupRequest(request_id);
898 if (request) {
899 if (abort) {
900 request->Stop();
901 DCHECK(request_map_.end() == request_map_.find(request_id));
902 } else {
903 request_map_.erase(request_id);
904 }
905 }
906 }
907
908 PluginUrlRequest* ChromeFrameAutomationClient::LookupRequest(
909 int request_id) const {
910 PluginUrlRequest* request = NULL;
911 RequestMap::const_iterator it = request_map_.find(request_id);
912 if (request_map_.end() != it)
913 request = (*it).second;
914 return request;
915 }
916
917 bool ChromeFrameAutomationClient::IsValidRequest(
918 PluginUrlRequest* request) const {
919 bool is_valid = false;
920 // if request is invalid then request->id() won't work
921 // hence perform reverse map lookup for validity of the
922 // request pointer.
923 if (request) {
924 for (RequestMap::const_iterator it = request_map_.begin();
925 it != request_map_.end(); it++) {
926 if (request == (*it).second) {
927 is_valid = true;
928 break;
929 }
930 }
931 }
932
933 return is_valid;
934 }
935
936 void ChromeFrameAutomationClient::CleanupRequests() {
937 while (request_map_.size()) {
938 PluginUrlRequest* request = request_map_.begin()->second;
939 if (request) {
940 int request_id = request->id();
941 request->Stop();
942 DCHECK(request_map_.end() == request_map_.find(request_id));
943 }
944 }
945
946 DCHECK(request_map_.empty());
947 request_map_.clear();
948 }
949
950 bool ChromeFrameAutomationClient::Reinitialize(
951 ChromeFrameDelegate* delegate) {
952 if (!tab_.get() || !::IsWindow(chrome_window_)) {
953 NOTREACHED();
954 DLOG(WARNING) << "ChromeFrameAutomationClient instance reused "
955 << "with invalid tab";
956 return false;
957 }
958
959 if (!delegate) {
960 NOTREACHED();
961 return false;
962 }
963
964 chrome_frame_delegate_ = delegate;
965 SetParentWindow(NULL);
966 return true;
967 }
968
969 void ChromeFrameAutomationClient::AttachExternalTab(
970 intptr_t external_tab_cookie) {
971 DCHECK(tab_.get() == NULL);
972 DCHECK(tab_handle_ == -1);
973
974 external_tab_cookie_ = external_tab_cookie;
975 }
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