Chromium Code Reviews| Index: ios/clean/chrome/browser/ui/overlays/overlay_scheduler.mm |
| diff --git a/ios/clean/chrome/browser/ui/overlays/overlay_scheduler.mm b/ios/clean/chrome/browser/ui/overlays/overlay_scheduler.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..277cdf2179e5ea1eadd3b8d0b69297767ef64e6e |
| --- /dev/null |
| +++ b/ios/clean/chrome/browser/ui/overlays/overlay_scheduler.mm |
| @@ -0,0 +1,180 @@ |
| +// Copyright 2017 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. |
| + |
| +#import "ios/clean/chrome/browser/ui/overlays/overlay_scheduler.h" |
| + |
| +#include <list> |
| + |
| +#include "base/logging.h" |
| +#import "ios/chrome/browser/web_state_list/web_state_list.h" |
| +#import "ios/clean/chrome/browser/ui/commands/tab_grid_commands.h" |
| +#import "ios/clean/chrome/browser/ui/overlays/browser_overlay_queue.h" |
| +#import "ios/clean/chrome/browser/ui/overlays/overlay_queue.h" |
| +#import "ios/clean/chrome/browser/ui/overlays/web_state_overlay_queue.h" |
| +#import "ios/web/public/web_state/web_state.h" |
| + |
| +#if !defined(__has_feature) || !__has_feature(objc_arc) |
| +#error "This file requires ARC support." |
| +#endif |
| + |
| +DEFINE_BROWSER_USER_DATA_KEY(OverlayScheduler); |
| + |
| +OverlayScheduler::OverlayScheduler(Browser* browser) |
| + : browser_(browser), observing_(false) { |
| + DCHECK(browser_); |
| + StartObservingBrowser(); |
| +} |
| + |
| +OverlayScheduler::~OverlayScheduler() {} |
| + |
| +#pragma mark - Public |
| + |
| +void OverlayScheduler::StartObservingBrowser() { |
| + if (observing_) |
| + return; |
| + BrowserOverlayQueue::CreateForBrowser(browser_); |
| + BrowserOverlayQueue::FromBrowser(browser_)->AddObserver(this); |
| + StartObservingWebStateList(&browser_->web_state_list()); |
| + observing_ = true; |
| +} |
| + |
| +void OverlayScheduler::StopObservingBrowser() { |
| + if (!observing_) |
| + return; |
| + BrowserOverlayQueue::FromBrowser(browser_)->RemoveObserver(this); |
| + StopObservingWebStateList(&browser_->web_state_list()); |
| + observing_ = false; |
| +} |
| + |
| +bool OverlayScheduler::IsShowingOverlay() const { |
| + return !overlay_queues_.empty() && |
| + overlay_queues_.front()->IsShowingOverlay(); |
| +} |
| + |
| +void OverlayScheduler::ReplaceVisibleOverlay( |
| + BrowserCoordinator* overlay_coordinator) { |
| + DCHECK(overlay_coordinator); |
| + DCHECK(IsShowingOverlay()); |
| + overlay_queues_.front()->ReplaceVisibleOverlay(overlay_coordinator); |
| +} |
| + |
| +void OverlayScheduler::CancelOverlays() { |
| + // |overlay_queues_| will be updated in OverlayQueueDidCancelOverlays(), so a |
| + // while loop is used to avoid using invalidated iterators. |
| + while (!overlay_queues_.empty()) { |
| + overlay_queues_.front()->CancelOverlays(); |
| + } |
| +} |
| + |
| +#pragma mark - OverlayQueueObserver |
| + |
| +void OverlayScheduler::OverlayQueueDidAddOverlay(OverlayQueue* queue) { |
| + DCHECK(queue); |
| + overlay_queues_.push_back(queue); |
| + TryToStartNextOverlay(); |
| +} |
| + |
| +void OverlayScheduler::OverlayQueueWillReplaceVisibleOverlay( |
| + OverlayQueue* queue) { |
| + DCHECK(queue); |
| + DCHECK_EQ(overlay_queues_.front(), queue); |
| + DCHECK(queue->IsShowingOverlay()); |
| + // An OverlayQueue's visible overlay can only be replaced if it's the first |
| + // queue in the scheduler and is already showing an overlay. The queue is |
| + // added here so that its replacement overlay can be displayed when its |
| + // currently-visible overlay is stopped. |
| + overlay_queues_.push_front(queue); |
| +} |
| + |
| +void OverlayScheduler::OverlayQueueDidStopVisibleOverlay(OverlayQueue* queue) { |
| + DCHECK(!overlay_queues_.empty()); |
| + DCHECK_EQ(overlay_queues_.front(), queue); |
| + // Only the first queue in the scheduler can start overlays, so it is expected |
| + // that this function is only called for that queue. |
| + overlay_queues_.pop_front(); |
| + TryToStartNextOverlay(); |
| +} |
| + |
| +void OverlayScheduler::OverlayQueueDidCancelOverlays(OverlayQueue* queue) { |
| + DCHECK(queue); |
| + // Remove all scheduled instances of |queue| from the |overlay_queues_|. |
| + auto i = overlay_queues_.begin(); |
| + while (i != overlay_queues_.end()) { |
| + if (*i == queue) |
| + overlay_queues_.erase(i); |
| + } |
| + // If |queue| is currently showing an overlay, prepend it to |
| + // |overlay_queues_|. It will be removed when the cancelled overlay is |
| + // stopped. |
| + if (queue->IsShowingOverlay()) |
| + overlay_queues_.push_front(queue); |
| +} |
| + |
| +#pragma mark - WebStateListObserver |
| + |
| +void OverlayScheduler::WebStateInsertedAt(WebStateList* web_state_list, |
| + web::WebState* web_state, |
| + int index) { |
| + StartObservingQueueForWebState(web_state); |
| +} |
| + |
| +void OverlayScheduler::WebStateDetachedAt(WebStateList* web_state_list, |
| + web::WebState* web_state, |
| + int index) { |
| + StopObservingQueueForWebState(web_state); |
| +} |
| + |
| +#pragma mark - |
| + |
| +void OverlayScheduler::TryToStartNextOverlay() { |
| + // Early return if an overlay is already started or if there are no queued |
| + // overlays to show. |
| + if (overlay_queues_.empty() || IsShowingOverlay()) |
| + return; |
| + // If the next queue requires a WebState's content area to be shown, switch |
| + // the active WebState before starting the next overlay. |
| + OverlayQueue* queue = overlay_queues_.front(); |
| + web::WebState* web_state = queue->GetWebState(); |
| + if (web_state) { |
| + WebStateList& web_state_list = browser_->web_state_list(); |
|
marq (ping after 24h)
2017/06/21 09:41:54
Rather than passing the whole browser into the sch
kkhorimoto
2017/06/23 06:11:16
This object is a BrowserUserData, so I don't think
marq (ping after 24h)
2017/06/23 10:42:01
Dispatcher is moving out of browser once I have ti
|
| + int new_active_index = web_state_list.GetIndexOfWebState(web_state); |
| + DCHECK_NE(new_active_index, WebStateList::kInvalidIndex); |
| + web_state_list.ActivateWebStateAt(new_active_index); |
|
marq (ping after 24h)
2017/06/21 09:41:53
We should arrive at some consensus about how activ
kkhorimoto
2017/06/23 06:11:16
As I mentioned in the meeting this morning, I thin
marq (ping after 24h)
2017/06/23 10:42:01
OK. I think I would like to start with pure model-
|
| + id<TabGridCommands> grid_dispatcher = |
| + static_cast<id<TabGridCommands>>(browser_->dispatcher()); |
| + [grid_dispatcher showTabGridTabAtIndex:new_active_index]; |
| + } |
| + // Start the next overlay in the first queue. |
| + queue->StartNextOverlay(); |
| +} |
| + |
| +void OverlayScheduler::StartObservingWebStateList( |
| + WebStateList* web_state_list) { |
| + DCHECK(web_state_list); |
| + web_state_list->AddObserver(this); |
| + for (int index = 0; index < web_state_list->count(); ++index) { |
| + StartObservingQueueForWebState(web_state_list->GetWebStateAt(index)); |
| + } |
| +} |
| + |
| +void OverlayScheduler::StopObservingWebStateList(WebStateList* web_state_list) { |
| + DCHECK(web_state_list); |
| + web_state_list->RemoveObserver(this); |
| + for (int index = 0; index < web_state_list->count(); ++index) { |
| + StopObservingQueueForWebState(web_state_list->GetWebStateAt(index)); |
| + } |
| +} |
| + |
| +void OverlayScheduler::StartObservingQueueForWebState( |
| + web::WebState* web_state) { |
| + DCHECK(web_state); |
| + WebStateOverlayQueue::CreateForWebState(web_state); |
| + WebStateOverlayQueue::FromWebState(web_state)->AddObserver(this); |
| +} |
| + |
| +void OverlayScheduler::StopObservingQueueForWebState(web::WebState* web_state) { |
| + DCHECK(web_state); |
| + WebStateOverlayQueue::FromWebState(web_state)->CancelOverlays(); |
|
marq (ping after 24h)
2017/06/21 09:41:53
If WSOQ::FromWebState(foo) returns nullptr, this w
kkhorimoto
2017/06/23 06:11:16
There is no codepath where that can occur.
|
| + WebStateOverlayQueue::FromWebState(web_state)->RemoveObserver(this); |
| +} |