selenium-webdriver 2.43.5 → 2.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +20 -0
- package/README.md +8 -13
- package/builder.js +4 -3
- package/docs/class_webdriver_Alert.html +6 -6
- package/docs/class_webdriver_AlertPromise.html +6 -6
- package/docs/class_webdriver_UnhandledAlertError.html +4 -4
- package/docs/class_webdriver_WebDriver.html +39 -37
- package/docs/class_webdriver_WebDriver_Logs.html +3 -3
- package/docs/class_webdriver_WebDriver_Navigation.html +5 -5
- package/docs/class_webdriver_WebDriver_Options.html +9 -9
- package/docs/class_webdriver_WebDriver_TargetLocator.html +6 -6
- package/docs/class_webdriver_WebDriver_Timeouts.html +4 -4
- package/docs/class_webdriver_WebDriver_Window.html +6 -6
- package/docs/class_webdriver_WebElement.html +23 -23
- package/docs/class_webdriver_WebElementPromise.html +22 -22
- package/docs/class_webdriver_promise_CancellationError.html +3 -0
- package/docs/class_webdriver_promise_ControlFlow.html +32 -32
- package/docs/class_webdriver_promise_Deferred.html +7 -8
- package/docs/class_webdriver_promise_Frame_.html +14 -30
- package/docs/class_webdriver_promise_Promise.html +2 -2
- package/docs/class_webdriver_promise_Task_.html +12 -12
- package/docs/class_webdriver_until_Condition.html +3 -0
- package/docs/enum_webdriver_Key.html +1 -1
- package/docs/enum_webdriver_promise_ControlFlow_EventType.html +1 -1
- package/docs/enum_webdriver_promise_Deferred_State_.html +1 -1
- package/docs/index.html +8 -13
- package/docs/interface_webdriver_promise_Thenable.html +8 -10
- package/docs/license.html +1 -1
- package/docs/module_selenium-webdriver.html +2 -2
- package/docs/module_selenium-webdriver__base.html +1 -1
- package/docs/module_selenium-webdriver__base_class_Context.html +1 -1
- package/docs/module_selenium-webdriver_builder.html +1 -1
- package/docs/module_selenium-webdriver_builder_class_Builder.html +1 -1
- package/docs/module_selenium-webdriver_chrome.html +1 -1
- package/docs/module_selenium-webdriver_chrome_class_Driver.html +2 -2
- package/docs/module_selenium-webdriver_chrome_class_Options.html +2 -2
- package/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html +1 -1
- package/docs/module_selenium-webdriver_error.html +1 -1
- package/docs/module_selenium-webdriver_executors.html +1 -1
- package/docs/module_selenium-webdriver_firefox.html +1 -1
- package/docs/module_selenium-webdriver_firefox_binary.html +1 -1
- package/docs/module_selenium-webdriver_firefox_binary_class_Binary.html +1 -1
- package/docs/module_selenium-webdriver_firefox_class_Driver.html +2 -2
- package/docs/module_selenium-webdriver_firefox_class_Options.html +1 -1
- package/docs/module_selenium-webdriver_firefox_extension.html +1 -1
- package/docs/module_selenium-webdriver_firefox_profile.html +1 -1
- package/docs/module_selenium-webdriver_firefox_profile_class_Profile.html +1 -1
- package/docs/module_selenium-webdriver_http.html +1 -1
- package/docs/module_selenium-webdriver_http_class_HttpClient.html +1 -1
- package/docs/module_selenium-webdriver_http_util.html +1 -1
- package/docs/module_selenium-webdriver_io.html +1 -1
- package/docs/module_selenium-webdriver_io_exec.html +1 -1
- package/docs/module_selenium-webdriver_net.html +1 -1
- package/docs/module_selenium-webdriver_net_portprober.html +1 -1
- package/docs/module_selenium-webdriver_phantomjs.html +1 -1
- package/docs/module_selenium-webdriver_phantomjs_class_Driver.html +2 -2
- package/docs/module_selenium-webdriver_proxy.html +1 -1
- package/docs/module_selenium-webdriver_remote.html +1 -1
- package/docs/module_selenium-webdriver_remote_class_DriverService.html +2 -2
- package/docs/module_selenium-webdriver_remote_class_SeleniumServer.html +2 -2
- package/docs/module_selenium-webdriver_testing.html +16 -29
- package/docs/module_selenium-webdriver_testing_assert.html +1 -1
- package/docs/namespace_webdriver_promise.html +18 -20
- package/docs/namespace_webdriver_stacktrace.html +15 -15
- package/docs/namespace_webdriver_until.html +32 -0
- package/docs/source/_base.js.src.html +1 -1
- package/docs/source/builder.js.src.html +1 -1
- package/docs/source/chrome.js.src.html +1 -1
- package/docs/source/error.js.src.html +1 -1
- package/docs/source/executors.js.src.html +1 -1
- package/docs/source/firefox/binary.js.src.html +1 -1
- package/docs/source/firefox/extension.js.src.html +1 -1
- package/docs/source/firefox/index.js.src.html +1 -1
- package/docs/source/firefox/profile.js.src.html +1 -1
- package/docs/source/http/index.js.src.html +1 -1
- package/docs/source/http/util.js.src.html +1 -1
- package/docs/source/index.js.src.html +1 -1
- package/docs/source/io/exec.js.src.html +1 -1
- package/docs/source/io/index.js.src.html +1 -1
- package/docs/source/lib/atoms/error.js.src.html +1 -1
- package/docs/source/lib/atoms/json.js.src.html +1 -1
- package/docs/source/lib/atoms/response.js.src.html +1 -1
- package/docs/source/lib/atoms/userAgent.js.src.html +1 -1
- package/docs/source/lib/goog/array/array.js.src.html +1 -1
- package/docs/source/lib/goog/asserts/asserts.js.src.html +1 -1
- package/docs/source/lib/goog/async/nexttick.js.src.html +1 -1
- package/docs/source/lib/goog/async/run.js.src.html +1 -1
- package/docs/source/lib/goog/base.js.src.html +1 -1
- package/docs/source/lib/goog/debug/debug.js.src.html +1 -1
- package/docs/source/lib/goog/debug/entrypointregistry.js.src.html +1 -1
- package/docs/source/lib/goog/debug/error.js.src.html +1 -1
- package/docs/source/lib/goog/debug/logbuffer.js.src.html +1 -1
- package/docs/source/lib/goog/debug/logger.js.src.html +1 -1
- package/docs/source/lib/goog/debug/logrecord.js.src.html +1 -1
- package/docs/source/lib/goog/deps.js.src.html +1 -1
- package/docs/source/lib/goog/disposable/disposable.js.src.html +1 -1
- package/docs/source/lib/goog/disposable/idisposable.js.src.html +1 -1
- package/docs/source/lib/goog/dom/browserfeature.js.src.html +1 -1
- package/docs/source/lib/goog/dom/dom.js.src.html +1 -1
- package/docs/source/lib/goog/dom/nodetype.js.src.html +1 -1
- package/docs/source/lib/goog/dom/tagname.js.src.html +1 -1
- package/docs/source/lib/goog/dom/vendor.js.src.html +1 -1
- package/docs/source/lib/goog/events/browserevent.js.src.html +1 -1
- package/docs/source/lib/goog/events/browserfeature.js.src.html +1 -1
- package/docs/source/lib/goog/events/event.js.src.html +1 -1
- package/docs/source/lib/goog/events/eventid.js.src.html +1 -1
- package/docs/source/lib/goog/events/events.js.src.html +1 -1
- package/docs/source/lib/goog/events/eventtarget.js.src.html +1 -1
- package/docs/source/lib/goog/events/eventtype.js.src.html +1 -1
- package/docs/source/lib/goog/events/keycodes.js.src.html +1 -1
- package/docs/source/lib/goog/events/listenable.js.src.html +1 -1
- package/docs/source/lib/goog/events/listener.js.src.html +1 -1
- package/docs/source/lib/goog/events/listenermap.js.src.html +1 -1
- package/docs/source/lib/goog/functions/functions.js.src.html +1 -1
- package/docs/source/lib/goog/iter/iter.js.src.html +1 -1
- package/docs/source/lib/goog/json/json.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/assertthat.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/matcher.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html +1 -1
- package/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html +1 -1
- package/docs/source/lib/goog/labs/useragent/browser.js.src.html +1 -1
- package/docs/source/lib/goog/labs/useragent/engine.js.src.html +1 -1
- package/docs/source/lib/goog/labs/useragent/util.js.src.html +1 -1
- package/docs/source/lib/goog/math/box.js.src.html +1 -1
- package/docs/source/lib/goog/math/coordinate.js.src.html +1 -1
- package/docs/source/lib/goog/math/math.js.src.html +1 -1
- package/docs/source/lib/goog/math/rect.js.src.html +1 -1
- package/docs/source/lib/goog/math/size.js.src.html +1 -1
- package/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html +1 -1
- package/docs/source/lib/goog/net/xhrlike.js.src.html +1 -1
- package/docs/source/lib/goog/net/xmlhttp.js.src.html +1 -1
- package/docs/source/lib/goog/net/xmlhttpfactory.js.src.html +1 -1
- package/docs/source/lib/goog/object/object.js.src.html +1 -1
- package/docs/source/lib/goog/reflect/reflect.js.src.html +1 -1
- package/docs/source/lib/goog/string/string.js.src.html +1 -1
- package/docs/source/lib/goog/structs/collection.js.src.html +1 -1
- package/docs/source/lib/goog/structs/map.js.src.html +1 -1
- package/docs/source/lib/goog/structs/set.js.src.html +1 -1
- package/docs/source/lib/goog/structs/structs.js.src.html +1 -1
- package/docs/source/lib/goog/style/style.js.src.html +1 -1
- package/docs/source/lib/goog/testing/asserts.js.src.html +1 -1
- package/docs/source/lib/goog/testing/asynctestcase.js.src.html +1 -1
- package/docs/source/lib/goog/testing/events/events.js.src.html +1 -1
- package/docs/source/lib/goog/testing/functionmock.js.src.html +1 -1
- package/docs/source/lib/goog/testing/jsunit.js.src.html +1 -1
- package/docs/source/lib/goog/testing/loosemock.js.src.html +1 -1
- package/docs/source/lib/goog/testing/mock.js.src.html +1 -1
- package/docs/source/lib/goog/testing/mockclock.js.src.html +1 -1
- package/docs/source/lib/goog/testing/mockcontrol.js.src.html +1 -1
- package/docs/source/lib/goog/testing/mockinterface.js.src.html +1 -1
- package/docs/source/lib/goog/testing/mockmatchers.js.src.html +1 -1
- package/docs/source/lib/goog/testing/objectpropertystring.js.src.html +1 -1
- package/docs/source/lib/goog/testing/propertyreplacer.js.src.html +1 -1
- package/docs/source/lib/goog/testing/recordfunction.js.src.html +1 -1
- package/docs/source/lib/goog/testing/stacktrace.js.src.html +1 -1
- package/docs/source/lib/goog/testing/strictmock.js.src.html +1 -1
- package/docs/source/lib/goog/testing/testcase.js.src.html +1 -1
- package/docs/source/lib/goog/testing/testrunner.js.src.html +1 -1
- package/docs/source/lib/goog/testing/watchers.js.src.html +1 -1
- package/docs/source/lib/goog/uri/uri.js.src.html +1 -1
- package/docs/source/lib/goog/uri/utils.js.src.html +1 -1
- package/docs/source/lib/goog/useragent/product.js.src.html +1 -1
- package/docs/source/lib/goog/useragent/product_isversion.js.src.html +1 -1
- package/docs/source/lib/goog/useragent/useragent.js.src.html +1 -1
- package/docs/source/lib/webdriver/abstractbuilder.js.src.html +1 -1
- package/docs/source/lib/webdriver/actionsequence.js.src.html +1 -1
- package/docs/source/lib/webdriver/builder.js.src.html +1 -1
- package/docs/source/lib/webdriver/button.js.src.html +1 -1
- package/docs/source/lib/webdriver/capabilities.js.src.html +1 -1
- package/docs/source/lib/webdriver/command.js.src.html +1 -1
- package/docs/source/lib/webdriver/events.js.src.html +1 -1
- package/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html +1 -1
- package/docs/source/lib/webdriver/http/corsclient.js.src.html +1 -1
- package/docs/source/lib/webdriver/http/http.js.src.html +1 -1
- package/docs/source/lib/webdriver/http/xhrclient.js.src.html +1 -1
- package/docs/source/lib/webdriver/key.js.src.html +1 -1
- package/docs/source/lib/webdriver/locators.js.src.html +1 -1
- package/docs/source/lib/webdriver/logging.js.src.html +1 -1
- package/docs/source/lib/webdriver/process.js.src.html +1 -1
- package/docs/source/lib/webdriver/promise.js.src.html +1 -1
- package/docs/source/lib/webdriver/session.js.src.html +1 -1
- package/docs/source/lib/webdriver/stacktrace.js.src.html +1 -1
- package/docs/source/lib/webdriver/testing/asserts.js.src.html +1 -1
- package/docs/source/lib/webdriver/testing/testcase.js.src.html +1 -1
- package/docs/source/lib/webdriver/until.js.src.html +1 -0
- package/docs/source/lib/webdriver/webdriver.js.src.html +1 -1
- package/docs/source/net/index.js.src.html +1 -1
- package/docs/source/net/portprober.js.src.html +1 -1
- package/docs/source/phantomjs.js.src.html +1 -1
- package/docs/source/proxy.js.src.html +1 -1
- package/docs/source/remote/index.js.src.html +1 -1
- package/docs/source/testing/assert.js.src.html +1 -1
- package/docs/source/testing/index.js.src.html +1 -1
- package/docs/types.js +1 -1
- package/example/google_search.js +9 -17
- package/example/google_search_generator.js +6 -7
- package/example/google_search_test.js +10 -18
- package/example/parallel_flows.js +4 -7
- package/index.js +6 -0
- package/lib/firefox/webdriver.xpi +0 -0
- package/lib/goog/deps.js +7 -5
- package/lib/test/index.js +0 -8
- package/lib/webdriver/promise.js +263 -251
- package/lib/webdriver/stacktrace.js +15 -1
- package/lib/webdriver/test/promise_flow_test.js +82 -76
- package/lib/webdriver/test/promise_test.js +22 -62
- package/lib/webdriver/test/stacktrace_test.js +18 -0
- package/lib/webdriver/test/until_test.js +388 -0
- package/lib/webdriver/test/webdriver_test.js +41 -0
- package/lib/webdriver/until.js +408 -0
- package/lib/webdriver/webdriver.js +91 -29
- package/package.json +2 -2
- package/test/element_finding_test.js +6 -6
- package/test/http/util_test.js +4 -5
- package/test/page_loading_test.js +7 -7
- package/test/stale_element_test.js +2 -10
- package/testing/index.js +9 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><meta charset="UTF-8"><meta http-equiv="Content-Language" content="en" /><title>promise.js</title><link href="../../../dossier.css" rel="stylesheet" type="text/css"><div id="main-wrapper"><input type="checkbox" id="sidenav-toggle" /><main><header><h1>lib/webdriver/promise.js</h1></header><pre><table class="srcfile"><tbody><tr><td><a name="l1" href="#l1">1</a><td>// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.<tr><td><a name="l2" href="#l2">2</a><td>//<tr><td><a name="l3" href="#l3">3</a><td>// Licensed under the Apache License, Version 2.0 (the "License");<tr><td><a name="l4" href="#l4">4</a><td>// you may not use this file except in compliance with the License.<tr><td><a name="l5" href="#l5">5</a><td>// You may obtain a copy of the License at<tr><td><a name="l6" href="#l6">6</a><td>//<tr><td><a name="l7" href="#l7">7</a><td>// http://www.apache.org/licenses/LICENSE-2.0<tr><td><a name="l8" href="#l8">8</a><td>//<tr><td><a name="l9" href="#l9">9</a><td>// Unless required by applicable law or agreed to in writing, software<tr><td><a name="l10" href="#l10">10</a><td>// distributed under the License is distributed on an "AS IS" BASIS,<tr><td><a name="l11" href="#l11">11</a><td>// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<tr><td><a name="l12" href="#l12">12</a><td>// See the License for the specific language governing permissions and<tr><td><a name="l13" href="#l13">13</a><td>// limitations under the License.<tr><td><a name="l14" href="#l14">14</a><td><tr><td><a name="l15" href="#l15">15</a><td>/**<tr><td><a name="l16" href="#l16">16</a><td> * @license Portions of this code are from the Dojo toolkit, received under the<tr><td><a name="l17" href="#l17">17</a><td> * BSD License:<tr><td><a name="l18" href="#l18">18</a><td> * Redistribution and use in source and binary forms, with or without<tr><td><a name="l19" href="#l19">19</a><td> * modification, are permitted provided that the following conditions are met:<tr><td><a name="l20" href="#l20">20</a><td> *<tr><td><a name="l21" href="#l21">21</a><td> * * Redistributions of source code must retain the above copyright notice,<tr><td><a name="l22" href="#l22">22</a><td> * this list of conditions and the following disclaimer.<tr><td><a name="l23" href="#l23">23</a><td> * * Redistributions in binary form must reproduce the above copyright notice,<tr><td><a name="l24" href="#l24">24</a><td> * this list of conditions and the following disclaimer in the documentation<tr><td><a name="l25" href="#l25">25</a><td> * and/or other materials provided with the distribution.<tr><td><a name="l26" href="#l26">26</a><td> * * Neither the name of the Dojo Foundation nor the names of its contributors<tr><td><a name="l27" href="#l27">27</a><td> * may be used to endorse or promote products derived from this software<tr><td><a name="l28" href="#l28">28</a><td> * without specific prior written permission.<tr><td><a name="l29" href="#l29">29</a><td> *<tr><td><a name="l30" href="#l30">30</a><td> * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<tr><td><a name="l31" href="#l31">31</a><td> * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<tr><td><a name="l32" href="#l32">32</a><td> * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<tr><td><a name="l33" href="#l33">33</a><td> * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<tr><td><a name="l34" href="#l34">34</a><td> * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<tr><td><a name="l35" href="#l35">35</a><td> * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<tr><td><a name="l36" href="#l36">36</a><td> * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<tr><td><a name="l37" href="#l37">37</a><td> * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<tr><td><a name="l38" href="#l38">38</a><td> * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<tr><td><a name="l39" href="#l39">39</a><td> * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<tr><td><a name="l40" href="#l40">40</a><td> * POSSIBILITY OF SUCH DAMAGE.<tr><td><a name="l41" href="#l41">41</a><td> */<tr><td><a name="l42" href="#l42">42</a><td><tr><td><a name="l43" href="#l43">43</a><td>/**<tr><td><a name="l44" href="#l44">44</a><td> * @fileoverview A promise implementation based on the CommonJS promise/A and<tr><td><a name="l45" href="#l45">45</a><td> * promise/B proposals. For more information, see<tr><td><a name="l46" href="#l46">46</a><td> * http://wiki.commonjs.org/wiki/Promises.<tr><td><a name="l47" href="#l47">47</a><td> */<tr><td><a name="l48" href="#l48">48</a><td><tr><td><a name="l49" href="#l49">49</a><td>goog.provide('webdriver.promise');<tr><td><a name="l50" href="#l50">50</a><td>goog.provide('webdriver.promise.ControlFlow');<tr><td><a name="l51" href="#l51">51</a><td>goog.provide('webdriver.promise.ControlFlow.Timer');<tr><td><a name="l52" href="#l52">52</a><td>goog.provide('webdriver.promise.Deferred');<tr><td><a name="l53" href="#l53">53</a><td>goog.provide('webdriver.promise.Promise');<tr><td><a name="l54" href="#l54">54</a><td>goog.provide('webdriver.promise.Thenable');<tr><td><a name="l55" href="#l55">55</a><td><tr><td><a name="l56" href="#l56">56</a><td>goog.require('goog.array');<tr><td><a name="l57" href="#l57">57</a><td>goog.require('goog.debug.Error');<tr><td><a name="l58" href="#l58">58</a><td>goog.require('goog.object');<tr><td><a name="l59" href="#l59">59</a><td>goog.require('webdriver.EventEmitter');<tr><td><a name="l60" href="#l60">60</a><td>goog.require('webdriver.stacktrace.Snapshot');<tr><td><a name="l61" href="#l61">61</a><td><tr><td><a name="l62" href="#l62">62</a><td><tr><td><a name="l63" href="#l63">63</a><td><tr><td><a name="l64" href="#l64">64</a><td>/**<tr><td><a name="l65" href="#l65">65</a><td> * Thenable is a promise-like object with a {@code then} method which may be<tr><td><a name="l66" href="#l66">66</a><td> * used to schedule callbacks on a promised value.<tr><td><a name="l67" href="#l67">67</a><td> *<tr><td><a name="l68" href="#l68">68</a><td> * @interface<tr><td><a name="l69" href="#l69">69</a><td> * @template T<tr><td><a name="l70" href="#l70">70</a><td> */<tr><td><a name="l71" href="#l71">71</a><td>webdriver.promise.Thenable = function() {};<tr><td><a name="l72" href="#l72">72</a><td><tr><td><a name="l73" href="#l73">73</a><td><tr><td><a name="l74" href="#l74">74</a><td>/**<tr><td><a name="l75" href="#l75">75</a><td> * Cancels the computation of this promise's value, rejecting the promise in the<tr><td><a name="l76" href="#l76">76</a><td> * process. This method is a no-op if the promise has alreayd been resolved.<tr><td><a name="l77" href="#l77">77</a><td> *<tr><td><a name="l78" href="#l78">78</a><td> * @param {*=} opt_reason The reason this promise is being cancelled. If not an<tr><td><a name="l79" href="#l79">79</a><td> * {@code Error}, one will be created using the value's string<tr><td><a name="l80" href="#l80">80</a><td> * representation.<tr><td><a name="l81" href="#l81">81</a><td> */<tr><td><a name="l82" href="#l82">82</a><td>webdriver.promise.Thenable.prototype.cancel = function(opt_reason) {};<tr><td><a name="l83" href="#l83">83</a><td><tr><td><a name="l84" href="#l84">84</a><td><tr><td><a name="l85" href="#l85">85</a><td>/** @return {boolean} Whether this promise's value is still being computed. */<tr><td><a name="l86" href="#l86">86</a><td>webdriver.promise.Thenable.prototype.isPending = function() {};<tr><td><a name="l87" href="#l87">87</a><td><tr><td><a name="l88" href="#l88">88</a><td><tr><td><a name="l89" href="#l89">89</a><td>/**<tr><td><a name="l90" href="#l90">90</a><td> * Registers listeners for when this instance is resolved.<tr><td><a name="l91" href="#l91">91</a><td> *<tr><td><a name="l92" href="#l92">92</a><td> * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback The<tr><td><a name="l93" href="#l93">93</a><td> * function to call if this promise is successfully resolved. The function<tr><td><a name="l94" href="#l94">94</a><td> * should expect a single argument: the promise's resolved value.<tr><td><a name="l95" href="#l95">95</a><td> * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback The<tr><td><a name="l96" href="#l96">96</a><td> * function to call if this promise is rejected. The function should expect<tr><td><a name="l97" href="#l97">97</a><td> * a single argument: the rejection reason.<tr><td><a name="l98" href="#l98">98</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise which will be<tr><td><a name="l99" href="#l99">99</a><td> * resolved with the result of the invoked callback.<tr><td><a name="l100" href="#l100">100</a><td> * @template R<tr><td><a name="l101" href="#l101">101</a><td> */<tr><td><a name="l102" href="#l102">102</a><td>webdriver.promise.Thenable.prototype.then = function(<tr><td><a name="l103" href="#l103">103</a><td> opt_callback, opt_errback) {};<tr><td><a name="l104" href="#l104">104</a><td><tr><td><a name="l105" href="#l105">105</a><td><tr><td><a name="l106" href="#l106">106</a><td>/**<tr><td><a name="l107" href="#l107">107</a><td> * Registers a listener for when this promise is rejected. This is synonymous<tr><td><a name="l108" href="#l108">108</a><td> * with the {@code catch} clause in a synchronous API:<tr><td><a name="l109" href="#l109">109</a><td> * <pre><code><tr><td><a name="l110" href="#l110">110</a><td> * // Synchronous API:<tr><td><a name="l111" href="#l111">111</a><td> * try {<tr><td><a name="l112" href="#l112">112</a><td> * doSynchronousWork();<tr><td><a name="l113" href="#l113">113</a><td> * } catch (ex) {<tr><td><a name="l114" href="#l114">114</a><td> * console.error(ex);<tr><td><a name="l115" href="#l115">115</a><td> * }<tr><td><a name="l116" href="#l116">116</a><td> *<tr><td><a name="l117" href="#l117">117</a><td> * // Asynchronous promise API:<tr><td><a name="l118" href="#l118">118</a><td> * doAsynchronousWork().thenCatch(function(ex) {<tr><td><a name="l119" href="#l119">119</a><td> * console.error(ex);<tr><td><a name="l120" href="#l120">120</a><td> * });<tr><td><a name="l121" href="#l121">121</a><td> * </code></pre><tr><td><a name="l122" href="#l122">122</a><td> *<tr><td><a name="l123" href="#l123">123</a><td> * @param {function(*): (R|webdriver.promise.Promise.<R>)} errback The function<tr><td><a name="l124" href="#l124">124</a><td> * to call if this promise is rejected. The function should expect a single<tr><td><a name="l125" href="#l125">125</a><td> * argument: the rejection reason.<tr><td><a name="l126" href="#l126">126</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise which will be<tr><td><a name="l127" href="#l127">127</a><td> * resolved with the result of the invoked callback.<tr><td><a name="l128" href="#l128">128</a><td> * @template R<tr><td><a name="l129" href="#l129">129</a><td> */<tr><td><a name="l130" href="#l130">130</a><td>webdriver.promise.Thenable.prototype.thenCatch = function(errback) {};<tr><td><a name="l131" href="#l131">131</a><td><tr><td><a name="l132" href="#l132">132</a><td><tr><td><a name="l133" href="#l133">133</a><td>/**<tr><td><a name="l134" href="#l134">134</a><td> * Registers a listener to invoke when this promise is resolved, regardless<tr><td><a name="l135" href="#l135">135</a><td> * of whether the promise's value was successfully computed. This function<tr><td><a name="l136" href="#l136">136</a><td> * is synonymous with the {@code finally} clause in a synchronous API:<tr><td><a name="l137" href="#l137">137</a><td> * <pre><code><tr><td><a name="l138" href="#l138">138</a><td> * // Synchronous API:<tr><td><a name="l139" href="#l139">139</a><td> * try {<tr><td><a name="l140" href="#l140">140</a><td> * doSynchronousWork();<tr><td><a name="l141" href="#l141">141</a><td> * } finally {<tr><td><a name="l142" href="#l142">142</a><td> * cleanUp();<tr><td><a name="l143" href="#l143">143</a><td> * }<tr><td><a name="l144" href="#l144">144</a><td> *<tr><td><a name="l145" href="#l145">145</a><td> * // Asynchronous promise API:<tr><td><a name="l146" href="#l146">146</a><td> * doAsynchronousWork().thenFinally(cleanUp);<tr><td><a name="l147" href="#l147">147</a><td> * </code></pre><tr><td><a name="l148" href="#l148">148</a><td> *<tr><td><a name="l149" href="#l149">149</a><td> * <b>Note:</b> similar to the {@code finally} clause, if the registered<tr><td><a name="l150" href="#l150">150</a><td> * callback returns a rejected promise or throws an error, it will silently<tr><td><a name="l151" href="#l151">151</a><td> * replace the rejection error (if any) from this promise:<tr><td><a name="l152" href="#l152">152</a><td> * <pre><code><tr><td><a name="l153" href="#l153">153</a><td> * try {<tr><td><a name="l154" href="#l154">154</a><td> * throw Error('one');<tr><td><a name="l155" href="#l155">155</a><td> * } finally {<tr><td><a name="l156" href="#l156">156</a><td> * throw Error('two'); // Hides Error: one<tr><td><a name="l157" href="#l157">157</a><td> * }<tr><td><a name="l158" href="#l158">158</a><td> *<tr><td><a name="l159" href="#l159">159</a><td> * webdriver.promise.rejected(Error('one'))<tr><td><a name="l160" href="#l160">160</a><td> * .thenFinally(function() {<tr><td><a name="l161" href="#l161">161</a><td> * throw Error('two'); // Hides Error: one<tr><td><a name="l162" href="#l162">162</a><td> * });<tr><td><a name="l163" href="#l163">163</a><td> * </code></pre><tr><td><a name="l164" href="#l164">164</a><td> *<tr><td><a name="l165" href="#l165">165</a><td> *<tr><td><a name="l166" href="#l166">166</a><td> * @param {function(): (R|webdriver.promise.Promise.<R>)} callback The function<tr><td><a name="l167" href="#l167">167</a><td> * to call when this promise is resolved.<tr><td><a name="l168" href="#l168">168</a><td> * @return {!webdriver.promise.Promise.<R>} A promise that will be fulfilled<tr><td><a name="l169" href="#l169">169</a><td> * with the callback result.<tr><td><a name="l170" href="#l170">170</a><td> * @template R<tr><td><a name="l171" href="#l171">171</a><td> */<tr><td><a name="l172" href="#l172">172</a><td>webdriver.promise.Thenable.prototype.thenFinally = function(callback) {};<tr><td><a name="l173" href="#l173">173</a><td><tr><td><a name="l174" href="#l174">174</a><td><tr><td><a name="l175" href="#l175">175</a><td>/**<tr><td><a name="l176" href="#l176">176</a><td> * Property used to flag constructor's as implementing the Thenable interface<tr><td><a name="l177" href="#l177">177</a><td> * for runtime type checking.<tr><td><a name="l178" href="#l178">178</a><td> * @private {string}<tr><td><a name="l179" href="#l179">179</a><td> * @const<tr><td><a name="l180" href="#l180">180</a><td> */<tr><td><a name="l181" href="#l181">181</a><td>webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_ = '$webdriver_Thenable';<tr><td><a name="l182" href="#l182">182</a><td><tr><td><a name="l183" href="#l183">183</a><td><tr><td><a name="l184" href="#l184">184</a><td>/**<tr><td><a name="l185" href="#l185">185</a><td> * Adds a property to a class prototype to allow runtime checks of whether<tr><td><a name="l186" href="#l186">186</a><td> * instances of that class implement the Thenable interface. This function will<tr><td><a name="l187" href="#l187">187</a><td> * also ensure the prototype's {@code then} function is exported from compiled<tr><td><a name="l188" href="#l188">188</a><td> * code.<tr><td><a name="l189" href="#l189">189</a><td> * @param {function(new: webdriver.promise.Thenable, ...[?])} ctor The<tr><td><a name="l190" href="#l190">190</a><td> * constructor whose prototype to modify.<tr><td><a name="l191" href="#l191">191</a><td> */<tr><td><a name="l192" href="#l192">192</a><td>webdriver.promise.Thenable.addImplementation = function(ctor) {<tr><td><a name="l193" href="#l193">193</a><td> // Based on goog.promise.Thenable.isImplementation.<tr><td><a name="l194" href="#l194">194</a><td> ctor.prototype['then'] = ctor.prototype.then;<tr><td><a name="l195" href="#l195">195</a><td> try {<tr><td><a name="l196" href="#l196">196</a><td> // Old IE7 does not support defineProperty; IE8 only supports it for<tr><td><a name="l197" href="#l197">197</a><td> // DOM elements.<tr><td><a name="l198" href="#l198">198</a><td> Object.defineProperty(<tr><td><a name="l199" href="#l199">199</a><td> ctor.prototype,<tr><td><a name="l200" href="#l200">200</a><td> webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_,<tr><td><a name="l201" href="#l201">201</a><td> {'value': true, 'enumerable': false});<tr><td><a name="l202" href="#l202">202</a><td> } catch (ex) {<tr><td><a name="l203" href="#l203">203</a><td> ctor.prototype[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_] = true;<tr><td><a name="l204" href="#l204">204</a><td> }<tr><td><a name="l205" href="#l205">205</a><td>};<tr><td><a name="l206" href="#l206">206</a><td><tr><td><a name="l207" href="#l207">207</a><td><tr><td><a name="l208" href="#l208">208</a><td>/**<tr><td><a name="l209" href="#l209">209</a><td> * Checks if an object has been tagged for implementing the Thenable interface<tr><td><a name="l210" href="#l210">210</a><td> * as defined by {@link webdriver.promise.Thenable.addImplementation}.<tr><td><a name="l211" href="#l211">211</a><td> * @param {*} object The object to test.<tr><td><a name="l212" href="#l212">212</a><td> * @return {boolean} Whether the object is an implementation of the Thenable<tr><td><a name="l213" href="#l213">213</a><td> * interface.<tr><td><a name="l214" href="#l214">214</a><td> */<tr><td><a name="l215" href="#l215">215</a><td>webdriver.promise.Thenable.isImplementation = function(object) {<tr><td><a name="l216" href="#l216">216</a><td> // Based on goog.promise.Thenable.isImplementation.<tr><td><a name="l217" href="#l217">217</a><td> if (!object) {<tr><td><a name="l218" href="#l218">218</a><td> return false;<tr><td><a name="l219" href="#l219">219</a><td> }<tr><td><a name="l220" href="#l220">220</a><td> try {<tr><td><a name="l221" href="#l221">221</a><td> return !!object[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_];<tr><td><a name="l222" href="#l222">222</a><td> } catch (e) {<tr><td><a name="l223" href="#l223">223</a><td> return false; // Property access seems to be forbidden.<tr><td><a name="l224" href="#l224">224</a><td> }<tr><td><a name="l225" href="#l225">225</a><td>};<tr><td><a name="l226" href="#l226">226</a><td><tr><td><a name="l227" href="#l227">227</a><td><tr><td><a name="l228" href="#l228">228</a><td><tr><td><a name="l229" href="#l229">229</a><td>/**<tr><td><a name="l230" href="#l230">230</a><td> * Represents the eventual value of a completed operation. Each promise may be<tr><td><a name="l231" href="#l231">231</a><td> * in one of three states: pending, resolved, or rejected. Each promise starts<tr><td><a name="l232" href="#l232">232</a><td> * in the pending state and may make a single transition to either a<tr><td><a name="l233" href="#l233">233</a><td> * fulfilled or rejected state, at which point the promise is considered<tr><td><a name="l234" href="#l234">234</a><td> * resolved.<tr><td><a name="l235" href="#l235">235</a><td> *<tr><td><a name="l236" href="#l236">236</a><td> * @constructor<tr><td><a name="l237" href="#l237">237</a><td> * @implements {webdriver.promise.Thenable.<T>}<tr><td><a name="l238" href="#l238">238</a><td> * @template T<tr><td><a name="l239" href="#l239">239</a><td> * @see http://promises-aplus.github.io/promises-spec/<tr><td><a name="l240" href="#l240">240</a><td> */<tr><td><a name="l241" href="#l241">241</a><td>webdriver.promise.Promise = function() {};<tr><td><a name="l242" href="#l242">242</a><td>webdriver.promise.Thenable.addImplementation(webdriver.promise.Promise);<tr><td><a name="l243" href="#l243">243</a><td><tr><td><a name="l244" href="#l244">244</a><td><tr><td><a name="l245" href="#l245">245</a><td>/** @override */<tr><td><a name="l246" href="#l246">246</a><td>webdriver.promise.Promise.prototype.cancel = function(reason) {<tr><td><a name="l247" href="#l247">247</a><td> throw new TypeError('Unimplemented function: "cancel"');<tr><td><a name="l248" href="#l248">248</a><td>};<tr><td><a name="l249" href="#l249">249</a><td><tr><td><a name="l250" href="#l250">250</a><td><tr><td><a name="l251" href="#l251">251</a><td>/** @override */<tr><td><a name="l252" href="#l252">252</a><td>webdriver.promise.Promise.prototype.isPending = function() {<tr><td><a name="l253" href="#l253">253</a><td> throw new TypeError('Unimplemented function: "isPending"');<tr><td><a name="l254" href="#l254">254</a><td>};<tr><td><a name="l255" href="#l255">255</a><td><tr><td><a name="l256" href="#l256">256</a><td><tr><td><a name="l257" href="#l257">257</a><td>/** @override */<tr><td><a name="l258" href="#l258">258</a><td>webdriver.promise.Promise.prototype.then = function(<tr><td><a name="l259" href="#l259">259</a><td> opt_callback, opt_errback) {<tr><td><a name="l260" href="#l260">260</a><td> throw new TypeError('Unimplemented function: "then"');<tr><td><a name="l261" href="#l261">261</a><td>};<tr><td><a name="l262" href="#l262">262</a><td><tr><td><a name="l263" href="#l263">263</a><td><tr><td><a name="l264" href="#l264">264</a><td>/** @override */<tr><td><a name="l265" href="#l265">265</a><td>webdriver.promise.Promise.prototype.thenCatch = function(errback) {<tr><td><a name="l266" href="#l266">266</a><td> return this.then(null, errback);<tr><td><a name="l267" href="#l267">267</a><td>};<tr><td><a name="l268" href="#l268">268</a><td><tr><td><a name="l269" href="#l269">269</a><td><tr><td><a name="l270" href="#l270">270</a><td>/** @override */<tr><td><a name="l271" href="#l271">271</a><td>webdriver.promise.Promise.prototype.thenFinally = function(callback) {<tr><td><a name="l272" href="#l272">272</a><td> return this.then(callback, function(err) {<tr><td><a name="l273" href="#l273">273</a><td> var value = callback();<tr><td><a name="l274" href="#l274">274</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l275" href="#l275">275</a><td> return value.then(function() {<tr><td><a name="l276" href="#l276">276</a><td> throw err;<tr><td><a name="l277" href="#l277">277</a><td> });<tr><td><a name="l278" href="#l278">278</a><td> }<tr><td><a name="l279" href="#l279">279</a><td> throw err;<tr><td><a name="l280" href="#l280">280</a><td> });<tr><td><a name="l281" href="#l281">281</a><td>};<tr><td><a name="l282" href="#l282">282</a><td><tr><td><a name="l283" href="#l283">283</a><td><tr><td><a name="l284" href="#l284">284</a><td><tr><td><a name="l285" href="#l285">285</a><td>/**<tr><td><a name="l286" href="#l286">286</a><td> * Represents a value that will be resolved at some point in the future. This<tr><td><a name="l287" href="#l287">287</a><td> * class represents the protected "producer" half of a Promise - each Deferred<tr><td><a name="l288" href="#l288">288</a><td> * has a {@code promise} property that may be returned to consumers for<tr><td><a name="l289" href="#l289">289</a><td> * registering callbacks, reserving the ability to resolve the deferred to the<tr><td><a name="l290" href="#l290">290</a><td> * producer.<tr><td><a name="l291" href="#l291">291</a><td> *<tr><td><a name="l292" href="#l292">292</a><td> * <p>If this Deferred is rejected and there are no listeners registered before<tr><td><a name="l293" href="#l293">293</a><td> * the next turn of the event loop, the rejection will be passed to the<tr><td><a name="l294" href="#l294">294</a><td> * {@link webdriver.promise.ControlFlow} as an unhandled failure.<tr><td><a name="l295" href="#l295">295</a><td> *<tr><td><a name="l296" href="#l296">296</a><td> * <p>If this Deferred is cancelled, the cancellation reason will be forward to<tr><td><a name="l297" href="#l297">297</a><td> * the Deferred's canceller function (if provided). The canceller may return a<tr><td><a name="l298" href="#l298">298</a><td> * truth-y value to override the reason provided for rejection.<tr><td><a name="l299" href="#l299">299</a><td> *<tr><td><a name="l300" href="#l300">300</a><td> * @param {Function=} opt_canceller Function to call when cancelling the<tr><td><a name="l301" href="#l301">301</a><td> * computation of this instance's value.<tr><td><a name="l302" href="#l302">302</a><td> * @param {webdriver.promise.ControlFlow=} opt_flow The control flow<tr><td><a name="l303" href="#l303">303</a><td> * this instance was created under. This should only be provided during<tr><td><a name="l304" href="#l304">304</a><td> * unit tests.<tr><td><a name="l305" href="#l305">305</a><td> * @constructor<tr><td><a name="l306" href="#l306">306</a><td> * @extends {webdriver.promise.Promise.<T>}<tr><td><a name="l307" href="#l307">307</a><td> * @template T<tr><td><a name="l308" href="#l308">308</a><td> */<tr><td><a name="l309" href="#l309">309</a><td>webdriver.promise.Deferred = function(opt_canceller, opt_flow) {<tr><td><a name="l310" href="#l310">310</a><td> /* NOTE: This class's implementation diverges from the prototypical style<tr><td><a name="l311" href="#l311">311</a><td> * used in the rest of the atoms library. This was done intentionally to<tr><td><a name="l312" href="#l312">312</a><td> * protect the internal Deferred state from consumers, as outlined by<tr><td><a name="l313" href="#l313">313</a><td> * http://wiki.commonjs.org/wiki/Promises<tr><td><a name="l314" href="#l314">314</a><td> */<tr><td><a name="l315" href="#l315">315</a><td> goog.base(this);<tr><td><a name="l316" href="#l316">316</a><td><tr><td><a name="l317" href="#l317">317</a><td> var flow = opt_flow || webdriver.promise.controlFlow();<tr><td><a name="l318" href="#l318">318</a><td><tr><td><a name="l319" href="#l319">319</a><td> /**<tr><td><a name="l320" href="#l320">320</a><td> * The listeners registered with this Deferred. Each element in the list will<tr><td><a name="l321" href="#l321">321</a><td> * be a 3-tuple of the callback function, errback function, and the<tr><td><a name="l322" href="#l322">322</a><td> * corresponding deferred object.<tr><td><a name="l323" href="#l323">323</a><td> * @type {!Array.<!webdriver.promise.Deferred.Listener_>}<tr><td><a name="l324" href="#l324">324</a><td> */<tr><td><a name="l325" href="#l325">325</a><td> var listeners = [];<tr><td><a name="l326" href="#l326">326</a><td><tr><td><a name="l327" href="#l327">327</a><td> /**<tr><td><a name="l328" href="#l328">328</a><td> * Whether this Deferred's resolution was ever handled by a listener.<tr><td><a name="l329" href="#l329">329</a><td> * If the Deferred is rejected and its value is not handled by a listener<tr><td><a name="l330" href="#l330">330</a><td> * before the next turn of the event loop, the error will be passed to the<tr><td><a name="l331" href="#l331">331</a><td> * global error handler.<tr><td><a name="l332" href="#l332">332</a><td> * @type {boolean}<tr><td><a name="l333" href="#l333">333</a><td> */<tr><td><a name="l334" href="#l334">334</a><td> var handled = false;<tr><td><a name="l335" href="#l335">335</a><td><tr><td><a name="l336" href="#l336">336</a><td> /**<tr><td><a name="l337" href="#l337">337</a><td> * Key for the timeout used to delay reproting an unhandled rejection to the<tr><td><a name="l338" href="#l338">338</a><td> * parent {@link webdriver.promise.ControlFlow}.<tr><td><a name="l339" href="#l339">339</a><td> * @type {?number}<tr><td><a name="l340" href="#l340">340</a><td> */<tr><td><a name="l341" href="#l341">341</a><td> var pendingRejectionKey = null;<tr><td><a name="l342" href="#l342">342</a><td><tr><td><a name="l343" href="#l343">343</a><td> /**<tr><td><a name="l344" href="#l344">344</a><td> * This Deferred's current state.<tr><td><a name="l345" href="#l345">345</a><td> * @type {!webdriver.promise.Deferred.State_}<tr><td><a name="l346" href="#l346">346</a><td> */<tr><td><a name="l347" href="#l347">347</a><td> var state = webdriver.promise.Deferred.State_.PENDING;<tr><td><a name="l348" href="#l348">348</a><td><tr><td><a name="l349" href="#l349">349</a><td> /**<tr><td><a name="l350" href="#l350">350</a><td> * This Deferred's resolved value; set when the state transitions from<tr><td><a name="l351" href="#l351">351</a><td> * {@code webdriver.promise.Deferred.State_.PENDING}.<tr><td><a name="l352" href="#l352">352</a><td> * @type {*}<tr><td><a name="l353" href="#l353">353</a><td> */<tr><td><a name="l354" href="#l354">354</a><td> var value;<tr><td><a name="l355" href="#l355">355</a><td><tr><td><a name="l356" href="#l356">356</a><td> /** @return {boolean} Whether this promise's value is still pending. */<tr><td><a name="l357" href="#l357">357</a><td> function isPending() {<tr><td><a name="l358" href="#l358">358</a><td> return state == webdriver.promise.Deferred.State_.PENDING;<tr><td><a name="l359" href="#l359">359</a><td> }<tr><td><a name="l360" href="#l360">360</a><td><tr><td><a name="l361" href="#l361">361</a><td> /**<tr><td><a name="l362" href="#l362">362</a><td> * Removes all of the listeners previously registered on this deferred.<tr><td><a name="l363" href="#l363">363</a><td> * @throws {Error} If this deferred has already been resolved.<tr><td><a name="l364" href="#l364">364</a><td> */<tr><td><a name="l365" href="#l365">365</a><td> function removeAll() {<tr><td><a name="l366" href="#l366">366</a><td> listeners = [];<tr><td><a name="l367" href="#l367">367</a><td> }<tr><td><a name="l368" href="#l368">368</a><td><tr><td><a name="l369" href="#l369">369</a><td> /**<tr><td><a name="l370" href="#l370">370</a><td> * Resolves this deferred. If the new value is a promise, this function will<tr><td><a name="l371" href="#l371">371</a><td> * wait for it to be resolved before notifying the registered listeners.<tr><td><a name="l372" href="#l372">372</a><td> * @param {!webdriver.promise.Deferred.State_} newState The deferred's new<tr><td><a name="l373" href="#l373">373</a><td> * state.<tr><td><a name="l374" href="#l374">374</a><td> * @param {*} newValue The deferred's new value.<tr><td><a name="l375" href="#l375">375</a><td> */<tr><td><a name="l376" href="#l376">376</a><td> function resolve(newState, newValue) {<tr><td><a name="l377" href="#l377">377</a><td> if (webdriver.promise.Deferred.State_.PENDING !== state) {<tr><td><a name="l378" href="#l378">378</a><td> return;<tr><td><a name="l379" href="#l379">379</a><td> }<tr><td><a name="l380" href="#l380">380</a><td><tr><td><a name="l381" href="#l381">381</a><td> if (newValue === self) {<tr><td><a name="l382" href="#l382">382</a><td> // See promise a+, 2.3.1<tr><td><a name="l383" href="#l383">383</a><td> // http://promises-aplus.github.io/promises-spec/#point-48<tr><td><a name="l384" href="#l384">384</a><td> throw TypeError('A promise may not resolve to itself');<tr><td><a name="l385" href="#l385">385</a><td> }<tr><td><a name="l386" href="#l386">386</a><td><tr><td><a name="l387" href="#l387">387</a><td> state = webdriver.promise.Deferred.State_.BLOCKED;<tr><td><a name="l388" href="#l388">388</a><td><tr><td><a name="l389" href="#l389">389</a><td> if (webdriver.promise.isPromise(newValue)) {<tr><td><a name="l390" href="#l390">390</a><td> var onFulfill = goog.partial(notifyAll, newState);<tr><td><a name="l391" href="#l391">391</a><td> var onReject = goog.partial(<tr><td><a name="l392" href="#l392">392</a><td> notifyAll, webdriver.promise.Deferred.State_.REJECTED);<tr><td><a name="l393" href="#l393">393</a><td> if (newValue instanceof webdriver.promise.Deferred) {<tr><td><a name="l394" href="#l394">394</a><td> newValue.then(onFulfill, onReject);<tr><td><a name="l395" href="#l395">395</a><td> } else {<tr><td><a name="l396" href="#l396">396</a><td> webdriver.promise.asap(newValue, onFulfill, onReject);<tr><td><a name="l397" href="#l397">397</a><td> }<tr><td><a name="l398" href="#l398">398</a><td><tr><td><a name="l399" href="#l399">399</a><td> } else {<tr><td><a name="l400" href="#l400">400</a><td> notifyAll(newState, newValue);<tr><td><a name="l401" href="#l401">401</a><td> }<tr><td><a name="l402" href="#l402">402</a><td> }<tr><td><a name="l403" href="#l403">403</a><td><tr><td><a name="l404" href="#l404">404</a><td> /**<tr><td><a name="l405" href="#l405">405</a><td> * Notifies all of the listeners registered with this Deferred that its state<tr><td><a name="l406" href="#l406">406</a><td> * has changed.<tr><td><a name="l407" href="#l407">407</a><td> * @param {!webdriver.promise.Deferred.State_} newState The deferred's new<tr><td><a name="l408" href="#l408">408</a><td> * state.<tr><td><a name="l409" href="#l409">409</a><td> * @param {*} newValue The deferred's new value.<tr><td><a name="l410" href="#l410">410</a><td> */<tr><td><a name="l411" href="#l411">411</a><td> function notifyAll(newState, newValue) {<tr><td><a name="l412" href="#l412">412</a><td> if (newState === webdriver.promise.Deferred.State_.REJECTED &&<tr><td><a name="l413" href="#l413">413</a><td> // We cannot check instanceof Error since the object may have been<tr><td><a name="l414" href="#l414">414</a><td> // created in a different JS context.<tr><td><a name="l415" href="#l415">415</a><td> goog.isObject(newValue) && goog.isString(newValue.message)) {<tr><td><a name="l416" href="#l416">416</a><td> newValue = flow.annotateError(/** @type {!Error} */(newValue));<tr><td><a name="l417" href="#l417">417</a><td> }<tr><td><a name="l418" href="#l418">418</a><td><tr><td><a name="l419" href="#l419">419</a><td> state = newState;<tr><td><a name="l420" href="#l420">420</a><td> value = newValue;<tr><td><a name="l421" href="#l421">421</a><td> while (listeners.length) {<tr><td><a name="l422" href="#l422">422</a><td> notify(listeners.shift());<tr><td><a name="l423" href="#l423">423</a><td> }<tr><td><a name="l424" href="#l424">424</a><td><tr><td><a name="l425" href="#l425">425</a><td> if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) {<tr><td><a name="l426" href="#l426">426</a><td> flow.pendingRejections_ += 1;<tr><td><a name="l427" href="#l427">427</a><td> pendingRejectionKey = flow.timer.setTimeout(function() {<tr><td><a name="l428" href="#l428">428</a><td> pendingRejectionKey = null;<tr><td><a name="l429" href="#l429">429</a><td> flow.pendingRejections_ -= 1;<tr><td><a name="l430" href="#l430">430</a><td> flow.abortFrame_(value);<tr><td><a name="l431" href="#l431">431</a><td> }, 0);<tr><td><a name="l432" href="#l432">432</a><td> }<tr><td><a name="l433" href="#l433">433</a><td> }<tr><td><a name="l434" href="#l434">434</a><td><tr><td><a name="l435" href="#l435">435</a><td> /**<tr><td><a name="l436" href="#l436">436</a><td> * Notifies a single listener of this Deferred's change in state.<tr><td><a name="l437" href="#l437">437</a><td> * @param {!webdriver.promise.Deferred.Listener_} listener The listener to<tr><td><a name="l438" href="#l438">438</a><td> * notify.<tr><td><a name="l439" href="#l439">439</a><td> */<tr><td><a name="l440" href="#l440">440</a><td> function notify(listener) {<tr><td><a name="l441" href="#l441">441</a><td> var func = state == webdriver.promise.Deferred.State_.RESOLVED ?<tr><td><a name="l442" href="#l442">442</a><td> listener.callback : listener.errback;<tr><td><a name="l443" href="#l443">443</a><td> if (func) {<tr><td><a name="l444" href="#l444">444</a><td> flow.runInNewFrame_(goog.partial(func, value),<tr><td><a name="l445" href="#l445">445</a><td> listener.fulfill, listener.reject);<tr><td><a name="l446" href="#l446">446</a><td> } else if (state == webdriver.promise.Deferred.State_.REJECTED) {<tr><td><a name="l447" href="#l447">447</a><td> listener.reject(value);<tr><td><a name="l448" href="#l448">448</a><td> } else {<tr><td><a name="l449" href="#l449">449</a><td> listener.fulfill(value);<tr><td><a name="l450" href="#l450">450</a><td> }<tr><td><a name="l451" href="#l451">451</a><td> }<tr><td><a name="l452" href="#l452">452</a><td><tr><td><a name="l453" href="#l453">453</a><td> /**<tr><td><a name="l454" href="#l454">454</a><td> * The consumer promise for this instance. Provides protected access to the<tr><td><a name="l455" href="#l455">455</a><td> * callback registering functions.<tr><td><a name="l456" href="#l456">456</a><td> * @type {!webdriver.promise.Promise.<T>}<tr><td><a name="l457" href="#l457">457</a><td> */<tr><td><a name="l458" href="#l458">458</a><td> var promise = new webdriver.promise.Promise();<tr><td><a name="l459" href="#l459">459</a><td><tr><td><a name="l460" href="#l460">460</a><td> /**<tr><td><a name="l461" href="#l461">461</a><td> * Registers a callback on this Deferred.<tr><td><a name="l462" href="#l462">462</a><td> *<tr><td><a name="l463" href="#l463">463</a><td> * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback .<tr><td><a name="l464" href="#l464">464</a><td> * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback .<tr><td><a name="l465" href="#l465">465</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise representing the<tr><td><a name="l466" href="#l466">466</a><td> * result of the callback.<tr><td><a name="l467" href="#l467">467</a><td> * @template R<tr><td><a name="l468" href="#l468">468</a><td> * @see webdriver.promise.Promise#then<tr><td><a name="l469" href="#l469">469</a><td> */<tr><td><a name="l470" href="#l470">470</a><td> function then(opt_callback, opt_errback) {<tr><td><a name="l471" href="#l471">471</a><td> // Avoid unnecessary allocations if we weren't given any callback functions.<tr><td><a name="l472" href="#l472">472</a><td> if (!opt_callback && !opt_errback) {<tr><td><a name="l473" href="#l473">473</a><td> return promise;<tr><td><a name="l474" href="#l474">474</a><td> }<tr><td><a name="l475" href="#l475">475</a><td><tr><td><a name="l476" href="#l476">476</a><td> // The moment a listener is registered, we consider this deferred to be<tr><td><a name="l477" href="#l477">477</a><td> // handled; the callback must handle any rejection errors.<tr><td><a name="l478" href="#l478">478</a><td> handled = true;<tr><td><a name="l479" href="#l479">479</a><td> if (pendingRejectionKey !== null) {<tr><td><a name="l480" href="#l480">480</a><td> flow.pendingRejections_ -= 1;<tr><td><a name="l481" href="#l481">481</a><td> flow.timer.clearTimeout(pendingRejectionKey);<tr><td><a name="l482" href="#l482">482</a><td> pendingRejectionKey = null;<tr><td><a name="l483" href="#l483">483</a><td> }<tr><td><a name="l484" href="#l484">484</a><td><tr><td><a name="l485" href="#l485">485</a><td> var deferred = new webdriver.promise.Deferred(cancel, flow);<tr><td><a name="l486" href="#l486">486</a><td> var listener = {<tr><td><a name="l487" href="#l487">487</a><td> callback: opt_callback,<tr><td><a name="l488" href="#l488">488</a><td> errback: opt_errback,<tr><td><a name="l489" href="#l489">489</a><td> fulfill: deferred.fulfill,<tr><td><a name="l490" href="#l490">490</a><td> reject: deferred.reject<tr><td><a name="l491" href="#l491">491</a><td> };<tr><td><a name="l492" href="#l492">492</a><td><tr><td><a name="l493" href="#l493">493</a><td> if (state == webdriver.promise.Deferred.State_.PENDING ||<tr><td><a name="l494" href="#l494">494</a><td> state == webdriver.promise.Deferred.State_.BLOCKED) {<tr><td><a name="l495" href="#l495">495</a><td> listeners.push(listener);<tr><td><a name="l496" href="#l496">496</a><td> } else {<tr><td><a name="l497" href="#l497">497</a><td> notify(listener);<tr><td><a name="l498" href="#l498">498</a><td> }<tr><td><a name="l499" href="#l499">499</a><td><tr><td><a name="l500" href="#l500">500</a><td> return deferred.promise;<tr><td><a name="l501" href="#l501">501</a><td> }<tr><td><a name="l502" href="#l502">502</a><td><tr><td><a name="l503" href="#l503">503</a><td> var self = this;<tr><td><a name="l504" href="#l504">504</a><td><tr><td><a name="l505" href="#l505">505</a><td> /**<tr><td><a name="l506" href="#l506">506</a><td> * Resolves this promise with the given value. If the value is itself a<tr><td><a name="l507" href="#l507">507</a><td> * promise and not a reference to this deferred, this instance will wait for<tr><td><a name="l508" href="#l508">508</a><td> * it before resolving.<tr><td><a name="l509" href="#l509">509</a><td> * @param {T=} opt_value The fulfilled value.<tr><td><a name="l510" href="#l510">510</a><td> */<tr><td><a name="l511" href="#l511">511</a><td> function fulfill(opt_value) {<tr><td><a name="l512" href="#l512">512</a><td> resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value);<tr><td><a name="l513" href="#l513">513</a><td> }<tr><td><a name="l514" href="#l514">514</a><td><tr><td><a name="l515" href="#l515">515</a><td> /**<tr><td><a name="l516" href="#l516">516</a><td> * Rejects this promise. If the error is itself a promise, this instance will<tr><td><a name="l517" href="#l517">517</a><td> * be chained to it and be rejected with the error's resolved value.<tr><td><a name="l518" href="#l518">518</a><td> * @param {*=} opt_error The rejection reason, typically either a<tr><td><a name="l519" href="#l519">519</a><td> * {@code Error} or a {@code string}.<tr><td><a name="l520" href="#l520">520</a><td> */<tr><td><a name="l521" href="#l521">521</a><td> function reject(opt_error) {<tr><td><a name="l522" href="#l522">522</a><td> resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error);<tr><td><a name="l523" href="#l523">523</a><td> }<tr><td><a name="l524" href="#l524">524</a><td><tr><td><a name="l525" href="#l525">525</a><td> /**<tr><td><a name="l526" href="#l526">526</a><td> * Attempts to cancel the computation of this instance's value. This attempt<tr><td><a name="l527" href="#l527">527</a><td> * will silently fail if this instance has already resolved.<tr><td><a name="l528" href="#l528">528</a><td> * @param {*=} opt_reason The reason for cancelling this promise.<tr><td><a name="l529" href="#l529">529</a><td> */<tr><td><a name="l530" href="#l530">530</a><td> function cancel(opt_reason) {<tr><td><a name="l531" href="#l531">531</a><td> if (!isPending()) {<tr><td><a name="l532" href="#l532">532</a><td> return;<tr><td><a name="l533" href="#l533">533</a><td> }<tr><td><a name="l534" href="#l534">534</a><td><tr><td><a name="l535" href="#l535">535</a><td> if (opt_canceller) {<tr><td><a name="l536" href="#l536">536</a><td> opt_reason = opt_canceller(opt_reason) || opt_reason;<tr><td><a name="l537" href="#l537">537</a><td> }<tr><td><a name="l538" href="#l538">538</a><td><tr><td><a name="l539" href="#l539">539</a><td> reject(opt_reason);<tr><td><a name="l540" href="#l540">540</a><td> }<tr><td><a name="l541" href="#l541">541</a><td><tr><td><a name="l542" href="#l542">542</a><td> this.promise = promise;<tr><td><a name="l543" href="#l543">543</a><td> this.promise.then = this.then = then;<tr><td><a name="l544" href="#l544">544</a><td> this.promise.cancel = this.cancel = cancel;<tr><td><a name="l545" href="#l545">545</a><td> this.promise.isPending = this.isPending = isPending;<tr><td><a name="l546" href="#l546">546</a><td> this.fulfill = fulfill;<tr><td><a name="l547" href="#l547">547</a><td> this.reject = this.errback = reject;<tr><td><a name="l548" href="#l548">548</a><td><tr><td><a name="l549" href="#l549">549</a><td> // Only expose this function to our internal classes.<tr><td><a name="l550" href="#l550">550</a><td> // TODO: find a cleaner way of handling this.<tr><td><a name="l551" href="#l551">551</a><td> if (this instanceof webdriver.promise.Task_) {<tr><td><a name="l552" href="#l552">552</a><td> this.removeAll = removeAll;<tr><td><a name="l553" href="#l553">553</a><td> }<tr><td><a name="l554" href="#l554">554</a><td><tr><td><a name="l555" href="#l555">555</a><td> // Export symbols necessary for the contract on this object to work in<tr><td><a name="l556" href="#l556">556</a><td> // compiled mode.<tr><td><a name="l557" href="#l557">557</a><td> goog.exportProperty(this, 'then', this.then);<tr><td><a name="l558" href="#l558">558</a><td> goog.exportProperty(this, 'cancel', cancel);<tr><td><a name="l559" href="#l559">559</a><td> goog.exportProperty(this, 'fulfill', fulfill);<tr><td><a name="l560" href="#l560">560</a><td> goog.exportProperty(this, 'reject', reject);<tr><td><a name="l561" href="#l561">561</a><td> goog.exportProperty(this, 'isPending', isPending);<tr><td><a name="l562" href="#l562">562</a><td> goog.exportProperty(this, 'promise', this.promise);<tr><td><a name="l563" href="#l563">563</a><td> goog.exportProperty(this.promise, 'then', this.then);<tr><td><a name="l564" href="#l564">564</a><td> goog.exportProperty(this.promise, 'cancel', cancel);<tr><td><a name="l565" href="#l565">565</a><td> goog.exportProperty(this.promise, 'isPending', isPending);<tr><td><a name="l566" href="#l566">566</a><td>};<tr><td><a name="l567" href="#l567">567</a><td>goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise);<tr><td><a name="l568" href="#l568">568</a><td><tr><td><a name="l569" href="#l569">569</a><td><tr><td><a name="l570" href="#l570">570</a><td>/**<tr><td><a name="l571" href="#l571">571</a><td> * Type definition for a listener registered on a Deferred object.<tr><td><a name="l572" href="#l572">572</a><td> * @typedef {{callback:(Function|undefined),<tr><td><a name="l573" href="#l573">573</a><td> * errback:(Function|undefined),<tr><td><a name="l574" href="#l574">574</a><td> * fulfill: function(*), reject: function(*)}}<tr><td><a name="l575" href="#l575">575</a><td> * @private<tr><td><a name="l576" href="#l576">576</a><td> */<tr><td><a name="l577" href="#l577">577</a><td>webdriver.promise.Deferred.Listener_;<tr><td><a name="l578" href="#l578">578</a><td><tr><td><a name="l579" href="#l579">579</a><td><tr><td><a name="l580" href="#l580">580</a><td>/**<tr><td><a name="l581" href="#l581">581</a><td> * The three states a {@link webdriver.promise.Deferred} object may be in.<tr><td><a name="l582" href="#l582">582</a><td> * @enum {number}<tr><td><a name="l583" href="#l583">583</a><td> * @private<tr><td><a name="l584" href="#l584">584</a><td> */<tr><td><a name="l585" href="#l585">585</a><td>webdriver.promise.Deferred.State_ = {<tr><td><a name="l586" href="#l586">586</a><td> REJECTED: -1,<tr><td><a name="l587" href="#l587">587</a><td> PENDING: 0,<tr><td><a name="l588" href="#l588">588</a><td> BLOCKED: 1,<tr><td><a name="l589" href="#l589">589</a><td> RESOLVED: 2<tr><td><a name="l590" href="#l590">590</a><td>};<tr><td><a name="l591" href="#l591">591</a><td><tr><td><a name="l592" href="#l592">592</a><td><tr><td><a name="l593" href="#l593">593</a><td>/**<tr><td><a name="l594" href="#l594">594</a><td> * Tests if a value is an Error-like object. This is more than an straight<tr><td><a name="l595" href="#l595">595</a><td> * instanceof check since the value may originate from another context.<tr><td><a name="l596" href="#l596">596</a><td> * @param {*} value The value to test.<tr><td><a name="l597" href="#l597">597</a><td> * @return {boolean} Whether the value is an error.<tr><td><a name="l598" href="#l598">598</a><td> * @private<tr><td><a name="l599" href="#l599">599</a><td> */<tr><td><a name="l600" href="#l600">600</a><td>webdriver.promise.isError_ = function(value) {<tr><td><a name="l601" href="#l601">601</a><td> return value instanceof Error ||<tr><td><a name="l602" href="#l602">602</a><td> goog.isObject(value) &&<tr><td><a name="l603" href="#l603">603</a><td> (Object.prototype.toString.call(value) === '[object Error]' ||<tr><td><a name="l604" href="#l604">604</a><td> // A special test for goog.testing.JsUnitException.<tr><td><a name="l605" href="#l605">605</a><td> value.isJsUnitException);<tr><td><a name="l606" href="#l606">606</a><td><tr><td><a name="l607" href="#l607">607</a><td>};<tr><td><a name="l608" href="#l608">608</a><td><tr><td><a name="l609" href="#l609">609</a><td><tr><td><a name="l610" href="#l610">610</a><td>/**<tr><td><a name="l611" href="#l611">611</a><td> * Determines whether a {@code value} should be treated as a promise.<tr><td><a name="l612" href="#l612">612</a><td> * Any object whose "then" property is a function will be considered a promise.<tr><td><a name="l613" href="#l613">613</a><td> *<tr><td><a name="l614" href="#l614">614</a><td> * @param {*} value The value to test.<tr><td><a name="l615" href="#l615">615</a><td> * @return {boolean} Whether the value is a promise.<tr><td><a name="l616" href="#l616">616</a><td> */<tr><td><a name="l617" href="#l617">617</a><td>webdriver.promise.isPromise = function(value) {<tr><td><a name="l618" href="#l618">618</a><td> return !!value && goog.isObject(value) &&<tr><td><a name="l619" href="#l619">619</a><td> // Use array notation so the Closure compiler does not obfuscate away our<tr><td><a name="l620" href="#l620">620</a><td> // contract.<tr><td><a name="l621" href="#l621">621</a><td> goog.isFunction(value['then']);<tr><td><a name="l622" href="#l622">622</a><td>};<tr><td><a name="l623" href="#l623">623</a><td><tr><td><a name="l624" href="#l624">624</a><td><tr><td><a name="l625" href="#l625">625</a><td>/**<tr><td><a name="l626" href="#l626">626</a><td> * Creates a promise that will be resolved at a set time in the future.<tr><td><a name="l627" href="#l627">627</a><td> * @param {number} ms The amount of time, in milliseconds, to wait before<tr><td><a name="l628" href="#l628">628</a><td> * resolving the promise.<tr><td><a name="l629" href="#l629">629</a><td> * @return {!webdriver.promise.Promise} The promise.<tr><td><a name="l630" href="#l630">630</a><td> */<tr><td><a name="l631" href="#l631">631</a><td>webdriver.promise.delayed = function(ms) {<tr><td><a name="l632" href="#l632">632</a><td> var timer = webdriver.promise.controlFlow().timer;<tr><td><a name="l633" href="#l633">633</a><td> var key;<tr><td><a name="l634" href="#l634">634</a><td> var deferred = new webdriver.promise.Deferred(function() {<tr><td><a name="l635" href="#l635">635</a><td> timer.clearTimeout(key);<tr><td><a name="l636" href="#l636">636</a><td> });<tr><td><a name="l637" href="#l637">637</a><td> key = timer.setTimeout(deferred.fulfill, ms);<tr><td><a name="l638" href="#l638">638</a><td> return deferred.promise;<tr><td><a name="l639" href="#l639">639</a><td>};<tr><td><a name="l640" href="#l640">640</a><td><tr><td><a name="l641" href="#l641">641</a><td><tr><td><a name="l642" href="#l642">642</a><td>/**<tr><td><a name="l643" href="#l643">643</a><td> * Creates a new deferred object.<tr><td><a name="l644" href="#l644">644</a><td> * @param {Function=} opt_canceller Function to call when cancelling the<tr><td><a name="l645" href="#l645">645</a><td> * computation of this instance's value.<tr><td><a name="l646" href="#l646">646</a><td> * @return {!webdriver.promise.Deferred.<T>} The new deferred object.<tr><td><a name="l647" href="#l647">647</a><td> * @template T<tr><td><a name="l648" href="#l648">648</a><td> */<tr><td><a name="l649" href="#l649">649</a><td>webdriver.promise.defer = function(opt_canceller) {<tr><td><a name="l650" href="#l650">650</a><td> return new webdriver.promise.Deferred(opt_canceller);<tr><td><a name="l651" href="#l651">651</a><td>};<tr><td><a name="l652" href="#l652">652</a><td><tr><td><a name="l653" href="#l653">653</a><td><tr><td><a name="l654" href="#l654">654</a><td>/**<tr><td><a name="l655" href="#l655">655</a><td> * Creates a promise that has been resolved with the given value.<tr><td><a name="l656" href="#l656">656</a><td> * @param {T=} opt_value The resolved value.<tr><td><a name="l657" href="#l657">657</a><td> * @return {!webdriver.promise.Promise.<T>} The resolved promise.<tr><td><a name="l658" href="#l658">658</a><td> * @template T<tr><td><a name="l659" href="#l659">659</a><td> */<tr><td><a name="l660" href="#l660">660</a><td>webdriver.promise.fulfilled = function(opt_value) {<tr><td><a name="l661" href="#l661">661</a><td> if (opt_value instanceof webdriver.promise.Promise) {<tr><td><a name="l662" href="#l662">662</a><td> return opt_value;<tr><td><a name="l663" href="#l663">663</a><td> }<tr><td><a name="l664" href="#l664">664</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l665" href="#l665">665</a><td> deferred.fulfill(opt_value);<tr><td><a name="l666" href="#l666">666</a><td> return deferred.promise;<tr><td><a name="l667" href="#l667">667</a><td>};<tr><td><a name="l668" href="#l668">668</a><td><tr><td><a name="l669" href="#l669">669</a><td><tr><td><a name="l670" href="#l670">670</a><td>/**<tr><td><a name="l671" href="#l671">671</a><td> * Creates a promise that has been rejected with the given reason.<tr><td><a name="l672" href="#l672">672</a><td> * @param {*=} opt_reason The rejection reason; may be any value, but is<tr><td><a name="l673" href="#l673">673</a><td> * usually an Error or a string.<tr><td><a name="l674" href="#l674">674</a><td> * @return {!webdriver.promise.Promise.<T>} The rejected promise.<tr><td><a name="l675" href="#l675">675</a><td> * @template T<tr><td><a name="l676" href="#l676">676</a><td> */<tr><td><a name="l677" href="#l677">677</a><td>webdriver.promise.rejected = function(opt_reason) {<tr><td><a name="l678" href="#l678">678</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l679" href="#l679">679</a><td> deferred.reject(opt_reason);<tr><td><a name="l680" href="#l680">680</a><td> return deferred.promise;<tr><td><a name="l681" href="#l681">681</a><td>};<tr><td><a name="l682" href="#l682">682</a><td><tr><td><a name="l683" href="#l683">683</a><td><tr><td><a name="l684" href="#l684">684</a><td>/**<tr><td><a name="l685" href="#l685">685</a><td> * Wraps a function that is assumed to be a node-style callback as its final<tr><td><a name="l686" href="#l686">686</a><td> * argument. This callback takes two arguments: an error value (which will be<tr><td><a name="l687" href="#l687">687</a><td> * null if the call succeeded), and the success value as the second argument.<tr><td><a name="l688" href="#l688">688</a><td> * If the call fails, the returned promise will be rejected, otherwise it will<tr><td><a name="l689" href="#l689">689</a><td> * be resolved with the result.<tr><td><a name="l690" href="#l690">690</a><td> * @param {!Function} fn The function to wrap.<tr><td><a name="l691" href="#l691">691</a><td> * @param {...?} var_args The arguments to apply to the function, excluding the<tr><td><a name="l692" href="#l692">692</a><td> * final callback.<tr><td><a name="l693" href="#l693">693</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with the<tr><td><a name="l694" href="#l694">694</a><td> * result of the provided function's callback.<tr><td><a name="l695" href="#l695">695</a><td> */<tr><td><a name="l696" href="#l696">696</a><td>webdriver.promise.checkedNodeCall = function(fn, var_args) {<tr><td><a name="l697" href="#l697">697</a><td> var deferred = new webdriver.promise.Deferred(function() {<tr><td><a name="l698" href="#l698">698</a><td> throw Error('This Deferred may not be cancelled');<tr><td><a name="l699" href="#l699">699</a><td> });<tr><td><a name="l700" href="#l700">700</a><td> try {<tr><td><a name="l701" href="#l701">701</a><td> var args = goog.array.slice(arguments, 1);<tr><td><a name="l702" href="#l702">702</a><td> args.push(function(error, value) {<tr><td><a name="l703" href="#l703">703</a><td> error ? deferred.reject(error) : deferred.fulfill(value);<tr><td><a name="l704" href="#l704">704</a><td> });<tr><td><a name="l705" href="#l705">705</a><td> fn.apply(null, args);<tr><td><a name="l706" href="#l706">706</a><td> } catch (ex) {<tr><td><a name="l707" href="#l707">707</a><td> deferred.reject(ex);<tr><td><a name="l708" href="#l708">708</a><td> }<tr><td><a name="l709" href="#l709">709</a><td> return deferred.promise;<tr><td><a name="l710" href="#l710">710</a><td>};<tr><td><a name="l711" href="#l711">711</a><td><tr><td><a name="l712" href="#l712">712</a><td><tr><td><a name="l713" href="#l713">713</a><td>/**<tr><td><a name="l714" href="#l714">714</a><td> * Registers an observer on a promised {@code value}, returning a new promise<tr><td><a name="l715" href="#l715">715</a><td> * that will be resolved when the value is. If {@code value} is not a promise,<tr><td><a name="l716" href="#l716">716</a><td> * then the return promise will be immediately resolved.<tr><td><a name="l717" href="#l717">717</a><td> * @param {*} value The value to observe.<tr><td><a name="l718" href="#l718">718</a><td> * @param {Function=} opt_callback The function to call when the value is<tr><td><a name="l719" href="#l719">719</a><td> * resolved successfully.<tr><td><a name="l720" href="#l720">720</a><td> * @param {Function=} opt_errback The function to call when the value is<tr><td><a name="l721" href="#l721">721</a><td> * rejected.<tr><td><a name="l722" href="#l722">722</a><td> * @return {!webdriver.promise.Promise} A new promise.<tr><td><a name="l723" href="#l723">723</a><td> */<tr><td><a name="l724" href="#l724">724</a><td>webdriver.promise.when = function(value, opt_callback, opt_errback) {<tr><td><a name="l725" href="#l725">725</a><td> if (webdriver.promise.Thenable.isImplementation(value)) {<tr><td><a name="l726" href="#l726">726</a><td> return value.then(opt_callback, opt_errback);<tr><td><a name="l727" href="#l727">727</a><td> }<tr><td><a name="l728" href="#l728">728</a><td><tr><td><a name="l729" href="#l729">729</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l730" href="#l730">730</a><td><tr><td><a name="l731" href="#l731">731</a><td> webdriver.promise.asap(value, deferred.fulfill, deferred.reject);<tr><td><a name="l732" href="#l732">732</a><td><tr><td><a name="l733" href="#l733">733</a><td> return deferred.then(opt_callback, opt_errback);<tr><td><a name="l734" href="#l734">734</a><td>};<tr><td><a name="l735" href="#l735">735</a><td><tr><td><a name="l736" href="#l736">736</a><td><tr><td><a name="l737" href="#l737">737</a><td>/**<tr><td><a name="l738" href="#l738">738</a><td> * Invokes the appropriate callback function as soon as a promised<tr><td><a name="l739" href="#l739">739</a><td> * {@code value} is resolved. This function is similar to<tr><td><a name="l740" href="#l740">740</a><td> * {@link webdriver.promise.when}, except it does not return a new promise.<tr><td><a name="l741" href="#l741">741</a><td> * @param {*} value The value to observe.<tr><td><a name="l742" href="#l742">742</a><td> * @param {Function} callback The function to call when the value is<tr><td><a name="l743" href="#l743">743</a><td> * resolved successfully.<tr><td><a name="l744" href="#l744">744</a><td> * @param {Function=} opt_errback The function to call when the value is<tr><td><a name="l745" href="#l745">745</a><td> * rejected.<tr><td><a name="l746" href="#l746">746</a><td> */<tr><td><a name="l747" href="#l747">747</a><td>webdriver.promise.asap = function(value, callback, opt_errback) {<tr><td><a name="l748" href="#l748">748</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l749" href="#l749">749</a><td> value.then(callback, opt_errback);<tr><td><a name="l750" href="#l750">750</a><td><tr><td><a name="l751" href="#l751">751</a><td> // Maybe a Dojo-like deferred object?<tr><td><a name="l752" href="#l752">752</a><td> } else if (!!value && goog.isObject(value) &&<tr><td><a name="l753" href="#l753">753</a><td> goog.isFunction(value.addCallbacks)) {<tr><td><a name="l754" href="#l754">754</a><td> value.addCallbacks(callback, opt_errback);<tr><td><a name="l755" href="#l755">755</a><td><tr><td><a name="l756" href="#l756">756</a><td> // A raw value, return a resolved promise.<tr><td><a name="l757" href="#l757">757</a><td> } else if (callback) {<tr><td><a name="l758" href="#l758">758</a><td> callback(value);<tr><td><a name="l759" href="#l759">759</a><td> }<tr><td><a name="l760" href="#l760">760</a><td>};<tr><td><a name="l761" href="#l761">761</a><td><tr><td><a name="l762" href="#l762">762</a><td><tr><td><a name="l763" href="#l763">763</a><td>/**<tr><td><a name="l764" href="#l764">764</a><td> * Given an array of promises, will return a promise that will be fulfilled<tr><td><a name="l765" href="#l765">765</a><td> * with the fulfillment values of the input array's values. If any of the<tr><td><a name="l766" href="#l766">766</a><td> * input array's promises are rejected, the returned promise will be rejected<tr><td><a name="l767" href="#l767">767</a><td> * with the same reason.<tr><td><a name="l768" href="#l768">768</a><td> *<tr><td><a name="l769" href="#l769">769</a><td> * @param {!Array.<(T|!webdriver.promise.Promise.<T>)>} arr An array of<tr><td><a name="l770" href="#l770">770</a><td> * promises to wait on.<tr><td><a name="l771" href="#l771">771</a><td> * @return {!webdriver.promise.Promise.<!Array.<T>>} A promise that is<tr><td><a name="l772" href="#l772">772</a><td> * fulfilled with an array containing the fulfilled values of the<tr><td><a name="l773" href="#l773">773</a><td> * input array, or rejected with the same reason as the first<tr><td><a name="l774" href="#l774">774</a><td> * rejected value.<tr><td><a name="l775" href="#l775">775</a><td> * @template T<tr><td><a name="l776" href="#l776">776</a><td> */<tr><td><a name="l777" href="#l777">777</a><td>webdriver.promise.all = function(arr) {<tr><td><a name="l778" href="#l778">778</a><td> var n = arr.length;<tr><td><a name="l779" href="#l779">779</a><td> if (!n) {<tr><td><a name="l780" href="#l780">780</a><td> return webdriver.promise.fulfilled([]);<tr><td><a name="l781" href="#l781">781</a><td> }<tr><td><a name="l782" href="#l782">782</a><td><tr><td><a name="l783" href="#l783">783</a><td> var toFulfill = n;<tr><td><a name="l784" href="#l784">784</a><td> var result = webdriver.promise.defer();<tr><td><a name="l785" href="#l785">785</a><td> var values = [];<tr><td><a name="l786" href="#l786">786</a><td><tr><td><a name="l787" href="#l787">787</a><td> var onFulfill = function(index, value) {<tr><td><a name="l788" href="#l788">788</a><td> values[index] = value;<tr><td><a name="l789" href="#l789">789</a><td> toFulfill--;<tr><td><a name="l790" href="#l790">790</a><td> if (toFulfill == 0) {<tr><td><a name="l791" href="#l791">791</a><td> result.fulfill(values);<tr><td><a name="l792" href="#l792">792</a><td> }<tr><td><a name="l793" href="#l793">793</a><td> };<tr><td><a name="l794" href="#l794">794</a><td><tr><td><a name="l795" href="#l795">795</a><td> for (var i = 0; i < n; ++i) {<tr><td><a name="l796" href="#l796">796</a><td> webdriver.promise.asap(<tr><td><a name="l797" href="#l797">797</a><td> arr[i], goog.partial(onFulfill, i), result.reject);<tr><td><a name="l798" href="#l798">798</a><td> }<tr><td><a name="l799" href="#l799">799</a><td><tr><td><a name="l800" href="#l800">800</a><td> return result.promise;<tr><td><a name="l801" href="#l801">801</a><td>};<tr><td><a name="l802" href="#l802">802</a><td><tr><td><a name="l803" href="#l803">803</a><td><tr><td><a name="l804" href="#l804">804</a><td>/**<tr><td><a name="l805" href="#l805">805</a><td> * Calls a function for each element in an array and inserts the result into a<tr><td><a name="l806" href="#l806">806</a><td> * new array, which is used as the fulfillment value of the promise returned<tr><td><a name="l807" href="#l807">807</a><td> * by this function.<tr><td><a name="l808" href="#l808">808</a><td> *<tr><td><a name="l809" href="#l809">809</a><td> * <p>If the return value of the mapping function is a promise, this function<tr><td><a name="l810" href="#l810">810</a><td> * will wait for it to be fulfilled before inserting it into the new array.<tr><td><a name="l811" href="#l811">811</a><td> *<tr><td><a name="l812" href="#l812">812</a><td> * <p>If the mapping function throws or returns a rejected promise, the<tr><td><a name="l813" href="#l813">813</a><td> * promise returned by this function will be rejected with the same reason.<tr><td><a name="l814" href="#l814">814</a><td> * Only the first failure will be reported; all subsequent errors will be<tr><td><a name="l815" href="#l815">815</a><td> * silently ignored.<tr><td><a name="l816" href="#l816">816</a><td> *<tr><td><a name="l817" href="#l817">817</a><td> * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The<tr><td><a name="l818" href="#l818">818</a><td> * array to iterator over, or a promise that will resolve to said array.<tr><td><a name="l819" href="#l819">819</a><td> * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ?} fn The<tr><td><a name="l820" href="#l820">820</a><td> * function to call for each element in the array. This function should<tr><td><a name="l821" href="#l821">821</a><td> * expect three arguments (the element, the index, and the array itself.<tr><td><a name="l822" href="#l822">822</a><td> * @param {SELF=} opt_self The object to be used as the value of 'this' within<tr><td><a name="l823" href="#l823">823</a><td> * {@code fn}.<tr><td><a name="l824" href="#l824">824</a><td> * @template TYPE, SELF<tr><td><a name="l825" href="#l825">825</a><td> */<tr><td><a name="l826" href="#l826">826</a><td>webdriver.promise.map = function(arr, fn, opt_self) {<tr><td><a name="l827" href="#l827">827</a><td> return webdriver.promise.when(arr, function(arr) {<tr><td><a name="l828" href="#l828">828</a><td> var result = goog.array.map(arr, fn, opt_self);<tr><td><a name="l829" href="#l829">829</a><td> return webdriver.promise.all(result);<tr><td><a name="l830" href="#l830">830</a><td> });<tr><td><a name="l831" href="#l831">831</a><td>};<tr><td><a name="l832" href="#l832">832</a><td><tr><td><a name="l833" href="#l833">833</a><td><tr><td><a name="l834" href="#l834">834</a><td>/**<tr><td><a name="l835" href="#l835">835</a><td> * Calls a function for each element in an array, and if the function returns<tr><td><a name="l836" href="#l836">836</a><td> * true adds the element to a new array.<tr><td><a name="l837" href="#l837">837</a><td> *<tr><td><a name="l838" href="#l838">838</a><td> * <p>If the return value of the filter function is a promise, this function<tr><td><a name="l839" href="#l839">839</a><td> * will wait for it to be fulfilled before determining whether to insert the<tr><td><a name="l840" href="#l840">840</a><td> * element into the new array.<tr><td><a name="l841" href="#l841">841</a><td> *<tr><td><a name="l842" href="#l842">842</a><td> * <p>If the filter function throws or returns a rejected promise, the promise<tr><td><a name="l843" href="#l843">843</a><td> * returned by this function will be rejected with the same reason. Only the<tr><td><a name="l844" href="#l844">844</a><td> * first failure will be reported; all subsequent errors will be silently<tr><td><a name="l845" href="#l845">845</a><td> * ignored.<tr><td><a name="l846" href="#l846">846</a><td> *<tr><td><a name="l847" href="#l847">847</a><td> * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The<tr><td><a name="l848" href="#l848">848</a><td> * array to iterator over, or a promise that will resolve to said array.<tr><td><a name="l849" href="#l849">849</a><td> * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): (<tr><td><a name="l850" href="#l850">850</a><td> * boolean|webdriver.promise.Promise.<boolean>)} fn The function<tr><td><a name="l851" href="#l851">851</a><td> * to call for each element in the array.<tr><td><a name="l852" href="#l852">852</a><td> * @param {SELF=} opt_self The object to be used as the value of 'this' within<tr><td><a name="l853" href="#l853">853</a><td> * {@code fn}.<tr><td><a name="l854" href="#l854">854</a><td> * @template TYPE, SELF<tr><td><a name="l855" href="#l855">855</a><td> */<tr><td><a name="l856" href="#l856">856</a><td>webdriver.promise.filter = function(arr, fn, opt_self) {<tr><td><a name="l857" href="#l857">857</a><td> return webdriver.promise.when(arr, function(arr) {<tr><td><a name="l858" href="#l858">858</a><td> var originalValues = goog.array.clone(arr);<tr><td><a name="l859" href="#l859">859</a><td> return webdriver.promise.map(arr, fn, opt_self).then(function(include) {<tr><td><a name="l860" href="#l860">860</a><td> return goog.array.filter(originalValues, function(value, index) {<tr><td><a name="l861" href="#l861">861</a><td> return include[index];<tr><td><a name="l862" href="#l862">862</a><td> });<tr><td><a name="l863" href="#l863">863</a><td> });<tr><td><a name="l864" href="#l864">864</a><td> });<tr><td><a name="l865" href="#l865">865</a><td>};<tr><td><a name="l866" href="#l866">866</a><td><tr><td><a name="l867" href="#l867">867</a><td><tr><td><a name="l868" href="#l868">868</a><td>/**<tr><td><a name="l869" href="#l869">869</a><td> * Returns a promise that will be resolved with the input value in a<tr><td><a name="l870" href="#l870">870</a><td> * fully-resolved state. If the value is an array, each element will be fully<tr><td><a name="l871" href="#l871">871</a><td> * resolved. Likewise, if the value is an object, all keys will be fully<tr><td><a name="l872" href="#l872">872</a><td> * resolved. In both cases, all nested arrays and objects will also be<tr><td><a name="l873" href="#l873">873</a><td> * fully resolved. All fields are resolved in place; the returned promise will<tr><td><a name="l874" href="#l874">874</a><td> * resolve on {@code value} and not a copy.<tr><td><a name="l875" href="#l875">875</a><td> *<tr><td><a name="l876" href="#l876">876</a><td> * Warning: This function makes no checks against objects that contain<tr><td><a name="l877" href="#l877">877</a><td> * cyclical references:<tr><td><a name="l878" href="#l878">878</a><td> * <pre><code><tr><td><a name="l879" href="#l879">879</a><td> * var value = {};<tr><td><a name="l880" href="#l880">880</a><td> * value['self'] = value;<tr><td><a name="l881" href="#l881">881</a><td> * webdriver.promise.fullyResolved(value); // Stack overflow.<tr><td><a name="l882" href="#l882">882</a><td> * </code></pre><tr><td><a name="l883" href="#l883">883</a><td> *<tr><td><a name="l884" href="#l884">884</a><td> * @param {*} value The value to fully resolve.<tr><td><a name="l885" href="#l885">885</a><td> * @return {!webdriver.promise.Promise} A promise for a fully resolved version<tr><td><a name="l886" href="#l886">886</a><td> * of the input value.<tr><td><a name="l887" href="#l887">887</a><td> */<tr><td><a name="l888" href="#l888">888</a><td>webdriver.promise.fullyResolved = function(value) {<tr><td><a name="l889" href="#l889">889</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l890" href="#l890">890</a><td> return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_);<tr><td><a name="l891" href="#l891">891</a><td> }<tr><td><a name="l892" href="#l892">892</a><td> return webdriver.promise.fullyResolveValue_(value);<tr><td><a name="l893" href="#l893">893</a><td>};<tr><td><a name="l894" href="#l894">894</a><td><tr><td><a name="l895" href="#l895">895</a><td><tr><td><a name="l896" href="#l896">896</a><td>/**<tr><td><a name="l897" href="#l897">897</a><td> * @param {*} value The value to fully resolve. If a promise, assumed to<tr><td><a name="l898" href="#l898">898</a><td> * already be resolved.<tr><td><a name="l899" href="#l899">899</a><td> * @return {!webdriver.promise.Promise} A promise for a fully resolved version<tr><td><a name="l900" href="#l900">900</a><td> * of the input value.<tr><td><a name="l901" href="#l901">901</a><td> * @private<tr><td><a name="l902" href="#l902">902</a><td> */<tr><td><a name="l903" href="#l903">903</a><td>webdriver.promise.fullyResolveValue_ = function(value) {<tr><td><a name="l904" href="#l904">904</a><td> switch (goog.typeOf(value)) {<tr><td><a name="l905" href="#l905">905</a><td> case 'array':<tr><td><a name="l906" href="#l906">906</a><td> return webdriver.promise.fullyResolveKeys_(<tr><td><a name="l907" href="#l907">907</a><td> /** @type {!Array} */ (value));<tr><td><a name="l908" href="#l908">908</a><td><tr><td><a name="l909" href="#l909">909</a><td> case 'object':<tr><td><a name="l910" href="#l910">910</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l911" href="#l911">911</a><td> // We get here when the original input value is a promise that<tr><td><a name="l912" href="#l912">912</a><td> // resolves to itself. When the user provides us with such a promise,<tr><td><a name="l913" href="#l913">913</a><td> // trust that it counts as a "fully resolved" value and return it.<tr><td><a name="l914" href="#l914">914</a><td> // Of course, since it's already a promise, we can just return it<tr><td><a name="l915" href="#l915">915</a><td> // to the user instead of wrapping it in another promise.<tr><td><a name="l916" href="#l916">916</a><td> return /** @type {!webdriver.promise.Promise} */ (value);<tr><td><a name="l917" href="#l917">917</a><td> }<tr><td><a name="l918" href="#l918">918</a><td><tr><td><a name="l919" href="#l919">919</a><td> if (goog.isNumber(value.nodeType) &&<tr><td><a name="l920" href="#l920">920</a><td> goog.isObject(value.ownerDocument) &&<tr><td><a name="l921" href="#l921">921</a><td> goog.isNumber(value.ownerDocument.nodeType)) {<tr><td><a name="l922" href="#l922">922</a><td> // DOM node; return early to avoid infinite recursion. Should we<tr><td><a name="l923" href="#l923">923</a><td> // only support objects with a certain level of nesting?<tr><td><a name="l924" href="#l924">924</a><td> return webdriver.promise.fulfilled(value);<tr><td><a name="l925" href="#l925">925</a><td> }<tr><td><a name="l926" href="#l926">926</a><td><tr><td><a name="l927" href="#l927">927</a><td> return webdriver.promise.fullyResolveKeys_(<tr><td><a name="l928" href="#l928">928</a><td> /** @type {!Object} */ (value));<tr><td><a name="l929" href="#l929">929</a><td><tr><td><a name="l930" href="#l930">930</a><td> default: // boolean, function, null, number, string, undefined<tr><td><a name="l931" href="#l931">931</a><td> return webdriver.promise.fulfilled(value);<tr><td><a name="l932" href="#l932">932</a><td> }<tr><td><a name="l933" href="#l933">933</a><td>};<tr><td><a name="l934" href="#l934">934</a><td><tr><td><a name="l935" href="#l935">935</a><td><tr><td><a name="l936" href="#l936">936</a><td>/**<tr><td><a name="l937" href="#l937">937</a><td> * @param {!(Array|Object)} obj the object to resolve.<tr><td><a name="l938" href="#l938">938</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with the<tr><td><a name="l939" href="#l939">939</a><td> * input object once all of its values have been fully resolved.<tr><td><a name="l940" href="#l940">940</a><td> * @private<tr><td><a name="l941" href="#l941">941</a><td> */<tr><td><a name="l942" href="#l942">942</a><td>webdriver.promise.fullyResolveKeys_ = function(obj) {<tr><td><a name="l943" href="#l943">943</a><td> var isArray = goog.isArray(obj);<tr><td><a name="l944" href="#l944">944</a><td> var numKeys = isArray ? obj.length : goog.object.getCount(obj);<tr><td><a name="l945" href="#l945">945</a><td> if (!numKeys) {<tr><td><a name="l946" href="#l946">946</a><td> return webdriver.promise.fulfilled(obj);<tr><td><a name="l947" href="#l947">947</a><td> }<tr><td><a name="l948" href="#l948">948</a><td><tr><td><a name="l949" href="#l949">949</a><td> var numResolved = 0;<tr><td><a name="l950" href="#l950">950</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l951" href="#l951">951</a><td><tr><td><a name="l952" href="#l952">952</a><td> // In pre-IE9, goog.array.forEach will not iterate properly over arrays<tr><td><a name="l953" href="#l953">953</a><td> // containing undefined values because "index in array" returns false<tr><td><a name="l954" href="#l954">954</a><td> // when array[index] === undefined (even for x = [undefined, 1]). To get<tr><td><a name="l955" href="#l955">955</a><td> // around this, we need to use our own forEach implementation.<tr><td><a name="l956" href="#l956">956</a><td> // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be<tr><td><a name="l957" href="#l957">957</a><td> // reproduced in IE9 by changing the browser/document modes, it requires an<tr><td><a name="l958" href="#l958">958</a><td> // actual pre-IE9 browser. Yay, IE!<tr><td><a name="l959" href="#l959">959</a><td> var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) {<tr><td><a name="l960" href="#l960">960</a><td> var n = arr.length;<tr><td><a name="l961" href="#l961">961</a><td> for (var i = 0; i < n; ++i) {<tr><td><a name="l962" href="#l962">962</a><td> fn.call(null, arr[i], i, arr);<tr><td><a name="l963" href="#l963">963</a><td> }<tr><td><a name="l964" href="#l964">964</a><td> };<tr><td><a name="l965" href="#l965">965</a><td><tr><td><a name="l966" href="#l966">966</a><td> forEachKey(obj, function(partialValue, key) {<tr><td><a name="l967" href="#l967">967</a><td> var type = goog.typeOf(partialValue);<tr><td><a name="l968" href="#l968">968</a><td> if (type != 'array' && type != 'object') {<tr><td><a name="l969" href="#l969">969</a><td> maybeResolveValue();<tr><td><a name="l970" href="#l970">970</a><td> return;<tr><td><a name="l971" href="#l971">971</a><td> }<tr><td><a name="l972" href="#l972">972</a><td><tr><td><a name="l973" href="#l973">973</a><td> webdriver.promise.fullyResolved(partialValue).then(<tr><td><a name="l974" href="#l974">974</a><td> function(resolvedValue) {<tr><td><a name="l975" href="#l975">975</a><td> obj[key] = resolvedValue;<tr><td><a name="l976" href="#l976">976</a><td> maybeResolveValue();<tr><td><a name="l977" href="#l977">977</a><td> },<tr><td><a name="l978" href="#l978">978</a><td> deferred.reject);<tr><td><a name="l979" href="#l979">979</a><td> });<tr><td><a name="l980" href="#l980">980</a><td><tr><td><a name="l981" href="#l981">981</a><td> return deferred.promise;<tr><td><a name="l982" href="#l982">982</a><td><tr><td><a name="l983" href="#l983">983</a><td> function maybeResolveValue() {<tr><td><a name="l984" href="#l984">984</a><td> if (++numResolved == numKeys) {<tr><td><a name="l985" href="#l985">985</a><td> deferred.fulfill(obj);<tr><td><a name="l986" href="#l986">986</a><td> }<tr><td><a name="l987" href="#l987">987</a><td> }<tr><td><a name="l988" href="#l988">988</a><td>};<tr><td><a name="l989" href="#l989">989</a><td><tr><td><a name="l990" href="#l990">990</a><td><tr><td><a name="l991" href="#l991">991</a><td>//////////////////////////////////////////////////////////////////////////////<tr><td><a name="l992" href="#l992">992</a><td>//<tr><td><a name="l993" href="#l993">993</a><td>// webdriver.promise.ControlFlow<tr><td><a name="l994" href="#l994">994</a><td>//<tr><td><a name="l995" href="#l995">995</a><td>//////////////////////////////////////////////////////////////////////////////<tr><td><a name="l996" href="#l996">996</a><td><tr><td><a name="l997" href="#l997">997</a><td><tr><td><a name="l998" href="#l998">998</a><td><tr><td><a name="l999" href="#l999">999</a><td>/**<tr><td><a name="l1000" href="#l1000">1000</a><td> * Handles the execution of scheduled tasks, each of which may be an<tr><td><a name="l1001" href="#l1001">1001</a><td> * asynchronous operation. The control flow will ensure tasks are executed in<tr><td><a name="l1002" href="#l1002">1002</a><td> * the ordered scheduled, starting each task only once those before it have<tr><td><a name="l1003" href="#l1003">1003</a><td> * completed.<tr><td><a name="l1004" href="#l1004">1004</a><td> *<tr><td><a name="l1005" href="#l1005">1005</a><td> * <p>Each task scheduled within this flow may return a<tr><td><a name="l1006" href="#l1006">1006</a><td> * {@link webdriver.promise.Promise} to indicate it is an asynchronous<tr><td><a name="l1007" href="#l1007">1007</a><td> * operation. The ControlFlow will wait for such promises to be resolved before<tr><td><a name="l1008" href="#l1008">1008</a><td> * marking the task as completed.<tr><td><a name="l1009" href="#l1009">1009</a><td> *<tr><td><a name="l1010" href="#l1010">1010</a><td> * <p>Tasks and each callback registered on a {@link webdriver.promise.Deferred}<tr><td><a name="l1011" href="#l1011">1011</a><td> * will be run in their own ControlFlow frame. Any tasks scheduled within a<tr><td><a name="l1012" href="#l1012">1012</a><td> * frame will have priority over previously scheduled tasks. Furthermore, if<tr><td><a name="l1013" href="#l1013">1013</a><td> * any of the tasks in the frame fails, the remainder of the tasks in that frame<tr><td><a name="l1014" href="#l1014">1014</a><td> * will be discarded and the failure will be propagated to the user through the<tr><td><a name="l1015" href="#l1015">1015</a><td> * callback/task's promised result.<tr><td><a name="l1016" href="#l1016">1016</a><td> *<tr><td><a name="l1017" href="#l1017">1017</a><td> * <p>Each time a ControlFlow empties its task queue, it will fire an<tr><td><a name="l1018" href="#l1018">1018</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely,<tr><td><a name="l1019" href="#l1019">1019</a><td> * whenever the flow terminates due to an unhandled error, it will remove all<tr><td><a name="l1020" href="#l1020">1020</a><td> * remaining tasks in its queue and fire an<tr><td><a name="l1021" href="#l1021">1021</a><td> * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. If<tr><td><a name="l1022" href="#l1022">1022</a><td> * there are no listeners registered with the flow, the error will be<tr><td><a name="l1023" href="#l1023">1023</a><td> * rethrown to the global error handler.<tr><td><a name="l1024" href="#l1024">1024</a><td> *<tr><td><a name="l1025" href="#l1025">1025</a><td> * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object<tr><td><a name="l1026" href="#l1026">1026</a><td> * to use. Should only be set for testing.<tr><td><a name="l1027" href="#l1027">1027</a><td> * @constructor<tr><td><a name="l1028" href="#l1028">1028</a><td> * @extends {webdriver.EventEmitter}<tr><td><a name="l1029" href="#l1029">1029</a><td> */<tr><td><a name="l1030" href="#l1030">1030</a><td>webdriver.promise.ControlFlow = function(opt_timer) {<tr><td><a name="l1031" href="#l1031">1031</a><td> webdriver.EventEmitter.call(this);<tr><td><a name="l1032" href="#l1032">1032</a><td><tr><td><a name="l1033" href="#l1033">1033</a><td> /**<tr><td><a name="l1034" href="#l1034">1034</a><td> * The timer used by this instance.<tr><td><a name="l1035" href="#l1035">1035</a><td> * @type {webdriver.promise.ControlFlow.Timer}<tr><td><a name="l1036" href="#l1036">1036</a><td> */<tr><td><a name="l1037" href="#l1037">1037</a><td> this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer;<tr><td><a name="l1038" href="#l1038">1038</a><td><tr><td><a name="l1039" href="#l1039">1039</a><td> /**<tr><td><a name="l1040" href="#l1040">1040</a><td> * A list of recent tasks. Each time a new task is started, or a frame is<tr><td><a name="l1041" href="#l1041">1041</a><td> * completed, the previously recorded task is removed from this list. If<tr><td><a name="l1042" href="#l1042">1042</a><td> * there are multiple tasks, task N+1 is considered a sub-task of task<tr><td><a name="l1043" href="#l1043">1043</a><td> * N.<tr><td><a name="l1044" href="#l1044">1044</a><td> * @private {!Array.<!webdriver.promise.Task_>}<tr><td><a name="l1045" href="#l1045">1045</a><td> */<tr><td><a name="l1046" href="#l1046">1046</a><td> this.history_ = [];<tr><td><a name="l1047" href="#l1047">1047</a><td>};<tr><td><a name="l1048" href="#l1048">1048</a><td>goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter);<tr><td><a name="l1049" href="#l1049">1049</a><td><tr><td><a name="l1050" href="#l1050">1050</a><td><tr><td><a name="l1051" href="#l1051">1051</a><td>/**<tr><td><a name="l1052" href="#l1052">1052</a><td> * @typedef {{clearInterval: function(number),<tr><td><a name="l1053" href="#l1053">1053</a><td> * clearTimeout: function(number),<tr><td><a name="l1054" href="#l1054">1054</a><td> * setInterval: function(!Function, number): number,<tr><td><a name="l1055" href="#l1055">1055</a><td> * setTimeout: function(!Function, number): number}}<tr><td><a name="l1056" href="#l1056">1056</a><td> */<tr><td><a name="l1057" href="#l1057">1057</a><td>webdriver.promise.ControlFlow.Timer;<tr><td><a name="l1058" href="#l1058">1058</a><td><tr><td><a name="l1059" href="#l1059">1059</a><td><tr><td><a name="l1060" href="#l1060">1060</a><td>/**<tr><td><a name="l1061" href="#l1061">1061</a><td> * The default timer object, which uses the global timer functions.<tr><td><a name="l1062" href="#l1062">1062</a><td> * @type {webdriver.promise.ControlFlow.Timer}<tr><td><a name="l1063" href="#l1063">1063</a><td> */<tr><td><a name="l1064" href="#l1064">1064</a><td>webdriver.promise.ControlFlow.defaultTimer = (function() {<tr><td><a name="l1065" href="#l1065">1065</a><td> // The default timer functions may be defined as free variables for the<tr><td><a name="l1066" href="#l1066">1066</a><td> // current context, so do not reference them using "window" or<tr><td><a name="l1067" href="#l1067">1067</a><td> // "goog.global". Also, we must invoke them in a closure, and not using<tr><td><a name="l1068" href="#l1068">1068</a><td> // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or<tr><td><a name="l1069" href="#l1069">1069</a><td> // "Invalid calling object" (IE) errors.<tr><td><a name="l1070" href="#l1070">1070</a><td> return {<tr><td><a name="l1071" href="#l1071">1071</a><td> clearInterval: wrap(clearInterval),<tr><td><a name="l1072" href="#l1072">1072</a><td> clearTimeout: wrap(clearTimeout),<tr><td><a name="l1073" href="#l1073">1073</a><td> setInterval: wrap(setInterval),<tr><td><a name="l1074" href="#l1074">1074</a><td> setTimeout: wrap(setTimeout)<tr><td><a name="l1075" href="#l1075">1075</a><td> };<tr><td><a name="l1076" href="#l1076">1076</a><td><tr><td><a name="l1077" href="#l1077">1077</a><td> function wrap(fn) {<tr><td><a name="l1078" href="#l1078">1078</a><td> return function() {<tr><td><a name="l1079" href="#l1079">1079</a><td> // Cannot use .call() or .apply() since we do not know which variable<tr><td><a name="l1080" href="#l1080">1080</a><td> // the function is bound to, and using the wrong one will generate<tr><td><a name="l1081" href="#l1081">1081</a><td> // an error.<tr><td><a name="l1082" href="#l1082">1082</a><td> return fn(arguments[0], arguments[1]);<tr><td><a name="l1083" href="#l1083">1083</a><td> };<tr><td><a name="l1084" href="#l1084">1084</a><td> }<tr><td><a name="l1085" href="#l1085">1085</a><td>})();<tr><td><a name="l1086" href="#l1086">1086</a><td><tr><td><a name="l1087" href="#l1087">1087</a><td><tr><td><a name="l1088" href="#l1088">1088</a><td>/**<tr><td><a name="l1089" href="#l1089">1089</a><td> * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.<tr><td><a name="l1090" href="#l1090">1090</a><td> * @enum {string}<tr><td><a name="l1091" href="#l1091">1091</a><td> */<tr><td><a name="l1092" href="#l1092">1092</a><td>webdriver.promise.ControlFlow.EventType = {<tr><td><a name="l1093" href="#l1093">1093</a><td><tr><td><a name="l1094" href="#l1094">1094</a><td> /** Emitted when all tasks have been successfully executed. */<tr><td><a name="l1095" href="#l1095">1095</a><td> IDLE: 'idle',<tr><td><a name="l1096" href="#l1096">1096</a><td><tr><td><a name="l1097" href="#l1097">1097</a><td> /** Emitted when a ControlFlow has been reset. */<tr><td><a name="l1098" href="#l1098">1098</a><td> RESET: 'reset',<tr><td><a name="l1099" href="#l1099">1099</a><td><tr><td><a name="l1100" href="#l1100">1100</a><td> /** Emitted whenever a new task has been scheduled. */<tr><td><a name="l1101" href="#l1101">1101</a><td> SCHEDULE_TASK: 'scheduleTask',<tr><td><a name="l1102" href="#l1102">1102</a><td><tr><td><a name="l1103" href="#l1103">1103</a><td> /**<tr><td><a name="l1104" href="#l1104">1104</a><td> * Emitted whenever a control flow aborts due to an unhandled promise<tr><td><a name="l1105" href="#l1105">1105</a><td> * rejection. This event will be emitted along with the offending rejection<tr><td><a name="l1106" href="#l1106">1106</a><td> * reason. Upon emitting this event, the control flow will empty its task<tr><td><a name="l1107" href="#l1107">1107</a><td> * queue and revert to its initial state.<tr><td><a name="l1108" href="#l1108">1108</a><td> */<tr><td><a name="l1109" href="#l1109">1109</a><td> UNCAUGHT_EXCEPTION: 'uncaughtException'<tr><td><a name="l1110" href="#l1110">1110</a><td>};<tr><td><a name="l1111" href="#l1111">1111</a><td><tr><td><a name="l1112" href="#l1112">1112</a><td><tr><td><a name="l1113" href="#l1113">1113</a><td>/**<tr><td><a name="l1114" href="#l1114">1114</a><td> * How often, in milliseconds, the event loop should run.<tr><td><a name="l1115" href="#l1115">1115</a><td> * @type {number}<tr><td><a name="l1116" href="#l1116">1116</a><td> * @const<tr><td><a name="l1117" href="#l1117">1117</a><td> */<tr><td><a name="l1118" href="#l1118">1118</a><td>webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10;<tr><td><a name="l1119" href="#l1119">1119</a><td><tr><td><a name="l1120" href="#l1120">1120</a><td><tr><td><a name="l1121" href="#l1121">1121</a><td>/**<tr><td><a name="l1122" href="#l1122">1122</a><td> * Tracks the active execution frame for this instance. Lazily initialized<tr><td><a name="l1123" href="#l1123">1123</a><td> * when the first task is scheduled.<tr><td><a name="l1124" href="#l1124">1124</a><td> * @private {webdriver.promise.Frame_}<tr><td><a name="l1125" href="#l1125">1125</a><td> */<tr><td><a name="l1126" href="#l1126">1126</a><td>webdriver.promise.ControlFlow.prototype.activeFrame_ = null;<tr><td><a name="l1127" href="#l1127">1127</a><td><tr><td><a name="l1128" href="#l1128">1128</a><td><tr><td><a name="l1129" href="#l1129">1129</a><td>/**<tr><td><a name="l1130" href="#l1130">1130</a><td> * A reference to the frame in which new tasks should be scheduled. If<tr><td><a name="l1131" href="#l1131">1131</a><td> * {@code null}, tasks will be scheduled within the active frame. When forcing<tr><td><a name="l1132" href="#l1132">1132</a><td> * a function to run in the context of a new frame, this pointer is used to<tr><td><a name="l1133" href="#l1133">1133</a><td> * ensure tasks are scheduled within the newly created frame, even though it<tr><td><a name="l1134" href="#l1134">1134</a><td> * won't be active yet.<tr><td><a name="l1135" href="#l1135">1135</a><td> * @private {webdriver.promise.Frame_}<tr><td><a name="l1136" href="#l1136">1136</a><td> * @see {#runInNewFrame_}<tr><td><a name="l1137" href="#l1137">1137</a><td> */<tr><td><a name="l1138" href="#l1138">1138</a><td>webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null;<tr><td><a name="l1139" href="#l1139">1139</a><td><tr><td><a name="l1140" href="#l1140">1140</a><td><tr><td><a name="l1141" href="#l1141">1141</a><td>/**<tr><td><a name="l1142" href="#l1142">1142</a><td> * Timeout ID set when the flow is about to shutdown without any errors<tr><td><a name="l1143" href="#l1143">1143</a><td> * being detected. Upon shutting down, the flow will emit an<tr><td><a name="l1144" href="#l1144">1144</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events<tr><td><a name="l1145" href="#l1145">1145</a><td> * always follow a brief timeout in order to catch latent errors from the last<tr><td><a name="l1146" href="#l1146">1146</a><td> * completed task. If this task had a callback registered, but no errback, and<tr><td><a name="l1147" href="#l1147">1147</a><td> * the task fails, the unhandled failure would not be reported by the promise<tr><td><a name="l1148" href="#l1148">1148</a><td> * system until the next turn of the event loop:<tr><td><a name="l1149" href="#l1149">1149</a><td> *<tr><td><a name="l1150" href="#l1150">1150</a><td> * // Schedule 1 task that fails.<tr><td><a name="l1151" href="#l1151">1151</a><td> * var result = webriver.promise.controlFlow().schedule('example',<tr><td><a name="l1152" href="#l1152">1152</a><td> * function() { return webdriver.promise.rejected('failed'); });<tr><td><a name="l1153" href="#l1153">1153</a><td> * // Set a callback on the result. This delays reporting the unhandled<tr><td><a name="l1154" href="#l1154">1154</a><td> * // failure for 1 turn of the event loop.<tr><td><a name="l1155" href="#l1155">1155</a><td> * result.then(goog.nullFunction);<tr><td><a name="l1156" href="#l1156">1156</a><td> *<tr><td><a name="l1157" href="#l1157">1157</a><td> * @private {?number}<tr><td><a name="l1158" href="#l1158">1158</a><td> */<tr><td><a name="l1159" href="#l1159">1159</a><td>webdriver.promise.ControlFlow.prototype.shutdownId_ = null;<tr><td><a name="l1160" href="#l1160">1160</a><td><tr><td><a name="l1161" href="#l1161">1161</a><td><tr><td><a name="l1162" href="#l1162">1162</a><td>/**<tr><td><a name="l1163" href="#l1163">1163</a><td> * Interval ID for this instance's event loop.<tr><td><a name="l1164" href="#l1164">1164</a><td> * @private {?number}<tr><td><a name="l1165" href="#l1165">1165</a><td> */<tr><td><a name="l1166" href="#l1166">1166</a><td>webdriver.promise.ControlFlow.prototype.eventLoopId_ = null;<tr><td><a name="l1167" href="#l1167">1167</a><td><tr><td><a name="l1168" href="#l1168">1168</a><td><tr><td><a name="l1169" href="#l1169">1169</a><td>/**<tr><td><a name="l1170" href="#l1170">1170</a><td> * The number of "pending" promise rejections.<tr><td><a name="l1171" href="#l1171">1171</a><td> *<tr><td><a name="l1172" href="#l1172">1172</a><td> * <p>Each time a promise is rejected and is not handled by a listener, it will<tr><td><a name="l1173" href="#l1173">1173</a><td> * schedule a 0-based timeout to check if it is still unrejected in the next<tr><td><a name="l1174" href="#l1174">1174</a><td> * turn of the JS-event loop. This allows listeners to attach to, and handle,<tr><td><a name="l1175" href="#l1175">1175</a><td> * the rejected promise at any point in same turn of the event loop that the<tr><td><a name="l1176" href="#l1176">1176</a><td> * promise was rejected.<tr><td><a name="l1177" href="#l1177">1177</a><td> *<tr><td><a name="l1178" href="#l1178">1178</a><td> * <p>When this flow's own event loop triggers, it will not run if there<tr><td><a name="l1179" href="#l1179">1179</a><td> * are any outstanding promise rejections. This allows unhandled promises to<tr><td><a name="l1180" href="#l1180">1180</a><td> * be reported before a new task is started, ensuring the error is reported to<tr><td><a name="l1181" href="#l1181">1181</a><td> * the current task queue.<tr><td><a name="l1182" href="#l1182">1182</a><td> *<tr><td><a name="l1183" href="#l1183">1183</a><td> * @private {number}<tr><td><a name="l1184" href="#l1184">1184</a><td> */<tr><td><a name="l1185" href="#l1185">1185</a><td>webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0;<tr><td><a name="l1186" href="#l1186">1186</a><td><tr><td><a name="l1187" href="#l1187">1187</a><td><tr><td><a name="l1188" href="#l1188">1188</a><td>/**<tr><td><a name="l1189" href="#l1189">1189</a><td> * The number of aborted frames since the last time a task was executed or a<tr><td><a name="l1190" href="#l1190">1190</a><td> * frame completed successfully.<tr><td><a name="l1191" href="#l1191">1191</a><td> * @private {number}<tr><td><a name="l1192" href="#l1192">1192</a><td> */<tr><td><a name="l1193" href="#l1193">1193</a><td>webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0;<tr><td><a name="l1194" href="#l1194">1194</a><td><tr><td><a name="l1195" href="#l1195">1195</a><td><tr><td><a name="l1196" href="#l1196">1196</a><td>/**<tr><td><a name="l1197" href="#l1197">1197</a><td> * Resets this instance, clearing its queue and removing all event listeners.<tr><td><a name="l1198" href="#l1198">1198</a><td> */<tr><td><a name="l1199" href="#l1199">1199</a><td>webdriver.promise.ControlFlow.prototype.reset = function() {<tr><td><a name="l1200" href="#l1200">1200</a><td> this.activeFrame_ = null;<tr><td><a name="l1201" href="#l1201">1201</a><td> this.clearHistory();<tr><td><a name="l1202" href="#l1202">1202</a><td> this.emit(webdriver.promise.ControlFlow.EventType.RESET);<tr><td><a name="l1203" href="#l1203">1203</a><td> this.removeAllListeners();<tr><td><a name="l1204" href="#l1204">1204</a><td> this.cancelShutdown_();<tr><td><a name="l1205" href="#l1205">1205</a><td> this.cancelEventLoop_();<tr><td><a name="l1206" href="#l1206">1206</a><td>};<tr><td><a name="l1207" href="#l1207">1207</a><td><tr><td><a name="l1208" href="#l1208">1208</a><td><tr><td><a name="l1209" href="#l1209">1209</a><td>/**<tr><td><a name="l1210" href="#l1210">1210</a><td> * Returns a summary of the recent task activity for this instance. This<tr><td><a name="l1211" href="#l1211">1211</a><td> * includes the most recently completed task, as well as any parent tasks. In<tr><td><a name="l1212" href="#l1212">1212</a><td> * the returned summary, the task at index N is considered a sub-task of the<tr><td><a name="l1213" href="#l1213">1213</a><td> * task at index N+1.<tr><td><a name="l1214" href="#l1214">1214</a><td> * @return {!Array.<string>} A summary of this instance's recent task<tr><td><a name="l1215" href="#l1215">1215</a><td> * activity.<tr><td><a name="l1216" href="#l1216">1216</a><td> */<tr><td><a name="l1217" href="#l1217">1217</a><td>webdriver.promise.ControlFlow.prototype.getHistory = function() {<tr><td><a name="l1218" href="#l1218">1218</a><td> var pendingTasks = [];<tr><td><a name="l1219" href="#l1219">1219</a><td> var currentFrame = this.activeFrame_;<tr><td><a name="l1220" href="#l1220">1220</a><td> while (currentFrame) {<tr><td><a name="l1221" href="#l1221">1221</a><td> var task = currentFrame.getPendingTask();<tr><td><a name="l1222" href="#l1222">1222</a><td> if (task) {<tr><td><a name="l1223" href="#l1223">1223</a><td> pendingTasks.push(task);<tr><td><a name="l1224" href="#l1224">1224</a><td> }<tr><td><a name="l1225" href="#l1225">1225</a><td> // A frame's parent node will always be another frame.<tr><td><a name="l1226" href="#l1226">1226</a><td> currentFrame =<tr><td><a name="l1227" href="#l1227">1227</a><td> /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent());<tr><td><a name="l1228" href="#l1228">1228</a><td> }<tr><td><a name="l1229" href="#l1229">1229</a><td><tr><td><a name="l1230" href="#l1230">1230</a><td> var fullHistory = goog.array.concat(this.history_, pendingTasks);<tr><td><a name="l1231" href="#l1231">1231</a><td> return goog.array.map(fullHistory, function(task) {<tr><td><a name="l1232" href="#l1232">1232</a><td> return task.toString();<tr><td><a name="l1233" href="#l1233">1233</a><td> });<tr><td><a name="l1234" href="#l1234">1234</a><td>};<tr><td><a name="l1235" href="#l1235">1235</a><td><tr><td><a name="l1236" href="#l1236">1236</a><td><tr><td><a name="l1237" href="#l1237">1237</a><td>/** Clears this instance's task history. */<tr><td><a name="l1238" href="#l1238">1238</a><td>webdriver.promise.ControlFlow.prototype.clearHistory = function() {<tr><td><a name="l1239" href="#l1239">1239</a><td> this.history_ = [];<tr><td><a name="l1240" href="#l1240">1240</a><td>};<tr><td><a name="l1241" href="#l1241">1241</a><td><tr><td><a name="l1242" href="#l1242">1242</a><td><tr><td><a name="l1243" href="#l1243">1243</a><td>/**<tr><td><a name="l1244" href="#l1244">1244</a><td> * Removes a completed task from this instance's history record. If any<tr><td><a name="l1245" href="#l1245">1245</a><td> * tasks remain from aborted frames, those will be removed as well.<tr><td><a name="l1246" href="#l1246">1246</a><td> * @private<tr><td><a name="l1247" href="#l1247">1247</a><td> */<tr><td><a name="l1248" href="#l1248">1248</a><td>webdriver.promise.ControlFlow.prototype.trimHistory_ = function() {<tr><td><a name="l1249" href="#l1249">1249</a><td> if (this.numAbortedFrames_) {<tr><td><a name="l1250" href="#l1250">1250</a><td> goog.array.splice(this.history_,<tr><td><a name="l1251" href="#l1251">1251</a><td> this.history_.length - this.numAbortedFrames_,<tr><td><a name="l1252" href="#l1252">1252</a><td> this.numAbortedFrames_);<tr><td><a name="l1253" href="#l1253">1253</a><td> this.numAbortedFrames_ = 0;<tr><td><a name="l1254" href="#l1254">1254</a><td> }<tr><td><a name="l1255" href="#l1255">1255</a><td> this.history_.pop();<tr><td><a name="l1256" href="#l1256">1256</a><td>};<tr><td><a name="l1257" href="#l1257">1257</a><td><tr><td><a name="l1258" href="#l1258">1258</a><td><tr><td><a name="l1259" href="#l1259">1259</a><td>/**<tr><td><a name="l1260" href="#l1260">1260</a><td> * Property used to track whether an error has been annotated by<tr><td><a name="l1261" href="#l1261">1261</a><td> * {@link webdriver.promise.ControlFlow#annotateError}.<tr><td><a name="l1262" href="#l1262">1262</a><td> * @private {string}<tr><td><a name="l1263" href="#l1263">1263</a><td> * @const<tr><td><a name="l1264" href="#l1264">1264</a><td> */<tr><td><a name="l1265" href="#l1265">1265</a><td>webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ =<tr><td><a name="l1266" href="#l1266">1266</a><td> 'webdriver_promise_error_';<tr><td><a name="l1267" href="#l1267">1267</a><td><tr><td><a name="l1268" href="#l1268">1268</a><td><tr><td><a name="l1269" href="#l1269">1269</a><td>/**<tr><td><a name="l1270" href="#l1270">1270</a><td> * Appends a summary of this instance's recent task history to the given<tr><td><a name="l1271" href="#l1271">1271</a><td> * error's stack trace. This function will also ensure the error's stack trace<tr><td><a name="l1272" href="#l1272">1272</a><td> * is in canonical form.<tr><td><a name="l1273" href="#l1273">1273</a><td> * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate.<tr><td><a name="l1274" href="#l1274">1274</a><td> * @return {!(Error|goog.testing.JsUnitException)} The annotated error.<tr><td><a name="l1275" href="#l1275">1275</a><td> */<tr><td><a name="l1276" href="#l1276">1276</a><td>webdriver.promise.ControlFlow.prototype.annotateError = function(e) {<tr><td><a name="l1277" href="#l1277">1277</a><td> if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) {<tr><td><a name="l1278" href="#l1278">1278</a><td> return e;<tr><td><a name="l1279" href="#l1279">1279</a><td> }<tr><td><a name="l1280" href="#l1280">1280</a><td><tr><td><a name="l1281" href="#l1281">1281</a><td> var history = this.getHistory();<tr><td><a name="l1282" href="#l1282">1282</a><td> if (history.length) {<tr><td><a name="l1283" href="#l1283">1283</a><td> e = webdriver.stacktrace.format(e);<tr><td><a name="l1284" href="#l1284">1284</a><td><tr><td><a name="l1285" href="#l1285">1285</a><td> /** @type {!Error} */(e).stack += [<tr><td><a name="l1286" href="#l1286">1286</a><td> '\n==== async task ====\n',<tr><td><a name="l1287" href="#l1287">1287</a><td> history.join('\n==== async task ====\n')<tr><td><a name="l1288" href="#l1288">1288</a><td> ].join('');<tr><td><a name="l1289" href="#l1289">1289</a><td><tr><td><a name="l1290" href="#l1290">1290</a><td> e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true;<tr><td><a name="l1291" href="#l1291">1291</a><td> }<tr><td><a name="l1292" href="#l1292">1292</a><td><tr><td><a name="l1293" href="#l1293">1293</a><td> return e;<tr><td><a name="l1294" href="#l1294">1294</a><td>};<tr><td><a name="l1295" href="#l1295">1295</a><td><tr><td><a name="l1296" href="#l1296">1296</a><td><tr><td><a name="l1297" href="#l1297">1297</a><td>/**<tr><td><a name="l1298" href="#l1298">1298</a><td> * @return {string} The scheduled tasks still pending with this instance.<tr><td><a name="l1299" href="#l1299">1299</a><td> */<tr><td><a name="l1300" href="#l1300">1300</a><td>webdriver.promise.ControlFlow.prototype.getSchedule = function() {<tr><td><a name="l1301" href="#l1301">1301</a><td> return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]';<tr><td><a name="l1302" href="#l1302">1302</a><td>};<tr><td><a name="l1303" href="#l1303">1303</a><td><tr><td><a name="l1304" href="#l1304">1304</a><td><tr><td><a name="l1305" href="#l1305">1305</a><td>/**<tr><td><a name="l1306" href="#l1306">1306</a><td> * Schedules a task for execution. If there is nothing currently in the<tr><td><a name="l1307" href="#l1307">1307</a><td> * queue, the task will be executed in the next turn of the event loop. If<tr><td><a name="l1308" href="#l1308">1308</a><td> * the task function is a generator, the task will be executed using<tr><td><a name="l1309" href="#l1309">1309</a><td> * {@link webdriver.promise.consume}.<tr><td><a name="l1310" href="#l1310">1310</a><td> *<tr><td><a name="l1311" href="#l1311">1311</a><td> * @param {function(): (T|webdriver.promise.Promise.<T>)} fn The function to<tr><td><a name="l1312" href="#l1312">1312</a><td> * call to start the task. If the function returns a<tr><td><a name="l1313" href="#l1313">1313</a><td> * {@link webdriver.promise.Promise}, this instance will wait for it to be<tr><td><a name="l1314" href="#l1314">1314</a><td> * resolved before starting the next task.<tr><td><a name="l1315" href="#l1315">1315</a><td> * @param {string=} opt_description A description of the task.<tr><td><a name="l1316" href="#l1316">1316</a><td> * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved<tr><td><a name="l1317" href="#l1317">1317</a><td> * with the result of the action.<tr><td><a name="l1318" href="#l1318">1318</a><td> * @template T<tr><td><a name="l1319" href="#l1319">1319</a><td> */<tr><td><a name="l1320" href="#l1320">1320</a><td>webdriver.promise.ControlFlow.prototype.execute = function(<tr><td><a name="l1321" href="#l1321">1321</a><td> fn, opt_description) {<tr><td><a name="l1322" href="#l1322">1322</a><td> if (webdriver.promise.isGenerator(fn)) {<tr><td><a name="l1323" href="#l1323">1323</a><td> fn = goog.partial(webdriver.promise.consume, fn);<tr><td><a name="l1324" href="#l1324">1324</a><td> }<tr><td><a name="l1325" href="#l1325">1325</a><td><tr><td><a name="l1326" href="#l1326">1326</a><td> this.cancelShutdown_();<tr><td><a name="l1327" href="#l1327">1327</a><td><tr><td><a name="l1328" href="#l1328">1328</a><td> if (!this.activeFrame_) {<tr><td><a name="l1329" href="#l1329">1329</a><td> this.activeFrame_ = new webdriver.promise.Frame_(this);<tr><td><a name="l1330" href="#l1330">1330</a><td> }<tr><td><a name="l1331" href="#l1331">1331</a><td><tr><td><a name="l1332" href="#l1332">1332</a><td> // Trim an extra frame off the generated stack trace for the call to this<tr><td><a name="l1333" href="#l1333">1333</a><td> // function.<tr><td><a name="l1334" href="#l1334">1334</a><td> var snapshot = new webdriver.stacktrace.Snapshot(1);<tr><td><a name="l1335" href="#l1335">1335</a><td> var task = new webdriver.promise.Task_(<tr><td><a name="l1336" href="#l1336">1336</a><td> this, fn, opt_description || '', snapshot);<tr><td><a name="l1337" href="#l1337">1337</a><td> var scheduleIn = this.schedulingFrame_ || this.activeFrame_;<tr><td><a name="l1338" href="#l1338">1338</a><td> scheduleIn.addChild(task);<tr><td><a name="l1339" href="#l1339">1339</a><td><tr><td><a name="l1340" href="#l1340">1340</a><td> this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description);<tr><td><a name="l1341" href="#l1341">1341</a><td><tr><td><a name="l1342" href="#l1342">1342</a><td> this.scheduleEventLoopStart_();<tr><td><a name="l1343" href="#l1343">1343</a><td> return task.promise;<tr><td><a name="l1344" href="#l1344">1344</a><td>};<tr><td><a name="l1345" href="#l1345">1345</a><td><tr><td><a name="l1346" href="#l1346">1346</a><td><tr><td><a name="l1347" href="#l1347">1347</a><td>/**<tr><td><a name="l1348" href="#l1348">1348</a><td> * Inserts a {@code setTimeout} into the command queue. This is equivalent to<tr><td><a name="l1349" href="#l1349">1349</a><td> * a thread sleep in a synchronous programming language.<tr><td><a name="l1350" href="#l1350">1350</a><td> *<tr><td><a name="l1351" href="#l1351">1351</a><td> * @param {number} ms The timeout delay, in milliseconds.<tr><td><a name="l1352" href="#l1352">1352</a><td> * @param {string=} opt_description A description to accompany the timeout.<tr><td><a name="l1353" href="#l1353">1353</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with<tr><td><a name="l1354" href="#l1354">1354</a><td> * the result of the action.<tr><td><a name="l1355" href="#l1355">1355</a><td> */<tr><td><a name="l1356" href="#l1356">1356</a><td>webdriver.promise.ControlFlow.prototype.timeout = function(<tr><td><a name="l1357" href="#l1357">1357</a><td> ms, opt_description) {<tr><td><a name="l1358" href="#l1358">1358</a><td> return this.execute(function() {<tr><td><a name="l1359" href="#l1359">1359</a><td> return webdriver.promise.delayed(ms);<tr><td><a name="l1360" href="#l1360">1360</a><td> }, opt_description);<tr><td><a name="l1361" href="#l1361">1361</a><td>};<tr><td><a name="l1362" href="#l1362">1362</a><td><tr><td><a name="l1363" href="#l1363">1363</a><td><tr><td><a name="l1364" href="#l1364">1364</a><td>/**<tr><td><a name="l1365" href="#l1365">1365</a><td> * Schedules a task that shall wait for a condition to hold. Each condition<tr><td><a name="l1366" href="#l1366">1366</a><td> * function may return any value, but it will always be evaluated as a boolean.<tr><td><a name="l1367" href="#l1367">1367</a><td> *<tr><td><a name="l1368" href="#l1368">1368</a><td> * <p>Condition functions may schedule sub-tasks with this instance, however,<tr><td><a name="l1369" href="#l1369">1369</a><td> * their execution time will be factored into whether a wait has timed out.<tr><td><a name="l1370" href="#l1370">1370</a><td> *<tr><td><a name="l1371" href="#l1371">1371</a><td> * <p>In the event a condition returns a Promise, the polling loop will wait for<tr><td><a name="l1372" href="#l1372">1372</a><td> * it to be resolved before evaluating whether the condition has been satisfied.<tr><td><a name="l1373" href="#l1373">1373</a><td> * The resolution time for a promise is factored into whether a wait has timed<tr><td><a name="l1374" href="#l1374">1374</a><td> * out.<tr><td><a name="l1375" href="#l1375">1375</a><td> *<tr><td><a name="l1376" href="#l1376">1376</a><td> * <p>If the condition function throws, or returns a rejected promise, the<tr><td><a name="l1377" href="#l1377">1377</a><td> * wait task will fail.<tr><td><a name="l1378" href="#l1378">1378</a><td> *<tr><td><a name="l1379" href="#l1379">1379</a><td> * @param {!Function} condition The condition function to poll.<tr><td><a name="l1380" href="#l1380">1380</a><td> * @param {number} timeout How long to wait, in milliseconds, for the condition<tr><td><a name="l1381" href="#l1381">1381</a><td> * to hold before timing out.<tr><td><a name="l1382" href="#l1382">1382</a><td> * @param {string=} opt_message An optional error message to include if the<tr><td><a name="l1383" href="#l1383">1383</a><td> * wait times out; defaults to the empty string.<tr><td><a name="l1384" href="#l1384">1384</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved when the<tr><td><a name="l1385" href="#l1385">1385</a><td> * condition has been satisified. The promise shall be rejected if the wait<tr><td><a name="l1386" href="#l1386">1386</a><td> * times out waiting for the condition.<tr><td><a name="l1387" href="#l1387">1387</a><td> */<tr><td><a name="l1388" href="#l1388">1388</a><td>webdriver.promise.ControlFlow.prototype.wait = function(<tr><td><a name="l1389" href="#l1389">1389</a><td> condition, timeout, opt_message) {<tr><td><a name="l1390" href="#l1390">1390</a><td> var sleep = Math.min(timeout, 100);<tr><td><a name="l1391" href="#l1391">1391</a><td> var self = this;<tr><td><a name="l1392" href="#l1392">1392</a><td><tr><td><a name="l1393" href="#l1393">1393</a><td> if (webdriver.promise.isGenerator(condition)) {<tr><td><a name="l1394" href="#l1394">1394</a><td> condition = goog.partial(webdriver.promise.consume, condition);<tr><td><a name="l1395" href="#l1395">1395</a><td> }<tr><td><a name="l1396" href="#l1396">1396</a><td><tr><td><a name="l1397" href="#l1397">1397</a><td> return this.execute(function() {<tr><td><a name="l1398" href="#l1398">1398</a><td> var startTime = goog.now();<tr><td><a name="l1399" href="#l1399">1399</a><td> var waitResult = new webdriver.promise.Deferred();<tr><td><a name="l1400" href="#l1400">1400</a><td> var waitFrame = self.activeFrame_;<tr><td><a name="l1401" href="#l1401">1401</a><td> waitFrame.isWaiting = true;<tr><td><a name="l1402" href="#l1402">1402</a><td> pollCondition();<tr><td><a name="l1403" href="#l1403">1403</a><td> return waitResult.promise;<tr><td><a name="l1404" href="#l1404">1404</a><td><tr><td><a name="l1405" href="#l1405">1405</a><td> function pollCondition() {<tr><td><a name="l1406" href="#l1406">1406</a><td> self.runInNewFrame_(condition, function(value) {<tr><td><a name="l1407" href="#l1407">1407</a><td> var elapsed = goog.now() - startTime;<tr><td><a name="l1408" href="#l1408">1408</a><td> if (!!value) {<tr><td><a name="l1409" href="#l1409">1409</a><td> waitFrame.isWaiting = false;<tr><td><a name="l1410" href="#l1410">1410</a><td> waitResult.fulfill(value);<tr><td><a name="l1411" href="#l1411">1411</a><td> } else if (elapsed >= timeout) {<tr><td><a name="l1412" href="#l1412">1412</a><td> waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') +<tr><td><a name="l1413" href="#l1413">1413</a><td> 'Wait timed out after ' + elapsed + 'ms'));<tr><td><a name="l1414" href="#l1414">1414</a><td> } else {<tr><td><a name="l1415" href="#l1415">1415</a><td> self.timer.setTimeout(pollCondition, sleep);<tr><td><a name="l1416" href="#l1416">1416</a><td> }<tr><td><a name="l1417" href="#l1417">1417</a><td> }, waitResult.reject, true);<tr><td><a name="l1418" href="#l1418">1418</a><td> }<tr><td><a name="l1419" href="#l1419">1419</a><td> }, opt_message);<tr><td><a name="l1420" href="#l1420">1420</a><td>};<tr><td><a name="l1421" href="#l1421">1421</a><td><tr><td><a name="l1422" href="#l1422">1422</a><td><tr><td><a name="l1423" href="#l1423">1423</a><td>/**<tr><td><a name="l1424" href="#l1424">1424</a><td> * Schedules a task that will wait for another promise to resolve. The resolved<tr><td><a name="l1425" href="#l1425">1425</a><td> * promise's value will be returned as the task result.<tr><td><a name="l1426" href="#l1426">1426</a><td> * @param {!webdriver.promise.Promise} promise The promise to wait on.<tr><td><a name="l1427" href="#l1427">1427</a><td> * @return {!webdriver.promise.Promise} A promise that will resolve when the<tr><td><a name="l1428" href="#l1428">1428</a><td> * task has completed.<tr><td><a name="l1429" href="#l1429">1429</a><td> */<tr><td><a name="l1430" href="#l1430">1430</a><td>webdriver.promise.ControlFlow.prototype.await = function(promise) {<tr><td><a name="l1431" href="#l1431">1431</a><td> return this.execute(function() {<tr><td><a name="l1432" href="#l1432">1432</a><td> return promise;<tr><td><a name="l1433" href="#l1433">1433</a><td> });<tr><td><a name="l1434" href="#l1434">1434</a><td>};<tr><td><a name="l1435" href="#l1435">1435</a><td><tr><td><a name="l1436" href="#l1436">1436</a><td><tr><td><a name="l1437" href="#l1437">1437</a><td>/**<tr><td><a name="l1438" href="#l1438">1438</a><td> * Schedules the interval for this instance's event loop, if necessary.<tr><td><a name="l1439" href="#l1439">1439</a><td> * @private<tr><td><a name="l1440" href="#l1440">1440</a><td> */<tr><td><a name="l1441" href="#l1441">1441</a><td>webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {<tr><td><a name="l1442" href="#l1442">1442</a><td> if (!this.eventLoopId_) {<tr><td><a name="l1443" href="#l1443">1443</a><td> this.eventLoopId_ = this.timer.setInterval(<tr><td><a name="l1444" href="#l1444">1444</a><td> goog.bind(this.runEventLoop_, this),<tr><td><a name="l1445" href="#l1445">1445</a><td> webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY);<tr><td><a name="l1446" href="#l1446">1446</a><td> }<tr><td><a name="l1447" href="#l1447">1447</a><td>};<tr><td><a name="l1448" href="#l1448">1448</a><td><tr><td><a name="l1449" href="#l1449">1449</a><td><tr><td><a name="l1450" href="#l1450">1450</a><td>/**<tr><td><a name="l1451" href="#l1451">1451</a><td> * Cancels the event loop, if necessary.<tr><td><a name="l1452" href="#l1452">1452</a><td> * @private<tr><td><a name="l1453" href="#l1453">1453</a><td> */<tr><td><a name="l1454" href="#l1454">1454</a><td>webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() {<tr><td><a name="l1455" href="#l1455">1455</a><td> if (this.eventLoopId_) {<tr><td><a name="l1456" href="#l1456">1456</a><td> this.timer.clearInterval(this.eventLoopId_);<tr><td><a name="l1457" href="#l1457">1457</a><td> this.eventLoopId_ = null;<tr><td><a name="l1458" href="#l1458">1458</a><td> }<tr><td><a name="l1459" href="#l1459">1459</a><td>};<tr><td><a name="l1460" href="#l1460">1460</a><td><tr><td><a name="l1461" href="#l1461">1461</a><td><tr><td><a name="l1462" href="#l1462">1462</a><td>/**<tr><td><a name="l1463" href="#l1463">1463</a><td> * Executes the next task for the current frame. If the current frame has no<tr><td><a name="l1464" href="#l1464">1464</a><td> * more tasks, the frame's result will be resolved, returning control to the<tr><td><a name="l1465" href="#l1465">1465</a><td> * frame's creator. This will terminate the flow if the completed frame was at<tr><td><a name="l1466" href="#l1466">1466</a><td> * the top of the stack.<tr><td><a name="l1467" href="#l1467">1467</a><td> * @private<tr><td><a name="l1468" href="#l1468">1468</a><td> */<tr><td><a name="l1469" href="#l1469">1469</a><td>webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {<tr><td><a name="l1470" href="#l1470">1470</a><td> // If we get here and there are pending promise rejections, then those<tr><td><a name="l1471" href="#l1471">1471</a><td> // promises are queued up to run as soon as this (JS) event loop terminates.<tr><td><a name="l1472" href="#l1472">1472</a><td> // Short-circuit our loop to give those promises a chance to run. Otherwise,<tr><td><a name="l1473" href="#l1473">1473</a><td> // we might start a new task only to have it fail because of one of these<tr><td><a name="l1474" href="#l1474">1474</a><td> // pending rejections.<tr><td><a name="l1475" href="#l1475">1475</a><td> if (this.pendingRejections_) {<tr><td><a name="l1476" href="#l1476">1476</a><td> return;<tr><td><a name="l1477" href="#l1477">1477</a><td> }<tr><td><a name="l1478" href="#l1478">1478</a><td><tr><td><a name="l1479" href="#l1479">1479</a><td> // If the flow aborts due to an unhandled exception after we've scheduled<tr><td><a name="l1480" href="#l1480">1480</a><td> // another turn of the execution loop, we can end up in here with no tasks<tr><td><a name="l1481" href="#l1481">1481</a><td> // left. This is OK, just quietly return.<tr><td><a name="l1482" href="#l1482">1482</a><td> if (!this.activeFrame_) {<tr><td><a name="l1483" href="#l1483">1483</a><td> this.commenceShutdown_();<tr><td><a name="l1484" href="#l1484">1484</a><td> return;<tr><td><a name="l1485" href="#l1485">1485</a><td> }<tr><td><a name="l1486" href="#l1486">1486</a><td><tr><td><a name="l1487" href="#l1487">1487</a><td> var task;<tr><td><a name="l1488" href="#l1488">1488</a><td> if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) {<tr><td><a name="l1489" href="#l1489">1489</a><td> // Either the current frame is blocked on a pending task, or we don't have<tr><td><a name="l1490" href="#l1490">1490</a><td> // a task to finish because we've completed a frame. When completing a<tr><td><a name="l1491" href="#l1491">1491</a><td> // frame, we must abort the event loop to allow the frame's promise's<tr><td><a name="l1492" href="#l1492">1492</a><td> // callbacks to execute.<tr><td><a name="l1493" href="#l1493">1493</a><td> return;<tr><td><a name="l1494" href="#l1494">1494</a><td> }<tr><td><a name="l1495" href="#l1495">1495</a><td><tr><td><a name="l1496" href="#l1496">1496</a><td> var activeFrame = this.activeFrame_;<tr><td><a name="l1497" href="#l1497">1497</a><td> activeFrame.setPendingTask(task);<tr><td><a name="l1498" href="#l1498">1498</a><td> var markTaskComplete = goog.bind(function() {<tr><td><a name="l1499" href="#l1499">1499</a><td> this.history_.push(/** @type {!webdriver.promise.Task_} */ (task));<tr><td><a name="l1500" href="#l1500">1500</a><td> activeFrame.setPendingTask(null);<tr><td><a name="l1501" href="#l1501">1501</a><td> }, this);<tr><td><a name="l1502" href="#l1502">1502</a><td><tr><td><a name="l1503" href="#l1503">1503</a><td> this.trimHistory_();<tr><td><a name="l1504" href="#l1504">1504</a><td> var self = this;<tr><td><a name="l1505" href="#l1505">1505</a><td> this.runInNewFrame_(task.execute, function(result) {<tr><td><a name="l1506" href="#l1506">1506</a><td> markTaskComplete();<tr><td><a name="l1507" href="#l1507">1507</a><td> task.fulfill(result);<tr><td><a name="l1508" href="#l1508">1508</a><td> }, function(error) {<tr><td><a name="l1509" href="#l1509">1509</a><td> markTaskComplete();<tr><td><a name="l1510" href="#l1510">1510</a><td><tr><td><a name="l1511" href="#l1511">1511</a><td> if (!webdriver.promise.isError_(error) &&<tr><td><a name="l1512" href="#l1512">1512</a><td> !webdriver.promise.isPromise(error)) {<tr><td><a name="l1513" href="#l1513">1513</a><td> error = Error(error);<tr><td><a name="l1514" href="#l1514">1514</a><td> }<tr><td><a name="l1515" href="#l1515">1515</a><td><tr><td><a name="l1516" href="#l1516">1516</a><td> task.reject(self.annotateError(/** @type {!Error} */ (error)));<tr><td><a name="l1517" href="#l1517">1517</a><td> }, true);<tr><td><a name="l1518" href="#l1518">1518</a><td>};<tr><td><a name="l1519" href="#l1519">1519</a><td><tr><td><a name="l1520" href="#l1520">1520</a><td><tr><td><a name="l1521" href="#l1521">1521</a><td>/**<tr><td><a name="l1522" href="#l1522">1522</a><td> * @return {webdriver.promise.Task_} The next task to execute, or<tr><td><a name="l1523" href="#l1523">1523</a><td> * {@code null} if a frame was resolved.<tr><td><a name="l1524" href="#l1524">1524</a><td> * @private<tr><td><a name="l1525" href="#l1525">1525</a><td> */<tr><td><a name="l1526" href="#l1526">1526</a><td>webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {<tr><td><a name="l1527" href="#l1527">1527</a><td> var firstChild = this.activeFrame_.getFirstChild();<tr><td><a name="l1528" href="#l1528">1528</a><td> if (!firstChild) {<tr><td><a name="l1529" href="#l1529">1529</a><td> if (!this.activeFrame_.isWaiting) {<tr><td><a name="l1530" href="#l1530">1530</a><td> this.resolveFrame_(this.activeFrame_);<tr><td><a name="l1531" href="#l1531">1531</a><td> }<tr><td><a name="l1532" href="#l1532">1532</a><td> return null;<tr><td><a name="l1533" href="#l1533">1533</a><td> }<tr><td><a name="l1534" href="#l1534">1534</a><td><tr><td><a name="l1535" href="#l1535">1535</a><td> if (firstChild instanceof webdriver.promise.Frame_) {<tr><td><a name="l1536" href="#l1536">1536</a><td> this.activeFrame_ = firstChild;<tr><td><a name="l1537" href="#l1537">1537</a><td> return this.getNextTask_();<tr><td><a name="l1538" href="#l1538">1538</a><td> }<tr><td><a name="l1539" href="#l1539">1539</a><td><tr><td><a name="l1540" href="#l1540">1540</a><td> firstChild.getParent().removeChild(firstChild);<tr><td><a name="l1541" href="#l1541">1541</a><td> return firstChild;<tr><td><a name="l1542" href="#l1542">1542</a><td>};<tr><td><a name="l1543" href="#l1543">1543</a><td><tr><td><a name="l1544" href="#l1544">1544</a><td><tr><td><a name="l1545" href="#l1545">1545</a><td>/**<tr><td><a name="l1546" href="#l1546">1546</a><td> * @param {!webdriver.promise.Frame_} frame The frame to resolve.<tr><td><a name="l1547" href="#l1547">1547</a><td> * @private<tr><td><a name="l1548" href="#l1548">1548</a><td> */<tr><td><a name="l1549" href="#l1549">1549</a><td>webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) {<tr><td><a name="l1550" href="#l1550">1550</a><td> if (this.activeFrame_ === frame) {<tr><td><a name="l1551" href="#l1551">1551</a><td> // Frame parent is always another frame, but the compiler is not smart<tr><td><a name="l1552" href="#l1552">1552</a><td> // enough to recognize this.<tr><td><a name="l1553" href="#l1553">1553</a><td> this.activeFrame_ =<tr><td><a name="l1554" href="#l1554">1554</a><td> /** @type {webdriver.promise.Frame_} */ (frame.getParent());<tr><td><a name="l1555" href="#l1555">1555</a><td> }<tr><td><a name="l1556" href="#l1556">1556</a><td><tr><td><a name="l1557" href="#l1557">1557</a><td> if (frame.getParent()) {<tr><td><a name="l1558" href="#l1558">1558</a><td> frame.getParent().removeChild(frame);<tr><td><a name="l1559" href="#l1559">1559</a><td> }<tr><td><a name="l1560" href="#l1560">1560</a><td> this.trimHistory_();<tr><td><a name="l1561" href="#l1561">1561</a><td> frame.fulfill();<tr><td><a name="l1562" href="#l1562">1562</a><td><tr><td><a name="l1563" href="#l1563">1563</a><td> if (!this.activeFrame_) {<tr><td><a name="l1564" href="#l1564">1564</a><td> this.commenceShutdown_();<tr><td><a name="l1565" href="#l1565">1565</a><td> }<tr><td><a name="l1566" href="#l1566">1566</a><td>};<tr><td><a name="l1567" href="#l1567">1567</a><td><tr><td><a name="l1568" href="#l1568">1568</a><td><tr><td><a name="l1569" href="#l1569">1569</a><td>/**<tr><td><a name="l1570" href="#l1570">1570</a><td> * Aborts the current frame. The frame, and all of the tasks scheduled within it<tr><td><a name="l1571" href="#l1571">1571</a><td> * will be discarded. If this instance does not have an active frame, it will<tr><td><a name="l1572" href="#l1572">1572</a><td> * immediately terminate all execution.<tr><td><a name="l1573" href="#l1573">1573</a><td> * @param {*} error The reason the frame is being aborted; typically either<tr><td><a name="l1574" href="#l1574">1574</a><td> * an Error or string.<tr><td><a name="l1575" href="#l1575">1575</a><td> * @private<tr><td><a name="l1576" href="#l1576">1576</a><td> */<tr><td><a name="l1577" href="#l1577">1577</a><td>webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) {<tr><td><a name="l1578" href="#l1578">1578</a><td> // Annotate the error value if it is Error-like.<tr><td><a name="l1579" href="#l1579">1579</a><td> if (webdriver.promise.isError_(error)) {<tr><td><a name="l1580" href="#l1580">1580</a><td> this.annotateError(/** @type {!Error} */ (error));<tr><td><a name="l1581" href="#l1581">1581</a><td> }<tr><td><a name="l1582" href="#l1582">1582</a><td> this.numAbortedFrames_++;<tr><td><a name="l1583" href="#l1583">1583</a><td><tr><td><a name="l1584" href="#l1584">1584</a><td> if (!this.activeFrame_) {<tr><td><a name="l1585" href="#l1585">1585</a><td> this.abortNow_(error);<tr><td><a name="l1586" href="#l1586">1586</a><td> return;<tr><td><a name="l1587" href="#l1587">1587</a><td> }<tr><td><a name="l1588" href="#l1588">1588</a><td><tr><td><a name="l1589" href="#l1589">1589</a><td> // Frame parent is always another frame, but the compiler is not smart<tr><td><a name="l1590" href="#l1590">1590</a><td> // enough to recognize this.<tr><td><a name="l1591" href="#l1591">1591</a><td> var parent = /** @type {webdriver.promise.Frame_} */ (<tr><td><a name="l1592" href="#l1592">1592</a><td> this.activeFrame_.getParent());<tr><td><a name="l1593" href="#l1593">1593</a><td> if (parent) {<tr><td><a name="l1594" href="#l1594">1594</a><td> parent.removeChild(this.activeFrame_);<tr><td><a name="l1595" href="#l1595">1595</a><td> }<tr><td><a name="l1596" href="#l1596">1596</a><td><tr><td><a name="l1597" href="#l1597">1597</a><td> var frame = this.activeFrame_;<tr><td><a name="l1598" href="#l1598">1598</a><td> this.activeFrame_ = parent;<tr><td><a name="l1599" href="#l1599">1599</a><td> frame.reject(error);<tr><td><a name="l1600" href="#l1600">1600</a><td>};<tr><td><a name="l1601" href="#l1601">1601</a><td><tr><td><a name="l1602" href="#l1602">1602</a><td><tr><td><a name="l1603" href="#l1603">1603</a><td>/**<tr><td><a name="l1604" href="#l1604">1604</a><td> * Executes a function in a new frame. If the function does not schedule any new<tr><td><a name="l1605" href="#l1605">1605</a><td> * tasks, the frame will be discarded and the function's result returned<tr><td><a name="l1606" href="#l1606">1606</a><td> * immediately. Otherwise, a promise will be returned. This promise will be<tr><td><a name="l1607" href="#l1607">1607</a><td> * resolved with the function's result once all of the tasks scheduled within<tr><td><a name="l1608" href="#l1608">1608</a><td> * the function have been completed. If the function's frame is aborted, the<tr><td><a name="l1609" href="#l1609">1609</a><td> * returned promise will be rejected.<tr><td><a name="l1610" href="#l1610">1610</a><td> *<tr><td><a name="l1611" href="#l1611">1611</a><td> * @param {!Function} fn The function to execute.<tr><td><a name="l1612" href="#l1612">1612</a><td> * @param {function(*)} callback The function to call with a successful result.<tr><td><a name="l1613" href="#l1613">1613</a><td> * @param {function(*)} errback The function to call if there is an error.<tr><td><a name="l1614" href="#l1614">1614</a><td> * @param {boolean=} opt_activate Whether the active frame should be updated to<tr><td><a name="l1615" href="#l1615">1615</a><td> * the newly created frame so tasks are treated as sub-tasks.<tr><td><a name="l1616" href="#l1616">1616</a><td> * @private<tr><td><a name="l1617" href="#l1617">1617</a><td> */<tr><td><a name="l1618" href="#l1618">1618</a><td>webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(<tr><td><a name="l1619" href="#l1619">1619</a><td> fn, callback, errback, opt_activate) {<tr><td><a name="l1620" href="#l1620">1620</a><td> var newFrame = new webdriver.promise.Frame_(this),<tr><td><a name="l1621" href="#l1621">1621</a><td> self = this,<tr><td><a name="l1622" href="#l1622">1622</a><td> oldFrame = this.activeFrame_;<tr><td><a name="l1623" href="#l1623">1623</a><td><tr><td><a name="l1624" href="#l1624">1624</a><td> try {<tr><td><a name="l1625" href="#l1625">1625</a><td> if (!this.activeFrame_) {<tr><td><a name="l1626" href="#l1626">1626</a><td> this.activeFrame_ = newFrame;<tr><td><a name="l1627" href="#l1627">1627</a><td> } else {<tr><td><a name="l1628" href="#l1628">1628</a><td> this.activeFrame_.addChild(newFrame);<tr><td><a name="l1629" href="#l1629">1629</a><td> }<tr><td><a name="l1630" href="#l1630">1630</a><td><tr><td><a name="l1631" href="#l1631">1631</a><td> // Activate the new frame to force tasks to be treated as sub-tasks of<tr><td><a name="l1632" href="#l1632">1632</a><td> // the parent frame.<tr><td><a name="l1633" href="#l1633">1633</a><td> if (opt_activate) {<tr><td><a name="l1634" href="#l1634">1634</a><td> this.activeFrame_ = newFrame;<tr><td><a name="l1635" href="#l1635">1635</a><td> }<tr><td><a name="l1636" href="#l1636">1636</a><td><tr><td><a name="l1637" href="#l1637">1637</a><td> try {<tr><td><a name="l1638" href="#l1638">1638</a><td> this.schedulingFrame_ = newFrame;<tr><td><a name="l1639" href="#l1639">1639</a><td> webdriver.promise.pushFlow_(this);<tr><td><a name="l1640" href="#l1640">1640</a><td> var result = fn();<tr><td><a name="l1641" href="#l1641">1641</a><td> } finally {<tr><td><a name="l1642" href="#l1642">1642</a><td> webdriver.promise.popFlow_();<tr><td><a name="l1643" href="#l1643">1643</a><td> this.schedulingFrame_ = null;<tr><td><a name="l1644" href="#l1644">1644</a><td> }<tr><td><a name="l1645" href="#l1645">1645</a><td> newFrame.lockFrame();<tr><td><a name="l1646" href="#l1646">1646</a><td><tr><td><a name="l1647" href="#l1647">1647</a><td> // If there was nothing scheduled in the new frame we can discard the<tr><td><a name="l1648" href="#l1648">1648</a><td> // frame and return immediately.<tr><td><a name="l1649" href="#l1649">1649</a><td> if (!newFrame.children_.length) {<tr><td><a name="l1650" href="#l1650">1650</a><td> removeNewFrame();<tr><td><a name="l1651" href="#l1651">1651</a><td> webdriver.promise.asap(result, callback, errback);<tr><td><a name="l1652" href="#l1652">1652</a><td> return;<tr><td><a name="l1653" href="#l1653">1653</a><td> }<tr><td><a name="l1654" href="#l1654">1654</a><td><tr><td><a name="l1655" href="#l1655">1655</a><td> newFrame.then(function() {<tr><td><a name="l1656" href="#l1656">1656</a><td> webdriver.promise.asap(result, callback, errback);<tr><td><a name="l1657" href="#l1657">1657</a><td> }, function(e) {<tr><td><a name="l1658" href="#l1658">1658</a><td> if (webdriver.promise.Thenable.isImplementation(result) &&<tr><td><a name="l1659" href="#l1659">1659</a><td> result.isPending()) {<tr><td><a name="l1660" href="#l1660">1660</a><td> result.cancel(e);<tr><td><a name="l1661" href="#l1661">1661</a><td> e = result;<tr><td><a name="l1662" href="#l1662">1662</a><td> }<tr><td><a name="l1663" href="#l1663">1663</a><td> errback(e);<tr><td><a name="l1664" href="#l1664">1664</a><td> });<tr><td><a name="l1665" href="#l1665">1665</a><td> } catch (ex) {<tr><td><a name="l1666" href="#l1666">1666</a><td> removeNewFrame(new webdriver.promise.CanceledTaskError_(ex));<tr><td><a name="l1667" href="#l1667">1667</a><td> errback(ex);<tr><td><a name="l1668" href="#l1668">1668</a><td> }<tr><td><a name="l1669" href="#l1669">1669</a><td><tr><td><a name="l1670" href="#l1670">1670</a><td> /**<tr><td><a name="l1671" href="#l1671">1671</a><td> * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the<tr><td><a name="l1672" href="#l1672">1672</a><td> * error that triggered the removal of this frame.<tr><td><a name="l1673" href="#l1673">1673</a><td> */<tr><td><a name="l1674" href="#l1674">1674</a><td> function removeNewFrame(opt_err) {<tr><td><a name="l1675" href="#l1675">1675</a><td> var parent = newFrame.getParent();<tr><td><a name="l1676" href="#l1676">1676</a><td> if (parent) {<tr><td><a name="l1677" href="#l1677">1677</a><td> parent.removeChild(newFrame);<tr><td><a name="l1678" href="#l1678">1678</a><td> }<tr><td><a name="l1679" href="#l1679">1679</a><td><tr><td><a name="l1680" href="#l1680">1680</a><td> if (opt_err) {<tr><td><a name="l1681" href="#l1681">1681</a><td> newFrame.cancelRemainingTasks(opt_err);<tr><td><a name="l1682" href="#l1682">1682</a><td> }<tr><td><a name="l1683" href="#l1683">1683</a><td> self.activeFrame_ = oldFrame;<tr><td><a name="l1684" href="#l1684">1684</a><td> }<tr><td><a name="l1685" href="#l1685">1685</a><td>};<tr><td><a name="l1686" href="#l1686">1686</a><td><tr><td><a name="l1687" href="#l1687">1687</a><td><tr><td><a name="l1688" href="#l1688">1688</a><td>/**<tr><td><a name="l1689" href="#l1689">1689</a><td> * Commences the shutdown sequence for this instance. After one turn of the<tr><td><a name="l1690" href="#l1690">1690</a><td> * event loop, this object will emit the<tr><td><a name="l1691" href="#l1691">1691</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event to signal<tr><td><a name="l1692" href="#l1692">1692</a><td> * listeners that it has completed. During this wait, if another task is<tr><td><a name="l1693" href="#l1693">1693</a><td> * scheduled, the shutdown will be aborted.<tr><td><a name="l1694" href="#l1694">1694</a><td> * @private<tr><td><a name="l1695" href="#l1695">1695</a><td> */<tr><td><a name="l1696" href="#l1696">1696</a><td>webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() {<tr><td><a name="l1697" href="#l1697">1697</a><td> if (!this.shutdownId_) {<tr><td><a name="l1698" href="#l1698">1698</a><td> // Go ahead and stop the event loop now. If we're in here, then there are<tr><td><a name="l1699" href="#l1699">1699</a><td> // no more frames with tasks to execute. If we waited to cancel the event<tr><td><a name="l1700" href="#l1700">1700</a><td> // loop in our timeout below, the event loop could trigger *before* the<tr><td><a name="l1701" href="#l1701">1701</a><td> // timeout, generating an error from there being no frames.<tr><td><a name="l1702" href="#l1702">1702</a><td> // If #execute is called before the timeout below fires, it will cancel<tr><td><a name="l1703" href="#l1703">1703</a><td> // the timeout and restart the event loop.<tr><td><a name="l1704" href="#l1704">1704</a><td> this.cancelEventLoop_();<tr><td><a name="l1705" href="#l1705">1705</a><td><tr><td><a name="l1706" href="#l1706">1706</a><td> var self = this;<tr><td><a name="l1707" href="#l1707">1707</a><td> self.shutdownId_ = self.timer.setTimeout(function() {<tr><td><a name="l1708" href="#l1708">1708</a><td> self.shutdownId_ = null;<tr><td><a name="l1709" href="#l1709">1709</a><td> self.emit(webdriver.promise.ControlFlow.EventType.IDLE);<tr><td><a name="l1710" href="#l1710">1710</a><td> }, 0);<tr><td><a name="l1711" href="#l1711">1711</a><td> }<tr><td><a name="l1712" href="#l1712">1712</a><td>};<tr><td><a name="l1713" href="#l1713">1713</a><td><tr><td><a name="l1714" href="#l1714">1714</a><td><tr><td><a name="l1715" href="#l1715">1715</a><td>/**<tr><td><a name="l1716" href="#l1716">1716</a><td> * Cancels the shutdown sequence if it is currently scheduled.<tr><td><a name="l1717" href="#l1717">1717</a><td> * @private<tr><td><a name="l1718" href="#l1718">1718</a><td> */<tr><td><a name="l1719" href="#l1719">1719</a><td>webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() {<tr><td><a name="l1720" href="#l1720">1720</a><td> if (this.shutdownId_) {<tr><td><a name="l1721" href="#l1721">1721</a><td> this.timer.clearTimeout(this.shutdownId_);<tr><td><a name="l1722" href="#l1722">1722</a><td> this.shutdownId_ = null;<tr><td><a name="l1723" href="#l1723">1723</a><td> }<tr><td><a name="l1724" href="#l1724">1724</a><td>};<tr><td><a name="l1725" href="#l1725">1725</a><td><tr><td><a name="l1726" href="#l1726">1726</a><td><tr><td><a name="l1727" href="#l1727">1727</a><td>/**<tr><td><a name="l1728" href="#l1728">1728</a><td> * Aborts this flow, abandoning all remaining tasks. If there are<tr><td><a name="l1729" href="#l1729">1729</a><td> * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the<tr><td><a name="l1730" href="#l1730">1730</a><td> * offending {@code error}, otherwise, the {@code error} will be rethrown to the<tr><td><a name="l1731" href="#l1731">1731</a><td> * global error handler.<tr><td><a name="l1732" href="#l1732">1732</a><td> * @param {*} error Object describing the error that caused the flow to<tr><td><a name="l1733" href="#l1733">1733</a><td> * abort; usually either an Error or string value.<tr><td><a name="l1734" href="#l1734">1734</a><td> * @private<tr><td><a name="l1735" href="#l1735">1735</a><td> */<tr><td><a name="l1736" href="#l1736">1736</a><td>webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) {<tr><td><a name="l1737" href="#l1737">1737</a><td> this.activeFrame_ = null;<tr><td><a name="l1738" href="#l1738">1738</a><td> this.cancelShutdown_();<tr><td><a name="l1739" href="#l1739">1739</a><td> this.cancelEventLoop_();<tr><td><a name="l1740" href="#l1740">1740</a><td><tr><td><a name="l1741" href="#l1741">1741</a><td> var listeners = this.listeners(<tr><td><a name="l1742" href="#l1742">1742</a><td> webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);<tr><td><a name="l1743" href="#l1743">1743</a><td> if (!listeners.length) {<tr><td><a name="l1744" href="#l1744">1744</a><td> this.timer.setTimeout(function() {<tr><td><a name="l1745" href="#l1745">1745</a><td> throw error;<tr><td><a name="l1746" href="#l1746">1746</a><td> }, 0);<tr><td><a name="l1747" href="#l1747">1747</a><td> } else {<tr><td><a name="l1748" href="#l1748">1748</a><td> this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,<tr><td><a name="l1749" href="#l1749">1749</a><td> error);<tr><td><a name="l1750" href="#l1750">1750</a><td> }<tr><td><a name="l1751" href="#l1751">1751</a><td>};<tr><td><a name="l1752" href="#l1752">1752</a><td><tr><td><a name="l1753" href="#l1753">1753</a><td><tr><td><a name="l1754" href="#l1754">1754</a><td><tr><td><a name="l1755" href="#l1755">1755</a><td>/**<tr><td><a name="l1756" href="#l1756">1756</a><td> * A single node in an {@link webdriver.promise.ControlFlow}'s task tree.<tr><td><a name="l1757" href="#l1757">1757</a><td> * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs<tr><td><a name="l1758" href="#l1758">1758</a><td> * to.<tr><td><a name="l1759" href="#l1759">1759</a><td> * @constructor<tr><td><a name="l1760" href="#l1760">1760</a><td> * @extends {webdriver.promise.Deferred}<tr><td><a name="l1761" href="#l1761">1761</a><td> * @private<tr><td><a name="l1762" href="#l1762">1762</a><td> */<tr><td><a name="l1763" href="#l1763">1763</a><td>webdriver.promise.Node_ = function(flow) {<tr><td><a name="l1764" href="#l1764">1764</a><td> webdriver.promise.Deferred.call(this, null, flow);<tr><td><a name="l1765" href="#l1765">1765</a><td>};<tr><td><a name="l1766" href="#l1766">1766</a><td>goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred);<tr><td><a name="l1767" href="#l1767">1767</a><td><tr><td><a name="l1768" href="#l1768">1768</a><td><tr><td><a name="l1769" href="#l1769">1769</a><td>/**<tr><td><a name="l1770" href="#l1770">1770</a><td> * This node's parent.<tr><td><a name="l1771" href="#l1771">1771</a><td> * @private {webdriver.promise.Node_}<tr><td><a name="l1772" href="#l1772">1772</a><td> */<tr><td><a name="l1773" href="#l1773">1773</a><td>webdriver.promise.Node_.prototype.parent_ = null;<tr><td><a name="l1774" href="#l1774">1774</a><td><tr><td><a name="l1775" href="#l1775">1775</a><td><tr><td><a name="l1776" href="#l1776">1776</a><td>/** @return {webdriver.promise.Node_} This node's parent. */<tr><td><a name="l1777" href="#l1777">1777</a><td>webdriver.promise.Node_.prototype.getParent = function() {<tr><td><a name="l1778" href="#l1778">1778</a><td> return this.parent_;<tr><td><a name="l1779" href="#l1779">1779</a><td>};<tr><td><a name="l1780" href="#l1780">1780</a><td><tr><td><a name="l1781" href="#l1781">1781</a><td><tr><td><a name="l1782" href="#l1782">1782</a><td>/**<tr><td><a name="l1783" href="#l1783">1783</a><td> * @param {webdriver.promise.Node_} parent This node's new parent.<tr><td><a name="l1784" href="#l1784">1784</a><td> */<tr><td><a name="l1785" href="#l1785">1785</a><td>webdriver.promise.Node_.prototype.setParent = function(parent) {<tr><td><a name="l1786" href="#l1786">1786</a><td> this.parent_ = parent;<tr><td><a name="l1787" href="#l1787">1787</a><td>};<tr><td><a name="l1788" href="#l1788">1788</a><td><tr><td><a name="l1789" href="#l1789">1789</a><td><tr><td><a name="l1790" href="#l1790">1790</a><td>/**<tr><td><a name="l1791" href="#l1791">1791</a><td> * @return {!webdriver.promise.Node_} The root of this node's tree.<tr><td><a name="l1792" href="#l1792">1792</a><td> */<tr><td><a name="l1793" href="#l1793">1793</a><td>webdriver.promise.Node_.prototype.getRoot = function() {<tr><td><a name="l1794" href="#l1794">1794</a><td> var root = this;<tr><td><a name="l1795" href="#l1795">1795</a><td> while (root.parent_) {<tr><td><a name="l1796" href="#l1796">1796</a><td> root = root.parent_;<tr><td><a name="l1797" href="#l1797">1797</a><td> }<tr><td><a name="l1798" href="#l1798">1798</a><td> return root;<tr><td><a name="l1799" href="#l1799">1799</a><td>};<tr><td><a name="l1800" href="#l1800">1800</a><td><tr><td><a name="l1801" href="#l1801">1801</a><td><tr><td><a name="l1802" href="#l1802">1802</a><td><tr><td><a name="l1803" href="#l1803">1803</a><td>/**<tr><td><a name="l1804" href="#l1804">1804</a><td> * An execution frame within a {@link webdriver.promise.ControlFlow}. Each<tr><td><a name="l1805" href="#l1805">1805</a><td> * frame represents the execution context for either a<tr><td><a name="l1806" href="#l1806">1806</a><td> * {@link webdriver.promise.Task_} or a callback on a<tr><td><a name="l1807" href="#l1807">1807</a><td> * {@link webdriver.promise.Deferred}.<tr><td><a name="l1808" href="#l1808">1808</a><td> *<tr><td><a name="l1809" href="#l1809">1809</a><td> * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the<tr><td><a name="l1810" href="#l1810">1810</a><td> * items queued within it are given priority over child N+1.<tr><td><a name="l1811" href="#l1811">1811</a><td> *<tr><td><a name="l1812" href="#l1812">1812</a><td> * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs<tr><td><a name="l1813" href="#l1813">1813</a><td> * to.<tr><td><a name="l1814" href="#l1814">1814</a><td> * @constructor<tr><td><a name="l1815" href="#l1815">1815</a><td> * @extends {webdriver.promise.Node_}<tr><td><a name="l1816" href="#l1816">1816</a><td> * @private<tr><td><a name="l1817" href="#l1817">1817</a><td> */<tr><td><a name="l1818" href="#l1818">1818</a><td>webdriver.promise.Frame_ = function(flow) {<tr><td><a name="l1819" href="#l1819">1819</a><td> webdriver.promise.Node_.call(this, flow);<tr><td><a name="l1820" href="#l1820">1820</a><td><tr><td><a name="l1821" href="#l1821">1821</a><td> var reject = goog.bind(this.reject, this);<tr><td><a name="l1822" href="#l1822">1822</a><td> var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this);<tr><td><a name="l1823" href="#l1823">1823</a><td><tr><td><a name="l1824" href="#l1824">1824</a><td> /** @override */<tr><td><a name="l1825" href="#l1825">1825</a><td> this.reject = function(e) {<tr><td><a name="l1826" href="#l1826">1826</a><td> cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e));<tr><td><a name="l1827" href="#l1827">1827</a><td> reject(e);<tr><td><a name="l1828" href="#l1828">1828</a><td> };<tr><td><a name="l1829" href="#l1829">1829</a><td><tr><td><a name="l1830" href="#l1830">1830</a><td> /**<tr><td><a name="l1831" href="#l1831">1831</a><td> * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}<tr><td><a name="l1832" href="#l1832">1832</a><td> */<tr><td><a name="l1833" href="#l1833">1833</a><td> this.children_ = [];<tr><td><a name="l1834" href="#l1834">1834</a><td>};<tr><td><a name="l1835" href="#l1835">1835</a><td>goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_);<tr><td><a name="l1836" href="#l1836">1836</a><td><tr><td><a name="l1837" href="#l1837">1837</a><td><tr><td><a name="l1838" href="#l1838">1838</a><td>/**<tr><td><a name="l1839" href="#l1839">1839</a><td> * The task currently being executed within this frame.<tr><td><a name="l1840" href="#l1840">1840</a><td> * @private {webdriver.promise.Task_}<tr><td><a name="l1841" href="#l1841">1841</a><td> */<tr><td><a name="l1842" href="#l1842">1842</a><td>webdriver.promise.Frame_.prototype.pendingTask_ = null;<tr><td><a name="l1843" href="#l1843">1843</a><td><tr><td><a name="l1844" href="#l1844">1844</a><td><tr><td><a name="l1845" href="#l1845">1845</a><td>/**<tr><td><a name="l1846" href="#l1846">1846</a><td> * Whether this frame is active. A frame is considered active once one of its<tr><td><a name="l1847" href="#l1847">1847</a><td> * descendants has been removed for execution.<tr><td><a name="l1848" href="#l1848">1848</a><td> *<tr><td><a name="l1849" href="#l1849">1849</a><td> * Adding a sub-frame as a child to an active frame is an indication that<tr><td><a name="l1850" href="#l1850">1850</a><td> * a callback to a {@link webdriver.promise.Deferred} is being invoked and any<tr><td><a name="l1851" href="#l1851">1851</a><td> * tasks scheduled within it should have priority over previously scheduled<tr><td><a name="l1852" href="#l1852">1852</a><td> * tasks:<tr><td><a name="l1853" href="#l1853">1853</a><td> * <code><pre><tr><td><a name="l1854" href="#l1854">1854</a><td> * var flow = webdriver.promise.controlFlow();<tr><td><a name="l1855" href="#l1855">1855</a><td> * flow.execute('start here', goog.nullFunction).then(function() {<tr><td><a name="l1856" href="#l1856">1856</a><td> * flow.execute('this should execute 2nd', goog.nullFunction);<tr><td><a name="l1857" href="#l1857">1857</a><td> * });<tr><td><a name="l1858" href="#l1858">1858</a><td> * flow.execute('this should execute last', goog.nullFunction);<tr><td><a name="l1859" href="#l1859">1859</a><td> * </pre></code><tr><td><a name="l1860" href="#l1860">1860</a><td> *<tr><td><a name="l1861" href="#l1861">1861</a><td> * @private {boolean}<tr><td><a name="l1862" href="#l1862">1862</a><td> */<tr><td><a name="l1863" href="#l1863">1863</a><td>webdriver.promise.Frame_.prototype.isActive_ = false;<tr><td><a name="l1864" href="#l1864">1864</a><td><tr><td><a name="l1865" href="#l1865">1865</a><td><tr><td><a name="l1866" href="#l1866">1866</a><td>/**<tr><td><a name="l1867" href="#l1867">1867</a><td> * Whether this frame is currently locked. A locked frame represents a callback<tr><td><a name="l1868" href="#l1868">1868</a><td> * or task function which has run to completion and scheduled all of its tasks.<tr><td><a name="l1869" href="#l1869">1869</a><td> *<tr><td><a name="l1870" href="#l1870">1870</a><td> * <p>Once a frame becomes {@link #isActive_ active}, any new frames which are<tr><td><a name="l1871" href="#l1871">1871</a><td> * added represent callbacks on a {@link webdriver.promise.Deferred}, whose<tr><td><a name="l1872" href="#l1872">1872</a><td> * tasks must be given priority over previously scheduled tasks.<tr><td><a name="l1873" href="#l1873">1873</a><td> *<tr><td><a name="l1874" href="#l1874">1874</a><td> * @private {boolean}<tr><td><a name="l1875" href="#l1875">1875</a><td> */<tr><td><a name="l1876" href="#l1876">1876</a><td>webdriver.promise.Frame_.prototype.isLocked_ = false;<tr><td><a name="l1877" href="#l1877">1877</a><td><tr><td><a name="l1878" href="#l1878">1878</a><td><tr><td><a name="l1879" href="#l1879">1879</a><td>/**<tr><td><a name="l1880" href="#l1880">1880</a><td> * A reference to the last node inserted in this frame.<tr><td><a name="l1881" href="#l1881">1881</a><td> * @private {webdriver.promise.Node_}<tr><td><a name="l1882" href="#l1882">1882</a><td> */<tr><td><a name="l1883" href="#l1883">1883</a><td>webdriver.promise.Frame_.prototype.lastInsertedChild_ = null;<tr><td><a name="l1884" href="#l1884">1884</a><td><tr><td><a name="l1885" href="#l1885">1885</a><td><tr><td><a name="l1886" href="#l1886">1886</a><td>/**<tr><td><a name="l1887" href="#l1887">1887</a><td> * Marks all of the tasks that are descendants of this frame in the execution<tr><td><a name="l1888" href="#l1888">1888</a><td> * tree as cancelled. This is necessary for callbacks scheduled asynchronous.<tr><td><a name="l1889" href="#l1889">1889</a><td> * For example:<tr><td><a name="l1890" href="#l1890">1890</a><td> *<tr><td><a name="l1891" href="#l1891">1891</a><td> * var someResult;<tr><td><a name="l1892" href="#l1892">1892</a><td> * webdriver.promise.createFlow(function(flow) {<tr><td><a name="l1893" href="#l1893">1893</a><td> * someResult = flow.execute(function() {});<tr><td><a name="l1894" href="#l1894">1894</a><td> * throw Error();<tr><td><a name="l1895" href="#l1895">1895</a><td> * }).addErrback(function(err) {<tr><td><a name="l1896" href="#l1896">1896</a><td> * console.log('flow failed: ' + err);<tr><td><a name="l1897" href="#l1897">1897</a><td> * someResult.then(function() {<tr><td><a name="l1898" href="#l1898">1898</a><td> * console.log('task succeeded!');<tr><td><a name="l1899" href="#l1899">1899</a><td> * }, function(err) {<tr><td><a name="l1900" href="#l1900">1900</a><td> * console.log('task failed! ' + err);<tr><td><a name="l1901" href="#l1901">1901</a><td> * });<tr><td><a name="l1902" href="#l1902">1902</a><td> * });<tr><td><a name="l1903" href="#l1903">1903</a><td> * // flow failed: Error: boom<tr><td><a name="l1904" href="#l1904">1904</a><td> * // task failed! CanceledTaskError: Task discarded due to a previous<tr><td><a name="l1905" href="#l1905">1905</a><td> * // task failure: Error: boom<tr><td><a name="l1906" href="#l1906">1906</a><td> *<tr><td><a name="l1907" href="#l1907">1907</a><td> * @param {!webdriver.promise.CanceledTaskError_} error The cancellation<tr><td><a name="l1908" href="#l1908">1908</a><td> * error.<tr><td><a name="l1909" href="#l1909">1909</a><td> */<tr><td><a name="l1910" href="#l1910">1910</a><td>webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) {<tr><td><a name="l1911" href="#l1911">1911</a><td> goog.array.forEach(this.children_, function(child) {<tr><td><a name="l1912" href="#l1912">1912</a><td> if (child instanceof webdriver.promise.Frame_) {<tr><td><a name="l1913" href="#l1913">1913</a><td> child.cancelRemainingTasks(error);<tr><td><a name="l1914" href="#l1914">1914</a><td> } else {<tr><td><a name="l1915" href="#l1915">1915</a><td> // None of the previously registered listeners should be notified that<tr><td><a name="l1916" href="#l1916">1916</a><td> // the task is being canceled, however, we need at least one errback<tr><td><a name="l1917" href="#l1917">1917</a><td> // to prevent the cancellation from bubbling up.<tr><td><a name="l1918" href="#l1918">1918</a><td> child.removeAll();<tr><td><a name="l1919" href="#l1919">1919</a><td> child.thenCatch(goog.nullFunction);<tr><td><a name="l1920" href="#l1920">1920</a><td> child.cancel(error);<tr><td><a name="l1921" href="#l1921">1921</a><td> }<tr><td><a name="l1922" href="#l1922">1922</a><td> });<tr><td><a name="l1923" href="#l1923">1923</a><td>};<tr><td><a name="l1924" href="#l1924">1924</a><td><tr><td><a name="l1925" href="#l1925">1925</a><td><tr><td><a name="l1926" href="#l1926">1926</a><td>/**<tr><td><a name="l1927" href="#l1927">1927</a><td> * @return {webdriver.promise.Task_} The task currently executing<tr><td><a name="l1928" href="#l1928">1928</a><td> * within this frame, if any.<tr><td><a name="l1929" href="#l1929">1929</a><td> */<tr><td><a name="l1930" href="#l1930">1930</a><td>webdriver.promise.Frame_.prototype.getPendingTask = function() {<tr><td><a name="l1931" href="#l1931">1931</a><td> return this.pendingTask_;<tr><td><a name="l1932" href="#l1932">1932</a><td>};<tr><td><a name="l1933" href="#l1933">1933</a><td><tr><td><a name="l1934" href="#l1934">1934</a><td><tr><td><a name="l1935" href="#l1935">1935</a><td>/**<tr><td><a name="l1936" href="#l1936">1936</a><td> * @param {webdriver.promise.Task_} task The task currently<tr><td><a name="l1937" href="#l1937">1937</a><td> * executing within this frame, if any.<tr><td><a name="l1938" href="#l1938">1938</a><td> */<tr><td><a name="l1939" href="#l1939">1939</a><td>webdriver.promise.Frame_.prototype.setPendingTask = function(task) {<tr><td><a name="l1940" href="#l1940">1940</a><td> this.pendingTask_ = task;<tr><td><a name="l1941" href="#l1941">1941</a><td>};<tr><td><a name="l1942" href="#l1942">1942</a><td><tr><td><a name="l1943" href="#l1943">1943</a><td><tr><td><a name="l1944" href="#l1944">1944</a><td>/** Locks this frame. */<tr><td><a name="l1945" href="#l1945">1945</a><td>webdriver.promise.Frame_.prototype.lockFrame = function() {<tr><td><a name="l1946" href="#l1946">1946</a><td> this.isLocked_ = true;<tr><td><a name="l1947" href="#l1947">1947</a><td>};<tr><td><a name="l1948" href="#l1948">1948</a><td><tr><td><a name="l1949" href="#l1949">1949</a><td><tr><td><a name="l1950" href="#l1950">1950</a><td>/**<tr><td><a name="l1951" href="#l1951">1951</a><td> * Adds a new node to this frame.<tr><td><a name="l1952" href="#l1952">1952</a><td> * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node<tr><td><a name="l1953" href="#l1953">1953</a><td> * The node to insert.<tr><td><a name="l1954" href="#l1954">1954</a><td> */<tr><td><a name="l1955" href="#l1955">1955</a><td>webdriver.promise.Frame_.prototype.addChild = function(node) {<tr><td><a name="l1956" href="#l1956">1956</a><td> if (this.lastInsertedChild_ &&<tr><td><a name="l1957" href="#l1957">1957</a><td> this.lastInsertedChild_ instanceof webdriver.promise.Frame_ &&<tr><td><a name="l1958" href="#l1958">1958</a><td> !this.lastInsertedChild_.isLocked_) {<tr><td><a name="l1959" href="#l1959">1959</a><td> this.lastInsertedChild_.addChild(node);<tr><td><a name="l1960" href="#l1960">1960</a><td> return;<tr><td><a name="l1961" href="#l1961">1961</a><td> }<tr><td><a name="l1962" href="#l1962">1962</a><td><tr><td><a name="l1963" href="#l1963">1963</a><td> node.setParent(this);<tr><td><a name="l1964" href="#l1964">1964</a><td><tr><td><a name="l1965" href="#l1965">1965</a><td> if (this.isActive_ && node instanceof webdriver.promise.Frame_) {<tr><td><a name="l1966" href="#l1966">1966</a><td> var index = 0;<tr><td><a name="l1967" href="#l1967">1967</a><td> if (this.lastInsertedChild_ instanceof<tr><td><a name="l1968" href="#l1968">1968</a><td> webdriver.promise.Frame_) {<tr><td><a name="l1969" href="#l1969">1969</a><td> index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1;<tr><td><a name="l1970" href="#l1970">1970</a><td> }<tr><td><a name="l1971" href="#l1971">1971</a><td> goog.array.insertAt(this.children_, node, index);<tr><td><a name="l1972" href="#l1972">1972</a><td> this.lastInsertedChild_ = node;<tr><td><a name="l1973" href="#l1973">1973</a><td> return;<tr><td><a name="l1974" href="#l1974">1974</a><td> }<tr><td><a name="l1975" href="#l1975">1975</a><td><tr><td><a name="l1976" href="#l1976">1976</a><td> this.lastInsertedChild_ = node;<tr><td><a name="l1977" href="#l1977">1977</a><td> this.children_.push(node);<tr><td><a name="l1978" href="#l1978">1978</a><td>};<tr><td><a name="l1979" href="#l1979">1979</a><td><tr><td><a name="l1980" href="#l1980">1980</a><td><tr><td><a name="l1981" href="#l1981">1981</a><td>/**<tr><td><a name="l1982" href="#l1982">1982</a><td> * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's<tr><td><a name="l1983" href="#l1983">1983</a><td> * fist child.<tr><td><a name="l1984" href="#l1984">1984</a><td> */<tr><td><a name="l1985" href="#l1985">1985</a><td>webdriver.promise.Frame_.prototype.getFirstChild = function() {<tr><td><a name="l1986" href="#l1986">1986</a><td> this.isActive_ = true;<tr><td><a name="l1987" href="#l1987">1987</a><td> this.lastInsertedChild_ = null;<tr><td><a name="l1988" href="#l1988">1988</a><td> return this.children_[0];<tr><td><a name="l1989" href="#l1989">1989</a><td>};<tr><td><a name="l1990" href="#l1990">1990</a><td><tr><td><a name="l1991" href="#l1991">1991</a><td><tr><td><a name="l1992" href="#l1992">1992</a><td>/**<tr><td><a name="l1993" href="#l1993">1993</a><td> * Removes a child from this frame.<tr><td><a name="l1994" href="#l1994">1994</a><td> * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child<tr><td><a name="l1995" href="#l1995">1995</a><td> * The child to remove.<tr><td><a name="l1996" href="#l1996">1996</a><td> */<tr><td><a name="l1997" href="#l1997">1997</a><td>webdriver.promise.Frame_.prototype.removeChild = function(child) {<tr><td><a name="l1998" href="#l1998">1998</a><td> var index = goog.array.indexOf(this.children_, child);<tr><td><a name="l1999" href="#l1999">1999</a><td> child.setParent(null);<tr><td><a name="l2000" href="#l2000">2000</a><td> goog.array.removeAt(this.children_, index);<tr><td><a name="l2001" href="#l2001">2001</a><td> if (this.lastInsertedChild_ === child) {<tr><td><a name="l2002" href="#l2002">2002</a><td> this.lastInsertedChild_ = null;<tr><td><a name="l2003" href="#l2003">2003</a><td> }<tr><td><a name="l2004" href="#l2004">2004</a><td>};<tr><td><a name="l2005" href="#l2005">2005</a><td><tr><td><a name="l2006" href="#l2006">2006</a><td><tr><td><a name="l2007" href="#l2007">2007</a><td>/** @override */<tr><td><a name="l2008" href="#l2008">2008</a><td>webdriver.promise.Frame_.prototype.toString = function() {<tr><td><a name="l2009" href="#l2009">2009</a><td> return '[' + goog.array.map(this.children_, function(child) {<tr><td><a name="l2010" href="#l2010">2010</a><td> return child.toString();<tr><td><a name="l2011" href="#l2011">2011</a><td> }).join(', ') + ']';<tr><td><a name="l2012" href="#l2012">2012</a><td>};<tr><td><a name="l2013" href="#l2013">2013</a><td><tr><td><a name="l2014" href="#l2014">2014</a><td><tr><td><a name="l2015" href="#l2015">2015</a><td><tr><td><a name="l2016" href="#l2016">2016</a><td>/**<tr><td><a name="l2017" href="#l2017">2017</a><td> * A task to be executed by a {@link webdriver.promise.ControlFlow}.<tr><td><a name="l2018" href="#l2018">2018</a><td> *<tr><td><a name="l2019" href="#l2019">2019</a><td> * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs<tr><td><a name="l2020" href="#l2020">2020</a><td> * to.<tr><td><a name="l2021" href="#l2021">2021</a><td> * @param {!Function} fn The function to call when the task executes. If it<tr><td><a name="l2022" href="#l2022">2022</a><td> * returns a {@code webdriver.promise.Promise}, the flow will wait<tr><td><a name="l2023" href="#l2023">2023</a><td> * for it to be resolved before starting the next task.<tr><td><a name="l2024" href="#l2024">2024</a><td> * @param {string} description A description of the task for debugging.<tr><td><a name="l2025" href="#l2025">2025</a><td> * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack<tr><td><a name="l2026" href="#l2026">2026</a><td> * when this task was scheduled.<tr><td><a name="l2027" href="#l2027">2027</a><td> * @constructor<tr><td><a name="l2028" href="#l2028">2028</a><td> * @extends {webdriver.promise.Node_}<tr><td><a name="l2029" href="#l2029">2029</a><td> * @private<tr><td><a name="l2030" href="#l2030">2030</a><td> */<tr><td><a name="l2031" href="#l2031">2031</a><td>webdriver.promise.Task_ = function(flow, fn, description, snapshot) {<tr><td><a name="l2032" href="#l2032">2032</a><td> webdriver.promise.Node_.call(this, flow);<tr><td><a name="l2033" href="#l2033">2033</a><td><tr><td><a name="l2034" href="#l2034">2034</a><td> /**<tr><td><a name="l2035" href="#l2035">2035</a><td> * Executes this task.<tr><td><a name="l2036" href="#l2036">2036</a><td> * @type {!Function}<tr><td><a name="l2037" href="#l2037">2037</a><td> */<tr><td><a name="l2038" href="#l2038">2038</a><td> this.execute = fn;<tr><td><a name="l2039" href="#l2039">2039</a><td><tr><td><a name="l2040" href="#l2040">2040</a><td> /** @private {string} */<tr><td><a name="l2041" href="#l2041">2041</a><td> this.description_ = description;<tr><td><a name="l2042" href="#l2042">2042</a><td><tr><td><a name="l2043" href="#l2043">2043</a><td> /** @private {!webdriver.stacktrace.Snapshot} */<tr><td><a name="l2044" href="#l2044">2044</a><td> this.snapshot_ = snapshot;<tr><td><a name="l2045" href="#l2045">2045</a><td>};<tr><td><a name="l2046" href="#l2046">2046</a><td>goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_);<tr><td><a name="l2047" href="#l2047">2047</a><td><tr><td><a name="l2048" href="#l2048">2048</a><td><tr><td><a name="l2049" href="#l2049">2049</a><td>/** @return {string} This task's description. */<tr><td><a name="l2050" href="#l2050">2050</a><td>webdriver.promise.Task_.prototype.getDescription = function() {<tr><td><a name="l2051" href="#l2051">2051</a><td> return this.description_;<tr><td><a name="l2052" href="#l2052">2052</a><td>};<tr><td><a name="l2053" href="#l2053">2053</a><td><tr><td><a name="l2054" href="#l2054">2054</a><td><tr><td><a name="l2055" href="#l2055">2055</a><td>/** @override */<tr><td><a name="l2056" href="#l2056">2056</a><td>webdriver.promise.Task_.prototype.toString = function() {<tr><td><a name="l2057" href="#l2057">2057</a><td> var stack = this.snapshot_.getStacktrace();<tr><td><a name="l2058" href="#l2058">2058</a><td> var ret = this.description_;<tr><td><a name="l2059" href="#l2059">2059</a><td> if (stack.length) {<tr><td><a name="l2060" href="#l2060">2060</a><td> if (this.description_) {<tr><td><a name="l2061" href="#l2061">2061</a><td> ret += '\n';<tr><td><a name="l2062" href="#l2062">2062</a><td> }<tr><td><a name="l2063" href="#l2063">2063</a><td> ret += stack.join('\n');<tr><td><a name="l2064" href="#l2064">2064</a><td> }<tr><td><a name="l2065" href="#l2065">2065</a><td> return ret;<tr><td><a name="l2066" href="#l2066">2066</a><td>};<tr><td><a name="l2067" href="#l2067">2067</a><td><tr><td><a name="l2068" href="#l2068">2068</a><td><tr><td><a name="l2069" href="#l2069">2069</a><td><tr><td><a name="l2070" href="#l2070">2070</a><td>/**<tr><td><a name="l2071" href="#l2071">2071</a><td> * Special error used to signal when a task is canceled because a previous<tr><td><a name="l2072" href="#l2072">2072</a><td> * task in the same frame failed.<tr><td><a name="l2073" href="#l2073">2073</a><td> * @param {*} err The error that caused the task cancellation.<tr><td><a name="l2074" href="#l2074">2074</a><td> * @constructor<tr><td><a name="l2075" href="#l2075">2075</a><td> * @extends {goog.debug.Error}<tr><td><a name="l2076" href="#l2076">2076</a><td> * @private<tr><td><a name="l2077" href="#l2077">2077</a><td> */<tr><td><a name="l2078" href="#l2078">2078</a><td>webdriver.promise.CanceledTaskError_ = function(err) {<tr><td><a name="l2079" href="#l2079">2079</a><td> goog.base(this, 'Task discarded due to a previous task failure: ' + err);<tr><td><a name="l2080" href="#l2080">2080</a><td>};<tr><td><a name="l2081" href="#l2081">2081</a><td>goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error);<tr><td><a name="l2082" href="#l2082">2082</a><td><tr><td><a name="l2083" href="#l2083">2083</a><td><tr><td><a name="l2084" href="#l2084">2084</a><td>/** @override */<tr><td><a name="l2085" href="#l2085">2085</a><td>webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError';<tr><td><a name="l2086" href="#l2086">2086</a><td><tr><td><a name="l2087" href="#l2087">2087</a><td><tr><td><a name="l2088" href="#l2088">2088</a><td><tr><td><a name="l2089" href="#l2089">2089</a><td>/**<tr><td><a name="l2090" href="#l2090">2090</a><td> * The default flow to use if no others are active.<tr><td><a name="l2091" href="#l2091">2091</a><td> * @private {!webdriver.promise.ControlFlow}<tr><td><a name="l2092" href="#l2092">2092</a><td> */<tr><td><a name="l2093" href="#l2093">2093</a><td>webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow();<tr><td><a name="l2094" href="#l2094">2094</a><td><tr><td><a name="l2095" href="#l2095">2095</a><td><tr><td><a name="l2096" href="#l2096">2096</a><td>/**<tr><td><a name="l2097" href="#l2097">2097</a><td> * A stack of active control flows, with the top of the stack used to schedule<tr><td><a name="l2098" href="#l2098">2098</a><td> * commands. When there are multiple flows on the stack, the flow at index N<tr><td><a name="l2099" href="#l2099">2099</a><td> * represents a callback triggered within a task owned by the flow at index<tr><td><a name="l2100" href="#l2100">2100</a><td> * N-1.<tr><td><a name="l2101" href="#l2101">2101</a><td> * @private {!Array.<!webdriver.promise.ControlFlow>}<tr><td><a name="l2102" href="#l2102">2102</a><td> */<tr><td><a name="l2103" href="#l2103">2103</a><td>webdriver.promise.activeFlows_ = [];<tr><td><a name="l2104" href="#l2104">2104</a><td><tr><td><a name="l2105" href="#l2105">2105</a><td><tr><td><a name="l2106" href="#l2106">2106</a><td>/**<tr><td><a name="l2107" href="#l2107">2107</a><td> * Changes the default flow to use when no others are active.<tr><td><a name="l2108" href="#l2108">2108</a><td> * @param {!webdriver.promise.ControlFlow} flow The new default flow.<tr><td><a name="l2109" href="#l2109">2109</a><td> * @throws {Error} If the default flow is not currently active.<tr><td><a name="l2110" href="#l2110">2110</a><td> */<tr><td><a name="l2111" href="#l2111">2111</a><td>webdriver.promise.setDefaultFlow = function(flow) {<tr><td><a name="l2112" href="#l2112">2112</a><td> if (webdriver.promise.activeFlows_.length) {<tr><td><a name="l2113" href="#l2113">2113</a><td> throw Error('You may only change the default flow while it is active');<tr><td><a name="l2114" href="#l2114">2114</a><td> }<tr><td><a name="l2115" href="#l2115">2115</a><td> webdriver.promise.defaultFlow_ = flow;<tr><td><a name="l2116" href="#l2116">2116</a><td>};<tr><td><a name="l2117" href="#l2117">2117</a><td><tr><td><a name="l2118" href="#l2118">2118</a><td><tr><td><a name="l2119" href="#l2119">2119</a><td>/**<tr><td><a name="l2120" href="#l2120">2120</a><td> * @return {!webdriver.promise.ControlFlow} The currently active control flow.<tr><td><a name="l2121" href="#l2121">2121</a><td> */<tr><td><a name="l2122" href="#l2122">2122</a><td>webdriver.promise.controlFlow = function() {<tr><td><a name="l2123" href="#l2123">2123</a><td> return /** @type {!webdriver.promise.ControlFlow} */ (<tr><td><a name="l2124" href="#l2124">2124</a><td> goog.array.peek(webdriver.promise.activeFlows_) ||<tr><td><a name="l2125" href="#l2125">2125</a><td> webdriver.promise.defaultFlow_);<tr><td><a name="l2126" href="#l2126">2126</a><td>};<tr><td><a name="l2127" href="#l2127">2127</a><td><tr><td><a name="l2128" href="#l2128">2128</a><td><tr><td><a name="l2129" href="#l2129">2129</a><td>/**<tr><td><a name="l2130" href="#l2130">2130</a><td> * @param {!webdriver.promise.ControlFlow} flow The new flow.<tr><td><a name="l2131" href="#l2131">2131</a><td> * @private<tr><td><a name="l2132" href="#l2132">2132</a><td> */<tr><td><a name="l2133" href="#l2133">2133</a><td>webdriver.promise.pushFlow_ = function(flow) {<tr><td><a name="l2134" href="#l2134">2134</a><td> webdriver.promise.activeFlows_.push(flow);<tr><td><a name="l2135" href="#l2135">2135</a><td>};<tr><td><a name="l2136" href="#l2136">2136</a><td><tr><td><a name="l2137" href="#l2137">2137</a><td><tr><td><a name="l2138" href="#l2138">2138</a><td>/** @private */<tr><td><a name="l2139" href="#l2139">2139</a><td>webdriver.promise.popFlow_ = function() {<tr><td><a name="l2140" href="#l2140">2140</a><td> webdriver.promise.activeFlows_.pop();<tr><td><a name="l2141" href="#l2141">2141</a><td>};<tr><td><a name="l2142" href="#l2142">2142</a><td><tr><td><a name="l2143" href="#l2143">2143</a><td><tr><td><a name="l2144" href="#l2144">2144</a><td>/**<tr><td><a name="l2145" href="#l2145">2145</a><td> * Creates a new control flow. The provided callback will be invoked as the<tr><td><a name="l2146" href="#l2146">2146</a><td> * first task within the new flow, with the flow as its sole argument. Returns<tr><td><a name="l2147" href="#l2147">2147</a><td> * a promise that resolves to the callback result.<tr><td><a name="l2148" href="#l2148">2148</a><td> * @param {function(!webdriver.promise.ControlFlow)} callback The entry point<tr><td><a name="l2149" href="#l2149">2149</a><td> * to the newly created flow.<tr><td><a name="l2150" href="#l2150">2150</a><td> * @return {!webdriver.promise.Promise} A promise that resolves to the callback<tr><td><a name="l2151" href="#l2151">2151</a><td> * result.<tr><td><a name="l2152" href="#l2152">2152</a><td> */<tr><td><a name="l2153" href="#l2153">2153</a><td>webdriver.promise.createFlow = function(callback) {<tr><td><a name="l2154" href="#l2154">2154</a><td> var flow = new webdriver.promise.ControlFlow(<tr><td><a name="l2155" href="#l2155">2155</a><td> webdriver.promise.defaultFlow_.timer);<tr><td><a name="l2156" href="#l2156">2156</a><td> return flow.execute(function() {<tr><td><a name="l2157" href="#l2157">2157</a><td> return callback(flow);<tr><td><a name="l2158" href="#l2158">2158</a><td> });<tr><td><a name="l2159" href="#l2159">2159</a><td>};<tr><td><a name="l2160" href="#l2160">2160</a><td><tr><td><a name="l2161" href="#l2161">2161</a><td><tr><td><a name="l2162" href="#l2162">2162</a><td>/**<tr><td><a name="l2163" href="#l2163">2163</a><td> * Tests is a function is a generator.<tr><td><a name="l2164" href="#l2164">2164</a><td> * @param {!Function} fn The function to test.<tr><td><a name="l2165" href="#l2165">2165</a><td> * @return {boolean} Whether the function is a generator.<tr><td><a name="l2166" href="#l2166">2166</a><td> */<tr><td><a name="l2167" href="#l2167">2167</a><td>webdriver.promise.isGenerator = function(fn) {<tr><td><a name="l2168" href="#l2168">2168</a><td> return fn.constructor.name === 'GeneratorFunction';<tr><td><a name="l2169" href="#l2169">2169</a><td>};<tr><td><a name="l2170" href="#l2170">2170</a><td><tr><td><a name="l2171" href="#l2171">2171</a><td><tr><td><a name="l2172" href="#l2172">2172</a><td>/**<tr><td><a name="l2173" href="#l2173">2173</a><td> * Consumes a {@code GeneratorFunction}. Each time the generator yields a<tr><td><a name="l2174" href="#l2174">2174</a><td> * promise, this function will wait for it to be fulfilled before feeding the<tr><td><a name="l2175" href="#l2175">2175</a><td> * fulfilled value back into {@code next}. Likewise, if a yielded promise is<tr><td><a name="l2176" href="#l2176">2176</a><td> * rejected, the rejection error will be passed to {@code throw}.<tr><td><a name="l2177" href="#l2177">2177</a><td> *<tr><td><a name="l2178" href="#l2178">2178</a><td> * <p>Example 1: the Fibonacci Sequence.<tr><td><a name="l2179" href="#l2179">2179</a><td> * <pre><code><tr><td><a name="l2180" href="#l2180">2180</a><td> * webdriver.promise.consume(function* fibonacci() {<tr><td><a name="l2181" href="#l2181">2181</a><td> * var n1 = 1, n2 = 1;<tr><td><a name="l2182" href="#l2182">2182</a><td> * for (var i = 0; i < 4; ++i) {<tr><td><a name="l2183" href="#l2183">2183</a><td> * var tmp = yield n1 + n2;<tr><td><a name="l2184" href="#l2184">2184</a><td> * n1 = n2;<tr><td><a name="l2185" href="#l2185">2185</a><td> * n2 = tmp;<tr><td><a name="l2186" href="#l2186">2186</a><td> * }<tr><td><a name="l2187" href="#l2187">2187</a><td> * return n1 + n2;<tr><td><a name="l2188" href="#l2188">2188</a><td> * }).then(function(result) {<tr><td><a name="l2189" href="#l2189">2189</a><td> * console.log(result); // 13<tr><td><a name="l2190" href="#l2190">2190</a><td> * });<tr><td><a name="l2191" href="#l2191">2191</a><td> * </code></pre><tr><td><a name="l2192" href="#l2192">2192</a><td> *<tr><td><a name="l2193" href="#l2193">2193</a><td> * <p>Example 2: a generator that throws.<tr><td><a name="l2194" href="#l2194">2194</a><td> * <pre><code><tr><td><a name="l2195" href="#l2195">2195</a><td> * webdriver.promise.consume(function* () {<tr><td><a name="l2196" href="#l2196">2196</a><td> * yield webdriver.promise.delayed(250).then(function() {<tr><td><a name="l2197" href="#l2197">2197</a><td> * throw Error('boom');<tr><td><a name="l2198" href="#l2198">2198</a><td> * });<tr><td><a name="l2199" href="#l2199">2199</a><td> * }).thenCatch(function(e) {<tr><td><a name="l2200" href="#l2200">2200</a><td> * console.log(e.toString()); // Error: boom<tr><td><a name="l2201" href="#l2201">2201</a><td> * });<tr><td><a name="l2202" href="#l2202">2202</a><td> * </code></pre><tr><td><a name="l2203" href="#l2203">2203</a><td> *<tr><td><a name="l2204" href="#l2204">2204</a><td> * @param {!Function} generatorFn The generator function to execute.<tr><td><a name="l2205" href="#l2205">2205</a><td> * @param {Object=} opt_self The object to use as "this" when invoking the<tr><td><a name="l2206" href="#l2206">2206</a><td> * initial generator.<tr><td><a name="l2207" href="#l2207">2207</a><td> * @param {...*} var_args Any arguments to pass to the initial generator.<tr><td><a name="l2208" href="#l2208">2208</a><td> * @return {!webdriver.promise.Promise.<?>} A promise that will resolve to the<tr><td><a name="l2209" href="#l2209">2209</a><td> * generator's final result.<tr><td><a name="l2210" href="#l2210">2210</a><td> * @throws {TypeError} If the given function is not a generator.<tr><td><a name="l2211" href="#l2211">2211</a><td> */<tr><td><a name="l2212" href="#l2212">2212</a><td>webdriver.promise.consume = function(generatorFn, opt_self, var_args) {<tr><td><a name="l2213" href="#l2213">2213</a><td> if (!webdriver.promise.isGenerator(generatorFn)) {<tr><td><a name="l2214" href="#l2214">2214</a><td> throw TypeError('Input is not a GeneratorFunction: ' +<tr><td><a name="l2215" href="#l2215">2215</a><td> generatorFn.constructor.name);<tr><td><a name="l2216" href="#l2216">2216</a><td> }<tr><td><a name="l2217" href="#l2217">2217</a><td><tr><td><a name="l2218" href="#l2218">2218</a><td> var deferred = webdriver.promise.defer();<tr><td><a name="l2219" href="#l2219">2219</a><td> var generator = generatorFn.apply(opt_self, goog.array.slice(arguments, 2));<tr><td><a name="l2220" href="#l2220">2220</a><td> callNext();<tr><td><a name="l2221" href="#l2221">2221</a><td> return deferred.promise;<tr><td><a name="l2222" href="#l2222">2222</a><td><tr><td><a name="l2223" href="#l2223">2223</a><td> /** @param {*=} opt_value . */<tr><td><a name="l2224" href="#l2224">2224</a><td> function callNext(opt_value) {<tr><td><a name="l2225" href="#l2225">2225</a><td> pump(generator.next, opt_value);<tr><td><a name="l2226" href="#l2226">2226</a><td> }<tr><td><a name="l2227" href="#l2227">2227</a><td><tr><td><a name="l2228" href="#l2228">2228</a><td> /** @param {*=} opt_error . */<tr><td><a name="l2229" href="#l2229">2229</a><td> function callThrow(opt_error) {<tr><td><a name="l2230" href="#l2230">2230</a><td> // Dictionary lookup required because Closure compiler's built-in<tr><td><a name="l2231" href="#l2231">2231</a><td> // externs does not include GeneratorFunction.prototype.throw.<tr><td><a name="l2232" href="#l2232">2232</a><td> pump(generator['throw'], opt_error);<tr><td><a name="l2233" href="#l2233">2233</a><td> }<tr><td><a name="l2234" href="#l2234">2234</a><td><tr><td><a name="l2235" href="#l2235">2235</a><td> function pump(fn, opt_arg) {<tr><td><a name="l2236" href="#l2236">2236</a><td> if (!deferred.isPending()) {<tr><td><a name="l2237" href="#l2237">2237</a><td> return; // Defererd was cancelled; silently abort.<tr><td><a name="l2238" href="#l2238">2238</a><td> }<tr><td><a name="l2239" href="#l2239">2239</a><td><tr><td><a name="l2240" href="#l2240">2240</a><td> try {<tr><td><a name="l2241" href="#l2241">2241</a><td> var result = fn.call(generator, opt_arg);<tr><td><a name="l2242" href="#l2242">2242</a><td> } catch (ex) {<tr><td><a name="l2243" href="#l2243">2243</a><td> deferred.reject(ex);<tr><td><a name="l2244" href="#l2244">2244</a><td> return;<tr><td><a name="l2245" href="#l2245">2245</a><td> }<tr><td><a name="l2246" href="#l2246">2246</a><td><tr><td><a name="l2247" href="#l2247">2247</a><td> if (result.done) {<tr><td><a name="l2248" href="#l2248">2248</a><td> deferred.fulfill(result.value);<tr><td><a name="l2249" href="#l2249">2249</a><td> return;<tr><td><a name="l2250" href="#l2250">2250</a><td> }<tr><td><a name="l2251" href="#l2251">2251</a><td><tr><td><a name="l2252" href="#l2252">2252</a><td> webdriver.promise.asap(result.value, callNext, callThrow);<tr><td><a name="l2253" href="#l2253">2253</a><td> }<tr><td><a name="l2254" href="#l2254">2254</a><td>};</table></pre></main><nav id="topnav"><div><div id="menubutton"><label for="sidenav-toggle">Menu</label></div><form id="searchbox"><div><input type="search" placeholder="Search" tabindex="1"></div></form></div></nav><nav id="sidenav"><input type="checkbox" id="sidenav-types-ctrl" /><input type="checkbox" id="sidenav-files-ctrl" /><input type="checkbox" id="sidenav-modules-ctrl" /><a id="sidenav-overview"><div><h4>Overview</h4></div></a><div id="sidenav-types"><label for="sidenav-types-ctrl"><h4>Types</h4></label><i>Loading</i></div><div id="sidenav-modules"><label for="sidenav-modules-ctrl"><h4>Modules</h4></label><i>Loading</i></div><div id="sidenav-files"><label for="sidenav-files-ctrl"><h4>Files</h4></label><i>Loading</i></div><a href="license.html"><div><h4>License</h4></div></a></nav><div id="push-footer"></div></div><footer><a href="https://github.com/jleyba/js-dossier">Generated by dossier</a></footer><script src="../../../types.js"></script><script src="../../../dossier.js"></script>
|
|
1
|
+
<!DOCTYPE html><meta charset="UTF-8"><meta http-equiv="Content-Language" content="en" /><title>promise.js</title><link href="../../../dossier.css" rel="stylesheet" type="text/css"><div id="main-wrapper"><input type="checkbox" id="sidenav-toggle" /><main><header><h1>lib/webdriver/promise.js</h1></header><pre><table class="srcfile"><tbody><tr><td><a name="l1" href="#l1">1</a><td>// Copyright 2011 Software Freedom Conservancy. All Rights Reserved.<tr><td><a name="l2" href="#l2">2</a><td>//<tr><td><a name="l3" href="#l3">3</a><td>// Licensed under the Apache License, Version 2.0 (the "License");<tr><td><a name="l4" href="#l4">4</a><td>// you may not use this file except in compliance with the License.<tr><td><a name="l5" href="#l5">5</a><td>// You may obtain a copy of the License at<tr><td><a name="l6" href="#l6">6</a><td>//<tr><td><a name="l7" href="#l7">7</a><td>// http://www.apache.org/licenses/LICENSE-2.0<tr><td><a name="l8" href="#l8">8</a><td>//<tr><td><a name="l9" href="#l9">9</a><td>// Unless required by applicable law or agreed to in writing, software<tr><td><a name="l10" href="#l10">10</a><td>// distributed under the License is distributed on an "AS IS" BASIS,<tr><td><a name="l11" href="#l11">11</a><td>// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.<tr><td><a name="l12" href="#l12">12</a><td>// See the License for the specific language governing permissions and<tr><td><a name="l13" href="#l13">13</a><td>// limitations under the License.<tr><td><a name="l14" href="#l14">14</a><td><tr><td><a name="l15" href="#l15">15</a><td>/**<tr><td><a name="l16" href="#l16">16</a><td> * @license Portions of this code are from the Dojo toolkit, received under the<tr><td><a name="l17" href="#l17">17</a><td> * BSD License:<tr><td><a name="l18" href="#l18">18</a><td> * Redistribution and use in source and binary forms, with or without<tr><td><a name="l19" href="#l19">19</a><td> * modification, are permitted provided that the following conditions are met:<tr><td><a name="l20" href="#l20">20</a><td> *<tr><td><a name="l21" href="#l21">21</a><td> * * Redistributions of source code must retain the above copyright notice,<tr><td><a name="l22" href="#l22">22</a><td> * this list of conditions and the following disclaimer.<tr><td><a name="l23" href="#l23">23</a><td> * * Redistributions in binary form must reproduce the above copyright notice,<tr><td><a name="l24" href="#l24">24</a><td> * this list of conditions and the following disclaimer in the documentation<tr><td><a name="l25" href="#l25">25</a><td> * and/or other materials provided with the distribution.<tr><td><a name="l26" href="#l26">26</a><td> * * Neither the name of the Dojo Foundation nor the names of its contributors<tr><td><a name="l27" href="#l27">27</a><td> * may be used to endorse or promote products derived from this software<tr><td><a name="l28" href="#l28">28</a><td> * without specific prior written permission.<tr><td><a name="l29" href="#l29">29</a><td> *<tr><td><a name="l30" href="#l30">30</a><td> * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"<tr><td><a name="l31" href="#l31">31</a><td> * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE<tr><td><a name="l32" href="#l32">32</a><td> * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE<tr><td><a name="l33" href="#l33">33</a><td> * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE<tr><td><a name="l34" href="#l34">34</a><td> * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR<tr><td><a name="l35" href="#l35">35</a><td> * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF<tr><td><a name="l36" href="#l36">36</a><td> * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS<tr><td><a name="l37" href="#l37">37</a><td> * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN<tr><td><a name="l38" href="#l38">38</a><td> * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)<tr><td><a name="l39" href="#l39">39</a><td> * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE<tr><td><a name="l40" href="#l40">40</a><td> * POSSIBILITY OF SUCH DAMAGE.<tr><td><a name="l41" href="#l41">41</a><td> */<tr><td><a name="l42" href="#l42">42</a><td><tr><td><a name="l43" href="#l43">43</a><td>/**<tr><td><a name="l44" href="#l44">44</a><td> * @fileoverview A promise implementation based on the CommonJS promise/A and<tr><td><a name="l45" href="#l45">45</a><td> * promise/B proposals. For more information, see<tr><td><a name="l46" href="#l46">46</a><td> * http://wiki.commonjs.org/wiki/Promises.<tr><td><a name="l47" href="#l47">47</a><td> */<tr><td><a name="l48" href="#l48">48</a><td><tr><td><a name="l49" href="#l49">49</a><td>goog.provide('webdriver.promise');<tr><td><a name="l50" href="#l50">50</a><td>goog.provide('webdriver.promise.ControlFlow');<tr><td><a name="l51" href="#l51">51</a><td>goog.provide('webdriver.promise.ControlFlow.Timer');<tr><td><a name="l52" href="#l52">52</a><td>goog.provide('webdriver.promise.Deferred');<tr><td><a name="l53" href="#l53">53</a><td>goog.provide('webdriver.promise.Promise');<tr><td><a name="l54" href="#l54">54</a><td>goog.provide('webdriver.promise.Thenable');<tr><td><a name="l55" href="#l55">55</a><td><tr><td><a name="l56" href="#l56">56</a><td>goog.require('goog.array');<tr><td><a name="l57" href="#l57">57</a><td>goog.require('goog.debug.Error');<tr><td><a name="l58" href="#l58">58</a><td>goog.require('goog.object');<tr><td><a name="l59" href="#l59">59</a><td>goog.require('webdriver.EventEmitter');<tr><td><a name="l60" href="#l60">60</a><td>goog.require('webdriver.stacktrace.Snapshot');<tr><td><a name="l61" href="#l61">61</a><td><tr><td><a name="l62" href="#l62">62</a><td><tr><td><a name="l63" href="#l63">63</a><td><tr><td><a name="l64" href="#l64">64</a><td>/**<tr><td><a name="l65" href="#l65">65</a><td> * Error used when the computation of a promise is cancelled.<tr><td><a name="l66" href="#l66">66</a><td> *<tr><td><a name="l67" href="#l67">67</a><td> * @param {string=} opt_msg The cancellation message.<tr><td><a name="l68" href="#l68">68</a><td> * @constructor<tr><td><a name="l69" href="#l69">69</a><td> * @extends {goog.debug.Error}<tr><td><a name="l70" href="#l70">70</a><td> * @final<tr><td><a name="l71" href="#l71">71</a><td> */<tr><td><a name="l72" href="#l72">72</a><td>webdriver.promise.CancellationError = function(opt_msg) {<tr><td><a name="l73" href="#l73">73</a><td> goog.debug.Error.call(this, opt_msg);<tr><td><a name="l74" href="#l74">74</a><td><tr><td><a name="l75" href="#l75">75</a><td> /** @override */<tr><td><a name="l76" href="#l76">76</a><td> this.name = 'CancellationError';<tr><td><a name="l77" href="#l77">77</a><td>};<tr><td><a name="l78" href="#l78">78</a><td>goog.inherits(webdriver.promise.CancellationError, goog.debug.Error);<tr><td><a name="l79" href="#l79">79</a><td><tr><td><a name="l80" href="#l80">80</a><td><tr><td><a name="l81" href="#l81">81</a><td><tr><td><a name="l82" href="#l82">82</a><td>/**<tr><td><a name="l83" href="#l83">83</a><td> * Thenable is a promise-like object with a {@code then} method which may be<tr><td><a name="l84" href="#l84">84</a><td> * used to schedule callbacks on a promised value.<tr><td><a name="l85" href="#l85">85</a><td> *<tr><td><a name="l86" href="#l86">86</a><td> * @interface<tr><td><a name="l87" href="#l87">87</a><td> * @template T<tr><td><a name="l88" href="#l88">88</a><td> */<tr><td><a name="l89" href="#l89">89</a><td>webdriver.promise.Thenable = function() {};<tr><td><a name="l90" href="#l90">90</a><td><tr><td><a name="l91" href="#l91">91</a><td><tr><td><a name="l92" href="#l92">92</a><td>/**<tr><td><a name="l93" href="#l93">93</a><td> * Cancels the computation of this promise's value, rejecting the promise in the<tr><td><a name="l94" href="#l94">94</a><td> * process. This method is a no-op if the promise has alreayd been resolved.<tr><td><a name="l95" href="#l95">95</a><td> *<tr><td><a name="l96" href="#l96">96</a><td> * @param {string=} opt_reason The reason this promise is being cancelled.<tr><td><a name="l97" href="#l97">97</a><td> */<tr><td><a name="l98" href="#l98">98</a><td>webdriver.promise.Thenable.prototype.cancel = function(opt_reason) {};<tr><td><a name="l99" href="#l99">99</a><td><tr><td><a name="l100" href="#l100">100</a><td><tr><td><a name="l101" href="#l101">101</a><td>/** @return {boolean} Whether this promise's value is still being computed. */<tr><td><a name="l102" href="#l102">102</a><td>webdriver.promise.Thenable.prototype.isPending = function() {};<tr><td><a name="l103" href="#l103">103</a><td><tr><td><a name="l104" href="#l104">104</a><td><tr><td><a name="l105" href="#l105">105</a><td>/**<tr><td><a name="l106" href="#l106">106</a><td> * Registers listeners for when this instance is resolved.<tr><td><a name="l107" href="#l107">107</a><td> *<tr><td><a name="l108" href="#l108">108</a><td> * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback The<tr><td><a name="l109" href="#l109">109</a><td> * function to call if this promise is successfully resolved. The function<tr><td><a name="l110" href="#l110">110</a><td> * should expect a single argument: the promise's resolved value.<tr><td><a name="l111" href="#l111">111</a><td> * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback The<tr><td><a name="l112" href="#l112">112</a><td> * function to call if this promise is rejected. The function should expect<tr><td><a name="l113" href="#l113">113</a><td> * a single argument: the rejection reason.<tr><td><a name="l114" href="#l114">114</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise which will be<tr><td><a name="l115" href="#l115">115</a><td> * resolved with the result of the invoked callback.<tr><td><a name="l116" href="#l116">116</a><td> * @template R<tr><td><a name="l117" href="#l117">117</a><td> */<tr><td><a name="l118" href="#l118">118</a><td>webdriver.promise.Thenable.prototype.then = function(<tr><td><a name="l119" href="#l119">119</a><td> opt_callback, opt_errback) {};<tr><td><a name="l120" href="#l120">120</a><td><tr><td><a name="l121" href="#l121">121</a><td><tr><td><a name="l122" href="#l122">122</a><td>/**<tr><td><a name="l123" href="#l123">123</a><td> * Registers a listener for when this promise is rejected. This is synonymous<tr><td><a name="l124" href="#l124">124</a><td> * with the {@code catch} clause in a synchronous API:<tr><td><a name="l125" href="#l125">125</a><td> * <pre><code><tr><td><a name="l126" href="#l126">126</a><td> * // Synchronous API:<tr><td><a name="l127" href="#l127">127</a><td> * try {<tr><td><a name="l128" href="#l128">128</a><td> * doSynchronousWork();<tr><td><a name="l129" href="#l129">129</a><td> * } catch (ex) {<tr><td><a name="l130" href="#l130">130</a><td> * console.error(ex);<tr><td><a name="l131" href="#l131">131</a><td> * }<tr><td><a name="l132" href="#l132">132</a><td> *<tr><td><a name="l133" href="#l133">133</a><td> * // Asynchronous promise API:<tr><td><a name="l134" href="#l134">134</a><td> * doAsynchronousWork().thenCatch(function(ex) {<tr><td><a name="l135" href="#l135">135</a><td> * console.error(ex);<tr><td><a name="l136" href="#l136">136</a><td> * });<tr><td><a name="l137" href="#l137">137</a><td> * </code></pre><tr><td><a name="l138" href="#l138">138</a><td> *<tr><td><a name="l139" href="#l139">139</a><td> * @param {function(*): (R|webdriver.promise.Promise.<R>)} errback The function<tr><td><a name="l140" href="#l140">140</a><td> * to call if this promise is rejected. The function should expect a single<tr><td><a name="l141" href="#l141">141</a><td> * argument: the rejection reason.<tr><td><a name="l142" href="#l142">142</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise which will be<tr><td><a name="l143" href="#l143">143</a><td> * resolved with the result of the invoked callback.<tr><td><a name="l144" href="#l144">144</a><td> * @template R<tr><td><a name="l145" href="#l145">145</a><td> */<tr><td><a name="l146" href="#l146">146</a><td>webdriver.promise.Thenable.prototype.thenCatch = function(errback) {};<tr><td><a name="l147" href="#l147">147</a><td><tr><td><a name="l148" href="#l148">148</a><td><tr><td><a name="l149" href="#l149">149</a><td>/**<tr><td><a name="l150" href="#l150">150</a><td> * Registers a listener to invoke when this promise is resolved, regardless<tr><td><a name="l151" href="#l151">151</a><td> * of whether the promise's value was successfully computed. This function<tr><td><a name="l152" href="#l152">152</a><td> * is synonymous with the {@code finally} clause in a synchronous API:<tr><td><a name="l153" href="#l153">153</a><td> * <pre><code><tr><td><a name="l154" href="#l154">154</a><td> * // Synchronous API:<tr><td><a name="l155" href="#l155">155</a><td> * try {<tr><td><a name="l156" href="#l156">156</a><td> * doSynchronousWork();<tr><td><a name="l157" href="#l157">157</a><td> * } finally {<tr><td><a name="l158" href="#l158">158</a><td> * cleanUp();<tr><td><a name="l159" href="#l159">159</a><td> * }<tr><td><a name="l160" href="#l160">160</a><td> *<tr><td><a name="l161" href="#l161">161</a><td> * // Asynchronous promise API:<tr><td><a name="l162" href="#l162">162</a><td> * doAsynchronousWork().thenFinally(cleanUp);<tr><td><a name="l163" href="#l163">163</a><td> * </code></pre><tr><td><a name="l164" href="#l164">164</a><td> *<tr><td><a name="l165" href="#l165">165</a><td> * <b>Note:</b> similar to the {@code finally} clause, if the registered<tr><td><a name="l166" href="#l166">166</a><td> * callback returns a rejected promise or throws an error, it will silently<tr><td><a name="l167" href="#l167">167</a><td> * replace the rejection error (if any) from this promise:<tr><td><a name="l168" href="#l168">168</a><td> * <pre><code><tr><td><a name="l169" href="#l169">169</a><td> * try {<tr><td><a name="l170" href="#l170">170</a><td> * throw Error('one');<tr><td><a name="l171" href="#l171">171</a><td> * } finally {<tr><td><a name="l172" href="#l172">172</a><td> * throw Error('two'); // Hides Error: one<tr><td><a name="l173" href="#l173">173</a><td> * }<tr><td><a name="l174" href="#l174">174</a><td> *<tr><td><a name="l175" href="#l175">175</a><td> * webdriver.promise.rejected(Error('one'))<tr><td><a name="l176" href="#l176">176</a><td> * .thenFinally(function() {<tr><td><a name="l177" href="#l177">177</a><td> * throw Error('two'); // Hides Error: one<tr><td><a name="l178" href="#l178">178</a><td> * });<tr><td><a name="l179" href="#l179">179</a><td> * </code></pre><tr><td><a name="l180" href="#l180">180</a><td> *<tr><td><a name="l181" href="#l181">181</a><td> *<tr><td><a name="l182" href="#l182">182</a><td> * @param {function(): (R|webdriver.promise.Promise.<R>)} callback The function<tr><td><a name="l183" href="#l183">183</a><td> * to call when this promise is resolved.<tr><td><a name="l184" href="#l184">184</a><td> * @return {!webdriver.promise.Promise.<R>} A promise that will be fulfilled<tr><td><a name="l185" href="#l185">185</a><td> * with the callback result.<tr><td><a name="l186" href="#l186">186</a><td> * @template R<tr><td><a name="l187" href="#l187">187</a><td> */<tr><td><a name="l188" href="#l188">188</a><td>webdriver.promise.Thenable.prototype.thenFinally = function(callback) {};<tr><td><a name="l189" href="#l189">189</a><td><tr><td><a name="l190" href="#l190">190</a><td><tr><td><a name="l191" href="#l191">191</a><td>/**<tr><td><a name="l192" href="#l192">192</a><td> * Property used to flag constructor's as implementing the Thenable interface<tr><td><a name="l193" href="#l193">193</a><td> * for runtime type checking.<tr><td><a name="l194" href="#l194">194</a><td> * @private {string}<tr><td><a name="l195" href="#l195">195</a><td> * @const<tr><td><a name="l196" href="#l196">196</a><td> */<tr><td><a name="l197" href="#l197">197</a><td>webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_ = '$webdriver_Thenable';<tr><td><a name="l198" href="#l198">198</a><td><tr><td><a name="l199" href="#l199">199</a><td><tr><td><a name="l200" href="#l200">200</a><td>/**<tr><td><a name="l201" href="#l201">201</a><td> * Adds a property to a class prototype to allow runtime checks of whether<tr><td><a name="l202" href="#l202">202</a><td> * instances of that class implement the Thenable interface. This function will<tr><td><a name="l203" href="#l203">203</a><td> * also ensure the prototype's {@code then} function is exported from compiled<tr><td><a name="l204" href="#l204">204</a><td> * code.<tr><td><a name="l205" href="#l205">205</a><td> * @param {function(new: webdriver.promise.Thenable, ...[?])} ctor The<tr><td><a name="l206" href="#l206">206</a><td> * constructor whose prototype to modify.<tr><td><a name="l207" href="#l207">207</a><td> */<tr><td><a name="l208" href="#l208">208</a><td>webdriver.promise.Thenable.addImplementation = function(ctor) {<tr><td><a name="l209" href="#l209">209</a><td> // Based on goog.promise.Thenable.isImplementation.<tr><td><a name="l210" href="#l210">210</a><td> ctor.prototype['then'] = ctor.prototype.then;<tr><td><a name="l211" href="#l211">211</a><td> try {<tr><td><a name="l212" href="#l212">212</a><td> // Old IE7 does not support defineProperty; IE8 only supports it for<tr><td><a name="l213" href="#l213">213</a><td> // DOM elements.<tr><td><a name="l214" href="#l214">214</a><td> Object.defineProperty(<tr><td><a name="l215" href="#l215">215</a><td> ctor.prototype,<tr><td><a name="l216" href="#l216">216</a><td> webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_,<tr><td><a name="l217" href="#l217">217</a><td> {'value': true, 'enumerable': false});<tr><td><a name="l218" href="#l218">218</a><td> } catch (ex) {<tr><td><a name="l219" href="#l219">219</a><td> ctor.prototype[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_] = true;<tr><td><a name="l220" href="#l220">220</a><td> }<tr><td><a name="l221" href="#l221">221</a><td>};<tr><td><a name="l222" href="#l222">222</a><td><tr><td><a name="l223" href="#l223">223</a><td><tr><td><a name="l224" href="#l224">224</a><td>/**<tr><td><a name="l225" href="#l225">225</a><td> * Checks if an object has been tagged for implementing the Thenable interface<tr><td><a name="l226" href="#l226">226</a><td> * as defined by {@link webdriver.promise.Thenable.addImplementation}.<tr><td><a name="l227" href="#l227">227</a><td> * @param {*} object The object to test.<tr><td><a name="l228" href="#l228">228</a><td> * @return {boolean} Whether the object is an implementation of the Thenable<tr><td><a name="l229" href="#l229">229</a><td> * interface.<tr><td><a name="l230" href="#l230">230</a><td> */<tr><td><a name="l231" href="#l231">231</a><td>webdriver.promise.Thenable.isImplementation = function(object) {<tr><td><a name="l232" href="#l232">232</a><td> // Based on goog.promise.Thenable.isImplementation.<tr><td><a name="l233" href="#l233">233</a><td> if (!object) {<tr><td><a name="l234" href="#l234">234</a><td> return false;<tr><td><a name="l235" href="#l235">235</a><td> }<tr><td><a name="l236" href="#l236">236</a><td> try {<tr><td><a name="l237" href="#l237">237</a><td> return !!object[webdriver.promise.Thenable.IMPLEMENTED_BY_PROP_];<tr><td><a name="l238" href="#l238">238</a><td> } catch (e) {<tr><td><a name="l239" href="#l239">239</a><td> return false; // Property access seems to be forbidden.<tr><td><a name="l240" href="#l240">240</a><td> }<tr><td><a name="l241" href="#l241">241</a><td>};<tr><td><a name="l242" href="#l242">242</a><td><tr><td><a name="l243" href="#l243">243</a><td><tr><td><a name="l244" href="#l244">244</a><td><tr><td><a name="l245" href="#l245">245</a><td>/**<tr><td><a name="l246" href="#l246">246</a><td> * Represents the eventual value of a completed operation. Each promise may be<tr><td><a name="l247" href="#l247">247</a><td> * in one of three states: pending, resolved, or rejected. Each promise starts<tr><td><a name="l248" href="#l248">248</a><td> * in the pending state and may make a single transition to either a<tr><td><a name="l249" href="#l249">249</a><td> * fulfilled or rejected state, at which point the promise is considered<tr><td><a name="l250" href="#l250">250</a><td> * resolved.<tr><td><a name="l251" href="#l251">251</a><td> *<tr><td><a name="l252" href="#l252">252</a><td> * @constructor<tr><td><a name="l253" href="#l253">253</a><td> * @implements {webdriver.promise.Thenable.<T>}<tr><td><a name="l254" href="#l254">254</a><td> * @template T<tr><td><a name="l255" href="#l255">255</a><td> * @see http://promises-aplus.github.io/promises-spec/<tr><td><a name="l256" href="#l256">256</a><td> */<tr><td><a name="l257" href="#l257">257</a><td>webdriver.promise.Promise = function() {};<tr><td><a name="l258" href="#l258">258</a><td>webdriver.promise.Thenable.addImplementation(webdriver.promise.Promise);<tr><td><a name="l259" href="#l259">259</a><td><tr><td><a name="l260" href="#l260">260</a><td><tr><td><a name="l261" href="#l261">261</a><td>/** @override */<tr><td><a name="l262" href="#l262">262</a><td>webdriver.promise.Promise.prototype.cancel = function(reason) {<tr><td><a name="l263" href="#l263">263</a><td> throw new TypeError('Unimplemented function: "cancel"');<tr><td><a name="l264" href="#l264">264</a><td>};<tr><td><a name="l265" href="#l265">265</a><td><tr><td><a name="l266" href="#l266">266</a><td><tr><td><a name="l267" href="#l267">267</a><td>/** @override */<tr><td><a name="l268" href="#l268">268</a><td>webdriver.promise.Promise.prototype.isPending = function() {<tr><td><a name="l269" href="#l269">269</a><td> throw new TypeError('Unimplemented function: "isPending"');<tr><td><a name="l270" href="#l270">270</a><td>};<tr><td><a name="l271" href="#l271">271</a><td><tr><td><a name="l272" href="#l272">272</a><td><tr><td><a name="l273" href="#l273">273</a><td>/** @override */<tr><td><a name="l274" href="#l274">274</a><td>webdriver.promise.Promise.prototype.then = function(<tr><td><a name="l275" href="#l275">275</a><td> opt_callback, opt_errback) {<tr><td><a name="l276" href="#l276">276</a><td> throw new TypeError('Unimplemented function: "then"');<tr><td><a name="l277" href="#l277">277</a><td>};<tr><td><a name="l278" href="#l278">278</a><td><tr><td><a name="l279" href="#l279">279</a><td><tr><td><a name="l280" href="#l280">280</a><td>/** @override */<tr><td><a name="l281" href="#l281">281</a><td>webdriver.promise.Promise.prototype.thenCatch = function(errback) {<tr><td><a name="l282" href="#l282">282</a><td> return this.then(null, errback);<tr><td><a name="l283" href="#l283">283</a><td>};<tr><td><a name="l284" href="#l284">284</a><td><tr><td><a name="l285" href="#l285">285</a><td><tr><td><a name="l286" href="#l286">286</a><td>/** @override */<tr><td><a name="l287" href="#l287">287</a><td>webdriver.promise.Promise.prototype.thenFinally = function(callback) {<tr><td><a name="l288" href="#l288">288</a><td> return this.then(callback, function(err) {<tr><td><a name="l289" href="#l289">289</a><td> var value = callback();<tr><td><a name="l290" href="#l290">290</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l291" href="#l291">291</a><td> return value.then(function() {<tr><td><a name="l292" href="#l292">292</a><td> throw err;<tr><td><a name="l293" href="#l293">293</a><td> });<tr><td><a name="l294" href="#l294">294</a><td> }<tr><td><a name="l295" href="#l295">295</a><td> throw err;<tr><td><a name="l296" href="#l296">296</a><td> });<tr><td><a name="l297" href="#l297">297</a><td>};<tr><td><a name="l298" href="#l298">298</a><td><tr><td><a name="l299" href="#l299">299</a><td><tr><td><a name="l300" href="#l300">300</a><td><tr><td><a name="l301" href="#l301">301</a><td>/**<tr><td><a name="l302" href="#l302">302</a><td> * Represents a value that will be resolved at some point in the future. This<tr><td><a name="l303" href="#l303">303</a><td> * class represents the protected "producer" half of a Promise - each Deferred<tr><td><a name="l304" href="#l304">304</a><td> * has a {@code promise} property that may be returned to consumers for<tr><td><a name="l305" href="#l305">305</a><td> * registering callbacks, reserving the ability to resolve the deferred to the<tr><td><a name="l306" href="#l306">306</a><td> * producer.<tr><td><a name="l307" href="#l307">307</a><td> *<tr><td><a name="l308" href="#l308">308</a><td> * <p>If this Deferred is rejected and there are no listeners registered before<tr><td><a name="l309" href="#l309">309</a><td> * the next turn of the event loop, the rejection will be passed to the<tr><td><a name="l310" href="#l310">310</a><td> * {@link webdriver.promise.ControlFlow} as an unhandled failure.<tr><td><a name="l311" href="#l311">311</a><td> *<tr><td><a name="l312" href="#l312">312</a><td> * <p>If this Deferred is cancelled, the cancellation reason will be forward to<tr><td><a name="l313" href="#l313">313</a><td> * the Deferred's canceller function (if provided). The canceller may return a<tr><td><a name="l314" href="#l314">314</a><td> * truth-y value to override the reason provided for rejection.<tr><td><a name="l315" href="#l315">315</a><td> *<tr><td><a name="l316" href="#l316">316</a><td> * @param {webdriver.promise.ControlFlow=} opt_flow The control flow<tr><td><a name="l317" href="#l317">317</a><td> * this instance was created under. This should only be provided during<tr><td><a name="l318" href="#l318">318</a><td> * unit tests.<tr><td><a name="l319" href="#l319">319</a><td> * @constructor<tr><td><a name="l320" href="#l320">320</a><td> * @extends {webdriver.promise.Promise.<T>}<tr><td><a name="l321" href="#l321">321</a><td> * @template T<tr><td><a name="l322" href="#l322">322</a><td> */<tr><td><a name="l323" href="#l323">323</a><td>webdriver.promise.Deferred = function(opt_flow) {<tr><td><a name="l324" href="#l324">324</a><td> /* NOTE: This class's implementation diverges from the prototypical style<tr><td><a name="l325" href="#l325">325</a><td> * used in the rest of the atoms library. This was done intentionally to<tr><td><a name="l326" href="#l326">326</a><td> * protect the internal Deferred state from consumers, as outlined by<tr><td><a name="l327" href="#l327">327</a><td> * http://wiki.commonjs.org/wiki/Promises<tr><td><a name="l328" href="#l328">328</a><td> */<tr><td><a name="l329" href="#l329">329</a><td> goog.base(this);<tr><td><a name="l330" href="#l330">330</a><td><tr><td><a name="l331" href="#l331">331</a><td> var flow = opt_flow || webdriver.promise.controlFlow();<tr><td><a name="l332" href="#l332">332</a><td><tr><td><a name="l333" href="#l333">333</a><td> /**<tr><td><a name="l334" href="#l334">334</a><td> * The deferred this instance is chained from, if any.<tr><td><a name="l335" href="#l335">335</a><td> * @private {webdriver.promise.Deferred.<?>}<tr><td><a name="l336" href="#l336">336</a><td> */<tr><td><a name="l337" href="#l337">337</a><td> this.parent_ = null;<tr><td><a name="l338" href="#l338">338</a><td><tr><td><a name="l339" href="#l339">339</a><td> /**<tr><td><a name="l340" href="#l340">340</a><td> * The listeners registered with this Deferred. Each element in the list will<tr><td><a name="l341" href="#l341">341</a><td> * be a 3-tuple of the callback function, errback function, and the<tr><td><a name="l342" href="#l342">342</a><td> * corresponding deferred object.<tr><td><a name="l343" href="#l343">343</a><td> * @type {!Array.<!webdriver.promise.Deferred.Listener_>}<tr><td><a name="l344" href="#l344">344</a><td> */<tr><td><a name="l345" href="#l345">345</a><td> var listeners = [];<tr><td><a name="l346" href="#l346">346</a><td><tr><td><a name="l347" href="#l347">347</a><td> /**<tr><td><a name="l348" href="#l348">348</a><td> * Whether this Deferred's resolution was ever handled by a listener.<tr><td><a name="l349" href="#l349">349</a><td> * If the Deferred is rejected and its value is not handled by a listener<tr><td><a name="l350" href="#l350">350</a><td> * before the next turn of the event loop, the error will be passed to the<tr><td><a name="l351" href="#l351">351</a><td> * global error handler.<tr><td><a name="l352" href="#l352">352</a><td> * @type {boolean}<tr><td><a name="l353" href="#l353">353</a><td> */<tr><td><a name="l354" href="#l354">354</a><td> var handled = false;<tr><td><a name="l355" href="#l355">355</a><td><tr><td><a name="l356" href="#l356">356</a><td> /**<tr><td><a name="l357" href="#l357">357</a><td> * Key for the timeout used to delay reproting an unhandled rejection to the<tr><td><a name="l358" href="#l358">358</a><td> * parent {@link webdriver.promise.ControlFlow}.<tr><td><a name="l359" href="#l359">359</a><td> * @type {?number}<tr><td><a name="l360" href="#l360">360</a><td> */<tr><td><a name="l361" href="#l361">361</a><td> var pendingRejectionKey = null;<tr><td><a name="l362" href="#l362">362</a><td><tr><td><a name="l363" href="#l363">363</a><td> /**<tr><td><a name="l364" href="#l364">364</a><td> * This Deferred's current state.<tr><td><a name="l365" href="#l365">365</a><td> * @type {!webdriver.promise.Deferred.State_}<tr><td><a name="l366" href="#l366">366</a><td> */<tr><td><a name="l367" href="#l367">367</a><td> var state = webdriver.promise.Deferred.State_.PENDING;<tr><td><a name="l368" href="#l368">368</a><td><tr><td><a name="l369" href="#l369">369</a><td> /**<tr><td><a name="l370" href="#l370">370</a><td> * This Deferred's resolved value; set when the state transitions from<tr><td><a name="l371" href="#l371">371</a><td> * {@code webdriver.promise.Deferred.State_.PENDING}.<tr><td><a name="l372" href="#l372">372</a><td> * @type {*}<tr><td><a name="l373" href="#l373">373</a><td> */<tr><td><a name="l374" href="#l374">374</a><td> var value;<tr><td><a name="l375" href="#l375">375</a><td><tr><td><a name="l376" href="#l376">376</a><td> /** @return {boolean} Whether this promise's value is still pending. */<tr><td><a name="l377" href="#l377">377</a><td> function isPending() {<tr><td><a name="l378" href="#l378">378</a><td> return state == webdriver.promise.Deferred.State_.PENDING;<tr><td><a name="l379" href="#l379">379</a><td> }<tr><td><a name="l380" href="#l380">380</a><td><tr><td><a name="l381" href="#l381">381</a><td> /**<tr><td><a name="l382" href="#l382">382</a><td> * Removes all of the listeners previously registered on this deferred.<tr><td><a name="l383" href="#l383">383</a><td> * @throws {Error} If this deferred has already been resolved.<tr><td><a name="l384" href="#l384">384</a><td> */<tr><td><a name="l385" href="#l385">385</a><td> function removeAll() {<tr><td><a name="l386" href="#l386">386</a><td> listeners = [];<tr><td><a name="l387" href="#l387">387</a><td> }<tr><td><a name="l388" href="#l388">388</a><td><tr><td><a name="l389" href="#l389">389</a><td> /**<tr><td><a name="l390" href="#l390">390</a><td> * Resolves this deferred. If the new value is a promise, this function will<tr><td><a name="l391" href="#l391">391</a><td> * wait for it to be resolved before notifying the registered listeners.<tr><td><a name="l392" href="#l392">392</a><td> * @param {!webdriver.promise.Deferred.State_} newState The deferred's new<tr><td><a name="l393" href="#l393">393</a><td> * state.<tr><td><a name="l394" href="#l394">394</a><td> * @param {*} newValue The deferred's new value.<tr><td><a name="l395" href="#l395">395</a><td> */<tr><td><a name="l396" href="#l396">396</a><td> function resolve(newState, newValue) {<tr><td><a name="l397" href="#l397">397</a><td> if (webdriver.promise.Deferred.State_.PENDING !== state) {<tr><td><a name="l398" href="#l398">398</a><td> return;<tr><td><a name="l399" href="#l399">399</a><td> }<tr><td><a name="l400" href="#l400">400</a><td><tr><td><a name="l401" href="#l401">401</a><td> if (newValue === self) {<tr><td><a name="l402" href="#l402">402</a><td> // See promise a+, 2.3.1<tr><td><a name="l403" href="#l403">403</a><td> // http://promises-aplus.github.io/promises-spec/#point-48<tr><td><a name="l404" href="#l404">404</a><td> throw TypeError('A promise may not resolve to itself');<tr><td><a name="l405" href="#l405">405</a><td> }<tr><td><a name="l406" href="#l406">406</a><td><tr><td><a name="l407" href="#l407">407</a><td> state = webdriver.promise.Deferred.State_.BLOCKED;<tr><td><a name="l408" href="#l408">408</a><td><tr><td><a name="l409" href="#l409">409</a><td> if (webdriver.promise.isPromise(newValue)) {<tr><td><a name="l410" href="#l410">410</a><td> var onFulfill = goog.partial(notifyAll, newState);<tr><td><a name="l411" href="#l411">411</a><td> var onReject = goog.partial(<tr><td><a name="l412" href="#l412">412</a><td> notifyAll, webdriver.promise.Deferred.State_.REJECTED);<tr><td><a name="l413" href="#l413">413</a><td> if (newValue instanceof webdriver.promise.Deferred) {<tr><td><a name="l414" href="#l414">414</a><td> newValue.then(onFulfill, onReject);<tr><td><a name="l415" href="#l415">415</a><td> } else {<tr><td><a name="l416" href="#l416">416</a><td> webdriver.promise.asap(newValue, onFulfill, onReject);<tr><td><a name="l417" href="#l417">417</a><td> }<tr><td><a name="l418" href="#l418">418</a><td><tr><td><a name="l419" href="#l419">419</a><td> } else {<tr><td><a name="l420" href="#l420">420</a><td> notifyAll(newState, newValue);<tr><td><a name="l421" href="#l421">421</a><td> }<tr><td><a name="l422" href="#l422">422</a><td> }<tr><td><a name="l423" href="#l423">423</a><td><tr><td><a name="l424" href="#l424">424</a><td> /**<tr><td><a name="l425" href="#l425">425</a><td> * Notifies all of the listeners registered with this Deferred that its state<tr><td><a name="l426" href="#l426">426</a><td> * has changed.<tr><td><a name="l427" href="#l427">427</a><td> * @param {!webdriver.promise.Deferred.State_} newState The deferred's new<tr><td><a name="l428" href="#l428">428</a><td> * state.<tr><td><a name="l429" href="#l429">429</a><td> * @param {*} newValue The deferred's new value.<tr><td><a name="l430" href="#l430">430</a><td> */<tr><td><a name="l431" href="#l431">431</a><td> function notifyAll(newState, newValue) {<tr><td><a name="l432" href="#l432">432</a><td> if (newState === webdriver.promise.Deferred.State_.REJECTED &&<tr><td><a name="l433" href="#l433">433</a><td> // We cannot check instanceof Error since the object may have been<tr><td><a name="l434" href="#l434">434</a><td> // created in a different JS context.<tr><td><a name="l435" href="#l435">435</a><td> goog.isObject(newValue) && goog.isString(newValue.message)) {<tr><td><a name="l436" href="#l436">436</a><td> newValue = flow.annotateError(/** @type {!Error} */(newValue));<tr><td><a name="l437" href="#l437">437</a><td> }<tr><td><a name="l438" href="#l438">438</a><td><tr><td><a name="l439" href="#l439">439</a><td> state = newState;<tr><td><a name="l440" href="#l440">440</a><td> value = newValue;<tr><td><a name="l441" href="#l441">441</a><td> while (listeners.length) {<tr><td><a name="l442" href="#l442">442</a><td> notify(listeners.shift());<tr><td><a name="l443" href="#l443">443</a><td> }<tr><td><a name="l444" href="#l444">444</a><td><tr><td><a name="l445" href="#l445">445</a><td> if (!handled && state == webdriver.promise.Deferred.State_.REJECTED &&<tr><td><a name="l446" href="#l446">446</a><td> !(value instanceof webdriver.promise.CancellationError)) {<tr><td><a name="l447" href="#l447">447</a><td> flow.pendingRejections_ += 1;<tr><td><a name="l448" href="#l448">448</a><td> pendingRejectionKey = flow.timer.setTimeout(function() {<tr><td><a name="l449" href="#l449">449</a><td> pendingRejectionKey = null;<tr><td><a name="l450" href="#l450">450</a><td> flow.pendingRejections_ -= 1;<tr><td><a name="l451" href="#l451">451</a><td> flow.abortFrame_(value);<tr><td><a name="l452" href="#l452">452</a><td> }, 0);<tr><td><a name="l453" href="#l453">453</a><td> }<tr><td><a name="l454" href="#l454">454</a><td> }<tr><td><a name="l455" href="#l455">455</a><td><tr><td><a name="l456" href="#l456">456</a><td> /**<tr><td><a name="l457" href="#l457">457</a><td> * Notifies a single listener of this Deferred's change in state.<tr><td><a name="l458" href="#l458">458</a><td> * @param {!webdriver.promise.Deferred.Listener_} listener The listener to<tr><td><a name="l459" href="#l459">459</a><td> * notify.<tr><td><a name="l460" href="#l460">460</a><td> */<tr><td><a name="l461" href="#l461">461</a><td> function notify(listener) {<tr><td><a name="l462" href="#l462">462</a><td> var func = state == webdriver.promise.Deferred.State_.RESOLVED ?<tr><td><a name="l463" href="#l463">463</a><td> listener.callback : listener.errback;<tr><td><a name="l464" href="#l464">464</a><td> if (func) {<tr><td><a name="l465" href="#l465">465</a><td> flow.runInNewFrame_(goog.partial(func, value),<tr><td><a name="l466" href="#l466">466</a><td> listener.fulfill, listener.reject);<tr><td><a name="l467" href="#l467">467</a><td> } else if (state == webdriver.promise.Deferred.State_.REJECTED) {<tr><td><a name="l468" href="#l468">468</a><td> listener.reject(value);<tr><td><a name="l469" href="#l469">469</a><td> } else {<tr><td><a name="l470" href="#l470">470</a><td> listener.fulfill(value);<tr><td><a name="l471" href="#l471">471</a><td> }<tr><td><a name="l472" href="#l472">472</a><td> }<tr><td><a name="l473" href="#l473">473</a><td><tr><td><a name="l474" href="#l474">474</a><td> /**<tr><td><a name="l475" href="#l475">475</a><td> * The consumer promise for this instance. Provides protected access to the<tr><td><a name="l476" href="#l476">476</a><td> * callback registering functions.<tr><td><a name="l477" href="#l477">477</a><td> * @type {!webdriver.promise.Promise.<T>}<tr><td><a name="l478" href="#l478">478</a><td> */<tr><td><a name="l479" href="#l479">479</a><td> var promise = new webdriver.promise.Promise();<tr><td><a name="l480" href="#l480">480</a><td><tr><td><a name="l481" href="#l481">481</a><td> var self = this;<tr><td><a name="l482" href="#l482">482</a><td><tr><td><a name="l483" href="#l483">483</a><td> /**<tr><td><a name="l484" href="#l484">484</a><td> * Registers a callback on this Deferred.<tr><td><a name="l485" href="#l485">485</a><td> *<tr><td><a name="l486" href="#l486">486</a><td> * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback .<tr><td><a name="l487" href="#l487">487</a><td> * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback .<tr><td><a name="l488" href="#l488">488</a><td> * @return {!webdriver.promise.Promise.<R>} A new promise representing the<tr><td><a name="l489" href="#l489">489</a><td> * result of the callback.<tr><td><a name="l490" href="#l490">490</a><td> * @template R<tr><td><a name="l491" href="#l491">491</a><td> * @see webdriver.promise.Promise#then<tr><td><a name="l492" href="#l492">492</a><td> */<tr><td><a name="l493" href="#l493">493</a><td> function then(opt_callback, opt_errback) {<tr><td><a name="l494" href="#l494">494</a><td> // Avoid unnecessary allocations if we weren't given any callback functions.<tr><td><a name="l495" href="#l495">495</a><td> if (!opt_callback && !opt_errback) {<tr><td><a name="l496" href="#l496">496</a><td> return promise;<tr><td><a name="l497" href="#l497">497</a><td> }<tr><td><a name="l498" href="#l498">498</a><td><tr><td><a name="l499" href="#l499">499</a><td> // The moment a listener is registered, we consider this deferred to be<tr><td><a name="l500" href="#l500">500</a><td> // handled; the callback must handle any rejection errors.<tr><td><a name="l501" href="#l501">501</a><td> handled = true;<tr><td><a name="l502" href="#l502">502</a><td> if (pendingRejectionKey !== null) {<tr><td><a name="l503" href="#l503">503</a><td> flow.pendingRejections_ -= 1;<tr><td><a name="l504" href="#l504">504</a><td> flow.timer.clearTimeout(pendingRejectionKey);<tr><td><a name="l505" href="#l505">505</a><td> pendingRejectionKey = null;<tr><td><a name="l506" href="#l506">506</a><td> }<tr><td><a name="l507" href="#l507">507</a><td><tr><td><a name="l508" href="#l508">508</a><td> var deferred = new webdriver.promise.Deferred(flow);<tr><td><a name="l509" href="#l509">509</a><td> deferred.parent_ = self;<tr><td><a name="l510" href="#l510">510</a><td><tr><td><a name="l511" href="#l511">511</a><td> var listener = {<tr><td><a name="l512" href="#l512">512</a><td> callback: opt_callback,<tr><td><a name="l513" href="#l513">513</a><td> errback: opt_errback,<tr><td><a name="l514" href="#l514">514</a><td> fulfill: deferred.fulfill,<tr><td><a name="l515" href="#l515">515</a><td> reject: deferred.reject<tr><td><a name="l516" href="#l516">516</a><td> };<tr><td><a name="l517" href="#l517">517</a><td><tr><td><a name="l518" href="#l518">518</a><td> if (state == webdriver.promise.Deferred.State_.PENDING ||<tr><td><a name="l519" href="#l519">519</a><td> state == webdriver.promise.Deferred.State_.BLOCKED) {<tr><td><a name="l520" href="#l520">520</a><td> listeners.push(listener);<tr><td><a name="l521" href="#l521">521</a><td> } else {<tr><td><a name="l522" href="#l522">522</a><td> notify(listener);<tr><td><a name="l523" href="#l523">523</a><td> }<tr><td><a name="l524" href="#l524">524</a><td><tr><td><a name="l525" href="#l525">525</a><td> return deferred.promise;<tr><td><a name="l526" href="#l526">526</a><td> }<tr><td><a name="l527" href="#l527">527</a><td><tr><td><a name="l528" href="#l528">528</a><td> /**<tr><td><a name="l529" href="#l529">529</a><td> * Resolves this promise with the given value. If the value is itself a<tr><td><a name="l530" href="#l530">530</a><td> * promise and not a reference to this deferred, this instance will wait for<tr><td><a name="l531" href="#l531">531</a><td> * it before resolving.<tr><td><a name="l532" href="#l532">532</a><td> * @param {T=} opt_value The fulfilled value.<tr><td><a name="l533" href="#l533">533</a><td> */<tr><td><a name="l534" href="#l534">534</a><td> function fulfill(opt_value) {<tr><td><a name="l535" href="#l535">535</a><td> resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value);<tr><td><a name="l536" href="#l536">536</a><td> }<tr><td><a name="l537" href="#l537">537</a><td><tr><td><a name="l538" href="#l538">538</a><td> /**<tr><td><a name="l539" href="#l539">539</a><td> * Rejects this promise. If the error is itself a promise, this instance will<tr><td><a name="l540" href="#l540">540</a><td> * be chained to it and be rejected with the error's resolved value.<tr><td><a name="l541" href="#l541">541</a><td> * @param {*=} opt_error The rejection reason, typically either a<tr><td><a name="l542" href="#l542">542</a><td> * {@code Error} or a {@code string}.<tr><td><a name="l543" href="#l543">543</a><td> */<tr><td><a name="l544" href="#l544">544</a><td> function reject(opt_error) {<tr><td><a name="l545" href="#l545">545</a><td> resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error);<tr><td><a name="l546" href="#l546">546</a><td> }<tr><td><a name="l547" href="#l547">547</a><td><tr><td><a name="l548" href="#l548">548</a><td> /**<tr><td><a name="l549" href="#l549">549</a><td> * Attempts to cancel the computation of this instance's value. This attempt<tr><td><a name="l550" href="#l550">550</a><td> * will silently fail if this instance has already resolved.<tr><td><a name="l551" href="#l551">551</a><td> * @param {string=} opt_reason The reason for cancelling this promise. */<tr><td><a name="l552" href="#l552">552</a><td> function cancel(opt_reason) {<tr><td><a name="l553" href="#l553">553</a><td> if (!isPending()) {<tr><td><a name="l554" href="#l554">554</a><td> return;<tr><td><a name="l555" href="#l555">555</a><td> }<tr><td><a name="l556" href="#l556">556</a><td><tr><td><a name="l557" href="#l557">557</a><td> if (self.parent_) {<tr><td><a name="l558" href="#l558">558</a><td> self.parent_.cancel(opt_reason);<tr><td><a name="l559" href="#l559">559</a><td> } else {<tr><td><a name="l560" href="#l560">560</a><td> reject(new webdriver.promise.CancellationError(opt_reason));<tr><td><a name="l561" href="#l561">561</a><td> }<tr><td><a name="l562" href="#l562">562</a><td> }<tr><td><a name="l563" href="#l563">563</a><td><tr><td><a name="l564" href="#l564">564</a><td> this.promise = promise;<tr><td><a name="l565" href="#l565">565</a><td> this.promise.then = this.then = then;<tr><td><a name="l566" href="#l566">566</a><td> this.promise.cancel = this.cancel = cancel;<tr><td><a name="l567" href="#l567">567</a><td> this.promise.isPending = this.isPending = isPending;<tr><td><a name="l568" href="#l568">568</a><td> this.fulfill = fulfill;<tr><td><a name="l569" href="#l569">569</a><td> this.reject = this.errback = reject;<tr><td><a name="l570" href="#l570">570</a><td><tr><td><a name="l571" href="#l571">571</a><td> // Only expose this function to our internal classes.<tr><td><a name="l572" href="#l572">572</a><td> // TODO: find a cleaner way of handling this.<tr><td><a name="l573" href="#l573">573</a><td> if (this instanceof webdriver.promise.Task_) {<tr><td><a name="l574" href="#l574">574</a><td> this.removeAll = removeAll;<tr><td><a name="l575" href="#l575">575</a><td> }<tr><td><a name="l576" href="#l576">576</a><td><tr><td><a name="l577" href="#l577">577</a><td> // Export symbols necessary for the contract on this object to work in<tr><td><a name="l578" href="#l578">578</a><td> // compiled mode.<tr><td><a name="l579" href="#l579">579</a><td> goog.exportProperty(this, 'then', this.then);<tr><td><a name="l580" href="#l580">580</a><td> goog.exportProperty(this, 'cancel', cancel);<tr><td><a name="l581" href="#l581">581</a><td> goog.exportProperty(this, 'fulfill', fulfill);<tr><td><a name="l582" href="#l582">582</a><td> goog.exportProperty(this, 'reject', reject);<tr><td><a name="l583" href="#l583">583</a><td> goog.exportProperty(this, 'isPending', isPending);<tr><td><a name="l584" href="#l584">584</a><td> goog.exportProperty(this, 'promise', this.promise);<tr><td><a name="l585" href="#l585">585</a><td> goog.exportProperty(this.promise, 'then', this.then);<tr><td><a name="l586" href="#l586">586</a><td> goog.exportProperty(this.promise, 'cancel', cancel);<tr><td><a name="l587" href="#l587">587</a><td> goog.exportProperty(this.promise, 'isPending', isPending);<tr><td><a name="l588" href="#l588">588</a><td>};<tr><td><a name="l589" href="#l589">589</a><td>goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise);<tr><td><a name="l590" href="#l590">590</a><td><tr><td><a name="l591" href="#l591">591</a><td><tr><td><a name="l592" href="#l592">592</a><td>/**<tr><td><a name="l593" href="#l593">593</a><td> * Type definition for a listener registered on a Deferred object.<tr><td><a name="l594" href="#l594">594</a><td> * @typedef {{callback:(Function|undefined),<tr><td><a name="l595" href="#l595">595</a><td> * errback:(Function|undefined),<tr><td><a name="l596" href="#l596">596</a><td> * fulfill: function(*), reject: function(*)}}<tr><td><a name="l597" href="#l597">597</a><td> * @private<tr><td><a name="l598" href="#l598">598</a><td> */<tr><td><a name="l599" href="#l599">599</a><td>webdriver.promise.Deferred.Listener_;<tr><td><a name="l600" href="#l600">600</a><td><tr><td><a name="l601" href="#l601">601</a><td><tr><td><a name="l602" href="#l602">602</a><td>/**<tr><td><a name="l603" href="#l603">603</a><td> * The three states a {@link webdriver.promise.Deferred} object may be in.<tr><td><a name="l604" href="#l604">604</a><td> * @enum {number}<tr><td><a name="l605" href="#l605">605</a><td> * @private<tr><td><a name="l606" href="#l606">606</a><td> */<tr><td><a name="l607" href="#l607">607</a><td>webdriver.promise.Deferred.State_ = {<tr><td><a name="l608" href="#l608">608</a><td> REJECTED: -1,<tr><td><a name="l609" href="#l609">609</a><td> PENDING: 0,<tr><td><a name="l610" href="#l610">610</a><td> BLOCKED: 1,<tr><td><a name="l611" href="#l611">611</a><td> RESOLVED: 2<tr><td><a name="l612" href="#l612">612</a><td>};<tr><td><a name="l613" href="#l613">613</a><td><tr><td><a name="l614" href="#l614">614</a><td><tr><td><a name="l615" href="#l615">615</a><td>/**<tr><td><a name="l616" href="#l616">616</a><td> * Tests if a value is an Error-like object. This is more than an straight<tr><td><a name="l617" href="#l617">617</a><td> * instanceof check since the value may originate from another context.<tr><td><a name="l618" href="#l618">618</a><td> * @param {*} value The value to test.<tr><td><a name="l619" href="#l619">619</a><td> * @return {boolean} Whether the value is an error.<tr><td><a name="l620" href="#l620">620</a><td> * @private<tr><td><a name="l621" href="#l621">621</a><td> */<tr><td><a name="l622" href="#l622">622</a><td>webdriver.promise.isError_ = function(value) {<tr><td><a name="l623" href="#l623">623</a><td> return value instanceof Error ||<tr><td><a name="l624" href="#l624">624</a><td> goog.isObject(value) &&<tr><td><a name="l625" href="#l625">625</a><td> (Object.prototype.toString.call(value) === '[object Error]' ||<tr><td><a name="l626" href="#l626">626</a><td> // A special test for goog.testing.JsUnitException.<tr><td><a name="l627" href="#l627">627</a><td> value.isJsUnitException);<tr><td><a name="l628" href="#l628">628</a><td><tr><td><a name="l629" href="#l629">629</a><td>};<tr><td><a name="l630" href="#l630">630</a><td><tr><td><a name="l631" href="#l631">631</a><td><tr><td><a name="l632" href="#l632">632</a><td>/**<tr><td><a name="l633" href="#l633">633</a><td> * Determines whether a {@code value} should be treated as a promise.<tr><td><a name="l634" href="#l634">634</a><td> * Any object whose "then" property is a function will be considered a promise.<tr><td><a name="l635" href="#l635">635</a><td> *<tr><td><a name="l636" href="#l636">636</a><td> * @param {*} value The value to test.<tr><td><a name="l637" href="#l637">637</a><td> * @return {boolean} Whether the value is a promise.<tr><td><a name="l638" href="#l638">638</a><td> */<tr><td><a name="l639" href="#l639">639</a><td>webdriver.promise.isPromise = function(value) {<tr><td><a name="l640" href="#l640">640</a><td> return !!value && goog.isObject(value) &&<tr><td><a name="l641" href="#l641">641</a><td> // Use array notation so the Closure compiler does not obfuscate away our<tr><td><a name="l642" href="#l642">642</a><td> // contract.<tr><td><a name="l643" href="#l643">643</a><td> goog.isFunction(value['then']);<tr><td><a name="l644" href="#l644">644</a><td>};<tr><td><a name="l645" href="#l645">645</a><td><tr><td><a name="l646" href="#l646">646</a><td><tr><td><a name="l647" href="#l647">647</a><td>/**<tr><td><a name="l648" href="#l648">648</a><td> * Creates a promise that will be resolved at a set time in the future.<tr><td><a name="l649" href="#l649">649</a><td> * @param {number} ms The amount of time, in milliseconds, to wait before<tr><td><a name="l650" href="#l650">650</a><td> * resolving the promise.<tr><td><a name="l651" href="#l651">651</a><td> * @return {!webdriver.promise.Promise} The promise.<tr><td><a name="l652" href="#l652">652</a><td> */<tr><td><a name="l653" href="#l653">653</a><td>webdriver.promise.delayed = function(ms) {<tr><td><a name="l654" href="#l654">654</a><td> var timer = webdriver.promise.controlFlow().timer;<tr><td><a name="l655" href="#l655">655</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l656" href="#l656">656</a><td> var key = timer.setTimeout(deferred.fulfill, ms);<tr><td><a name="l657" href="#l657">657</a><td> return deferred.thenCatch(function(e) {<tr><td><a name="l658" href="#l658">658</a><td> timer.clearTimeout(key);<tr><td><a name="l659" href="#l659">659</a><td> throw e;<tr><td><a name="l660" href="#l660">660</a><td> });<tr><td><a name="l661" href="#l661">661</a><td>};<tr><td><a name="l662" href="#l662">662</a><td><tr><td><a name="l663" href="#l663">663</a><td><tr><td><a name="l664" href="#l664">664</a><td>/**<tr><td><a name="l665" href="#l665">665</a><td> * Creates a new deferred object.<tr><td><a name="l666" href="#l666">666</a><td> * @return {!webdriver.promise.Deferred.<T>} The new deferred object.<tr><td><a name="l667" href="#l667">667</a><td> * @template T<tr><td><a name="l668" href="#l668">668</a><td> */<tr><td><a name="l669" href="#l669">669</a><td>webdriver.promise.defer = function() {<tr><td><a name="l670" href="#l670">670</a><td> return new webdriver.promise.Deferred();<tr><td><a name="l671" href="#l671">671</a><td>};<tr><td><a name="l672" href="#l672">672</a><td><tr><td><a name="l673" href="#l673">673</a><td><tr><td><a name="l674" href="#l674">674</a><td>/**<tr><td><a name="l675" href="#l675">675</a><td> * Creates a promise that has been resolved with the given value.<tr><td><a name="l676" href="#l676">676</a><td> * @param {T=} opt_value The resolved value.<tr><td><a name="l677" href="#l677">677</a><td> * @return {!webdriver.promise.Promise.<T>} The resolved promise.<tr><td><a name="l678" href="#l678">678</a><td> * @template T<tr><td><a name="l679" href="#l679">679</a><td> */<tr><td><a name="l680" href="#l680">680</a><td>webdriver.promise.fulfilled = function(opt_value) {<tr><td><a name="l681" href="#l681">681</a><td> if (opt_value instanceof webdriver.promise.Promise) {<tr><td><a name="l682" href="#l682">682</a><td> return opt_value;<tr><td><a name="l683" href="#l683">683</a><td> }<tr><td><a name="l684" href="#l684">684</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l685" href="#l685">685</a><td> deferred.fulfill(opt_value);<tr><td><a name="l686" href="#l686">686</a><td> return deferred.promise;<tr><td><a name="l687" href="#l687">687</a><td>};<tr><td><a name="l688" href="#l688">688</a><td><tr><td><a name="l689" href="#l689">689</a><td><tr><td><a name="l690" href="#l690">690</a><td>/**<tr><td><a name="l691" href="#l691">691</a><td> * Creates a promise that has been rejected with the given reason.<tr><td><a name="l692" href="#l692">692</a><td> * @param {*=} opt_reason The rejection reason; may be any value, but is<tr><td><a name="l693" href="#l693">693</a><td> * usually an Error or a string.<tr><td><a name="l694" href="#l694">694</a><td> * @return {!webdriver.promise.Promise.<T>} The rejected promise.<tr><td><a name="l695" href="#l695">695</a><td> * @template T<tr><td><a name="l696" href="#l696">696</a><td> */<tr><td><a name="l697" href="#l697">697</a><td>webdriver.promise.rejected = function(opt_reason) {<tr><td><a name="l698" href="#l698">698</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l699" href="#l699">699</a><td> deferred.reject(opt_reason);<tr><td><a name="l700" href="#l700">700</a><td> return deferred.promise;<tr><td><a name="l701" href="#l701">701</a><td>};<tr><td><a name="l702" href="#l702">702</a><td><tr><td><a name="l703" href="#l703">703</a><td><tr><td><a name="l704" href="#l704">704</a><td>/**<tr><td><a name="l705" href="#l705">705</a><td> * Wraps a function that is assumed to be a node-style callback as its final<tr><td><a name="l706" href="#l706">706</a><td> * argument. This callback takes two arguments: an error value (which will be<tr><td><a name="l707" href="#l707">707</a><td> * null if the call succeeded), and the success value as the second argument.<tr><td><a name="l708" href="#l708">708</a><td> * If the call fails, the returned promise will be rejected, otherwise it will<tr><td><a name="l709" href="#l709">709</a><td> * be resolved with the result.<tr><td><a name="l710" href="#l710">710</a><td> * @param {!Function} fn The function to wrap.<tr><td><a name="l711" href="#l711">711</a><td> * @param {...?} var_args The arguments to apply to the function, excluding the<tr><td><a name="l712" href="#l712">712</a><td> * final callback.<tr><td><a name="l713" href="#l713">713</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with the<tr><td><a name="l714" href="#l714">714</a><td> * result of the provided function's callback.<tr><td><a name="l715" href="#l715">715</a><td> */<tr><td><a name="l716" href="#l716">716</a><td>webdriver.promise.checkedNodeCall = function(fn, var_args) {<tr><td><a name="l717" href="#l717">717</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l718" href="#l718">718</a><td> try {<tr><td><a name="l719" href="#l719">719</a><td> var args = goog.array.slice(arguments, 1);<tr><td><a name="l720" href="#l720">720</a><td> args.push(function(error, value) {<tr><td><a name="l721" href="#l721">721</a><td> error ? deferred.reject(error) : deferred.fulfill(value);<tr><td><a name="l722" href="#l722">722</a><td> });<tr><td><a name="l723" href="#l723">723</a><td> fn.apply(null, args);<tr><td><a name="l724" href="#l724">724</a><td> } catch (ex) {<tr><td><a name="l725" href="#l725">725</a><td> deferred.reject(ex);<tr><td><a name="l726" href="#l726">726</a><td> }<tr><td><a name="l727" href="#l727">727</a><td> return deferred.promise;<tr><td><a name="l728" href="#l728">728</a><td>};<tr><td><a name="l729" href="#l729">729</a><td><tr><td><a name="l730" href="#l730">730</a><td><tr><td><a name="l731" href="#l731">731</a><td>/**<tr><td><a name="l732" href="#l732">732</a><td> * Registers an observer on a promised {@code value}, returning a new promise<tr><td><a name="l733" href="#l733">733</a><td> * that will be resolved when the value is. If {@code value} is not a promise,<tr><td><a name="l734" href="#l734">734</a><td> * then the return promise will be immediately resolved.<tr><td><a name="l735" href="#l735">735</a><td> * @param {*} value The value to observe.<tr><td><a name="l736" href="#l736">736</a><td> * @param {Function=} opt_callback The function to call when the value is<tr><td><a name="l737" href="#l737">737</a><td> * resolved successfully.<tr><td><a name="l738" href="#l738">738</a><td> * @param {Function=} opt_errback The function to call when the value is<tr><td><a name="l739" href="#l739">739</a><td> * rejected.<tr><td><a name="l740" href="#l740">740</a><td> * @return {!webdriver.promise.Promise} A new promise.<tr><td><a name="l741" href="#l741">741</a><td> */<tr><td><a name="l742" href="#l742">742</a><td>webdriver.promise.when = function(value, opt_callback, opt_errback) {<tr><td><a name="l743" href="#l743">743</a><td> if (webdriver.promise.Thenable.isImplementation(value)) {<tr><td><a name="l744" href="#l744">744</a><td> return value.then(opt_callback, opt_errback);<tr><td><a name="l745" href="#l745">745</a><td> }<tr><td><a name="l746" href="#l746">746</a><td><tr><td><a name="l747" href="#l747">747</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l748" href="#l748">748</a><td><tr><td><a name="l749" href="#l749">749</a><td> webdriver.promise.asap(value, deferred.fulfill, deferred.reject);<tr><td><a name="l750" href="#l750">750</a><td><tr><td><a name="l751" href="#l751">751</a><td> return deferred.then(opt_callback, opt_errback);<tr><td><a name="l752" href="#l752">752</a><td>};<tr><td><a name="l753" href="#l753">753</a><td><tr><td><a name="l754" href="#l754">754</a><td><tr><td><a name="l755" href="#l755">755</a><td>/**<tr><td><a name="l756" href="#l756">756</a><td> * Invokes the appropriate callback function as soon as a promised<tr><td><a name="l757" href="#l757">757</a><td> * {@code value} is resolved. This function is similar to<tr><td><a name="l758" href="#l758">758</a><td> * {@link webdriver.promise.when}, except it does not return a new promise.<tr><td><a name="l759" href="#l759">759</a><td> * @param {*} value The value to observe.<tr><td><a name="l760" href="#l760">760</a><td> * @param {Function} callback The function to call when the value is<tr><td><a name="l761" href="#l761">761</a><td> * resolved successfully.<tr><td><a name="l762" href="#l762">762</a><td> * @param {Function=} opt_errback The function to call when the value is<tr><td><a name="l763" href="#l763">763</a><td> * rejected.<tr><td><a name="l764" href="#l764">764</a><td> */<tr><td><a name="l765" href="#l765">765</a><td>webdriver.promise.asap = function(value, callback, opt_errback) {<tr><td><a name="l766" href="#l766">766</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l767" href="#l767">767</a><td> value.then(callback, opt_errback);<tr><td><a name="l768" href="#l768">768</a><td><tr><td><a name="l769" href="#l769">769</a><td> // Maybe a Dojo-like deferred object?<tr><td><a name="l770" href="#l770">770</a><td> } else if (!!value && goog.isObject(value) &&<tr><td><a name="l771" href="#l771">771</a><td> goog.isFunction(value.addCallbacks)) {<tr><td><a name="l772" href="#l772">772</a><td> value.addCallbacks(callback, opt_errback);<tr><td><a name="l773" href="#l773">773</a><td><tr><td><a name="l774" href="#l774">774</a><td> // A raw value, return a resolved promise.<tr><td><a name="l775" href="#l775">775</a><td> } else if (callback) {<tr><td><a name="l776" href="#l776">776</a><td> callback(value);<tr><td><a name="l777" href="#l777">777</a><td> }<tr><td><a name="l778" href="#l778">778</a><td>};<tr><td><a name="l779" href="#l779">779</a><td><tr><td><a name="l780" href="#l780">780</a><td><tr><td><a name="l781" href="#l781">781</a><td>/**<tr><td><a name="l782" href="#l782">782</a><td> * Given an array of promises, will return a promise that will be fulfilled<tr><td><a name="l783" href="#l783">783</a><td> * with the fulfillment values of the input array's values. If any of the<tr><td><a name="l784" href="#l784">784</a><td> * input array's promises are rejected, the returned promise will be rejected<tr><td><a name="l785" href="#l785">785</a><td> * with the same reason.<tr><td><a name="l786" href="#l786">786</a><td> *<tr><td><a name="l787" href="#l787">787</a><td> * @param {!Array.<(T|!webdriver.promise.Promise.<T>)>} arr An array of<tr><td><a name="l788" href="#l788">788</a><td> * promises to wait on.<tr><td><a name="l789" href="#l789">789</a><td> * @return {!webdriver.promise.Promise.<!Array.<T>>} A promise that is<tr><td><a name="l790" href="#l790">790</a><td> * fulfilled with an array containing the fulfilled values of the<tr><td><a name="l791" href="#l791">791</a><td> * input array, or rejected with the same reason as the first<tr><td><a name="l792" href="#l792">792</a><td> * rejected value.<tr><td><a name="l793" href="#l793">793</a><td> * @template T<tr><td><a name="l794" href="#l794">794</a><td> */<tr><td><a name="l795" href="#l795">795</a><td>webdriver.promise.all = function(arr) {<tr><td><a name="l796" href="#l796">796</a><td> var n = arr.length;<tr><td><a name="l797" href="#l797">797</a><td> if (!n) {<tr><td><a name="l798" href="#l798">798</a><td> return webdriver.promise.fulfilled([]);<tr><td><a name="l799" href="#l799">799</a><td> }<tr><td><a name="l800" href="#l800">800</a><td><tr><td><a name="l801" href="#l801">801</a><td> var toFulfill = n;<tr><td><a name="l802" href="#l802">802</a><td> var result = webdriver.promise.defer();<tr><td><a name="l803" href="#l803">803</a><td> var values = [];<tr><td><a name="l804" href="#l804">804</a><td><tr><td><a name="l805" href="#l805">805</a><td> var onFulfill = function(index, value) {<tr><td><a name="l806" href="#l806">806</a><td> values[index] = value;<tr><td><a name="l807" href="#l807">807</a><td> toFulfill--;<tr><td><a name="l808" href="#l808">808</a><td> if (toFulfill == 0) {<tr><td><a name="l809" href="#l809">809</a><td> result.fulfill(values);<tr><td><a name="l810" href="#l810">810</a><td> }<tr><td><a name="l811" href="#l811">811</a><td> };<tr><td><a name="l812" href="#l812">812</a><td><tr><td><a name="l813" href="#l813">813</a><td> for (var i = 0; i < n; ++i) {<tr><td><a name="l814" href="#l814">814</a><td> webdriver.promise.asap(<tr><td><a name="l815" href="#l815">815</a><td> arr[i], goog.partial(onFulfill, i), result.reject);<tr><td><a name="l816" href="#l816">816</a><td> }<tr><td><a name="l817" href="#l817">817</a><td><tr><td><a name="l818" href="#l818">818</a><td> return result.promise;<tr><td><a name="l819" href="#l819">819</a><td>};<tr><td><a name="l820" href="#l820">820</a><td><tr><td><a name="l821" href="#l821">821</a><td><tr><td><a name="l822" href="#l822">822</a><td>/**<tr><td><a name="l823" href="#l823">823</a><td> * Calls a function for each element in an array and inserts the result into a<tr><td><a name="l824" href="#l824">824</a><td> * new array, which is used as the fulfillment value of the promise returned<tr><td><a name="l825" href="#l825">825</a><td> * by this function.<tr><td><a name="l826" href="#l826">826</a><td> *<tr><td><a name="l827" href="#l827">827</a><td> * <p>If the return value of the mapping function is a promise, this function<tr><td><a name="l828" href="#l828">828</a><td> * will wait for it to be fulfilled before inserting it into the new array.<tr><td><a name="l829" href="#l829">829</a><td> *<tr><td><a name="l830" href="#l830">830</a><td> * <p>If the mapping function throws or returns a rejected promise, the<tr><td><a name="l831" href="#l831">831</a><td> * promise returned by this function will be rejected with the same reason.<tr><td><a name="l832" href="#l832">832</a><td> * Only the first failure will be reported; all subsequent errors will be<tr><td><a name="l833" href="#l833">833</a><td> * silently ignored.<tr><td><a name="l834" href="#l834">834</a><td> *<tr><td><a name="l835" href="#l835">835</a><td> * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The<tr><td><a name="l836" href="#l836">836</a><td> * array to iterator over, or a promise that will resolve to said array.<tr><td><a name="l837" href="#l837">837</a><td> * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ?} fn The<tr><td><a name="l838" href="#l838">838</a><td> * function to call for each element in the array. This function should<tr><td><a name="l839" href="#l839">839</a><td> * expect three arguments (the element, the index, and the array itself.<tr><td><a name="l840" href="#l840">840</a><td> * @param {SELF=} opt_self The object to be used as the value of 'this' within<tr><td><a name="l841" href="#l841">841</a><td> * {@code fn}.<tr><td><a name="l842" href="#l842">842</a><td> * @template TYPE, SELF<tr><td><a name="l843" href="#l843">843</a><td> */<tr><td><a name="l844" href="#l844">844</a><td>webdriver.promise.map = function(arr, fn, opt_self) {<tr><td><a name="l845" href="#l845">845</a><td> return webdriver.promise.when(arr, function(arr) {<tr><td><a name="l846" href="#l846">846</a><td> var result = goog.array.map(arr, fn, opt_self);<tr><td><a name="l847" href="#l847">847</a><td> return webdriver.promise.all(result);<tr><td><a name="l848" href="#l848">848</a><td> });<tr><td><a name="l849" href="#l849">849</a><td>};<tr><td><a name="l850" href="#l850">850</a><td><tr><td><a name="l851" href="#l851">851</a><td><tr><td><a name="l852" href="#l852">852</a><td>/**<tr><td><a name="l853" href="#l853">853</a><td> * Calls a function for each element in an array, and if the function returns<tr><td><a name="l854" href="#l854">854</a><td> * true adds the element to a new array.<tr><td><a name="l855" href="#l855">855</a><td> *<tr><td><a name="l856" href="#l856">856</a><td> * <p>If the return value of the filter function is a promise, this function<tr><td><a name="l857" href="#l857">857</a><td> * will wait for it to be fulfilled before determining whether to insert the<tr><td><a name="l858" href="#l858">858</a><td> * element into the new array.<tr><td><a name="l859" href="#l859">859</a><td> *<tr><td><a name="l860" href="#l860">860</a><td> * <p>If the filter function throws or returns a rejected promise, the promise<tr><td><a name="l861" href="#l861">861</a><td> * returned by this function will be rejected with the same reason. Only the<tr><td><a name="l862" href="#l862">862</a><td> * first failure will be reported; all subsequent errors will be silently<tr><td><a name="l863" href="#l863">863</a><td> * ignored.<tr><td><a name="l864" href="#l864">864</a><td> *<tr><td><a name="l865" href="#l865">865</a><td> * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The<tr><td><a name="l866" href="#l866">866</a><td> * array to iterator over, or a promise that will resolve to said array.<tr><td><a name="l867" href="#l867">867</a><td> * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): (<tr><td><a name="l868" href="#l868">868</a><td> * boolean|webdriver.promise.Promise.<boolean>)} fn The function<tr><td><a name="l869" href="#l869">869</a><td> * to call for each element in the array.<tr><td><a name="l870" href="#l870">870</a><td> * @param {SELF=} opt_self The object to be used as the value of 'this' within<tr><td><a name="l871" href="#l871">871</a><td> * {@code fn}.<tr><td><a name="l872" href="#l872">872</a><td> * @template TYPE, SELF<tr><td><a name="l873" href="#l873">873</a><td> */<tr><td><a name="l874" href="#l874">874</a><td>webdriver.promise.filter = function(arr, fn, opt_self) {<tr><td><a name="l875" href="#l875">875</a><td> return webdriver.promise.when(arr, function(arr) {<tr><td><a name="l876" href="#l876">876</a><td> var originalValues = goog.array.clone(arr);<tr><td><a name="l877" href="#l877">877</a><td> return webdriver.promise.map(arr, fn, opt_self).then(function(include) {<tr><td><a name="l878" href="#l878">878</a><td> return goog.array.filter(originalValues, function(value, index) {<tr><td><a name="l879" href="#l879">879</a><td> return include[index];<tr><td><a name="l880" href="#l880">880</a><td> });<tr><td><a name="l881" href="#l881">881</a><td> });<tr><td><a name="l882" href="#l882">882</a><td> });<tr><td><a name="l883" href="#l883">883</a><td>};<tr><td><a name="l884" href="#l884">884</a><td><tr><td><a name="l885" href="#l885">885</a><td><tr><td><a name="l886" href="#l886">886</a><td>/**<tr><td><a name="l887" href="#l887">887</a><td> * Returns a promise that will be resolved with the input value in a<tr><td><a name="l888" href="#l888">888</a><td> * fully-resolved state. If the value is an array, each element will be fully<tr><td><a name="l889" href="#l889">889</a><td> * resolved. Likewise, if the value is an object, all keys will be fully<tr><td><a name="l890" href="#l890">890</a><td> * resolved. In both cases, all nested arrays and objects will also be<tr><td><a name="l891" href="#l891">891</a><td> * fully resolved. All fields are resolved in place; the returned promise will<tr><td><a name="l892" href="#l892">892</a><td> * resolve on {@code value} and not a copy.<tr><td><a name="l893" href="#l893">893</a><td> *<tr><td><a name="l894" href="#l894">894</a><td> * Warning: This function makes no checks against objects that contain<tr><td><a name="l895" href="#l895">895</a><td> * cyclical references:<tr><td><a name="l896" href="#l896">896</a><td> * <pre><code><tr><td><a name="l897" href="#l897">897</a><td> * var value = {};<tr><td><a name="l898" href="#l898">898</a><td> * value['self'] = value;<tr><td><a name="l899" href="#l899">899</a><td> * webdriver.promise.fullyResolved(value); // Stack overflow.<tr><td><a name="l900" href="#l900">900</a><td> * </code></pre><tr><td><a name="l901" href="#l901">901</a><td> *<tr><td><a name="l902" href="#l902">902</a><td> * @param {*} value The value to fully resolve.<tr><td><a name="l903" href="#l903">903</a><td> * @return {!webdriver.promise.Promise} A promise for a fully resolved version<tr><td><a name="l904" href="#l904">904</a><td> * of the input value.<tr><td><a name="l905" href="#l905">905</a><td> */<tr><td><a name="l906" href="#l906">906</a><td>webdriver.promise.fullyResolved = function(value) {<tr><td><a name="l907" href="#l907">907</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l908" href="#l908">908</a><td> return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_);<tr><td><a name="l909" href="#l909">909</a><td> }<tr><td><a name="l910" href="#l910">910</a><td> return webdriver.promise.fullyResolveValue_(value);<tr><td><a name="l911" href="#l911">911</a><td>};<tr><td><a name="l912" href="#l912">912</a><td><tr><td><a name="l913" href="#l913">913</a><td><tr><td><a name="l914" href="#l914">914</a><td>/**<tr><td><a name="l915" href="#l915">915</a><td> * @param {*} value The value to fully resolve. If a promise, assumed to<tr><td><a name="l916" href="#l916">916</a><td> * already be resolved.<tr><td><a name="l917" href="#l917">917</a><td> * @return {!webdriver.promise.Promise} A promise for a fully resolved version<tr><td><a name="l918" href="#l918">918</a><td> * of the input value.<tr><td><a name="l919" href="#l919">919</a><td> * @private<tr><td><a name="l920" href="#l920">920</a><td> */<tr><td><a name="l921" href="#l921">921</a><td>webdriver.promise.fullyResolveValue_ = function(value) {<tr><td><a name="l922" href="#l922">922</a><td> switch (goog.typeOf(value)) {<tr><td><a name="l923" href="#l923">923</a><td> case 'array':<tr><td><a name="l924" href="#l924">924</a><td> return webdriver.promise.fullyResolveKeys_(<tr><td><a name="l925" href="#l925">925</a><td> /** @type {!Array} */ (value));<tr><td><a name="l926" href="#l926">926</a><td><tr><td><a name="l927" href="#l927">927</a><td> case 'object':<tr><td><a name="l928" href="#l928">928</a><td> if (webdriver.promise.isPromise(value)) {<tr><td><a name="l929" href="#l929">929</a><td> // We get here when the original input value is a promise that<tr><td><a name="l930" href="#l930">930</a><td> // resolves to itself. When the user provides us with such a promise,<tr><td><a name="l931" href="#l931">931</a><td> // trust that it counts as a "fully resolved" value and return it.<tr><td><a name="l932" href="#l932">932</a><td> // Of course, since it's already a promise, we can just return it<tr><td><a name="l933" href="#l933">933</a><td> // to the user instead of wrapping it in another promise.<tr><td><a name="l934" href="#l934">934</a><td> return /** @type {!webdriver.promise.Promise} */ (value);<tr><td><a name="l935" href="#l935">935</a><td> }<tr><td><a name="l936" href="#l936">936</a><td><tr><td><a name="l937" href="#l937">937</a><td> if (goog.isNumber(value.nodeType) &&<tr><td><a name="l938" href="#l938">938</a><td> goog.isObject(value.ownerDocument) &&<tr><td><a name="l939" href="#l939">939</a><td> goog.isNumber(value.ownerDocument.nodeType)) {<tr><td><a name="l940" href="#l940">940</a><td> // DOM node; return early to avoid infinite recursion. Should we<tr><td><a name="l941" href="#l941">941</a><td> // only support objects with a certain level of nesting?<tr><td><a name="l942" href="#l942">942</a><td> return webdriver.promise.fulfilled(value);<tr><td><a name="l943" href="#l943">943</a><td> }<tr><td><a name="l944" href="#l944">944</a><td><tr><td><a name="l945" href="#l945">945</a><td> return webdriver.promise.fullyResolveKeys_(<tr><td><a name="l946" href="#l946">946</a><td> /** @type {!Object} */ (value));<tr><td><a name="l947" href="#l947">947</a><td><tr><td><a name="l948" href="#l948">948</a><td> default: // boolean, function, null, number, string, undefined<tr><td><a name="l949" href="#l949">949</a><td> return webdriver.promise.fulfilled(value);<tr><td><a name="l950" href="#l950">950</a><td> }<tr><td><a name="l951" href="#l951">951</a><td>};<tr><td><a name="l952" href="#l952">952</a><td><tr><td><a name="l953" href="#l953">953</a><td><tr><td><a name="l954" href="#l954">954</a><td>/**<tr><td><a name="l955" href="#l955">955</a><td> * @param {!(Array|Object)} obj the object to resolve.<tr><td><a name="l956" href="#l956">956</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with the<tr><td><a name="l957" href="#l957">957</a><td> * input object once all of its values have been fully resolved.<tr><td><a name="l958" href="#l958">958</a><td> * @private<tr><td><a name="l959" href="#l959">959</a><td> */<tr><td><a name="l960" href="#l960">960</a><td>webdriver.promise.fullyResolveKeys_ = function(obj) {<tr><td><a name="l961" href="#l961">961</a><td> var isArray = goog.isArray(obj);<tr><td><a name="l962" href="#l962">962</a><td> var numKeys = isArray ? obj.length : goog.object.getCount(obj);<tr><td><a name="l963" href="#l963">963</a><td> if (!numKeys) {<tr><td><a name="l964" href="#l964">964</a><td> return webdriver.promise.fulfilled(obj);<tr><td><a name="l965" href="#l965">965</a><td> }<tr><td><a name="l966" href="#l966">966</a><td><tr><td><a name="l967" href="#l967">967</a><td> var numResolved = 0;<tr><td><a name="l968" href="#l968">968</a><td> var deferred = new webdriver.promise.Deferred();<tr><td><a name="l969" href="#l969">969</a><td><tr><td><a name="l970" href="#l970">970</a><td> // In pre-IE9, goog.array.forEach will not iterate properly over arrays<tr><td><a name="l971" href="#l971">971</a><td> // containing undefined values because "index in array" returns false<tr><td><a name="l972" href="#l972">972</a><td> // when array[index] === undefined (even for x = [undefined, 1]). To get<tr><td><a name="l973" href="#l973">973</a><td> // around this, we need to use our own forEach implementation.<tr><td><a name="l974" href="#l974">974</a><td> // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be<tr><td><a name="l975" href="#l975">975</a><td> // reproduced in IE9 by changing the browser/document modes, it requires an<tr><td><a name="l976" href="#l976">976</a><td> // actual pre-IE9 browser. Yay, IE!<tr><td><a name="l977" href="#l977">977</a><td> var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) {<tr><td><a name="l978" href="#l978">978</a><td> var n = arr.length;<tr><td><a name="l979" href="#l979">979</a><td> for (var i = 0; i < n; ++i) {<tr><td><a name="l980" href="#l980">980</a><td> fn.call(null, arr[i], i, arr);<tr><td><a name="l981" href="#l981">981</a><td> }<tr><td><a name="l982" href="#l982">982</a><td> };<tr><td><a name="l983" href="#l983">983</a><td><tr><td><a name="l984" href="#l984">984</a><td> forEachKey(obj, function(partialValue, key) {<tr><td><a name="l985" href="#l985">985</a><td> var type = goog.typeOf(partialValue);<tr><td><a name="l986" href="#l986">986</a><td> if (type != 'array' && type != 'object') {<tr><td><a name="l987" href="#l987">987</a><td> maybeResolveValue();<tr><td><a name="l988" href="#l988">988</a><td> return;<tr><td><a name="l989" href="#l989">989</a><td> }<tr><td><a name="l990" href="#l990">990</a><td><tr><td><a name="l991" href="#l991">991</a><td> webdriver.promise.fullyResolved(partialValue).then(<tr><td><a name="l992" href="#l992">992</a><td> function(resolvedValue) {<tr><td><a name="l993" href="#l993">993</a><td> obj[key] = resolvedValue;<tr><td><a name="l994" href="#l994">994</a><td> maybeResolveValue();<tr><td><a name="l995" href="#l995">995</a><td> },<tr><td><a name="l996" href="#l996">996</a><td> deferred.reject);<tr><td><a name="l997" href="#l997">997</a><td> });<tr><td><a name="l998" href="#l998">998</a><td><tr><td><a name="l999" href="#l999">999</a><td> return deferred.promise;<tr><td><a name="l1000" href="#l1000">1000</a><td><tr><td><a name="l1001" href="#l1001">1001</a><td> function maybeResolveValue() {<tr><td><a name="l1002" href="#l1002">1002</a><td> if (++numResolved == numKeys) {<tr><td><a name="l1003" href="#l1003">1003</a><td> deferred.fulfill(obj);<tr><td><a name="l1004" href="#l1004">1004</a><td> }<tr><td><a name="l1005" href="#l1005">1005</a><td> }<tr><td><a name="l1006" href="#l1006">1006</a><td>};<tr><td><a name="l1007" href="#l1007">1007</a><td><tr><td><a name="l1008" href="#l1008">1008</a><td><tr><td><a name="l1009" href="#l1009">1009</a><td>//////////////////////////////////////////////////////////////////////////////<tr><td><a name="l1010" href="#l1010">1010</a><td>//<tr><td><a name="l1011" href="#l1011">1011</a><td>// webdriver.promise.ControlFlow<tr><td><a name="l1012" href="#l1012">1012</a><td>//<tr><td><a name="l1013" href="#l1013">1013</a><td>//////////////////////////////////////////////////////////////////////////////<tr><td><a name="l1014" href="#l1014">1014</a><td><tr><td><a name="l1015" href="#l1015">1015</a><td><tr><td><a name="l1016" href="#l1016">1016</a><td><tr><td><a name="l1017" href="#l1017">1017</a><td>/**<tr><td><a name="l1018" href="#l1018">1018</a><td> * Handles the execution of scheduled tasks, each of which may be an<tr><td><a name="l1019" href="#l1019">1019</a><td> * asynchronous operation. The control flow will ensure tasks are executed in<tr><td><a name="l1020" href="#l1020">1020</a><td> * the ordered scheduled, starting each task only once those before it have<tr><td><a name="l1021" href="#l1021">1021</a><td> * completed.<tr><td><a name="l1022" href="#l1022">1022</a><td> *<tr><td><a name="l1023" href="#l1023">1023</a><td> * <p>Each task scheduled within this flow may return a<tr><td><a name="l1024" href="#l1024">1024</a><td> * {@link webdriver.promise.Promise} to indicate it is an asynchronous<tr><td><a name="l1025" href="#l1025">1025</a><td> * operation. The ControlFlow will wait for such promises to be resolved before<tr><td><a name="l1026" href="#l1026">1026</a><td> * marking the task as completed.<tr><td><a name="l1027" href="#l1027">1027</a><td> *<tr><td><a name="l1028" href="#l1028">1028</a><td> * <p>Tasks and each callback registered on a {@link webdriver.promise.Deferred}<tr><td><a name="l1029" href="#l1029">1029</a><td> * will be run in their own ControlFlow frame. Any tasks scheduled within a<tr><td><a name="l1030" href="#l1030">1030</a><td> * frame will have priority over previously scheduled tasks. Furthermore, if<tr><td><a name="l1031" href="#l1031">1031</a><td> * any of the tasks in the frame fails, the remainder of the tasks in that frame<tr><td><a name="l1032" href="#l1032">1032</a><td> * will be discarded and the failure will be propagated to the user through the<tr><td><a name="l1033" href="#l1033">1033</a><td> * callback/task's promised result.<tr><td><a name="l1034" href="#l1034">1034</a><td> *<tr><td><a name="l1035" href="#l1035">1035</a><td> * <p>Each time a ControlFlow empties its task queue, it will fire an<tr><td><a name="l1036" href="#l1036">1036</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely,<tr><td><a name="l1037" href="#l1037">1037</a><td> * whenever the flow terminates due to an unhandled error, it will remove all<tr><td><a name="l1038" href="#l1038">1038</a><td> * remaining tasks in its queue and fire an<tr><td><a name="l1039" href="#l1039">1039</a><td> * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. If<tr><td><a name="l1040" href="#l1040">1040</a><td> * there are no listeners registered with the flow, the error will be<tr><td><a name="l1041" href="#l1041">1041</a><td> * rethrown to the global error handler.<tr><td><a name="l1042" href="#l1042">1042</a><td> *<tr><td><a name="l1043" href="#l1043">1043</a><td> * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object<tr><td><a name="l1044" href="#l1044">1044</a><td> * to use. Should only be set for testing.<tr><td><a name="l1045" href="#l1045">1045</a><td> * @constructor<tr><td><a name="l1046" href="#l1046">1046</a><td> * @extends {webdriver.EventEmitter}<tr><td><a name="l1047" href="#l1047">1047</a><td> */<tr><td><a name="l1048" href="#l1048">1048</a><td>webdriver.promise.ControlFlow = function(opt_timer) {<tr><td><a name="l1049" href="#l1049">1049</a><td> webdriver.EventEmitter.call(this);<tr><td><a name="l1050" href="#l1050">1050</a><td><tr><td><a name="l1051" href="#l1051">1051</a><td> /**<tr><td><a name="l1052" href="#l1052">1052</a><td> * The timer used by this instance.<tr><td><a name="l1053" href="#l1053">1053</a><td> * @type {webdriver.promise.ControlFlow.Timer}<tr><td><a name="l1054" href="#l1054">1054</a><td> */<tr><td><a name="l1055" href="#l1055">1055</a><td> this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer;<tr><td><a name="l1056" href="#l1056">1056</a><td><tr><td><a name="l1057" href="#l1057">1057</a><td> /**<tr><td><a name="l1058" href="#l1058">1058</a><td> * A list of recent tasks. Each time a new task is started, or a frame is<tr><td><a name="l1059" href="#l1059">1059</a><td> * completed, the previously recorded task is removed from this list. If<tr><td><a name="l1060" href="#l1060">1060</a><td> * there are multiple tasks, task N+1 is considered a sub-task of task<tr><td><a name="l1061" href="#l1061">1061</a><td> * N.<tr><td><a name="l1062" href="#l1062">1062</a><td> * @private {!Array.<!webdriver.promise.Task_>}<tr><td><a name="l1063" href="#l1063">1063</a><td> */<tr><td><a name="l1064" href="#l1064">1064</a><td> this.history_ = [];<tr><td><a name="l1065" href="#l1065">1065</a><td><tr><td><a name="l1066" href="#l1066">1066</a><td> /**<tr><td><a name="l1067" href="#l1067">1067</a><td> * Tracks the active execution frame for this instance. Lazily initialized<tr><td><a name="l1068" href="#l1068">1068</a><td> * when the first task is scheduled.<tr><td><a name="l1069" href="#l1069">1069</a><td> * @private {webdriver.promise.Frame_}<tr><td><a name="l1070" href="#l1070">1070</a><td> */<tr><td><a name="l1071" href="#l1071">1071</a><td> this.activeFrame_ = null;<tr><td><a name="l1072" href="#l1072">1072</a><td><tr><td><a name="l1073" href="#l1073">1073</a><td> /**<tr><td><a name="l1074" href="#l1074">1074</a><td> * A reference to the frame in which new tasks should be scheduled. If<tr><td><a name="l1075" href="#l1075">1075</a><td> * {@code null}, tasks will be scheduled within the active frame. When forcing<tr><td><a name="l1076" href="#l1076">1076</a><td> * a function to run in the context of a new frame, this pointer is used to<tr><td><a name="l1077" href="#l1077">1077</a><td> * ensure tasks are scheduled within the newly created frame, even though it<tr><td><a name="l1078" href="#l1078">1078</a><td> * won't be active yet.<tr><td><a name="l1079" href="#l1079">1079</a><td> * @private {webdriver.promise.Frame_}<tr><td><a name="l1080" href="#l1080">1080</a><td> * @see {#runInNewFrame_}<tr><td><a name="l1081" href="#l1081">1081</a><td> */<tr><td><a name="l1082" href="#l1082">1082</a><td> this.schedulingFrame_ = null;<tr><td><a name="l1083" href="#l1083">1083</a><td><tr><td><a name="l1084" href="#l1084">1084</a><td> /**<tr><td><a name="l1085" href="#l1085">1085</a><td> * Timeout ID set when the flow is about to shutdown without any errors<tr><td><a name="l1086" href="#l1086">1086</a><td> * being detected. Upon shutting down, the flow will emit an<tr><td><a name="l1087" href="#l1087">1087</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events<tr><td><a name="l1088" href="#l1088">1088</a><td> * always follow a brief timeout in order to catch latent errors from the last<tr><td><a name="l1089" href="#l1089">1089</a><td> * completed task. If this task had a callback registered, but no errback, and<tr><td><a name="l1090" href="#l1090">1090</a><td> * the task fails, the unhandled failure would not be reported by the promise<tr><td><a name="l1091" href="#l1091">1091</a><td> * system until the next turn of the event loop:<tr><td><a name="l1092" href="#l1092">1092</a><td> *<tr><td><a name="l1093" href="#l1093">1093</a><td> * // Schedule 1 task that fails.<tr><td><a name="l1094" href="#l1094">1094</a><td> * var result = webriver.promise.controlFlow().schedule('example',<tr><td><a name="l1095" href="#l1095">1095</a><td> * function() { return webdriver.promise.rejected('failed'); });<tr><td><a name="l1096" href="#l1096">1096</a><td> * // Set a callback on the result. This delays reporting the unhandled<tr><td><a name="l1097" href="#l1097">1097</a><td> * // failure for 1 turn of the event loop.<tr><td><a name="l1098" href="#l1098">1098</a><td> * result.then(goog.nullFunction);<tr><td><a name="l1099" href="#l1099">1099</a><td> *<tr><td><a name="l1100" href="#l1100">1100</a><td> * @private {?number}<tr><td><a name="l1101" href="#l1101">1101</a><td> */<tr><td><a name="l1102" href="#l1102">1102</a><td> this.shutdownId_ = null;<tr><td><a name="l1103" href="#l1103">1103</a><td><tr><td><a name="l1104" href="#l1104">1104</a><td> /**<tr><td><a name="l1105" href="#l1105">1105</a><td> * Interval ID for this instance's event loop.<tr><td><a name="l1106" href="#l1106">1106</a><td> * @private {?number}<tr><td><a name="l1107" href="#l1107">1107</a><td> */<tr><td><a name="l1108" href="#l1108">1108</a><td> this.eventLoopId_ = null;<tr><td><a name="l1109" href="#l1109">1109</a><td><tr><td><a name="l1110" href="#l1110">1110</a><td> /**<tr><td><a name="l1111" href="#l1111">1111</a><td> * The number of "pending" promise rejections.<tr><td><a name="l1112" href="#l1112">1112</a><td> *<tr><td><a name="l1113" href="#l1113">1113</a><td> * <p>Each time a promise is rejected and is not handled by a listener, it<tr><td><a name="l1114" href="#l1114">1114</a><td> * will schedule a 0-based timeout to check if it is still unrejected in the<tr><td><a name="l1115" href="#l1115">1115</a><td> * next turn of the JS-event loop. This allows listeners to attach to, and<tr><td><a name="l1116" href="#l1116">1116</a><td> * handle, the rejected promise at any point in same turn of the event loop<tr><td><a name="l1117" href="#l1117">1117</a><td> * that the promise was rejected.<tr><td><a name="l1118" href="#l1118">1118</a><td> *<tr><td><a name="l1119" href="#l1119">1119</a><td> * <p>When this flow's own event loop triggers, it will not run if there<tr><td><a name="l1120" href="#l1120">1120</a><td> * are any outstanding promise rejections. This allows unhandled promises to<tr><td><a name="l1121" href="#l1121">1121</a><td> * be reported before a new task is started, ensuring the error is reported<tr><td><a name="l1122" href="#l1122">1122</a><td> * to the current task queue.<tr><td><a name="l1123" href="#l1123">1123</a><td> *<tr><td><a name="l1124" href="#l1124">1124</a><td> * @private {number}<tr><td><a name="l1125" href="#l1125">1125</a><td> */<tr><td><a name="l1126" href="#l1126">1126</a><td> this.pendingRejections_ = 0;<tr><td><a name="l1127" href="#l1127">1127</a><td><tr><td><a name="l1128" href="#l1128">1128</a><td> /**<tr><td><a name="l1129" href="#l1129">1129</a><td> * The number of aborted frames since the last time a task was executed or a<tr><td><a name="l1130" href="#l1130">1130</a><td> * frame completed successfully.<tr><td><a name="l1131" href="#l1131">1131</a><td> * @private {number}<tr><td><a name="l1132" href="#l1132">1132</a><td> */<tr><td><a name="l1133" href="#l1133">1133</a><td> this.numAbortedFrames_ = 0;<tr><td><a name="l1134" href="#l1134">1134</a><td>};<tr><td><a name="l1135" href="#l1135">1135</a><td>goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter);<tr><td><a name="l1136" href="#l1136">1136</a><td><tr><td><a name="l1137" href="#l1137">1137</a><td><tr><td><a name="l1138" href="#l1138">1138</a><td>/**<tr><td><a name="l1139" href="#l1139">1139</a><td> * @typedef {{clearInterval: function(number),<tr><td><a name="l1140" href="#l1140">1140</a><td> * clearTimeout: function(number),<tr><td><a name="l1141" href="#l1141">1141</a><td> * setInterval: function(!Function, number): number,<tr><td><a name="l1142" href="#l1142">1142</a><td> * setTimeout: function(!Function, number): number}}<tr><td><a name="l1143" href="#l1143">1143</a><td> */<tr><td><a name="l1144" href="#l1144">1144</a><td>webdriver.promise.ControlFlow.Timer;<tr><td><a name="l1145" href="#l1145">1145</a><td><tr><td><a name="l1146" href="#l1146">1146</a><td><tr><td><a name="l1147" href="#l1147">1147</a><td>/**<tr><td><a name="l1148" href="#l1148">1148</a><td> * The default timer object, which uses the global timer functions.<tr><td><a name="l1149" href="#l1149">1149</a><td> * @type {webdriver.promise.ControlFlow.Timer}<tr><td><a name="l1150" href="#l1150">1150</a><td> */<tr><td><a name="l1151" href="#l1151">1151</a><td>webdriver.promise.ControlFlow.defaultTimer = (function() {<tr><td><a name="l1152" href="#l1152">1152</a><td> // The default timer functions may be defined as free variables for the<tr><td><a name="l1153" href="#l1153">1153</a><td> // current context, so do not reference them using "window" or<tr><td><a name="l1154" href="#l1154">1154</a><td> // "goog.global". Also, we must invoke them in a closure, and not using<tr><td><a name="l1155" href="#l1155">1155</a><td> // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or<tr><td><a name="l1156" href="#l1156">1156</a><td> // "Invalid calling object" (IE) errors.<tr><td><a name="l1157" href="#l1157">1157</a><td> return {<tr><td><a name="l1158" href="#l1158">1158</a><td> clearInterval: wrap(clearInterval),<tr><td><a name="l1159" href="#l1159">1159</a><td> clearTimeout: wrap(clearTimeout),<tr><td><a name="l1160" href="#l1160">1160</a><td> setInterval: wrap(setInterval),<tr><td><a name="l1161" href="#l1161">1161</a><td> setTimeout: wrap(setTimeout)<tr><td><a name="l1162" href="#l1162">1162</a><td> };<tr><td><a name="l1163" href="#l1163">1163</a><td><tr><td><a name="l1164" href="#l1164">1164</a><td> function wrap(fn) {<tr><td><a name="l1165" href="#l1165">1165</a><td> return function() {<tr><td><a name="l1166" href="#l1166">1166</a><td> // Cannot use .call() or .apply() since we do not know which variable<tr><td><a name="l1167" href="#l1167">1167</a><td> // the function is bound to, and using the wrong one will generate<tr><td><a name="l1168" href="#l1168">1168</a><td> // an error.<tr><td><a name="l1169" href="#l1169">1169</a><td> return fn(arguments[0], arguments[1]);<tr><td><a name="l1170" href="#l1170">1170</a><td> };<tr><td><a name="l1171" href="#l1171">1171</a><td> }<tr><td><a name="l1172" href="#l1172">1172</a><td>})();<tr><td><a name="l1173" href="#l1173">1173</a><td><tr><td><a name="l1174" href="#l1174">1174</a><td><tr><td><a name="l1175" href="#l1175">1175</a><td>/**<tr><td><a name="l1176" href="#l1176">1176</a><td> * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.<tr><td><a name="l1177" href="#l1177">1177</a><td> * @enum {string}<tr><td><a name="l1178" href="#l1178">1178</a><td> */<tr><td><a name="l1179" href="#l1179">1179</a><td>webdriver.promise.ControlFlow.EventType = {<tr><td><a name="l1180" href="#l1180">1180</a><td><tr><td><a name="l1181" href="#l1181">1181</a><td> /** Emitted when all tasks have been successfully executed. */<tr><td><a name="l1182" href="#l1182">1182</a><td> IDLE: 'idle',<tr><td><a name="l1183" href="#l1183">1183</a><td><tr><td><a name="l1184" href="#l1184">1184</a><td> /** Emitted when a ControlFlow has been reset. */<tr><td><a name="l1185" href="#l1185">1185</a><td> RESET: 'reset',<tr><td><a name="l1186" href="#l1186">1186</a><td><tr><td><a name="l1187" href="#l1187">1187</a><td> /** Emitted whenever a new task has been scheduled. */<tr><td><a name="l1188" href="#l1188">1188</a><td> SCHEDULE_TASK: 'scheduleTask',<tr><td><a name="l1189" href="#l1189">1189</a><td><tr><td><a name="l1190" href="#l1190">1190</a><td> /**<tr><td><a name="l1191" href="#l1191">1191</a><td> * Emitted whenever a control flow aborts due to an unhandled promise<tr><td><a name="l1192" href="#l1192">1192</a><td> * rejection. This event will be emitted along with the offending rejection<tr><td><a name="l1193" href="#l1193">1193</a><td> * reason. Upon emitting this event, the control flow will empty its task<tr><td><a name="l1194" href="#l1194">1194</a><td> * queue and revert to its initial state.<tr><td><a name="l1195" href="#l1195">1195</a><td> */<tr><td><a name="l1196" href="#l1196">1196</a><td> UNCAUGHT_EXCEPTION: 'uncaughtException'<tr><td><a name="l1197" href="#l1197">1197</a><td>};<tr><td><a name="l1198" href="#l1198">1198</a><td><tr><td><a name="l1199" href="#l1199">1199</a><td><tr><td><a name="l1200" href="#l1200">1200</a><td>/**<tr><td><a name="l1201" href="#l1201">1201</a><td> * How often, in milliseconds, the event loop should run.<tr><td><a name="l1202" href="#l1202">1202</a><td> * @type {number}<tr><td><a name="l1203" href="#l1203">1203</a><td> * @const<tr><td><a name="l1204" href="#l1204">1204</a><td> */<tr><td><a name="l1205" href="#l1205">1205</a><td>webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10;<tr><td><a name="l1206" href="#l1206">1206</a><td><tr><td><a name="l1207" href="#l1207">1207</a><td><tr><td><a name="l1208" href="#l1208">1208</a><td>/**<tr><td><a name="l1209" href="#l1209">1209</a><td> * Resets this instance, clearing its queue and removing all event listeners.<tr><td><a name="l1210" href="#l1210">1210</a><td> */<tr><td><a name="l1211" href="#l1211">1211</a><td>webdriver.promise.ControlFlow.prototype.reset = function() {<tr><td><a name="l1212" href="#l1212">1212</a><td> this.activeFrame_ = null;<tr><td><a name="l1213" href="#l1213">1213</a><td> this.clearHistory();<tr><td><a name="l1214" href="#l1214">1214</a><td> this.emit(webdriver.promise.ControlFlow.EventType.RESET);<tr><td><a name="l1215" href="#l1215">1215</a><td> this.removeAllListeners();<tr><td><a name="l1216" href="#l1216">1216</a><td> this.cancelShutdown_();<tr><td><a name="l1217" href="#l1217">1217</a><td> this.cancelEventLoop_();<tr><td><a name="l1218" href="#l1218">1218</a><td>};<tr><td><a name="l1219" href="#l1219">1219</a><td><tr><td><a name="l1220" href="#l1220">1220</a><td><tr><td><a name="l1221" href="#l1221">1221</a><td>/**<tr><td><a name="l1222" href="#l1222">1222</a><td> * Returns a summary of the recent task activity for this instance. This<tr><td><a name="l1223" href="#l1223">1223</a><td> * includes the most recently completed task, as well as any parent tasks. In<tr><td><a name="l1224" href="#l1224">1224</a><td> * the returned summary, the task at index N is considered a sub-task of the<tr><td><a name="l1225" href="#l1225">1225</a><td> * task at index N+1.<tr><td><a name="l1226" href="#l1226">1226</a><td> * @return {!Array.<string>} A summary of this instance's recent task<tr><td><a name="l1227" href="#l1227">1227</a><td> * activity.<tr><td><a name="l1228" href="#l1228">1228</a><td> */<tr><td><a name="l1229" href="#l1229">1229</a><td>webdriver.promise.ControlFlow.prototype.getHistory = function() {<tr><td><a name="l1230" href="#l1230">1230</a><td> var pendingTasks = [];<tr><td><a name="l1231" href="#l1231">1231</a><td> var currentFrame = this.activeFrame_;<tr><td><a name="l1232" href="#l1232">1232</a><td> while (currentFrame) {<tr><td><a name="l1233" href="#l1233">1233</a><td> var task = currentFrame.getPendingTask();<tr><td><a name="l1234" href="#l1234">1234</a><td> if (task) {<tr><td><a name="l1235" href="#l1235">1235</a><td> pendingTasks.push(task);<tr><td><a name="l1236" href="#l1236">1236</a><td> }<tr><td><a name="l1237" href="#l1237">1237</a><td> // A frame's parent node will always be another frame.<tr><td><a name="l1238" href="#l1238">1238</a><td> currentFrame =<tr><td><a name="l1239" href="#l1239">1239</a><td> /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent());<tr><td><a name="l1240" href="#l1240">1240</a><td> }<tr><td><a name="l1241" href="#l1241">1241</a><td><tr><td><a name="l1242" href="#l1242">1242</a><td> var fullHistory = goog.array.concat(this.history_, pendingTasks);<tr><td><a name="l1243" href="#l1243">1243</a><td> return goog.array.map(fullHistory, function(task) {<tr><td><a name="l1244" href="#l1244">1244</a><td> return task.toString();<tr><td><a name="l1245" href="#l1245">1245</a><td> });<tr><td><a name="l1246" href="#l1246">1246</a><td>};<tr><td><a name="l1247" href="#l1247">1247</a><td><tr><td><a name="l1248" href="#l1248">1248</a><td><tr><td><a name="l1249" href="#l1249">1249</a><td>/** Clears this instance's task history. */<tr><td><a name="l1250" href="#l1250">1250</a><td>webdriver.promise.ControlFlow.prototype.clearHistory = function() {<tr><td><a name="l1251" href="#l1251">1251</a><td> this.history_ = [];<tr><td><a name="l1252" href="#l1252">1252</a><td>};<tr><td><a name="l1253" href="#l1253">1253</a><td><tr><td><a name="l1254" href="#l1254">1254</a><td><tr><td><a name="l1255" href="#l1255">1255</a><td>/**<tr><td><a name="l1256" href="#l1256">1256</a><td> * Removes a completed task from this instance's history record. If any<tr><td><a name="l1257" href="#l1257">1257</a><td> * tasks remain from aborted frames, those will be removed as well.<tr><td><a name="l1258" href="#l1258">1258</a><td> * @private<tr><td><a name="l1259" href="#l1259">1259</a><td> */<tr><td><a name="l1260" href="#l1260">1260</a><td>webdriver.promise.ControlFlow.prototype.trimHistory_ = function() {<tr><td><a name="l1261" href="#l1261">1261</a><td> if (this.numAbortedFrames_) {<tr><td><a name="l1262" href="#l1262">1262</a><td> goog.array.splice(this.history_,<tr><td><a name="l1263" href="#l1263">1263</a><td> this.history_.length - this.numAbortedFrames_,<tr><td><a name="l1264" href="#l1264">1264</a><td> this.numAbortedFrames_);<tr><td><a name="l1265" href="#l1265">1265</a><td> this.numAbortedFrames_ = 0;<tr><td><a name="l1266" href="#l1266">1266</a><td> }<tr><td><a name="l1267" href="#l1267">1267</a><td> this.history_.pop();<tr><td><a name="l1268" href="#l1268">1268</a><td>};<tr><td><a name="l1269" href="#l1269">1269</a><td><tr><td><a name="l1270" href="#l1270">1270</a><td><tr><td><a name="l1271" href="#l1271">1271</a><td>/**<tr><td><a name="l1272" href="#l1272">1272</a><td> * Property used to track whether an error has been annotated by<tr><td><a name="l1273" href="#l1273">1273</a><td> * {@link webdriver.promise.ControlFlow#annotateError}.<tr><td><a name="l1274" href="#l1274">1274</a><td> * @private {string}<tr><td><a name="l1275" href="#l1275">1275</a><td> * @const<tr><td><a name="l1276" href="#l1276">1276</a><td> */<tr><td><a name="l1277" href="#l1277">1277</a><td>webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ =<tr><td><a name="l1278" href="#l1278">1278</a><td> 'webdriver_promise_error_';<tr><td><a name="l1279" href="#l1279">1279</a><td><tr><td><a name="l1280" href="#l1280">1280</a><td><tr><td><a name="l1281" href="#l1281">1281</a><td>/**<tr><td><a name="l1282" href="#l1282">1282</a><td> * Appends a summary of this instance's recent task history to the given<tr><td><a name="l1283" href="#l1283">1283</a><td> * error's stack trace. This function will also ensure the error's stack trace<tr><td><a name="l1284" href="#l1284">1284</a><td> * is in canonical form.<tr><td><a name="l1285" href="#l1285">1285</a><td> * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate.<tr><td><a name="l1286" href="#l1286">1286</a><td> * @return {!(Error|goog.testing.JsUnitException)} The annotated error.<tr><td><a name="l1287" href="#l1287">1287</a><td> */<tr><td><a name="l1288" href="#l1288">1288</a><td>webdriver.promise.ControlFlow.prototype.annotateError = function(e) {<tr><td><a name="l1289" href="#l1289">1289</a><td> if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) {<tr><td><a name="l1290" href="#l1290">1290</a><td> return e;<tr><td><a name="l1291" href="#l1291">1291</a><td> }<tr><td><a name="l1292" href="#l1292">1292</a><td><tr><td><a name="l1293" href="#l1293">1293</a><td> var history = this.getHistory();<tr><td><a name="l1294" href="#l1294">1294</a><td> if (history.length) {<tr><td><a name="l1295" href="#l1295">1295</a><td> e = webdriver.stacktrace.format(e);<tr><td><a name="l1296" href="#l1296">1296</a><td><tr><td><a name="l1297" href="#l1297">1297</a><td> /** @type {!Error} */(e).stack += [<tr><td><a name="l1298" href="#l1298">1298</a><td> '\n==== async task ====\n',<tr><td><a name="l1299" href="#l1299">1299</a><td> history.join('\n==== async task ====\n')<tr><td><a name="l1300" href="#l1300">1300</a><td> ].join('');<tr><td><a name="l1301" href="#l1301">1301</a><td><tr><td><a name="l1302" href="#l1302">1302</a><td> e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true;<tr><td><a name="l1303" href="#l1303">1303</a><td> }<tr><td><a name="l1304" href="#l1304">1304</a><td><tr><td><a name="l1305" href="#l1305">1305</a><td> return e;<tr><td><a name="l1306" href="#l1306">1306</a><td>};<tr><td><a name="l1307" href="#l1307">1307</a><td><tr><td><a name="l1308" href="#l1308">1308</a><td><tr><td><a name="l1309" href="#l1309">1309</a><td>/**<tr><td><a name="l1310" href="#l1310">1310</a><td> * @return {string} The scheduled tasks still pending with this instance.<tr><td><a name="l1311" href="#l1311">1311</a><td> */<tr><td><a name="l1312" href="#l1312">1312</a><td>webdriver.promise.ControlFlow.prototype.getSchedule = function() {<tr><td><a name="l1313" href="#l1313">1313</a><td> return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]';<tr><td><a name="l1314" href="#l1314">1314</a><td>};<tr><td><a name="l1315" href="#l1315">1315</a><td><tr><td><a name="l1316" href="#l1316">1316</a><td><tr><td><a name="l1317" href="#l1317">1317</a><td>/**<tr><td><a name="l1318" href="#l1318">1318</a><td> * Schedules a task for execution. If there is nothing currently in the<tr><td><a name="l1319" href="#l1319">1319</a><td> * queue, the task will be executed in the next turn of the event loop. If<tr><td><a name="l1320" href="#l1320">1320</a><td> * the task function is a generator, the task will be executed using<tr><td><a name="l1321" href="#l1321">1321</a><td> * {@link webdriver.promise.consume}.<tr><td><a name="l1322" href="#l1322">1322</a><td> *<tr><td><a name="l1323" href="#l1323">1323</a><td> * @param {function(): (T|webdriver.promise.Promise.<T>)} fn The function to<tr><td><a name="l1324" href="#l1324">1324</a><td> * call to start the task. If the function returns a<tr><td><a name="l1325" href="#l1325">1325</a><td> * {@link webdriver.promise.Promise}, this instance will wait for it to be<tr><td><a name="l1326" href="#l1326">1326</a><td> * resolved before starting the next task.<tr><td><a name="l1327" href="#l1327">1327</a><td> * @param {string=} opt_description A description of the task.<tr><td><a name="l1328" href="#l1328">1328</a><td> * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved<tr><td><a name="l1329" href="#l1329">1329</a><td> * with the result of the action.<tr><td><a name="l1330" href="#l1330">1330</a><td> * @template T<tr><td><a name="l1331" href="#l1331">1331</a><td> */<tr><td><a name="l1332" href="#l1332">1332</a><td>webdriver.promise.ControlFlow.prototype.execute = function(<tr><td><a name="l1333" href="#l1333">1333</a><td> fn, opt_description) {<tr><td><a name="l1334" href="#l1334">1334</a><td> if (webdriver.promise.isGenerator(fn)) {<tr><td><a name="l1335" href="#l1335">1335</a><td> fn = goog.partial(webdriver.promise.consume, fn);<tr><td><a name="l1336" href="#l1336">1336</a><td> }<tr><td><a name="l1337" href="#l1337">1337</a><td><tr><td><a name="l1338" href="#l1338">1338</a><td> this.cancelShutdown_();<tr><td><a name="l1339" href="#l1339">1339</a><td><tr><td><a name="l1340" href="#l1340">1340</a><td> if (!this.activeFrame_) {<tr><td><a name="l1341" href="#l1341">1341</a><td> this.activeFrame_ = new webdriver.promise.Frame_(this);<tr><td><a name="l1342" href="#l1342">1342</a><td> }<tr><td><a name="l1343" href="#l1343">1343</a><td><tr><td><a name="l1344" href="#l1344">1344</a><td> // Trim an extra frame off the generated stack trace for the call to this<tr><td><a name="l1345" href="#l1345">1345</a><td> // function.<tr><td><a name="l1346" href="#l1346">1346</a><td> var snapshot = new webdriver.stacktrace.Snapshot(1);<tr><td><a name="l1347" href="#l1347">1347</a><td> var task = new webdriver.promise.Task_(<tr><td><a name="l1348" href="#l1348">1348</a><td> this, fn, opt_description || '', snapshot);<tr><td><a name="l1349" href="#l1349">1349</a><td> var scheduleIn = this.schedulingFrame_ || this.activeFrame_;<tr><td><a name="l1350" href="#l1350">1350</a><td> scheduleIn.addChild(task);<tr><td><a name="l1351" href="#l1351">1351</a><td><tr><td><a name="l1352" href="#l1352">1352</a><td> this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description);<tr><td><a name="l1353" href="#l1353">1353</a><td><tr><td><a name="l1354" href="#l1354">1354</a><td> this.scheduleEventLoopStart_();<tr><td><a name="l1355" href="#l1355">1355</a><td> return task.promise;<tr><td><a name="l1356" href="#l1356">1356</a><td>};<tr><td><a name="l1357" href="#l1357">1357</a><td><tr><td><a name="l1358" href="#l1358">1358</a><td><tr><td><a name="l1359" href="#l1359">1359</a><td>/**<tr><td><a name="l1360" href="#l1360">1360</a><td> * Inserts a {@code setTimeout} into the command queue. This is equivalent to<tr><td><a name="l1361" href="#l1361">1361</a><td> * a thread sleep in a synchronous programming language.<tr><td><a name="l1362" href="#l1362">1362</a><td> *<tr><td><a name="l1363" href="#l1363">1363</a><td> * @param {number} ms The timeout delay, in milliseconds.<tr><td><a name="l1364" href="#l1364">1364</a><td> * @param {string=} opt_description A description to accompany the timeout.<tr><td><a name="l1365" href="#l1365">1365</a><td> * @return {!webdriver.promise.Promise} A promise that will be resolved with<tr><td><a name="l1366" href="#l1366">1366</a><td> * the result of the action.<tr><td><a name="l1367" href="#l1367">1367</a><td> */<tr><td><a name="l1368" href="#l1368">1368</a><td>webdriver.promise.ControlFlow.prototype.timeout = function(<tr><td><a name="l1369" href="#l1369">1369</a><td> ms, opt_description) {<tr><td><a name="l1370" href="#l1370">1370</a><td> return this.execute(function() {<tr><td><a name="l1371" href="#l1371">1371</a><td> return webdriver.promise.delayed(ms);<tr><td><a name="l1372" href="#l1372">1372</a><td> }, opt_description);<tr><td><a name="l1373" href="#l1373">1373</a><td>};<tr><td><a name="l1374" href="#l1374">1374</a><td><tr><td><a name="l1375" href="#l1375">1375</a><td><tr><td><a name="l1376" href="#l1376">1376</a><td>/**<tr><td><a name="l1377" href="#l1377">1377</a><td> * Schedules a task that shall wait for a condition to hold. Each condition<tr><td><a name="l1378" href="#l1378">1378</a><td> * function may return any value, but it will always be evaluated as a boolean.<tr><td><a name="l1379" href="#l1379">1379</a><td> *<tr><td><a name="l1380" href="#l1380">1380</a><td> * <p>Condition functions may schedule sub-tasks with this instance, however,<tr><td><a name="l1381" href="#l1381">1381</a><td> * their execution time will be factored into whether a wait has timed out.<tr><td><a name="l1382" href="#l1382">1382</a><td> *<tr><td><a name="l1383" href="#l1383">1383</a><td> * <p>In the event a condition returns a Promise, the polling loop will wait for<tr><td><a name="l1384" href="#l1384">1384</a><td> * it to be resolved before evaluating whether the condition has been satisfied.<tr><td><a name="l1385" href="#l1385">1385</a><td> * The resolution time for a promise is factored into whether a wait has timed<tr><td><a name="l1386" href="#l1386">1386</a><td> * out.<tr><td><a name="l1387" href="#l1387">1387</a><td> *<tr><td><a name="l1388" href="#l1388">1388</a><td> * <p>If the condition function throws, or returns a rejected promise, the<tr><td><a name="l1389" href="#l1389">1389</a><td> * wait task will fail.<tr><td><a name="l1390" href="#l1390">1390</a><td> *<tr><td><a name="l1391" href="#l1391">1391</a><td> * @param {function(): T} condition The condition function to poll.<tr><td><a name="l1392" href="#l1392">1392</a><td> * @param {number} timeout How long to wait, in milliseconds, for the condition<tr><td><a name="l1393" href="#l1393">1393</a><td> * to hold before timing out.<tr><td><a name="l1394" href="#l1394">1394</a><td> * @param {string=} opt_message An optional error message to include if the<tr><td><a name="l1395" href="#l1395">1395</a><td> * wait times out; defaults to the empty string.<tr><td><a name="l1396" href="#l1396">1396</a><td> * @return {!webdriver.promise.Promise.<T>} A promise that will be fulfilled<tr><td><a name="l1397" href="#l1397">1397</a><td> * when the condition has been satisified. The promise shall be rejected if<tr><td><a name="l1398" href="#l1398">1398</a><td> * the wait times out waiting for the condition.<tr><td><a name="l1399" href="#l1399">1399</a><td> * @template T<tr><td><a name="l1400" href="#l1400">1400</a><td> */<tr><td><a name="l1401" href="#l1401">1401</a><td>webdriver.promise.ControlFlow.prototype.wait = function(<tr><td><a name="l1402" href="#l1402">1402</a><td> condition, timeout, opt_message) {<tr><td><a name="l1403" href="#l1403">1403</a><td> var sleep = Math.min(timeout, 100);<tr><td><a name="l1404" href="#l1404">1404</a><td> var self = this;<tr><td><a name="l1405" href="#l1405">1405</a><td><tr><td><a name="l1406" href="#l1406">1406</a><td> if (webdriver.promise.isGenerator(condition)) {<tr><td><a name="l1407" href="#l1407">1407</a><td> condition = goog.partial(webdriver.promise.consume, condition);<tr><td><a name="l1408" href="#l1408">1408</a><td> }<tr><td><a name="l1409" href="#l1409">1409</a><td><tr><td><a name="l1410" href="#l1410">1410</a><td> return this.execute(function() {<tr><td><a name="l1411" href="#l1411">1411</a><td> var startTime = goog.now();<tr><td><a name="l1412" href="#l1412">1412</a><td> var waitResult = new webdriver.promise.Deferred();<tr><td><a name="l1413" href="#l1413">1413</a><td> var waitFrame = self.activeFrame_;<tr><td><a name="l1414" href="#l1414">1414</a><td> waitFrame.isWaiting = true;<tr><td><a name="l1415" href="#l1415">1415</a><td> pollCondition();<tr><td><a name="l1416" href="#l1416">1416</a><td> return waitResult.promise;<tr><td><a name="l1417" href="#l1417">1417</a><td><tr><td><a name="l1418" href="#l1418">1418</a><td> function pollCondition() {<tr><td><a name="l1419" href="#l1419">1419</a><td> self.runInNewFrame_(condition, function(value) {<tr><td><a name="l1420" href="#l1420">1420</a><td> var elapsed = goog.now() - startTime;<tr><td><a name="l1421" href="#l1421">1421</a><td> if (!!value) {<tr><td><a name="l1422" href="#l1422">1422</a><td> waitFrame.isWaiting = false;<tr><td><a name="l1423" href="#l1423">1423</a><td> waitResult.fulfill(value);<tr><td><a name="l1424" href="#l1424">1424</a><td> } else if (elapsed >= timeout) {<tr><td><a name="l1425" href="#l1425">1425</a><td> waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') +<tr><td><a name="l1426" href="#l1426">1426</a><td> 'Wait timed out after ' + elapsed + 'ms'));<tr><td><a name="l1427" href="#l1427">1427</a><td> } else {<tr><td><a name="l1428" href="#l1428">1428</a><td> self.timer.setTimeout(pollCondition, sleep);<tr><td><a name="l1429" href="#l1429">1429</a><td> }<tr><td><a name="l1430" href="#l1430">1430</a><td> }, waitResult.reject, true);<tr><td><a name="l1431" href="#l1431">1431</a><td> }<tr><td><a name="l1432" href="#l1432">1432</a><td> }, opt_message);<tr><td><a name="l1433" href="#l1433">1433</a><td>};<tr><td><a name="l1434" href="#l1434">1434</a><td><tr><td><a name="l1435" href="#l1435">1435</a><td><tr><td><a name="l1436" href="#l1436">1436</a><td>/**<tr><td><a name="l1437" href="#l1437">1437</a><td> * Schedules a task that will wait for another promise to resolve. The resolved<tr><td><a name="l1438" href="#l1438">1438</a><td> * promise's value will be returned as the task result.<tr><td><a name="l1439" href="#l1439">1439</a><td> * @param {!webdriver.promise.Promise} promise The promise to wait on.<tr><td><a name="l1440" href="#l1440">1440</a><td> * @return {!webdriver.promise.Promise} A promise that will resolve when the<tr><td><a name="l1441" href="#l1441">1441</a><td> * task has completed.<tr><td><a name="l1442" href="#l1442">1442</a><td> */<tr><td><a name="l1443" href="#l1443">1443</a><td>webdriver.promise.ControlFlow.prototype.await = function(promise) {<tr><td><a name="l1444" href="#l1444">1444</a><td> return this.execute(function() {<tr><td><a name="l1445" href="#l1445">1445</a><td> return promise;<tr><td><a name="l1446" href="#l1446">1446</a><td> });<tr><td><a name="l1447" href="#l1447">1447</a><td>};<tr><td><a name="l1448" href="#l1448">1448</a><td><tr><td><a name="l1449" href="#l1449">1449</a><td><tr><td><a name="l1450" href="#l1450">1450</a><td>/**<tr><td><a name="l1451" href="#l1451">1451</a><td> * Schedules the interval for this instance's event loop, if necessary.<tr><td><a name="l1452" href="#l1452">1452</a><td> * @private<tr><td><a name="l1453" href="#l1453">1453</a><td> */<tr><td><a name="l1454" href="#l1454">1454</a><td>webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {<tr><td><a name="l1455" href="#l1455">1455</a><td> if (!this.eventLoopId_) {<tr><td><a name="l1456" href="#l1456">1456</a><td> this.eventLoopId_ = this.timer.setInterval(<tr><td><a name="l1457" href="#l1457">1457</a><td> goog.bind(this.runEventLoop_, this),<tr><td><a name="l1458" href="#l1458">1458</a><td> webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY);<tr><td><a name="l1459" href="#l1459">1459</a><td> }<tr><td><a name="l1460" href="#l1460">1460</a><td>};<tr><td><a name="l1461" href="#l1461">1461</a><td><tr><td><a name="l1462" href="#l1462">1462</a><td><tr><td><a name="l1463" href="#l1463">1463</a><td>/**<tr><td><a name="l1464" href="#l1464">1464</a><td> * Cancels the event loop, if necessary.<tr><td><a name="l1465" href="#l1465">1465</a><td> * @private<tr><td><a name="l1466" href="#l1466">1466</a><td> */<tr><td><a name="l1467" href="#l1467">1467</a><td>webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() {<tr><td><a name="l1468" href="#l1468">1468</a><td> if (this.eventLoopId_) {<tr><td><a name="l1469" href="#l1469">1469</a><td> this.timer.clearInterval(this.eventLoopId_);<tr><td><a name="l1470" href="#l1470">1470</a><td> this.eventLoopId_ = null;<tr><td><a name="l1471" href="#l1471">1471</a><td> }<tr><td><a name="l1472" href="#l1472">1472</a><td>};<tr><td><a name="l1473" href="#l1473">1473</a><td><tr><td><a name="l1474" href="#l1474">1474</a><td><tr><td><a name="l1475" href="#l1475">1475</a><td>/**<tr><td><a name="l1476" href="#l1476">1476</a><td> * Executes the next task for the current frame. If the current frame has no<tr><td><a name="l1477" href="#l1477">1477</a><td> * more tasks, the frame's result will be resolved, returning control to the<tr><td><a name="l1478" href="#l1478">1478</a><td> * frame's creator. This will terminate the flow if the completed frame was at<tr><td><a name="l1479" href="#l1479">1479</a><td> * the top of the stack.<tr><td><a name="l1480" href="#l1480">1480</a><td> * @private<tr><td><a name="l1481" href="#l1481">1481</a><td> */<tr><td><a name="l1482" href="#l1482">1482</a><td>webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {<tr><td><a name="l1483" href="#l1483">1483</a><td> // If we get here and there are pending promise rejections, then those<tr><td><a name="l1484" href="#l1484">1484</a><td> // promises are queued up to run as soon as this (JS) event loop terminates.<tr><td><a name="l1485" href="#l1485">1485</a><td> // Short-circuit our loop to give those promises a chance to run. Otherwise,<tr><td><a name="l1486" href="#l1486">1486</a><td> // we might start a new task only to have it fail because of one of these<tr><td><a name="l1487" href="#l1487">1487</a><td> // pending rejections.<tr><td><a name="l1488" href="#l1488">1488</a><td> if (this.pendingRejections_) {<tr><td><a name="l1489" href="#l1489">1489</a><td> return;<tr><td><a name="l1490" href="#l1490">1490</a><td> }<tr><td><a name="l1491" href="#l1491">1491</a><td><tr><td><a name="l1492" href="#l1492">1492</a><td> // If the flow aborts due to an unhandled exception after we've scheduled<tr><td><a name="l1493" href="#l1493">1493</a><td> // another turn of the execution loop, we can end up in here with no tasks<tr><td><a name="l1494" href="#l1494">1494</a><td> // left. This is OK, just quietly return.<tr><td><a name="l1495" href="#l1495">1495</a><td> if (!this.activeFrame_) {<tr><td><a name="l1496" href="#l1496">1496</a><td> this.commenceShutdown_();<tr><td><a name="l1497" href="#l1497">1497</a><td> return;<tr><td><a name="l1498" href="#l1498">1498</a><td> }<tr><td><a name="l1499" href="#l1499">1499</a><td><tr><td><a name="l1500" href="#l1500">1500</a><td> var task;<tr><td><a name="l1501" href="#l1501">1501</a><td> if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) {<tr><td><a name="l1502" href="#l1502">1502</a><td> // Either the current frame is blocked on a pending task, or we don't have<tr><td><a name="l1503" href="#l1503">1503</a><td> // a task to finish because we've completed a frame. When completing a<tr><td><a name="l1504" href="#l1504">1504</a><td> // frame, we must abort the event loop to allow the frame's promise's<tr><td><a name="l1505" href="#l1505">1505</a><td> // callbacks to execute.<tr><td><a name="l1506" href="#l1506">1506</a><td> return;<tr><td><a name="l1507" href="#l1507">1507</a><td> }<tr><td><a name="l1508" href="#l1508">1508</a><td><tr><td><a name="l1509" href="#l1509">1509</a><td> var activeFrame = this.activeFrame_;<tr><td><a name="l1510" href="#l1510">1510</a><td> activeFrame.setPendingTask(task);<tr><td><a name="l1511" href="#l1511">1511</a><td> var markTaskComplete = goog.bind(function() {<tr><td><a name="l1512" href="#l1512">1512</a><td> this.history_.push(/** @type {!webdriver.promise.Task_} */ (task));<tr><td><a name="l1513" href="#l1513">1513</a><td> activeFrame.setPendingTask(null);<tr><td><a name="l1514" href="#l1514">1514</a><td> }, this);<tr><td><a name="l1515" href="#l1515">1515</a><td><tr><td><a name="l1516" href="#l1516">1516</a><td> this.trimHistory_();<tr><td><a name="l1517" href="#l1517">1517</a><td> var self = this;<tr><td><a name="l1518" href="#l1518">1518</a><td> this.runInNewFrame_(task.execute, function(result) {<tr><td><a name="l1519" href="#l1519">1519</a><td> markTaskComplete();<tr><td><a name="l1520" href="#l1520">1520</a><td> task.fulfill(result);<tr><td><a name="l1521" href="#l1521">1521</a><td> }, function(error) {<tr><td><a name="l1522" href="#l1522">1522</a><td> markTaskComplete();<tr><td><a name="l1523" href="#l1523">1523</a><td><tr><td><a name="l1524" href="#l1524">1524</a><td> if (!webdriver.promise.isError_(error) &&<tr><td><a name="l1525" href="#l1525">1525</a><td> !webdriver.promise.isPromise(error)) {<tr><td><a name="l1526" href="#l1526">1526</a><td> error = Error(error);<tr><td><a name="l1527" href="#l1527">1527</a><td> }<tr><td><a name="l1528" href="#l1528">1528</a><td><tr><td><a name="l1529" href="#l1529">1529</a><td> task.reject(self.annotateError(/** @type {!Error} */ (error)));<tr><td><a name="l1530" href="#l1530">1530</a><td> }, true);<tr><td><a name="l1531" href="#l1531">1531</a><td>};<tr><td><a name="l1532" href="#l1532">1532</a><td><tr><td><a name="l1533" href="#l1533">1533</a><td><tr><td><a name="l1534" href="#l1534">1534</a><td>/**<tr><td><a name="l1535" href="#l1535">1535</a><td> * @return {webdriver.promise.Task_} The next task to execute, or<tr><td><a name="l1536" href="#l1536">1536</a><td> * {@code null} if a frame was resolved.<tr><td><a name="l1537" href="#l1537">1537</a><td> * @private<tr><td><a name="l1538" href="#l1538">1538</a><td> */<tr><td><a name="l1539" href="#l1539">1539</a><td>webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {<tr><td><a name="l1540" href="#l1540">1540</a><td> var frame = this.activeFrame_;<tr><td><a name="l1541" href="#l1541">1541</a><td> var firstChild = frame.getFirstChild();<tr><td><a name="l1542" href="#l1542">1542</a><td> if (!firstChild) {<tr><td><a name="l1543" href="#l1543">1543</a><td> if (!frame.isWaiting) {<tr><td><a name="l1544" href="#l1544">1544</a><td> this.resolveFrame_(frame);<tr><td><a name="l1545" href="#l1545">1545</a><td> }<tr><td><a name="l1546" href="#l1546">1546</a><td> return null;<tr><td><a name="l1547" href="#l1547">1547</a><td> }<tr><td><a name="l1548" href="#l1548">1548</a><td><tr><td><a name="l1549" href="#l1549">1549</a><td> if (firstChild instanceof webdriver.promise.Frame_) {<tr><td><a name="l1550" href="#l1550">1550</a><td> this.activeFrame_ = firstChild;<tr><td><a name="l1551" href="#l1551">1551</a><td> return this.getNextTask_();<tr><td><a name="l1552" href="#l1552">1552</a><td> }<tr><td><a name="l1553" href="#l1553">1553</a><td><tr><td><a name="l1554" href="#l1554">1554</a><td> frame.removeChild(firstChild);<tr><td><a name="l1555" href="#l1555">1555</a><td> return firstChild;<tr><td><a name="l1556" href="#l1556">1556</a><td>};<tr><td><a name="l1557" href="#l1557">1557</a><td><tr><td><a name="l1558" href="#l1558">1558</a><td><tr><td><a name="l1559" href="#l1559">1559</a><td>/**<tr><td><a name="l1560" href="#l1560">1560</a><td> * @param {!webdriver.promise.Frame_} frame The frame to resolve.<tr><td><a name="l1561" href="#l1561">1561</a><td> * @private<tr><td><a name="l1562" href="#l1562">1562</a><td> */<tr><td><a name="l1563" href="#l1563">1563</a><td>webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) {<tr><td><a name="l1564" href="#l1564">1564</a><td> if (this.activeFrame_ === frame) {<tr><td><a name="l1565" href="#l1565">1565</a><td> // Frame parent is always another frame, but the compiler is not smart<tr><td><a name="l1566" href="#l1566">1566</a><td> // enough to recognize this.<tr><td><a name="l1567" href="#l1567">1567</a><td> this.activeFrame_ =<tr><td><a name="l1568" href="#l1568">1568</a><td> /** @type {webdriver.promise.Frame_} */ (frame.getParent());<tr><td><a name="l1569" href="#l1569">1569</a><td> }<tr><td><a name="l1570" href="#l1570">1570</a><td><tr><td><a name="l1571" href="#l1571">1571</a><td> if (frame.getParent()) {<tr><td><a name="l1572" href="#l1572">1572</a><td> frame.getParent().removeChild(frame);<tr><td><a name="l1573" href="#l1573">1573</a><td> }<tr><td><a name="l1574" href="#l1574">1574</a><td> this.trimHistory_();<tr><td><a name="l1575" href="#l1575">1575</a><td> frame.close();<tr><td><a name="l1576" href="#l1576">1576</a><td><tr><td><a name="l1577" href="#l1577">1577</a><td> if (!this.activeFrame_) {<tr><td><a name="l1578" href="#l1578">1578</a><td> this.commenceShutdown_();<tr><td><a name="l1579" href="#l1579">1579</a><td> }<tr><td><a name="l1580" href="#l1580">1580</a><td>};<tr><td><a name="l1581" href="#l1581">1581</a><td><tr><td><a name="l1582" href="#l1582">1582</a><td><tr><td><a name="l1583" href="#l1583">1583</a><td>/**<tr><td><a name="l1584" href="#l1584">1584</a><td> * Aborts the current frame. The frame, and all of the tasks scheduled within it<tr><td><a name="l1585" href="#l1585">1585</a><td> * will be discarded. If this instance does not have an active frame, it will<tr><td><a name="l1586" href="#l1586">1586</a><td> * immediately terminate all execution.<tr><td><a name="l1587" href="#l1587">1587</a><td> * @param {*} error The reason the frame is being aborted; typically either<tr><td><a name="l1588" href="#l1588">1588</a><td> * an Error or string.<tr><td><a name="l1589" href="#l1589">1589</a><td> * @private<tr><td><a name="l1590" href="#l1590">1590</a><td> */<tr><td><a name="l1591" href="#l1591">1591</a><td>webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) {<tr><td><a name="l1592" href="#l1592">1592</a><td> // Annotate the error value if it is Error-like.<tr><td><a name="l1593" href="#l1593">1593</a><td> if (webdriver.promise.isError_(error)) {<tr><td><a name="l1594" href="#l1594">1594</a><td> this.annotateError(/** @type {!Error} */ (error));<tr><td><a name="l1595" href="#l1595">1595</a><td> }<tr><td><a name="l1596" href="#l1596">1596</a><td> this.numAbortedFrames_++;<tr><td><a name="l1597" href="#l1597">1597</a><td><tr><td><a name="l1598" href="#l1598">1598</a><td> if (!this.activeFrame_) {<tr><td><a name="l1599" href="#l1599">1599</a><td> this.abortNow_(error);<tr><td><a name="l1600" href="#l1600">1600</a><td> return;<tr><td><a name="l1601" href="#l1601">1601</a><td> }<tr><td><a name="l1602" href="#l1602">1602</a><td><tr><td><a name="l1603" href="#l1603">1603</a><td> // Frame parent is always another frame, but the compiler is not smart<tr><td><a name="l1604" href="#l1604">1604</a><td> // enough to recognize this.<tr><td><a name="l1605" href="#l1605">1605</a><td> var parent = /** @type {webdriver.promise.Frame_} */ (<tr><td><a name="l1606" href="#l1606">1606</a><td> this.activeFrame_.getParent());<tr><td><a name="l1607" href="#l1607">1607</a><td> if (parent) {<tr><td><a name="l1608" href="#l1608">1608</a><td> parent.removeChild(this.activeFrame_);<tr><td><a name="l1609" href="#l1609">1609</a><td> }<tr><td><a name="l1610" href="#l1610">1610</a><td><tr><td><a name="l1611" href="#l1611">1611</a><td> var frame = this.activeFrame_;<tr><td><a name="l1612" href="#l1612">1612</a><td> this.activeFrame_ = parent;<tr><td><a name="l1613" href="#l1613">1613</a><td> frame.abort(error);<tr><td><a name="l1614" href="#l1614">1614</a><td>};<tr><td><a name="l1615" href="#l1615">1615</a><td><tr><td><a name="l1616" href="#l1616">1616</a><td><tr><td><a name="l1617" href="#l1617">1617</a><td>/**<tr><td><a name="l1618" href="#l1618">1618</a><td> * Executes a function in a new frame. If the function does not schedule any new<tr><td><a name="l1619" href="#l1619">1619</a><td> * tasks, the frame will be discarded and the function's result returned<tr><td><a name="l1620" href="#l1620">1620</a><td> * immediately. Otherwise, a promise will be returned. This promise will be<tr><td><a name="l1621" href="#l1621">1621</a><td> * resolved with the function's result once all of the tasks scheduled within<tr><td><a name="l1622" href="#l1622">1622</a><td> * the function have been completed. If the function's frame is aborted, the<tr><td><a name="l1623" href="#l1623">1623</a><td> * returned promise will be rejected.<tr><td><a name="l1624" href="#l1624">1624</a><td> *<tr><td><a name="l1625" href="#l1625">1625</a><td> * @param {!Function} fn The function to execute.<tr><td><a name="l1626" href="#l1626">1626</a><td> * @param {function(*)} callback The function to call with a successful result.<tr><td><a name="l1627" href="#l1627">1627</a><td> * @param {function(*)} errback The function to call if there is an error.<tr><td><a name="l1628" href="#l1628">1628</a><td> * @param {boolean=} opt_activate Whether the active frame should be updated to<tr><td><a name="l1629" href="#l1629">1629</a><td> * the newly created frame so tasks are treated as sub-tasks.<tr><td><a name="l1630" href="#l1630">1630</a><td> * @private<tr><td><a name="l1631" href="#l1631">1631</a><td> */<tr><td><a name="l1632" href="#l1632">1632</a><td>webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(<tr><td><a name="l1633" href="#l1633">1633</a><td> fn, callback, errback, opt_activate) {<tr><td><a name="l1634" href="#l1634">1634</a><td> var newFrame = new webdriver.promise.Frame_(this),<tr><td><a name="l1635" href="#l1635">1635</a><td> self = this,<tr><td><a name="l1636" href="#l1636">1636</a><td> oldFrame = this.activeFrame_;<tr><td><a name="l1637" href="#l1637">1637</a><td><tr><td><a name="l1638" href="#l1638">1638</a><td> try {<tr><td><a name="l1639" href="#l1639">1639</a><td> if (!this.activeFrame_) {<tr><td><a name="l1640" href="#l1640">1640</a><td> this.activeFrame_ = newFrame;<tr><td><a name="l1641" href="#l1641">1641</a><td> } else {<tr><td><a name="l1642" href="#l1642">1642</a><td> this.activeFrame_.addChild(newFrame);<tr><td><a name="l1643" href="#l1643">1643</a><td> }<tr><td><a name="l1644" href="#l1644">1644</a><td><tr><td><a name="l1645" href="#l1645">1645</a><td> // Activate the new frame to force tasks to be treated as sub-tasks of<tr><td><a name="l1646" href="#l1646">1646</a><td> // the parent frame.<tr><td><a name="l1647" href="#l1647">1647</a><td> if (opt_activate) {<tr><td><a name="l1648" href="#l1648">1648</a><td> this.activeFrame_ = newFrame;<tr><td><a name="l1649" href="#l1649">1649</a><td> }<tr><td><a name="l1650" href="#l1650">1650</a><td><tr><td><a name="l1651" href="#l1651">1651</a><td> try {<tr><td><a name="l1652" href="#l1652">1652</a><td> this.schedulingFrame_ = newFrame;<tr><td><a name="l1653" href="#l1653">1653</a><td> webdriver.promise.pushFlow_(this);<tr><td><a name="l1654" href="#l1654">1654</a><td> var result = fn();<tr><td><a name="l1655" href="#l1655">1655</a><td> } finally {<tr><td><a name="l1656" href="#l1656">1656</a><td> webdriver.promise.popFlow_();<tr><td><a name="l1657" href="#l1657">1657</a><td> this.schedulingFrame_ = null;<tr><td><a name="l1658" href="#l1658">1658</a><td> }<tr><td><a name="l1659" href="#l1659">1659</a><td> newFrame.isLocked_ = true;<tr><td><a name="l1660" href="#l1660">1660</a><td><tr><td><a name="l1661" href="#l1661">1661</a><td> // If there was nothing scheduled in the new frame we can discard the<tr><td><a name="l1662" href="#l1662">1662</a><td> // frame and return immediately.<tr><td><a name="l1663" href="#l1663">1663</a><td> if (!newFrame.children_.length) {<tr><td><a name="l1664" href="#l1664">1664</a><td> removeNewFrame();<tr><td><a name="l1665" href="#l1665">1665</a><td> webdriver.promise.asap(result, callback, errback);<tr><td><a name="l1666" href="#l1666">1666</a><td> return;<tr><td><a name="l1667" href="#l1667">1667</a><td> }<tr><td><a name="l1668" href="#l1668">1668</a><td><tr><td><a name="l1669" href="#l1669">1669</a><td> newFrame.onComplete = function() {<tr><td><a name="l1670" href="#l1670">1670</a><td> webdriver.promise.asap(result, callback, errback);<tr><td><a name="l1671" href="#l1671">1671</a><td> };<tr><td><a name="l1672" href="#l1672">1672</a><td><tr><td><a name="l1673" href="#l1673">1673</a><td> newFrame.onAbort = function(e) {<tr><td><a name="l1674" href="#l1674">1674</a><td> if (webdriver.promise.Thenable.isImplementation(result) &&<tr><td><a name="l1675" href="#l1675">1675</a><td> result.isPending()) {<tr><td><a name="l1676" href="#l1676">1676</a><td> result.cancel(e);<tr><td><a name="l1677" href="#l1677">1677</a><td> e = result;<tr><td><a name="l1678" href="#l1678">1678</a><td> }<tr><td><a name="l1679" href="#l1679">1679</a><td> errback(e);<tr><td><a name="l1680" href="#l1680">1680</a><td> };<tr><td><a name="l1681" href="#l1681">1681</a><td> } catch (ex) {<tr><td><a name="l1682" href="#l1682">1682</a><td> removeNewFrame(ex);<tr><td><a name="l1683" href="#l1683">1683</a><td> errback(ex);<tr><td><a name="l1684" href="#l1684">1684</a><td> }<tr><td><a name="l1685" href="#l1685">1685</a><td><tr><td><a name="l1686" href="#l1686">1686</a><td> /**<tr><td><a name="l1687" href="#l1687">1687</a><td> * @param {*=} opt_err If provided, the reason that the frame was removed.<tr><td><a name="l1688" href="#l1688">1688</a><td> */<tr><td><a name="l1689" href="#l1689">1689</a><td> function removeNewFrame(opt_err) {<tr><td><a name="l1690" href="#l1690">1690</a><td> var parent = newFrame.getParent();<tr><td><a name="l1691" href="#l1691">1691</a><td> if (parent) {<tr><td><a name="l1692" href="#l1692">1692</a><td> parent.removeChild(newFrame);<tr><td><a name="l1693" href="#l1693">1693</a><td> }<tr><td><a name="l1694" href="#l1694">1694</a><td><tr><td><a name="l1695" href="#l1695">1695</a><td> if (opt_err) {<tr><td><a name="l1696" href="#l1696">1696</a><td> newFrame.cancelRemainingTasks(<tr><td><a name="l1697" href="#l1697">1697</a><td> 'Tasks cancelled due to uncaught error: ' + opt_err);<tr><td><a name="l1698" href="#l1698">1698</a><td> }<tr><td><a name="l1699" href="#l1699">1699</a><td> self.activeFrame_ = oldFrame;<tr><td><a name="l1700" href="#l1700">1700</a><td> }<tr><td><a name="l1701" href="#l1701">1701</a><td>};<tr><td><a name="l1702" href="#l1702">1702</a><td><tr><td><a name="l1703" href="#l1703">1703</a><td><tr><td><a name="l1704" href="#l1704">1704</a><td>/**<tr><td><a name="l1705" href="#l1705">1705</a><td> * Commences the shutdown sequence for this instance. After one turn of the<tr><td><a name="l1706" href="#l1706">1706</a><td> * event loop, this object will emit the<tr><td><a name="l1707" href="#l1707">1707</a><td> * {@link webdriver.promise.ControlFlow.EventType.IDLE} event to signal<tr><td><a name="l1708" href="#l1708">1708</a><td> * listeners that it has completed. During this wait, if another task is<tr><td><a name="l1709" href="#l1709">1709</a><td> * scheduled, the shutdown will be aborted.<tr><td><a name="l1710" href="#l1710">1710</a><td> * @private<tr><td><a name="l1711" href="#l1711">1711</a><td> */<tr><td><a name="l1712" href="#l1712">1712</a><td>webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() {<tr><td><a name="l1713" href="#l1713">1713</a><td> if (!this.shutdownId_) {<tr><td><a name="l1714" href="#l1714">1714</a><td> // Go ahead and stop the event loop now. If we're in here, then there are<tr><td><a name="l1715" href="#l1715">1715</a><td> // no more frames with tasks to execute. If we waited to cancel the event<tr><td><a name="l1716" href="#l1716">1716</a><td> // loop in our timeout below, the event loop could trigger *before* the<tr><td><a name="l1717" href="#l1717">1717</a><td> // timeout, generating an error from there being no frames.<tr><td><a name="l1718" href="#l1718">1718</a><td> // If #execute is called before the timeout below fires, it will cancel<tr><td><a name="l1719" href="#l1719">1719</a><td> // the timeout and restart the event loop.<tr><td><a name="l1720" href="#l1720">1720</a><td> this.cancelEventLoop_();<tr><td><a name="l1721" href="#l1721">1721</a><td><tr><td><a name="l1722" href="#l1722">1722</a><td> var self = this;<tr><td><a name="l1723" href="#l1723">1723</a><td> self.shutdownId_ = self.timer.setTimeout(function() {<tr><td><a name="l1724" href="#l1724">1724</a><td> self.shutdownId_ = null;<tr><td><a name="l1725" href="#l1725">1725</a><td> self.emit(webdriver.promise.ControlFlow.EventType.IDLE);<tr><td><a name="l1726" href="#l1726">1726</a><td> }, 0);<tr><td><a name="l1727" href="#l1727">1727</a><td> }<tr><td><a name="l1728" href="#l1728">1728</a><td>};<tr><td><a name="l1729" href="#l1729">1729</a><td><tr><td><a name="l1730" href="#l1730">1730</a><td><tr><td><a name="l1731" href="#l1731">1731</a><td>/**<tr><td><a name="l1732" href="#l1732">1732</a><td> * Cancels the shutdown sequence if it is currently scheduled.<tr><td><a name="l1733" href="#l1733">1733</a><td> * @private<tr><td><a name="l1734" href="#l1734">1734</a><td> */<tr><td><a name="l1735" href="#l1735">1735</a><td>webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() {<tr><td><a name="l1736" href="#l1736">1736</a><td> if (this.shutdownId_) {<tr><td><a name="l1737" href="#l1737">1737</a><td> this.timer.clearTimeout(this.shutdownId_);<tr><td><a name="l1738" href="#l1738">1738</a><td> this.shutdownId_ = null;<tr><td><a name="l1739" href="#l1739">1739</a><td> }<tr><td><a name="l1740" href="#l1740">1740</a><td>};<tr><td><a name="l1741" href="#l1741">1741</a><td><tr><td><a name="l1742" href="#l1742">1742</a><td><tr><td><a name="l1743" href="#l1743">1743</a><td>/**<tr><td><a name="l1744" href="#l1744">1744</a><td> * Aborts this flow, abandoning all remaining tasks. If there are<tr><td><a name="l1745" href="#l1745">1745</a><td> * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the<tr><td><a name="l1746" href="#l1746">1746</a><td> * offending {@code error}, otherwise, the {@code error} will be rethrown to the<tr><td><a name="l1747" href="#l1747">1747</a><td> * global error handler.<tr><td><a name="l1748" href="#l1748">1748</a><td> * @param {*} error Object describing the error that caused the flow to<tr><td><a name="l1749" href="#l1749">1749</a><td> * abort; usually either an Error or string value.<tr><td><a name="l1750" href="#l1750">1750</a><td> * @private<tr><td><a name="l1751" href="#l1751">1751</a><td> */<tr><td><a name="l1752" href="#l1752">1752</a><td>webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) {<tr><td><a name="l1753" href="#l1753">1753</a><td> this.activeFrame_ = null;<tr><td><a name="l1754" href="#l1754">1754</a><td> this.cancelShutdown_();<tr><td><a name="l1755" href="#l1755">1755</a><td> this.cancelEventLoop_();<tr><td><a name="l1756" href="#l1756">1756</a><td><tr><td><a name="l1757" href="#l1757">1757</a><td> var listeners = this.listeners(<tr><td><a name="l1758" href="#l1758">1758</a><td> webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);<tr><td><a name="l1759" href="#l1759">1759</a><td> if (!listeners.length) {<tr><td><a name="l1760" href="#l1760">1760</a><td> this.timer.setTimeout(function() {<tr><td><a name="l1761" href="#l1761">1761</a><td> throw error;<tr><td><a name="l1762" href="#l1762">1762</a><td> }, 0);<tr><td><a name="l1763" href="#l1763">1763</a><td> } else {<tr><td><a name="l1764" href="#l1764">1764</a><td> this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,<tr><td><a name="l1765" href="#l1765">1765</a><td> error);<tr><td><a name="l1766" href="#l1766">1766</a><td> }<tr><td><a name="l1767" href="#l1767">1767</a><td>};<tr><td><a name="l1768" href="#l1768">1768</a><td><tr><td><a name="l1769" href="#l1769">1769</a><td><tr><td><a name="l1770" href="#l1770">1770</a><td><tr><td><a name="l1771" href="#l1771">1771</a><td>/**<tr><td><a name="l1772" href="#l1772">1772</a><td> * An execution frame within a {@link webdriver.promise.ControlFlow}. Each<tr><td><a name="l1773" href="#l1773">1773</a><td> * frame represents the execution context for either a<tr><td><a name="l1774" href="#l1774">1774</a><td> * {@link webdriver.promise.Task_} or a callback on a<tr><td><a name="l1775" href="#l1775">1775</a><td> * {@link webdriver.promise.Deferred}.<tr><td><a name="l1776" href="#l1776">1776</a><td> *<tr><td><a name="l1777" href="#l1777">1777</a><td> * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the<tr><td><a name="l1778" href="#l1778">1778</a><td> * items queued within it are given priority over child N+1.<tr><td><a name="l1779" href="#l1779">1779</a><td> *<tr><td><a name="l1780" href="#l1780">1780</a><td> * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs<tr><td><a name="l1781" href="#l1781">1781</a><td> * to.<tr><td><a name="l1782" href="#l1782">1782</a><td> * @constructor<tr><td><a name="l1783" href="#l1783">1783</a><td> * @private<tr><td><a name="l1784" href="#l1784">1784</a><td> * @final<tr><td><a name="l1785" href="#l1785">1785</a><td> * @struct<tr><td><a name="l1786" href="#l1786">1786</a><td> */<tr><td><a name="l1787" href="#l1787">1787</a><td>webdriver.promise.Frame_ = function(flow) {<tr><td><a name="l1788" href="#l1788">1788</a><td> /** @private {!webdriver.promise.ControlFlow} */<tr><td><a name="l1789" href="#l1789">1789</a><td> this.flow_ = flow;<tr><td><a name="l1790" href="#l1790">1790</a><td><tr><td><a name="l1791" href="#l1791">1791</a><td> /** @private {webdriver.promise.Frame_} */<tr><td><a name="l1792" href="#l1792">1792</a><td> this.parent_ = null;<tr><td><a name="l1793" href="#l1793">1793</a><td><tr><td><a name="l1794" href="#l1794">1794</a><td> /**<tr><td><a name="l1795" href="#l1795">1795</a><td> * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}<tr><td><a name="l1796" href="#l1796">1796</a><td> */<tr><td><a name="l1797" href="#l1797">1797</a><td> this.children_ = [];<tr><td><a name="l1798" href="#l1798">1798</a><td><tr><td><a name="l1799" href="#l1799">1799</a><td><tr><td><a name="l1800" href="#l1800">1800</a><td> /** @private {(webdriver.promise.Frame_|webdriver.promise.Task_)} */<tr><td><a name="l1801" href="#l1801">1801</a><td> this.lastInsertedChild_ = null;<tr><td><a name="l1802" href="#l1802">1802</a><td><tr><td><a name="l1803" href="#l1803">1803</a><td> /**<tr><td><a name="l1804" href="#l1804">1804</a><td> * The task currently being executed within this frame.<tr><td><a name="l1805" href="#l1805">1805</a><td> * @private {webdriver.promise.Task_}<tr><td><a name="l1806" href="#l1806">1806</a><td> */<tr><td><a name="l1807" href="#l1807">1807</a><td> this.pendingTask_ = null;<tr><td><a name="l1808" href="#l1808">1808</a><td><tr><td><a name="l1809" href="#l1809">1809</a><td> /**<tr><td><a name="l1810" href="#l1810">1810</a><td> * Whether this frame is currently locked. A locked frame represents an<tr><td><a name="l1811" href="#l1811">1811</a><td> * executed function that has scheduled all of its tasks.<tr><td><a name="l1812" href="#l1812">1812</a><td> *<tr><td><a name="l1813" href="#l1813">1813</a><td> * <p>Once a frame becomes locked, any new frames which are added as children<tr><td><a name="l1814" href="#l1814">1814</a><td> * represent interrupts (such as a {@link webdriver.promise.Promise}<tr><td><a name="l1815" href="#l1815">1815</a><td> * callback) whose tasks must be given priority over those already scheduled<tr><td><a name="l1816" href="#l1816">1816</a><td> * within this frame. For example:<tr><td><a name="l1817" href="#l1817">1817</a><td> * <code><pre><tr><td><a name="l1818" href="#l1818">1818</a><td> * var flow = webdriver.promise.controlFlow();<tr><td><a name="l1819" href="#l1819">1819</a><td> * flow.execute('start here', goog.nullFunction).then(function() {<tr><td><a name="l1820" href="#l1820">1820</a><td> * flow.execute('this should execute 2nd', goog.nullFunction);<tr><td><a name="l1821" href="#l1821">1821</a><td> * });<tr><td><a name="l1822" href="#l1822">1822</a><td> * flow.execute('this should execute last', goog.nullFunction);<tr><td><a name="l1823" href="#l1823">1823</a><td> * </pre></code><tr><td><a name="l1824" href="#l1824">1824</a><td> *<tr><td><a name="l1825" href="#l1825">1825</a><td> * @private {boolean}<tr><td><a name="l1826" href="#l1826">1826</a><td> */<tr><td><a name="l1827" href="#l1827">1827</a><td> this.isLocked_ = false;<tr><td><a name="l1828" href="#l1828">1828</a><td><tr><td><a name="l1829" href="#l1829">1829</a><td> /** @type {boolean} */<tr><td><a name="l1830" href="#l1830">1830</a><td> this.isWaiting = false;<tr><td><a name="l1831" href="#l1831">1831</a><td><tr><td><a name="l1832" href="#l1832">1832</a><td> /**<tr><td><a name="l1833" href="#l1833">1833</a><td> * The function to notify if this frame executes without error.<tr><td><a name="l1834" href="#l1834">1834</a><td> * @type {?function()}<tr><td><a name="l1835" href="#l1835">1835</a><td> */<tr><td><a name="l1836" href="#l1836">1836</a><td> this.onComplete = null;<tr><td><a name="l1837" href="#l1837">1837</a><td><tr><td><a name="l1838" href="#l1838">1838</a><td> /**<tr><td><a name="l1839" href="#l1839">1839</a><td> * The function to notify if this frame is aborted with an error.<tr><td><a name="l1840" href="#l1840">1840</a><td> * @type {?function(*)}<tr><td><a name="l1841" href="#l1841">1841</a><td> */<tr><td><a name="l1842" href="#l1842">1842</a><td> this.onAbort = null;<tr><td><a name="l1843" href="#l1843">1843</a><td>};<tr><td><a name="l1844" href="#l1844">1844</a><td><tr><td><a name="l1845" href="#l1845">1845</a><td><tr><td><a name="l1846" href="#l1846">1846</a><td>/** @return {webdriver.promise.Frame_} This frame's parent, if any. */<tr><td><a name="l1847" href="#l1847">1847</a><td>webdriver.promise.Frame_.prototype.getParent = function() {<tr><td><a name="l1848" href="#l1848">1848</a><td> return this.parent_;<tr><td><a name="l1849" href="#l1849">1849</a><td>};<tr><td><a name="l1850" href="#l1850">1850</a><td><tr><td><a name="l1851" href="#l1851">1851</a><td><tr><td><a name="l1852" href="#l1852">1852</a><td>/**<tr><td><a name="l1853" href="#l1853">1853</a><td> * @param {webdriver.promise.Frame_} parent This frame's new parent.<tr><td><a name="l1854" href="#l1854">1854</a><td> */<tr><td><a name="l1855" href="#l1855">1855</a><td>webdriver.promise.Frame_.prototype.setParent = function(parent) {<tr><td><a name="l1856" href="#l1856">1856</a><td> this.parent_ = parent;<tr><td><a name="l1857" href="#l1857">1857</a><td>};<tr><td><a name="l1858" href="#l1858">1858</a><td><tr><td><a name="l1859" href="#l1859">1859</a><td><tr><td><a name="l1860" href="#l1860">1860</a><td>/**<tr><td><a name="l1861" href="#l1861">1861</a><td> * @return {!webdriver.promise.Frame_} The root of this frame's tree.<tr><td><a name="l1862" href="#l1862">1862</a><td> */<tr><td><a name="l1863" href="#l1863">1863</a><td>webdriver.promise.Frame_.prototype.getRoot = function() {<tr><td><a name="l1864" href="#l1864">1864</a><td> var root = this;<tr><td><a name="l1865" href="#l1865">1865</a><td> while (root.parent_) {<tr><td><a name="l1866" href="#l1866">1866</a><td> root = root.parent_;<tr><td><a name="l1867" href="#l1867">1867</a><td> }<tr><td><a name="l1868" href="#l1868">1868</a><td> return root;<tr><td><a name="l1869" href="#l1869">1869</a><td>};<tr><td><a name="l1870" href="#l1870">1870</a><td><tr><td><a name="l1871" href="#l1871">1871</a><td><tr><td><a name="l1872" href="#l1872">1872</a><td>/**<tr><td><a name="l1873" href="#l1873">1873</a><td> * Aborts the execution of this frame, cancelling all outstanding tasks<tr><td><a name="l1874" href="#l1874">1874</a><td> * scheduled within this frame.<tr><td><a name="l1875" href="#l1875">1875</a><td> *<tr><td><a name="l1876" href="#l1876">1876</a><td> * @param {*} error The error that triggered this abortion.<tr><td><a name="l1877" href="#l1877">1877</a><td> */<tr><td><a name="l1878" href="#l1878">1878</a><td>webdriver.promise.Frame_.prototype.abort = function(error) {<tr><td><a name="l1879" href="#l1879">1879</a><td> this.cancelRemainingTasks(<tr><td><a name="l1880" href="#l1880">1880</a><td> 'Task discarded due to a previous task failure: ' + error);<tr><td><a name="l1881" href="#l1881">1881</a><td><tr><td><a name="l1882" href="#l1882">1882</a><td> var frame = this;<tr><td><a name="l1883" href="#l1883">1883</a><td> frame.flow_.pendingRejections_ += 1;<tr><td><a name="l1884" href="#l1884">1884</a><td> this.flow_.timer.setTimeout(function() {<tr><td><a name="l1885" href="#l1885">1885</a><td> frame.flow_.pendingRejections_ -= 1;<tr><td><a name="l1886" href="#l1886">1886</a><td> if (frame.onAbort) {<tr><td><a name="l1887" href="#l1887">1887</a><td> frame.notify_(frame.onAbort, error);<tr><td><a name="l1888" href="#l1888">1888</a><td> } else {<tr><td><a name="l1889" href="#l1889">1889</a><td> frame.flow_.abortFrame_(error);<tr><td><a name="l1890" href="#l1890">1890</a><td> }<tr><td><a name="l1891" href="#l1891">1891</a><td> }, 0);<tr><td><a name="l1892" href="#l1892">1892</a><td>};<tr><td><a name="l1893" href="#l1893">1893</a><td><tr><td><a name="l1894" href="#l1894">1894</a><td><tr><td><a name="l1895" href="#l1895">1895</a><td>/**<tr><td><a name="l1896" href="#l1896">1896</a><td> * Signals that this frame has successfully finished executing.<tr><td><a name="l1897" href="#l1897">1897</a><td> */<tr><td><a name="l1898" href="#l1898">1898</a><td>webdriver.promise.Frame_.prototype.close = function() {<tr><td><a name="l1899" href="#l1899">1899</a><td> var frame = this;<tr><td><a name="l1900" href="#l1900">1900</a><td> this.flow_.timer.setTimeout(function() {<tr><td><a name="l1901" href="#l1901">1901</a><td> frame.notify_(frame.onComplete);<tr><td><a name="l1902" href="#l1902">1902</a><td> }, 0);<tr><td><a name="l1903" href="#l1903">1903</a><td>};<tr><td><a name="l1904" href="#l1904">1904</a><td><tr><td><a name="l1905" href="#l1905">1905</a><td><tr><td><a name="l1906" href="#l1906">1906</a><td>/**<tr><td><a name="l1907" href="#l1907">1907</a><td> * @param {?(function(*)|function())} fn The function to notify.<tr><td><a name="l1908" href="#l1908">1908</a><td> * @param {*=} opt_error Value to pass to the notified function, if any.<tr><td><a name="l1909" href="#l1909">1909</a><td> * @private<tr><td><a name="l1910" href="#l1910">1910</a><td> */<tr><td><a name="l1911" href="#l1911">1911</a><td>webdriver.promise.Frame_.prototype.notify_ = function(fn, opt_error) {<tr><td><a name="l1912" href="#l1912">1912</a><td> this.onAbort = this.onComplete = null;<tr><td><a name="l1913" href="#l1913">1913</a><td> if (fn) {<tr><td><a name="l1914" href="#l1914">1914</a><td> fn(opt_error);<tr><td><a name="l1915" href="#l1915">1915</a><td> }<tr><td><a name="l1916" href="#l1916">1916</a><td>};<tr><td><a name="l1917" href="#l1917">1917</a><td><tr><td><a name="l1918" href="#l1918">1918</a><td><tr><td><a name="l1919" href="#l1919">1919</a><td>/**<tr><td><a name="l1920" href="#l1920">1920</a><td> * Marks all of the tasks that are descendants of this frame in the execution<tr><td><a name="l1921" href="#l1921">1921</a><td> * tree as cancelled. This is necessary for callbacks scheduled asynchronous.<tr><td><a name="l1922" href="#l1922">1922</a><td> * For example:<tr><td><a name="l1923" href="#l1923">1923</a><td> *<tr><td><a name="l1924" href="#l1924">1924</a><td> * var someResult;<tr><td><a name="l1925" href="#l1925">1925</a><td> * webdriver.promise.createFlow(function(flow) {<tr><td><a name="l1926" href="#l1926">1926</a><td> * someResult = flow.execute(function() {});<tr><td><a name="l1927" href="#l1927">1927</a><td> * throw Error();<tr><td><a name="l1928" href="#l1928">1928</a><td> * }).addErrback(function(err) {<tr><td><a name="l1929" href="#l1929">1929</a><td> * console.log('flow failed: ' + err);<tr><td><a name="l1930" href="#l1930">1930</a><td> * someResult.then(function() {<tr><td><a name="l1931" href="#l1931">1931</a><td> * console.log('task succeeded!');<tr><td><a name="l1932" href="#l1932">1932</a><td> * }, function(err) {<tr><td><a name="l1933" href="#l1933">1933</a><td> * console.log('task failed! ' + err);<tr><td><a name="l1934" href="#l1934">1934</a><td> * });<tr><td><a name="l1935" href="#l1935">1935</a><td> * });<tr><td><a name="l1936" href="#l1936">1936</a><td> * // flow failed: Error: boom<tr><td><a name="l1937" href="#l1937">1937</a><td> * // task failed! CancelledTaskError: Task discarded due to a previous<tr><td><a name="l1938" href="#l1938">1938</a><td> * // task failure: Error: boom<tr><td><a name="l1939" href="#l1939">1939</a><td> *<tr><td><a name="l1940" href="#l1940">1940</a><td> * @param {string} reason The cancellation reason.<tr><td><a name="l1941" href="#l1941">1941</a><td> */<tr><td><a name="l1942" href="#l1942">1942</a><td>webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(reason) {<tr><td><a name="l1943" href="#l1943">1943</a><td> goog.array.forEach(this.children_, function(child) {<tr><td><a name="l1944" href="#l1944">1944</a><td> if (child instanceof webdriver.promise.Frame_) {<tr><td><a name="l1945" href="#l1945">1945</a><td> child.cancelRemainingTasks(reason);<tr><td><a name="l1946" href="#l1946">1946</a><td> } else {<tr><td><a name="l1947" href="#l1947">1947</a><td> // None of the previously registered listeners should be notified that<tr><td><a name="l1948" href="#l1948">1948</a><td> // the task is being canceled, however, we need at least one errback<tr><td><a name="l1949" href="#l1949">1949</a><td> // to prevent the cancellation from bubbling up.<tr><td><a name="l1950" href="#l1950">1950</a><td> child.removeAll();<tr><td><a name="l1951" href="#l1951">1951</a><td> child.thenCatch(goog.nullFunction);<tr><td><a name="l1952" href="#l1952">1952</a><td> child.cancel(reason);<tr><td><a name="l1953" href="#l1953">1953</a><td> }<tr><td><a name="l1954" href="#l1954">1954</a><td> });<tr><td><a name="l1955" href="#l1955">1955</a><td>};<tr><td><a name="l1956" href="#l1956">1956</a><td><tr><td><a name="l1957" href="#l1957">1957</a><td><tr><td><a name="l1958" href="#l1958">1958</a><td>/**<tr><td><a name="l1959" href="#l1959">1959</a><td> * @return {webdriver.promise.Task_} The task currently executing<tr><td><a name="l1960" href="#l1960">1960</a><td> * within this frame, if any.<tr><td><a name="l1961" href="#l1961">1961</a><td> */<tr><td><a name="l1962" href="#l1962">1962</a><td>webdriver.promise.Frame_.prototype.getPendingTask = function() {<tr><td><a name="l1963" href="#l1963">1963</a><td> return this.pendingTask_;<tr><td><a name="l1964" href="#l1964">1964</a><td>};<tr><td><a name="l1965" href="#l1965">1965</a><td><tr><td><a name="l1966" href="#l1966">1966</a><td><tr><td><a name="l1967" href="#l1967">1967</a><td>/**<tr><td><a name="l1968" href="#l1968">1968</a><td> * @param {webdriver.promise.Task_} task The task currently<tr><td><a name="l1969" href="#l1969">1969</a><td> * executing within this frame, if any.<tr><td><a name="l1970" href="#l1970">1970</a><td> */<tr><td><a name="l1971" href="#l1971">1971</a><td>webdriver.promise.Frame_.prototype.setPendingTask = function(task) {<tr><td><a name="l1972" href="#l1972">1972</a><td> this.pendingTask_ = task;<tr><td><a name="l1973" href="#l1973">1973</a><td>};<tr><td><a name="l1974" href="#l1974">1974</a><td><tr><td><a name="l1975" href="#l1975">1975</a><td><tr><td><a name="l1976" href="#l1976">1976</a><td>/**<tr><td><a name="l1977" href="#l1977">1977</a><td> * Adds a new node to this frame.<tr><td><a name="l1978" href="#l1978">1978</a><td> * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node<tr><td><a name="l1979" href="#l1979">1979</a><td> * The node to insert.<tr><td><a name="l1980" href="#l1980">1980</a><td> */<tr><td><a name="l1981" href="#l1981">1981</a><td>webdriver.promise.Frame_.prototype.addChild = function(node) {<tr><td><a name="l1982" href="#l1982">1982</a><td> if (this.lastInsertedChild_ &&<tr><td><a name="l1983" href="#l1983">1983</a><td> this.lastInsertedChild_ instanceof webdriver.promise.Frame_ &&<tr><td><a name="l1984" href="#l1984">1984</a><td> !this.lastInsertedChild_.isLocked_) {<tr><td><a name="l1985" href="#l1985">1985</a><td> this.lastInsertedChild_.addChild(node);<tr><td><a name="l1986" href="#l1986">1986</a><td> return;<tr><td><a name="l1987" href="#l1987">1987</a><td> }<tr><td><a name="l1988" href="#l1988">1988</a><td><tr><td><a name="l1989" href="#l1989">1989</a><td> if (node instanceof webdriver.promise.Frame_) {<tr><td><a name="l1990" href="#l1990">1990</a><td> node.setParent(this);<tr><td><a name="l1991" href="#l1991">1991</a><td> }<tr><td><a name="l1992" href="#l1992">1992</a><td><tr><td><a name="l1993" href="#l1993">1993</a><td> if (this.isLocked_ && node instanceof webdriver.promise.Frame_) {<tr><td><a name="l1994" href="#l1994">1994</a><td> var index = 0;<tr><td><a name="l1995" href="#l1995">1995</a><td> if (this.lastInsertedChild_ instanceof<tr><td><a name="l1996" href="#l1996">1996</a><td> webdriver.promise.Frame_) {<tr><td><a name="l1997" href="#l1997">1997</a><td> index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1;<tr><td><a name="l1998" href="#l1998">1998</a><td> }<tr><td><a name="l1999" href="#l1999">1999</a><td> goog.array.insertAt(this.children_, node, index);<tr><td><a name="l2000" href="#l2000">2000</a><td> this.lastInsertedChild_ = node;<tr><td><a name="l2001" href="#l2001">2001</a><td> return;<tr><td><a name="l2002" href="#l2002">2002</a><td> }<tr><td><a name="l2003" href="#l2003">2003</a><td><tr><td><a name="l2004" href="#l2004">2004</a><td> this.lastInsertedChild_ = node;<tr><td><a name="l2005" href="#l2005">2005</a><td> this.children_.push(node);<tr><td><a name="l2006" href="#l2006">2006</a><td>};<tr><td><a name="l2007" href="#l2007">2007</a><td><tr><td><a name="l2008" href="#l2008">2008</a><td><tr><td><a name="l2009" href="#l2009">2009</a><td>/**<tr><td><a name="l2010" href="#l2010">2010</a><td> * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's<tr><td><a name="l2011" href="#l2011">2011</a><td> * fist child.<tr><td><a name="l2012" href="#l2012">2012</a><td> */<tr><td><a name="l2013" href="#l2013">2013</a><td>webdriver.promise.Frame_.prototype.getFirstChild = function() {<tr><td><a name="l2014" href="#l2014">2014</a><td> this.isLocked_ = true;<tr><td><a name="l2015" href="#l2015">2015</a><td> this.lastInsertedChild_ = null;<tr><td><a name="l2016" href="#l2016">2016</a><td> return this.children_[0];<tr><td><a name="l2017" href="#l2017">2017</a><td>};<tr><td><a name="l2018" href="#l2018">2018</a><td><tr><td><a name="l2019" href="#l2019">2019</a><td><tr><td><a name="l2020" href="#l2020">2020</a><td>/**<tr><td><a name="l2021" href="#l2021">2021</a><td> * Removes a child from this frame.<tr><td><a name="l2022" href="#l2022">2022</a><td> * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child<tr><td><a name="l2023" href="#l2023">2023</a><td> * The child to remove.<tr><td><a name="l2024" href="#l2024">2024</a><td> */<tr><td><a name="l2025" href="#l2025">2025</a><td>webdriver.promise.Frame_.prototype.removeChild = function(child) {<tr><td><a name="l2026" href="#l2026">2026</a><td> var index = goog.array.indexOf(this.children_, child);<tr><td><a name="l2027" href="#l2027">2027</a><td> if (child instanceof webdriver.promise.Frame_) {<tr><td><a name="l2028" href="#l2028">2028</a><td> child.setParent(null);<tr><td><a name="l2029" href="#l2029">2029</a><td> }<tr><td><a name="l2030" href="#l2030">2030</a><td> goog.array.removeAt(this.children_, index);<tr><td><a name="l2031" href="#l2031">2031</a><td> if (this.lastInsertedChild_ === child) {<tr><td><a name="l2032" href="#l2032">2032</a><td> this.lastInsertedChild_ = null;<tr><td><a name="l2033" href="#l2033">2033</a><td> }<tr><td><a name="l2034" href="#l2034">2034</a><td>};<tr><td><a name="l2035" href="#l2035">2035</a><td><tr><td><a name="l2036" href="#l2036">2036</a><td><tr><td><a name="l2037" href="#l2037">2037</a><td>/** @override */<tr><td><a name="l2038" href="#l2038">2038</a><td>webdriver.promise.Frame_.prototype.toString = function() {<tr><td><a name="l2039" href="#l2039">2039</a><td> return '[' + goog.array.map(this.children_, function(child) {<tr><td><a name="l2040" href="#l2040">2040</a><td> return child.toString();<tr><td><a name="l2041" href="#l2041">2041</a><td> }).join(', ') + ']';<tr><td><a name="l2042" href="#l2042">2042</a><td>};<tr><td><a name="l2043" href="#l2043">2043</a><td><tr><td><a name="l2044" href="#l2044">2044</a><td><tr><td><a name="l2045" href="#l2045">2045</a><td><tr><td><a name="l2046" href="#l2046">2046</a><td>/**<tr><td><a name="l2047" href="#l2047">2047</a><td> * A task to be executed by a {@link webdriver.promise.ControlFlow}.<tr><td><a name="l2048" href="#l2048">2048</a><td> *<tr><td><a name="l2049" href="#l2049">2049</a><td> * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs<tr><td><a name="l2050" href="#l2050">2050</a><td> * to.<tr><td><a name="l2051" href="#l2051">2051</a><td> * @param {function(): (T|!webdriver.promise.Promise.<T>)} fn The function to<tr><td><a name="l2052" href="#l2052">2052</a><td> * call when the task executes. If it returns a<tr><td><a name="l2053" href="#l2053">2053</a><td> * {@link webdriver.promise.Promise}, the flow will wait for it to be<tr><td><a name="l2054" href="#l2054">2054</a><td> * resolved before starting the next task.<tr><td><a name="l2055" href="#l2055">2055</a><td> * @param {string} description A description of the task for debugging.<tr><td><a name="l2056" href="#l2056">2056</a><td> * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack<tr><td><a name="l2057" href="#l2057">2057</a><td> * when this task was scheduled.<tr><td><a name="l2058" href="#l2058">2058</a><td> * @constructor<tr><td><a name="l2059" href="#l2059">2059</a><td> * @extends {webdriver.promise.Deferred.<T>}<tr><td><a name="l2060" href="#l2060">2060</a><td> * @template T<tr><td><a name="l2061" href="#l2061">2061</a><td> * @private<tr><td><a name="l2062" href="#l2062">2062</a><td> */<tr><td><a name="l2063" href="#l2063">2063</a><td>webdriver.promise.Task_ = function(flow, fn, description, snapshot) {<tr><td><a name="l2064" href="#l2064">2064</a><td> webdriver.promise.Deferred.call(this, flow);<tr><td><a name="l2065" href="#l2065">2065</a><td><tr><td><a name="l2066" href="#l2066">2066</a><td> /**<tr><td><a name="l2067" href="#l2067">2067</a><td> * @type {function(): (T|!webdriver.promise.Promise.<T>)}<tr><td><a name="l2068" href="#l2068">2068</a><td> */<tr><td><a name="l2069" href="#l2069">2069</a><td> this.execute = fn;<tr><td><a name="l2070" href="#l2070">2070</a><td><tr><td><a name="l2071" href="#l2071">2071</a><td> /** @private {string} */<tr><td><a name="l2072" href="#l2072">2072</a><td> this.description_ = description;<tr><td><a name="l2073" href="#l2073">2073</a><td><tr><td><a name="l2074" href="#l2074">2074</a><td> /** @private {!webdriver.stacktrace.Snapshot} */<tr><td><a name="l2075" href="#l2075">2075</a><td> this.snapshot_ = snapshot;<tr><td><a name="l2076" href="#l2076">2076</a><td>};<tr><td><a name="l2077" href="#l2077">2077</a><td>goog.inherits(webdriver.promise.Task_, webdriver.promise.Deferred);<tr><td><a name="l2078" href="#l2078">2078</a><td><tr><td><a name="l2079" href="#l2079">2079</a><td><tr><td><a name="l2080" href="#l2080">2080</a><td>/** @return {string} This task's description. */<tr><td><a name="l2081" href="#l2081">2081</a><td>webdriver.promise.Task_.prototype.getDescription = function() {<tr><td><a name="l2082" href="#l2082">2082</a><td> return this.description_;<tr><td><a name="l2083" href="#l2083">2083</a><td>};<tr><td><a name="l2084" href="#l2084">2084</a><td><tr><td><a name="l2085" href="#l2085">2085</a><td><tr><td><a name="l2086" href="#l2086">2086</a><td>/** @override */<tr><td><a name="l2087" href="#l2087">2087</a><td>webdriver.promise.Task_.prototype.toString = function() {<tr><td><a name="l2088" href="#l2088">2088</a><td> var stack = this.snapshot_.getStacktrace();<tr><td><a name="l2089" href="#l2089">2089</a><td> var ret = this.description_;<tr><td><a name="l2090" href="#l2090">2090</a><td> if (stack.length) {<tr><td><a name="l2091" href="#l2091">2091</a><td> if (this.description_) {<tr><td><a name="l2092" href="#l2092">2092</a><td> ret += '\n';<tr><td><a name="l2093" href="#l2093">2093</a><td> }<tr><td><a name="l2094" href="#l2094">2094</a><td> ret += stack.join('\n');<tr><td><a name="l2095" href="#l2095">2095</a><td> }<tr><td><a name="l2096" href="#l2096">2096</a><td> return ret;<tr><td><a name="l2097" href="#l2097">2097</a><td>};<tr><td><a name="l2098" href="#l2098">2098</a><td><tr><td><a name="l2099" href="#l2099">2099</a><td><tr><td><a name="l2100" href="#l2100">2100</a><td><tr><td><a name="l2101" href="#l2101">2101</a><td>/**<tr><td><a name="l2102" href="#l2102">2102</a><td> * The default flow to use if no others are active.<tr><td><a name="l2103" href="#l2103">2103</a><td> * @private {!webdriver.promise.ControlFlow}<tr><td><a name="l2104" href="#l2104">2104</a><td> */<tr><td><a name="l2105" href="#l2105">2105</a><td>webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow();<tr><td><a name="l2106" href="#l2106">2106</a><td><tr><td><a name="l2107" href="#l2107">2107</a><td><tr><td><a name="l2108" href="#l2108">2108</a><td>/**<tr><td><a name="l2109" href="#l2109">2109</a><td> * A stack of active control flows, with the top of the stack used to schedule<tr><td><a name="l2110" href="#l2110">2110</a><td> * commands. When there are multiple flows on the stack, the flow at index N<tr><td><a name="l2111" href="#l2111">2111</a><td> * represents a callback triggered within a task owned by the flow at index<tr><td><a name="l2112" href="#l2112">2112</a><td> * N-1.<tr><td><a name="l2113" href="#l2113">2113</a><td> * @private {!Array.<!webdriver.promise.ControlFlow>}<tr><td><a name="l2114" href="#l2114">2114</a><td> */<tr><td><a name="l2115" href="#l2115">2115</a><td>webdriver.promise.activeFlows_ = [];<tr><td><a name="l2116" href="#l2116">2116</a><td><tr><td><a name="l2117" href="#l2117">2117</a><td><tr><td><a name="l2118" href="#l2118">2118</a><td>/**<tr><td><a name="l2119" href="#l2119">2119</a><td> * Changes the default flow to use when no others are active.<tr><td><a name="l2120" href="#l2120">2120</a><td> * @param {!webdriver.promise.ControlFlow} flow The new default flow.<tr><td><a name="l2121" href="#l2121">2121</a><td> * @throws {Error} If the default flow is not currently active.<tr><td><a name="l2122" href="#l2122">2122</a><td> */<tr><td><a name="l2123" href="#l2123">2123</a><td>webdriver.promise.setDefaultFlow = function(flow) {<tr><td><a name="l2124" href="#l2124">2124</a><td> if (webdriver.promise.activeFlows_.length) {<tr><td><a name="l2125" href="#l2125">2125</a><td> throw Error('You may only change the default flow while it is active');<tr><td><a name="l2126" href="#l2126">2126</a><td> }<tr><td><a name="l2127" href="#l2127">2127</a><td> webdriver.promise.defaultFlow_ = flow;<tr><td><a name="l2128" href="#l2128">2128</a><td>};<tr><td><a name="l2129" href="#l2129">2129</a><td><tr><td><a name="l2130" href="#l2130">2130</a><td><tr><td><a name="l2131" href="#l2131">2131</a><td>/**<tr><td><a name="l2132" href="#l2132">2132</a><td> * @return {!webdriver.promise.ControlFlow} The currently active control flow.<tr><td><a name="l2133" href="#l2133">2133</a><td> */<tr><td><a name="l2134" href="#l2134">2134</a><td>webdriver.promise.controlFlow = function() {<tr><td><a name="l2135" href="#l2135">2135</a><td> return /** @type {!webdriver.promise.ControlFlow} */ (<tr><td><a name="l2136" href="#l2136">2136</a><td> goog.array.peek(webdriver.promise.activeFlows_) ||<tr><td><a name="l2137" href="#l2137">2137</a><td> webdriver.promise.defaultFlow_);<tr><td><a name="l2138" href="#l2138">2138</a><td>};<tr><td><a name="l2139" href="#l2139">2139</a><td><tr><td><a name="l2140" href="#l2140">2140</a><td><tr><td><a name="l2141" href="#l2141">2141</a><td>/**<tr><td><a name="l2142" href="#l2142">2142</a><td> * @param {!webdriver.promise.ControlFlow} flow The new flow.<tr><td><a name="l2143" href="#l2143">2143</a><td> * @private<tr><td><a name="l2144" href="#l2144">2144</a><td> */<tr><td><a name="l2145" href="#l2145">2145</a><td>webdriver.promise.pushFlow_ = function(flow) {<tr><td><a name="l2146" href="#l2146">2146</a><td> webdriver.promise.activeFlows_.push(flow);<tr><td><a name="l2147" href="#l2147">2147</a><td>};<tr><td><a name="l2148" href="#l2148">2148</a><td><tr><td><a name="l2149" href="#l2149">2149</a><td><tr><td><a name="l2150" href="#l2150">2150</a><td>/** @private */<tr><td><a name="l2151" href="#l2151">2151</a><td>webdriver.promise.popFlow_ = function() {<tr><td><a name="l2152" href="#l2152">2152</a><td> webdriver.promise.activeFlows_.pop();<tr><td><a name="l2153" href="#l2153">2153</a><td>};<tr><td><a name="l2154" href="#l2154">2154</a><td><tr><td><a name="l2155" href="#l2155">2155</a><td><tr><td><a name="l2156" href="#l2156">2156</a><td>/**<tr><td><a name="l2157" href="#l2157">2157</a><td> * Creates a new control flow. The provided callback will be invoked as the<tr><td><a name="l2158" href="#l2158">2158</a><td> * first task within the new flow, with the flow as its sole argument. Returns<tr><td><a name="l2159" href="#l2159">2159</a><td> * a promise that resolves to the callback result.<tr><td><a name="l2160" href="#l2160">2160</a><td> * @param {function(!webdriver.promise.ControlFlow)} callback The entry point<tr><td><a name="l2161" href="#l2161">2161</a><td> * to the newly created flow.<tr><td><a name="l2162" href="#l2162">2162</a><td> * @return {!webdriver.promise.Promise} A promise that resolves to the callback<tr><td><a name="l2163" href="#l2163">2163</a><td> * result.<tr><td><a name="l2164" href="#l2164">2164</a><td> */<tr><td><a name="l2165" href="#l2165">2165</a><td>webdriver.promise.createFlow = function(callback) {<tr><td><a name="l2166" href="#l2166">2166</a><td> var flow = new webdriver.promise.ControlFlow(<tr><td><a name="l2167" href="#l2167">2167</a><td> webdriver.promise.defaultFlow_.timer);<tr><td><a name="l2168" href="#l2168">2168</a><td> return flow.execute(function() {<tr><td><a name="l2169" href="#l2169">2169</a><td> return callback(flow);<tr><td><a name="l2170" href="#l2170">2170</a><td> });<tr><td><a name="l2171" href="#l2171">2171</a><td>};<tr><td><a name="l2172" href="#l2172">2172</a><td><tr><td><a name="l2173" href="#l2173">2173</a><td><tr><td><a name="l2174" href="#l2174">2174</a><td>/**<tr><td><a name="l2175" href="#l2175">2175</a><td> * Tests is a function is a generator.<tr><td><a name="l2176" href="#l2176">2176</a><td> * @param {!Function} fn The function to test.<tr><td><a name="l2177" href="#l2177">2177</a><td> * @return {boolean} Whether the function is a generator.<tr><td><a name="l2178" href="#l2178">2178</a><td> */<tr><td><a name="l2179" href="#l2179">2179</a><td>webdriver.promise.isGenerator = function(fn) {<tr><td><a name="l2180" href="#l2180">2180</a><td> return fn.constructor.name === 'GeneratorFunction';<tr><td><a name="l2181" href="#l2181">2181</a><td>};<tr><td><a name="l2182" href="#l2182">2182</a><td><tr><td><a name="l2183" href="#l2183">2183</a><td><tr><td><a name="l2184" href="#l2184">2184</a><td>/**<tr><td><a name="l2185" href="#l2185">2185</a><td> * Consumes a {@code GeneratorFunction}. Each time the generator yields a<tr><td><a name="l2186" href="#l2186">2186</a><td> * promise, this function will wait for it to be fulfilled before feeding the<tr><td><a name="l2187" href="#l2187">2187</a><td> * fulfilled value back into {@code next}. Likewise, if a yielded promise is<tr><td><a name="l2188" href="#l2188">2188</a><td> * rejected, the rejection error will be passed to {@code throw}.<tr><td><a name="l2189" href="#l2189">2189</a><td> *<tr><td><a name="l2190" href="#l2190">2190</a><td> * <p>Example 1: the Fibonacci Sequence.<tr><td><a name="l2191" href="#l2191">2191</a><td> * <pre><code><tr><td><a name="l2192" href="#l2192">2192</a><td> * webdriver.promise.consume(function* fibonacci() {<tr><td><a name="l2193" href="#l2193">2193</a><td> * var n1 = 1, n2 = 1;<tr><td><a name="l2194" href="#l2194">2194</a><td> * for (var i = 0; i < 4; ++i) {<tr><td><a name="l2195" href="#l2195">2195</a><td> * var tmp = yield n1 + n2;<tr><td><a name="l2196" href="#l2196">2196</a><td> * n1 = n2;<tr><td><a name="l2197" href="#l2197">2197</a><td> * n2 = tmp;<tr><td><a name="l2198" href="#l2198">2198</a><td> * }<tr><td><a name="l2199" href="#l2199">2199</a><td> * return n1 + n2;<tr><td><a name="l2200" href="#l2200">2200</a><td> * }).then(function(result) {<tr><td><a name="l2201" href="#l2201">2201</a><td> * console.log(result); // 13<tr><td><a name="l2202" href="#l2202">2202</a><td> * });<tr><td><a name="l2203" href="#l2203">2203</a><td> * </code></pre><tr><td><a name="l2204" href="#l2204">2204</a><td> *<tr><td><a name="l2205" href="#l2205">2205</a><td> * <p>Example 2: a generator that throws.<tr><td><a name="l2206" href="#l2206">2206</a><td> * <pre><code><tr><td><a name="l2207" href="#l2207">2207</a><td> * webdriver.promise.consume(function* () {<tr><td><a name="l2208" href="#l2208">2208</a><td> * yield webdriver.promise.delayed(250).then(function() {<tr><td><a name="l2209" href="#l2209">2209</a><td> * throw Error('boom');<tr><td><a name="l2210" href="#l2210">2210</a><td> * });<tr><td><a name="l2211" href="#l2211">2211</a><td> * }).thenCatch(function(e) {<tr><td><a name="l2212" href="#l2212">2212</a><td> * console.log(e.toString()); // Error: boom<tr><td><a name="l2213" href="#l2213">2213</a><td> * });<tr><td><a name="l2214" href="#l2214">2214</a><td> * </code></pre><tr><td><a name="l2215" href="#l2215">2215</a><td> *<tr><td><a name="l2216" href="#l2216">2216</a><td> * @param {!Function} generatorFn The generator function to execute.<tr><td><a name="l2217" href="#l2217">2217</a><td> * @param {Object=} opt_self The object to use as "this" when invoking the<tr><td><a name="l2218" href="#l2218">2218</a><td> * initial generator.<tr><td><a name="l2219" href="#l2219">2219</a><td> * @param {...*} var_args Any arguments to pass to the initial generator.<tr><td><a name="l2220" href="#l2220">2220</a><td> * @return {!webdriver.promise.Promise.<?>} A promise that will resolve to the<tr><td><a name="l2221" href="#l2221">2221</a><td> * generator's final result.<tr><td><a name="l2222" href="#l2222">2222</a><td> * @throws {TypeError} If the given function is not a generator.<tr><td><a name="l2223" href="#l2223">2223</a><td> */<tr><td><a name="l2224" href="#l2224">2224</a><td>webdriver.promise.consume = function(generatorFn, opt_self, var_args) {<tr><td><a name="l2225" href="#l2225">2225</a><td> if (!webdriver.promise.isGenerator(generatorFn)) {<tr><td><a name="l2226" href="#l2226">2226</a><td> throw TypeError('Input is not a GeneratorFunction: ' +<tr><td><a name="l2227" href="#l2227">2227</a><td> generatorFn.constructor.name);<tr><td><a name="l2228" href="#l2228">2228</a><td> }<tr><td><a name="l2229" href="#l2229">2229</a><td><tr><td><a name="l2230" href="#l2230">2230</a><td> var deferred = webdriver.promise.defer();<tr><td><a name="l2231" href="#l2231">2231</a><td> var generator = generatorFn.apply(opt_self, goog.array.slice(arguments, 2));<tr><td><a name="l2232" href="#l2232">2232</a><td> callNext();<tr><td><a name="l2233" href="#l2233">2233</a><td> return deferred.promise;<tr><td><a name="l2234" href="#l2234">2234</a><td><tr><td><a name="l2235" href="#l2235">2235</a><td> /** @param {*=} opt_value . */<tr><td><a name="l2236" href="#l2236">2236</a><td> function callNext(opt_value) {<tr><td><a name="l2237" href="#l2237">2237</a><td> pump(generator.next, opt_value);<tr><td><a name="l2238" href="#l2238">2238</a><td> }<tr><td><a name="l2239" href="#l2239">2239</a><td><tr><td><a name="l2240" href="#l2240">2240</a><td> /** @param {*=} opt_error . */<tr><td><a name="l2241" href="#l2241">2241</a><td> function callThrow(opt_error) {<tr><td><a name="l2242" href="#l2242">2242</a><td> // Dictionary lookup required because Closure compiler's built-in<tr><td><a name="l2243" href="#l2243">2243</a><td> // externs does not include GeneratorFunction.prototype.throw.<tr><td><a name="l2244" href="#l2244">2244</a><td> pump(generator['throw'], opt_error);<tr><td><a name="l2245" href="#l2245">2245</a><td> }<tr><td><a name="l2246" href="#l2246">2246</a><td><tr><td><a name="l2247" href="#l2247">2247</a><td> function pump(fn, opt_arg) {<tr><td><a name="l2248" href="#l2248">2248</a><td> if (!deferred.isPending()) {<tr><td><a name="l2249" href="#l2249">2249</a><td> return; // Defererd was cancelled; silently abort.<tr><td><a name="l2250" href="#l2250">2250</a><td> }<tr><td><a name="l2251" href="#l2251">2251</a><td><tr><td><a name="l2252" href="#l2252">2252</a><td> try {<tr><td><a name="l2253" href="#l2253">2253</a><td> var result = fn.call(generator, opt_arg);<tr><td><a name="l2254" href="#l2254">2254</a><td> } catch (ex) {<tr><td><a name="l2255" href="#l2255">2255</a><td> deferred.reject(ex);<tr><td><a name="l2256" href="#l2256">2256</a><td> return;<tr><td><a name="l2257" href="#l2257">2257</a><td> }<tr><td><a name="l2258" href="#l2258">2258</a><td><tr><td><a name="l2259" href="#l2259">2259</a><td> if (result.done) {<tr><td><a name="l2260" href="#l2260">2260</a><td> deferred.fulfill(result.value);<tr><td><a name="l2261" href="#l2261">2261</a><td> return;<tr><td><a name="l2262" href="#l2262">2262</a><td> }<tr><td><a name="l2263" href="#l2263">2263</a><td><tr><td><a name="l2264" href="#l2264">2264</a><td> webdriver.promise.asap(result.value, callNext, callThrow);<tr><td><a name="l2265" href="#l2265">2265</a><td> }<tr><td><a name="l2266" href="#l2266">2266</a><td>};</table></pre></main><nav id="topnav"><div><div id="menubutton"><label for="sidenav-toggle">Menu</label></div><form id="searchbox"><div><input type="search" placeholder="Search" tabindex="1"></div></form></div></nav><nav id="sidenav"><input type="checkbox" id="sidenav-types-ctrl" /><input type="checkbox" id="sidenav-files-ctrl" /><input type="checkbox" id="sidenav-modules-ctrl" /><a id="sidenav-overview"><div><h4>Overview</h4></div></a><div id="sidenav-types"><label for="sidenav-types-ctrl"><h4>Types</h4></label><i>Loading</i></div><div id="sidenav-modules"><label for="sidenav-modules-ctrl"><h4>Modules</h4></label><i>Loading</i></div><div id="sidenav-files"><label for="sidenav-files-ctrl"><h4>Files</h4></label><i>Loading</i></div><a href="../../../license.html"><div><h4>License</h4></div></a></nav><div id="push-footer"></div></div><footer><a href="https://github.com/jleyba/js-dossier">Generated by dossier</a></footer><script src="../../../types.js"></script><script src="../../../dossier.js"></script>
|