1<?php 2 3namespace Facebook\WebDriver\Remote; 4 5use Facebook\WebDriver\Interactions\Internal\WebDriverCoordinates; 6use Facebook\WebDriver\WebDriverMouse; 7 8/** 9 * Execute mouse commands for RemoteWebDriver. 10 */ 11class RemoteMouse implements WebDriverMouse 12{ 13 /** @internal */ 14 const BUTTON_LEFT = 0; 15 /** @internal */ 16 const BUTTON_MIDDLE = 1; 17 /** @internal */ 18 const BUTTON_RIGHT = 2; 19 20 /** 21 * @var RemoteExecuteMethod 22 */ 23 private $executor; 24 /** 25 * @var bool 26 */ 27 private $isW3cCompliant; 28 29 /** 30 * @param RemoteExecuteMethod $executor 31 * @param bool $isW3cCompliant 32 */ 33 public function __construct(RemoteExecuteMethod $executor, $isW3cCompliant = false) 34 { 35 $this->executor = $executor; 36 $this->isW3cCompliant = $isW3cCompliant; 37 } 38 39 /** 40 * @param null|WebDriverCoordinates $where 41 * 42 * @return RemoteMouse 43 */ 44 public function click(WebDriverCoordinates $where = null) 45 { 46 if ($this->isW3cCompliant) { 47 $moveAction = $where ? [$this->createMoveAction($where)] : []; 48 $this->executor->execute(DriverCommand::ACTIONS, [ 49 'actions' => [ 50 [ 51 'type' => 'pointer', 52 'id' => 'mouse', 53 'parameters' => ['pointerType' => 'mouse'], 54 'actions' => array_merge($moveAction, $this->createClickActions()), 55 ], 56 ], 57 ]); 58 59 return $this; 60 } 61 62 $this->moveIfNeeded($where); 63 $this->executor->execute(DriverCommand::CLICK, [ 64 'button' => self::BUTTON_LEFT, 65 ]); 66 67 return $this; 68 } 69 70 /** 71 * @param WebDriverCoordinates $where 72 * 73 * @return RemoteMouse 74 */ 75 public function contextClick(WebDriverCoordinates $where = null) 76 { 77 if ($this->isW3cCompliant) { 78 $moveAction = $where ? [$this->createMoveAction($where)] : []; 79 $this->executor->execute(DriverCommand::ACTIONS, [ 80 'actions' => [ 81 [ 82 'type' => 'pointer', 83 'id' => 'mouse', 84 'parameters' => ['pointerType' => 'mouse'], 85 'actions' => array_merge($moveAction, [ 86 [ 87 'type' => 'pointerDown', 88 'button' => self::BUTTON_RIGHT, 89 ], 90 [ 91 'type' => 'pointerUp', 92 'button' => self::BUTTON_RIGHT, 93 ], 94 ]), 95 ], 96 ], 97 ]); 98 99 return $this; 100 } 101 102 $this->moveIfNeeded($where); 103 $this->executor->execute(DriverCommand::CLICK, [ 104 'button' => self::BUTTON_RIGHT, 105 ]); 106 107 return $this; 108 } 109 110 /** 111 * @param WebDriverCoordinates $where 112 * 113 * @return RemoteMouse 114 */ 115 public function doubleClick(WebDriverCoordinates $where = null) 116 { 117 if ($this->isW3cCompliant) { 118 $clickActions = $this->createClickActions(); 119 $moveAction = $where === null ? [] : [$this->createMoveAction($where)]; 120 $this->executor->execute(DriverCommand::ACTIONS, [ 121 'actions' => [ 122 [ 123 'type' => 'pointer', 124 'id' => 'mouse', 125 'parameters' => ['pointerType' => 'mouse'], 126 'actions' => array_merge($moveAction, $clickActions, $clickActions), 127 ], 128 ], 129 ]); 130 131 return $this; 132 } 133 134 $this->moveIfNeeded($where); 135 $this->executor->execute(DriverCommand::DOUBLE_CLICK); 136 137 return $this; 138 } 139 140 /** 141 * @param WebDriverCoordinates $where 142 * 143 * @return RemoteMouse 144 */ 145 public function mouseDown(WebDriverCoordinates $where = null) 146 { 147 if ($this->isW3cCompliant) { 148 $this->executor->execute(DriverCommand::ACTIONS, [ 149 'actions' => [ 150 [ 151 'type' => 'pointer', 152 'id' => 'mouse', 153 'parameters' => ['pointerType' => 'mouse'], 154 'actions' => [ 155 $this->createMoveAction($where), 156 [ 157 'type' => 'pointerDown', 158 'button' => self::BUTTON_LEFT, 159 ], 160 ], 161 ], 162 ], 163 ]); 164 165 return $this; 166 } 167 168 $this->moveIfNeeded($where); 169 $this->executor->execute(DriverCommand::MOUSE_DOWN); 170 171 return $this; 172 } 173 174 /** 175 * @param WebDriverCoordinates $where 176 * @param int|null $x_offset 177 * @param int|null $y_offset 178 * 179 * @return RemoteMouse 180 */ 181 public function mouseMove( 182 WebDriverCoordinates $where = null, 183 $x_offset = null, 184 $y_offset = null 185 ) { 186 if ($this->isW3cCompliant) { 187 $this->executor->execute(DriverCommand::ACTIONS, [ 188 'actions' => [ 189 [ 190 'type' => 'pointer', 191 'id' => 'mouse', 192 'parameters' => ['pointerType' => 'mouse'], 193 'actions' => [$this->createMoveAction($where, $x_offset, $y_offset)], 194 ], 195 ], 196 ]); 197 198 return $this; 199 } 200 201 $params = []; 202 if ($where !== null) { 203 $params['element'] = $where->getAuxiliary(); 204 } 205 if ($x_offset !== null) { 206 $params['xoffset'] = $x_offset; 207 } 208 if ($y_offset !== null) { 209 $params['yoffset'] = $y_offset; 210 } 211 212 $this->executor->execute(DriverCommand::MOVE_TO, $params); 213 214 return $this; 215 } 216 217 /** 218 * @param WebDriverCoordinates $where 219 * 220 * @return RemoteMouse 221 */ 222 public function mouseUp(WebDriverCoordinates $where = null) 223 { 224 if ($this->isW3cCompliant) { 225 $moveAction = $where ? [$this->createMoveAction($where)] : []; 226 227 $this->executor->execute(DriverCommand::ACTIONS, [ 228 'actions' => [ 229 [ 230 'type' => 'pointer', 231 'id' => 'mouse', 232 'parameters' => ['pointerType' => 'mouse'], 233 'actions' => array_merge($moveAction, [ 234 [ 235 'type' => 'pointerUp', 236 'button' => self::BUTTON_LEFT, 237 ], 238 ]), 239 ], 240 ], 241 ]); 242 243 return $this; 244 } 245 246 $this->moveIfNeeded($where); 247 $this->executor->execute(DriverCommand::MOUSE_UP); 248 249 return $this; 250 } 251 252 /** 253 * @param WebDriverCoordinates $where 254 */ 255 protected function moveIfNeeded(WebDriverCoordinates $where = null) 256 { 257 if ($where) { 258 $this->mouseMove($where); 259 } 260 } 261 262 /** 263 * @param WebDriverCoordinates $where 264 * @param int|null $x_offset 265 * @param int|null $y_offset 266 * 267 * @return array 268 */ 269 private function createMoveAction( 270 WebDriverCoordinates $where = null, 271 $x_offset = null, 272 $y_offset = null 273 ) { 274 $move_action = [ 275 'type' => 'pointerMove', 276 'duration' => 100, // to simulate human delay 277 'x' => $x_offset === null ? 0 : $x_offset, 278 'y' => $y_offset === null ? 0 : $y_offset, 279 ]; 280 281 if ($where !== null) { 282 $move_action['origin'] = [JsonWireCompat::WEB_DRIVER_ELEMENT_IDENTIFIER => $where->getAuxiliary()]; 283 } else { 284 $move_action['origin'] = 'pointer'; 285 } 286 287 return $move_action; 288 } 289 290 /** 291 * @return array 292 */ 293 private function createClickActions() 294 { 295 return [ 296 [ 297 'type' => 'pointerDown', 298 'button' => self::BUTTON_LEFT, 299 ], 300 [ 301 'type' => 'pointerUp', 302 'button' => self::BUTTON_LEFT, 303 ], 304 ]; 305 } 306} 307