// 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/renderer/gpu/actions_parser.h" #include "base/format_macros.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( 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 == "pause") return SyntheticPointerActionParams::PointerActionType::IDLE; return SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED; } SyntheticGestureParams::GestureSourceType ToSyntheticGestureSourceType( std::string source_type) { if (source_type == "touch") return SyntheticGestureParams::TOUCH_INPUT; else if (source_type == "mouse") return SyntheticGestureParams::MOUSE_INPUT; else if (source_type == "pen") return SyntheticGestureParams::PEN_INPUT; else return SyntheticGestureParams::DEFAULT_INPUT; } SyntheticPointerActionParams::Button ToSyntheticMouseButton( std::string button) { if (button == "left") return SyntheticPointerActionParams::Button::LEFT; if (button == "middle") return SyntheticPointerActionParams::Button::MIDDLE; if (button == "right") return SyntheticPointerActionParams::Button::RIGHT; if (button == "back") return SyntheticPointerActionParams::Button::BACK; if (button == "forward") return SyntheticPointerActionParams::Button::FORWARD; NOTREACHED() << "Unexpected button"; return SyntheticPointerActionParams::Button(); } } // namespace ActionsParser::ActionsParser(base::Value* pointer_actions_value) : longest_action_sequence_(0), pointer_actions_value_(pointer_actions_value), action_index_(0) {} ActionsParser::~ActionsParser() {} bool ActionsParser::ParsePointerActionSequence() { const base::ListValue* pointer_list; if (!pointer_actions_value_ || !pointer_actions_value_->GetAsList(&pointer_list)) { error_message_ = base::StringPrintf("pointer_list is missing or not a list"); return false; } for (const auto& pointer_value : *pointer_list) { const base::DictionaryValue* pointer_actions; if (!pointer_value.GetAsDictionary(&pointer_actions)) { error_message_ = base::StringPrintf("pointer actions is missing or not a dictionary"); return false; } else if (!ParsePointerActions(*pointer_actions)) { return false; } action_index_++; } gesture_params_.gesture_source_type = ToSyntheticGestureSourceType(source_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; 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]); } gesture_params_.PushPointerActionParamsList(param_list); } return true; } bool ActionsParser::ParsePointerActions(const base::DictionaryValue& pointer) { std::string source_type; if (!pointer.GetString("source", &source_type)) { error_message_ = base::StringPrintf("source type is missing or not a string"); return false; } else if (source_type != "touch" && source_type != "mouse" && source_type != "pen") { error_message_ = base::StringPrintf("source type is an unsupported input source"); return false; } if (source_type_.empty()) { source_type_ = source_type; #if defined(OS_MACOSX) if (source_type == "touch") { error_message_ = base::StringPrintf("Mac OS does not support touch events"); return false; } #endif // defined(OS_MACOSX) } if (source_type_ != source_type) { error_message_ = base::StringPrintf( "currently multiple input sources are not not supported"); return false; } if (source_type != "touch" && action_index_ > 0) { error_message_ = base::StringPrintf( "for input source type of mouse and pen, we only support one device in " "one sequence"); return false; } const base::ListValue* actions; if (!pointer.GetList("actions", &actions)) { error_message_ = base::StringPrintf( "pointer[%d].actions is missing or not a list", action_index_); return false; } if (!ParseActions(*actions)) return false; return true; } bool ActionsParser::ParseActions(const base::ListValue& actions) { SyntheticPointerActionListParams::ParamList param_list; for (const auto& action_value : actions) { const base::DictionaryValue* action; if (!action_value.GetAsDictionary(&action)) { error_message_ = base::StringPrintf( "actions[%d].actions is missing or not a dictionary", action_index_); return false; } else if (!ParseAction(*action, param_list)) { 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::DictionaryValue& action, SyntheticPointerActionListParams::ParamList& param_list) { SyntheticPointerActionParams::PointerActionType pointer_action_type = SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED; std::string name; if (!action.GetString("name", &name)) { error_message_ = base::StringPrintf( "actions[%d].actions.name is missing or not a string", action_index_); return false; } pointer_action_type = ToSyntheticPointerActionType(name); 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; if (action.HasKey("x") && !action.GetDouble("x", &position_x)) { error_message_ = base::StringPrintf("actions[%d].actions.x is not a number", action_index_); return false; } if (action.HasKey("y") && !action.GetDouble("y", &position_y)) { error_message_ = base::StringPrintf("actions[%d].actions.y is not a number", action_index_); return false; } std::string button_name = "left"; if (action.HasKey("button") && !action.GetString("button", &button_name)) { error_message_ = base::StringPrintf( "actions[%d].actions.button is not a string", action_index_); return false; } else if (button_name != "left" && button_name != "middle" && button_name != "right" && button_name != "back" && button_name != "forward") { error_message_ = base::StringPrintf( "actions[%d].actions.button is an unsupported button", action_index_); return false; } SyntheticPointerActionParams::Button button = ToSyntheticMouseButton(button_name); double duration = 0; int num_idle = 0; if (pointer_action_type == SyntheticPointerActionParams::PointerActionType::IDLE) { num_idle = 1; if (action.HasKey("duration") && !action.GetDouble("duration", &duration)) { error_message_ = base::StringPrintf( "actions[%d].actions.x is not a number", action_index_); return false; } } // If users pause for given seconds, we convert to the number of idle frames. if (duration > 0) { num_idle = static_cast(std::ceil( duration / viz::BeginFrameArgs::DefaultInterval().InSecondsF())); } SyntheticPointerActionParams action_param(pointer_action_type); action_param.set_index(action_index_); switch (pointer_action_type) { case SyntheticPointerActionParams::PointerActionType::PRESS: action_param.set_position(gfx::PointF(position_x, position_y)); action_param.set_button(button); break; case SyntheticPointerActionParams::PointerActionType::MOVE: action_param.set_position(gfx::PointF(position_x, position_y)); break; case SyntheticPointerActionParams::PointerActionType::RELEASE: action_param.set_button(button); break; case SyntheticPointerActionParams::PointerActionType::IDLE: case SyntheticPointerActionParams::PointerActionType::NOT_INITIALIZED: break; } param_list.push_back(action_param); // We queue all the IDLE actions in the action parameter list to make sure we // will pause long enough on the given pointer. for (int count = 1; count < num_idle; ++count) param_list.push_back(action_param); return true; } } // namespace content