| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // 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. |
| |
| part of webdriver.core; |
| |
| class WebDriver implements SearchContext { |
| final CommandProcessor _commandProcessor; |
| final Uri _prefix; |
| final Map<String, dynamic> capabilities; |
| final String id; |
| final Uri uri; |
| |
| final _onCommandController = |
| new StreamController<WebDriverCommandEvent>.broadcast(); |
| |
| WebDriver(this._commandProcessor, Uri uri, String id, this.capabilities) |
| : this.uri = uri, |
| this.id = id, |
| this._prefix = uri.resolve('session/$id/'); |
| |
| Stream<WebDriverCommandEvent> get onCommand => _onCommandController.stream; |
| |
| /// The current url. |
| Future<String> get currentUrl => getRequest('url'); |
| |
| /// navigate to the specified url |
| Future get(/* Uri | String */ url) async { |
| if (url is Uri) { |
| url = url.toString(); |
| } |
| await postRequest('url', {'url': url as String}); |
| } |
| |
| /// The title of the current page. |
| Future<String> get title => getRequest('title'); |
| |
| /// Search for multiple elements within the entire current page. |
| @override |
| Stream<WebElement> findElements(By by) async* { |
| var elements = await postRequest('elements', by); |
| int i = 0; |
| |
| for (var element in elements) { |
| yield new WebElement._(this, element[_element], this, by, i); |
| i++; |
| } |
| } |
| |
| /// Search for an element within the entire current page. |
| /// Throws [NoSuchElementException] if a matching element is not found. |
| @override |
| Future<WebElement> findElement(By by) async { |
| var element = await postRequest('element', by); |
| return new WebElement._(this, element[_element], this, by); |
| } |
| |
| /// An artist's rendition of the current page's source. |
| Future<String> get pageSource => getRequest('source'); |
| |
| /// Close the current window, quitting the browser if it is the last window. |
| Future close() async { |
| await deleteRequest('window'); |
| } |
| |
| /// Quit the browser. |
| Future quit() async { |
| try { |
| await _commandProcessor.delete(uri.resolve('session/$id')); |
| } finally { |
| await _commandProcessor.close(); |
| } |
| } |
| |
| /// Handles for all of the currently displayed tabs/windows. |
| Stream<Window> get windows async* { |
| var handles = await getRequest('window_handles'); |
| |
| for (var handle in handles) { |
| yield new Window._(this, handle); |
| } |
| } |
| |
| /// Handle for the active tab/window. |
| Future<Window> get window async { |
| var handle = await getRequest('window_handle'); |
| return new Window._(this, handle); |
| } |
| |
| /// The currently focused element, or the body element if no element has |
| /// focus. |
| Future<WebElement> get activeElement async { |
| var element = await postRequest('element/active'); |
| if (element != null) { |
| return new WebElement._(this, element[_element], this, 'activeElement'); |
| } |
| return null; |
| } |
| |
| TargetLocator get switchTo => new TargetLocator._(this); |
| |
| Navigation get navigate => new Navigation._(this); |
| |
| Cookies get cookies => new Cookies._(this); |
| |
| Logs get logs => new Logs._(this); |
| |
| Timeouts get timeouts => new Timeouts._(this); |
| |
| Keyboard get keyboard => new Keyboard._(this); |
| |
| Mouse get mouse => new Mouse._(this); |
| |
| /// Take a screenshot of the current page as PNG. |
| Stream<int> captureScreenshot() async* { |
| var encoded = await getRequest('screenshot'); |
| yield* new Stream.fromIterable(CryptoUtils.base64StringToBytes(encoded)); |
| } |
| |
| /// Inject a snippet of JavaScript into the page for execution in the context |
| /// of the currently selected frame. The executed script is assumed to be |
| /// asynchronous and must signal that is done by invoking the provided |
| /// callback, which is always provided as the final argument to the function. |
| /// The value to this callback will be returned to the client. |
| /// |
| /// Asynchronous script commands may not span page loads. If an unload event |
| /// is fired while waiting for a script result, an error will be thrown. |
| /// |
| /// The script argument defines the script to execute in the form of a |
| /// function body. The function will be invoked with the provided args array |
| /// and the values may be accessed via the arguments object in the order |
| /// specified. The final argument will always be a callback function that must |
| /// be invoked to signal that the script has finished. |
| /// |
| /// Arguments may be any JSON-able object. WebElements will be converted to |
| /// the corresponding DOM element. Likewise, any DOM Elements in the script |
| /// result will be converted to WebElements. |
| Future executeAsync(String script, List args) => postRequest( |
| 'execute_async', {'script': script, 'args': args}) |
| .then(_recursiveElementify); |
| |
| /// Inject a snippet of JavaScript into the page for execution in the context |
| /// of the currently selected frame. The executed script is assumed to be |
| /// synchronous and the result of evaluating the script is returned. |
| /// |
| /// The script argument defines the script to execute in the form of a |
| /// function body. The value returned by that function will be returned to the |
| /// client. The function will be invoked with the provided args array and the |
| /// values may be accessed via the arguments object in the order specified. |
| /// |
| /// Arguments may be any JSON-able object. WebElements will be converted to |
| /// the corresponding DOM element. Likewise, any DOM Elements in the script |
| /// result will be converted to WebElements. |
| Future execute(String script, List args) => postRequest( |
| 'execute', {'script': script, 'args': args}).then(_recursiveElementify); |
| |
| dynamic _recursiveElementify(result) { |
| if (result is Map) { |
| if (result.length == 1 && result.containsKey(_element)) { |
| return new WebElement._(this, result[_element], this, 'javascript'); |
| } else { |
| var newResult = {}; |
| result.forEach((key, value) { |
| newResult[key] = _recursiveElementify(value); |
| }); |
| return newResult; |
| } |
| } else if (result is List) { |
| return result.map((value) => _recursiveElementify(value)).toList(); |
| } else { |
| return result; |
| } |
| } |
| |
| Future postRequest(String command, [params]) => _performRequest( |
| () => _commandProcessor.post(_resolve(command), params), 'POST', command, |
| params); |
| |
| Future getRequest(String command) => _performRequest( |
| () => _commandProcessor.get(_resolve(command)), 'GET', command, null); |
| |
| Future deleteRequest(String command) => _performRequest( |
| () => _commandProcessor.delete(_resolve(command)), 'DELETE', command, |
| null); |
| |
| Future _performRequest( |
| Function fn, String method, String command, params) async { |
| var startTime = new DateTime.now(); |
| var trace = new Trace.current(1); |
| var result; |
| var exception; |
| try { |
| result = await fn(); |
| return result; |
| } catch (e) { |
| exception = e; |
| rethrow; |
| } finally { |
| _onCommandController.add(new WebDriverCommandEvent( |
| method: method, |
| endPoint: command, |
| params: params, |
| startTime: startTime, |
| endTime: new DateTime.now(), |
| exception: exception, |
| result: result, |
| stackTrace: trace)); |
| } |
| } |
| |
| Uri _resolve(String command) { |
| var uri = _prefix.resolve(command); |
| if (uri.path.endsWith('/')) { |
| uri = uri.replace(path: uri.path.substring(0, uri.path.length - 1)); |
| } |
| return uri; |
| } |
| |
| @override |
| WebDriver get driver => this; |
| |
| @override |
| String toString() => 'WebDriver($_prefix)'; |
| } |