summaryrefslogtreecommitdiffstats
path: root/third_party
diff options
context:
space:
mode:
authorjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-24 19:28:34 +0000
committerjmikhail@google.com <jmikhail@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-24 19:28:34 +0000
commitf7eb1ddf7852f37b01cdd6e163742e4d135f9b78 (patch)
tree729f70519f9d2e782ec8d0787aedf74603c83188 /third_party
parente26d977f4f074cc68d40f7ff439992c105f260a8 (diff)
downloadchromium_src-f7eb1ddf7852f37b01cdd6e163742e4d135f9b78.zip
chromium_src-f7eb1ddf7852f37b01cdd6e163742e4d135f9b78.tar.gz
chromium_src-f7eb1ddf7852f37b01cdd6e163742e4d135f9b78.tar.bz2
Python bindings for the remote webdriver protcol.
For more information see: http://code.google.com/p/selenium/wiki/PythonBindings BUG=none TEST=none Review URL: http://codereview.chromium.org/3358016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60507 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party')
-rw-r--r--third_party/webdriver/py/COPYING204
-rw-r--r--third_party/webdriver/py/LICENSE4
-rw-r--r--third_party/webdriver/py/README.chromium14
-rw-r--r--third_party/webdriver/py/__init__.py16
-rw-r--r--third_party/webdriver/py/selenium/__init__.py16
-rw-r--r--third_party/webdriver/py/selenium/common/__init__.py16
-rw-r--r--third_party/webdriver/py/selenium/common/exceptions.py76
-rw-r--r--third_party/webdriver/py/selenium/remote/__init__.py16
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/WebDriver.py362
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/__init__.py16
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/command.py79
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/errorhandler.py98
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/remote_connection.py245
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/utils.py112
-rw-r--r--third_party/webdriver/py/selenium/remote/webdriver/webelement.py162
15 files changed, 1436 insertions, 0 deletions
diff --git a/third_party/webdriver/py/COPYING b/third_party/webdriver/py/COPYING
new file mode 100644
index 0000000..80a4762
--- /dev/null
+++ b/third_party/webdriver/py/COPYING
@@ -0,0 +1,204 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2007-2009 Google Inc.
+ Copyright 2007-2009 WebDriver committers
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/third_party/webdriver/py/LICENSE b/third_party/webdriver/py/LICENSE
new file mode 100644
index 0000000..d4e0e72
--- /dev/null
+++ b/third_party/webdriver/py/LICENSE
@@ -0,0 +1,4 @@
+NAME: WebDriver
+URL: http://selenium.googlecode.com/svn/trunk/remote/client/src/py/
+ http://selenium.googlecode.com/svn/trunk/common/src/py/
+LICENSE: Apache 2
diff --git a/third_party/webdriver/py/README.chromium b/third_party/webdriver/py/README.chromium
new file mode 100644
index 0000000..c8c699f
--- /dev/null
+++ b/third_party/webdriver/py/README.chromium
@@ -0,0 +1,14 @@
+Name: Webdriver
+URL: http://seleniumhq.org
+LICENSE: APACHE 2
+VERSION: Selenium 2 alpha 5
+
+The code in this tree is a manual checkin of the python bindings
+found on the Selenium HQ website. The source code is the same
+however the directory layout has changed.
+
+Source code pulled from:
+http://selenium.googlecode.com/svn/trunk/remote/client/src/py/
+http://selenium.googlecode.com/svn/trunk/common/src/py/
+At revision 9575
+
diff --git a/third_party/webdriver/py/__init__.py b/third_party/webdriver/py/__init__.py
new file mode 100644
index 0000000..5555f0b
--- /dev/null
+++ b/third_party/webdriver/py/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/third_party/webdriver/py/selenium/__init__.py b/third_party/webdriver/py/selenium/__init__.py
new file mode 100644
index 0000000..5555f0b
--- /dev/null
+++ b/third_party/webdriver/py/selenium/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/third_party/webdriver/py/selenium/common/__init__.py b/third_party/webdriver/py/selenium/common/__init__.py
new file mode 100644
index 0000000..5555f0b
--- /dev/null
+++ b/third_party/webdriver/py/selenium/common/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/third_party/webdriver/py/selenium/common/exceptions.py b/third_party/webdriver/py/selenium/common/exceptions.py
new file mode 100644
index 0000000..7b54af9
--- /dev/null
+++ b/third_party/webdriver/py/selenium/common/exceptions.py
@@ -0,0 +1,76 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Exceptions that may happen in all the webdriver code."""
+
+class ErrorInResponseException(Exception):
+ """An error has occurred on the server side.
+
+ This may happen when communicating with the firefox extension
+ or the remote driver server."""
+ def __init__(self, response, msg):
+ Exception.__init__(self, msg)
+ self.response = response
+
+class InvalidSwitchToTargetException(Exception):
+ """The frame or window target to be switched doesn't exist."""
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class NoSuchFrameException(InvalidSwitchToTargetException):
+ def __init__(self, msg=None):
+ InvalidSwitchToTargetException.__init__(self, msg)
+
+class NoSuchWindowException(InvalidSwitchToTargetException):
+ def __init__(self, msg=None):
+ InvalidSwitchToTargetException.__init__(self, msg)
+
+class NoSuchElementException(Exception):
+ """find_element_by_* can't find the element."""
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class NoSuchAttributeException(Exception):
+ """find_element_by_* can't find the element."""
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class StaleElementReferenceException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class ElementNotVisibleException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class InvalidElementStateException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class ElementNotSelectableException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class InvalidCookieDomainException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class UnableToSetCookieException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
+
+class RemoteDriverServerException(Exception):
+ def __init__(self, msg=None):
+ Exception.__init__(self, msg)
diff --git a/third_party/webdriver/py/selenium/remote/__init__.py b/third_party/webdriver/py/selenium/remote/__init__.py
new file mode 100644
index 0000000..5555f0b
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/WebDriver.py b/third_party/webdriver/py/selenium/remote/webdriver/WebDriver.py
new file mode 100644
index 0000000..b7246a0
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/WebDriver.py
@@ -0,0 +1,362 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""The WebDriver implementation."""
+
+import base64
+from command import Command
+from webelement import WebElement
+from remote_connection import RemoteConnection
+from errorhandler import ErrorHandler
+
+class WebDriver(object):
+ """Controls a browser by sending commands to a remote server.
+
+ This server is expected to be running the WebDriver wire protocol as defined
+ here:
+ http://code.google.com/p/selenium/wiki/JsonWireProtocol
+
+ Attributes:
+ command_executor - The command.CommandExecutor object used to execute
+ commands.
+ error_handler - errorhandler.ErrorHandler object used to verify that the
+ server did not return an error.
+ session_id - The session ID to send with every command.
+ capabilities - A dictionary of capabilities of the underlying browser for
+ this instance's session.
+ """
+
+ def __init__(self, command_executor, browser_name, platform, version='',
+ javascript_enabled=True):
+ """Create a new driver that will issue commands using the wire protocol.
+
+ Args:
+ command_executor - Either a command.CommandExecutor object or a string
+ that specifies the URL of a remote server to send commands to.
+ browser_name - A string indicating which browser to request a new
+ session for from the remote server. Should be one of
+ {mobile safari|firefox|internet explorer|htmlunit|chrome}.
+ platform - A string indicating the desired platform to request from
+ the remote server. Should be one of
+ {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY}.
+ version - A string indicating a specific browser version to request,
+ or an empty string ot use any available browser. Defaults to the
+ empty string.
+ javascript_enabled - Whether the requested browser should support
+ JavaScript. Defaults to True.
+ """
+ self.command_executor = command_executor
+ if type(self.command_executor) is str:
+ self.command_executor = RemoteConnection(command_executor)
+
+ self.session_id = None
+ self.capabilities = {}
+ self.error_handler = ErrorHandler()
+
+ self.start_client()
+ self.start_session(browser_name=browser_name,
+ platform=platform,
+ version=version,
+ javascript_enabled=javascript_enabled)
+
+ @property
+ def name(self):
+ """Returns the name of the underlying browser for this instance."""
+ if 'browserName' in self.capabilities:
+ return self.capabilities['browserName']
+ else:
+ raise KeyError('browserName not specified in session capabilities')
+
+ def start_client(self):
+ """Called before starting a new session.
+
+ This method may be overridden to define custom startup behavior.
+ """
+ pass
+
+ def stop_client(self):
+ """Called after executing a quit command.
+
+ This method may be overridden to define custom shutdown behavior.
+ """
+ pass
+
+ def start_session(self, browser_name, platform=None, version=None,
+ javascript_enabled=False):
+ """Creates a new session with the desired capabilities.
+
+ Args:
+ browser_name: The name of the browser to request.
+ version: Which browser version to request.
+ platform: Which platform to request the browser on.
+ javascript_enabled: Whether the new session should support JavaScript.
+ """
+ response = self._execute(Command.NEW_SESSION, {
+ 'desiredCapabilities': {
+ 'browserName': browser_name,
+ 'platform': platform or 'ANY',
+ 'version': version or '',
+ 'javascriptEnabled': javascript_enabled
+ }
+ })
+ self.session_id = response['sessionId']
+ self.capabilities = response['value']
+
+ def _wrap_value(self, value):
+ if isinstance(value, dict):
+ converted = {}
+ for key, val in value.items():
+ converted[key] = self._wrap_value(val)
+ return converted
+ elif isinstance(value, WebElement):
+ return {'ELEMENT': value.id}
+ elif isinstance(value, list):
+ return list(self._wrap_value(item) for item in value)
+ else:
+ return value
+
+ def create_web_element(self, element_id):
+ return WebElement(self, element_id)
+
+ def _unwrap_value(self, value):
+ if isinstance(value, dict) and 'ELEMENT' in value:
+ return self.create_web_element(value['ELEMENT'])
+ elif isinstance(value, list):
+ return list(self._unwrap_value(item) for item in value)
+ else:
+ return value
+
+ def _execute(self, driver_command, params=None):
+ """Sends a command to be executed by a command.CommandExecutor.
+
+ Args:
+ driver_command: The name of the command to execute as a string.
+ params: A dictionary of named parameters to send with the command.
+
+ Returns:
+ The command's JSON response loaded into a dictionary object.
+ """
+ if not params:
+ params = {'sessionId': self.session_id}
+ elif 'sessionId' not in params:
+ params['sessionId'] = self.session_id
+
+ params = self._wrap_value(params)
+ response = self.command_executor.execute(driver_command, params)
+ if response:
+ self.error_handler.check_response(response)
+ response['value'] = self._unwrap_value(
+ response.get('value', None))
+ return response
+ # If the server doesn't send a response, assume the command was
+ # a success
+ return {'success': 0, 'value': None}
+
+ def get(self, url):
+ """Loads a web page in the current browser."""
+ self._execute(Command.GET, {'url': url})
+
+ def get_title(self):
+ """Gets the title of the current page."""
+ resp = self._execute(Command.GET_TITLE)
+ return resp['value']
+
+ def find_element_by_id(self, id_):
+ """Finds element by id."""
+ return self._find_element_by("id", id_)
+
+ def find_elements_by_xpath(self, xpath):
+ """Finds multiple elements by xpath."""
+ return self._find_elements_by("xpath", xpath)
+
+ def find_element_by_xpath(self, xpath):
+ """Finds an element by xpath."""
+ return self._find_element_by("xpath", xpath)
+
+ def find_element_by_link_text(self, link_text):
+ """Finds an element by its link text."""
+ return self._find_element_by("link text", link_text)
+
+ def find_element_by_partial_link_text(self, link_text):
+ """Finds an element by a partial match of its link text."""
+ return self._find_element_by("partial link text", link_text)
+
+ def find_elements_by_link_text(self, link_text):
+ """Finds elements by their link text."""
+ return self._find_elements_by("link text", link_text)
+
+ def find_elements_by_partial_link_text(self, link_text):
+ """Finds elements by a partial match of their link text."""
+ return self._find_elements_by("partial link text", link_text)
+
+ def find_element_by_name(self, name):
+ """Finds an element by its name."""
+ return self._find_element_by("name", name)
+
+ def find_elements_by_name(self, name):
+ """Finds elements by their name."""
+ return self._find_elements_by("name", name)
+
+ def find_element_by_tag_name(self, name):
+ """Finds an element by its tag name."""
+ return self._find_element_by("tag name", name)
+
+ def find_elements_by_tag_name(self, name):
+ """Finds elements by their tag name."""
+ return self._find_elements_by("tag name", name)
+
+ def find_element_by_class_name(self, name):
+ """Finds an element by their class name."""
+ return self._find_element_by("class name", name)
+
+ def find_elements_by_class_name(self, name):
+ """Finds elements by their class name."""
+ return self._find_elements_by("class name", name)
+
+ def execute_script(self, script, *args):
+ if len(args) == 1:
+ converted_args = args[0]
+ else:
+ converted_args = list(args)
+ converted_args = list(args)
+ return self._execute(
+ Command.EXECUTE_SCRIPT,
+ {'script': script, 'args':converted_args})['value']
+
+ def get_current_url(self):
+ """Gets the current url."""
+ return self._execute(Command.GET_CURRENT_URL)['value']
+
+ def get_page_source(self):
+ """Gets the page source."""
+ return self._execute(Command.GET_PAGE_SOURCE)['value']
+
+ def close(self):
+ """Closes the current window."""
+ self._execute(Command.CLOSE)
+
+ def quit(self):
+ """Quits the driver and close every associated window."""
+ try:
+ self._execute(Command.QUIT)
+ finally:
+ self.stop_client()
+
+ def get_current_window_handle(self):
+ return self._execute(Command.GET_CURRENT_WINDOW_HANDLE)['value']
+
+ def get_window_handles(self):
+ return self._execute(Command.GET_WINDOW_HANDLES)['value']
+
+ def switch_to_active_element(self):
+ """Returns the element with focus, or BODY if nothing has focus."""
+ return self._execute(Command.GET_ACTIVE_ELEMENT)['value']
+
+ def switch_to_window(self, window_name):
+ """Switches focus to a window."""
+ self._execute(Command.SWITCH_TO_WINDOW, {'name': window_name})
+
+ def switch_to_frame(self, index_or_name):
+ """Switches focus to a frame by index or name."""
+ self._execute(Command.SWITCH_TO_FRAME, {'id': index_or_name})
+
+ def refresh(self):
+ """Reloads the current page."""
+ self._execute(Command.REFRESH)
+
+ def back(self):
+ """Goes back in browser history."""
+ self._execute(Command.GO_BACK)
+
+ def forward(self):
+ """Goes forward in browser history."""
+ self._execute(Command.GO_FORWARD)
+ # Options
+ def get_cookies(self):
+ """Gets all the cookies. Return a set of dicts."""
+ return self._execute(Command.GET_ALL_COOKIES)['value']
+
+ def get_cookie(self, name):
+ """Get a single cookie. Returns the desired cookie dict or None."""
+ cookies = self.get_cookies()
+ for cookie in cookies:
+ if cookie['name'] == name:
+ return cookie
+ return None
+
+ def delete_cookie(self, name):
+ """Delete a cookie with the given name."""
+ self._execute(Command.DELETE_COOKIE, {'name': name})
+
+ def delete_all_cookies(self):
+ """Delete all the cookies."""
+ self._execute(Command.DELETE_ALL_COOKIES)
+
+ def add_cookie(self, cookie_dict):
+ self._execute(Command.ADD_COOKIE, {'cookie': cookie_dict})
+
+ def _find_element_by(self, by, value):
+ return self._execute(Command.FIND_ELEMENT,
+ {'using': by, 'value': value})['value']
+
+ def _find_elements_by(self, by, value):
+ return self._execute(Command.FIND_ELEMENTS,
+ {'using': by, 'value': value})['value']
+
+ def get_screenshot_as_file(self, filename):
+ """Gets the screenshot of the current window. Returns False if there is
+ any IOError, else returns True."""
+ png = self._execute(Command.SCREENSHOT)['value']
+ f = None
+ try:
+ f = open(filename, 'w')
+ f.write(base64.decode(png))
+ f.close()
+ except IOError:
+ return False
+
+ return True
+
+ def get_screenshot_as_base64(self):
+ """Gets the screenshot of the current window as a base64 encoded string which
+ is useful in embedded images in HTML."""
+ return self._execute(Command.SCREENSHOT)['value']
+
+
+def connect(name, version="", server="http://localhost:4444", platform=None,
+ javascript_enabled=True, path="/wd/hub"):
+ """Convenience function to connect to a server
+ Args:
+ name - A string indicating which browser to request a new
+ session for from the remote server. Should be one of
+ {mobile safari|firefox|internet explorer|htmlunit|chrome}.
+ version - A string indicating a specific browser version to request,
+ or an empty string ot use any available browser. Defaults to the
+ empty string.
+ server - Server location (without path). Defaults to
+ "http://localhost:4444".
+ platform - A string indicating the desired platform to request from
+ the remote server. Should be one of
+ {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY} or None. Defaults to None.
+ javascript_enabled - Whether the requested browser should support
+ JavaScript. Defaults to True.
+ path - path in server url. Defaults to "/wd/hub/"
+ """
+ if not path.startswith("/"):
+ path = "/" + path
+ url = "%s%s" % (server, path)
+ wd = WebDriver(url, name, platform, version, javascript_enabled)
+
+ return wd
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/__init__.py b/third_party/webdriver/py/selenium/remote/webdriver/__init__.py
new file mode 100644
index 0000000..5555f0b
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/__init__.py
@@ -0,0 +1,16 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/command.py b/third_party/webdriver/py/selenium/remote/webdriver/command.py
new file mode 100644
index 0000000..3eae3b1
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/command.py
@@ -0,0 +1,79 @@
+# Copyright 2010 WebDriver committers
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class Command(object):
+ """Defines constants for the standard WebDriver commands.
+
+ While these constants have no meaning in and of themselves, they are
+ used to marshal commands through a service that implements WebDriver's
+ remote wire protocol:
+ http://code.google.com/p/selenium/wiki/JsonWireProtocol
+ """
+
+ # Keep in sync with org.openqa.selenium.remote.DriverCommand
+
+ NEW_SESSION = "newSession"
+ DELETE_SESSION = "deleteSession"
+ CLOSE = "close"
+ QUIT = "quit"
+ GET = "get"
+ REFRESH = "refresh"
+ GO_BACK = "goBack"
+ GO_FORWARD = "goForward"
+ REFRESH = "refresh"
+ ADD_COOKIE = "addCookie"
+ GET_COOKIE = "getCookie"
+ GET_ALL_COOKIES = "getCookies"
+ DELETE_COOKIE = "deleteCookie"
+ DELETE_ALL_COOKIES = "deleteAllCookies"
+ FIND_ELEMENT = "findElement"
+ FIND_ELEMENTS = "findElements"
+ FIND_CHILD_ELEMENT = "findChildElement"
+ FIND_CHILD_ELEMENTS = "findChildElements"
+ CLEAR_ELEMENT = "clearElement"
+ CLICK_ELEMENT = "clickElement"
+ HOVER_OVER_ELEMENT = "hoverOverElement"
+ SEND_KEYS_TO_ELEMENT = "sendKeysToElement"
+ SUBMIT_ELEMENT = "submitElement"
+ TOGGLE_ELEMENT = "toggleElement"
+ GET_CURRENT_WINDOW_HANDLE = "getCurrentWindowHandle"
+ GET_WINDOW_HANDLES = "getWindowHandles"
+ SWITCH_TO_WINDOW = "switchToWindow"
+ SWITCH_TO_FRAME = "switchToFrame"
+ GET_ACTIVE_ELEMENT = "getActiveElement"
+ GET_CURRENT_URL = "getCurrentUrl"
+ GET_PAGE_SOURCE = "getPageSource"
+ GET_TITLE = "getTitle"
+ EXECUTE_SCRIPT = "executeScript"
+ GET_SPEED = "getSpeed"
+ SET_SPEED = "setSpeed"
+ SET_BROWSER_VISIBLE = "setBrowserVisible"
+ IS_BROWSER_VISIBLE = "isBrowserVisible"
+ GET_ELEMENT_TEXT = "getElementText"
+ GET_ELEMENT_VALUE = "getElementValue"
+ GET_ELEMENT_TAG_NAME = "getElementTagName"
+ SET_ELEMENT_SELECTED = "setElementSelected"
+ DRAG_ELEMENT = "dragElement"
+ IS_ELEMENT_SELECTED = "isElementSelected"
+ IS_ELEMENT_ENABLED = "isElementEnabled"
+ IS_ELEMENT_DISPLAYED = "isElementDisplayed"
+ GET_ELEMENT_LOCATION = "getElementLocation"
+ GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW = (
+ "getElementLocationOnceScrolledIntoView")
+ GET_ELEMENT_SIZE = "getElementSize"
+ GET_ELEMENT_ATTRIBUTE = "getElementAttribute"
+ GET_ELEMENT_VALUE_OF_CSS_PROPERTY = "getElementValueOfCssProperty"
+ ELEMENT_EQUALS = "elementEquals"
+ SCREENSHOT = "screenshot"
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/errorhandler.py b/third_party/webdriver/py/selenium/remote/webdriver/errorhandler.py
new file mode 100644
index 0000000..98058c1
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/errorhandler.py
@@ -0,0 +1,98 @@
+# Copyright 2010 WebDriver committers
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from selenium.common.exceptions import ElementNotSelectableException
+from selenium.common.exceptions import ElementNotVisibleException
+from selenium.common.exceptions import InvalidCookieDomainException
+from selenium.common.exceptions import InvalidElementStateException
+from selenium.common.exceptions import NoSuchElementException
+from selenium.common.exceptions import NoSuchFrameException
+from selenium.common.exceptions import NoSuchWindowException
+from selenium.common.exceptions import StaleElementReferenceException
+from selenium.common.exceptions import UnableToSetCookieException
+from selenium.common.exceptions import ErrorInResponseException
+
+
+class ErrorCode(object):
+ """Error codes defined in the WebDriver wire protocol."""
+
+ # Keep in sync with org.openqa.selenium.remote.ErrorCodes and errorcodes.h
+
+ SUCCESS = 0
+ NO_SUCH_ELEMENT = 7
+ NO_SUCH_FRAME = 8
+ UNKNOWN_COMMAND = 9
+ STALE_ELEMENT_REFERENCE = 10
+ ELEMENT_NOT_VISIBLE = 11
+ INVALID_ELEMENT_STATE = 12
+ UNKNOWN_ERROR = 13
+ ELEMENT_IS_NOT_SELECTABLE = 15
+ XPATH_LOOKUP_ERROR = 19
+ NO_SUCH_WINDOW = 23
+ INVALID_COOKIE_DOMAIN = 24
+ UNABLE_TO_SET_COOKIE = 25
+
+
+class ErrorHandler(object):
+ """Handles errors returned by the WebDriver server."""
+
+ def check_response(self, response):
+ """Checks that a JSON response from the WebDriver does not have an error.
+
+ Args:
+ response - The JSON response from the WebDriver server as a dictionary
+ object.
+
+ Raises:
+ If the response contains an error message.
+ """
+ status = response['status']
+ if status == ErrorCode.SUCCESS:
+ return
+
+ exception_class = ErrorInResponseException
+ if status == ErrorCode.NO_SUCH_ELEMENT:
+ exception_class = NoSuchElementException
+ elif status == ErrorCode.NO_SUCH_FRAME:
+ exception_class = NoSuchFrameException
+ elif status == ErrorCode.NO_SUCH_WINDOW:
+ exception_class = NoSuchWindowException
+ elif status == ErrorCode.STALE_ELEMENT_REFERENCE:
+ exception_class = StaleElementReferenceException
+ elif status == ErrorCode.ELEMENT_NOT_VISIBLE:
+ exception_class = ElementNotVisibleException
+ elif status == ErrorCode.INVALID_ELEMENT_STATE:
+ exception_class = InvalidElementStateException
+ elif status == ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
+ exception_class = ElementNotSelectableException
+ elif status == ErrorCode.INVALID_COOKIE_DOMAIN:
+ exception_class = InvalidCookieDomainException
+ elif status == ErrorCode.UNABLE_TO_SET_COOKIE:
+ exception_class = UnableToSetCookieException
+
+ value = response['value']
+ if type(value) is str:
+ if exception_class == ErrorInResponseException:
+ raise exception_class(response, value)
+ raise exception_class(value)
+
+ message = ''
+ if 'message' in value:
+ message = value['message']
+
+ # TODO: What about 'screen' and 'stackTrace'?
+ if exception_class == ErrorInResponseException:
+ raise exception_class(response, message)
+ raise exception_class(message)
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/remote_connection.py b/third_party/webdriver/py/selenium/remote/webdriver/remote_connection.py
new file mode 100644
index 0000000..6c23684
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/remote_connection.py
@@ -0,0 +1,245 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import string
+import urllib2
+
+from command import Command
+import utils
+
+
+class Request(urllib2.Request):
+ """Extends the urllib2.Request to support all HTTP request types."""
+
+ def __init__(self, url, data=None, method=None):
+ """Initialise a new HTTP request.
+
+ Args:
+ url - String for the URL to send the request to.
+ data - Data to send with the request.
+ """
+ if method is None:
+ method = data is not None and 'POST' or 'GET'
+ elif method != 'POST' and method != 'PUT':
+ data = None
+ self._method = method
+ urllib2.Request.__init__(self, url, data=data)
+
+ def get_method(self):
+ """Returns the HTTP method used by this request."""
+ return self._method
+
+
+class Response(object):
+ """Represents an HTTP response.
+
+ Attributes:
+ fp - File object for the response body.
+ code - The HTTP status code returned by the server.
+ headers - A dictionary of headers returned by the server.
+ url - URL of the retrieved resource represented by this Response.
+ """
+
+ def __init__(self, fp, code, headers, url):
+ """Initialise a new Response.
+
+ Args:
+ fp - The response body file object.
+ code - The HTTP status code returned by the server.
+ headers - A dictionary of headers returned by the server.
+ url - URL of the retrieved resource represented by this Response.
+ """
+ self.fp = fp
+ self.read = fp.read
+ self.code = code
+ self.headers = headers
+ self.url = url
+
+ def close(self):
+ """Close the response body file object."""
+ self.read = None
+ self.fp = None
+
+ def info(self):
+ """Returns the response headers."""
+ return self.headers
+
+ def geturl(self):
+ """Returns the URL for the resource returned in this response."""
+ return self.url
+
+
+class HttpErrorHandler(urllib2.HTTPDefaultErrorHandler):
+ """A custom HTTP error handler.
+
+ Used to return Response objects instead of raising an HTTPError exception.
+ """
+
+ def http_error_default(self, req, fp, code, msg, headers):
+ """Default HTTP error handler.
+
+ Args:
+ req - The original Request object.
+ fp - The response body file object.
+ code - The HTTP status code returned by the server.
+ msg - The HTTP status message returned by the server.
+ headers - The response headers.
+
+ Returns:
+ A new Response object.
+ """
+ return Response(fp, code, headers, req.get_full_url())
+
+
+class RemoteConnection(object):
+ """A connection with the Remote WebDriver server.
+
+ Communicates with the server using the WebDriver wire protocol:
+ http://code.google.com/p/selenium/wiki/JsonWireProtocol
+ """
+
+ def __init__(self, remote_server_addr):
+ self._url = remote_server_addr
+ self._commands = {
+ Command.NEW_SESSION: ('POST', '/session'),
+ Command.QUIT: ('DELETE', '/session/$sessionId'),
+ Command.GET_CURRENT_WINDOW_HANDLE:
+ ('GET', '/session/$sessionId/window_handle'),
+ Command.GET_WINDOW_HANDLES:
+ ('GET', '/session/$sessionId/window_handles'),
+ Command.GET: ('POST', '/session/$sessionId/url'),
+ Command.GO_FORWARD: ('POST', '/session/$sessionId/forward'),
+ Command.GO_BACK: ('POST', '/session/$sessionId/back'),
+ Command.REFRESH: ('POST', '/session/$sessionId/refresh'),
+ Command.EXECUTE_SCRIPT: ('POST', '/session/$sessionId/execute'),
+ Command.GET_CURRENT_URL: ('GET', '/session/$sessionId/url'),
+ Command.GET_TITLE: ('GET', '/session/$sessionId/title'),
+ Command.GET_PAGE_SOURCE: ('GET', '/session/$sessionId/source'),
+ Command.SCREENSHOT: ('GET', '/session/$sessionId/screenshot'),
+ Command.SET_BROWSER_VISIBLE:
+ ('POST', '/session/$sessionId/visible'),
+ Command.IS_BROWSER_VISIBLE: ('GET', '/session/$sessionId/visible'),
+ Command.FIND_ELEMENT: ('POST', '/session/$sessionId/element'),
+ Command.FIND_ELEMENTS: ('POST', '/session/$sessionId/elements'),
+ Command.GET_ACTIVE_ELEMENT:
+ ('POST', '/session/$sessionId/element/active'),
+ Command.FIND_CHILD_ELEMENT:
+ ('POST', '/session/$sessionId/element/$id/element'),
+ Command.FIND_CHILD_ELEMENTS:
+ ('POST', '/session/$sessionId/element/$id/elements'),
+ Command.CLICK_ELEMENT: ('POST', '/session/$sessionId/element/$id/click'),
+ Command.CLEAR_ELEMENT: ('POST', '/session/$sessionId/element/$id/clear'),
+ Command.SUBMIT_ELEMENT: ('POST', '/session/$sessionId/element/$id/submit'),
+ Command.GET_ELEMENT_TEXT: ('GET', '/session/$sessionId/element/$id/text'),
+ Command.SEND_KEYS_TO_ELEMENT:
+ ('POST', '/session/$sessionId/element/$id/value'),
+ Command.GET_ELEMENT_VALUE:
+ ('GET', '/session/$sessionId/element/$id/value'),
+ Command.GET_ELEMENT_TAG_NAME:
+ ('GET', '/session/$sessionId/element/$id/name'),
+ Command.IS_ELEMENT_SELECTED:
+ ('GET', '/session/$sessionId/element/$id/selected'),
+ Command.SET_ELEMENT_SELECTED:
+ ('POST', '/session/$sessionId/element/$id/selected'),
+ Command.TOGGLE_ELEMENT:
+ ('POST', '/session/$sessionId/element/$id/toggle'),
+ Command.IS_ELEMENT_ENABLED:
+ ('GET', '/session/$sessionId/element/$id/enabled'),
+ Command.IS_ELEMENT_DISPLAYED:
+ ('GET', '/session/$sessionId/element/$id/displayed'),
+ Command.HOVER_OVER_ELEMENT:
+ ('POST', '/session/$sessionId/element/$id/hover'),
+ Command.GET_ELEMENT_LOCATION:
+ ('GET', '/session/$sessionId/element/$id/location'),
+ Command.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW:
+ ('GET', '/session/$sessionId/element/$id/location_in_view'),
+ Command.GET_ELEMENT_SIZE:
+ ('GET', '/session/$sessionId/element/$id/size'),
+ Command.GET_ELEMENT_ATTRIBUTE:
+ ('GET', '/session/$sessionId/element/$id/attribute/$name'),
+ Command.ELEMENT_EQUALS:
+ ('GET', '/session/$sessionId/element/$id/equals/$other'),
+ Command.GET_ALL_COOKIES: ('GET', '/session/$sessionId/cookie'),
+ Command.ADD_COOKIE: ('POST', '/session/$sessionId/cookie'),
+ Command.DELETE_ALL_COOKIES:
+ ('DELETE', '/session/$sessionId/cookie'),
+ Command.DELETE_COOKIE:
+ ('DELETE', '/session/$sessionId/cookie/$name'),
+ Command.SWITCH_TO_FRAME: ('POST', '/session/$sessionId/frame'),
+ Command.SWITCH_TO_WINDOW: ('POST', '/session/$sessionId/window'),
+ Command.CLOSE: ('DELETE', '/session/$sessionId/window'),
+ Command.DRAG_ELEMENT:
+ ('POST', '/session/$sessionId/element/$id/drag'),
+ Command.GET_SPEED: ('GET', '/session/$sessionId/speed'),
+ Command.SET_SPEED: ('POST', '/session/$sessionId/speed'),
+ Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY:
+ ('GET', '/session/$sessionId/element/$id/css/$propertyName')
+ }
+
+ def execute(self, command, params):
+ """Send a command to the remote server.
+
+ Any path subtitutions required for the URL mapped to the command should be
+ included in the command parameters.
+
+ Args:
+ command - A string specifying the command to execute.
+ params - A dictionary of named parameters to send with the command as
+ its JSON payload.
+ """
+ command_info = self._commands[command]
+ assert command_info is not None, 'Unrecognised command %s' % command
+ data = utils.dump_json(params)
+ path = string.Template(command_info[1]).substitute(params)
+ url = '%s%s' % (self._url, path)
+ return self._request(url, method=command_info[0], data=data)
+
+ def _request(self, url, data=None, method=None):
+ """Send an HTTP request to the remote server.
+
+ Args:
+ method - A string for the HTTP method to send the request with.
+ url - The URL to send the request to.
+ body - The message body to send.
+
+ Returns:
+ A dictionary with the server's parsed JSON response.
+ """
+ logging.debug('%s %s %s' % (method, url, data))
+
+ request = Request(url, data=data, method=method)
+ request.add_header('Accept', 'application/json')
+
+ opener = urllib2.build_opener(urllib2.HTTPRedirectHandler(),
+ HttpErrorHandler())
+ response = opener.open(request)
+ try:
+ if response.code > 399 and response.code < 500:
+ return {'status': response.code, 'value': response.read()}
+ body = response.read().replace('\x00', '').strip()
+ if body:
+ data = utils.load_json(body.strip())
+ assert type(data) is dict, (
+ 'Invalid server response body: %s' % body)
+ assert 'status' in data, (
+ 'Invalid server response; no status: %s' % body)
+ # Some of the drivers incorrectly return a response
+ # with no 'value' field when they should return null.
+ if 'value' not in data:
+ data['value'] = None
+ return data
+ finally:
+ response.close()
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/utils.py b/third_party/webdriver/py/selenium/remote/webdriver/utils.py
new file mode 100644
index 0000000..07d4aa6
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/utils.py
@@ -0,0 +1,112 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import tempfile
+import zipfile
+
+try:
+ import json
+except ImportError: # < 2.6
+ import simplejson as json
+
+if not hasattr(json, 'dumps'):
+ import simplejson as json
+
+from selenium.common.exceptions import NoSuchElementException
+
+
+def format_json(json_struct):
+ return json.dumps(json_struct, indent=4)
+
+def dump_json(json_struct):
+ return json.dumps(json_struct)
+
+def load_json(s):
+ return json.loads(s)
+
+def handle_find_element_exception(e):
+ if ("Unable to find" in e.response["value"]["message"] or
+ "Unable to locate" in e.response["value"]["message"]):
+ raise NoSuchElementException("Unable to locate element:")
+ else:
+ raise e
+
+def return_value_if_exists(resp):
+ if resp and "value" in resp:
+ return resp["value"]
+
+def get_root_parent(elem):
+ parent = elem.parent
+ while True:
+ try:
+ parent.parent
+ parent = parent.parent
+ except AttributeError:
+ return parent
+
+def unzip_to_temp_dir(zip_file_name):
+ """Unzip zipfile to a temporary directory.
+
+ The directory of the unzipped files is returned if success,
+ otherwise None is returned. """
+ if not zip_file_name or not os.path.exists(zip_file_name):
+ return None
+
+ zf = zipfile.ZipFile(zip_file_name)
+
+ if zf.testzip() is not None:
+ return None
+
+ # Unzip the files into a temporary directory
+ logging.info("Extracting zipped file: %s" % zip_file_name)
+ tempdir = tempfile.mkdtemp()
+
+ try:
+ # Create directories that don't exist
+ for zip_name in zf.namelist():
+ # We have no knowledge on the os where the zipped file was
+ # created, so we restrict to zip files with paths without
+ # charactor "\" and "/".
+ name = (zip_name.replace("\\", os.path.sep).
+ replace("/", os.path.sep))
+ dest = os.path.join(tempdir, name)
+ if (name.endswith(os.path.sep) and not os.path.exists(dest)):
+ os.mkdir(dest)
+ logging.debug("Directory %s created." % dest)
+
+ # Copy files
+ for zip_name in zf.namelist():
+ # We have no knowledge on the os where the zipped file was
+ # created, so we restrict to zip files with paths without
+ # charactor "\" and "/".
+ name = (zip_name.replace("\\", os.path.sep).
+ replace("/", os.path.sep))
+ dest = os.path.join(tempdir, name)
+ if not (name.endswith(os.path.sep)):
+ logging.debug("Copying file %s......" % dest)
+ outfile = open(dest, 'wb')
+ outfile.write(zf.read(zip_name))
+ outfile.close()
+ logging.debug("File %s copied." % dest)
+
+ logging.info("Unzipped file can be found at %s" % tempdir)
+ return tempdir
+
+ except IOError, err:
+ logging.error(
+ "Error in extracting webdriver.xpi: %s" % err)
+ return None
diff --git a/third_party/webdriver/py/selenium/remote/webdriver/webelement.py b/third_party/webdriver/py/selenium/remote/webdriver/webelement.py
new file mode 100644
index 0000000..e924a4c
--- /dev/null
+++ b/third_party/webdriver/py/selenium/remote/webdriver/webelement.py
@@ -0,0 +1,162 @@
+# Copyright 2008-2009 WebDriver committers
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""WebElement implementation."""
+from command import Command
+
+from selenium.common.exceptions import NoSuchAttributeException
+
+class WebElement(object):
+ """Represents an HTML element.
+
+ Generally, all interesting operations to do with interacting with a page
+ will be performed through this interface."""
+ def __init__(self, parent, id_):
+ self._parent = parent
+ self._id = id_
+
+ def get_text(self):
+ """Gets the text of the element."""
+ return self._execute(Command.GET_ELEMENT_TEXT)['value']
+
+ def click(self):
+ """Clicks the element."""
+ self._execute(Command.CLICK_ELEMENT)
+
+ def submit(self):
+ """Submits a form."""
+ self._execute(Command.SUBMIT_ELEMENT)
+
+ def get_value(self):
+ """Gets the value of the element's value attribute."""
+ return self._execute(Command.GET_ELEMENT_VALUE)['value']
+
+ def clear(self):
+ """Clears the text if it's a text entry element."""
+ self._execute(Command.CLEAR_ELEMENT)
+
+ def get_attribute(self, name):
+ """Gets the attribute value."""
+ try:
+ resp = self._execute(Command.GET_ELEMENT_ATTRIBUTE, {'name':name})
+ return str(resp['value'])
+ # FIXME: This is a hack around selenium server bad response, remove this
+ # code when it's fixed
+ except AssertionError, e:
+ raise NoSuchAttributeException(name)
+
+ def toggle(self):
+ """Toggles the element state."""
+ self._execute(Command.TOGGLE_ELEMENT)
+
+ def is_selected(self):
+ """Whether the element is selected."""
+ return self._execute(Command.IS_ELEMENT_SELECTED)['value']
+
+ def set_selected(self):
+ """Selects an elmeent."""
+ self._execute(Command.SET_ELEMENT_SELECTED)
+
+ def is_enabled(self):
+ """Whether the element is enabled."""
+ return self._execute(Command.IS_ELEMENT_ENABLED)['value']
+
+ def is_displayed(self):
+ """Whether the element would be visible to a user"""
+ return self._execute(Command.IS_ELEMENT_DISPLAYED)['value']
+
+ def find_element_by_id(self, id_):
+ """Finds element by id."""
+ return self._get_elem_by("id", id_)
+
+ def find_elements_by_id(self, id_):
+ return self._get_elems_by("id", id_)
+
+ def find_element_by_name(self, name):
+ """Find element by name."""
+ return self._get_elem_by("name", name)
+
+ def find_elements_by_name(self, name):
+ return self._get_elems_by("name", name)
+
+ def find_element_by_link_text(self, link_text):
+ """Finds element by link text."""
+ return self._get_elem_by("link text", link_text)
+
+ def find_elements_by_link_text(self, link_text):
+ return self._get_elems_by("link text", link_text)
+
+ def find_element_by_partial_link_text(self, link_text):
+ return self._get_elem_by("partial link text", link_text)
+
+ def find_elements_by_partial_link_text(self, link_text):
+ return self._get_elems_by("partial link text", link_text)
+
+ def find_element_by_tag_name(self, name):
+ return self._get_elem_by("tag name", name)
+
+ def find_elements_by_tag_name(self, name):
+ return self._get_elems_by("tag name", name)
+
+ def find_element_by_xpath(self, xpath):
+ """Finds element by xpath."""
+ return self._get_elem_by("xpath", xpath)
+
+ def find_elements_by_xpath(self, xpath):
+ """Finds elements within the elements by xpath."""
+ return self._get_elems_by("xpath", xpath)
+
+ def find_element_by_class_name(self, name):
+ """Finds an element by their class name."""
+ return self._get_elem_by("class name", name)
+
+ def find_elements_by_class_name(self, name):
+ """Finds elements by their class name."""
+ return self._get_elems_by("class name", name)
+
+ def send_keys(self, *value):
+ """Simulates typing into the element."""
+ self._execute(Command.SEND_KEYS_TO_ELEMENT, {'value':value})
+
+ @property
+ def parent(self):
+ return self._parent
+
+ @property
+ def id(self):
+ return self._id
+
+ def _execute(self, command, params=None):
+ """Executes a command against the underlying HTML element.
+
+ Args:
+ command: The name of the command to execute as a string.
+ params: A dictionary of named parameters to send with the command.
+
+ Returns:
+ The command's JSON response loaded into a dictionary object.
+ """
+ if not params:
+ params = {}
+ params['id'] = self._id
+ return self._parent._execute(command, params)
+
+ def _get_elem_by(self, by, value):
+ return self._execute(Command.FIND_CHILD_ELEMENT,
+ {"using": by, "value": value})['value']
+
+ def _get_elems_by(self, by, value):
+ return self._execute(Command.FIND_CHILD_ELEMENTS,
+ {"using": by, "value": value})['value']