// Copyright (c) 2016 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 "content/common/input/actions_parser.h" #include #include "base/format_macros.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" namespace content { namespace { SyntheticPointerActionParams::PointerActionType ToSyntheticPointerActionType( const std::string& action_type) { if (action_type == "pointerDown") return SyntheticPointerActionParams::PointerActionType::PRESS; if (action_type == "pointerMove") return SyntheticPointerActionParams::PointerActionType::MOVE; if (action_type == "pointerUp") return SyntheticPointerActionParams::PointerActionType::RELEASE; if (action_type == "pointerLeave") return SyntheticPointerActionParams::PointerActionType::LEAVE; if (action_type == "pause") return SyntheticPointerActionParams::PointerActionType::IDLE; return SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED; } SyntheticGestureParams::GestureSourceType ToSyntheticGestureSourceType( const std::string& pointer_type) { if (pointer_type == "touch") return SyntheticGestureParams::TOUCH_INPUT; else if (pointer_type == "mouse") return SyntheticGestureParams::MOUSE_INPUT; else if (pointer_type == "pen") return SyntheticGestureParams::PEN_INPUT; return SyntheticGestureParams::DEFAULT_INPUT; } SyntheticPointerActionParams::Button ToSyntheticMouseButton(int button) { if (button == 0) return SyntheticPointerActionParams::Button::LEFT; if (button == 1) return SyntheticPointerActionParams::Button::MIDDLE; if (button == 2) return SyntheticPointerActionParams::Button::RIGHT; if (button == 3) return SyntheticPointerActionParams::Button::BACK; if (button == 4) return SyntheticPointerActionParams::Button::FORWARD; NOTREACHED() << "Unexpected button"; return SyntheticPointerActionParams::Button(); } int ToKeyModifiers(const std::string& key) { if (key == "Alt") return blink::WebInputEvent::kAltKey; if (key == "Control") return blink::WebInputEvent::kControlKey; if (key == "Meta") return blink::WebInputEvent::kMetaKey; if (key == "Shift") return blink::WebInputEvent::kShiftKey; if (key == "CapsLock") return blink::WebInputEvent::kCapsLockOn; if (key == "NumLock") return blink::WebInputEvent::kNumLockOn; if (key == "AltGraph") return blink::WebInputEvent::kAltGrKey; return 0; } } // namespace ActionsParser::ActionsParser(base::Value pointer_actions_value) : longest_action_sequence_(0), pointer_actions_value_(std::move(pointer_actions_value)), action_index_(0), use_testdriver_api_(true) {} ActionsParser::~ActionsParser() {} bool ActionsParser::ParsePointerActionSequence() { if (!pointer_actions_value_.is_list()) { error_message_ = std::string("provided value is not a list"); return false; } int index = 0; for (const auto& pointer_actions : pointer_actions_value_.GetList()) { if (!pointer_actions.is_dict()) { error_message_ = std::string("pointer actions is missing or not a dictionary"); return false; } else if (!ParsePointerActions(pointer_actions, index)) { return false; } index++; if (source_type_ == "pointer" || !use_testdriver_api_) action_index_++; } gesture_params_.gesture_source_type = ToSyntheticGestureSourceType(pointer_type_); // Group a list of actions from all pointers into a // SyntheticPointerActionListParams object, which is a list of actions, which // will be dispatched together. for (size_t action_index = 0; action_index < longest_action_sequence_; ++action_index) { SyntheticPointerActionListParams::ParamList param_list; size_t longest_pause_frame = 0; for (const auto& pointer_action_list : pointer_actions_list_) { if (action_index < pointer_action_list.size()) { param_list.push_back(pointer_action_list[action_index]); if (pointer_action_list[action_index].pointer_action_type() == SyntheticPointerActionParams::PointerActionType::IDLE) { size_t num_pause_frame = static_cast(std::ceil( pointer_action_list[action_index].duration().InMilliseconds() / viz::BeginFrameArgs::DefaultInterval().InMilliseconds())); longest_pause_frame = std::max(longest_pause_frame, num_pause_frame); } } } gesture_params_.PushPointerActionParamsList(param_list); for (size_t pause_index = 1; pause_index < longest_pause_frame; ++pause_index) { SyntheticPointerActionListParams::ParamList pause_param_list; SyntheticPointerActionParams pause_action_param( SyntheticPointerActionParams::PointerActionType::IDLE); for (size_t i = 0; i < param_list.size(); ++i) { pause_param_list.push_back(pause_action_param); } gesture_params_.PushPointerActionParamsList(pause_param_list); } } return true; } bool ActionsParser::ParsePointerActions(const base::Value& pointer, int index) { int pointer_id = -1; // If the json format of each pointer has "type" element, it is from the new // Action API, otherwise it is from gpuBenchmarking.pointerActionSequence // API. We have to keep both formats for now, but later on once we switch to // the new Action API in all tests, we will remove the old format. const base::Value* type_key = pointer.FindKey("type"); if (index == 0) use_testdriver_api_ = type_key != nullptr; if (use_testdriver_api_) { DCHECK_NE(nullptr, type_key); if (!type_key->is_string()) { error_message_ = std::string("action sequence type is missing or not a string"); return false; } std::string source_type = type_key->GetString(); if (source_type == "") { error_message_ = std::string("action sequence type cannot be empty"); return false; } else if (source_type == "key") { error_message_ = std::string("we do not support action sequence type of key"); return false; } if (source_type == "pointer") { if (source_type_.empty()) source_type_ = source_type; const base::Value* parameters = pointer.FindKey("parameters"); if (source_type_ == "pointer" && !parameters) { error_message_ = std::string( "action sequence parameters is missing for pointer type"); return false; } if (!parameters->is_dict()) { error_message_ = std::string("action sequence parameters is not a dictionary"); return false; } const std::string* pointer_type = parameters->FindStringKey("pointerType"); if (!pointer_type) { error_message_ = std::string( "action sequence pointer type is missing or not a string"); return false; } else if (*pointer_type != "touch" && *pointer_type != "mouse" && *pointer_type != "pen") { error_message_ = std::string( "action sequence pointer type is an unsupported input type"); return false; } if (pointer_type_.empty()) { pointer_type_ = *pointer_type; } if (pointer_type_ != *pointer_type) { error_message_ = std::string( "currently multiple action sequence pointer type are not " "supported"); return false; } if (*pointer_type != "touch" && action_index_ > 0) { error_message_ = std::string( "for input type of mouse and pen, we only support one device"); return false; } const std::string* pointer_name = pointer.FindStringKey("id"); if (!pointer_name) { error_message_ = std::string("pointer name is missing or not a string"); return false; } if (pointer_name_set_.find(*pointer_name) != pointer_name_set_.end()) { error_message_ = std::string("pointer name already exists"); return false; } pointer_name_set_.insert(*pointer_name); pointer_id_set_.insert(action_index_); } pointer_id = action_index_; } else { DCHECK_EQ(nullptr, type_key); const std::string* pointer_type = pointer.FindStringKey("source"); if (!pointer_type) { error_message_ = std::string("source type is missing or not a string"); return false; } else if (*pointer_type != "touch" && *pointer_type != "mouse" && *pointer_type != "pen") { error_message_ = std::string("source type is an unsupported input source"); return false; } if (pointer_type_.empty()) { pointer_type_ = *pointer_type; } if (pointer_type_ != *pointer_type) { error_message_ = std::string("currently multiple input sources are not not supported"); return false; } if (*pointer_type != "touch" && action_index_ > 0) { error_message_ = std::string( "for input source type of mouse and pen, we only support one device " "in one sequence"); return false; } const base::Value* id_key = pointer.FindKey("id"); if (id_key) { if (!id_key->is_int()) { error_message_ = std::string("pointer id is not an integer"); return false; } pointer_id = id_key->GetInt(); if (pointer_id < 0) { error_message_ = std::string("pointer id can not be negative"); return false; } } if (pointer_id != -1) { if (pointer_id_set_.find(pointer_id) != pointer_id_set_.end()) { error_message_ = std::string("pointer id already exists"); return false; } if (action_index_ != static_cast(pointer_id_set_.size())) { error_message_ = std::string("some pointers do not have a pointer id"); return false; } pointer_id_set_.insert(pointer_id); } else { if (pointer_id_set_.size() > 0) { error_message_ = std::string("this pointer does not have a pointer id"); return false; } } } const base::Value* actions = pointer.FindKeyOfType("actions", base::Value::Type::LIST); if (!actions) { error_message_ = base::StringPrintf( "pointer[%d].actions is missing or not a list", action_index_); return false; } if (!ParseActions(*actions, pointer_id)) return false; return true; } bool ActionsParser::ParseActions(const base::Value& actions, int pointer_id) { SyntheticPointerActionListParams::ParamList param_list; for (const auto& action : actions.GetList()) { if (!action.is_dict()) { error_message_ = base::StringPrintf( "actions[%d].actions is missing or not a dictionary", action_index_); return false; } else if (!ParseAction(action, param_list, pointer_id)) { return false; } } if (param_list.size() > longest_action_sequence_) longest_action_sequence_ = param_list.size(); pointer_actions_list_.push_back(param_list); return true; } bool ActionsParser::ParseAction( const base::Value& action, SyntheticPointerActionListParams::ParamList& param_list, int pointer_id) { SyntheticPointerActionParams::PointerActionType pointer_action_type = SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED; std::string type; const base::Value* type_key = action.FindKey("type"); if (type_key) { if (!type_key->is_string()) { error_message_ = base::StringPrintf( "actions[%d].actions.name is missing or not a string", action_index_); return false; } type = type_key->GetString(); } else { const std::string* name_key = action.FindStringKey("name"); if (!name_key) { error_message_ = base::StringPrintf( "actions[%d].actions.name is missing or not a string", action_index_); return false; } type = *name_key; } pointer_action_type = ToSyntheticPointerActionType(type); if (pointer_action_type == SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED) { error_message_ = base::StringPrintf( "actions[%d].actions.name is an unsupported action name", action_index_); return false; } double position_x = 0; double position_y = 0; const base::Value* position_x_key = action.FindKey("x"); const base::Value* position_y_key = action.FindKey("y"); if (position_x_key) { if (!position_x_key->is_int() && !position_x_key->is_double()) { error_message_ = base::StringPrintf( "actions[%d].actions.x is not a number", action_index_); return false; } position_x = position_x_key->GetDouble(); } if (position_y_key) { if (!position_y_key->is_int() && !position_y_key->is_double()) { error_message_ = base::StringPrintf( "actions[%d].actions.y is not a number", action_index_); return false; } position_y = position_y_key->GetDouble(); } int button_id = 0; const base::Value* button_id_key = action.FindKey("button"); if (button_id_key) { if (!button_id_key->is_int()) { error_message_ = base::StringPrintf( "actions[%d].actions.button is not a string", action_index_); return false; } button_id = button_id_key->GetInt(); } if (button_id < 0 || button_id > 4) { error_message_ = base::StringPrintf( "actions[%d].actions.button is an unsupported button", action_index_); return false; } SyntheticPointerActionParams::Button button = ToSyntheticMouseButton(button_id); std::string keys; const base::Value* keys_key = action.FindKey("keys"); if (keys_key) { if (!keys_key->is_string()) { error_message_ = base::StringPrintf( "actions[%d].actions.key is not a string", action_index_); return false; } keys = keys_key->GetString(); } int key_modifiers = 0; std::vector key_list = base::SplitString(keys, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); for (std::string& key : key_list) { int key_modifier = ToKeyModifiers(key); if (key_modifier == 0) { error_message_ = base::StringPrintf( "actions[%d].actions.key is not a valid key", action_index_); return false; } key_modifiers |= key_modifier; } int duration = viz::BeginFrameArgs::DefaultInterval().InMilliseconds(); if (pointer_action_type == SyntheticPointerActionParams::PointerActionType::IDLE) { const base::Value* duration_key = action.FindKey("duration"); if (duration_key) { if (!duration_key->is_double() && !duration_key->is_int()) { error_message_ = base::StringPrintf( "actions[%d].actions.duration is not a number", action_index_); return false; } duration = duration_key->GetDouble(); } if (duration < 0) { error_message_ = base::StringPrintf( "actions[%d].actions.duration should not be negative", action_index_); return false; } } SyntheticPointerActionParams action_param(pointer_action_type); if (pointer_id == -1) action_param.set_pointer_id(action_index_); else action_param.set_pointer_id(pointer_id); switch (pointer_action_type) { case SyntheticPointerActionParams::PointerActionType::PRESS: action_param.set_position(gfx::PointF(position_x, position_y)); action_param.set_button(button); action_param.set_key_modifiers(key_modifiers); break; case SyntheticPointerActionParams::PointerActionType::MOVE: action_param.set_position(gfx::PointF(position_x, position_y)); action_param.set_key_modifiers(key_modifiers); break; case SyntheticPointerActionParams::PointerActionType::RELEASE: action_param.set_button(button); action_param.set_key_modifiers(key_modifiers); break; case SyntheticPointerActionParams::PointerActionType::IDLE: action_param.set_duration(base::TimeDelta::FromMilliseconds(duration)); break; case SyntheticPointerActionParams::PointerActionType::CANCEL: case SyntheticPointerActionParams::PointerActionType::LEAVE: case SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED: break; } param_list.push_back(action_param); return true; } } // namespace content