diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 16:56:38 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 16:56:38 +0000 |
commit | 0c9ee9904acbfe0766f488edb207ff232635efd3 (patch) | |
tree | 5118e9af778ed35f8a68490171a75676ce202c4d /chrome/browser | |
parent | bbf87943104ec3343bd93152500fdb0bf81ee563 (diff) | |
download | chromium_src-0c9ee9904acbfe0766f488edb207ff232635efd3.zip chromium_src-0c9ee9904acbfe0766f488edb207ff232635efd3.tar.gz chromium_src-0c9ee9904acbfe0766f488edb207ff232635efd3.tar.bz2 |
Revert "This is the first of probably several patches trying to clean up the BlockedPopupContainer
into something that can be cross-platform."
This reverts commit 0247f4d628f8f56c0a42ab44efd1d29058167a11 (r17483).
Review URL: http://codereview.chromium.org/118166
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17485 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 21 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 3 | ||||
-rw-r--r-- | chrome/browser/blocked_popup_container.cc | 337 | ||||
-rw-r--r-- | chrome/browser/blocked_popup_container.h | 258 | ||||
-rw-r--r-- | chrome/browser/browser.vcproj | 8 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.cc | 40 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents.h | 12 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_win.cc | 2 | ||||
-rw-r--r-- | chrome/browser/views/blocked_popup_container.cc | 433 | ||||
-rw-r--r-- | chrome/browser/views/blocked_popup_container.h | 216 | ||||
-rw-r--r-- | chrome/browser/views/constrained_window_impl_interactive_uitest.cc | 30 |
11 files changed, 614 insertions, 746 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index fac1b83..c2e0877 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -21,7 +21,6 @@ #include "chrome/browser/automation/url_request_failed_dns_job.h" #include "chrome/browser/automation/url_request_mock_http_job.h" #include "chrome/browser/automation/url_request_slow_download_job.h" -#include "chrome/browser/blocked_popup_container.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/download/download_manager.h" @@ -1083,7 +1082,6 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(AutomationMsg_WindowTitle, GetWindowTitle) IPC_MESSAGE_HANDLER(AutomationMsg_SetEnableExtensionAutomation, SetEnableExtensionAutomation) - IPC_MESSAGE_HANDLER(AutomationMsg_BlockedPopupCount, GetBlockedPopupCount) IPC_END_MESSAGE_MAP() } @@ -2988,22 +2986,3 @@ void AutomationProvider::GetWindowTitle(int handle, string16* text) { gfx::NativeWindow window = window_tracker_->GetResource(handle); text->assign(platform_util::GetWindowTitle(window)); } - -void AutomationProvider::GetBlockedPopupCount(int handle, int* count) { - *count = -1; // -1 is the error code - if (tab_tracker_->ContainsHandle(handle)) { - NavigationController* nav_controller = tab_tracker_->GetResource(handle); - TabContents* tab_contents = nav_controller->tab_contents(); - if (tab_contents) { - BlockedPopupContainer* container = - tab_contents->blocked_popup_container(); - if (container) { - *count = static_cast<int>(container->GetBlockedPopupCount()); - } else { - // If we don't have a container, we don't have any blocked popups to - // contain! - *count = 0; - } - } - } -} diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 3c49ddb..1506868 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -451,9 +451,6 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, void GetWindowTitle(int handle, string16* text); - // Returns the number of blocked popups in the tab |handle|. - void GetBlockedPopupCount(int handle, int* count); - // Convert a tab handle into a TabContents. If |tab| is non-NULL a pointer // to the tab is also returned. Returns NULL in case of failure or if the tab // is not of the TabContents type. diff --git a/chrome/browser/blocked_popup_container.cc b/chrome/browser/blocked_popup_container.cc deleted file mode 100644 index ad9c7c6..0000000 --- a/chrome/browser/blocked_popup_container.cc +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -#include "chrome/browser/blocked_popup_container.h" - -#include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/pref_service.h" -#include "chrome/common/notification_service.h" - -// static -void BlockedPopupContainer::RegisterUserPrefs(PrefService* prefs) { - prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); -} - -void BlockedPopupContainer::AddTabContents(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host) { - // Show whitelisted popups immediately. - bool whitelisted = !!whitelist_.count(host); - if (whitelisted) - owner_->AddNewContents(tab_contents, NEW_POPUP, bounds, true, GURL()); - - if (has_been_dismissed_) { - // Don't want to show any other UI. - if (!whitelisted) - delete tab_contents; // Discard blocked popups entirely. - return; - } - - if (whitelisted) { - // Listen for this popup's destruction, so if the user closes it manually, - // we'll know to stop caring about it. - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_contents)); - - unblocked_popups_[tab_contents] = host; - } else { - if (blocked_popups_.size() >= kImpossibleNumberOfPopups) { - delete tab_contents; - LOG(INFO) << "Warning: Renderer is sending more popups to us than should " - "be possible. Renderer compromised?"; - return; - } - blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); - - tab_contents->set_delegate(this); - } - - PopupHosts::const_iterator i(popup_hosts_.find(host)); - if (i == popup_hosts_.end()) - popup_hosts_[host] = whitelisted; - else - DCHECK_EQ(whitelisted, i->second); - - // Update UI. - UpdateLabel(); - ShowSelf(); - owner_->PopupNotificationVisibilityChanged(true); -} - -void BlockedPopupContainer::LaunchPopupAtIndex(size_t index) { - if (index >= blocked_popups_.size()) - return; - - // Open the popup. - BlockedPopups::iterator i(blocked_popups_.begin() + index); - TabContents* tab_contents = i->tab_contents; - tab_contents->set_delegate(NULL); - owner_->AddNewContents(tab_contents, NEW_POPUP, i->bounds, true, GURL()); - - const std::string& host = i->host; - if (!host.empty()) { - // Listen for this popup's destruction, so if the user closes it manually, - // we'll know to stop caring about it. - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(tab_contents)); - - // Add the popup to the unblocked list. (Do this before the below call!) - unblocked_popups_[tab_contents] = i->host; - } - - // Remove the popup from the blocked list. - EraseDataForPopupAndUpdateUI(i); -} - -size_t BlockedPopupContainer::GetBlockedPopupCount() const { - return blocked_popups_.size(); -} - -bool BlockedPopupContainer::IsHostWhitelisted(size_t index) const { - PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); - return (i == popup_hosts_.end()) ? false : i->second; -} - -void BlockedPopupContainer::ToggleWhitelistingForHost(size_t index) { - PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); - const std::string& host = i->first; - bool should_whitelist = !i->second; - popup_hosts_[host] = should_whitelist; - - ListValue* whitelist_pref = - prefs_->GetMutableList(prefs::kPopupWhitelistedHosts); - if (should_whitelist) { - whitelist_.insert(host); - whitelist_pref->Append(new StringValue(host)); - - // Open the popups in order. - for (size_t j = 0; j < blocked_popups_.size(); ) { - if (blocked_popups_[j].host == host) - LaunchPopupAtIndex(j); // This shifts the rest of the entries down. - else - ++j; - } - } else { - // Remove from whitelist. - whitelist_.erase(host); - StringValue host_as_string(host); - whitelist_pref->Remove(host_as_string); - - for (UnblockedPopups::iterator i(unblocked_popups_.begin()); - i != unblocked_popups_.end(); ) { - TabContents* tab_contents = i->first; - TabContentsDelegate* delegate = tab_contents->delegate(); - if ((i->second == host) && delegate->IsPopup(tab_contents)) { - // Convert the popup back into a blocked popup. - delegate->DetachContents(tab_contents); - tab_contents->set_delegate(this); - - // Add the popup to the blocked list. (Do this before the below call!) - gfx::Rect bounds; - tab_contents->GetContainerBounds(&bounds); - blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); - - // Remove the popup from the unblocked list. - UnblockedPopups::iterator to_erase = i; - ++i; - EraseDataForPopupAndUpdateUI(to_erase); - } else { - ++i; - } - } - } -} - -void BlockedPopupContainer::CloseAll() { - ClearData(); - HideSelf(); -} - -// Overridden from TabContentsDelegate: -void BlockedPopupContainer::OpenURLFromTab(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition) { - owner_->OpenURL(url, referrer, disposition, transition); -} - -void BlockedPopupContainer::AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_position, - bool user_gesture) { - owner_->AddNewContents(new_contents, disposition, initial_position, - user_gesture, GURL()); -} - -void BlockedPopupContainer::CloseContents(TabContents* source) { - for (BlockedPopups::iterator it = blocked_popups_.begin(); - it != blocked_popups_.end(); ++it) { - TabContents* tab_contents = it->tab_contents; - if (tab_contents == source) { - tab_contents->set_delegate(NULL); - EraseDataForPopupAndUpdateUI(it); - delete tab_contents; - break; - } - } -} - -void BlockedPopupContainer::MoveContents(TabContents* source, - const gfx::Rect& new_bounds) { - for (BlockedPopups::iterator it = blocked_popups_.begin(); - it != blocked_popups_.end(); ++it) { - if (it->tab_contents == source) { - it->bounds = new_bounds; - break; - } - } -} - -bool BlockedPopupContainer::IsPopup(TabContents* source) { - return true; -} - -TabContents* BlockedPopupContainer::GetConstrainingContents( - TabContents* source) { - return owner_; -} - -ExtensionFunctionDispatcher* BlockedPopupContainer:: - CreateExtensionFunctionDispatcher(RenderViewHost* render_view_host, - const std::string& extension_id) { - return new ExtensionFunctionDispatcher(render_view_host, NULL, extension_id); -} - -void BlockedPopupContainer::HideSelf() { - owner_->PopupNotificationVisibilityChanged(false); -} - -void BlockedPopupContainer::ClearData() { - for (BlockedPopups::iterator i(blocked_popups_.begin()); - i != blocked_popups_.end(); ++i) { - TabContents* tab_contents = i->tab_contents; - tab_contents->set_delegate(NULL); - delete tab_contents; - } - blocked_popups_.clear(); - - registrar_.RemoveAll(); - unblocked_popups_.clear(); - - popup_hosts_.clear(); -} - -BlockedPopupContainer::PopupHosts::const_iterator - BlockedPopupContainer::ConvertHostIndexToIterator(size_t index) const { - if (index >= popup_hosts_.size()) - return popup_hosts_.end(); - // If only there was a std::map::const_iterator::operator +=() ... - PopupHosts::const_iterator i(popup_hosts_.begin()); - for (size_t j = 0; j < index; ++j) - ++i; - return i; -} - -void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( - BlockedPopups::iterator i) { - // Erase the host if this is the last popup for that host. - const std::string& host = i->host; - if (!host.empty()) { - bool should_erase_host = true; - for (BlockedPopups::const_iterator j(blocked_popups_.begin()); - j != blocked_popups_.end(); ++j) { - if ((j != i) && (j->host == host)) { - should_erase_host = false; - break; - } - } - if (should_erase_host) { - for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); - j != unblocked_popups_.end(); ++j) { - if (j->second == host) { - should_erase_host = false; - break; - } - } - if (should_erase_host) - popup_hosts_.erase(host); - } - } - - // Erase the popup and update the UI. - blocked_popups_.erase(i); - UpdateLabel(); -} - -void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( - UnblockedPopups::iterator i) { - // Stop listening for this popup's destruction. - registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(i->first)); - - // Erase the host if this is the last popup for that host. - const std::string& host = i->second; - if (!host.empty()) { - bool should_erase_host = true; - for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); - j != unblocked_popups_.end(); ++j) { - if ((j != i) && (j->second == host)) { - should_erase_host = false; - break; - } - } - if (should_erase_host) { - for (BlockedPopups::const_iterator j(blocked_popups_.begin()); - j != blocked_popups_.end(); ++j) { - if (j->host == host) { - should_erase_host = false; - break; - } - } - if (should_erase_host) - popup_hosts_.erase(host); - } - } - - // Erase the popup and update the UI. - unblocked_popups_.erase(i); - UpdateLabel(); -} - - -// private: - -BlockedPopupContainer::BlockedPopupContainer(TabContents* owner, - PrefService* prefs) - : owner_(owner), - prefs_(prefs), - has_been_dismissed_(false) { - // Copy whitelist pref into local member that's easier to use. - const ListValue* whitelist_pref = - prefs_->GetList(prefs::kPopupWhitelistedHosts); - // Careful: The returned value could be NULL if the pref has never been set. - if (whitelist_pref != NULL) { - for (ListValue::const_iterator i(whitelist_pref->begin()); - i != whitelist_pref->end(); ++i) { - std::string host; - (*i)->GetAsString(&host); - whitelist_.insert(host); - } - } -} - -void BlockedPopupContainer::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); - TabContents* tab_contents = Source<TabContents>(source).ptr(); - UnblockedPopups::iterator i(unblocked_popups_.find(tab_contents)); - DCHECK(i != unblocked_popups_.end()); - EraseDataForPopupAndUpdateUI(i); -} diff --git a/chrome/browser/blocked_popup_container.h b/chrome/browser/blocked_popup_container.h deleted file mode 100644 index f9b5d14..0000000 --- a/chrome/browser/blocked_popup_container.h +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this -// source code is governed by a BSD-style license that can be found in the -// LICENSE file. - -// Defines the public interface for the blocked popup notifications. This -// interface should only be used by TabContents. Users and subclasses of -// TabContents should use the appropriate methods on TabContents to access -// information about blocked popups. - -#ifndef CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ -#define CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ - -#include <map> -#include <set> - -#include "base/gfx/native_widget_types.h" -#include "base/gfx/rect.h" -#include "chrome/browser/tab_contents/constrained_window.h" -#include "chrome/browser/tab_contents/tab_contents_delegate.h" -#include "chrome/common/notification_registrar.h" - -class BlockedPopupContainerImpl; -class PrefService; -class Profile; -class TabContents; - -// Takes ownership of TabContents that are unrequested popup windows and -// presents an interface to the user for launching them. (Or never showing them -// again). This class contains all the cross-platform bits that can be used in -// all ports. -// -// Currently, BlockedPopupContainer only exists as a cross platform model -// extracted from browser/views/blocked_popup_container.cc. This is what it -// used to look like before: -// -// +- BlockedPopupContainer -------------------------------+ -// | <views::WidgetWin> | -// | All model logic. | -// | All views code. | -// +-------------------------------------------------------+ -// -// As of now, it looks like this: -// -// +- BlockedPopupContainer -------------------------------+ -// | All model logic | -// +-------------------------------------------------------+ -// ^ -// | -// +- BlockedPopupContainerImpl ---------------------------+ -// | views::WidgetWin | -// | All views code. | -// | | -// +-------------------------------------------------------+ -// -// TODO(erg): While it is not in this state yet, I want it to look like this: -// -// +- BlockedPopupContainer ---+ +- BlockedPopupContainerView -----+ -// | All model logic | +--->| Abstract cross platform | -// | | | | interface | -// | | | | | -// | Owns a platform view_ +----+ | | -// +---------------------------+ +---------------------------------+ -// ^ -// | -// +-------------------------------+-----------+ -// | | -// +- BPCViewGtk -----------+ +- BPCViewWin ----------------------+ -// | Gtk UI | | Views UI | -// | | | | -// +------------------------+ +-----------------------------------+ -// -// Getting here will take multiple patches. -class BlockedPopupContainer : public TabContentsDelegate, - public NotificationObserver { - public: - // Creates a BlockedPopupContainer, anchoring the container to the lower - // right corner. - static BlockedPopupContainer* Create( - TabContents* owner, Profile* profile, const gfx::Point& initial_anchor); - - static void RegisterUserPrefs(PrefService* prefs); - - // Adds a popup to this container. |bounds| are the window bounds requested by - // the popup window. - void AddTabContents(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host); - - // Shows the blocked popup at index |index|. - void LaunchPopupAtIndex(size_t index); - - // Returns the number of blocked popups - size_t GetBlockedPopupCount() const; - - // Returns true if host |index| is whitelisted. Returns false if |index| is - // invalid. - bool IsHostWhitelisted(size_t index) const; - - // If host |index| is currently whitelisted, un-whitelists it. Otherwise, - // whitelists it and opens all blocked popups from it. - void ToggleWhitelistingForHost(size_t index); - - // Deletes all popups and hides the interface parts. - void CloseAll(); - - // Sets this object up to delete itself. - virtual void Destroy() = 0; - - // Message called when a BlockedPopupContainer should move itself to the - // bottom right corner of |view|. - virtual void RepositionBlockedPopupContainer(gfx::NativeView view) = 0; - - // Called to force this container to never show itself again. - void set_dismissed() { has_been_dismissed_ = true; } - - // Overridden from TabContentsDelegate: - - // Forwards OpenURLFromTab to our |owner_|. - virtual void OpenURLFromTab(TabContents* source, - const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition); - - // Ignored; BlockedPopupContainer doesn't display a throbber. - virtual void NavigationStateChanged(const TabContents* source, - unsigned changed_flags) { } - - // Forwards AddNewContents to our |owner_|. - virtual void AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_position, - bool user_gesture); - - // Ignore activation requests from the TabContents we're blocking. - virtual void ActivateContents(TabContents* contents) { } - - // Ignored; BlockedPopupContainer doesn't display a throbber. - virtual void LoadingStateChanged(TabContents* source) { } - - // Removes |source| from our internal list of blocked popups. - virtual void CloseContents(TabContents* source); - - // Changes the opening rectangle associated with |source|. - virtual void MoveContents(TabContents* source, const gfx::Rect& new_bounds); - - // Always returns true. - virtual bool IsPopup(TabContents* source); - - // Returns our |owner_|. - virtual TabContents* GetConstrainingContents(TabContents* source); - - // Ignored; BlockedPopupContainer doesn't display a toolbar. - virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) { } - - // Ignored; BlockedPopupContainer doesn't display a bookmarking star. - virtual void URLStarredChanged(TabContents* source, bool starred) { } - - // Ignored; BlockedPopupContainer doesn't display a URL bar. - virtual void UpdateTargetURL(TabContents* source, const GURL& url) { } - - // Creates an ExtensionFunctionDispatcher that has no browser - virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( - RenderViewHost* render_view_host, - const std::string& extension_id); - - // A number larger than the internal popup count on the Renderer; meant for - // preventing a compromised renderer from exhausting GDI memory by spawning - // infinite windows. - static const size_t kImpossibleNumberOfPopups = 30; - - protected: - struct BlockedPopup { - BlockedPopup(TabContents* tab_contents, - const gfx::Rect& bounds, - const std::string& host) - : tab_contents(tab_contents), bounds(bounds), host(host) { - } - - TabContents* tab_contents; - gfx::Rect bounds; - std::string host; - }; - typedef std::vector<BlockedPopup> BlockedPopups; - - // TabContents is the popup contents. string is opener hostname. - typedef std::map<TabContents*, std::string> UnblockedPopups; - - // string is hostname. bool is whitelisted status. - typedef std::map<std::string, bool> PopupHosts; - - // Shows the UI. - virtual void ShowSelf() = 0; - - // Hides the UI portion of the container. - virtual void HideSelf(); - - // Updates the text on the label on the notification. - virtual void UpdateLabel() = 0; - - // Deletes all local state. - virtual void ClearData(); - - // Helper function to convert a host index (which the view uses) into an - // iterator into |popup_hosts_|. Returns popup_hosts_.end() if |index| is - // invalid. - PopupHosts::const_iterator ConvertHostIndexToIterator(size_t index) const; - - // Removes the popup at |i| from the blocked popup list. If this popup's host - // is not otherwised referenced on either popup list, removes the host from - // the host list. Updates the view's label to match the new state. - void EraseDataForPopupAndUpdateUI(BlockedPopups::iterator i); - - // Same as above, but works on the unblocked popup list. - void EraseDataForPopupAndUpdateUI(UnblockedPopups::iterator i); - - private: - friend class BlockedPopupContainerImpl; - - // string is hostname. - typedef std::set<std::string> Whitelist; - - // Creates a container for a certain TabContents: - BlockedPopupContainer(TabContents* owner, PrefService* prefs); - - // Overridden from notificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // The TabContents that owns and constrains this BlockedPopupContainer. - TabContents* owner_; - - // The PrefService we can query to find out what's on the whitelist. - PrefService* prefs_; - - // Once the container is hidden, this is set to prevent it from reappearing. - bool has_been_dismissed_; - - // Registrar to handle notifications we care about. - NotificationRegistrar registrar_; - - // The whitelisted hosts, which we allow to open popups directly. - Whitelist whitelist_; - - // Information about all blocked popups. - BlockedPopups blocked_popups_; - - // Information about all unblocked popups. - UnblockedPopups unblocked_popups_; - - // Information about all popup hosts. - PopupHosts popup_hosts_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockedPopupContainer); -}; - -#endif // CHROME_BROWSER_BLOCKED_POPUP_CONTAINER_H_ diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index bf7d2df..d11b0dc 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -2669,14 +2669,6 @@ </File> </Filter> <File - RelativePath=".\blocked_popup_container.cc" - > - </File> - <File - RelativePath=".\blocked_popup_container.h" - > - </File> - <File RelativePath=".\browser_trial.cc" > </File> diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index 509b38f..db05c00 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -11,7 +11,6 @@ #include "base/string16.h" #include "base/time.h" #include "chrome/browser/autofill_manager.h" -#include "chrome/browser/blocked_popup_container.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/cert_store.h" @@ -58,6 +57,7 @@ #if defined(OS_WIN) // TODO(port): some of these headers should be ported. #include "chrome/browser/modal_html_dialog_delegate.h" +#include "chrome/browser/views/blocked_popup_container.h" #include "views/controls/scrollbar/native_scroll_bar.h" #endif @@ -315,9 +315,6 @@ TabContents::~TabContents() { window->CloseConstrainedWindow(); } - if (blocked_popups_) - blocked_popups_->Destroy(); - // Notify any observer that have a reference on this tab contents. NotificationService::current()->Notify( NotificationType::TAB_CONTENTS_DESTROYED, @@ -827,10 +824,12 @@ void TabContents::AddNewContents(TabContents* new_contents, #endif } +#if defined(OS_WIN) void TabContents::CloseAllSuppressedPopups() { if (blocked_popups_) blocked_popups_->CloseAll(); } +#endif void TabContents::PopupNotificationVisibilityChanged(bool visible) { render_view_host()->PopupNotificationVisibilityChanged(visible); @@ -1007,12 +1006,18 @@ void TabContents::WillClose(ConstrainedWindow* window) { find(child_windows_.begin(), child_windows_.end(), window); if (it != child_windows_.end()) child_windows_.erase(it); -} -void TabContents::WillCloseBlockedPopupContainer( - BlockedPopupContainer* container) { - DCHECK(blocked_popups_ == container); - blocked_popups_ = NULL; +#if defined(OS_WIN) + if (window == blocked_popups_) + blocked_popups_ = NULL; + + if (::IsWindow(GetNativeView())) { + CRect client_rect; + GetClientRect(GetNativeView(), &client_rect); + RepositionSupressedPopupsToFit( + gfx::Size(client_rect.Width(), client_rect.Height())); + } +#endif } void TabContents::DidMoveOrResize(ConstrainedWindow* window) { @@ -1176,6 +1181,7 @@ void TabContents::CreateBlockedPopupContainerIfNecessary() { client_rect.Height()); blocked_popups_ = BlockedPopupContainer::Create(this, profile(), anchor_position); + child_windows_.push_back(blocked_popups_); } void TabContents::AddPopup(TabContents* new_contents, @@ -1184,18 +1190,28 @@ void TabContents::AddPopup(TabContents* new_contents, CreateBlockedPopupContainerIfNecessary(); blocked_popups_->AddTabContents(new_contents, initial_pos, host); } -#endif // TODO(brettw) This should be on the TabContentsView. -void TabContents::RepositionSupressedPopupsToFit() { +void TabContents::RepositionSupressedPopupsToFit(const gfx::Size& new_size) { + // TODO(erg): There's no way to detect whether scroll bars are + // visible, so for beta, we're just going to assume that the + // vertical scroll bar is visible, and not care about covering up + // the horizontal scroll bar. Fixing this is half of + // http://b/1118139. + gfx::Point anchor_position( + new_size.width() - + views::NativeScrollBar::GetVerticalScrollBarWidth(), + new_size.height()); + if (blocked_popups_) - blocked_popups_->RepositionBlockedPopupContainer(GetNativeView()); + blocked_popups_->RepositionConstrainedWindowTo(anchor_position); } bool TabContents::ShowingBlockedPopupNotification() const { return blocked_popups_ != NULL && blocked_popups_->GetBlockedPopupCount() != 0; } +#endif // defined(OS_WIN) namespace { bool TransitionIsReload(PageTransition::Type transition) { diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index c766f56..c43a919 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -432,9 +432,6 @@ class TabContents : public PageNavigator, // Called when a ConstrainedWindow we own is about to be closed. void WillClose(ConstrainedWindow* window); - // Called when a BlockedPopupContainer we own is about to be closed. - void WillCloseBlockedPopupContainer(BlockedPopupContainer* container); - // Called when a ConstrainedWindow we own is moved or resized. void DidMoveOrResize(ConstrainedWindow* window); @@ -577,10 +574,6 @@ class TabContents : public PageNavigator, render_view_host()->WindowMoveOrResizeStarted(); } - BlockedPopupContainer* blocked_popup_container() const { - return blocked_popups_; - } - private: friend class NavigationController; // Used to access the child_windows_ (ConstrainedWindowList) for testing @@ -641,7 +634,7 @@ class TabContents : public PageNavigator, // Called by a derived class when the TabContents is resized, causing // suppressed constrained web popups to be repositioned to the new bounds // if necessary. - void RepositionSupressedPopupsToFit(); + void RepositionSupressedPopupsToFit(const gfx::Size& new_size); // Whether we have a notification AND the notification owns popups windows. // (We keep the notification object around even when it's not shown since it @@ -1006,7 +999,8 @@ class TabContents : public PageNavigator, bool shelf_visible_; // ConstrainedWindow with additional methods for managing blocked - // popups. + // popups. This pointer also goes in |child_windows_| for ownership, + // repositioning, etc. BlockedPopupContainer* blocked_popups_; // Delegates for InfoBars associated with this TabContents. diff --git a/chrome/browser/tab_contents/tab_contents_view_win.cc b/chrome/browser/tab_contents/tab_contents_view_win.cc index 6b673e8..fac170d 100644 --- a/chrome/browser/tab_contents/tab_contents_view_win.cc +++ b/chrome/browser/tab_contents/tab_contents_view_win.cc @@ -570,7 +570,7 @@ void TabContentsViewWin::WasSized(const gfx::Size& size) { tab_contents()->render_widget_host_view()->SetSize(size); // TODO(brettw) this function can probably be moved to this class. - tab_contents()->RepositionSupressedPopupsToFit(); + tab_contents()->RepositionSupressedPopupsToFit(size); } bool TabContentsViewWin::ScrollZoom(int scroll_type) { diff --git a/chrome/browser/views/blocked_popup_container.cc b/chrome/browser/views/blocked_popup_container.cc index 38fa3a5..fba9780 100644 --- a/chrome/browser/views/blocked_popup_container.cc +++ b/chrome/browser/views/blocked_popup_container.cc @@ -11,7 +11,6 @@ #include "chrome/browser/views/blocked_popup_container.h" #include <math.h> -#include <windows.h> #include "app/gfx/canvas.h" #include "app/gfx/path.h" @@ -22,14 +21,19 @@ #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/background.h" #include "views/controls/button/image_button.h" -#include "views/controls/scrollbar/native_scroll_bar.h" - namespace { +// A number larger than the internal popup count on the Renderer; meant for +// preventing a compromised renderer from exhausting GDI memory by spawning +// infinite windows. +const int kImpossibleNumberOfPopups = 30; + // The minimal border around the edge of the notification. const int kSmallPadding = 2; @@ -74,10 +78,10 @@ const SkScalar kRoundedCornerRad[8] = { 0 }; -} // namespace +} BlockedPopupContainerView::BlockedPopupContainerView( - BlockedPopupContainerImpl* container) + BlockedPopupContainer* container) : container_(container) { ResourceBundle &resource_bundle = ResourceBundle::GetSharedInstance(); @@ -196,8 +200,7 @@ void BlockedPopupContainerView::ButtonPressed(views::Button* sender) { if (!hosts.empty() && (popup_count > 0)) launch_menu_->AppendSeparator(); for (size_t i = 0; i < hosts.size(); ++i) { - launch_menu_->AppendMenuItem( - BlockedPopupContainer::kImpossibleNumberOfPopups + i + 1, + launch_menu_->AppendMenuItem(kImpossibleNumberOfPopups + i + 1, l10n_util::GetStringF(IDS_POPUP_HOST_FORMAT, hosts[i]), views::Menu::NORMAL); } @@ -212,9 +215,9 @@ void BlockedPopupContainerView::ButtonPressed(views::Button* sender) { } bool BlockedPopupContainerView::IsItemChecked(int id) const { - if (id > BlockedPopupContainer::kImpossibleNumberOfPopups) { + if (id > kImpossibleNumberOfPopups) { return container_->IsHostWhitelisted(static_cast<size_t>( - id - BlockedPopupContainer::kImpossibleNumberOfPopups - 1)); + id - kImpossibleNumberOfPopups - 1)); } return false; @@ -223,29 +226,114 @@ bool BlockedPopupContainerView::IsItemChecked(int id) const { void BlockedPopupContainerView::ExecuteCommand(int id) { DCHECK_GT(id, 0); size_t id_size_t = static_cast<size_t>(id); - if (id_size_t > BlockedPopupContainer::kImpossibleNumberOfPopups) { + if (id_size_t > kImpossibleNumberOfPopups) { // Decrement id since all index based commands have 1 added to them. (See // ButtonPressed() for detail). container_->ToggleWhitelistingForHost( - id_size_t - BlockedPopupContainer::kImpossibleNumberOfPopups - 1); + id_size_t - kImpossibleNumberOfPopups - 1); } else { container_->LaunchPopupAtIndex(id_size_t - 1); } } -BlockedPopupContainerImpl::~BlockedPopupContainerImpl() { +BlockedPopupContainer::~BlockedPopupContainer() { +} + +// static +void BlockedPopupContainer::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterListPref(prefs::kPopupWhitelistedHosts); } // static BlockedPopupContainer* BlockedPopupContainer::Create( TabContents* owner, Profile* profile, const gfx::Point& initial_anchor) { - BlockedPopupContainerImpl* container = - new BlockedPopupContainerImpl(owner, profile->GetPrefs()); + BlockedPopupContainer* container = + new BlockedPopupContainer(owner, profile->GetPrefs()); container->Init(initial_anchor); return container; } -void BlockedPopupContainerImpl::GetURLAndTitleForPopup(size_t index, +void BlockedPopupContainer::AddTabContents(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host) { + // Show whitelisted popups immediately. + bool whitelisted = !!whitelist_.count(host); + if (whitelisted) + owner_->AddNewContents(tab_contents, NEW_POPUP, bounds, true, GURL()); + + if (has_been_dismissed_) { + // Don't want to show any other UI. + if (!whitelisted) + delete tab_contents; // Discard blocked popups entirely. + return; + } + + if (whitelisted) { + // Listen for this popup's destruction, so if the user closes it manually, + // we'll know to stop caring about it. + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents)); + + unblocked_popups_[tab_contents] = host; + } else { + if (blocked_popups_.size() >= kImpossibleNumberOfPopups) { + delete tab_contents; + LOG(INFO) << "Warning: Renderer is sending more popups to us than should " + "be possible. Renderer compromised?"; + return; + } + blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); + + tab_contents->set_delegate(this); + } + + PopupHosts::const_iterator i(popup_hosts_.find(host)); + if (i == popup_hosts_.end()) + popup_hosts_[host] = whitelisted; + else + DCHECK_EQ(whitelisted, i->second); + + // Update UI. + container_view_->UpdateLabel(); + SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + if (!Animation::IsAnimating() && visibility_percentage_ < 1.0) { + in_show_animation_ = true; + Animation::SetDuration(kShowAnimationDurationMS); + Animation::Start(); + } + owner_->PopupNotificationVisibilityChanged(true); +} + +void BlockedPopupContainer::LaunchPopupAtIndex(size_t index) { + if (index >= blocked_popups_.size()) + return; + + // Open the popup. + BlockedPopups::iterator i(blocked_popups_.begin() + index); + TabContents* tab_contents = i->tab_contents; + tab_contents->set_delegate(NULL); + owner_->AddNewContents(tab_contents, NEW_POPUP, i->bounds, true, GURL()); + + const std::string& host = i->host; + if (!host.empty()) { + // Listen for this popup's destruction, so if the user closes it manually, + // we'll know to stop caring about it. + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents)); + + // Add the popup to the unblocked list. (Do this before the below call!) + unblocked_popups_[tab_contents] = i->host; + } + + // Remove the popup from the blocked list. + EraseDataForPopupAndUpdateUI(i); +} + +size_t BlockedPopupContainer::GetBlockedPopupCount() const { + return blocked_popups_.size(); +} + +void BlockedPopupContainer::GetURLAndTitleForPopup(size_t index, std::wstring* url, std::wstring* title) const { DCHECK(url); @@ -256,7 +344,7 @@ void BlockedPopupContainerImpl::GetURLAndTitleForPopup(size_t index, *title = UTF16ToWideHack(tab_contents->GetTitle()); } -std::vector<std::wstring> BlockedPopupContainerImpl::GetHosts() const { +std::vector<std::wstring> BlockedPopupContainer::GetHosts() const { std::vector<std::wstring> hosts; for (PopupHosts::const_iterator i(popup_hosts_.begin()); i != popup_hosts_.end(); ++i) @@ -264,61 +352,187 @@ std::vector<std::wstring> BlockedPopupContainerImpl::GetHosts() const { return hosts; } +bool BlockedPopupContainer::IsHostWhitelisted(size_t index) const { + PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); + return (i == popup_hosts_.end()) ? false : i->second; +} + +void BlockedPopupContainer::ToggleWhitelistingForHost(size_t index) { + PopupHosts::const_iterator i(ConvertHostIndexToIterator(index)); + const std::string& host = i->first; + bool should_whitelist = !i->second; + popup_hosts_[host] = should_whitelist; + + ListValue* whitelist_pref = + prefs_->GetMutableList(prefs::kPopupWhitelistedHosts); + if (should_whitelist) { + whitelist_.insert(host); + whitelist_pref->Append(new StringValue(host)); + + // Open the popups in order. + for (size_t j = 0; j < blocked_popups_.size(); ) { + if (blocked_popups_[j].host == host) + LaunchPopupAtIndex(j); // This shifts the rest of the entries down. + else + ++j; + } + } else { + // Remove from whitelist. + whitelist_.erase(host); + whitelist_pref->Remove(StringValue(host)); + + for (UnblockedPopups::iterator i(unblocked_popups_.begin()); + i != unblocked_popups_.end(); ) { + TabContents* tab_contents = i->first; + TabContentsDelegate* delegate = tab_contents->delegate(); + if ((i->second == host) && delegate->IsPopup(tab_contents)) { + // Convert the popup back into a blocked popup. + delegate->DetachContents(tab_contents); + tab_contents->set_delegate(this); + + // Add the popup to the blocked list. (Do this before the below call!) + gfx::Rect bounds; + tab_contents->GetContainerBounds(&bounds); + blocked_popups_.push_back(BlockedPopup(tab_contents, bounds, host)); + + // Remove the popup from the unblocked list. + i = EraseDataForPopupAndUpdateUI(i); + } else { + ++i; + } + } + } +} + +void BlockedPopupContainer::CloseAll() { + ClearData(); + HideSelf(); +} + // Overridden from ConstrainedWindow: -void BlockedPopupContainerImpl::Destroy() { +void BlockedPopupContainer::CloseConstrainedWindow() { ClearData(); + + // Broadcast to all observers of NOTIFY_CWINDOW_CLOSED. + // One example of such an observer is AutomationCWindowTracker in the + // automation component. + NotificationService::current()->Notify(NotificationType::CWINDOW_CLOSED, + Source<ConstrainedWindow>(this), + NotificationService::NoDetails()); + Close(); } -void BlockedPopupContainerImpl::RepositionBlockedPopupContainer( - gfx::NativeView view) { - if (::IsWindow(view)) { - CRect client_rect; - ::GetClientRect(view, &client_rect); +void BlockedPopupContainer::RepositionConstrainedWindowTo( + const gfx::Point& anchor_point) { + anchor_point_ = anchor_point; + SetPosition(); +} - // TODO(erg): There's no way to detect whether scroll bars are - // visible, so for beta, we're just going to assume that the - // vertical scroll bar is visible, and not care about covering up - // the horizontal scroll bar. Fixing this is half of - // http://b/1118139. - gfx::Point anchor_position( - client_rect.Width() - - views::NativeScrollBar::GetVerticalScrollBarWidth(), - client_rect.Height()); +// Overridden from TabContentsDelegate: +void BlockedPopupContainer::OpenURLFromTab(TabContents* source, + const GURL& url, + const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition) { + owner_->OpenURL(url, referrer, disposition, transition); +} - RepositionWindowTo(anchor_position); +void BlockedPopupContainer::AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_position, + bool user_gesture) { + owner_->AddNewContents(new_contents, disposition, initial_position, + user_gesture, GURL()); +} + +void BlockedPopupContainer::CloseContents(TabContents* source) { + for (BlockedPopups::iterator it = blocked_popups_.begin(); + it != blocked_popups_.end(); ++it) { + TabContents* tab_contents = it->tab_contents; + if (tab_contents == source) { + tab_contents->set_delegate(NULL); + EraseDataForPopupAndUpdateUI(it); + delete tab_contents; + break; + } + } +} + +void BlockedPopupContainer::MoveContents(TabContents* source, + const gfx::Rect& new_bounds) { + for (BlockedPopups::iterator it = blocked_popups_.begin(); + it != blocked_popups_.end(); ++it) { + if (it->tab_contents == source) { + it->bounds = new_bounds; + break; + } } } +bool BlockedPopupContainer::IsPopup(TabContents* source) { + return true; +} + +TabContents* BlockedPopupContainer::GetConstrainingContents( + TabContents* source) { + return owner_; +} + +ExtensionFunctionDispatcher* BlockedPopupContainer:: + CreateExtensionFunctionDispatcher(RenderViewHost* render_view_host, + const std::string& extension_id) { + return new ExtensionFunctionDispatcher(render_view_host, NULL, extension_id); +} + // private: -BlockedPopupContainerImpl::BlockedPopupContainerImpl(TabContents* owner, - PrefService* prefs) - : BlockedPopupContainer(owner, prefs), - Animation(kFramerate, NULL), +BlockedPopupContainer::BlockedPopupContainer(TabContents* owner, + PrefService* prefs) + : Animation(kFramerate, NULL), + owner_(owner), + prefs_(prefs), container_view_(NULL), + has_been_dismissed_(false), in_show_animation_(false), visibility_percentage_(0) { + // Copy whitelist pref into local member that's easier to use. + const ListValue* whitelist_pref = + prefs_->GetList(prefs::kPopupWhitelistedHosts); + // Careful: The returned value could be NULL if the pref has never been set. + if (whitelist_pref != NULL) { + for (ListValue::const_iterator i(whitelist_pref->begin()); + i != whitelist_pref->end(); ++i) { + std::string host; + (*i)->GetAsString(&host); + whitelist_.insert(host); + } + } } -void BlockedPopupContainerImpl::RepositionWindowTo( - const gfx::Point& anchor_point) { - anchor_point_ = anchor_point; +void BlockedPopupContainer::AnimateToState(double state) { + visibility_percentage_ = in_show_animation_ ? state : (1 - state); SetPosition(); } -void BlockedPopupContainerImpl::AnimateToState(double state) { - visibility_percentage_ = in_show_animation_ ? state : (1 - state); - SetPosition(); +void BlockedPopupContainer::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); + TabContents* tab_contents = Source<TabContents>(source).ptr(); + UnblockedPopups::iterator i(unblocked_popups_.find(tab_contents)); + DCHECK(i != unblocked_popups_.end()); + EraseDataForPopupAndUpdateUI(i); } -void BlockedPopupContainerImpl::OnFinalMessage(HWND window) { - GetConstrainingContents(NULL)->WillCloseBlockedPopupContainer(this); +void BlockedPopupContainer::OnFinalMessage(HWND window) { + owner_->WillClose(this); ClearData(); WidgetWin::OnFinalMessage(window); } -void BlockedPopupContainerImpl::OnSize(UINT param, const CSize& size) { +void BlockedPopupContainer::OnSize(UINT param, const CSize& size) { // Set the window region so we have rounded corners on the top. SkRect rect; rect.set(0, 0, SkIntToScalar(size.cx), SkIntToScalar(size.cy)); @@ -329,41 +543,24 @@ void BlockedPopupContainerImpl::OnSize(UINT param, const CSize& size) { ChangeSize(param, size); } -void BlockedPopupContainerImpl::Init(const gfx::Point& initial_anchor) { +void BlockedPopupContainer::Init(const gfx::Point& initial_anchor) { container_view_ = new BlockedPopupContainerView(this); container_view_->SetVisible(true); set_window_style(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); - WidgetWin::Init(GetConstrainingContents(NULL)->GetNativeView(), gfx::Rect(), - false); + WidgetWin::Init(owner_->GetNativeView(), gfx::Rect(), false); SetContentsView(container_view_); - RepositionWindowTo(initial_anchor); + RepositionConstrainedWindowTo(initial_anchor); } -void BlockedPopupContainerImpl::ShowSelf() { - SetWindowPos(HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - if (!Animation::IsAnimating() && visibility_percentage_ < 1.0) { - in_show_animation_ = true; - Animation::SetDuration(kShowAnimationDurationMS); - Animation::Start(); - } -} - -void BlockedPopupContainerImpl::HideSelf() { +void BlockedPopupContainer::HideSelf() { in_show_animation_ = false; Animation::SetDuration(kHideAnimationDurationMS); Animation::Start(); - BlockedPopupContainer::HideSelf(); -} - -void BlockedPopupContainerImpl::UpdateLabel() { - if (blocked_popups_.empty() && unblocked_popups_.empty()) - HideSelf(); - else - container_view_->UpdateLabel(); + owner_->PopupNotificationVisibilityChanged(false); } -void BlockedPopupContainerImpl::SetPosition() { +void BlockedPopupContainer::SetPosition() { gfx::Size size = container_view_->GetPreferredSize(); int base_x = anchor_point_.x() - size.width(); int base_y = anchor_point_.y() - size.height(); @@ -393,3 +590,103 @@ void BlockedPopupContainerImpl::SetPosition() { SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); } } + +void BlockedPopupContainer::ClearData() { + for (BlockedPopups::iterator i(blocked_popups_.begin()); + i != blocked_popups_.end(); ++i) { + TabContents* tab_contents = i->tab_contents; + tab_contents->set_delegate(NULL); + delete tab_contents; + } + blocked_popups_.clear(); + + registrar_.RemoveAll(); + unblocked_popups_.clear(); + + popup_hosts_.clear(); +} + +BlockedPopupContainer::PopupHosts::const_iterator + BlockedPopupContainer::ConvertHostIndexToIterator(size_t index) const { + if (index >= popup_hosts_.size()) + return popup_hosts_.end(); + // If only there was a std::map::const_iterator::operator +=() ... + PopupHosts::const_iterator i(popup_hosts_.begin()); + for (size_t j = 0; j < index; ++j) + ++i; + return i; +} + +void BlockedPopupContainer::EraseDataForPopupAndUpdateUI( + BlockedPopups::iterator i) { + // Erase the host if this is the last popup for that host. + const std::string& host = i->host; + if (!host.empty()) { + bool should_erase_host = true; + for (BlockedPopups::const_iterator j(blocked_popups_.begin()); + j != blocked_popups_.end(); ++j) { + if ((j != i) && (j->host == host)) { + should_erase_host = false; + break; + } + } + if (should_erase_host) { + for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); + j != unblocked_popups_.end(); ++j) { + if (j->second == host) { + should_erase_host = false; + break; + } + } + if (should_erase_host) + popup_hosts_.erase(host); + } + } + + // Erase the popup and update the UI. + blocked_popups_.erase(i); + if (blocked_popups_.empty() && unblocked_popups_.empty()) + HideSelf(); + else + container_view_->UpdateLabel(); +} + +BlockedPopupContainer::UnblockedPopups::iterator + BlockedPopupContainer::EraseDataForPopupAndUpdateUI( + UnblockedPopups::iterator i) { + // Stop listening for this popup's destruction. + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(i->first)); + + // Erase the host if this is the last popup for that host. + const std::string& host = i->second; + if (!host.empty()) { + bool should_erase_host = true; + for (UnblockedPopups::const_iterator j(unblocked_popups_.begin()); + j != unblocked_popups_.end(); ++j) { + if ((j != i) && (j->second == host)) { + should_erase_host = false; + break; + } + } + if (should_erase_host) { + for (BlockedPopups::const_iterator j(blocked_popups_.begin()); + j != blocked_popups_.end(); ++j) { + if (j->host == host) { + should_erase_host = false; + break; + } + } + if (should_erase_host) + popup_hosts_.erase(host); + } + } + + // Erase the popup and update the UI. + UnblockedPopups::iterator next_popup = unblocked_popups_.erase(i); + if (blocked_popups_.empty() && unblocked_popups_.empty()) + HideSelf(); + else + container_view_->UpdateLabel(); + return next_popup; +} diff --git a/chrome/browser/views/blocked_popup_container.h b/chrome/browser/views/blocked_popup_container.h index 74d3504..291b0ee 100644 --- a/chrome/browser/views/blocked_popup_container.h +++ b/chrome/browser/views/blocked_popup_container.h @@ -2,6 +2,11 @@ // source code is governed by a BSD-style license that can be found in the // LICENSE file. +// Defines the public interface for the blocked popup notifications. This +// interface should only be used by TabContents. Users and subclasses of +// TabContents should use the appropriate methods on TabContents to access +// information about blocked popups. + #ifndef CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_H_ #define CHROME_BROWSER_VIEWS_BLOCKED_POPUP_CONTAINER_H_ @@ -10,17 +15,17 @@ #include <vector> #include "app/animation.h" -#include "base/gfx/native_widget_types.h" #include "base/gfx/rect.h" -#include "chrome/browser/blocked_popup_container.h" +#include "chrome/browser/tab_contents/constrained_window.h" #include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/common/notification_registrar.h" #include "views/controls/button/button.h" #include "views/controls/button/menu_button.h" #include "views/controls/menu/menu.h" #include "views/view.h" #include "views/widget/widget_win.h" -class BlockedPopupContainerImpl; +class BlockedPopupContainer; class PrefService; class Profile; class TabContents; @@ -36,7 +41,7 @@ class BlockedPopupContainerView : public views::View, public views::ButtonListener, public views::Menu::Delegate { public: - explicit BlockedPopupContainerView(BlockedPopupContainerImpl* container); + explicit BlockedPopupContainerView(BlockedPopupContainer* container); ~BlockedPopupContainerView(); // Sets the label on the menu button @@ -65,7 +70,7 @@ class BlockedPopupContainerView : public views::View, private: // Our owner and HWND parent. - BlockedPopupContainerImpl* container_; + BlockedPopupContainer* container_; // The button which brings up the popup menu. views::MenuButton* popup_count_label_; @@ -82,14 +87,32 @@ class BlockedPopupContainerView : public views::View, // Takes ownership of TabContents that are unrequested popup windows and // presents an interface to the user for launching them. (Or never showing them // again). -// -// TODO(erg): When this class goes away, whatever is replaced shouldn't -// multiply inherit. -class BlockedPopupContainerImpl : public BlockedPopupContainer, - public Animation, - public views::WidgetWin { +class BlockedPopupContainer : public Animation, + public ConstrainedWindow, + public NotificationObserver, + public TabContentsDelegate, + public views::WidgetWin { public: - virtual ~BlockedPopupContainerImpl(); + virtual ~BlockedPopupContainer(); + + static void RegisterUserPrefs(PrefService* prefs); + + // Creates a BlockedPopupContainer, anchoring the container to the lower + // right corner. + static BlockedPopupContainer* Create( + TabContents* owner, Profile* profile, const gfx::Point& initial_anchor); + + // Adds a popup to this container. |bounds| are the window bounds requested by + // the popup window. + void AddTabContents(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host); + + // Shows the blocked popup at index |index|. + void LaunchPopupAtIndex(size_t index); + + // Returns the number of blocked popups + size_t GetBlockedPopupCount() const; // Returns the URL and title for popup |index|, used to construct a string for // display. @@ -100,25 +123,127 @@ class BlockedPopupContainerImpl : public BlockedPopupContainer, // Returns the names of hosts showing popups. std::vector<std::wstring> GetHosts() const; - virtual void Destroy(); + // Returns true if host |index| is whitelisted. Returns false if |index| is + // invalid. + bool IsHostWhitelisted(size_t index) const; - virtual void RepositionBlockedPopupContainer(gfx::NativeView view); + // If host |index| is currently whitelisted, un-whitelists it. Otherwise, + // whitelists it and opens all blocked popups from it. + void ToggleWhitelistingForHost(size_t index); - private: - friend class BlockedPopupContainer; + // Deletes all popups and hides the interface parts. + void CloseAll(); - // Creates a container for a certain TabContents. - BlockedPopupContainerImpl(TabContents* owner, PrefService* prefs); + // Called to force this container to never show itself again. + void set_dismissed() { has_been_dismissed_ = true; } + + // Overridden from ConstrainedWindow: + + // Closes all of our blocked popups and then closes the BlockedPopupContainer. + virtual void CloseConstrainedWindow(); // Repositions our blocked popup notification so that the lower right corner // is at |anchor_point|. - void RepositionWindowTo(const gfx::Point& anchor_point); + virtual void RepositionConstrainedWindowTo(const gfx::Point& anchor_point); + + // A BlockedPopupContainer is part of the HWND heiarchy and therefore doesn't + // need to manually respond to hide and show events. + virtual void WasHidden() { } + virtual void DidBecomeSelected() { } + + // Debugging accessors only called from the unit tests. + virtual std::wstring GetWindowTitle() const { + return container_view_->label(); + } + virtual const gfx::Rect& GetCurrentBounds() const { return bounds_; } + + // Overridden from TabContentsDelegate: + + // Forwards OpenURLFromTab to our |owner_|. + virtual void OpenURLFromTab(TabContents* source, + const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition, + PageTransition::Type transition); + + // Ignored; BlockedPopupContainer doesn't display a throbber. + virtual void NavigationStateChanged(const TabContents* source, + unsigned changed_flags) { } + + // Forwards AddNewContents to our |owner_|. + virtual void AddNewContents(TabContents* source, + TabContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_position, + bool user_gesture); + + // Ignore activation requests from the TabContents we're blocking. + virtual void ActivateContents(TabContents* contents) { } + + // Ignored; BlockedPopupContainer doesn't display a throbber. + virtual void LoadingStateChanged(TabContents* source) { } + + // Removes |source| from our internal list of blocked popups. + virtual void CloseContents(TabContents* source); + + // Changes the opening rectangle associated with |source|. + virtual void MoveContents(TabContents* source, const gfx::Rect& new_bounds); + + // Always returns true. + virtual bool IsPopup(TabContents* source); + + // Returns our |owner_|. + virtual TabContents* GetConstrainingContents(TabContents* source); + + // Ignored; BlockedPopupContainer doesn't display a toolbar. + virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) { } + + // Ignored; BlockedPopupContainer doesn't display a bookmarking star. + virtual void URLStarredChanged(TabContents* source, bool starred) { } + + // Ignored; BlockedPopupContainer doesn't display a URL bar. + virtual void UpdateTargetURL(TabContents* source, const GURL& url) { } + + // Creates an ExtensionFunctionDispatcher that has no browser + virtual ExtensionFunctionDispatcher* CreateExtensionFunctionDispatcher( + RenderViewHost* render_view_host, + const std::string& extension_id); + + private: + struct BlockedPopup { + BlockedPopup(TabContents* tab_contents, + const gfx::Rect& bounds, + const std::string& host) + : tab_contents(tab_contents), bounds(bounds), host(host) { + } + + TabContents* tab_contents; + gfx::Rect bounds; + std::string host; + }; + typedef std::vector<BlockedPopup> BlockedPopups; + + // TabContents is the popup contents. string is opener hostname. + typedef std::map<TabContents*, std::string> UnblockedPopups; + + // string is hostname. bool is whitelisted status. + typedef std::map<std::string, bool> PopupHosts; + + // string is hostname. + typedef std::set<std::string> Whitelist; + + // Creates a container for a certain TabContents. + BlockedPopupContainer(TabContents* owner, PrefService* prefs); // Overridden from Animation: - // Changes the visibility percentage of the BlockedPopupContainerImpl. This is + // Changes the visibility percentage of the BlockedPopupContainer. This is // called while animating in or out. virtual void AnimateToState(double state); + // Overridden from notificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Overridden from views::WidgetWin: // Alerts our |owner_| that we are closing ourselves. Cleans up any remaining @@ -132,22 +257,59 @@ class BlockedPopupContainerImpl : public BlockedPopupContainer, // browser window. void Init(const gfx::Point& initial_anchor); - // Shows the UI. - virtual void ShowSelf(); - // Hides the UI portion of the container. - virtual void HideSelf(); - - virtual void UpdateLabel(); + void HideSelf(); // Sets our position, based on our |anchor_point_| and on our // |visibility_percentage_|. This method is called whenever either of those // change. void SetPosition(); + // Deletes all local state. + void ClearData(); + + // Helper function to convert a host index (which the view uses) into an + // iterator into |popup_hosts_|. Returns popup_hosts_.end() if |index| is + // invalid. + PopupHosts::const_iterator ConvertHostIndexToIterator(size_t index) const; + + // Removes the popup at |i| from the blocked popup list. If this popup's host + // is not otherwised referenced on either popup list, removes the host from + // the host list. Updates the view's label to match the new state. + void EraseDataForPopupAndUpdateUI(BlockedPopups::iterator i); + + // Same as above, but works on the unblocked popup list, and returns the + // iterator that results from calling erase(). + UnblockedPopups::iterator EraseDataForPopupAndUpdateUI( + UnblockedPopups::iterator i); + + // The TabContents that owns and constrains this BlockedPopupContainer. + TabContents* owner_; + + // The PrefService we can query to find out what's on the whitelist. + PrefService* prefs_; + + // Registrar to handle notifications we care about. + NotificationRegistrar registrar_; + + // The whitelisted hosts, which we allow to open popups directly. + Whitelist whitelist_; + + // Information about all blocked popups. + BlockedPopups blocked_popups_; + + // Information about all unblocked popups. + UnblockedPopups unblocked_popups_; + + // Information about all popup hosts. + PopupHosts popup_hosts_; + // Our associated view object. BlockedPopupContainerView* container_view_; + // Once the container is hidden, this is set to prevent it from reappearing. + bool has_been_dismissed_; + // True while animating in; false while animating out. bool in_show_animation_; @@ -160,6 +322,8 @@ class BlockedPopupContainerImpl : public BlockedPopupContainer, // The bottom right corner of where we should appear in our parent window. gfx::Point anchor_point_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(BlockedPopupContainer); }; #endif diff --git a/chrome/browser/views/constrained_window_impl_interactive_uitest.cc b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc index fcd7639..df8dd60 100644 --- a/chrome/browser/views/constrained_window_impl_interactive_uitest.cc +++ b/chrome/browser/views/constrained_window_impl_interactive_uitest.cc @@ -132,6 +132,14 @@ TEST_F(InteractiveConstrainedWindowTest, DontSpawnEndlessPopups) { scoped_refptr<TabProxy> popup_tab(popup_browser->GetTab(0)); ASSERT_TRUE(popup_tab.get()); + int constrained_window_count = 0; + ASSERT_TRUE(popup_tab->WaitForChildWindowCountToChange( + 0, &constrained_window_count, 10000)); + ASSERT_EQ(1, constrained_window_count); + scoped_refptr<ConstrainedWindowProxy> constrained_window( + popup_tab->GetConstrainedWindow(0)); + ASSERT_TRUE(constrained_window.get()); + // And now we spin, waiting to make sure that we don't spawn popup // windows endlessly. The current limit is 25, so allowing for possible race // conditions and one off errors, don't break out until we go over 30 popup @@ -143,7 +151,9 @@ TEST_F(InteractiveConstrainedWindowTest, DontSpawnEndlessPopups) { int times_slept = 0; bool continuing = true; while (continuing && popup_window_count < kMaxPopupWindows) { - ASSERT_TRUE(popup_tab->GetBlockedPopupCount(&new_popup_window_count)); + std::wstring title; + ASSERT_TRUE(constrained_window->GetTitle(&title)); + ASSERT_TRUE(ParseCountOutOfTitle(title, &new_popup_window_count)); if (new_popup_window_count == popup_window_count) { if (times_slept == 10) { continuing = false; @@ -170,6 +180,8 @@ TEST_F(InteractiveConstrainedWindowTest, WindowOpenWindowClosePopup) { ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 5000)); + PlatformThread::Sleep(1000); + // Make sure we have a blocked popup notification scoped_refptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1)); ASSERT_TRUE(popup_browser.get()); @@ -177,7 +189,14 @@ TEST_F(InteractiveConstrainedWindowTest, WindowOpenWindowClosePopup) { ASSERT_TRUE(popup_window.get()); scoped_refptr<TabProxy> popup_tab(popup_browser->GetTab(0)); ASSERT_TRUE(popup_tab.get()); - ASSERT_TRUE(popup_tab->WaitForBlockedPopupCountToChangeTo(1, 1000)); + scoped_refptr<ConstrainedWindowProxy> popup_notification( + popup_tab->GetConstrainedWindow(0)); + ASSERT_TRUE(popup_notification.get()); + std::wstring title; + ASSERT_TRUE(popup_notification->GetTitle(&title)); + int count = 0; + ASSERT_TRUE(ParseCountOutOfTitle(title, &count)); + ASSERT_EQ(1, count); // Ensure we didn't close the first popup window. ASSERT_FALSE(automation()->WaitForWindowCountToBecome(1, 3000)); @@ -195,8 +214,13 @@ TEST_F(InteractiveConstrainedWindowTest, BlockAlertFromBlockedPopup) { ASSERT_EQ(1, browser_window_count); // Ensure one blocked popup window: the popup didn't escape. + scoped_refptr<ConstrainedWindowProxy> popup_notification( + tab_->GetConstrainedWindow(0)); + ASSERT_TRUE(popup_notification.get()); + std::wstring title; + ASSERT_TRUE(popup_notification->GetTitle(&title)); int popup_count = 0; - ASSERT_TRUE(tab_->GetBlockedPopupCount(&popup_count)); + ASSERT_TRUE(ParseCountOutOfTitle(title, &popup_count)); ASSERT_EQ(1, popup_count); } |