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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/autotest_ambient_api.h"
#include <utility>
#include "ash/ambient/ambient_controller.h"
#include "ash/ambient/metrics/ambient_metrics.h"
#include "ash/ambient/model/ambient_photo_config.h"
#include "ash/ambient/ui/ambient_view_delegate.h"
#include "ash/ambient/ui/ambient_view_ids.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
class PhotoTransitionAnimationObserver : public AmbientViewDelegateObserver {
public:
PhotoTransitionAnimationObserver(int num_completions,
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceClosure on_timeout)
: num_completions_(num_completions),
on_complete_(std::move(on_complete)),
on_timeout_(std::move(on_timeout)) {
DCHECK_GT(num_completions, 0);
DCHECK_GT(timeout, base::TimeDelta());
DCHECK(on_complete_);
DCHECK(on_timeout_);
// |base::Unretained| is safe here because this timer will be abandoned in
// the destructor.
timer_.Start(FROM_HERE, timeout,
base::BindOnce(&PhotoTransitionAnimationObserver::OnTimeout,
base::Unretained(this)));
scoped_observation_.Observe(
Shell::Get()->ambient_controller()->ambient_view_delegate());
}
PhotoTransitionAnimationObserver(const PhotoTransitionAnimationObserver&) =
delete;
PhotoTransitionAnimationObserver& operator=(
const PhotoTransitionAnimationObserver&) = delete;
~PhotoTransitionAnimationObserver() override = default;
// AmbientViewDelegateObserver:
void OnMarkerHit(AmbientPhotoConfig::Marker marker) override {
if (marker != AmbientPhotoConfig::Marker::kUiCycleEnded)
return;
--num_completions_;
if (num_completions_ == 0) {
Cleanup();
std::move(on_complete_).Run();
delete this;
}
}
private:
void OnTimeout() {
Cleanup();
std::move(on_timeout_).Run();
delete this;
}
void Cleanup() {
timer_.Stop();
scoped_observation_.Reset();
}
int num_completions_;
base::OnceClosure on_complete_;
base::OnceClosure on_timeout_;
base::OneShotTimer timer_;
base::ScopedObservation<AmbientViewDelegate, AmbientViewDelegateObserver>
scoped_observation_{this};
};
// Parameters needed to complete one call to `WaitForVideoToStart()`. They get
// forwarded through the async sequence of functions below until `on_complete`
// or `on_error` is run.
struct VideoPlaybackStatusTestParams {
// Time at which the call to `WaitForVideoToStart()` was made.
base::TimeTicks start_time;
base::TimeDelta timeout;
base::OnceClosure on_complete;
base::OnceCallback<void(std::string)> on_error;
// Never null. Points to default clock if a testing clock was not provided.
raw_ptr<const base::TickClock> tick_clock;
};
void ScheduleVideoPlaybackStatusCheck(VideoPlaybackStatusTestParams params);
void OnVideoPlaybackStatusReceived(VideoPlaybackStatusTestParams params,
ambient::AmbientVideoSessionStatus status);
// Polls the ambient video view once every second to see if playback has started
// successfully. The signal it uses is the "playback_started" field in the
// video view's URL that gets set when the <video> element has started playback.
// This coincidentally is the same signal that's used for metrics purposes.
void CheckVideoPlaybackStatusForTesting(VideoPlaybackStatusTestParams params) {
CHECK(params.tick_clock);
if (params.tick_clock->NowTicks() - params.start_time >= params.timeout) {
std::move(params.on_error)
.Run("Timed out waiting for ambient video playback to start.");
return;
}
views::Widget* ambient_widget =
Shell::Get()
->GetPrimaryRootWindowController()
->ambient_widget_for_testing(); // IN-TEST
if (!ambient_widget) {
DVLOG(4) << "Ambient session not active yet";
ScheduleVideoPlaybackStatusCheck(std::move(params));
return;
}
AshWebView* video_web_view = static_cast<AshWebView*>(
ambient_widget->GetContentsView()->GetViewByID(kAmbientVideoWebView));
if (!video_web_view) {
std::move(params.on_error)
.Run(
"Video view missing from ambient widget. Video theme must be "
"inactive.");
return;
}
ambient::GetAmbientModeVideoSessionStatus(
video_web_view,
base::BindOnce(&OnVideoPlaybackStatusReceived, std::move(params)));
}
// Schedules the next polling check for whether video playback started.
void ScheduleVideoPlaybackStatusCheck(VideoPlaybackStatusTestParams params) {
static constexpr base::TimeDelta kPollingPeriod = base::Seconds(1);
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&CheckVideoPlaybackStatusForTesting, std::move(params)),
kPollingPeriod);
}
// Either runs one of the completion callbacks or schedules the next polling
// check if the the video is still loading.
void OnVideoPlaybackStatusReceived(VideoPlaybackStatusTestParams params,
ambient::AmbientVideoSessionStatus status) {
CHECK(params.on_complete && params.on_error);
switch (status) {
case ambient::AmbientVideoSessionStatus::kSuccess:
std::move(params.on_complete).Run();
break;
case ambient::AmbientVideoSessionStatus::kFailed:
std::move(params.on_error)
.Run(
"Ambient video playback failed with a hard error in the "
"webview.");
break;
case ambient::AmbientVideoSessionStatus::kLoading:
ScheduleVideoPlaybackStatusCheck(std::move(params));
break;
}
}
} // namespace
AutotestAmbientApi::AutotestAmbientApi() = default;
AutotestAmbientApi::~AutotestAmbientApi() = default;
void AutotestAmbientApi::WaitForPhotoTransitionAnimationCompleted(
int num_completions,
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceClosure on_timeout) {
new PhotoTransitionAnimationObserver(
num_completions, timeout, std::move(on_complete), std::move(on_timeout));
}
void AutotestAmbientApi::WaitForVideoToStart(
base::TimeDelta timeout,
base::OnceClosure on_complete,
base::OnceCallback<void(std::string)> on_error,
const base::TickClock* tick_clock) {
if (!tick_clock) {
tick_clock = base::DefaultTickClock::GetInstance();
}
CheckVideoPlaybackStatusForTesting({tick_clock->NowTicks(), timeout,
std::move(on_complete),
std::move(on_error), tick_clock});
}
} // namespace ash
|