1<?php 2 3namespace Facebook\WebDriver\Remote; 4 5use Exception; 6use Facebook\WebDriver\Chrome\ChromeOptions; 7use Facebook\WebDriver\Firefox\FirefoxDriver; 8use Facebook\WebDriver\Firefox\FirefoxOptions; 9use Facebook\WebDriver\Firefox\FirefoxProfile; 10use Facebook\WebDriver\WebDriverCapabilities; 11use Facebook\WebDriver\WebDriverPlatform; 12 13class DesiredCapabilities implements WebDriverCapabilities 14{ 15 /** @var array */ 16 private $capabilities; 17 18 /** @var array */ 19 private static $ossToW3c = [ 20 WebDriverCapabilityType::PLATFORM => 'platformName', 21 WebDriverCapabilityType::VERSION => 'browserVersion', 22 WebDriverCapabilityType::ACCEPT_SSL_CERTS => 'acceptInsecureCerts', 23 ChromeOptions::CAPABILITY => ChromeOptions::CAPABILITY_W3C, 24 ]; 25 26 public function __construct(array $capabilities = []) 27 { 28 $this->capabilities = $capabilities; 29 } 30 31 public static function createFromW3cCapabilities(array $capabilities = []) 32 { 33 $w3cToOss = array_flip(self::$ossToW3c); 34 35 foreach ($w3cToOss as $w3cCapability => $ossCapability) { 36 // Copy W3C capabilities to OSS ones 37 if (array_key_exists($w3cCapability, $capabilities)) { 38 $capabilities[$ossCapability] = $capabilities[$w3cCapability]; 39 } 40 } 41 42 return new self($capabilities); 43 } 44 45 /** 46 * @return string The name of the browser. 47 */ 48 public function getBrowserName() 49 { 50 return $this->get(WebDriverCapabilityType::BROWSER_NAME, ''); 51 } 52 53 /** 54 * @param string $browser_name 55 * @return DesiredCapabilities 56 */ 57 public function setBrowserName($browser_name) 58 { 59 $this->set(WebDriverCapabilityType::BROWSER_NAME, $browser_name); 60 61 return $this; 62 } 63 64 /** 65 * @return string The version of the browser. 66 */ 67 public function getVersion() 68 { 69 return $this->get(WebDriverCapabilityType::VERSION, ''); 70 } 71 72 /** 73 * @param string $version 74 * @return DesiredCapabilities 75 */ 76 public function setVersion($version) 77 { 78 $this->set(WebDriverCapabilityType::VERSION, $version); 79 80 return $this; 81 } 82 83 /** 84 * @param string $name 85 * @return mixed The value of a capability. 86 */ 87 public function getCapability($name) 88 { 89 return $this->get($name); 90 } 91 92 /** 93 * @param string $name 94 * @param mixed $value 95 * @return DesiredCapabilities 96 */ 97 public function setCapability($name, $value) 98 { 99 // When setting 'moz:firefoxOptions' from an array and not from instance of FirefoxOptions, we must merge 100 // it with default FirefoxOptions to keep previous behavior (where the default preferences were added 101 // using FirefoxProfile, thus not overwritten by adding 'moz:firefoxOptions') 102 // TODO: remove in next major version, once FirefoxOptions are only accepted as object instance and not as array 103 if ($name === FirefoxOptions::CAPABILITY && is_array($value)) { 104 $defaultOptions = (new FirefoxOptions())->toArray(); 105 $value = array_merge($defaultOptions, $value); 106 } 107 108 $this->set($name, $value); 109 110 return $this; 111 } 112 113 /** 114 * @return string The name of the platform. 115 */ 116 public function getPlatform() 117 { 118 return $this->get(WebDriverCapabilityType::PLATFORM, ''); 119 } 120 121 /** 122 * @param string $platform 123 * @return DesiredCapabilities 124 */ 125 public function setPlatform($platform) 126 { 127 $this->set(WebDriverCapabilityType::PLATFORM, $platform); 128 129 return $this; 130 } 131 132 /** 133 * @param string $capability_name 134 * @return bool Whether the value is not null and not false. 135 */ 136 public function is($capability_name) 137 { 138 return (bool) $this->get($capability_name); 139 } 140 141 /** 142 * @todo Remove in next major release (BC) 143 * @deprecated All browsers are always JS enabled except HtmlUnit and it's not meaningful to disable JS execution. 144 * @return bool Whether javascript is enabled. 145 */ 146 public function isJavascriptEnabled() 147 { 148 return $this->get(WebDriverCapabilityType::JAVASCRIPT_ENABLED, false); 149 } 150 151 /** 152 * This is a htmlUnit-only option. 153 * 154 * @param bool $enabled 155 * @throws Exception 156 * @return DesiredCapabilities 157 * @see https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities 158 */ 159 public function setJavascriptEnabled($enabled) 160 { 161 $browser = $this->getBrowserName(); 162 if ($browser && $browser !== WebDriverBrowserType::HTMLUNIT) { 163 throw new Exception( 164 'isJavascriptEnabled() is a htmlunit-only option. ' . 165 'See https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#read-write-capabilities.' 166 ); 167 } 168 169 $this->set(WebDriverCapabilityType::JAVASCRIPT_ENABLED, $enabled); 170 171 return $this; 172 } 173 174 /** 175 * @todo Remove side-effects - not change eg. ChromeOptions::CAPABILITY from instance of ChromeOptions to an array 176 * @return array 177 */ 178 public function toArray() 179 { 180 if (isset($this->capabilities[ChromeOptions::CAPABILITY]) && 181 $this->capabilities[ChromeOptions::CAPABILITY] instanceof ChromeOptions 182 ) { 183 $this->capabilities[ChromeOptions::CAPABILITY] = 184 $this->capabilities[ChromeOptions::CAPABILITY]->toArray(); 185 } 186 187 if (isset($this->capabilities[FirefoxOptions::CAPABILITY]) && 188 $this->capabilities[FirefoxOptions::CAPABILITY] instanceof FirefoxOptions 189 ) { 190 $this->capabilities[FirefoxOptions::CAPABILITY] = 191 $this->capabilities[FirefoxOptions::CAPABILITY]->toArray(); 192 } 193 194 if (isset($this->capabilities[FirefoxDriver::PROFILE]) && 195 $this->capabilities[FirefoxDriver::PROFILE] instanceof FirefoxProfile 196 ) { 197 $this->capabilities[FirefoxDriver::PROFILE] = 198 $this->capabilities[FirefoxDriver::PROFILE]->encode(); 199 } 200 201 return $this->capabilities; 202 } 203 204 /** 205 * @return array 206 */ 207 public function toW3cCompatibleArray() 208 { 209 $allowedW3cCapabilities = [ 210 'browserName', 211 'browserVersion', 212 'platformName', 213 'acceptInsecureCerts', 214 'pageLoadStrategy', 215 'proxy', 216 'setWindowRect', 217 'timeouts', 218 'strictFileInteractability', 219 'unhandledPromptBehavior', 220 ]; 221 222 $ossCapabilities = $this->toArray(); 223 $w3cCapabilities = []; 224 225 foreach ($ossCapabilities as $capabilityKey => $capabilityValue) { 226 // Copy already W3C compatible capabilities 227 if (in_array($capabilityKey, $allowedW3cCapabilities, true)) { 228 $w3cCapabilities[$capabilityKey] = $capabilityValue; 229 } 230 231 // Convert capabilities with changed name 232 if (array_key_exists($capabilityKey, self::$ossToW3c)) { 233 if ($capabilityKey === WebDriverCapabilityType::PLATFORM) { 234 $w3cCapabilities[self::$ossToW3c[$capabilityKey]] = mb_strtolower($capabilityValue); 235 236 // Remove platformName if it is set to "any" 237 if ($w3cCapabilities[self::$ossToW3c[$capabilityKey]] === 'any') { 238 unset($w3cCapabilities[self::$ossToW3c[$capabilityKey]]); 239 } 240 } else { 241 $w3cCapabilities[self::$ossToW3c[$capabilityKey]] = $capabilityValue; 242 } 243 } 244 245 // Copy vendor extensions 246 if (mb_strpos($capabilityKey, ':') !== false) { 247 $w3cCapabilities[$capabilityKey] = $capabilityValue; 248 } 249 } 250 251 // Convert ChromeOptions 252 if (array_key_exists(ChromeOptions::CAPABILITY, $ossCapabilities)) { 253 if (array_key_exists(ChromeOptions::CAPABILITY_W3C, $ossCapabilities)) { 254 $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = new \ArrayObject( 255 array_merge_recursive( 256 (array) $ossCapabilities[ChromeOptions::CAPABILITY], 257 (array) $ossCapabilities[ChromeOptions::CAPABILITY_W3C] 258 ) 259 ); 260 } else { 261 $w3cCapabilities[ChromeOptions::CAPABILITY_W3C] = $ossCapabilities[ChromeOptions::CAPABILITY]; 262 } 263 } 264 265 // Convert Firefox profile 266 if (array_key_exists(FirefoxDriver::PROFILE, $ossCapabilities)) { 267 // Convert profile only if not already set in moz:firefoxOptions 268 if (!array_key_exists(FirefoxOptions::CAPABILITY, $ossCapabilities) 269 || !array_key_exists('profile', $ossCapabilities[FirefoxOptions::CAPABILITY])) { 270 $w3cCapabilities[FirefoxOptions::CAPABILITY]['profile'] = $ossCapabilities[FirefoxDriver::PROFILE]; 271 } 272 } 273 274 return $w3cCapabilities; 275 } 276 277 /** 278 * @return static 279 */ 280 public static function android() 281 { 282 return new static([ 283 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::ANDROID, 284 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANDROID, 285 ]); 286 } 287 288 /** 289 * @return static 290 */ 291 public static function chrome() 292 { 293 return new static([ 294 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::CHROME, 295 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 296 ]); 297 } 298 299 /** 300 * @return static 301 */ 302 public static function firefox() 303 { 304 $caps = new static([ 305 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::FIREFOX, 306 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 307 ]); 308 309 $caps->setCapability(FirefoxOptions::CAPABILITY, new FirefoxOptions()); // to add default options 310 311 return $caps; 312 } 313 314 /** 315 * @return static 316 */ 317 public static function htmlUnit() 318 { 319 return new static([ 320 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT, 321 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 322 ]); 323 } 324 325 /** 326 * @return static 327 */ 328 public static function htmlUnitWithJS() 329 { 330 $caps = new static([ 331 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::HTMLUNIT, 332 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 333 ]); 334 335 return $caps->setJavascriptEnabled(true); 336 } 337 338 /** 339 * @return static 340 */ 341 public static function internetExplorer() 342 { 343 return new static([ 344 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IE, 345 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS, 346 ]); 347 } 348 349 /** 350 * @return static 351 */ 352 public static function microsoftEdge() 353 { 354 return new static([ 355 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::MICROSOFT_EDGE, 356 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::WINDOWS, 357 ]); 358 } 359 360 /** 361 * @return static 362 */ 363 public static function iphone() 364 { 365 return new static([ 366 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPHONE, 367 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC, 368 ]); 369 } 370 371 /** 372 * @return static 373 */ 374 public static function ipad() 375 { 376 return new static([ 377 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::IPAD, 378 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::MAC, 379 ]); 380 } 381 382 /** 383 * @return static 384 */ 385 public static function opera() 386 { 387 return new static([ 388 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::OPERA, 389 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 390 ]); 391 } 392 393 /** 394 * @return static 395 */ 396 public static function safari() 397 { 398 return new static([ 399 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::SAFARI, 400 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 401 ]); 402 } 403 404 /** 405 * @deprecated PhantomJS is no longer developed and its support will be removed in next major version. 406 * Use headless Chrome or Firefox instead. 407 * @return static 408 */ 409 public static function phantomjs() 410 { 411 return new static([ 412 WebDriverCapabilityType::BROWSER_NAME => WebDriverBrowserType::PHANTOMJS, 413 WebDriverCapabilityType::PLATFORM => WebDriverPlatform::ANY, 414 ]); 415 } 416 417 /** 418 * @param string $key 419 * @param mixed $value 420 * @return DesiredCapabilities 421 */ 422 private function set($key, $value) 423 { 424 $this->capabilities[$key] = $value; 425 426 return $this; 427 } 428 429 /** 430 * @param string $key 431 * @param mixed $default 432 * @return mixed 433 */ 434 private function get($key, $default = null) 435 { 436 return isset($this->capabilities[$key]) 437 ? $this->capabilities[$key] 438 : $default; 439 } 440} 441