| // 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/memory/ptr_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "cc/output/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; |
| 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_++; |
| } |
| |
| if (!gesture_params_) |
| gesture_params_ = base::MakeUnique<SyntheticPointerActionListParams>(); |
| |
| 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_list : pointer_actions_list_) { |
| if (action_index < pointer_list.size()) |
| param_list.push_back(pointer_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") { |
| 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<int>(std::ceil( |
| duration / cc::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 |