blob: 76f100c141ca4b4b0111e6245267911bd3bdc5fa [file] [log] [blame]
// Copyright 2014 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 "services/ws/test_change_tracker.h"
#include <stddef.h>
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "mojo/public/cpp/bindings/map.h"
#include "services/ws/common/util.h"
#include "services/ws/ids.h"
#include "ui/base/cursor/cursor.h"
#include "ui/gfx/geometry/point_conversions.h"
namespace ws {
std::string WindowIdToString(Id id) {
return (id == 0) ? "null"
: base::StringPrintf("%" PRIu32 ",%" PRIu32,
ClientIdFromTransportId(id),
ClientWindowIdFromTransportId(id));
}
namespace {
std::string DirectionToString(mojom::OrderDirection direction) {
return direction == mojom::OrderDirection::ABOVE ? "above" : "below";
}
std::string OcclusionStateToString(
const base::Optional<mojom::OcclusionState>& occlusion_state) {
if (!occlusion_state.has_value()) {
NOTREACHED();
return "(null)";
}
switch (occlusion_state.value()) {
case mojom::OcclusionState::kUnknown:
return "UNKNOWN";
case mojom::OcclusionState::kVisible:
return "VISIBLE";
case mojom::OcclusionState::kOccluded:
return "OCCLUDED";
case mojom::OcclusionState::kHidden:
return "HIDDEN";
}
NOTREACHED();
return "UNKNOWN";
}
std::string OcclusionChangesToString(
const base::flat_map<Id, mojom::OcclusionState>& changes) {
std::string ret("{");
bool first = true;
for (const auto& change : changes) {
if (!first)
ret += ", ";
ret += std::string("{window_id=") + WindowIdToString(change.first) +
", state=" + OcclusionStateToString(change.second) + "}";
first = false;
}
ret += "}";
return ret;
}
enum class ChangeDescriptionType { ONE, TWO };
std::string ChangeToDescription(const Change& change,
ChangeDescriptionType type) {
switch (change.type) {
case CHANGE_TYPE_EMBED:
if (type == ChangeDescriptionType::ONE)
return "OnEmbed";
return base::StringPrintf("OnEmbed drawn=%s",
change.bool_value ? "true" : "false");
case CHANGE_TYPE_EMBED_FROM_TOKEN:
return base::StringPrintf("OnEmbedFromToken");
case CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED:
return base::StringPrintf("OnEmbeddedAppDisconnected window=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_UNEMBED:
return base::StringPrintf("OnUnembed window=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_CAPTURE_CHANGED:
return base::StringPrintf("OnCaptureChanged new_window=%s old_window=%s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str());
case CHANGE_TYPE_FRAME_SINK_ID_ALLOCATED:
return base::StringPrintf("OnFrameSinkIdAllocated window=%s %s",
WindowIdToString(change.window_id).c_str(),
change.frame_sink_id.ToString().c_str());
case CHANGE_TYPE_NODE_ADD_TRANSIENT_WINDOW:
return base::StringPrintf("AddTransientWindow parent = %s child = %s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str());
case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
return base::StringPrintf(
"BoundsChanged window=%s old_bounds=%s new_bounds=%s "
"local_surface_id=%s",
WindowIdToString(change.window_id).c_str(),
change.bounds.ToString().c_str(), change.bounds2.ToString().c_str(),
change.local_surface_id ? change.local_surface_id->ToString().c_str()
: "(none)");
case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
return base::StringPrintf(
"HierarchyChanged window=%s old_parent=%s new_parent=%s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str(),
WindowIdToString(change.window_id3).c_str());
case CHANGE_TYPE_NODE_REMOVE_TRANSIENT_WINDOW_FROM_PARENT:
return base::StringPrintf(
"RemoveTransientWindowFromParent parent = %s child = %s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str());
case CHANGE_TYPE_NODE_REORDERED:
return base::StringPrintf("Reordered window=%s relative=%s direction=%s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str(),
DirectionToString(change.direction).c_str());
case CHANGE_TYPE_NODE_DELETED:
return base::StringPrintf("WindowDeleted window=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_NODE_VISIBILITY_CHANGED:
return base::StringPrintf("VisibilityChanged window=%s visible=%s",
WindowIdToString(change.window_id).c_str(),
change.bool_value ? "true" : "false");
case CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED:
return base::StringPrintf("DrawnStateChanged window=%s drawn=%s",
WindowIdToString(change.window_id).c_str(),
change.bool_value ? "true" : "false");
case CHANGE_TYPE_INPUT_EVENT: {
std::string result = base::StringPrintf(
"InputEvent window=%s event_action=%d",
WindowIdToString(change.window_id).c_str(), change.event_action);
if (change.matches_event_observer)
result += " matches_event_observer";
return result;
}
case CHANGE_TYPE_OBSERVED_EVENT:
return base::StringPrintf("ObservedEvent event_action=%d",
change.event_action);
case CHANGE_TYPE_PROPERTY_CHANGED:
return base::StringPrintf("PropertyChanged window=%s key=%s value=%s",
WindowIdToString(change.window_id).c_str(),
change.property_key.c_str(),
change.property_value.c_str());
case CHANGE_TYPE_FOCUSED:
return base::StringPrintf("Focused id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_CURSOR_CHANGED:
return base::StringPrintf("CursorChanged id=%s cursor_type=%d",
WindowIdToString(change.window_id).c_str(),
static_cast<int>(change.cursor_type));
case CHANGE_TYPE_ON_CHANGE_COMPLETED:
return base::StringPrintf("ChangeCompleted id=%d success=%s",
change.change_id,
change.bool_value ? "true" : "false");
case CHANGE_TYPE_ON_TOP_LEVEL_CREATED:
return base::StringPrintf("TopLevelCreated id=%d window_id=%s drawn=%s",
change.change_id,
WindowIdToString(change.window_id).c_str(),
change.bool_value ? "true" : "false");
case CHANGE_TYPE_OPACITY:
return base::StringPrintf("OpacityChanged window_id=%s opacity=%.2f",
WindowIdToString(change.window_id).c_str(),
change.float_value);
case CHANGE_TYPE_REQUEST_CLOSE:
return "RequestClose";
case CHANGE_TYPE_TRANSFORM_CHANGED:
return base::StringPrintf("TransformChanged window_id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_DISPLAY_CHANGED:
return base::StringPrintf(
"DisplayChanged window_id=%s display_id=%s",
WindowIdToString(change.window_id).c_str(),
base::NumberToString(change.display_id).c_str());
case CHANGE_TYPE_DRAG_DROP_START:
return "DragDropStart";
case CHANGE_TYPE_DRAG_ENTER:
return base::StringPrintf("DragEnter window_id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_DRAG_OVER:
return base::StringPrintf("DragOver window_id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_DRAG_LEAVE:
return base::StringPrintf("DragLeave window_id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_COMPLETE_DROP:
return base::StringPrintf("CompleteDrop window_id=%s",
WindowIdToString(change.window_id).c_str());
case CHANGE_TYPE_DRAG_DROP_DONE:
return "DragDropDone";
case CHANGE_TYPE_TOPMOST_WINDOW_CHANGED:
return base::StringPrintf(
"TopmostWindowChanged window_id=%s window_id2=%s",
WindowIdToString(change.window_id).c_str(),
WindowIdToString(change.window_id2).c_str());
case CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED:
return base::StringPrintf(
"OnPerformDragDropCompleted id=%d success=%s action=%d",
change.change_id, change.bool_value ? "true" : "false",
change.drag_drop_action);
case CHANGE_TYPE_ON_OCCLUSION_STATES_CHANGED:
return base::StringPrintf(
"OnOcclusionStatesChanged %s",
OcclusionChangesToString(change.occlusion_changes).c_str());
}
return std::string();
}
std::string SingleChangeToDescriptionImpl(const std::vector<Change>& changes,
ChangeDescriptionType change_type) {
std::string result;
for (auto& change : changes) {
if (!result.empty())
result += "\n";
result += ChangeToDescription(change, change_type);
}
return result;
}
} // namespace
std::string ChangeToDescription(const Change& change) {
return ChangeToDescription(change, ChangeDescriptionType::ONE);
}
std::vector<std::string> ChangesToDescription1(
const std::vector<Change>& changes) {
std::vector<std::string> strings(changes.size());
for (size_t i = 0; i < changes.size(); ++i)
strings[i] = ChangeToDescription(changes[i], ChangeDescriptionType::ONE);
return strings;
}
std::string SingleChangeToDescription(const std::vector<Change>& changes) {
return SingleChangeToDescriptionImpl(changes, ChangeDescriptionType::ONE);
}
std::string SingleChangeToDescription2(const std::vector<Change>& changes) {
return SingleChangeToDescriptionImpl(changes, ChangeDescriptionType::TWO);
}
std::string SingleWindowDescription(const std::vector<TestWindow>& windows) {
if (windows.empty())
return "no windows";
std::string result;
for (const TestWindow& window : windows)
result += window.ToString();
return result;
}
std::string ChangeWindowDescription(const std::vector<Change>& changes) {
if (changes.size() != 1)
return std::string();
std::vector<std::string> window_strings(changes[0].windows.size());
for (size_t i = 0; i < changes[0].windows.size(); ++i)
window_strings[i] = "[" + changes[0].windows[i].ToString() + "]";
return base::JoinString(window_strings, ",");
}
TestWindow WindowDataToTestWindow(const mojom::WindowDataPtr& data) {
TestWindow window;
window.parent_id = data->parent_id;
window.window_id = data->window_id;
window.visible = data->visible;
window.properties = mojo::FlatMapToMap(data->properties);
window.bounds = data->bounds;
return window;
}
void WindowDatasToTestWindows(const std::vector<mojom::WindowDataPtr>& data,
std::vector<TestWindow>* test_windows) {
for (size_t i = 0; i < data.size(); ++i)
test_windows->push_back(WindowDataToTestWindow(data[i]));
}
bool ContainsChange(const std::vector<Change>& changes,
const std::string& change_description) {
for (auto& change : changes) {
if (base::MatchPattern(ChangeToDescription(change), change_description))
return true;
}
return false;
}
std::vector<Change>::const_iterator FirstChangeOfType(
const std::vector<Change>& changes,
ChangeType type) {
return std::find_if(
changes.begin(), changes.end(),
[&type](const Change& change) { return type == change.type; });
}
Change::Change() = default;
Change::Change(const Change& other) = default;
Change::~Change() = default;
TestChangeTracker::TestChangeTracker() : delegate_(nullptr) {}
TestChangeTracker::~TestChangeTracker() = default;
void TestChangeTracker::OnEmbed(mojom::WindowDataPtr root, bool drawn) {
Change change;
change.type = CHANGE_TYPE_EMBED;
change.bool_value = drawn;
change.windows.push_back(WindowDataToTestWindow(root));
AddChange(change);
}
void TestChangeTracker::OnEmbedFromToken(
mojom::WindowDataPtr root,
int64_t display_id,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
Change change;
change.type = CHANGE_TYPE_EMBED_FROM_TOKEN;
change.display_id = display_id;
change.windows.push_back(WindowDataToTestWindow(root));
AddChange(change);
}
void TestChangeTracker::OnEmbeddedAppDisconnected(Id window_id) {
Change change;
change.type = CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnWindowBoundsChanged(
Id window_id,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
Change change;
change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
change.window_id = window_id;
change.bounds = old_bounds;
change.bounds2 = new_bounds;
change.local_surface_id = local_surface_id;
AddChange(change);
}
void TestChangeTracker::OnWindowTransformChanged(Id window_id) {
Change change;
change.type = CHANGE_TYPE_TRANSFORM_CHANGED;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnUnembed(Id window_id) {
Change change;
change.type = CHANGE_TYPE_UNEMBED;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnTransientWindowAdded(Id window_id,
Id transient_window_id) {
Change change;
change.type = CHANGE_TYPE_NODE_ADD_TRANSIENT_WINDOW;
change.window_id = window_id;
change.window_id2 = transient_window_id;
AddChange(change);
}
void TestChangeTracker::OnTransientWindowRemoved(Id window_id,
Id transient_window_id) {
Change change;
change.type = CHANGE_TYPE_NODE_REMOVE_TRANSIENT_WINDOW_FROM_PARENT;
change.window_id = window_id;
change.window_id2 = transient_window_id;
AddChange(change);
}
void TestChangeTracker::OnCaptureChanged(Id new_capture_window_id,
Id old_capture_window_id) {
Change change;
change.type = CHANGE_TYPE_CAPTURE_CHANGED;
change.window_id = new_capture_window_id;
change.window_id2 = old_capture_window_id;
AddChange(change);
}
void TestChangeTracker::OnFrameSinkIdAllocated(
Id window_id,
const viz::FrameSinkId& frame_sink_id) {
Change change;
change.type = CHANGE_TYPE_FRAME_SINK_ID_ALLOCATED;
change.window_id = window_id;
change.frame_sink_id = frame_sink_id;
AddChange(change);
}
void TestChangeTracker::OnWindowHierarchyChanged(
Id window_id,
Id old_parent_id,
Id new_parent_id,
std::vector<mojom::WindowDataPtr> windows) {
Change change;
change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
change.window_id = window_id;
change.window_id2 = old_parent_id;
change.window_id3 = new_parent_id;
WindowDatasToTestWindows(windows, &change.windows);
AddChange(change);
}
void TestChangeTracker::OnWindowReordered(Id window_id,
Id relative_window_id,
mojom::OrderDirection direction) {
Change change;
change.type = CHANGE_TYPE_NODE_REORDERED;
change.window_id = window_id;
change.window_id2 = relative_window_id;
change.direction = direction;
AddChange(change);
}
void TestChangeTracker::OnWindowDeleted(Id window_id) {
Change change;
change.type = CHANGE_TYPE_NODE_DELETED;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnWindowVisibilityChanged(Id window_id, bool visible) {
Change change;
change.type = CHANGE_TYPE_NODE_VISIBILITY_CHANGED;
change.window_id = window_id;
change.bool_value = visible;
AddChange(change);
}
void TestChangeTracker::OnWindowOpacityChanged(Id window_id, float opacity) {
Change change;
change.type = CHANGE_TYPE_OPACITY;
change.window_id = window_id;
change.float_value = opacity;
AddChange(change);
}
void TestChangeTracker::OnWindowDisplayChanged(Id window_id,
int64_t display_id) {
Change change;
change.type = CHANGE_TYPE_DISPLAY_CHANGED;
change.window_id = window_id;
change.display_id = display_id;
AddChange(change);
}
void TestChangeTracker::OnWindowParentDrawnStateChanged(Id window_id,
bool drawn) {
Change change;
change.type = CHANGE_TYPE_NODE_DRAWN_STATE_CHANGED;
change.window_id = window_id;
change.bool_value = drawn;
AddChange(change);
}
void TestChangeTracker::OnWindowInputEvent(Id window_id,
const ui::Event& event,
int64_t display_id,
bool matches_event_observer) {
Change change;
change.type = CHANGE_TYPE_INPUT_EVENT;
change.window_id = window_id;
change.event_action = static_cast<int32_t>(event.type());
change.matches_event_observer = matches_event_observer;
change.display_id = display_id;
if (event.IsLocatedEvent())
change.location1 = event.AsLocatedEvent()->root_location();
if (event.IsKeyEvent() && event.AsKeyEvent()->properties())
change.key_event_properties = *event.AsKeyEvent()->properties();
AddChange(change);
}
void TestChangeTracker::OnObservedInputEvent(const ui::Event& event) {
Change change;
change.type = CHANGE_TYPE_OBSERVED_EVENT;
change.event_action = static_cast<int32_t>(event.type());
AddChange(change);
}
void TestChangeTracker::OnWindowSharedPropertyChanged(
Id window_id,
const std::string& name,
const base::Optional<std::vector<uint8_t>>& data) {
Change change;
change.type = CHANGE_TYPE_PROPERTY_CHANGED;
change.window_id = window_id;
change.property_key = name;
if (!data)
change.property_value = "NULL";
else
change.property_value = base::HexEncode(data->data(), data->size());
AddChange(change);
}
void TestChangeTracker::OnWindowFocused(Id window_id) {
Change change;
change.type = CHANGE_TYPE_FOCUSED;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnWindowCursorChanged(Id window_id,
const ui::Cursor& cursor) {
Change change;
change.type = CHANGE_TYPE_CURSOR_CHANGED;
change.window_id = window_id;
change.cursor_type = cursor.native_type();
AddChange(change);
}
void TestChangeTracker::OnChangeCompleted(uint32_t change_id, bool success) {
Change change;
change.type = CHANGE_TYPE_ON_CHANGE_COMPLETED;
change.change_id = change_id;
change.bool_value = success;
AddChange(change);
}
void TestChangeTracker::OnTopLevelCreated(uint32_t change_id,
mojom::WindowDataPtr window_data,
bool drawn) {
Change change;
change.type = CHANGE_TYPE_ON_TOP_LEVEL_CREATED;
change.change_id = change_id;
change.window_id = window_data->window_id;
change.bool_value = drawn;
AddChange(change);
}
void TestChangeTracker::OnDragDropStart(
const base::flat_map<std::string, std::vector<uint8_t>>& drag_data) {
Change change;
change.type = CHANGE_TYPE_DRAG_DROP_START;
change.drag_data = drag_data;
AddChange(change);
}
void TestChangeTracker::OnDragEnter(Id window_id) {
Change change;
change.type = CHANGE_TYPE_DRAG_ENTER;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnDragOver(Id window_id) {
Change change;
change.type = CHANGE_TYPE_DRAG_OVER;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnDragLeave(Id window_id) {
Change change;
change.type = CHANGE_TYPE_DRAG_LEAVE;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnCompleteDrop(Id window_id) {
Change change;
change.type = CHANGE_TYPE_COMPLETE_DROP;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnDragDropDone() {
Change change;
change.type = CHANGE_TYPE_DRAG_DROP_DONE;
AddChange(change);
}
void TestChangeTracker::OnTopmostWindowChanged(
const std::vector<Id>& topmost_ids) {
DCHECK_LE(topmost_ids.size(), 2u);
Change change;
change.type = CHANGE_TYPE_TOPMOST_WINDOW_CHANGED;
change.window_id =
(topmost_ids.size() > 0) ? topmost_ids[0] : kInvalidTransportId;
change.window_id2 =
(topmost_ids.size() > 1) ? topmost_ids[1] : kInvalidTransportId;
AddChange(change);
}
void TestChangeTracker::OnPerformDragDropCompleted(uint32_t change_id,
bool success,
uint32_t action_taken) {
Change change;
change.type = CHANGE_TYPE_ON_PERFORM_DRAG_DROP_COMPLETED;
change.change_id = change_id;
change.bool_value = success;
change.drag_drop_action = action_taken;
AddChange(change);
}
void TestChangeTracker::RequestClose(Id window_id) {
Change change;
change.type = CHANGE_TYPE_REQUEST_CLOSE;
change.window_id = window_id;
AddChange(change);
}
void TestChangeTracker::OnOcclusionStatesChanged(
const base::flat_map<Id, mojom::OcclusionState>& occlusion_changes) {
Change change;
change.type = CHANGE_TYPE_ON_OCCLUSION_STATES_CHANGED;
change.occlusion_changes = occlusion_changes;
AddChange(change);
}
void TestChangeTracker::AddChange(const Change& change) {
changes_.push_back(change);
if (delegate_)
delegate_->OnChangeAdded();
}
TestWindow::TestWindow() = default;
TestWindow::TestWindow(const TestWindow& other) = default;
TestWindow::~TestWindow() = default;
std::string TestWindow::ToString() const {
return base::StringPrintf("window=%s parent=%s",
WindowIdToString(window_id).c_str(),
WindowIdToString(parent_id).c_str());
}
std::string TestWindow::ToString2() const {
return base::StringPrintf(
"window=%s parent=%s visible=%s", WindowIdToString(window_id).c_str(),
WindowIdToString(parent_id).c_str(), visible ? "true" : "false");
}
} // namespace ws