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 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 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 145 ChromeFrameAutomationProxyImpl::~ChromeFrameAutomationProxyImpl() { 146 TRACE_EVENT_END_ETW("chromeframe.automationproxy", this, ""); 147 } 148 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 156 void ChromeFrameAutomationProxyImpl::CancelAsync(void* key) { 157 sync_->Cancel(key); 158 } 159 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 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 178 void ChromeFrameAutomationProxyImpl::ReleaseTabProxy(AutomationHandle handle) { 179 message_filter_->RemoveTabProxy(handle); 180 } 181 182 struct LaunchTimeStats { 183 #ifndef NDEBUG 184 LaunchTimeStats() { 185 launch_time_begin_ = base::Time::Now(); 186 } 187 188 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 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 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 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 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 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 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 419 ProxyFactory::ProxyFactory() { 420 } 421 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 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 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 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 543 ChromeFrameAutomationClient::~ChromeFrameAutomationClient() { 544 // Uninitialize must be called prior to the destructor 545 DCHECK(automation_server_ == NULL); 546 } 547 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 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 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 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 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 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 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 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 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 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 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 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. 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. 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 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 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 case AutomationMsg_GetCookiesFromHost::ID: 990 if (ui_thread || (url_fetcher_flags_ & 991 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE)) { 992 AutomationMsg_GetCookiesFromHost::Dispatch(&msg, url_fetcher_, this, 993 &PluginUrlRequestManager::GetCookiesFromHost); 994 return true; 995 } 996 break; 997 998 case AutomationMsg_SetCookieAsync::ID: 999 if (ui_thread || (url_fetcher_flags_ & 1000 PluginUrlRequestManager::COOKIE_REQUEST_THREADSAFE)) { 1001 AutomationMsg_SetCookieAsync::Dispatch(&msg, url_fetcher_, this, 1002 &PluginUrlRequestManager::SetCookiesInHost); 1003 return true; 1004 } 1005 break; 1006 } 1007 1008 PostTask( 1009 FROM_HERE, 1010 base::Bind( 1011 base::IgnoreResult( 1012 &ChromeFrameAutomationClient::ProcessUrlRequestMessage), 1013 base::Unretained(this), tab, msg, true)); 1014 return true; 1015 } 1016 1017 // These are invoked in channel's background thread. 1018 // Cannot call any method of the activex here since it is a STA kind of being. 1019 // By default we marshal the IPC message to the main/GUI thread and from there 1020 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg). 1021 bool ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab, 1022 const IPC::Message& msg) { 1023 DCHECK(tab == tab_.get()); 1024 // Quickly process network related messages. 1025 if (url_fetcher_ && ProcessUrlRequestMessage(tab, msg, false)) 1026 return true; 1027 1028 // Early check to avoid needless marshaling 1029 if (chrome_frame_delegate_ == NULL) 1030 return false; 1031 1032 PostTask(FROM_HERE, 1033 base::Bind(&ChromeFrameAutomationClient::OnMessageReceivedUIThread, 1034 base::Unretained(this), msg)); 1035 return true; 1036 } 1037 1038 void ChromeFrameAutomationClient::OnChannelError(TabProxy* tab) { 1039 DCHECK(tab == tab_.get()); 1040 // Early check to avoid needless marshaling 1041 if (chrome_frame_delegate_ == NULL) 1042 return; 1043 1044 PostTask( 1045 FROM_HERE, 1046 base::Bind(&ChromeFrameAutomationClient::OnChannelErrorUIThread, 1047 base::Unretained(this))); 1048 } 1049 1050 void ChromeFrameAutomationClient::OnMessageReceivedUIThread( 1051 const IPC::Message& msg) { 1052 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_); 1053 // Forward to the delegate. 1054 if (chrome_frame_delegate_) 1055 chrome_frame_delegate_->OnMessageReceived(msg); 1056 } 1057 1058 void ChromeFrameAutomationClient::OnChannelErrorUIThread() { 1059 DCHECK_EQ(base::PlatformThread::CurrentId(), ui_thread_id_); 1060 1061 // Report a metric that something went wrong unexpectedly. 1062 CrashMetricsReporter::GetInstance()->IncrementMetric( 1063 CrashMetricsReporter::CHANNEL_ERROR_COUNT); 1064 1065 // Forward to the delegate. 1066 if (chrome_frame_delegate_) 1067 chrome_frame_delegate_->OnChannelError(); 1068 } 1069 1070 void ChromeFrameAutomationClient::ReportNavigationError( 1071 AutomationMsg_NavigationResponseValues error_code, 1072 const std::string& url) { 1073 if (!chrome_frame_delegate_) 1074 return; 1075 1076 if (ui_thread_id_ == base::PlatformThread::CurrentId()) { 1077 chrome_frame_delegate_->OnLoadFailed(error_code, url); 1078 } else { 1079 PostTask(FROM_HERE, 1080 base::Bind(&ChromeFrameAutomationClient::ReportNavigationError, 1081 base::Unretained(this), error_code, url)); 1082 } 1083 } 1084 1085 void ChromeFrameAutomationClient::Resize(int width, int height, 1086 int flags) { 1087 if (tab_.get() && ::IsWindow(chrome_window())) { 1088 SetWindowPos(HWND_TOP, 0, 0, width, height, flags); 1089 tab_->Reposition(chrome_window(), HWND_TOP, 0, 0, width, height, 1090 flags, m_hWnd); 1091 } 1092 } 1093 1094 void ChromeFrameAutomationClient::SetParentWindow(HWND parent_window) { 1095 parent_window_ = parent_window; 1096 // If we're done with the initialization step, go ahead 1097 if (is_initialized()) { 1098 if (parent_window == NULL) { 1099 // Hide and reparent the automation window. This window will get 1100 // reparented to the new ActiveX/Active document window when it gets 1101 // created. 1102 ShowWindow(SW_HIDE); 1103 SetParent(GetDesktopWindow()); 1104 } else { 1105 if (!::IsWindow(chrome_window())) { 1106 DLOG(WARNING) << "Invalid Chrome Window handle in SetParentWindow"; 1107 return; 1108 } 1109 1110 if (!SetParent(parent_window)) { 1111 DLOG(WARNING) << "Failed to set parent window for automation window. " 1112 << "Error = " 1113 << GetLastError(); 1114 return; 1115 } 1116 1117 RECT parent_client_rect = {0}; 1118 ::GetClientRect(parent_window, &parent_client_rect); 1119 int width = parent_client_rect.right - parent_client_rect.left; 1120 int height = parent_client_rect.bottom - parent_client_rect.top; 1121 1122 Resize(width, height, SWP_SHOWWINDOW | SWP_NOZORDER); 1123 } 1124 } 1125 } 1126 1127 void ChromeFrameAutomationClient::ReleaseAutomationServer() { 1128 if (automation_server_id_) { 1129 // Cache the server id and clear the automation_server_id_ before 1130 // calling ReleaseAutomationServer. The reason we do this is that 1131 // we must cancel pending messages before we release the automation server. 1132 // Furthermore, while ReleaseAutomationServer is running, we could get 1133 // a callback to LaunchComplete which could cause an external tab to be 1134 // created. Ideally the callbacks should be dropped. 1135 // TODO(ananta) 1136 // Refactor the ChromeFrameAutomationProxy code to not depend on 1137 // AutomationProxy and simplify the whole mess. 1138 void* server_id = automation_server_id_; 1139 automation_server_id_ = NULL; 1140 1141 if (automation_server_) { 1142 // Make sure to clean up any pending sync messages before we go away. 1143 automation_server_->CancelAsync(this); 1144 } 1145 1146 proxy_factory_->ReleaseAutomationServer(server_id, this); 1147 automation_server_ = NULL; 1148 1149 // automation_server_ must not have been set to non NULL. 1150 // (if this regresses, start by looking at LaunchComplete()). 1151 DCHECK(automation_server_ == NULL); 1152 } else { 1153 DCHECK(automation_server_ == NULL); 1154 } 1155 } 1156 1157 void ChromeFrameAutomationClient::SendContextMenuCommandToChromeFrame( 1158 int selected_command) { 1159 if (tab_) 1160 tab_->SendContextMenuCommand(selected_command); 1161 } 1162 1163 std::wstring ChromeFrameAutomationClient::GetVersion() const { 1164 return GetCurrentModuleVersion(); 1165 } 1166 1167 void ChromeFrameAutomationClient::Print(HDC print_dc, 1168 const RECT& print_bounds) { 1169 if (!tab_window_) { 1170 NOTREACHED(); 1171 return; 1172 } 1173 1174 HDC window_dc = ::GetDC(tab_window_); 1175 1176 BitBlt(print_dc, print_bounds.left, print_bounds.top, 1177 print_bounds.right - print_bounds.left, 1178 print_bounds.bottom - print_bounds.top, 1179 window_dc, print_bounds.left, print_bounds.top, 1180 SRCCOPY); 1181 1182 ::ReleaseDC(tab_window_, window_dc); 1183 } 1184 1185 void ChromeFrameAutomationClient::PrintTab() { 1186 if (tab_) 1187 tab_->PrintAsync(); 1188 } 1189 1190 void ChromeFrameAutomationClient::AttachExternalTab( 1191 uint64 external_tab_cookie) { 1192 DCHECK_EQ(static_cast<TabProxy*>(NULL), tab_.get()); 1193 DCHECK_EQ(-1, tab_handle_); 1194 1195 external_tab_cookie_ = external_tab_cookie; 1196 } 1197 1198 void ChromeFrameAutomationClient::BlockExternalTab(uint64 cookie) { 1199 // The host does not want this tab to be shown (due popup blocker). 1200 IPC::SyncMessage* message = 1201 new AutomationMsg_ConnectExternalTab(cookie, false, m_hWnd, 1202 NULL, NULL, NULL, 0); 1203 automation_server_->SendAsAsync(message, NULL, this); 1204 } 1205 1206 void ChromeFrameAutomationClient::SetPageFontSize( 1207 enum AutomationPageFontSize font_size) { 1208 if (font_size < SMALLEST_FONT || 1209 font_size > LARGEST_FONT) { 1210 NOTREACHED() << "Invalid font size specified : " 1211 << font_size; 1212 return; 1213 } 1214 1215 automation_server_->Send( 1216 new AutomationMsg_SetPageFontSize(tab_handle_, font_size)); 1217 } 1218 1219 void ChromeFrameAutomationClient::RemoveBrowsingData(int remove_mask) { 1220 automation_server_->Send(new AutomationMsg_RemoveBrowsingData(remove_mask)); 1221 } 1222 1223 void ChromeFrameAutomationClient::SetUrlFetcher( 1224 PluginUrlRequestManager* url_fetcher) { 1225 DCHECK(url_fetcher != NULL); 1226 url_fetcher_ = url_fetcher; 1227 url_fetcher_flags_ = url_fetcher->GetThreadSafeFlags(); 1228 url_fetcher_->set_delegate(this); 1229 } 1230 1231 void ChromeFrameAutomationClient::SetZoomLevel(content::PageZoom zoom_level) { 1232 if (automation_server_) { 1233 automation_server_->Send(new AutomationMsg_SetZoomLevel(tab_handle_, 1234 zoom_level)); 1235 } 1236 } 1237 1238 void ChromeFrameAutomationClient::OnUnload(bool* should_unload) { 1239 *should_unload = true; 1240 if (automation_server_) { 1241 const DWORD kUnloadEventTimeout = 20000; 1242 1243 IPC::SyncMessage* msg = new AutomationMsg_RunUnloadHandlers(tab_handle_, 1244 should_unload); 1245 base::WaitableEvent unload_call_finished(false, false); 1246 UnloadContext* unload_context = new UnloadContext(&unload_call_finished, 1247 should_unload); 1248 automation_server_->SendAsAsync(msg, unload_context, this); 1249 HANDLE done = unload_call_finished.handle(); 1250 WaitWithMessageLoop(&done, 1, kUnloadEventTimeout); 1251 } 1252 } 1253 1254 ////////////////////////////////////////////////////////////////////////// 1255 // PluginUrlRequestDelegate implementation. 1256 // Forward network related responses to Chrome. 1257 1258 void ChromeFrameAutomationClient::OnResponseStarted( 1259 int request_id, const char* mime_type, const char* headers, int size, 1260 base::Time last_modified, const std::string& redirect_url, 1261 int redirect_status, const net::HostPortPair& socket_address, 1262 uint64 upload_size) { 1263 AutomationURLResponse response; 1264 response.mime_type = mime_type; 1265 if (headers) 1266 response.headers = headers; 1267 response.content_length = size; 1268 response.last_modified = last_modified; 1269 response.redirect_url = redirect_url; 1270 response.redirect_status = redirect_status; 1271 response.socket_address = socket_address; 1272 response.upload_size = upload_size; 1273 1274 automation_server_->Send(new AutomationMsg_RequestStarted( 1275 tab_->handle(), request_id, response)); 1276 } 1277 1278 void ChromeFrameAutomationClient::OnReadComplete(int request_id, 1279 const std::string& data) { 1280 automation_server_->Send(new AutomationMsg_RequestData( 1281 tab_->handle(), request_id, data)); 1282 } 1283 1284 void ChromeFrameAutomationClient::OnResponseEnd( 1285 int request_id, 1286 const net::URLRequestStatus& status) { 1287 automation_server_->Send(new AutomationMsg_RequestEnd( 1288 tab_->handle(), request_id, status)); 1289 } 1290 1291 void ChromeFrameAutomationClient::OnCookiesRetrieved(bool success, 1292 const GURL& url, const std::string& cookie_string, int cookie_id) { 1293 automation_server_->Send(new AutomationMsg_GetCookiesHostResponse( 1294 tab_->handle(), success, url, cookie_string, cookie_id)); 1295 } 1296