| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome_frame/chrome_frame_automation.h" | 5 #include "chrome_frame/chrome_frame_automation.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
| 10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 #include "chrome_frame/simple_resource_loader.h" | 31 #include "chrome_frame/simple_resource_loader.h" |
| 32 #include "chrome_frame/utils.h" | 32 #include "chrome_frame/utils.h" |
| 33 #include "ui/base/ui_base_switches.h" | 33 #include "ui/base/ui_base_switches.h" |
| 34 | 34 |
| 35 #ifdef NDEBUG | 35 #ifdef NDEBUG |
| 36 int64 kAutomationServerReasonableLaunchDelay = 1000; // in milliseconds | 36 int64 kAutomationServerReasonableLaunchDelay = 1000; // in milliseconds |
| 37 #else | 37 #else |
| 38 int64 kAutomationServerReasonableLaunchDelay = 1000 * 10; | 38 int64 kAutomationServerReasonableLaunchDelay = 1000 * 10; |
| 39 #endif | 39 #endif |
| 40 | 40 |
| 41 int kDefaultSendUMADataInterval = 20000; // in milliseconds. | |
| 42 | |
| 43 static const wchar_t kUmaSendIntervalValue[] = L"UmaSendInterval"; | |
| 44 | |
| 45 class ChromeFrameAutomationProxyImpl::TabProxyNotificationMessageFilter | 41 class ChromeFrameAutomationProxyImpl::TabProxyNotificationMessageFilter |
| 46 : public IPC::ChannelProxy::MessageFilter { | 42 : public IPC::ChannelProxy::MessageFilter { |
| 47 public: | 43 public: |
| 48 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker) | 44 explicit TabProxyNotificationMessageFilter(AutomationHandleTracker* tracker) |
| 49 : tracker_(tracker) { | 45 : tracker_(tracker) { |
| 50 } | 46 } |
| 51 | 47 |
| 52 void AddTabProxy(AutomationHandle tab_proxy) { | 48 void AddTabProxy(AutomationHandle tab_proxy) { |
| 53 base::AutoLock lock(lock_); | 49 base::AutoLock lock(lock_); |
| 54 tabs_list_.push_back(tab_proxy); | 50 tabs_list_.push_back(tab_proxy); |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 198 #else | 194 #else |
| 199 void Dump() {} | 195 void Dump() {} |
| 200 #endif | 196 #endif |
| 201 }; | 197 }; |
| 202 | 198 |
| 203 DISABLE_RUNNABLE_METHOD_REFCOUNT(AutomationProxyCacheEntry); | 199 DISABLE_RUNNABLE_METHOD_REFCOUNT(AutomationProxyCacheEntry); |
| 204 | 200 |
| 205 AutomationProxyCacheEntry::AutomationProxyCacheEntry( | 201 AutomationProxyCacheEntry::AutomationProxyCacheEntry( |
| 206 ChromeFrameLaunchParams* params, LaunchDelegate* delegate) | 202 ChromeFrameLaunchParams* params, LaunchDelegate* delegate) |
| 207 : profile_name(params->profile_name()), | 203 : profile_name(params->profile_name()), |
| 208 launch_result_(AUTOMATION_LAUNCH_RESULT_INVALID), | 204 launch_result_(AUTOMATION_LAUNCH_RESULT_INVALID) { |
| 209 snapshots_(NULL), uma_send_interval_(1) { | |
| 210 DCHECK(delegate); | 205 DCHECK(delegate); |
| 211 thread_.reset(new base::Thread(WideToASCII(profile_name).c_str())); | 206 thread_.reset(new base::Thread(WideToASCII(profile_name).c_str())); |
| 212 thread_->Start(); | 207 thread_->Start(); |
| 213 // Use scoped_refptr so that the params will get released when the task | 208 // Use scoped_refptr so that the params will get released when the task |
| 214 // has been run. | 209 // has been run. |
| 215 scoped_refptr<ChromeFrameLaunchParams> ref_params(params); | 210 scoped_refptr<ChromeFrameLaunchParams> ref_params(params); |
| 216 thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, | 211 thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 217 &AutomationProxyCacheEntry::CreateProxy, ref_params, delegate)); | 212 &AutomationProxyCacheEntry::CreateProxy, ref_params, delegate)); |
| 218 } | 213 } |
| 219 | 214 |
| 220 AutomationProxyCacheEntry::~AutomationProxyCacheEntry() { | 215 AutomationProxyCacheEntry::~AutomationProxyCacheEntry() { |
| 221 DVLOG(1) << __FUNCTION__ << profile_name; | 216 DVLOG(1) << __FUNCTION__ << profile_name; |
| 222 // Attempt to fix chrome_frame_tests crash seen at times on the IE6/IE7 | 217 // Attempt to fix chrome_frame_tests crash seen at times on the IE6/IE7 |
| 223 // builders. It appears that there are cases when we can enter here when the | 218 // builders. It appears that there are cases when we can enter here when the |
| 224 // AtExitManager is tearing down the global ProxyCache which causes a crash | 219 // AtExitManager is tearing down the global ProxyCache which causes a crash |
| 225 // while tearing down the AutomationProxy object due to a NULL MessageLoop | 220 // while tearing down the AutomationProxy object due to a NULL MessageLoop |
| 226 // The AutomationProxy class uses the SyncChannel which assumes the existence | 221 // The AutomationProxy class uses the SyncChannel which assumes the existence |
| 227 // of a MessageLoop instance. | 222 // of a MessageLoop instance. |
| 228 // We leak the AutomationProxy pointer here to avoid a crash. | 223 // We leak the AutomationProxy pointer here to avoid a crash. |
| 229 if (MessageLoop::current() == NULL) { | 224 if (MessageLoop::current() == NULL) { |
| 230 proxy_.release(); | 225 proxy_.release(); |
| 231 } | 226 } |
| 232 } | 227 } |
| 233 | 228 |
| 234 void AutomationProxyCacheEntry::StartSendUmaInterval( | |
| 235 ChromeFrameHistogramSnapshots* snapshots, int send_interval) { | |
| 236 DCHECK(snapshots); | |
| 237 DCHECK(!snapshots_); | |
| 238 snapshots_ = snapshots; | |
| 239 uma_send_interval_ = send_interval; | |
| 240 thread_->message_loop()->PostDelayedTask(FROM_HERE, | |
| 241 NewRunnableMethod(this, &AutomationProxyCacheEntry::SendUMAData), | |
| 242 send_interval); | |
| 243 } | |
| 244 | |
| 245 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params, | 229 void AutomationProxyCacheEntry::CreateProxy(ChromeFrameLaunchParams* params, |
| 246 LaunchDelegate* delegate) { | 230 LaunchDelegate* delegate) { |
| 247 DCHECK(IsSameThread(base::PlatformThread::CurrentId())); | 231 DCHECK(IsSameThread(base::PlatformThread::CurrentId())); |
| 248 DCHECK(delegate); | 232 DCHECK(delegate); |
| 249 DCHECK(params); | 233 DCHECK(params); |
| 250 DCHECK(proxy_.get() == NULL); | 234 DCHECK(proxy_.get() == NULL); |
| 251 | 235 |
| 252 // We *must* create automationproxy in a thread that has message loop, | 236 // We *must* create automationproxy in a thread that has message loop, |
| 253 // since SyncChannel::Context construction registers event to be watched | 237 // since SyncChannel::Context construction registers event to be watched |
| 254 // through ObjectWatcher which subscribes for the current thread message loop | 238 // through ObjectWatcher which subscribes for the current thread message loop |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 *was_last_delegate = false; | 358 *was_last_delegate = false; |
| 375 | 359 |
| 376 LaunchDelegates::iterator it = std::find(launch_delegates_.begin(), | 360 LaunchDelegates::iterator it = std::find(launch_delegates_.begin(), |
| 377 launch_delegates_.end(), delegate); | 361 launch_delegates_.end(), delegate); |
| 378 if (it == launch_delegates_.end()) { | 362 if (it == launch_delegates_.end()) { |
| 379 NOTREACHED(); | 363 NOTREACHED(); |
| 380 } else { | 364 } else { |
| 381 if (launch_delegates_.size() == 1) { | 365 if (launch_delegates_.size() == 1) { |
| 382 *was_last_delegate = true; | 366 *was_last_delegate = true; |
| 383 | 367 |
| 384 if (snapshots_) | |
| 385 SendUMAData(); | |
| 386 | |
| 387 // Process pending notifications. | 368 // Process pending notifications. |
| 388 thread_->message_loop()->RunAllPending(); | 369 thread_->message_loop()->RunAllPending(); |
| 389 | 370 |
| 390 // Take down the proxy since we no longer have any clients. | 371 // Take down the proxy since we no longer have any clients. |
| 391 // Make sure we only do this once all pending messages have been cleared. | 372 // Make sure we only do this once all pending messages have been cleared. |
| 392 proxy_.reset(NULL); | 373 proxy_.reset(NULL); |
| 393 } | 374 } |
| 394 // Be careful to remove from the list after running pending | 375 // Be careful to remove from the list after running pending |
| 395 // tasks. Otherwise the delegate being removed might miss out | 376 // tasks. Otherwise the delegate being removed might miss out |
| 396 // on pending notifications such as LaunchComplete. | 377 // on pending notifications such as LaunchComplete. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 414 | 395 |
| 415 void AutomationProxyCacheEntry::OnChannelError() { | 396 void AutomationProxyCacheEntry::OnChannelError() { |
| 416 DCHECK(IsSameThread(base::PlatformThread::CurrentId())); | 397 DCHECK(IsSameThread(base::PlatformThread::CurrentId())); |
| 417 launch_result_ = AUTOMATION_SERVER_CRASHED; | 398 launch_result_ = AUTOMATION_SERVER_CRASHED; |
| 418 LaunchDelegates::const_iterator it = launch_delegates_.begin(); | 399 LaunchDelegates::const_iterator it = launch_delegates_.begin(); |
| 419 for (; it != launch_delegates_.end(); ++it) { | 400 for (; it != launch_delegates_.end(); ++it) { |
| 420 (*it)->AutomationServerDied(); | 401 (*it)->AutomationServerDied(); |
| 421 } | 402 } |
| 422 } | 403 } |
| 423 | 404 |
| 424 void AutomationProxyCacheEntry::SendUMAData() { | |
| 425 DCHECK(IsSameThread(base::PlatformThread::CurrentId())); | |
| 426 DCHECK(snapshots_); | |
| 427 // IE uses the chrome frame provided UMA data uploading scheme. NPAPI | |
| 428 // continues to use Chrome to upload UMA data. | |
| 429 if (CrashMetricsReporter::GetInstance()->active()) { | |
| 430 return; | |
| 431 } | |
| 432 | |
| 433 if (!proxy_.get()) { | |
| 434 DLOG(WARNING) << __FUNCTION__ << " NULL proxy, can't send UMA data"; | |
| 435 } else { | |
| 436 ChromeFrameHistogramSnapshots::HistogramPickledList histograms = | |
| 437 snapshots_->GatherAllHistograms(); | |
| 438 | |
| 439 if (!histograms.empty()) { | |
| 440 proxy_->Send(new AutomationMsg_RecordHistograms(histograms)); | |
| 441 } | |
| 442 | |
| 443 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 444 NewRunnableMethod(this, &AutomationProxyCacheEntry::SendUMAData), | |
| 445 uma_send_interval_); | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 | |
| 450 DISABLE_RUNNABLE_METHOD_REFCOUNT(ProxyFactory); | 405 DISABLE_RUNNABLE_METHOD_REFCOUNT(ProxyFactory); |
| 451 | 406 |
| 452 ProxyFactory::ProxyFactory() | 407 ProxyFactory::ProxyFactory() { |
| 453 : uma_send_interval_(0) { | |
| 454 uma_send_interval_ = GetConfigInt(kDefaultSendUMADataInterval, | |
| 455 kUmaSendIntervalValue); | |
| 456 } | 408 } |
| 457 | 409 |
| 458 ProxyFactory::~ProxyFactory() { | 410 ProxyFactory::~ProxyFactory() { |
| 459 for (size_t i = 0; i < proxies_.container().size(); ++i) { | 411 for (size_t i = 0; i < proxies_.container().size(); ++i) { |
| 460 DWORD result = proxies_[i]->WaitForThread(0); | 412 DWORD result = proxies_[i]->WaitForThread(0); |
| 461 if (WAIT_OBJECT_0 != result) | 413 if (WAIT_OBJECT_0 != result) |
| 462 // TODO(stoyan): Don't leak proxies on exit. | 414 // TODO(stoyan): Don't leak proxies on exit. |
| 463 DLOG(ERROR) << "Proxies leaked on exit."; | 415 DLOG(ERROR) << "Proxies leaked on exit."; |
| 464 } | 416 } |
| 465 } | 417 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 476 if (proxies_[i]->IsSameProfile(params->profile_name())) { | 428 if (proxies_[i]->IsSameProfile(params->profile_name())) { |
| 477 entry = proxies_[i]; | 429 entry = proxies_[i]; |
| 478 break; | 430 break; |
| 479 } | 431 } |
| 480 } | 432 } |
| 481 | 433 |
| 482 if (entry == NULL) { | 434 if (entry == NULL) { |
| 483 DVLOG(1) << __FUNCTION__ << " creating new proxy entry"; | 435 DVLOG(1) << __FUNCTION__ << " creating new proxy entry"; |
| 484 entry = new AutomationProxyCacheEntry(params, delegate); | 436 entry = new AutomationProxyCacheEntry(params, delegate); |
| 485 proxies_.container().push_back(entry); | 437 proxies_.container().push_back(entry); |
| 486 | |
| 487 // IE uses the chrome frame provided UMA data uploading scheme. NPAPI | |
| 488 // continues to use Chrome to upload UMA data. | |
| 489 if (!CrashMetricsReporter::GetInstance()->active()) { | |
| 490 entry->StartSendUmaInterval(&chrome_frame_histograms_, | |
| 491 uma_send_interval_); | |
| 492 } | |
| 493 } else if (delegate) { | 438 } else if (delegate) { |
| 494 // Notify the new delegate of the launch status from the worker thread | 439 // Notify the new delegate of the launch status from the worker thread |
| 495 // and add it to the list of delegates. | 440 // and add it to the list of delegates. |
| 496 entry->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(entry.get(), | 441 entry->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(entry.get(), |
| 497 &AutomationProxyCacheEntry::AddDelegate, delegate)); | 442 &AutomationProxyCacheEntry::AddDelegate, delegate)); |
| 498 } | 443 } |
| 499 | 444 |
| 500 DCHECK(automation_server_id != NULL); | 445 DCHECK(automation_server_id != NULL); |
| 501 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId())); | 446 DCHECK(!entry->IsSameThread(base::PlatformThread::CurrentId())); |
| 502 | 447 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 614 DCHECK_LT(chrome_launch_params_->launch_timeout(), | 559 DCHECK_LT(chrome_launch_params_->launch_timeout(), |
| 615 MAXINT / 2000); | 560 MAXINT / 2000); |
| 616 chrome_launch_params_->set_launch_timeout( | 561 chrome_launch_params_->set_launch_timeout( |
| 617 chrome_launch_params_->launch_timeout() * 2); | 562 chrome_launch_params_->launch_timeout() * 2); |
| 618 } | 563 } |
| 619 #endif // NDEBUG | 564 #endif // NDEBUG |
| 620 | 565 |
| 621 // Create a window on the UI thread for marshaling messages back and forth | 566 // Create a window on the UI thread for marshaling messages back and forth |
| 622 // from the IPC thread. This window cannot be a message only window as the | 567 // from the IPC thread. This window cannot be a message only window as the |
| 623 // external chrome tab window is created as a child of this window. This | 568 // external chrome tab window is created as a child of this window. This |
| 624 // window is eventually reparented to the ActiveX/NPAPI plugin window. | 569 // window is eventually reparented to the ActiveX plugin window. |
| 625 if (!Create(GetDesktopWindow(), NULL, NULL, | 570 if (!Create(GetDesktopWindow(), NULL, NULL, |
| 626 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, | 571 WS_CHILDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, |
| 627 WS_EX_TOOLWINDOW)) { | 572 WS_EX_TOOLWINDOW)) { |
| 628 NOTREACHED(); | 573 NOTREACHED(); |
| 629 return false; | 574 return false; |
| 630 } | 575 } |
| 631 | 576 |
| 632 // Keep object in memory, while the window is alive. | 577 // Keep object in memory, while the window is alive. |
| 633 // Corresponding Release is in OnFinalMessage(); | 578 // Corresponding Release is in OnFinalMessage(); |
| 634 AddRef(); | 579 AddRef(); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 650 | 595 |
| 651 void ChromeFrameAutomationClient::Uninitialize() { | 596 void ChromeFrameAutomationClient::Uninitialize() { |
| 652 if (init_state_ == UNINITIALIZED) { | 597 if (init_state_ == UNINITIALIZED) { |
| 653 DLOG(WARNING) << __FUNCTION__ << ": Automation client not initialized"; | 598 DLOG(WARNING) << __FUNCTION__ << ": Automation client not initialized"; |
| 654 return; | 599 return; |
| 655 } | 600 } |
| 656 | 601 |
| 657 init_state_ = UNINITIALIZING; | 602 init_state_ = UNINITIALIZING; |
| 658 | 603 |
| 659 // Called from client's FinalRelease() / destructor | 604 // Called from client's FinalRelease() / destructor |
| 660 // ChromeFrameAutomationClient may wait for the initialization (launch) | |
| 661 // to complete while Uninitialize is called. | |
| 662 // We either have to: | |
| 663 // 1) Make ReleaseAutomationServer blocking call (wait until thread exits) | |
| 664 // 2) Behave like a COM object i.e. increase module lock count. | |
| 665 // Otherwise the DLL may get unloaded while we have running threads. | |
| 666 // Unfortunately in NPAPI case we cannot increase module lock count, hence | |
| 667 // we stick with blocking/waiting | |
| 668 if (url_fetcher_) { | 605 if (url_fetcher_) { |
| 669 // Clean up any outstanding requests | 606 // Clean up any outstanding requests |
| 670 url_fetcher_->StopAllRequests(); | 607 url_fetcher_->StopAllRequests(); |
| 671 url_fetcher_ = NULL; | 608 url_fetcher_ = NULL; |
| 672 } | 609 } |
| 673 | 610 |
| 674 if (tab_) { | 611 if (tab_) { |
| 675 tab_->RemoveObserver(this); | 612 tab_->RemoveObserver(this); |
| 676 if (automation_server_) | 613 if (automation_server_) |
| 677 automation_server_->ReleaseTabProxy(tab_->handle()); | 614 automation_server_->ReleaseTabProxy(tab_->handle()); |
| (...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1050 } | 987 } |
| 1051 break; | 988 break; |
| 1052 } | 989 } |
| 1053 | 990 |
| 1054 PostTask(FROM_HERE, NewRunnableMethod(this, | 991 PostTask(FROM_HERE, NewRunnableMethod(this, |
| 1055 &ChromeFrameAutomationClient::ProcessUrlRequestMessage, tab, msg, true)); | 992 &ChromeFrameAutomationClient::ProcessUrlRequestMessage, tab, msg, true)); |
| 1056 return true; | 993 return true; |
| 1057 } | 994 } |
| 1058 | 995 |
| 1059 // These are invoked in channel's background thread. | 996 // These are invoked in channel's background thread. |
| 1060 // Cannot call any method of the activex/npapi here since they are STA | 997 // Cannot call any method of the activex here since it is a STA kind of being. |
| 1061 // kind of beings. | |
| 1062 // By default we marshal the IPC message to the main/GUI thread and from there | 998 // By default we marshal the IPC message to the main/GUI thread and from there |
| 1063 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg). | 999 // we safely invoke chrome_frame_delegate_->OnMessageReceived(msg). |
| 1064 bool ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab, | 1000 bool ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab, |
| 1065 const IPC::Message& msg) { | 1001 const IPC::Message& msg) { |
| 1066 DCHECK(tab == tab_.get()); | 1002 DCHECK(tab == tab_.get()); |
| 1067 // Quickly process network related messages. | 1003 // Quickly process network related messages. |
| 1068 if (url_fetcher_ && ProcessUrlRequestMessage(tab, msg, false)) | 1004 if (url_fetcher_ && ProcessUrlRequestMessage(tab, msg, false)) |
| 1069 return true; | 1005 return true; |
| 1070 | 1006 |
| 1071 // Early check to avoid needless marshaling | 1007 // Early check to avoid needless marshaling |
| (...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1351 const net::URLRequestStatus& status) { | 1287 const net::URLRequestStatus& status) { |
| 1352 automation_server_->Send(new AutomationMsg_RequestEnd( | 1288 automation_server_->Send(new AutomationMsg_RequestEnd( |
| 1353 tab_->handle(), request_id, status)); | 1289 tab_->handle(), request_id, status)); |
| 1354 } | 1290 } |
| 1355 | 1291 |
| 1356 void ChromeFrameAutomationClient::OnCookiesRetrieved(bool success, | 1292 void ChromeFrameAutomationClient::OnCookiesRetrieved(bool success, |
| 1357 const GURL& url, const std::string& cookie_string, int cookie_id) { | 1293 const GURL& url, const std::string& cookie_string, int cookie_id) { |
| 1358 automation_server_->Send(new AutomationMsg_GetCookiesHostResponse( | 1294 automation_server_->Send(new AutomationMsg_GetCookiesHostResponse( |
| 1359 tab_->handle(), success, url, cookie_string, cookie_id)); | 1295 tab_->handle(), success, url, cookie_string, cookie_id)); |
| 1360 } | 1296 } |
| OLD | NEW |