diff options
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 3 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 3 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_win.cc | 5 | ||||
-rw-r--r-- | chrome/browser/external_tab_container_win.cc | 68 | ||||
-rw-r--r-- | chrome/browser/external_tab_container_win.h | 31 | ||||
-rw-r--r-- | chrome/test/automation/automation_messages_internal.h | 8 | ||||
-rw-r--r-- | chrome_frame/chrome_active_document.cc | 13 | ||||
-rw-r--r-- | chrome_frame/chrome_active_document.h | 7 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_activex_base.h | 37 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 32 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.h | 8 | ||||
-rw-r--r-- | chrome_frame/custom_sync_call_context.h | 26 | ||||
-rw-r--r-- | chrome_frame/utils.cc | 34 | ||||
-rw-r--r-- | chrome_frame/utils.h | 4 |
14 files changed, 175 insertions, 104 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 664c639..a7a59ad 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -381,7 +381,8 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(AutomationMsg_HandleMessageFromExternalHost, OnMessageFromExternalHost) IPC_MESSAGE_HANDLER(AutomationMsg_BrowserMove, OnBrowserMoved) - IPC_MESSAGE_HANDLER(AutomationMsg_RunUnloadHandlers, OnRunUnloadHandlers) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_RunUnloadHandlers, + OnRunUnloadHandlers) IPC_MESSAGE_HANDLER(AutomationMsg_SetZoomLevel, OnSetZoomLevel) #endif // defined(OS_WIN) #if defined(OS_CHROMEOS) diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 2ad6dea..233a8c7 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -361,8 +361,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, void OnBrowserMoved(int handle); - void OnRunUnloadHandlers(int handle, gfx::NativeWindow notification_window, - int notification_message); + void OnRunUnloadHandlers(int handle, IPC::Message* reply_message); void OnSetZoomLevel(int handle, int zoom_level); diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc index 86dfec1..98ee218 100644 --- a/chrome/browser/automation/automation_provider_win.cc +++ b/chrome/browser/automation/automation_provider_win.cc @@ -527,11 +527,10 @@ void AutomationProvider::NavigateExternalTabAtIndex( } void AutomationProvider::OnRunUnloadHandlers( - int handle, gfx::NativeWindow notification_window, - int notification_message) { + int handle, IPC::Message* reply_message) { ExternalTabContainer* external_tab = GetExternalTabForHandle(handle); if (external_tab) { - external_tab->RunUnloadHandlers(notification_window, notification_message); + external_tab->RunUnloadHandlers(reply_message); } } diff --git a/chrome/browser/external_tab_container_win.cc b/chrome/browser/external_tab_container_win.cc index 24d7f60..012a966 100644 --- a/chrome/browser/external_tab_container_win.cc +++ b/chrome/browser/external_tab_container_win.cc @@ -57,13 +57,11 @@ ExternalTabContainer::ExternalTabContainer( handle_top_level_requests_(false), external_method_factory_(this), enabled_extension_automation_(false), - waiting_for_unload_event_(false), pending_(false), infobars_enabled_(true), focus_manager_(NULL), external_tab_view_(NULL), - notification_window_(NULL), - notification_message_(NULL) { + unload_reply_message_(NULL) { } ExternalTabContainer::~ExternalTabContainer() { @@ -417,15 +415,16 @@ void ExternalTabContainer::LoadingStateChanged(TabContents* source) { } void ExternalTabContainer::CloseContents(TabContents* source) { - static const int kExternalTabCloseContentsDelayMS = 100; + if (!automation_) + return; - if (waiting_for_unload_event_) { - PostMessage(notification_window_, notification_message_, 0, 0); - waiting_for_unload_event_ = false; + if (unload_reply_message_) { + AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, + true); + automation_->Send(unload_reply_message_); + unload_reply_message_ = NULL; } else { - if (automation_) { - automation_->Send(new AutomationMsg_CloseExternalTab(0, tab_handle_)); - } + automation_->Send(new AutomationMsg_CloseExternalTab(0, tab_handle_)); } } @@ -612,6 +611,27 @@ void ExternalTabContainer::ShowHtmlDialog(HtmlDialogUIDelegate* delegate, browser_->window()->ShowHTMLDialog(delegate, parent); } +void ExternalTabContainer::BeforeUnloadFired(TabContents* tab, + bool proceed, + bool* proceed_to_fire_unload) { + DCHECK(unload_reply_message_); + *proceed_to_fire_unload = true; + + if (!automation_) { + delete unload_reply_message_; + unload_reply_message_ = NULL; + return; + } + + if (!proceed) { + AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, + false); + automation_->Send(unload_reply_message_); + unload_reply_message_ = NULL; + *proceed_to_fire_unload = false; + } +} + //////////////////////////////////////////////////////////////////////////////// // ExternalTabContainer, NotificationObserver implementation: @@ -717,20 +737,24 @@ void ExternalTabContainer::OnFinalMessage(HWND window) { Release(); } -void ExternalTabContainer::RunUnloadHandlers( - gfx::NativeWindow notification_window, - int notification_message) { - DCHECK(::IsWindow(notification_window)); - if (tab_contents_) { - notification_window_ = notification_window; - notification_message_ = notification_message; +void ExternalTabContainer::RunUnloadHandlers(IPC::Message* reply_message) { + if (!automation_) { + delete reply_message; + return; + } - if (Browser::RunUnloadEventsHelper(tab_contents_)) { - waiting_for_unload_event_ = true; - } + // If we have a pending unload message, then just respond back to this + // request and continue processing the previous unload message. + if (unload_reply_message_) { + AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); + automation_->Send(reply_message); + return; } - if (!waiting_for_unload_event_) { - PostMessage(notification_window, notification_message, 0, 0); + if (tab_contents_ && Browser::RunUnloadEventsHelper(tab_contents_)) { + unload_reply_message_ = reply_message; + } else { + AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); + automation_->Send(reply_message); } } diff --git a/chrome/browser/external_tab_container_win.h b/chrome/browser/external_tab_container_win.h index 591a1ff..597901b 100644 --- a/chrome/browser/external_tab_container_win.h +++ b/chrome/browser/external_tab_container_win.h @@ -148,15 +148,6 @@ class ExternalTabContainer : public TabContentsDelegate, const NavigationEntry::SSLStatus& ssl, bool show_history); - // Overriden from TabContentsDelegate::AutomationResourceRoutingDelegate - virtual void RegisterRenderViewHost(RenderViewHost* render_view_host); - virtual void UnregisterRenderViewHost(RenderViewHost* render_view_host); - - // Overridden from NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - // Handles the context menu display operation. This allows external // hosts to customize the menu. virtual bool HandleContextMenu(const ContextMenuParams& params); @@ -172,6 +163,19 @@ class ExternalTabContainer : public TabContentsDelegate, virtual void ShowHtmlDialog(HtmlDialogUIDelegate* delegate, gfx::NativeWindow parent_window); + virtual void BeforeUnloadFired(TabContents* tab, + bool proceed, + bool* proceed_to_fire_unload); + + // Overriden from TabContentsDelegate::AutomationResourceRoutingDelegate + virtual void RegisterRenderViewHost(RenderViewHost* render_view_host); + virtual void UnregisterRenderViewHost(RenderViewHost* render_view_host); + + // Overridden from NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Returns the ExternalTabContainer instance associated with the cookie // passed in. It also erases the corresponding reference from the map. // Returns NULL if we fail to find the cookie in the map. @@ -204,8 +208,7 @@ class ExternalTabContainer : public TabContentsDelegate, virtual bool infobars_enabled(); - void RunUnloadHandlers(gfx::NativeWindow notification_window, - int notification_message); + void RunUnloadHandlers(IPC::Message* reply_message); protected: // Overridden from views::WidgetWin: @@ -308,9 +311,6 @@ class ExternalTabContainer : public TabContentsDelegate, // A mapping between accelerators and commands. std::map<views::Accelerator, int> accelerator_table_; - // Set to true if the tab is waiting for the unload event to complete. - bool waiting_for_unload_event_; - // Contains the list of URL requests which are pending waiting for an ack // from the external host. std::vector<PendingTopLevelNavigation> pending_open_url_requests_; @@ -326,8 +326,7 @@ class ExternalTabContainer : public TabContentsDelegate, views::View* external_tab_view_; - gfx::NativeWindow notification_window_; - int notification_message_; + IPC::Message* unload_reply_message_; DISALLOW_COPY_AND_ASSIGN(ExternalTabContainer); }; diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index 2f413d7..2664ee6 100644 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -1413,12 +1413,8 @@ IPC_BEGIN_MESSAGES(Automation) // runs unload handlers if any on the current page. // Request: // -int: Tab handle - // -gfx::NativeWindow: notification window - // -int: notification message. - // Response: - // None expected - IPC_MESSAGE_ROUTED3(AutomationMsg_RunUnloadHandlers, int, gfx::NativeWindow, - int) + // -bool: result: true->unload, false->don't unload + IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_RunUnloadHandlers, int, bool) // This message sets the current zoom level on the tab // Request: diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc index 6d350f8..d5c4250 100644 --- a/chrome_frame/chrome_active_document.cc +++ b/chrome_frame/chrome_active_document.cc @@ -876,6 +876,19 @@ void ChromeActiveDocument::OnSetZoomRange(const GUID* cmd_group_guid, } } +void ChromeActiveDocument::OnUnload(const GUID* cmd_group_guid, + DWORD command_id, + DWORD cmd_exec_opt, + VARIANT* in_args, + VARIANT* out_args) { + if (IsValid() && out_args) { + bool should_unload = true; + automation_client_->OnUnload(&should_unload); + out_args->vt = VT_BOOL; + out_args->boolVal = should_unload ? VARIANT_TRUE : VARIANT_FALSE; + } +} + void ChromeActiveDocument::OnOpenURL(int tab_handle, const GURL& url_to_open, const GURL& referrer, diff --git a/chrome_frame/chrome_active_document.h b/chrome_frame/chrome_active_document.h index 146f091..370ba32 100644 --- a/chrome_frame/chrome_active_document.h +++ b/chrome_frame/chrome_active_document.h @@ -276,6 +276,7 @@ BEGIN_EXEC_COMMAND_MAP(ChromeActiveDocument) OnDisplayPrivacyInfo) EXEC_COMMAND_HANDLER(NULL, OLECMDID_OPTICAL_GETZOOMRANGE, OnGetZoomRange) EXEC_COMMAND_HANDLER(NULL, OLECMDID_OPTICAL_ZOOM, OnSetZoomRange) + EXEC_COMMAND_HANDLER(NULL, OLECMDID_ONUNLOAD, OnUnload) END_EXEC_COMMAND_MAP() // IPCs from automation server. @@ -390,6 +391,12 @@ END_EXEC_COMMAND_MAP() void OnSetZoomRange(const GUID* cmd_group_guid, DWORD command_id, DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args); + // This function handles the OLECMDID_ONUNLOAD command. It enables Chrome to + // invoke before unload and unload handlers on the page if any, thereby + // enabling a webpage to potentially cancel the operation. + void OnUnload(const GUID* cmd_group_guid, DWORD command_id, + DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args); + // Call exec on our site's command target HRESULT IEExec(const GUID* cmd_group_guid, DWORD command_id, DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args); diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h index dae53df..5f281f3 100644 --- a/chrome_frame/chrome_frame_activex_base.h +++ b/chrome_frame/chrome_frame_activex_base.h @@ -389,43 +389,6 @@ END_MSG_MAP() return true; } - // IOleInPlaceObject overrides. - STDMETHOD(InPlaceDeactivate)(void) { - static UINT onload_handlers_done_msg = - RegisterWindowMessage(L"ChromeFrame_OnloadHandlersDone"); - - if (m_bInPlaceActive && IsWindow() && IsValid()) { - static const int kChromeFrameUnloadEventTimerId = 0xdeadbeef; - static const int kChromeFrameUnloadEventTimeout = 1000; - - // To prevent us from indefinitely waiting for an acknowledgement from - // Chrome indicating that unload handlers have been run, we set a 1 - // second timer and exit the loop when it fires. - ::SetTimer(m_hWnd, kChromeFrameUnloadEventTimerId, - kChromeFrameUnloadEventTimeout, NULL); - - automation_client_->RunUnloadHandlers(m_hWnd, onload_handlers_done_msg); - - MSG msg = {0}; - while (GetMessage(&msg, NULL, 0, 0)) { - if (msg.message == onload_handlers_done_msg && - msg.hwnd == m_hWnd) { - break; - } - - if (msg.message == WM_TIMER && - msg.wParam == kChromeFrameUnloadEventTimerId) { - break; - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - ::KillTimer(m_hWnd, kChromeFrameUnloadEventTimerId); - } - return IOleInPlaceObjectWindowlessImpl<T>::InPlaceDeactivate(); - } - protected: virtual void GetProfilePath(const std::wstring& profile_name, FilePath* profile_path) { diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc index 908df61d..67d9ea8 100644 --- a/chrome_frame/chrome_frame_automation.cc +++ b/chrome_frame/chrome_frame_automation.cc @@ -122,6 +122,9 @@ class ChromeFrameAutomationProxyImpl::CFMsgDispatcher case AutomationMsg_GetEnabledExtensions::ID: InvokeCallback<GetEnabledExtensionsContext>(msg, context); break; + case AutomationMsg_RunUnloadHandlers::ID: + InvokeCallback<UnloadContext>(msg, context); + break; default: NOTREACHED(); } @@ -1367,19 +1370,6 @@ void ChromeFrameAutomationClient::RemoveBrowsingData(int remove_mask) { new AutomationMsg_RemoveBrowsingData(0, remove_mask)); } -void ChromeFrameAutomationClient::RunUnloadHandlers(HWND notification_window, - int notification_message) { - if (automation_server_) { - automation_server_->Send( - new AutomationMsg_RunUnloadHandlers(0, tab_handle_, - notification_window, - notification_message)); - } else { - // Post this message to ensure that the caller exits his message loop. - ::PostMessage(notification_window, notification_message, 0, 0); - } -} - void ChromeFrameAutomationClient::SetUrlFetcher( PluginUrlRequestManager* url_fetcher) { DCHECK(url_fetcher != NULL); @@ -1395,6 +1385,22 @@ void ChromeFrameAutomationClient::SetZoomLevel(PageZoom::Function zoom_level) { } } +void ChromeFrameAutomationClient::OnUnload(bool* should_unload) { + *should_unload = true; + if (automation_server_) { + const DWORD kUnloadEventTimeout = 20000; + + IPC::SyncMessage* msg = new AutomationMsg_RunUnloadHandlers(0, tab_handle_, + should_unload); + base::WaitableEvent unload_call_finished(false, false); + UnloadContext* unload_context = new UnloadContext(&unload_call_finished, + should_unload); + automation_server_->SendAsAsync(msg, unload_context, this); + HANDLE done = unload_call_finished.handle(); + WaitWithMessageLoop(&done, 1, kUnloadEventTimeout); + } +} + ////////////////////////////////////////////////////////////////////////// // PluginUrlRequestDelegate implementation. // Forward network related responses to Chrome. diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h index 7bb572a..0890fb7 100644 --- a/chrome_frame/chrome_frame_automation.h +++ b/chrome_frame/chrome_frame_automation.h @@ -415,13 +415,13 @@ class ChromeFrameAutomationClient // For IDeleteBrowsingHistorySupport void RemoveBrowsingData(int remove_mask); - // Sends an IPC message to the external tab container requesting it to run - // unload handlers on the page. - void RunUnloadHandlers(HWND notification_window, int notification_message); - // Sets the current zoom level on the tab. void SetZoomLevel(PageZoom::Function zoom_level); + // Fires before unload and unload handlers on the page if any. Allows the + // the website to put up a confirmation dialog on unload. + void OnUnload(bool* should_unload); + protected: // ChromeFrameAutomationProxy::LaunchDelegate implementation. virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy, diff --git a/chrome_frame/custom_sync_call_context.h b/chrome_frame/custom_sync_call_context.h index 28812ef..9cbe3d6 100644 --- a/chrome_frame/custom_sync_call_context.h +++ b/chrome_frame/custom_sync_call_context.h @@ -6,6 +6,7 @@ #include <vector> #include "base/ref_counted.h" +#include "base/waitable_event.h" #include "chrome_frame/sync_msg_reply_dispatcher.h" #include "chrome_frame/chrome_frame_automation.h" #include "ipc/ipc_sync_message.h" @@ -122,6 +123,31 @@ class BeginNavigateContext scoped_refptr<ChromeFrameAutomationClient> client_; }; +// Class that maintains contextual information for the unload operation, i.e. +// when the user attempts to navigate away from a page rendered in ChromeFrame. +class UnloadContext + : public SyncMessageReplyDispatcher::SyncMessageCallContext { + public: + typedef Tuple1<bool> output_type; + explicit UnloadContext(base::WaitableEvent* unload_done, bool* should_unload) + : should_unload_(should_unload), + unload_done_(unload_done) { + } + + void Completed(bool should_unload) { + *should_unload_ = should_unload; + unload_done_->Signal(); + should_unload_ = NULL; + unload_done_ = NULL; + // This object will be destroyed after this. Cannot access any members + // on returning from this function. + } + + private: + base::WaitableEvent* unload_done_; + bool* should_unload_; +}; + #endif // CHROME_FRAME_CUSTOM_SYNC_CALL_CONTEXT_H_ diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc index 35fbc83..80095428 100644 --- a/chrome_frame/utils.cc +++ b/chrome_frame/utils.cc @@ -1453,3 +1453,37 @@ void PinModule() { } } } + +void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout) { + base::Time now = base::Time::Now(); + base::Time wait_until = now + base::TimeDelta::FromMilliseconds(timeout); + + while (wait_until >= now) { + base::TimeDelta wait_time = wait_until - now; + DWORD wait = MsgWaitForMultipleObjects( + count, handles, FALSE, static_cast<DWORD>(wait_time.InMilliseconds()), + QS_ALLINPUT); + switch (wait) { + case WAIT_OBJECT_0: + case WAIT_TIMEOUT: + return; + + case WAIT_OBJECT_0 + 1: { + MSG msg = {0}; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + + default: { + NOTREACHED() << "Unexpected return from MsgWaitForMultipleObjects :" + << wait; + return; + } + } + now = base::Time::Now(); + } +} + diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h index e0da6ac..eadad72 100644 --- a/chrome_frame/utils.h +++ b/chrome_frame/utils.h @@ -550,4 +550,8 @@ bool CanNavigate(const GURL& url, IInternetSecurityManager* security_manager, // Call if you make irreversible patches. void PinModule(); +// Helper function to spin a message loop and dispatch messages while waiting +// for a handle to be signaled. +void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout); + #endif // CHROME_FRAME_UTILS_H_ |