1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
// 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 <utility>
#include <vector>
#include "components/bubble/bubble_controller.h"
#include "components/bubble/bubble_delegate.h"
BubbleManager::BubbleManager() : manager_state_(SHOW_BUBBLES) {}
BubbleManager::~BubbleManager() {
FinalizePendingRequests();
}
BubbleReference BubbleManager::ShowBubble(
std::unique_ptr<BubbleDelegate> bubble) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_NE(manager_state_, ITERATING_BUBBLES);
DCHECK(bubble);
std::unique_ptr<BubbleController> 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 (auto& observer : observers_)
observer.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<BubbleController> 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 (auto& observer : observers_)
observer.OnBubbleClosed(controller->AsWeakPtr(), reason);
}
return !close_queue.empty();
}
|