// Copyright 2015 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 "components/bubble/bubble_manager.h" #include #include #include "components/bubble/bubble_controller.h" #include "components/bubble/bubble_delegate.h" BubbleManager::BubbleManager() : manager_state_(SHOW_BUBBLES) {} BubbleManager::~BubbleManager() { FinalizePendingRequests(); } BubbleReference BubbleManager::ShowBubble(scoped_ptr bubble) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(manager_state_, ITERATING_BUBBLES); DCHECK(bubble); scoped_ptr controller( new BubbleController(this, std::move(bubble))); BubbleReference bubble_ref = controller->AsWeakPtr(); switch (manager_state_) { case SHOW_BUBBLES: controller->Show(); controllers_.push_back(std::move(controller)); break; case NO_MORE_BUBBLES: FOR_EACH_OBSERVER(BubbleManagerObserver, observers_, OnBubbleNeverShown(controller->AsWeakPtr())); break; default: NOTREACHED(); break; } return bubble_ref; } bool BubbleManager::CloseBubble(BubbleReference bubble, BubbleCloseReason reason) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(manager_state_, ITERATING_BUBBLES); return CloseAllMatchingBubbles(bubble.get(), nullptr, reason); } void BubbleManager::CloseAllBubbles(BubbleCloseReason reason) { // The following close reasons don't make sense for multiple bubbles: DCHECK_NE(reason, BUBBLE_CLOSE_ACCEPTED); DCHECK_NE(reason, BUBBLE_CLOSE_CANCELED); DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(manager_state_, ITERATING_BUBBLES); CloseAllMatchingBubbles(nullptr, nullptr, reason); } void BubbleManager::UpdateAllBubbleAnchors() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(manager_state_, ITERATING_BUBBLES); // Guard against bubbles being added or removed while iterating the bubbles. ManagerState original_state = manager_state_; manager_state_ = ITERATING_BUBBLES; for (auto controller : controllers_) controller->UpdateAnchorPosition(); manager_state_ = original_state; } void BubbleManager::AddBubbleManagerObserver(BubbleManagerObserver* observer) { observers_.AddObserver(observer); } void BubbleManager::RemoveBubbleManagerObserver( BubbleManagerObserver* observer) { observers_.RemoveObserver(observer); } void BubbleManager::FinalizePendingRequests() { // Return if already "Finalized". if (manager_state_ == NO_MORE_BUBBLES) return; manager_state_ = NO_MORE_BUBBLES; CloseAllBubbles(BUBBLE_CLOSE_FORCED); } void BubbleManager::CloseBubblesOwnedBy(const content::RenderFrameHost* frame) { CloseAllMatchingBubbles(nullptr, frame, BUBBLE_CLOSE_FRAME_DESTROYED); } bool BubbleManager::CloseAllMatchingBubbles( BubbleController* bubble, const content::RenderFrameHost* owner, BubbleCloseReason reason) { // Specifying both an owning frame and a particular bubble to close doesn't // make sense. If we have a frame, all bubbles owned by that frame need to // have the opportunity to close. If we want to close a specific bubble, then // it should get the close event regardless of which frame owns it. On the // other hand, OR'ing the conditions needs a special case in order to be able // to close all bubbles, so we disallow passing both until a need appears. DCHECK(!bubble || !owner); ScopedVector close_queue; // Guard against bubbles being added or removed while iterating the bubbles. ManagerState original_state = manager_state_; manager_state_ = ITERATING_BUBBLES; for (auto i = controllers_.begin(); i != controllers_.end();) { if ((!bubble || bubble == *i) && (!owner || (*i)->OwningFrameIs(owner)) && (*i)->ShouldClose(reason)) { close_queue.push_back(*i); i = controllers_.weak_erase(i); } else { ++i; } } manager_state_ = original_state; for (auto controller : close_queue) { controller->DoClose(reason); FOR_EACH_OBSERVER(BubbleManagerObserver, observers_, OnBubbleClosed(controller->AsWeakPtr(), reason)); } return !close_queue.empty(); }