selenium-webdriver 2.47.0 → 2.52.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 +96 -0
- package/NOTICE +1 -1
- package/README.md +57 -6
- package/builder.js +375 -358
- package/chrome.js +549 -586
- package/edge.js +378 -0
- package/error.js +597 -5
- package/example/google_search_generator.js +2 -4
- package/example/logging.js +6 -2
- package/executors.js +11 -27
- package/firefox/binary.js +80 -114
- package/firefox/extension.js +10 -19
- package/firefox/index.js +175 -171
- package/firefox/profile.js +221 -239
- package/http/index.js +396 -77
- package/http/util.js +35 -35
- package/ie.js +288 -309
- package/index.js +34 -125
- package/io/exec.js +58 -39
- package/io/index.js +54 -48
- package/lib/actions.js +594 -0
- package/lib/by.js +276 -0
- package/lib/capabilities.js +404 -0
- package/lib/{webdriver/command.js → command.js} +89 -72
- package/lib/{webdriver/button.js → devmode.js} +13 -8
- package/lib/events.js +210 -0
- package/lib/firefox/amd64/libnoblur64.so +0 -0
- package/lib/firefox/i386/libnoblur.so +0 -0
- package/lib/firefox/webdriver.json +6 -1
- package/lib/firefox/webdriver.xpi +0 -0
- package/lib/{webdriver/key.js → input.js} +82 -2
- package/lib/logging.js +668 -0
- package/lib/promise.js +3093 -0
- package/lib/safari/client.js +697 -659
- package/lib/session.js +79 -0
- package/{test/promise_aplus_test.js → lib/symbols.js} +15 -25
- package/lib/test/build.js +4 -4
- package/lib/test/data/click_tests/disappearing_element.html +62 -0
- package/lib/test/data/click_tests/html5_submit_buttons.html +2 -1
- package/lib/test/data/click_tests/overlapping_elements.html +70 -0
- package/lib/test/data/click_tests/partially_overlapping_elements.html +124 -0
- package/lib/test/data/click_tests/wrapped_overlapping_elements.html +13 -0
- package/lib/test/data/readOnlyPage.html +3 -1
- package/lib/test/data/selectPage.html +18 -2
- package/lib/test/data/simpleTest.html +1 -0
- package/lib/test/fileserver.js +3 -4
- package/lib/test/index.js +13 -4
- package/lib/test/resources.js +1 -1
- package/lib/until.js +428 -0
- package/lib/webdriver.js +2351 -0
- package/net/portprober.js +15 -11
- package/opera.js +295 -314
- package/package.json +6 -5
- package/phantomjs.js +135 -140
- package/proxy.js +6 -4
- package/remote/index.js +234 -219
- package/safari.js +319 -310
- package/test/chrome/options_test.js +11 -9
- package/test/element_finding_test.js +51 -13
- package/test/error_test.js +163 -0
- package/test/execute_script_test.js +1 -1
- package/test/firefox/firefox_test.js +24 -0
- package/test/http/http_test.js +354 -43
- package/test/http/util_test.js +25 -23
- package/test/io_test.js +6 -1
- package/test/lib/by_test.js +127 -0
- package/test/lib/capabilities_test.js +84 -0
- package/test/lib/events_test.js +177 -0
- package/test/lib/logging_test.js +272 -0
- package/test/lib/promise_aplus_test.js +74 -0
- package/test/lib/promise_error_test.js +808 -0
- package/test/lib/promise_flow_test.js +2282 -0
- package/test/lib/promise_generator_test.js +308 -0
- package/test/lib/promise_test.js +996 -0
- package/test/lib/testutil.js +90 -0
- package/test/lib/until_test.js +436 -0
- package/test/lib/webdriver_test.js +2207 -0
- package/test/page_loading_test.js +7 -9
- package/test/stale_element_test.js +2 -2
- package/test/testing/assert_test.js +374 -0
- package/test/testing/index_test.js +9 -11
- package/testing/assert.js +334 -17
- package/testing/index.js +10 -0
- package/_base.js +0 -200
- package/docs/Changes.html +0 -317
- package/docs/class_bot_Error.html +0 -15
- package/docs/class_webdriver_ActionSequence.html +0 -115
- package/docs/class_webdriver_Alert.html +0 -24
- package/docs/class_webdriver_AlertPromise.html +0 -91
- package/docs/class_webdriver_Capabilities.html +0 -60
- package/docs/class_webdriver_Command.html +0 -15
- package/docs/class_webdriver_EventEmitter.html +0 -35
- package/docs/class_webdriver_FileDetector.html +0 -21
- package/docs/class_webdriver_Locator.html +0 -12
- package/docs/class_webdriver_Serializable.html +0 -9
- package/docs/class_webdriver_Session.html +0 -13
- package/docs/class_webdriver_TouchSequence.html +0 -49
- package/docs/class_webdriver_UnhandledAlertError.html +0 -18
- package/docs/class_webdriver_WebDriver.html +0 -313
- package/docs/class_webdriver_WebDriver_Logs.html +0 -15
- package/docs/class_webdriver_WebDriver_Navigation.html +0 -16
- package/docs/class_webdriver_WebDriver_Options.html +0 -41
- package/docs/class_webdriver_WebDriver_TargetLocator.html +0 -38
- package/docs/class_webdriver_WebDriver_Timeouts.html +0 -29
- package/docs/class_webdriver_WebDriver_Window.html +0 -27
- package/docs/class_webdriver_WebElement.html +0 -216
- package/docs/class_webdriver_WebElementPromise.html +0 -266
- package/docs/class_webdriver_http_Executor.html +0 -22
- package/docs/class_webdriver_http_Request.html +0 -12
- package/docs/class_webdriver_http_Response.html +0 -13
- package/docs/class_webdriver_logging_Entry.html +0 -15
- package/docs/class_webdriver_logging_Level.html +0 -55
- package/docs/class_webdriver_logging_LogRecord.html +0 -45
- package/docs/class_webdriver_logging_Logger.html +0 -119
- package/docs/class_webdriver_logging_Preferences.html +0 -8
- package/docs/class_webdriver_promise_CancellationError.html +0 -18
- package/docs/class_webdriver_promise_ControlFlow.html +0 -109
- package/docs/class_webdriver_promise_Deferred.html +0 -84
- package/docs/class_webdriver_promise_Promise.html +0 -74
- package/docs/class_webdriver_stacktrace_Frame.html +0 -20
- package/docs/class_webdriver_stacktrace_Snapshot.html +0 -6
- package/docs/class_webdriver_testing_Assertion.html +0 -99
- package/docs/class_webdriver_testing_Assertion_DelegatingMatcher_.html +0 -13
- package/docs/class_webdriver_testing_ContainsMatcher.html +0 -11
- package/docs/class_webdriver_testing_NegatedAssertion.html +0 -97
- package/docs/class_webdriver_until_Condition.html +0 -7
- package/docs/dossier.css +0 -1
- package/docs/dossier.js +0 -86
- package/docs/enum_bot_ErrorCode.html +0 -3
- package/docs/enum_bot_Error_State.html +0 -2
- package/docs/enum_webdriver_Browser.html +0 -2
- package/docs/enum_webdriver_Button.html +0 -2
- package/docs/enum_webdriver_Capability.html +0 -33
- package/docs/enum_webdriver_CommandName.html +0 -3
- package/docs/enum_webdriver_Key.html +0 -12
- package/docs/enum_webdriver_logging_Type.html +0 -6
- package/docs/enum_webdriver_promise_ControlFlow_EventType.html +0 -9
- package/docs/index.html +0 -123
- package/docs/interface_webdriver_CommandExecutor.html +0 -9
- package/docs/interface_webdriver_http_Client.html +0 -10
- package/docs/interface_webdriver_promise_Thenable.html +0 -77
- package/docs/module_selenium-webdriver.html +0 -23
- package/docs/module_selenium-webdriver__base.html +0 -23
- package/docs/module_selenium-webdriver__base_class_Context.html +0 -5
- package/docs/module_selenium-webdriver_builder.html +0 -2
- package/docs/module_selenium-webdriver_builder_class_Builder.html +0 -138
- package/docs/module_selenium-webdriver_chrome.html +0 -90
- package/docs/module_selenium-webdriver_chrome_class_Driver.html +0 -280
- package/docs/module_selenium-webdriver_chrome_class_Options.html +0 -127
- package/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html +0 -46
- package/docs/module_selenium-webdriver_class_ActionSequence.html +0 -115
- package/docs/module_selenium-webdriver_class_Builder.html +0 -138
- package/docs/module_selenium-webdriver_class_Capabilities.html +0 -60
- package/docs/module_selenium-webdriver_class_Command.html +0 -15
- package/docs/module_selenium-webdriver_class_EventEmitter.html +0 -35
- package/docs/module_selenium-webdriver_class_FileDetector.html +0 -21
- package/docs/module_selenium-webdriver_class_Serializable.html +0 -9
- package/docs/module_selenium-webdriver_class_Session.html +0 -13
- package/docs/module_selenium-webdriver_class_WebDriver.html +0 -313
- package/docs/module_selenium-webdriver_class_WebElement.html +0 -216
- package/docs/module_selenium-webdriver_class_WebElementPromise.html +0 -266
- package/docs/module_selenium-webdriver_enum_Browser.html +0 -2
- package/docs/module_selenium-webdriver_enum_Button.html +0 -2
- package/docs/module_selenium-webdriver_enum_Capability.html +0 -33
- package/docs/module_selenium-webdriver_enum_CommandName.html +0 -3
- package/docs/module_selenium-webdriver_enum_Key.html +0 -12
- package/docs/module_selenium-webdriver_error.html +0 -4
- package/docs/module_selenium-webdriver_error_class_Error.html +0 -15
- package/docs/module_selenium-webdriver_error_enum_ErrorCode.html +0 -3
- package/docs/module_selenium-webdriver_executors.html +0 -10
- package/docs/module_selenium-webdriver_executors_class_DeferredExecutor.html +0 -12
- package/docs/module_selenium-webdriver_firefox.html +0 -67
- package/docs/module_selenium-webdriver_firefox_binary.html +0 -4
- package/docs/module_selenium-webdriver_firefox_binary_class_Binary.html +0 -21
- package/docs/module_selenium-webdriver_firefox_class_Binary.html +0 -21
- package/docs/module_selenium-webdriver_firefox_class_Driver.html +0 -276
- package/docs/module_selenium-webdriver_firefox_class_Options.html +0 -19
- package/docs/module_selenium-webdriver_firefox_class_Profile.html +0 -50
- package/docs/module_selenium-webdriver_firefox_extension.html +0 -8
- package/docs/module_selenium-webdriver_firefox_profile.html +0 -13
- package/docs/module_selenium-webdriver_firefox_profile_class_Profile.html +0 -50
- package/docs/module_selenium-webdriver_http.html +0 -9
- package/docs/module_selenium-webdriver_http_class_Executor.html +0 -22
- package/docs/module_selenium-webdriver_http_class_HttpClient.html +0 -16
- package/docs/module_selenium-webdriver_http_class_Request.html +0 -12
- package/docs/module_selenium-webdriver_http_class_Response.html +0 -13
- package/docs/module_selenium-webdriver_http_util.html +0 -17
- package/docs/module_selenium-webdriver_ie.html +0 -11
- package/docs/module_selenium-webdriver_ie_class_Driver.html +0 -274
- package/docs/module_selenium-webdriver_ie_class_Options.html +0 -89
- package/docs/module_selenium-webdriver_ie_enum_Level.html +0 -1
- package/docs/module_selenium-webdriver_io.html +0 -37
- package/docs/module_selenium-webdriver_io_exec.html +0 -6
- package/docs/module_selenium-webdriver_namespace_By.html +0 -56
- package/docs/module_selenium-webdriver_namespace_error.html +0 -4
- package/docs/module_selenium-webdriver_namespace_logging.html +0 -18
- package/docs/module_selenium-webdriver_namespace_promise.html +0 -162
- package/docs/module_selenium-webdriver_namespace_stacktrace.html +0 -17
- package/docs/module_selenium-webdriver_namespace_until.html +0 -82
- package/docs/module_selenium-webdriver_net.html +0 -7
- package/docs/module_selenium-webdriver_net_portprober.html +0 -12
- package/docs/module_selenium-webdriver_opera.html +0 -62
- package/docs/module_selenium-webdriver_opera_class_Driver.html +0 -276
- package/docs/module_selenium-webdriver_opera_class_Options.html +0 -44
- package/docs/module_selenium-webdriver_opera_class_ServiceBuilder.html +0 -35
- package/docs/module_selenium-webdriver_phantomjs.html +0 -2
- package/docs/module_selenium-webdriver_phantomjs_class_Driver.html +0 -299
- package/docs/module_selenium-webdriver_proxy.html +0 -28
- package/docs/module_selenium-webdriver_remote.html +0 -8
- package/docs/module_selenium-webdriver_remote_class_DriverService.html +0 -32
- package/docs/module_selenium-webdriver_remote_class_FileDetector.html +0 -22
- package/docs/module_selenium-webdriver_remote_class_SeleniumServer.html +0 -40
- package/docs/module_selenium-webdriver_safari.html +0 -9
- package/docs/module_selenium-webdriver_safari_class_Driver.html +0 -279
- package/docs/module_selenium-webdriver_safari_class_Options.html +0 -23
- package/docs/module_selenium-webdriver_testing.html +0 -74
- package/docs/module_selenium-webdriver_testing_assert.html +0 -9
- package/docs/namespace_bot.html +0 -4
- package/docs/namespace_bot_json.html +0 -20
- package/docs/namespace_bot_response.html +0 -18
- package/docs/namespace_bot_userAgent.html +0 -33
- package/docs/namespace_webdriver.html +0 -30
- package/docs/namespace_webdriver_By.html +0 -56
- package/docs/namespace_webdriver_http.html +0 -6
- package/docs/namespace_webdriver_logging.html +0 -70
- package/docs/namespace_webdriver_promise.html +0 -165
- package/docs/namespace_webdriver_stacktrace.html +0 -17
- package/docs/namespace_webdriver_testing.html +0 -7
- package/docs/namespace_webdriver_testing_assert.html +0 -9
- package/docs/namespace_webdriver_testing_asserts.html +0 -18
- package/docs/namespace_webdriver_until.html +0 -82
- package/docs/source/_base.js.src.html +0 -1
- package/docs/source/builder.js.src.html +0 -1
- package/docs/source/chrome.js.src.html +0 -1
- package/docs/source/error.js.src.html +0 -1
- package/docs/source/executors.js.src.html +0 -1
- package/docs/source/firefox/binary.js.src.html +0 -1
- package/docs/source/firefox/extension.js.src.html +0 -1
- package/docs/source/firefox/index.js.src.html +0 -1
- package/docs/source/firefox/profile.js.src.html +0 -1
- package/docs/source/http/index.js.src.html +0 -1
- package/docs/source/http/util.js.src.html +0 -1
- package/docs/source/ie.js.src.html +0 -1
- package/docs/source/index.js.src.html +0 -1
- package/docs/source/io/exec.js.src.html +0 -1
- package/docs/source/io/index.js.src.html +0 -1
- package/docs/source/lib/atoms/error.js.src.html +0 -1
- package/docs/source/lib/atoms/json.js.src.html +0 -1
- package/docs/source/lib/atoms/response.js.src.html +0 -1
- package/docs/source/lib/atoms/userAgent.js.src.html +0 -1
- package/docs/source/lib/goog/array/array.js.src.html +0 -1
- package/docs/source/lib/goog/asserts/asserts.js.src.html +0 -1
- package/docs/source/lib/goog/async/freelist.js.src.html +0 -1
- package/docs/source/lib/goog/async/nexttick.js.src.html +0 -1
- package/docs/source/lib/goog/async/run.js.src.html +0 -1
- package/docs/source/lib/goog/async/workqueue.js.src.html +0 -1
- package/docs/source/lib/goog/base.js.src.html +0 -1
- package/docs/source/lib/goog/debug/debug.js.src.html +0 -1
- package/docs/source/lib/goog/debug/entrypointregistry.js.src.html +0 -1
- package/docs/source/lib/goog/debug/error.js.src.html +0 -1
- package/docs/source/lib/goog/debug/logbuffer.js.src.html +0 -1
- package/docs/source/lib/goog/debug/logger.js.src.html +0 -1
- package/docs/source/lib/goog/debug/logrecord.js.src.html +0 -1
- package/docs/source/lib/goog/deps.js.src.html +0 -1
- package/docs/source/lib/goog/disposable/disposable.js.src.html +0 -1
- package/docs/source/lib/goog/disposable/idisposable.js.src.html +0 -1
- package/docs/source/lib/goog/dom/browserfeature.js.src.html +0 -1
- package/docs/source/lib/goog/dom/dom.js.src.html +0 -1
- package/docs/source/lib/goog/dom/nodetype.js.src.html +0 -1
- package/docs/source/lib/goog/dom/safe.js.src.html +0 -1
- package/docs/source/lib/goog/dom/tagname.js.src.html +0 -1
- package/docs/source/lib/goog/dom/tags.js.src.html +0 -1
- package/docs/source/lib/goog/dom/vendor.js.src.html +0 -1
- package/docs/source/lib/goog/events/browserevent.js.src.html +0 -1
- package/docs/source/lib/goog/events/browserfeature.js.src.html +0 -1
- package/docs/source/lib/goog/events/event.js.src.html +0 -1
- package/docs/source/lib/goog/events/eventid.js.src.html +0 -1
- package/docs/source/lib/goog/events/events.js.src.html +0 -1
- package/docs/source/lib/goog/events/eventtarget.js.src.html +0 -1
- package/docs/source/lib/goog/events/eventtype.js.src.html +0 -1
- package/docs/source/lib/goog/events/keycodes.js.src.html +0 -1
- package/docs/source/lib/goog/events/listenable.js.src.html +0 -1
- package/docs/source/lib/goog/events/listener.js.src.html +0 -1
- package/docs/source/lib/goog/events/listenermap.js.src.html +0 -1
- package/docs/source/lib/goog/fs/url.js.src.html +0 -1
- package/docs/source/lib/goog/functions/functions.js.src.html +0 -1
- package/docs/source/lib/goog/html/safehtml.js.src.html +0 -1
- package/docs/source/lib/goog/html/safescript.js.src.html +0 -1
- package/docs/source/lib/goog/html/safestyle.js.src.html +0 -1
- package/docs/source/lib/goog/html/safestylesheet.js.src.html +0 -1
- package/docs/source/lib/goog/html/safeurl.js.src.html +0 -1
- package/docs/source/lib/goog/html/trustedresourceurl.js.src.html +0 -1
- package/docs/source/lib/goog/html/uncheckedconversions.js.src.html +0 -1
- package/docs/source/lib/goog/i18n/bidi.js.src.html +0 -1
- package/docs/source/lib/goog/iter/iter.js.src.html +0 -1
- package/docs/source/lib/goog/json/json.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/assertthat.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/matcher.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html +0 -1
- package/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html +0 -1
- package/docs/source/lib/goog/labs/useragent/browser.js.src.html +0 -1
- package/docs/source/lib/goog/labs/useragent/engine.js.src.html +0 -1
- package/docs/source/lib/goog/labs/useragent/platform.js.src.html +0 -1
- package/docs/source/lib/goog/labs/useragent/util.js.src.html +0 -1
- package/docs/source/lib/goog/math/box.js.src.html +0 -1
- package/docs/source/lib/goog/math/coordinate.js.src.html +0 -1
- package/docs/source/lib/goog/math/math.js.src.html +0 -1
- package/docs/source/lib/goog/math/rect.js.src.html +0 -1
- package/docs/source/lib/goog/math/size.js.src.html +0 -1
- package/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html +0 -1
- package/docs/source/lib/goog/net/xhrlike.js.src.html +0 -1
- package/docs/source/lib/goog/net/xmlhttp.js.src.html +0 -1
- package/docs/source/lib/goog/net/xmlhttpfactory.js.src.html +0 -1
- package/docs/source/lib/goog/object/object.js.src.html +0 -1
- package/docs/source/lib/goog/promise/promise.js.src.html +0 -1
- package/docs/source/lib/goog/promise/resolver.js.src.html +0 -1
- package/docs/source/lib/goog/promise/thenable.js.src.html +0 -1
- package/docs/source/lib/goog/reflect/reflect.js.src.html +0 -1
- package/docs/source/lib/goog/string/const.js.src.html +0 -1
- package/docs/source/lib/goog/string/string.js.src.html +0 -1
- package/docs/source/lib/goog/string/typedstring.js.src.html +0 -1
- package/docs/source/lib/goog/structs/collection.js.src.html +0 -1
- package/docs/source/lib/goog/structs/map.js.src.html +0 -1
- package/docs/source/lib/goog/structs/set.js.src.html +0 -1
- package/docs/source/lib/goog/structs/structs.js.src.html +0 -1
- package/docs/source/lib/goog/style/style.js.src.html +0 -1
- package/docs/source/lib/goog/testing/asserts.js.src.html +0 -1
- package/docs/source/lib/goog/testing/events/events.js.src.html +0 -1
- package/docs/source/lib/goog/testing/functionmock.js.src.html +0 -1
- package/docs/source/lib/goog/testing/jsunit.js.src.html +0 -1
- package/docs/source/lib/goog/testing/loosemock.js.src.html +0 -1
- package/docs/source/lib/goog/testing/mock.js.src.html +0 -1
- package/docs/source/lib/goog/testing/mockclock.js.src.html +0 -1
- package/docs/source/lib/goog/testing/mockcontrol.js.src.html +0 -1
- package/docs/source/lib/goog/testing/mockinterface.js.src.html +0 -1
- package/docs/source/lib/goog/testing/mockmatchers.js.src.html +0 -1
- package/docs/source/lib/goog/testing/objectpropertystring.js.src.html +0 -1
- package/docs/source/lib/goog/testing/propertyreplacer.js.src.html +0 -1
- package/docs/source/lib/goog/testing/recordfunction.js.src.html +0 -1
- package/docs/source/lib/goog/testing/stacktrace.js.src.html +0 -1
- package/docs/source/lib/goog/testing/strictmock.js.src.html +0 -1
- package/docs/source/lib/goog/testing/testcase.js.src.html +0 -1
- package/docs/source/lib/goog/testing/testrunner.js.src.html +0 -1
- package/docs/source/lib/goog/testing/watchers.js.src.html +0 -1
- package/docs/source/lib/goog/uri/uri.js.src.html +0 -1
- package/docs/source/lib/goog/uri/utils.js.src.html +0 -1
- package/docs/source/lib/goog/useragent/product.js.src.html +0 -1
- package/docs/source/lib/goog/useragent/product_isversion.js.src.html +0 -1
- package/docs/source/lib/goog/useragent/useragent.js.src.html +0 -1
- package/docs/source/lib/webdriver/actionsequence.js.src.html +0 -1
- package/docs/source/lib/webdriver/button.js.src.html +0 -1
- package/docs/source/lib/webdriver/capabilities.js.src.html +0 -1
- package/docs/source/lib/webdriver/command.js.src.html +0 -1
- package/docs/source/lib/webdriver/events.js.src.html +0 -1
- package/docs/source/lib/webdriver/http/http.js.src.html +0 -1
- package/docs/source/lib/webdriver/key.js.src.html +0 -1
- package/docs/source/lib/webdriver/locators.js.src.html +0 -1
- package/docs/source/lib/webdriver/logging.js.src.html +0 -1
- package/docs/source/lib/webdriver/promise.js.src.html +0 -1
- package/docs/source/lib/webdriver/serializable.js.src.html +0 -1
- package/docs/source/lib/webdriver/session.js.src.html +0 -1
- package/docs/source/lib/webdriver/stacktrace.js.src.html +0 -1
- package/docs/source/lib/webdriver/testing/asserts.js.src.html +0 -1
- package/docs/source/lib/webdriver/touchsequence.js.src.html +0 -1
- package/docs/source/lib/webdriver/until.js.src.html +0 -1
- package/docs/source/lib/webdriver/webdriver.js.src.html +0 -1
- package/docs/source/net/index.js.src.html +0 -1
- package/docs/source/net/portprober.js.src.html +0 -1
- package/docs/source/opera.js.src.html +0 -1
- package/docs/source/phantomjs.js.src.html +0 -1
- package/docs/source/proxy.js.src.html +0 -1
- package/docs/source/remote/index.js.src.html +0 -1
- package/docs/source/safari.js.src.html +0 -1
- package/docs/source/testing/assert.js.src.html +0 -1
- package/docs/source/testing/index.js.src.html +0 -1
- package/docs/types.js +0 -1
- package/lib/atoms/error.js +0 -202
- package/lib/atoms/json.js +0 -80
- package/lib/atoms/response.js +0 -110
- package/lib/atoms/userAgent.js +0 -257
- package/lib/goog/LICENSE +0 -176
- package/lib/goog/array/array.js +0 -1655
- package/lib/goog/asserts/asserts.js +0 -365
- package/lib/goog/async/freelist.js +0 -88
- package/lib/goog/async/nexttick.js +0 -241
- package/lib/goog/async/run.js +0 -139
- package/lib/goog/async/workqueue.js +0 -139
- package/lib/goog/base.js +0 -2547
- package/lib/goog/debug/debug.js +0 -653
- package/lib/goog/debug/entrypointregistry.js +0 -158
- package/lib/goog/debug/error.js +0 -63
- package/lib/goog/debug/logbuffer.js +0 -148
- package/lib/goog/debug/logger.js +0 -873
- package/lib/goog/debug/logrecord.js +0 -242
- package/lib/goog/deps.js +0 -151
- package/lib/goog/disposable/disposable.js +0 -307
- package/lib/goog/disposable/idisposable.js +0 -45
- package/lib/goog/dom/browserfeature.js +0 -72
- package/lib/goog/dom/dom.js +0 -2995
- package/lib/goog/dom/nodetype.js +0 -48
- package/lib/goog/dom/safe.js +0 -347
- package/lib/goog/dom/tagname.js +0 -160
- package/lib/goog/dom/tags.js +0 -42
- package/lib/goog/dom/vendor.js +0 -96
- package/lib/goog/events/browserevent.js +0 -386
- package/lib/goog/events/browserfeature.js +0 -85
- package/lib/goog/events/event.js +0 -143
- package/lib/goog/events/eventid.js +0 -47
- package/lib/goog/events/events.js +0 -984
- package/lib/goog/events/eventtarget.js +0 -394
- package/lib/goog/events/eventtype.js +0 -233
- package/lib/goog/events/keycodes.js +0 -420
- package/lib/goog/events/listenable.js +0 -335
- package/lib/goog/events/listener.js +0 -131
- package/lib/goog/events/listenermap.js +0 -308
- package/lib/goog/fs/url.js +0 -105
- package/lib/goog/functions/functions.js +0 -332
- package/lib/goog/html/safehtml.js +0 -756
- package/lib/goog/html/safescript.js +0 -234
- package/lib/goog/html/safestyle.js +0 -442
- package/lib/goog/html/safestylesheet.js +0 -276
- package/lib/goog/html/safeurl.js +0 -431
- package/lib/goog/html/trustedresourceurl.js +0 -224
- package/lib/goog/html/uncheckedconversions.js +0 -231
- package/lib/goog/i18n/bidi.js +0 -908
- package/lib/goog/iter/iter.js +0 -1305
- package/lib/goog/json/json.js +0 -365
- package/lib/goog/labs/testing/assertthat.js +0 -58
- package/lib/goog/labs/testing/logicmatcher.js +0 -212
- package/lib/goog/labs/testing/matcher.js +0 -80
- package/lib/goog/labs/testing/numbermatcher.js +0 -346
- package/lib/goog/labs/testing/objectmatcher.js +0 -317
- package/lib/goog/labs/testing/stringmatcher.js +0 -415
- package/lib/goog/labs/useragent/browser.js +0 -327
- package/lib/goog/labs/useragent/engine.js +0 -160
- package/lib/goog/labs/useragent/platform.js +0 -160
- package/lib/goog/labs/useragent/util.js +0 -148
- package/lib/goog/math/box.js +0 -389
- package/lib/goog/math/coordinate.js +0 -268
- package/lib/goog/math/math.js +0 -451
- package/lib/goog/math/rect.js +0 -476
- package/lib/goog/math/size.js +0 -227
- package/lib/goog/net/wrapperxmlhttpfactory.js +0 -71
- package/lib/goog/net/xhrlike.js +0 -124
- package/lib/goog/net/xmlhttp.js +0 -246
- package/lib/goog/net/xmlhttpfactory.js +0 -67
- package/lib/goog/object/object.js +0 -686
- package/lib/goog/promise/promise.js +0 -1310
- package/lib/goog/promise/resolver.js +0 -48
- package/lib/goog/promise/thenable.js +0 -126
- package/lib/goog/reflect/reflect.js +0 -78
- package/lib/goog/string/const.js +0 -182
- package/lib/goog/string/string.js +0 -1565
- package/lib/goog/string/typedstring.js +0 -48
- package/lib/goog/structs/collection.js +0 -56
- package/lib/goog/structs/map.js +0 -456
- package/lib/goog/structs/set.js +0 -279
- package/lib/goog/structs/structs.js +0 -354
- package/lib/goog/style/style.js +0 -2054
- package/lib/goog/testing/asserts.js +0 -1265
- package/lib/goog/testing/events/events.js +0 -727
- package/lib/goog/testing/functionmock.js +0 -176
- package/lib/goog/testing/jsunit.js +0 -162
- package/lib/goog/testing/loosemock.js +0 -242
- package/lib/goog/testing/mock.js +0 -645
- package/lib/goog/testing/mockclock.js +0 -617
- package/lib/goog/testing/mockcontrol.js +0 -220
- package/lib/goog/testing/mockinterface.js +0 -45
- package/lib/goog/testing/mockmatchers.js +0 -400
- package/lib/goog/testing/objectpropertystring.js +0 -68
- package/lib/goog/testing/propertyreplacer.js +0 -245
- package/lib/goog/testing/recordfunction.js +0 -215
- package/lib/goog/testing/stacktrace.js +0 -594
- package/lib/goog/testing/strictmock.js +0 -130
- package/lib/goog/testing/testcase.js +0 -1569
- package/lib/goog/testing/testrunner.js +0 -440
- package/lib/goog/testing/watchers.js +0 -46
- package/lib/goog/uri/uri.js +0 -1507
- package/lib/goog/uri/utils.js +0 -1116
- package/lib/goog/useragent/product.js +0 -175
- package/lib/goog/useragent/product_isversion.js +0 -143
- package/lib/goog/useragent/useragent.js +0 -538
- package/lib/webdriver/actionsequence.js +0 -356
- package/lib/webdriver/builder.js +0 -208
- package/lib/webdriver/capabilities.js +0 -411
- package/lib/webdriver/events.js +0 -179
- package/lib/webdriver/firefoxdomexecutor.js +0 -175
- package/lib/webdriver/http/corsclient.js +0 -133
- package/lib/webdriver/http/http.js +0 -523
- package/lib/webdriver/http/xhrclient.js +0 -67
- package/lib/webdriver/locators.js +0 -257
- package/lib/webdriver/logging.js +0 -347
- package/lib/webdriver/promise.js +0 -2609
- package/lib/webdriver/serializable.js +0 -41
- package/lib/webdriver/session.js +0 -74
- package/lib/webdriver/stacktrace.js +0 -619
- package/lib/webdriver/test/builder_test.js +0 -53
- package/lib/webdriver/test/capabilities_test.js +0 -72
- package/lib/webdriver/test/events_test.js +0 -203
- package/lib/webdriver/test/http/corsclient_test.js +0 -151
- package/lib/webdriver/test/http/http_test.js +0 -428
- package/lib/webdriver/test/http/xhrclient_test.js +0 -196
- package/lib/webdriver/test/locators_test.js +0 -59
- package/lib/webdriver/test/logging_test.js +0 -85
- package/lib/webdriver/test/promise_error_test.js +0 -719
- package/lib/webdriver/test/promise_flow_test.js +0 -1957
- package/lib/webdriver/test/promise_generator_test.js +0 -251
- package/lib/webdriver/test/promise_test.js +0 -1981
- package/lib/webdriver/test/stacktrace_test.js +0 -480
- package/lib/webdriver/test/test_bootstrap.js +0 -67
- package/lib/webdriver/test/testing/asserts_test.js +0 -117
- package/lib/webdriver/test/testing/client_test.js +0 -88
- package/lib/webdriver/test/testing/testcase_test.js +0 -229
- package/lib/webdriver/test/testutil.js +0 -209
- package/lib/webdriver/test/testutil_test.js +0 -104
- package/lib/webdriver/test/until_test.js +0 -411
- package/lib/webdriver/test/webdriver_generator_test.js +0 -92
- package/lib/webdriver/test/webdriver_test.js +0 -2369
- package/lib/webdriver/testing/asserts.js +0 -487
- package/lib/webdriver/testing/client.js +0 -179
- package/lib/webdriver/testing/jsunit.js +0 -363
- package/lib/webdriver/testing/testcase.js +0 -168
- package/lib/webdriver/testing/window.js +0 -244
- package/lib/webdriver/touchsequence.js +0 -248
- package/lib/webdriver/until.js +0 -412
- package/lib/webdriver/webdriver.js +0 -2492
- package/test/_base_test.js +0 -129
package/lib/webdriver/promise.js
DELETED
|
@@ -1,2609 +0,0 @@
|
|
|
1
|
-
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
2
|
-
// or more contributor license agreements. See the NOTICE file
|
|
3
|
-
// distributed with this work for additional information
|
|
4
|
-
// regarding copyright ownership. The SFC licenses this file
|
|
5
|
-
// to you under the Apache License, Version 2.0 (the
|
|
6
|
-
// "License"); you may not use this file except in compliance
|
|
7
|
-
// with the License. You may obtain a copy of the License at
|
|
8
|
-
//
|
|
9
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
//
|
|
11
|
-
// Unless required by applicable law or agreed to in writing,
|
|
12
|
-
// software distributed under the License is distributed on an
|
|
13
|
-
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
-
// KIND, either express or implied. See the License for the
|
|
15
|
-
// specific language governing permissions and limitations
|
|
16
|
-
// under the License.
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @license Portions of this code are from the Dojo toolkit, received under the
|
|
20
|
-
* BSD License:
|
|
21
|
-
* Redistribution and use in source and binary forms, with or without
|
|
22
|
-
* modification, are permitted provided that the following conditions are met:
|
|
23
|
-
*
|
|
24
|
-
* * Redistributions of source code must retain the above copyright notice,
|
|
25
|
-
* this list of conditions and the following disclaimer.
|
|
26
|
-
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
27
|
-
* this list of conditions and the following disclaimer in the documentation
|
|
28
|
-
* and/or other materials provided with the distribution.
|
|
29
|
-
* * Neither the name of the Dojo Foundation nor the names of its contributors
|
|
30
|
-
* may be used to endorse or promote products derived from this software
|
|
31
|
-
* without specific prior written permission.
|
|
32
|
-
*
|
|
33
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
34
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
35
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
36
|
-
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
37
|
-
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
38
|
-
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
39
|
-
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
40
|
-
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
41
|
-
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
42
|
-
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
43
|
-
* POSSIBILITY OF SUCH DAMAGE.
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @fileoverview A promise implementation based on the CommonJS promise/A and
|
|
48
|
-
* promise/B proposals. For more information, see
|
|
49
|
-
* http://wiki.commonjs.org/wiki/Promises.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
goog.module('webdriver.promise');
|
|
53
|
-
goog.module.declareLegacyNamespace();
|
|
54
|
-
|
|
55
|
-
var Arrays = goog.require('goog.array');
|
|
56
|
-
var asserts = goog.require('goog.asserts');
|
|
57
|
-
var asyncRun = goog.require('goog.async.run');
|
|
58
|
-
var throwException = goog.require('goog.async.throwException');
|
|
59
|
-
var DebugError = goog.require('goog.debug.Error');
|
|
60
|
-
var Objects = goog.require('goog.object');
|
|
61
|
-
var EventEmitter = goog.require('webdriver.EventEmitter');
|
|
62
|
-
var stacktrace = goog.require('webdriver.stacktrace');
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @define {boolean} Whether to append traces of {@code then} to rejection
|
|
68
|
-
* errors.
|
|
69
|
-
*/
|
|
70
|
-
goog.define('webdriver.promise.LONG_STACK_TRACES', false);
|
|
71
|
-
|
|
72
|
-
/** @const */
|
|
73
|
-
var promise = exports;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Generates an error to capture the current stack trace.
|
|
78
|
-
* @param {string} name Error name for this stack trace.
|
|
79
|
-
* @param {string} msg Message to record.
|
|
80
|
-
* @param {!Function} topFn The function that should appear at the top of the
|
|
81
|
-
* stack; only applicable in V8.
|
|
82
|
-
* @return {!Error} The generated error.
|
|
83
|
-
*/
|
|
84
|
-
promise.captureStackTrace = function(name, msg, topFn) {
|
|
85
|
-
var e = Error(msg);
|
|
86
|
-
e.name = name;
|
|
87
|
-
if (Error.captureStackTrace) {
|
|
88
|
-
Error.captureStackTrace(e, topFn);
|
|
89
|
-
} else {
|
|
90
|
-
var stack = stacktrace.getStack(e);
|
|
91
|
-
e.stack = e.toString();
|
|
92
|
-
if (stack) {
|
|
93
|
-
e.stack += '\n' + stack;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return e;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Error used when the computation of a promise is cancelled.
|
|
102
|
-
*
|
|
103
|
-
* @param {string=} opt_msg The cancellation message.
|
|
104
|
-
* @constructor
|
|
105
|
-
* @extends {DebugError}
|
|
106
|
-
* @final
|
|
107
|
-
*/
|
|
108
|
-
promise.CancellationError = function(opt_msg) {
|
|
109
|
-
DebugError.call(this, opt_msg);
|
|
110
|
-
|
|
111
|
-
/** @override */
|
|
112
|
-
this.name = 'CancellationError';
|
|
113
|
-
};
|
|
114
|
-
goog.inherits(promise.CancellationError, DebugError);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Wraps the given error in a CancellationError. Will trivially return
|
|
119
|
-
* the error itself if it is an instanceof CancellationError.
|
|
120
|
-
*
|
|
121
|
-
* @param {*} error The error to wrap.
|
|
122
|
-
* @param {string=} opt_msg The prefix message to use.
|
|
123
|
-
* @return {!promise.CancellationError} A cancellation error.
|
|
124
|
-
*/
|
|
125
|
-
promise.CancellationError.wrap = function(error, opt_msg) {
|
|
126
|
-
if (error instanceof promise.CancellationError) {
|
|
127
|
-
return /** @type {!promise.CancellationError} */(error);
|
|
128
|
-
} else if (opt_msg) {
|
|
129
|
-
var message = opt_msg;
|
|
130
|
-
if (error) {
|
|
131
|
-
message += ': ' + error;
|
|
132
|
-
}
|
|
133
|
-
return new promise.CancellationError(message);
|
|
134
|
-
}
|
|
135
|
-
var message;
|
|
136
|
-
if (error) {
|
|
137
|
-
message = error + '';
|
|
138
|
-
}
|
|
139
|
-
return new promise.CancellationError(message);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Thenable is a promise-like object with a {@code then} method which may be
|
|
146
|
-
* used to schedule callbacks on a promised value.
|
|
147
|
-
*
|
|
148
|
-
* @interface
|
|
149
|
-
* @extends {IThenable<T>}
|
|
150
|
-
* @template T
|
|
151
|
-
*/
|
|
152
|
-
promise.Thenable = function() {};
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Cancels the computation of this promise's value, rejecting the promise in the
|
|
157
|
-
* process. This method is a no-op if the promise has already been resolved.
|
|
158
|
-
*
|
|
159
|
-
* @param {(string|promise.CancellationError)=} opt_reason The reason this
|
|
160
|
-
* promise is being cancelled.
|
|
161
|
-
*/
|
|
162
|
-
promise.Thenable.prototype.cancel = function(opt_reason) {};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
/** @return {boolean} Whether this promise's value is still being computed. */
|
|
166
|
-
promise.Thenable.prototype.isPending = function() {};
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Registers listeners for when this instance is resolved.
|
|
171
|
-
*
|
|
172
|
-
* @param {?(function(T): (R|IThenable<R>))=} opt_callback The
|
|
173
|
-
* function to call if this promise is successfully resolved. The function
|
|
174
|
-
* should expect a single argument: the promise's resolved value.
|
|
175
|
-
* @param {?(function(*): (R|IThenable<R>))=} opt_errback
|
|
176
|
-
* The function to call if this promise is rejected. The function should
|
|
177
|
-
* expect a single argument: the rejection reason.
|
|
178
|
-
* @return {!promise.Promise<R>} A new promise which will be
|
|
179
|
-
* resolved with the result of the invoked callback.
|
|
180
|
-
* @template R
|
|
181
|
-
*/
|
|
182
|
-
promise.Thenable.prototype.then = function(opt_callback, opt_errback) {};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Registers a listener for when this promise is rejected. This is synonymous
|
|
187
|
-
* with the {@code catch} clause in a synchronous API:
|
|
188
|
-
*
|
|
189
|
-
* // Synchronous API:
|
|
190
|
-
* try {
|
|
191
|
-
* doSynchronousWork();
|
|
192
|
-
* } catch (ex) {
|
|
193
|
-
* console.error(ex);
|
|
194
|
-
* }
|
|
195
|
-
*
|
|
196
|
-
* // Asynchronous promise API:
|
|
197
|
-
* doAsynchronousWork().thenCatch(function(ex) {
|
|
198
|
-
* console.error(ex);
|
|
199
|
-
* });
|
|
200
|
-
*
|
|
201
|
-
* @param {function(*): (R|IThenable<R>)} errback The
|
|
202
|
-
* function to call if this promise is rejected. The function should
|
|
203
|
-
* expect a single argument: the rejection reason.
|
|
204
|
-
* @return {!promise.Promise<R>} A new promise which will be
|
|
205
|
-
* resolved with the result of the invoked callback.
|
|
206
|
-
* @template R
|
|
207
|
-
*/
|
|
208
|
-
promise.Thenable.prototype.thenCatch = function(errback) {};
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Registers a listener to invoke when this promise is resolved, regardless
|
|
213
|
-
* of whether the promise's value was successfully computed. This function
|
|
214
|
-
* is synonymous with the {@code finally} clause in a synchronous API:
|
|
215
|
-
*
|
|
216
|
-
* // Synchronous API:
|
|
217
|
-
* try {
|
|
218
|
-
* doSynchronousWork();
|
|
219
|
-
* } finally {
|
|
220
|
-
* cleanUp();
|
|
221
|
-
* }
|
|
222
|
-
*
|
|
223
|
-
* // Asynchronous promise API:
|
|
224
|
-
* doAsynchronousWork().thenFinally(cleanUp);
|
|
225
|
-
*
|
|
226
|
-
* __Note:__ similar to the {@code finally} clause, if the registered
|
|
227
|
-
* callback returns a rejected promise or throws an error, it will silently
|
|
228
|
-
* replace the rejection error (if any) from this promise:
|
|
229
|
-
*
|
|
230
|
-
* try {
|
|
231
|
-
* throw Error('one');
|
|
232
|
-
* } finally {
|
|
233
|
-
* throw Error('two'); // Hides Error: one
|
|
234
|
-
* }
|
|
235
|
-
*
|
|
236
|
-
* promise.rejected(Error('one'))
|
|
237
|
-
* .thenFinally(function() {
|
|
238
|
-
* throw Error('two'); // Hides Error: one
|
|
239
|
-
* });
|
|
240
|
-
*
|
|
241
|
-
* @param {function(): (R|IThenable<R>)} callback The function
|
|
242
|
-
* to call when this promise is resolved.
|
|
243
|
-
* @return {!promise.Promise<R>} A promise that will be fulfilled
|
|
244
|
-
* with the callback result.
|
|
245
|
-
* @template R
|
|
246
|
-
*/
|
|
247
|
-
promise.Thenable.prototype.thenFinally = function(callback) {};
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Property used to flag constructor's as implementing the Thenable interface
|
|
252
|
-
* for runtime type checking.
|
|
253
|
-
* @type {string}
|
|
254
|
-
* @const
|
|
255
|
-
*/
|
|
256
|
-
var IMPLEMENTED_BY_PROP = '$webdriver_Thenable';
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Adds a property to a class prototype to allow runtime checks of whether
|
|
261
|
-
* instances of that class implement the Thenable interface. This function will
|
|
262
|
-
* also ensure the prototype's {@code then} function is exported from compiled
|
|
263
|
-
* code.
|
|
264
|
-
* @param {function(new: promise.Thenable, ...?)} ctor The
|
|
265
|
-
* constructor whose prototype to modify.
|
|
266
|
-
*/
|
|
267
|
-
promise.Thenable.addImplementation = function(ctor) {
|
|
268
|
-
// Based on goog.promise.Thenable.isImplementation.
|
|
269
|
-
ctor.prototype['then'] = ctor.prototype.then;
|
|
270
|
-
try {
|
|
271
|
-
// Old IE7 does not support defineProperty; IE8 only supports it for
|
|
272
|
-
// DOM elements.
|
|
273
|
-
Object.defineProperty(
|
|
274
|
-
ctor.prototype,
|
|
275
|
-
IMPLEMENTED_BY_PROP,
|
|
276
|
-
{'value': true, 'enumerable': false});
|
|
277
|
-
} catch (ex) {
|
|
278
|
-
ctor.prototype[IMPLEMENTED_BY_PROP] = true;
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Checks if an object has been tagged for implementing the Thenable interface
|
|
285
|
-
* as defined by {@link webdriver.promise.Thenable.addImplementation}.
|
|
286
|
-
* @param {*} object The object to test.
|
|
287
|
-
* @return {boolean} Whether the object is an implementation of the Thenable
|
|
288
|
-
* interface.
|
|
289
|
-
*/
|
|
290
|
-
promise.Thenable.isImplementation = function(object) {
|
|
291
|
-
// Based on goog.promise.Thenable.isImplementation.
|
|
292
|
-
if (!object) {
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
try {
|
|
296
|
-
return !!object[IMPLEMENTED_BY_PROP];
|
|
297
|
-
} catch (e) {
|
|
298
|
-
return false; // Property access seems to be forbidden.
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* @enum {string}
|
|
306
|
-
*/
|
|
307
|
-
var PromiseState = {
|
|
308
|
-
PENDING: 'pending',
|
|
309
|
-
BLOCKED: 'blocked',
|
|
310
|
-
REJECTED: 'rejected',
|
|
311
|
-
FULFILLED: 'fulfilled'
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Represents the eventual value of a completed operation. Each promise may be
|
|
318
|
-
* in one of three states: pending, fulfilled, or rejected. Each promise starts
|
|
319
|
-
* in the pending state and may make a single transition to either a
|
|
320
|
-
* fulfilled or rejected state, at which point the promise is considered
|
|
321
|
-
* resolved.
|
|
322
|
-
*
|
|
323
|
-
* @param {function(
|
|
324
|
-
* function((T|IThenable<T>|Thenable)=),
|
|
325
|
-
* function(*=))} resolver
|
|
326
|
-
* Function that is invoked immediately to begin computation of this
|
|
327
|
-
* promise's value. The function should accept a pair of callback functions,
|
|
328
|
-
* one for fulfilling the promise and another for rejecting it.
|
|
329
|
-
* @param {promise.ControlFlow=} opt_flow The control flow
|
|
330
|
-
* this instance was created under. Defaults to the currently active flow.
|
|
331
|
-
* @constructor
|
|
332
|
-
* @implements {promise.Thenable<T>}
|
|
333
|
-
* @template T
|
|
334
|
-
* @see http://promises-aplus.github.io/promises-spec/
|
|
335
|
-
*/
|
|
336
|
-
promise.Promise = function(resolver, opt_flow) {
|
|
337
|
-
goog.getUid(this);
|
|
338
|
-
|
|
339
|
-
/** @private {!promise.ControlFlow} */
|
|
340
|
-
this.flow_ = opt_flow || promise.controlFlow();
|
|
341
|
-
|
|
342
|
-
/** @private {Error} */
|
|
343
|
-
this.stack_ = null;
|
|
344
|
-
if (promise.LONG_STACK_TRACES) {
|
|
345
|
-
this.stack_ = promise.captureStackTrace('Promise', 'new', promise.Promise);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/** @private {promise.Promise<?>} */
|
|
349
|
-
this.parent_ = null;
|
|
350
|
-
|
|
351
|
-
/** @private {Array<!Callback>} */
|
|
352
|
-
this.callbacks_ = null;
|
|
353
|
-
|
|
354
|
-
/** @private {PromiseState} */
|
|
355
|
-
this.state_ = PromiseState.PENDING;
|
|
356
|
-
|
|
357
|
-
/** @private {boolean} */
|
|
358
|
-
this.handled_ = false;
|
|
359
|
-
|
|
360
|
-
/** @private {boolean} */
|
|
361
|
-
this.pendingNotifications_ = false;
|
|
362
|
-
|
|
363
|
-
/** @private {*} */
|
|
364
|
-
this.value_ = undefined;
|
|
365
|
-
|
|
366
|
-
try {
|
|
367
|
-
var self = this;
|
|
368
|
-
resolver(function(value) {
|
|
369
|
-
self.resolve_(PromiseState.FULFILLED, value);
|
|
370
|
-
}, function(reason) {
|
|
371
|
-
self.resolve_(PromiseState.REJECTED, reason);
|
|
372
|
-
});
|
|
373
|
-
} catch (ex) {
|
|
374
|
-
this.resolve_(PromiseState.REJECTED, ex);
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
promise.Thenable.addImplementation(promise.Promise);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
/** @override */
|
|
381
|
-
promise.Promise.prototype.toString = function() {
|
|
382
|
-
return 'Promise::' + goog.getUid(this) +
|
|
383
|
-
' {[[PromiseStatus]]: "' + this.state_ + '"}';
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Resolves this promise. If the new value is itself a promise, this function
|
|
389
|
-
* will wait for it to be resolved before notifying the registered listeners.
|
|
390
|
-
* @param {PromiseState} newState The promise's new state.
|
|
391
|
-
* @param {*} newValue The promise's new value.
|
|
392
|
-
* @throws {TypeError} If {@code newValue === this}.
|
|
393
|
-
* @private
|
|
394
|
-
*/
|
|
395
|
-
promise.Promise.prototype.resolve_ = function(newState, newValue) {
|
|
396
|
-
if (PromiseState.PENDING !== this.state_) {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (newValue === this) {
|
|
401
|
-
// See promise a+, 2.3.1
|
|
402
|
-
// http://promises-aplus.github.io/promises-spec/#point-48
|
|
403
|
-
throw new TypeError('A promise may not resolve to itself');
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
this.parent_ = null;
|
|
407
|
-
this.state_ = PromiseState.BLOCKED;
|
|
408
|
-
|
|
409
|
-
if (promise.Thenable.isImplementation(newValue)) {
|
|
410
|
-
// 2.3.2
|
|
411
|
-
newValue = /** @type {!promise.Thenable} */(newValue);
|
|
412
|
-
newValue.then(
|
|
413
|
-
this.unblockAndResolve_.bind(this, PromiseState.FULFILLED),
|
|
414
|
-
this.unblockAndResolve_.bind(this, PromiseState.REJECTED));
|
|
415
|
-
return;
|
|
416
|
-
|
|
417
|
-
} else if (goog.isObject(newValue)) {
|
|
418
|
-
// 2.3.3
|
|
419
|
-
|
|
420
|
-
try {
|
|
421
|
-
// 2.3.3.1
|
|
422
|
-
var then = newValue['then'];
|
|
423
|
-
} catch (e) {
|
|
424
|
-
// 2.3.3.2
|
|
425
|
-
this.state_ = PromiseState.REJECTED;
|
|
426
|
-
this.value_ = e;
|
|
427
|
-
this.scheduleNotifications_();
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// NB: goog.isFunction is loose and will accept instanceof Function.
|
|
432
|
-
if (typeof then === 'function') {
|
|
433
|
-
// 2.3.3.3
|
|
434
|
-
this.invokeThen_(newValue, then);
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (newState === PromiseState.REJECTED &&
|
|
440
|
-
isError(newValue) && newValue.stack && this.stack_) {
|
|
441
|
-
newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// 2.3.3.4 and 2.3.4
|
|
445
|
-
this.state_ = newState;
|
|
446
|
-
this.value_ = newValue;
|
|
447
|
-
this.scheduleNotifications_();
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Invokes a thenable's "then" method according to 2.3.3.3 of the promise
|
|
453
|
-
* A+ spec.
|
|
454
|
-
* @param {!Object} x The thenable object.
|
|
455
|
-
* @param {!Function} then The "then" function to invoke.
|
|
456
|
-
* @private
|
|
457
|
-
*/
|
|
458
|
-
promise.Promise.prototype.invokeThen_ = function(x, then) {
|
|
459
|
-
var called = false;
|
|
460
|
-
var self = this;
|
|
461
|
-
|
|
462
|
-
var resolvePromise = function(value) {
|
|
463
|
-
if (!called) { // 2.3.3.3.3
|
|
464
|
-
called = true;
|
|
465
|
-
// 2.3.3.3.1
|
|
466
|
-
self.unblockAndResolve_(PromiseState.FULFILLED, value);
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
var rejectPromise = function(reason) {
|
|
471
|
-
if (!called) { // 2.3.3.3.3
|
|
472
|
-
called = true;
|
|
473
|
-
// 2.3.3.3.2
|
|
474
|
-
self.unblockAndResolve_(PromiseState.REJECTED, reason);
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
try {
|
|
479
|
-
// 2.3.3.3
|
|
480
|
-
then.call(x, resolvePromise, rejectPromise);
|
|
481
|
-
} catch (e) {
|
|
482
|
-
// 2.3.3.3.4.2
|
|
483
|
-
rejectPromise(e);
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* @param {PromiseState} newState The promise's new state.
|
|
490
|
-
* @param {*} newValue The promise's new value.
|
|
491
|
-
* @private
|
|
492
|
-
*/
|
|
493
|
-
promise.Promise.prototype.unblockAndResolve_ = function(newState, newValue) {
|
|
494
|
-
if (this.state_ === PromiseState.BLOCKED) {
|
|
495
|
-
this.state_ = PromiseState.PENDING;
|
|
496
|
-
this.resolve_(newState, newValue);
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* @private
|
|
503
|
-
*/
|
|
504
|
-
promise.Promise.prototype.scheduleNotifications_ = function() {
|
|
505
|
-
if (!this.pendingNotifications_) {
|
|
506
|
-
this.pendingNotifications_ = true;
|
|
507
|
-
this.flow_.suspend_();
|
|
508
|
-
|
|
509
|
-
var activeFrame;
|
|
510
|
-
|
|
511
|
-
if (!this.handled_ &&
|
|
512
|
-
this.state_ === PromiseState.REJECTED &&
|
|
513
|
-
!(this.value_ instanceof promise.CancellationError)) {
|
|
514
|
-
activeFrame = this.flow_.getActiveFrame_();
|
|
515
|
-
activeFrame.pendingRejection = true;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (this.callbacks_ && this.callbacks_.length) {
|
|
519
|
-
activeFrame = this.flow_.getRunningFrame_();
|
|
520
|
-
var self = this;
|
|
521
|
-
this.callbacks_.forEach(function(callback) {
|
|
522
|
-
if (!callback.frame_.getParent()) {
|
|
523
|
-
activeFrame.addChild(callback.frame_);
|
|
524
|
-
}
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
asyncRun(goog.bind(this.notifyAll_, this, activeFrame));
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Notifies all of the listeners registered with this promise that its state
|
|
535
|
-
* has changed.
|
|
536
|
-
* @param {Frame} frame The active frame from when this round of
|
|
537
|
-
* notifications were scheduled.
|
|
538
|
-
* @private
|
|
539
|
-
*/
|
|
540
|
-
promise.Promise.prototype.notifyAll_ = function(frame) {
|
|
541
|
-
this.flow_.resume_();
|
|
542
|
-
this.pendingNotifications_ = false;
|
|
543
|
-
|
|
544
|
-
if (!this.handled_ &&
|
|
545
|
-
this.state_ === PromiseState.REJECTED &&
|
|
546
|
-
!(this.value_ instanceof promise.CancellationError)) {
|
|
547
|
-
this.flow_.abortFrame_(this.value_, frame);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (this.callbacks_) {
|
|
551
|
-
var callbacks = this.callbacks_;
|
|
552
|
-
this.callbacks_ = null;
|
|
553
|
-
callbacks.forEach(this.notify_, this);
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Notifies a single callback of this promise's change ins tate.
|
|
560
|
-
* @param {Callback} callback The callback to notify.
|
|
561
|
-
* @private
|
|
562
|
-
*/
|
|
563
|
-
promise.Promise.prototype.notify_ = function(callback) {
|
|
564
|
-
callback.notify(this.state_, this.value_);
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
/** @override */
|
|
569
|
-
promise.Promise.prototype.cancel = function(opt_reason) {
|
|
570
|
-
if (!this.isPending()) {
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
if (this.parent_) {
|
|
575
|
-
this.parent_.cancel(opt_reason);
|
|
576
|
-
} else {
|
|
577
|
-
this.resolve_(
|
|
578
|
-
PromiseState.REJECTED,
|
|
579
|
-
promise.CancellationError.wrap(opt_reason));
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
/** @override */
|
|
585
|
-
promise.Promise.prototype.isPending = function() {
|
|
586
|
-
return this.state_ === PromiseState.PENDING;
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
/** @override */
|
|
591
|
-
promise.Promise.prototype.then = function(opt_callback, opt_errback) {
|
|
592
|
-
return this.addCallback_(
|
|
593
|
-
opt_callback, opt_errback, 'then', promise.Promise.prototype.then);
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
/** @override */
|
|
598
|
-
promise.Promise.prototype.thenCatch = function(errback) {
|
|
599
|
-
return this.addCallback_(
|
|
600
|
-
null, errback, 'thenCatch', promise.Promise.prototype.thenCatch);
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
/** @override */
|
|
605
|
-
promise.Promise.prototype.thenFinally = function(callback) {
|
|
606
|
-
var error;
|
|
607
|
-
var mustThrow = false;
|
|
608
|
-
return this.then(function() {
|
|
609
|
-
return callback();
|
|
610
|
-
}, function(err) {
|
|
611
|
-
error = err;
|
|
612
|
-
mustThrow = true;
|
|
613
|
-
return callback();
|
|
614
|
-
}).then(function() {
|
|
615
|
-
if (mustThrow) {
|
|
616
|
-
throw error;
|
|
617
|
-
}
|
|
618
|
-
});
|
|
619
|
-
};
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
/**
|
|
623
|
-
* Registers a new callback with this promise
|
|
624
|
-
* @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
|
|
625
|
-
* fulfillment callback.
|
|
626
|
-
* @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
|
|
627
|
-
* rejection callback.
|
|
628
|
-
* @param {string} name The callback name.
|
|
629
|
-
* @param {!Function} fn The function to use as the top of the stack when
|
|
630
|
-
* recording the callback's creation point.
|
|
631
|
-
* @return {!promise.Promise<R>} A new promise which will be resolved with the
|
|
632
|
-
* esult of the invoked callback.
|
|
633
|
-
* @template R
|
|
634
|
-
* @private
|
|
635
|
-
*/
|
|
636
|
-
promise.Promise.prototype.addCallback_ = function(callback, errback, name, fn) {
|
|
637
|
-
if (!goog.isFunction(callback) && !goog.isFunction(errback)) {
|
|
638
|
-
return this;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
this.handled_ = true;
|
|
642
|
-
var cb = new Callback(this, callback, errback, name, fn);
|
|
643
|
-
|
|
644
|
-
if (!this.callbacks_) {
|
|
645
|
-
this.callbacks_ = [];
|
|
646
|
-
}
|
|
647
|
-
this.callbacks_.push(cb);
|
|
648
|
-
|
|
649
|
-
if (this.state_ !== PromiseState.PENDING &&
|
|
650
|
-
this.state_ !== PromiseState.BLOCKED) {
|
|
651
|
-
this.flow_.getSchedulingFrame_().addChild(cb.frame_);
|
|
652
|
-
this.scheduleNotifications_();
|
|
653
|
-
}
|
|
654
|
-
return cb.promise;
|
|
655
|
-
};
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Represents a value that will be resolved at some point in the future. This
|
|
660
|
-
* class represents the protected "producer" half of a Promise - each Deferred
|
|
661
|
-
* has a {@code promise} property that may be returned to consumers for
|
|
662
|
-
* registering callbacks, reserving the ability to resolve the deferred to the
|
|
663
|
-
* producer.
|
|
664
|
-
*
|
|
665
|
-
* If this Deferred is rejected and there are no listeners registered before
|
|
666
|
-
* the next turn of the event loop, the rejection will be passed to the
|
|
667
|
-
* {@link webdriver.promise.ControlFlow} as an unhandled failure.
|
|
668
|
-
*
|
|
669
|
-
* @param {promise.ControlFlow=} opt_flow The control flow
|
|
670
|
-
* this instance was created under. This should only be provided during
|
|
671
|
-
* unit tests.
|
|
672
|
-
* @constructor
|
|
673
|
-
* @implements {promise.Thenable<T>}
|
|
674
|
-
* @template T
|
|
675
|
-
*/
|
|
676
|
-
promise.Deferred = function(opt_flow) {
|
|
677
|
-
var fulfill, reject;
|
|
678
|
-
|
|
679
|
-
/** @type {!promise.Promise<T>} */
|
|
680
|
-
this.promise = new promise.Promise(function(f, r) {
|
|
681
|
-
fulfill = f;
|
|
682
|
-
reject = r;
|
|
683
|
-
}, opt_flow);
|
|
684
|
-
|
|
685
|
-
var self = this;
|
|
686
|
-
var checkNotSelf = function(value) {
|
|
687
|
-
if (value === self) {
|
|
688
|
-
throw new TypeError('May not resolve a Deferred with itself');
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
* Resolves this deferred with the given value. It is safe to call this as a
|
|
694
|
-
* normal function (with no bound "this").
|
|
695
|
-
* @param {(T|IThenable<T>|Thenable)=} opt_value The fulfilled value.
|
|
696
|
-
*/
|
|
697
|
-
this.fulfill = function(opt_value) {
|
|
698
|
-
checkNotSelf(opt_value);
|
|
699
|
-
fulfill(opt_value);
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Rejects this promise with the given reason. It is safe to call this as a
|
|
704
|
-
* normal function (with no bound "this").
|
|
705
|
-
* @param {*=} opt_reason The rejection reason.
|
|
706
|
-
*/
|
|
707
|
-
this.reject = function(opt_reason) {
|
|
708
|
-
checkNotSelf(opt_reason);
|
|
709
|
-
reject(opt_reason);
|
|
710
|
-
};
|
|
711
|
-
};
|
|
712
|
-
promise.Thenable.addImplementation(promise.Deferred);
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
/** @override */
|
|
716
|
-
promise.Deferred.prototype.isPending = function() {
|
|
717
|
-
return this.promise.isPending();
|
|
718
|
-
};
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
/** @override */
|
|
722
|
-
promise.Deferred.prototype.cancel = function(opt_reason) {
|
|
723
|
-
this.promise.cancel(opt_reason);
|
|
724
|
-
};
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
/**
|
|
728
|
-
* @override
|
|
729
|
-
* @deprecated Use {@code then} from the promise property directly.
|
|
730
|
-
*/
|
|
731
|
-
promise.Deferred.prototype.then = function(opt_cb, opt_eb) {
|
|
732
|
-
return this.promise.then(opt_cb, opt_eb);
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* @override
|
|
738
|
-
* @deprecated Use {@code thenCatch} from the promise property directly.
|
|
739
|
-
*/
|
|
740
|
-
promise.Deferred.prototype.thenCatch = function(opt_eb) {
|
|
741
|
-
return this.promise.thenCatch(opt_eb);
|
|
742
|
-
};
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* @override
|
|
747
|
-
* @deprecated Use {@code thenFinally} from the promise property directly.
|
|
748
|
-
*/
|
|
749
|
-
promise.Deferred.prototype.thenFinally = function(opt_cb) {
|
|
750
|
-
return this.promise.thenFinally(opt_cb);
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Tests if a value is an Error-like object. This is more than an straight
|
|
756
|
-
* instanceof check since the value may originate from another context.
|
|
757
|
-
* @param {*} value The value to test.
|
|
758
|
-
* @return {boolean} Whether the value is an error.
|
|
759
|
-
*/
|
|
760
|
-
function isError(value) {
|
|
761
|
-
return value instanceof Error ||
|
|
762
|
-
goog.isObject(value) &&
|
|
763
|
-
(goog.isString(value.message) ||
|
|
764
|
-
// A special test for goog.testing.JsUnitException.
|
|
765
|
-
value.isJsUnitException);
|
|
766
|
-
|
|
767
|
-
};
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* Determines whether a {@code value} should be treated as a promise.
|
|
772
|
-
* Any object whose "then" property is a function will be considered a promise.
|
|
773
|
-
*
|
|
774
|
-
* @param {*} value The value to test.
|
|
775
|
-
* @return {boolean} Whether the value is a promise.
|
|
776
|
-
*/
|
|
777
|
-
promise.isPromise = function(value) {
|
|
778
|
-
return !!value && goog.isObject(value) &&
|
|
779
|
-
// Use array notation so the Closure compiler does not obfuscate away our
|
|
780
|
-
// contract. Use typeof rather than goog.isFunction because
|
|
781
|
-
// goog.isFunction accepts instanceof Function, which the promise spec
|
|
782
|
-
// does not.
|
|
783
|
-
typeof value['then'] === 'function';
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* Creates a promise that will be resolved at a set time in the future.
|
|
789
|
-
* @param {number} ms The amount of time, in milliseconds, to wait before
|
|
790
|
-
* resolving the promise.
|
|
791
|
-
* @return {!promise.Promise} The promise.
|
|
792
|
-
*/
|
|
793
|
-
promise.delayed = function(ms) {
|
|
794
|
-
var key;
|
|
795
|
-
return new promise.Promise(function(fulfill) {
|
|
796
|
-
key = setTimeout(function() {
|
|
797
|
-
key = null;
|
|
798
|
-
fulfill();
|
|
799
|
-
}, ms);
|
|
800
|
-
}).thenCatch(function(e) {
|
|
801
|
-
clearTimeout(key);
|
|
802
|
-
key = null;
|
|
803
|
-
throw e;
|
|
804
|
-
});
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
/**
|
|
809
|
-
* Creates a new deferred object.
|
|
810
|
-
* @return {!promise.Deferred<T>} The new deferred object.
|
|
811
|
-
* @template T
|
|
812
|
-
*/
|
|
813
|
-
promise.defer = function() {
|
|
814
|
-
return new promise.Deferred();
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
/**
|
|
819
|
-
* Creates a promise that has been resolved with the given value.
|
|
820
|
-
* @param {T=} opt_value The resolved value.
|
|
821
|
-
* @return {!promise.Promise<T>} The resolved promise.
|
|
822
|
-
* @template T
|
|
823
|
-
*/
|
|
824
|
-
promise.fulfilled = function(opt_value) {
|
|
825
|
-
if (opt_value instanceof promise.Promise) {
|
|
826
|
-
return opt_value;
|
|
827
|
-
}
|
|
828
|
-
return new promise.Promise(function(fulfill) {
|
|
829
|
-
fulfill(opt_value);
|
|
830
|
-
});
|
|
831
|
-
};
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* Creates a promise that has been rejected with the given reason.
|
|
836
|
-
* @param {*=} opt_reason The rejection reason; may be any value, but is
|
|
837
|
-
* usually an Error or a string.
|
|
838
|
-
* @return {!promise.Promise<T>} The rejected promise.
|
|
839
|
-
* @template T
|
|
840
|
-
*/
|
|
841
|
-
promise.rejected = function(opt_reason) {
|
|
842
|
-
if (opt_reason instanceof promise.Promise) {
|
|
843
|
-
return opt_reason;
|
|
844
|
-
}
|
|
845
|
-
return new promise.Promise(function(_, reject) {
|
|
846
|
-
reject(opt_reason);
|
|
847
|
-
});
|
|
848
|
-
};
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Wraps a function that expects a node-style callback as its final
|
|
853
|
-
* argument. This callback expects two arguments: an error value (which will be
|
|
854
|
-
* null if the call succeeded), and the success value as the second argument.
|
|
855
|
-
* The callback will the resolve or reject the returned promise, based on its arguments.
|
|
856
|
-
* @param {!Function} fn The function to wrap.
|
|
857
|
-
* @param {...?} var_args The arguments to apply to the function, excluding the
|
|
858
|
-
* final callback.
|
|
859
|
-
* @return {!promise.Promise} A promise that will be resolved with the
|
|
860
|
-
* result of the provided function's callback.
|
|
861
|
-
*/
|
|
862
|
-
promise.checkedNodeCall = function(fn, var_args) {
|
|
863
|
-
var args = Arrays.slice(arguments, 1);
|
|
864
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
865
|
-
try {
|
|
866
|
-
args.push(function(error, value) {
|
|
867
|
-
error ? reject(error) : fulfill(value);
|
|
868
|
-
});
|
|
869
|
-
fn.apply(undefined, args);
|
|
870
|
-
} catch (ex) {
|
|
871
|
-
reject(ex);
|
|
872
|
-
}
|
|
873
|
-
});
|
|
874
|
-
};
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* Registers an observer on a promised {@code value}, returning a new promise
|
|
879
|
-
* that will be resolved when the value is. If {@code value} is not a promise,
|
|
880
|
-
* then the return promise will be immediately resolved.
|
|
881
|
-
* @param {*} value The value to observe.
|
|
882
|
-
* @param {Function=} opt_callback The function to call when the value is
|
|
883
|
-
* resolved successfully.
|
|
884
|
-
* @param {Function=} opt_errback The function to call when the value is
|
|
885
|
-
* rejected.
|
|
886
|
-
* @return {!promise.Promise} A new promise.
|
|
887
|
-
*/
|
|
888
|
-
promise.when = function(value, opt_callback, opt_errback) {
|
|
889
|
-
if (promise.Thenable.isImplementation(value)) {
|
|
890
|
-
return value.then(opt_callback, opt_errback);
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
894
|
-
promise.asap(value, fulfill, reject);
|
|
895
|
-
}).then(opt_callback, opt_errback);
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* Invokes the appropriate callback function as soon as a promised
|
|
901
|
-
* {@code value} is resolved. This function is similar to
|
|
902
|
-
* {@link webdriver.promise.when}, except it does not return a new promise.
|
|
903
|
-
* @param {*} value The value to observe.
|
|
904
|
-
* @param {Function} callback The function to call when the value is
|
|
905
|
-
* resolved successfully.
|
|
906
|
-
* @param {Function=} opt_errback The function to call when the value is
|
|
907
|
-
* rejected.
|
|
908
|
-
*/
|
|
909
|
-
promise.asap = function(value, callback, opt_errback) {
|
|
910
|
-
if (promise.isPromise(value)) {
|
|
911
|
-
value.then(callback, opt_errback);
|
|
912
|
-
|
|
913
|
-
// Maybe a Dojo-like deferred object?
|
|
914
|
-
} else if (!!value && goog.isObject(value) &&
|
|
915
|
-
goog.isFunction(value.addCallbacks)) {
|
|
916
|
-
value.addCallbacks(callback, opt_errback);
|
|
917
|
-
|
|
918
|
-
// A raw value, return a resolved promise.
|
|
919
|
-
} else if (callback) {
|
|
920
|
-
callback(value);
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
/**
|
|
926
|
-
* Given an array of promises, will return a promise that will be fulfilled
|
|
927
|
-
* with the fulfillment values of the input array's values. If any of the
|
|
928
|
-
* input array's promises are rejected, the returned promise will be rejected
|
|
929
|
-
* with the same reason.
|
|
930
|
-
*
|
|
931
|
-
* @param {!Array<(T|!promise.Promise<T>)>} arr An array of
|
|
932
|
-
* promises to wait on.
|
|
933
|
-
* @return {!promise.Promise<!Array<T>>} A promise that is
|
|
934
|
-
* fulfilled with an array containing the fulfilled values of the
|
|
935
|
-
* input array, or rejected with the same reason as the first
|
|
936
|
-
* rejected value.
|
|
937
|
-
* @template T
|
|
938
|
-
*/
|
|
939
|
-
promise.all = function(arr) {
|
|
940
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
941
|
-
var n = arr.length;
|
|
942
|
-
var values = [];
|
|
943
|
-
|
|
944
|
-
if (!n) {
|
|
945
|
-
fulfill(values);
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
var toFulfill = n;
|
|
950
|
-
var onFulfilled = function(index, value) {
|
|
951
|
-
values[index] = value;
|
|
952
|
-
toFulfill--;
|
|
953
|
-
if (toFulfill == 0) {
|
|
954
|
-
fulfill(values);
|
|
955
|
-
}
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
for (var i = 0; i < n; ++i) {
|
|
959
|
-
promise.asap(arr[i], goog.partial(onFulfilled, i), reject);
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
};
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* Calls a function for each element in an array and inserts the result into a
|
|
967
|
-
* new array, which is used as the fulfillment value of the promise returned
|
|
968
|
-
* by this function.
|
|
969
|
-
*
|
|
970
|
-
* If the return value of the mapping function is a promise, this function
|
|
971
|
-
* will wait for it to be fulfilled before inserting it into the new array.
|
|
972
|
-
*
|
|
973
|
-
* If the mapping function throws or returns a rejected promise, the
|
|
974
|
-
* promise returned by this function will be rejected with the same reason.
|
|
975
|
-
* Only the first failure will be reported; all subsequent errors will be
|
|
976
|
-
* silently ignored.
|
|
977
|
-
*
|
|
978
|
-
* @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
|
|
979
|
-
* array to iterator over, or a promise that will resolve to said array.
|
|
980
|
-
* @param {function(this: SELF, TYPE, number, !Array<TYPE>): ?} fn The
|
|
981
|
-
* function to call for each element in the array. This function should
|
|
982
|
-
* expect three arguments (the element, the index, and the array itself.
|
|
983
|
-
* @param {SELF=} opt_self The object to be used as the value of 'this' within
|
|
984
|
-
* {@code fn}.
|
|
985
|
-
* @template TYPE, SELF
|
|
986
|
-
*/
|
|
987
|
-
promise.map = function(arr, fn, opt_self) {
|
|
988
|
-
return promise.fulfilled(arr).then(function(arr) {
|
|
989
|
-
goog.asserts.assertNumber(arr.length, 'not an array like value');
|
|
990
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
991
|
-
var n = arr.length;
|
|
992
|
-
var values = new Array(n);
|
|
993
|
-
(function processNext(i) {
|
|
994
|
-
for (; i < n; i++) {
|
|
995
|
-
if (i in arr) {
|
|
996
|
-
break;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
if (i >= n) {
|
|
1000
|
-
fulfill(values);
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
try {
|
|
1004
|
-
promise.asap(
|
|
1005
|
-
fn.call(opt_self, arr[i], i, /** @type {!Array} */(arr)),
|
|
1006
|
-
function(value) {
|
|
1007
|
-
values[i] = value;
|
|
1008
|
-
processNext(i + 1);
|
|
1009
|
-
},
|
|
1010
|
-
reject);
|
|
1011
|
-
} catch (ex) {
|
|
1012
|
-
reject(ex);
|
|
1013
|
-
}
|
|
1014
|
-
})(0);
|
|
1015
|
-
});
|
|
1016
|
-
});
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
/**
|
|
1021
|
-
* Calls a function for each element in an array, and if the function returns
|
|
1022
|
-
* true adds the element to a new array.
|
|
1023
|
-
*
|
|
1024
|
-
* If the return value of the filter function is a promise, this function
|
|
1025
|
-
* will wait for it to be fulfilled before determining whether to insert the
|
|
1026
|
-
* element into the new array.
|
|
1027
|
-
*
|
|
1028
|
-
* If the filter function throws or returns a rejected promise, the promise
|
|
1029
|
-
* returned by this function will be rejected with the same reason. Only the
|
|
1030
|
-
* first failure will be reported; all subsequent errors will be silently
|
|
1031
|
-
* ignored.
|
|
1032
|
-
*
|
|
1033
|
-
* @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
|
|
1034
|
-
* array to iterator over, or a promise that will resolve to said array.
|
|
1035
|
-
* @param {function(this: SELF, TYPE, number, !Array<TYPE>): (
|
|
1036
|
-
* boolean|promise.Promise<boolean>)} fn The function
|
|
1037
|
-
* to call for each element in the array.
|
|
1038
|
-
* @param {SELF=} opt_self The object to be used as the value of 'this' within
|
|
1039
|
-
* {@code fn}.
|
|
1040
|
-
* @template TYPE, SELF
|
|
1041
|
-
*/
|
|
1042
|
-
promise.filter = function(arr, fn, opt_self) {
|
|
1043
|
-
return promise.fulfilled(arr).then(function(arr) {
|
|
1044
|
-
goog.asserts.assertNumber(arr.length, 'not an array like value');
|
|
1045
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
1046
|
-
var n = arr.length;
|
|
1047
|
-
var values = [];
|
|
1048
|
-
var valuesLength = 0;
|
|
1049
|
-
(function processNext(i) {
|
|
1050
|
-
for (; i < n; i++) {
|
|
1051
|
-
if (i in arr) {
|
|
1052
|
-
break;
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
if (i >= n) {
|
|
1056
|
-
fulfill(values);
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
try {
|
|
1060
|
-
var value = arr[i];
|
|
1061
|
-
var include = fn.call(opt_self, value, i, /** @type {!Array} */(arr));
|
|
1062
|
-
promise.asap(include, function(include) {
|
|
1063
|
-
if (include) {
|
|
1064
|
-
values[valuesLength++] = value;
|
|
1065
|
-
}
|
|
1066
|
-
processNext(i + 1);
|
|
1067
|
-
}, reject);
|
|
1068
|
-
} catch (ex) {
|
|
1069
|
-
reject(ex);
|
|
1070
|
-
}
|
|
1071
|
-
})(0);
|
|
1072
|
-
});
|
|
1073
|
-
});
|
|
1074
|
-
};
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
/**
|
|
1078
|
-
* Returns a promise that will be resolved with the input value in a
|
|
1079
|
-
* fully-resolved state. If the value is an array, each element will be fully
|
|
1080
|
-
* resolved. Likewise, if the value is an object, all keys will be fully
|
|
1081
|
-
* resolved. In both cases, all nested arrays and objects will also be
|
|
1082
|
-
* fully resolved. All fields are resolved in place; the returned promise will
|
|
1083
|
-
* resolve on {@code value} and not a copy.
|
|
1084
|
-
*
|
|
1085
|
-
* Warning: This function makes no checks against objects that contain
|
|
1086
|
-
* cyclical references:
|
|
1087
|
-
*
|
|
1088
|
-
* var value = {};
|
|
1089
|
-
* value['self'] = value;
|
|
1090
|
-
* promise.fullyResolved(value); // Stack overflow.
|
|
1091
|
-
*
|
|
1092
|
-
* @param {*} value The value to fully resolve.
|
|
1093
|
-
* @return {!promise.Promise} A promise for a fully resolved version
|
|
1094
|
-
* of the input value.
|
|
1095
|
-
*/
|
|
1096
|
-
promise.fullyResolved = function(value) {
|
|
1097
|
-
if (promise.isPromise(value)) {
|
|
1098
|
-
return promise.when(value, fullyResolveValue);
|
|
1099
|
-
}
|
|
1100
|
-
return fullyResolveValue(value);
|
|
1101
|
-
};
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
/**
|
|
1105
|
-
* @param {*} value The value to fully resolve. If a promise, assumed to
|
|
1106
|
-
* already be resolved.
|
|
1107
|
-
* @return {!promise.Promise} A promise for a fully resolved version
|
|
1108
|
-
* of the input value.
|
|
1109
|
-
*/
|
|
1110
|
-
function fullyResolveValue(value) {
|
|
1111
|
-
switch (goog.typeOf(value)) {
|
|
1112
|
-
case 'array':
|
|
1113
|
-
return fullyResolveKeys(/** @type {!Array} */ (value));
|
|
1114
|
-
|
|
1115
|
-
case 'object':
|
|
1116
|
-
if (promise.isPromise(value)) {
|
|
1117
|
-
// We get here when the original input value is a promise that
|
|
1118
|
-
// resolves to itself. When the user provides us with such a promise,
|
|
1119
|
-
// trust that it counts as a "fully resolved" value and return it.
|
|
1120
|
-
// Of course, since it's already a promise, we can just return it
|
|
1121
|
-
// to the user instead of wrapping it in another promise.
|
|
1122
|
-
return /** @type {!promise.Promise} */ (value);
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
if (goog.isNumber(value.nodeType) &&
|
|
1126
|
-
goog.isObject(value.ownerDocument) &&
|
|
1127
|
-
goog.isNumber(value.ownerDocument.nodeType)) {
|
|
1128
|
-
// DOM node; return early to avoid infinite recursion. Should we
|
|
1129
|
-
// only support objects with a certain level of nesting?
|
|
1130
|
-
return promise.fulfilled(value);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
return fullyResolveKeys(/** @type {!Object} */ (value));
|
|
1134
|
-
|
|
1135
|
-
default: // boolean, function, null, number, string, undefined
|
|
1136
|
-
return promise.fulfilled(value);
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* @param {!(Array|Object)} obj the object to resolve.
|
|
1143
|
-
* @return {!promise.Promise} A promise that will be resolved with the
|
|
1144
|
-
* input object once all of its values have been fully resolved.
|
|
1145
|
-
*/
|
|
1146
|
-
function fullyResolveKeys(obj) {
|
|
1147
|
-
var isArray = goog.isArray(obj);
|
|
1148
|
-
var numKeys = isArray ? obj.length : Objects.getCount(obj);
|
|
1149
|
-
if (!numKeys) {
|
|
1150
|
-
return promise.fulfilled(obj);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
var numResolved = 0;
|
|
1154
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
1155
|
-
// In pre-IE9, goog.array.forEach will not iterate properly over arrays
|
|
1156
|
-
// containing undefined values because "index in array" returns false
|
|
1157
|
-
// when array[index] === undefined (even for x = [undefined, 1]). To get
|
|
1158
|
-
// around this, we need to use our own forEach implementation.
|
|
1159
|
-
// DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be
|
|
1160
|
-
// reproduced in IE9 by changing the browser/document modes, it requires an
|
|
1161
|
-
// actual pre-IE9 browser. Yay, IE!
|
|
1162
|
-
var forEachKey = !isArray ? Objects.forEach : function(arr, fn) {
|
|
1163
|
-
var n = arr.length;
|
|
1164
|
-
for (var i = 0; i < n; ++i) {
|
|
1165
|
-
fn.call(null, arr[i], i, arr);
|
|
1166
|
-
}
|
|
1167
|
-
};
|
|
1168
|
-
|
|
1169
|
-
forEachKey(obj, function(partialValue, key) {
|
|
1170
|
-
var type = goog.typeOf(partialValue);
|
|
1171
|
-
if (type != 'array' && type != 'object') {
|
|
1172
|
-
maybeResolveValue();
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
promise.fullyResolved(partialValue).then(
|
|
1177
|
-
function(resolvedValue) {
|
|
1178
|
-
obj[key] = resolvedValue;
|
|
1179
|
-
maybeResolveValue();
|
|
1180
|
-
},
|
|
1181
|
-
reject);
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
function maybeResolveValue() {
|
|
1185
|
-
if (++numResolved == numKeys) {
|
|
1186
|
-
fulfill(obj);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
});
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
//////////////////////////////////////////////////////////////////////////////
|
|
1194
|
-
//
|
|
1195
|
-
// promise.ControlFlow
|
|
1196
|
-
//
|
|
1197
|
-
//////////////////////////////////////////////////////////////////////////////
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
/**
|
|
1202
|
-
* Handles the execution of scheduled tasks, each of which may be an
|
|
1203
|
-
* asynchronous operation. The control flow will ensure tasks are executed in
|
|
1204
|
-
* the ordered scheduled, starting each task only once those before it have
|
|
1205
|
-
* completed.
|
|
1206
|
-
*
|
|
1207
|
-
* Each task scheduled within this flow may return a
|
|
1208
|
-
* {@link webdriver.promise.Promise} to indicate it is an asynchronous
|
|
1209
|
-
* operation. The ControlFlow will wait for such promises to be resolved before
|
|
1210
|
-
* marking the task as completed.
|
|
1211
|
-
*
|
|
1212
|
-
* Tasks and each callback registered on a {@link webdriver.promise.Promise}
|
|
1213
|
-
* will be run in their own ControlFlow frame. Any tasks scheduled within a
|
|
1214
|
-
* frame will take priority over previously scheduled tasks. Furthermore, if any
|
|
1215
|
-
* of the tasks in the frame fail, the remainder of the tasks in that frame will
|
|
1216
|
-
* be discarded and the failure will be propagated to the user through the
|
|
1217
|
-
* callback/task's promised result.
|
|
1218
|
-
*
|
|
1219
|
-
* Each time a ControlFlow empties its task queue, it will fire an
|
|
1220
|
-
* {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event. Conversely,
|
|
1221
|
-
* whenever the flow terminates due to an unhandled error, it will remove all
|
|
1222
|
-
* remaining tasks in its queue and fire an
|
|
1223
|
-
* {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION
|
|
1224
|
-
* UNCAUGHT_EXCEPTION} event. If there are no listeners registered with the
|
|
1225
|
-
* flow, the error will be rethrown to the global error handler.
|
|
1226
|
-
*
|
|
1227
|
-
* @constructor
|
|
1228
|
-
* @extends {EventEmitter}
|
|
1229
|
-
* @final
|
|
1230
|
-
*/
|
|
1231
|
-
promise.ControlFlow = function() {
|
|
1232
|
-
EventEmitter.call(this);
|
|
1233
|
-
goog.getUid(this);
|
|
1234
|
-
|
|
1235
|
-
/**
|
|
1236
|
-
* Tracks the active execution frame for this instance. Lazily initialized
|
|
1237
|
-
* when the first task is scheduled.
|
|
1238
|
-
* @private {Frame}
|
|
1239
|
-
*/
|
|
1240
|
-
this.activeFrame_ = null;
|
|
1241
|
-
|
|
1242
|
-
/**
|
|
1243
|
-
* A reference to the frame which is currently top of the stack in
|
|
1244
|
-
* {@link #runInFrame_}. The {@link #activeFrame_} will always be an ancestor
|
|
1245
|
-
* of the {@link #runningFrame_}, but the two will often not be the same. The
|
|
1246
|
-
* active frame represents which frame is currently executing a task while the
|
|
1247
|
-
* running frame represents either the task itself or a promise callback which
|
|
1248
|
-
* has fired asynchronously.
|
|
1249
|
-
* @private {Frame}
|
|
1250
|
-
*/
|
|
1251
|
-
this.runningFrame_ = null;
|
|
1252
|
-
|
|
1253
|
-
/**
|
|
1254
|
-
* A reference to the frame in which new tasks should be scheduled. If
|
|
1255
|
-
* {@code null}, tasks will be scheduled within the active frame. When forcing
|
|
1256
|
-
* a function to run in the context of a new frame, this pointer is used to
|
|
1257
|
-
* ensure tasks are scheduled within the newly created frame, even though it
|
|
1258
|
-
* won't be active yet.
|
|
1259
|
-
* @private {Frame}
|
|
1260
|
-
* @see {#runInFrame_}
|
|
1261
|
-
*/
|
|
1262
|
-
this.schedulingFrame_ = null;
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* Micro task that controls shutting down the control flow. Upon shut down,
|
|
1266
|
-
* the flow will emit an {@link webdriver.promise.ControlFlow.EventType.IDLE}
|
|
1267
|
-
* event. Idle events always follow a brief timeout in order to catch latent
|
|
1268
|
-
* errors from the last completed task. If this task had a callback
|
|
1269
|
-
* registered, but no errback, and the task fails, the unhandled failure would
|
|
1270
|
-
* not be reported by the promise system until the next turn of the event
|
|
1271
|
-
* loop:
|
|
1272
|
-
*
|
|
1273
|
-
* // Schedule 1 task that fails.
|
|
1274
|
-
* var result = promise.controlFlow().schedule('example',
|
|
1275
|
-
* function() { return promise.rejected('failed'); });
|
|
1276
|
-
* // Set a callback on the result. This delays reporting the unhandled
|
|
1277
|
-
* // failure for 1 turn of the event loop.
|
|
1278
|
-
* result.then(goog.nullFunction);
|
|
1279
|
-
*
|
|
1280
|
-
* @private {MicroTask}
|
|
1281
|
-
*/
|
|
1282
|
-
this.shutdownTask_ = null;
|
|
1283
|
-
|
|
1284
|
-
/**
|
|
1285
|
-
* Micro task used to trigger execution of this instance's event loop.
|
|
1286
|
-
* @private {MicroTask}
|
|
1287
|
-
*/
|
|
1288
|
-
this.eventLoopTask_ = null;
|
|
1289
|
-
|
|
1290
|
-
/**
|
|
1291
|
-
* ID for a long running interval used to keep a Node.js process running
|
|
1292
|
-
* while a control flow's event loop has yielded. This is a cheap hack
|
|
1293
|
-
* required since the {@link #runEventLoop_} is only scheduled to run when
|
|
1294
|
-
* there is _actually_ something to run. When a control flow is waiting on
|
|
1295
|
-
* a task, there will be nothing in the JS event loop and the process would
|
|
1296
|
-
* terminate without this.
|
|
1297
|
-
*
|
|
1298
|
-
* An alternative solution would be to change {@link #runEventLoop_} to run
|
|
1299
|
-
* as an interval rather than as on-demand micro-tasks. While this approach
|
|
1300
|
-
* (which was previously used) requires fewer micro-task allocations, it
|
|
1301
|
-
* results in many unnecessary invocations of {@link #runEventLoop_}.
|
|
1302
|
-
*
|
|
1303
|
-
* @private {?number}
|
|
1304
|
-
*/
|
|
1305
|
-
this.hold_ = null;
|
|
1306
|
-
|
|
1307
|
-
/**
|
|
1308
|
-
* The number of holds placed on this flow. These represent points where the
|
|
1309
|
-
* flow must not execute any further actions so an asynchronous action may
|
|
1310
|
-
* run first. One such example are notifications fired by a
|
|
1311
|
-
* {@link webdriver.promise.Promise}: the Promise spec requires that callbacks
|
|
1312
|
-
* are invoked in a turn of the event loop after they are scheduled. To ensure
|
|
1313
|
-
* tasks within a callback are scheduled in the correct frame, a promise will
|
|
1314
|
-
* make the parent flow yield before its notifications are fired.
|
|
1315
|
-
* @private {number}
|
|
1316
|
-
*/
|
|
1317
|
-
this.yieldCount_ = 0;
|
|
1318
|
-
};
|
|
1319
|
-
goog.inherits(promise.ControlFlow, EventEmitter);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
/**
|
|
1323
|
-
* Events that may be emitted by an {@link webdriver.promise.ControlFlow}.
|
|
1324
|
-
* @enum {string}
|
|
1325
|
-
*/
|
|
1326
|
-
promise.ControlFlow.EventType = {
|
|
1327
|
-
|
|
1328
|
-
/** Emitted when all tasks have been successfully executed. */
|
|
1329
|
-
IDLE: 'idle',
|
|
1330
|
-
|
|
1331
|
-
/** Emitted when a ControlFlow has been reset. */
|
|
1332
|
-
RESET: 'reset',
|
|
1333
|
-
|
|
1334
|
-
/** Emitted whenever a new task has been scheduled. */
|
|
1335
|
-
SCHEDULE_TASK: 'scheduleTask',
|
|
1336
|
-
|
|
1337
|
-
/**
|
|
1338
|
-
* Emitted whenever a control flow aborts due to an unhandled promise
|
|
1339
|
-
* rejection. This event will be emitted along with the offending rejection
|
|
1340
|
-
* reason. Upon emitting this event, the control flow will empty its task
|
|
1341
|
-
* queue and revert to its initial state.
|
|
1342
|
-
*/
|
|
1343
|
-
UNCAUGHT_EXCEPTION: 'uncaughtException'
|
|
1344
|
-
};
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
/**
|
|
1348
|
-
* Returns a string representation of this control flow, which is its current
|
|
1349
|
-
* {@link #getSchedule() schedule}, sans task stack traces.
|
|
1350
|
-
* @return {string} The string representation of this contorl flow.
|
|
1351
|
-
* @override
|
|
1352
|
-
*/
|
|
1353
|
-
promise.ControlFlow.prototype.toString = function() {
|
|
1354
|
-
return this.getSchedule();
|
|
1355
|
-
};
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
/**
|
|
1359
|
-
* Resets this instance, clearing its queue and removing all event listeners.
|
|
1360
|
-
*/
|
|
1361
|
-
promise.ControlFlow.prototype.reset = function() {
|
|
1362
|
-
this.activeFrame_ = null;
|
|
1363
|
-
this.schedulingFrame_ = null;
|
|
1364
|
-
this.emit(promise.ControlFlow.EventType.RESET);
|
|
1365
|
-
this.removeAllListeners();
|
|
1366
|
-
this.cancelShutdown_();
|
|
1367
|
-
this.cancelEventLoop_();
|
|
1368
|
-
};
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
/**
|
|
1372
|
-
* Generates an annotated string describing the internal state of this control
|
|
1373
|
-
* flow, including the currently executing as well as pending tasks. If
|
|
1374
|
-
* {@code opt_includeStackTraces === true}, the string will include the
|
|
1375
|
-
* stack trace from when each task was scheduled.
|
|
1376
|
-
* @param {string=} opt_includeStackTraces Whether to include the stack traces
|
|
1377
|
-
* from when each task was scheduled. Defaults to false.
|
|
1378
|
-
* @return {string} String representation of this flow's internal state.
|
|
1379
|
-
*/
|
|
1380
|
-
promise.ControlFlow.prototype.getSchedule = function(opt_includeStackTraces) {
|
|
1381
|
-
var ret = 'ControlFlow::' + goog.getUid(this);
|
|
1382
|
-
var activeFrame = this.activeFrame_;
|
|
1383
|
-
var runningFrame = this.runningFrame_;
|
|
1384
|
-
if (!activeFrame) {
|
|
1385
|
-
return ret;
|
|
1386
|
-
}
|
|
1387
|
-
var childIndent = '| ';
|
|
1388
|
-
return ret + '\n' + toStringHelper(activeFrame.getRoot(), childIndent);
|
|
1389
|
-
|
|
1390
|
-
/**
|
|
1391
|
-
* @param {!(Frame|Task)} node .
|
|
1392
|
-
* @param {string} indent .
|
|
1393
|
-
* @param {boolean=} opt_isPending .
|
|
1394
|
-
* @return {string} .
|
|
1395
|
-
*/
|
|
1396
|
-
function toStringHelper(node, indent, opt_isPending) {
|
|
1397
|
-
var ret = node.toString();
|
|
1398
|
-
if (opt_isPending) {
|
|
1399
|
-
ret = '(pending) ' + ret;
|
|
1400
|
-
}
|
|
1401
|
-
if (node === activeFrame) {
|
|
1402
|
-
ret = '(active) ' + ret;
|
|
1403
|
-
}
|
|
1404
|
-
if (node === runningFrame) {
|
|
1405
|
-
ret = '(running) ' + ret;
|
|
1406
|
-
}
|
|
1407
|
-
if (node instanceof Frame) {
|
|
1408
|
-
if (node.getPendingTask()) {
|
|
1409
|
-
ret += '\n' + toStringHelper(
|
|
1410
|
-
/** @type {!Task} */(node.getPendingTask()),
|
|
1411
|
-
childIndent,
|
|
1412
|
-
true);
|
|
1413
|
-
}
|
|
1414
|
-
if (node.children_) {
|
|
1415
|
-
node.children_.forEach(function(child) {
|
|
1416
|
-
if (!node.getPendingTask() ||
|
|
1417
|
-
node.getPendingTask().getFrame() !== child) {
|
|
1418
|
-
ret += '\n' + toStringHelper(child, childIndent);
|
|
1419
|
-
}
|
|
1420
|
-
});
|
|
1421
|
-
}
|
|
1422
|
-
} else {
|
|
1423
|
-
var task = /** @type {!Task} */(node);
|
|
1424
|
-
if (opt_includeStackTraces && task.promise.stack_) {
|
|
1425
|
-
ret += '\n' + childIndent +
|
|
1426
|
-
(task.promise.stack_.stack || task.promise.stack_).
|
|
1427
|
-
replace(/\n/g, '\n' + childIndent);
|
|
1428
|
-
}
|
|
1429
|
-
if (task.getFrame()) {
|
|
1430
|
-
ret += '\n' + toStringHelper(
|
|
1431
|
-
/** @type {!Frame} */(task.getFrame()),
|
|
1432
|
-
childIndent);
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
return indent + ret.replace(/\n/g, '\n' + indent);
|
|
1436
|
-
}
|
|
1437
|
-
};
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
/**
|
|
1441
|
-
* @return {!Frame} The active frame for this flow.
|
|
1442
|
-
* @private
|
|
1443
|
-
*/
|
|
1444
|
-
promise.ControlFlow.prototype.getActiveFrame_ = function() {
|
|
1445
|
-
this.cancelShutdown_();
|
|
1446
|
-
if (!this.activeFrame_) {
|
|
1447
|
-
this.activeFrame_ = new Frame(this);
|
|
1448
|
-
this.activeFrame_.once(Frame.ERROR_EVENT, this.abortNow_, this);
|
|
1449
|
-
this.scheduleEventLoopStart_();
|
|
1450
|
-
}
|
|
1451
|
-
return this.activeFrame_;
|
|
1452
|
-
};
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
* @return {!Frame} The frame that new items should be added to.
|
|
1457
|
-
* @private
|
|
1458
|
-
*/
|
|
1459
|
-
promise.ControlFlow.prototype.getSchedulingFrame_ = function() {
|
|
1460
|
-
return this.schedulingFrame_ || this.getActiveFrame_();
|
|
1461
|
-
};
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
/**
|
|
1465
|
-
* @return {!Frame} The frame that is current executing.
|
|
1466
|
-
* @private
|
|
1467
|
-
*/
|
|
1468
|
-
promise.ControlFlow.prototype.getRunningFrame_ = function() {
|
|
1469
|
-
return this.runningFrame_ || this.getActiveFrame_();
|
|
1470
|
-
};
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* Schedules a task for execution. If there is nothing currently in the
|
|
1475
|
-
* queue, the task will be executed in the next turn of the event loop. If
|
|
1476
|
-
* the task function is a generator, the task will be executed using
|
|
1477
|
-
* {@link webdriver.promise.consume}.
|
|
1478
|
-
*
|
|
1479
|
-
* @param {function(): (T|promise.Promise<T>)} fn The function to
|
|
1480
|
-
* call to start the task. If the function returns a
|
|
1481
|
-
* {@link webdriver.promise.Promise}, this instance will wait for it to be
|
|
1482
|
-
* resolved before starting the next task.
|
|
1483
|
-
* @param {string=} opt_description A description of the task.
|
|
1484
|
-
* @return {!promise.Promise<T>} A promise that will be resolved
|
|
1485
|
-
* with the result of the action.
|
|
1486
|
-
* @template T
|
|
1487
|
-
*/
|
|
1488
|
-
promise.ControlFlow.prototype.execute = function(fn, opt_description) {
|
|
1489
|
-
if (promise.isGenerator(fn)) {
|
|
1490
|
-
fn = goog.partial(promise.consume, fn);
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
if (!this.hold_) {
|
|
1494
|
-
var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js
|
|
1495
|
-
this.hold_ = setInterval(goog.nullFunction, holdIntervalMs);
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
var description = opt_description || '<anonymous>';
|
|
1499
|
-
var task = new Task(this, fn, description);
|
|
1500
|
-
task.promise.stack_ = promise.captureStackTrace('Task', description,
|
|
1501
|
-
promise.ControlFlow.prototype.execute);
|
|
1502
|
-
|
|
1503
|
-
this.getSchedulingFrame_().addChild(task);
|
|
1504
|
-
this.emit(promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description);
|
|
1505
|
-
this.scheduleEventLoopStart_();
|
|
1506
|
-
return task.promise;
|
|
1507
|
-
};
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
/**
|
|
1511
|
-
* Inserts a {@code setTimeout} into the command queue. This is equivalent to
|
|
1512
|
-
* a thread sleep in a synchronous programming language.
|
|
1513
|
-
*
|
|
1514
|
-
* @param {number} ms The timeout delay, in milliseconds.
|
|
1515
|
-
* @param {string=} opt_description A description to accompany the timeout.
|
|
1516
|
-
* @return {!promise.Promise} A promise that will be resolved with
|
|
1517
|
-
* the result of the action.
|
|
1518
|
-
*/
|
|
1519
|
-
promise.ControlFlow.prototype.timeout = function(ms, opt_description) {
|
|
1520
|
-
return this.execute(function() {
|
|
1521
|
-
return promise.delayed(ms);
|
|
1522
|
-
}, opt_description);
|
|
1523
|
-
};
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
/**
|
|
1527
|
-
* Schedules a task that shall wait for a condition to hold. Each condition
|
|
1528
|
-
* function may return any value, but it will always be evaluated as a boolean.
|
|
1529
|
-
*
|
|
1530
|
-
* Condition functions may schedule sub-tasks with this instance, however,
|
|
1531
|
-
* their execution time will be factored into whether a wait has timed out.
|
|
1532
|
-
*
|
|
1533
|
-
* In the event a condition returns a Promise, the polling loop will wait for
|
|
1534
|
-
* it to be resolved before evaluating whether the condition has been satisfied.
|
|
1535
|
-
* The resolution time for a promise is factored into whether a wait has timed
|
|
1536
|
-
* out.
|
|
1537
|
-
*
|
|
1538
|
-
* If the condition function throws, or returns a rejected promise, the
|
|
1539
|
-
* wait task will fail.
|
|
1540
|
-
*
|
|
1541
|
-
* If the condition is defined as a promise, the flow will wait for it to
|
|
1542
|
-
* settle. If the timeout expires before the promise settles, the promise
|
|
1543
|
-
* returned by this function will be rejected.
|
|
1544
|
-
*
|
|
1545
|
-
* If this function is invoked with `timeout === 0`, or the timeout is omitted,
|
|
1546
|
-
* the flow will wait indefinitely for the condition to be satisfied.
|
|
1547
|
-
*
|
|
1548
|
-
* @param {(!promise.Promise<T>|function())} condition The condition to poll,
|
|
1549
|
-
* or a promise to wait on.
|
|
1550
|
-
* @param {number=} opt_timeout How long to wait, in milliseconds, for the
|
|
1551
|
-
* condition to hold before timing out. If omitted, the flow will wait
|
|
1552
|
-
* indefinitely.
|
|
1553
|
-
* @param {string=} opt_message An optional error message to include if the
|
|
1554
|
-
* wait times out; defaults to the empty string.
|
|
1555
|
-
* @return {!promise.Promise<T>} A promise that will be fulfilled
|
|
1556
|
-
* when the condition has been satisified. The promise shall be rejected if
|
|
1557
|
-
* the wait times out waiting for the condition.
|
|
1558
|
-
* @throws {TypeError} If condition is not a function or promise or if timeout
|
|
1559
|
-
* is not a number >= 0.
|
|
1560
|
-
* @template T
|
|
1561
|
-
*/
|
|
1562
|
-
promise.ControlFlow.prototype.wait = function(
|
|
1563
|
-
condition, opt_timeout, opt_message) {
|
|
1564
|
-
var timeout = opt_timeout || 0;
|
|
1565
|
-
if (!goog.isNumber(timeout) || timeout < 0) {
|
|
1566
|
-
throw TypeError('timeout must be a number >= 0: ' + timeout);
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
if (promise.isPromise(condition)) {
|
|
1570
|
-
return this.execute(function() {
|
|
1571
|
-
if (!timeout) {
|
|
1572
|
-
return condition;
|
|
1573
|
-
}
|
|
1574
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
1575
|
-
var start = goog.now();
|
|
1576
|
-
var timer = setTimeout(function() {
|
|
1577
|
-
timer = null;
|
|
1578
|
-
reject(Error((opt_message ? opt_message + '\n' : '') +
|
|
1579
|
-
'Timed out waiting for promise to resolve after ' +
|
|
1580
|
-
(goog.now() - start) + 'ms'));
|
|
1581
|
-
}, timeout);
|
|
1582
|
-
|
|
1583
|
-
/** @type {Thenable} */(condition).then(
|
|
1584
|
-
function(value) {
|
|
1585
|
-
timer && clearTimeout(timer);
|
|
1586
|
-
fulfill(value);
|
|
1587
|
-
},
|
|
1588
|
-
function(error) {
|
|
1589
|
-
timer && clearTimeout(timer);
|
|
1590
|
-
reject(error);
|
|
1591
|
-
});
|
|
1592
|
-
});
|
|
1593
|
-
}, opt_message || '<anonymous wait: promise resolution>');
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
if (!goog.isFunction(condition)) {
|
|
1597
|
-
throw TypeError('Invalid condition; must be a function or promise: ' +
|
|
1598
|
-
goog.typeOf(condition));
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
if (promise.isGenerator(condition)) {
|
|
1602
|
-
condition = goog.partial(promise.consume, condition);
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
var self = this;
|
|
1606
|
-
return this.execute(function() {
|
|
1607
|
-
var startTime = goog.now();
|
|
1608
|
-
return new promise.Promise(function(fulfill, reject) {
|
|
1609
|
-
self.suspend_();
|
|
1610
|
-
pollCondition();
|
|
1611
|
-
|
|
1612
|
-
function pollCondition() {
|
|
1613
|
-
self.resume_();
|
|
1614
|
-
self.execute(/**@type {function()}*/(condition)).then(function(value) {
|
|
1615
|
-
var elapsed = goog.now() - startTime;
|
|
1616
|
-
if (!!value) {
|
|
1617
|
-
fulfill(value);
|
|
1618
|
-
} else if (timeout && elapsed >= timeout) {
|
|
1619
|
-
reject(new Error((opt_message ? opt_message + '\n' : '') +
|
|
1620
|
-
'Wait timed out after ' + elapsed + 'ms'));
|
|
1621
|
-
} else {
|
|
1622
|
-
self.suspend_();
|
|
1623
|
-
// Do not use asyncRun here because we need a non-micro yield
|
|
1624
|
-
// here so the UI thread is given a chance when running in a
|
|
1625
|
-
// browser.
|
|
1626
|
-
setTimeout(pollCondition, 0);
|
|
1627
|
-
}
|
|
1628
|
-
}, reject);
|
|
1629
|
-
}
|
|
1630
|
-
});
|
|
1631
|
-
}, opt_message || '<anonymous wait>');
|
|
1632
|
-
};
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
/**
|
|
1636
|
-
* Schedules the interval for this instance's event loop, if necessary.
|
|
1637
|
-
* @private
|
|
1638
|
-
*/
|
|
1639
|
-
promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {
|
|
1640
|
-
if (!this.eventLoopTask_ && !this.yieldCount_ && this.activeFrame_ &&
|
|
1641
|
-
!this.activeFrame_.getPendingTask()) {
|
|
1642
|
-
this.eventLoopTask_ = new MicroTask(this.runEventLoop_, this);
|
|
1643
|
-
}
|
|
1644
|
-
};
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
/**
|
|
1648
|
-
* Cancels the event loop, if necessary.
|
|
1649
|
-
* @private
|
|
1650
|
-
*/
|
|
1651
|
-
promise.ControlFlow.prototype.cancelEventLoop_ = function() {
|
|
1652
|
-
if (this.eventLoopTask_) {
|
|
1653
|
-
this.eventLoopTask_.cancel();
|
|
1654
|
-
this.eventLoopTask_ = null;
|
|
1655
|
-
}
|
|
1656
|
-
};
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
/**
|
|
1660
|
-
* Suspends this control flow, preventing it from executing any more tasks.
|
|
1661
|
-
* @private
|
|
1662
|
-
*/
|
|
1663
|
-
promise.ControlFlow.prototype.suspend_ = function() {
|
|
1664
|
-
this.yieldCount_ += 1;
|
|
1665
|
-
this.cancelEventLoop_();
|
|
1666
|
-
};
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
/**
|
|
1670
|
-
* Resumes execution of tasks scheduled within this control flow.
|
|
1671
|
-
* @private
|
|
1672
|
-
*/
|
|
1673
|
-
promise.ControlFlow.prototype.resume_ = function() {
|
|
1674
|
-
this.yieldCount_ -= 1;
|
|
1675
|
-
if (!this.yieldCount_ && this.activeFrame_) {
|
|
1676
|
-
this.scheduleEventLoopStart_();
|
|
1677
|
-
}
|
|
1678
|
-
};
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
/**
|
|
1682
|
-
* Executes the next task for the current frame. If the current frame has no
|
|
1683
|
-
* more tasks, the frame's result will be resolved, returning control to the
|
|
1684
|
-
* frame's creator. This will terminate the flow if the completed frame was at
|
|
1685
|
-
* the top of the stack.
|
|
1686
|
-
* @private
|
|
1687
|
-
*/
|
|
1688
|
-
promise.ControlFlow.prototype.runEventLoop_ = function() {
|
|
1689
|
-
this.eventLoopTask_ = null;
|
|
1690
|
-
|
|
1691
|
-
if (this.yieldCount_) {
|
|
1692
|
-
return;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
if (!this.activeFrame_) {
|
|
1696
|
-
this.commenceShutdown_();
|
|
1697
|
-
return;
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1700
|
-
if (this.activeFrame_.getPendingTask()) {
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
var task = this.getNextTask_();
|
|
1705
|
-
if (!task) {
|
|
1706
|
-
return;
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
var activeFrame = this.activeFrame_;
|
|
1710
|
-
var scheduleEventLoop = goog.bind(this.scheduleEventLoopStart_, this);
|
|
1711
|
-
|
|
1712
|
-
var onSuccess = function(value) {
|
|
1713
|
-
activeFrame.setPendingTask(null);
|
|
1714
|
-
task.setFrame(null);
|
|
1715
|
-
task.fulfill(value);
|
|
1716
|
-
scheduleEventLoop();
|
|
1717
|
-
};
|
|
1718
|
-
|
|
1719
|
-
var onFailure = function(reason) {
|
|
1720
|
-
activeFrame.setPendingTask(null);
|
|
1721
|
-
task.setFrame(null);
|
|
1722
|
-
task.reject(reason);
|
|
1723
|
-
scheduleEventLoop();
|
|
1724
|
-
};
|
|
1725
|
-
|
|
1726
|
-
activeFrame.setPendingTask(task);
|
|
1727
|
-
var frame = new Frame(this);
|
|
1728
|
-
task.setFrame(frame);
|
|
1729
|
-
this.runInFrame_(frame, task.execute, function(result) {
|
|
1730
|
-
promise.asap(result, onSuccess, onFailure);
|
|
1731
|
-
}, onFailure, true);
|
|
1732
|
-
};
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
/**
|
|
1736
|
-
* @return {Task} The next task to execute, or
|
|
1737
|
-
* {@code null} if a frame was resolved.
|
|
1738
|
-
* @private
|
|
1739
|
-
*/
|
|
1740
|
-
promise.ControlFlow.prototype.getNextTask_ = function() {
|
|
1741
|
-
var frame = this.activeFrame_;
|
|
1742
|
-
var firstChild = frame.getFirstChild();
|
|
1743
|
-
if (!firstChild) {
|
|
1744
|
-
if (!frame.pendingCallback && !frame.isBlocked_) {
|
|
1745
|
-
this.resolveFrame_(frame);
|
|
1746
|
-
}
|
|
1747
|
-
return null;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
if (firstChild instanceof Frame) {
|
|
1751
|
-
this.activeFrame_ = firstChild;
|
|
1752
|
-
return this.getNextTask_();
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
frame.removeChild(firstChild);
|
|
1756
|
-
if (!firstChild.isPending()) {
|
|
1757
|
-
return this.getNextTask_();
|
|
1758
|
-
}
|
|
1759
|
-
return firstChild;
|
|
1760
|
-
};
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
/**
|
|
1764
|
-
* @param {!Frame} frame The frame to resolve.
|
|
1765
|
-
* @private
|
|
1766
|
-
*/
|
|
1767
|
-
promise.ControlFlow.prototype.resolveFrame_ = function(frame) {
|
|
1768
|
-
if (this.activeFrame_ === frame) {
|
|
1769
|
-
this.activeFrame_ = frame.getParent();
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
if (frame.getParent()) {
|
|
1773
|
-
frame.getParent().removeChild(frame);
|
|
1774
|
-
}
|
|
1775
|
-
frame.emit(Frame.CLOSE_EVENT);
|
|
1776
|
-
|
|
1777
|
-
if (!this.activeFrame_) {
|
|
1778
|
-
this.commenceShutdown_();
|
|
1779
|
-
} else {
|
|
1780
|
-
this.scheduleEventLoopStart_();
|
|
1781
|
-
}
|
|
1782
|
-
};
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
/**
|
|
1786
|
-
* Aborts the current frame. The frame, and all of the tasks scheduled within it
|
|
1787
|
-
* will be discarded. If this instance does not have an active frame, it will
|
|
1788
|
-
* immediately terminate all execution.
|
|
1789
|
-
* @param {*} error The reason the frame is being aborted; typically either
|
|
1790
|
-
* an Error or string.
|
|
1791
|
-
* @param {Frame=} opt_frame The frame to abort; will use the
|
|
1792
|
-
* currently active frame if not specified.
|
|
1793
|
-
* @private
|
|
1794
|
-
*/
|
|
1795
|
-
promise.ControlFlow.prototype.abortFrame_ = function(error, opt_frame) {
|
|
1796
|
-
if (!this.activeFrame_) {
|
|
1797
|
-
this.abortNow_(error);
|
|
1798
|
-
return;
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
// Frame parent is always another frame, but the compiler is not smart
|
|
1802
|
-
// enough to recognize this.
|
|
1803
|
-
var parent = /** @type {Frame} */ (
|
|
1804
|
-
this.activeFrame_.getParent());
|
|
1805
|
-
if (parent) {
|
|
1806
|
-
parent.removeChild(this.activeFrame_);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
var frame = this.activeFrame_;
|
|
1810
|
-
this.activeFrame_ = parent;
|
|
1811
|
-
frame.abort(error);
|
|
1812
|
-
};
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
/**
|
|
1816
|
-
* Executes a function within a specific frame. If the function does not
|
|
1817
|
-
* schedule any new tasks, the frame will be discarded and the function's result
|
|
1818
|
-
* returned passed to the given callback immediately. Otherwise, the callback
|
|
1819
|
-
* will be invoked once all of the tasks scheduled within the function have been
|
|
1820
|
-
* completed. If the frame is aborted, the `errback` will be invoked with the
|
|
1821
|
-
* offending error.
|
|
1822
|
-
*
|
|
1823
|
-
* @param {!Frame} newFrame The frame to use.
|
|
1824
|
-
* @param {!Function} fn The function to execute.
|
|
1825
|
-
* @param {function(T)} callback The function to call with a successful result.
|
|
1826
|
-
* @param {function(*)} errback The function to call if there is an error.
|
|
1827
|
-
* @param {boolean=} opt_isTask Whether the function is a task and the frame
|
|
1828
|
-
* should be immediately activated to capture subtasks and errors.
|
|
1829
|
-
* @throws {Error} If this function is invoked while another call to this
|
|
1830
|
-
* function is present on the stack.
|
|
1831
|
-
* @template T
|
|
1832
|
-
* @private
|
|
1833
|
-
*/
|
|
1834
|
-
promise.ControlFlow.prototype.runInFrame_ = function(
|
|
1835
|
-
newFrame, fn, callback, errback, opt_isTask) {
|
|
1836
|
-
asserts.assert(
|
|
1837
|
-
!this.runningFrame_, 'unexpected recursive call to runInFrame');
|
|
1838
|
-
|
|
1839
|
-
var self = this,
|
|
1840
|
-
oldFrame = this.activeFrame_;
|
|
1841
|
-
|
|
1842
|
-
try {
|
|
1843
|
-
if (this.activeFrame_ !== newFrame && !newFrame.getParent()) {
|
|
1844
|
-
this.activeFrame_.addChild(newFrame);
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
// Activate the new frame to force tasks to be treated as sub-tasks of
|
|
1848
|
-
// the parent frame.
|
|
1849
|
-
if (opt_isTask) {
|
|
1850
|
-
this.activeFrame_ = newFrame;
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
try {
|
|
1854
|
-
this.runningFrame_ = newFrame;
|
|
1855
|
-
this.schedulingFrame_ = newFrame;
|
|
1856
|
-
activeFlows.push(this);
|
|
1857
|
-
var result = fn();
|
|
1858
|
-
} finally {
|
|
1859
|
-
activeFlows.pop();
|
|
1860
|
-
this.schedulingFrame_ = null;
|
|
1861
|
-
|
|
1862
|
-
// `newFrame` should only be considered the running frame when it is
|
|
1863
|
-
// actually executing. After it finishes, set top of stack to its parent
|
|
1864
|
-
// so any failures/interrupts that occur while processing newFrame's
|
|
1865
|
-
// result are handled there.
|
|
1866
|
-
this.runningFrame_ = newFrame.parent_;
|
|
1867
|
-
}
|
|
1868
|
-
newFrame.isLocked_ = true;
|
|
1869
|
-
|
|
1870
|
-
// If there was nothing scheduled in the new frame we can discard the
|
|
1871
|
-
// frame and return immediately.
|
|
1872
|
-
if (isCloseable(newFrame) && (!opt_isTask || !promise.isPromise(result))) {
|
|
1873
|
-
removeNewFrame();
|
|
1874
|
-
callback(result);
|
|
1875
|
-
return;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
// If the executed function returned a promise, wait for it to resolve. If
|
|
1879
|
-
// there is nothing scheduled in the frame, go ahead and discard it.
|
|
1880
|
-
// Otherwise, we wait for the frame to be closed out by the event loop.
|
|
1881
|
-
var shortCircuitTask;
|
|
1882
|
-
if (promise.isPromise(result)) {
|
|
1883
|
-
newFrame.isBlocked_ = true;
|
|
1884
|
-
var onResolve = function() {
|
|
1885
|
-
newFrame.isBlocked_ = false;
|
|
1886
|
-
shortCircuitTask = new MicroTask(function() {
|
|
1887
|
-
if (isCloseable(newFrame)) {
|
|
1888
|
-
removeNewFrame();
|
|
1889
|
-
callback(result);
|
|
1890
|
-
}
|
|
1891
|
-
});
|
|
1892
|
-
};
|
|
1893
|
-
/** @type {Thenable} */(result).then(onResolve, onResolve);
|
|
1894
|
-
|
|
1895
|
-
// If the result is a thenable, attach a listener to silence any unhandled
|
|
1896
|
-
// rejection warnings. This is safe because we *will* handle it once the
|
|
1897
|
-
// frame has completed.
|
|
1898
|
-
} else if (promise.Thenable.isImplementation(result)) {
|
|
1899
|
-
/** @type {!promise.Thenable} */(result).thenCatch(goog.nullFunction);
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
newFrame.once(Frame.CLOSE_EVENT, function() {
|
|
1903
|
-
shortCircuitTask && shortCircuitTask.cancel();
|
|
1904
|
-
if (isCloseable(newFrame)) {
|
|
1905
|
-
removeNewFrame();
|
|
1906
|
-
}
|
|
1907
|
-
callback(result);
|
|
1908
|
-
}).once(Frame.ERROR_EVENT, function(reason) {
|
|
1909
|
-
shortCircuitTask && shortCircuitTask.cancel();
|
|
1910
|
-
if (promise.Thenable.isImplementation(result) && result.isPending()) {
|
|
1911
|
-
result.cancel(reason);
|
|
1912
|
-
}
|
|
1913
|
-
errback(reason);
|
|
1914
|
-
});
|
|
1915
|
-
} catch (ex) {
|
|
1916
|
-
removeNewFrame(ex);
|
|
1917
|
-
errback(ex);
|
|
1918
|
-
} finally {
|
|
1919
|
-
// No longer running anything, clear the reference.
|
|
1920
|
-
this.runningFrame_ = null;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
function isCloseable(frame) {
|
|
1924
|
-
return (!frame.children_ || !frame.children_.length)
|
|
1925
|
-
&& !frame.pendingRejection;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
/**
|
|
1929
|
-
* @param {*=} opt_err If provided, the reason that the frame was removed.
|
|
1930
|
-
*/
|
|
1931
|
-
function removeNewFrame(opt_err) {
|
|
1932
|
-
var parent = newFrame.getParent();
|
|
1933
|
-
if (parent) {
|
|
1934
|
-
parent.removeChild(newFrame);
|
|
1935
|
-
asyncRun(function() {
|
|
1936
|
-
if (isCloseable(parent) && parent !== self.activeFrame_) {
|
|
1937
|
-
parent.emit(Frame.CLOSE_EVENT);
|
|
1938
|
-
}
|
|
1939
|
-
});
|
|
1940
|
-
self.scheduleEventLoopStart_();
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
if (opt_err) {
|
|
1944
|
-
newFrame.cancelRemainingTasks(promise.CancellationError.wrap(
|
|
1945
|
-
opt_err, 'Tasks cancelled due to uncaught error'));
|
|
1946
|
-
}
|
|
1947
|
-
self.activeFrame_ = oldFrame;
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
/**
|
|
1953
|
-
* Commences the shutdown sequence for this instance. After one turn of the
|
|
1954
|
-
* event loop, this object will emit the
|
|
1955
|
-
* {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event to signal
|
|
1956
|
-
* listeners that it has completed. During this wait, if another task is
|
|
1957
|
-
* scheduled, the shutdown will be aborted.
|
|
1958
|
-
* @private
|
|
1959
|
-
*/
|
|
1960
|
-
promise.ControlFlow.prototype.commenceShutdown_ = function() {
|
|
1961
|
-
if (!this.shutdownTask_) {
|
|
1962
|
-
// Go ahead and stop the event loop now. If we're in here, then there are
|
|
1963
|
-
// no more frames with tasks to execute. If we waited to cancel the event
|
|
1964
|
-
// loop in our timeout below, the event loop could trigger *before* the
|
|
1965
|
-
// timeout, generating an error from there being no frames.
|
|
1966
|
-
// If #execute is called before the timeout below fires, it will cancel
|
|
1967
|
-
// the timeout and restart the event loop.
|
|
1968
|
-
this.cancelEventLoop_();
|
|
1969
|
-
this.shutdownTask_ = new MicroTask(this.shutdown_, this);
|
|
1970
|
-
}
|
|
1971
|
-
};
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
/** @private */
|
|
1975
|
-
promise.ControlFlow.prototype.cancelHold_ = function() {
|
|
1976
|
-
if (this.hold_) {
|
|
1977
|
-
clearInterval(this.hold_);
|
|
1978
|
-
this.hold_ = null;
|
|
1979
|
-
}
|
|
1980
|
-
};
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
/** @private */
|
|
1984
|
-
promise.ControlFlow.prototype.shutdown_ = function() {
|
|
1985
|
-
this.cancelHold_();
|
|
1986
|
-
this.shutdownTask_ = null;
|
|
1987
|
-
this.emit(promise.ControlFlow.EventType.IDLE);
|
|
1988
|
-
};
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
/**
|
|
1992
|
-
* Cancels the shutdown sequence if it is currently scheduled.
|
|
1993
|
-
* @private
|
|
1994
|
-
*/
|
|
1995
|
-
promise.ControlFlow.prototype.cancelShutdown_ = function() {
|
|
1996
|
-
if (this.shutdownTask_) {
|
|
1997
|
-
this.shutdownTask_.cancel();
|
|
1998
|
-
this.shutdownTask_ = null;
|
|
1999
|
-
}
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
/**
|
|
2004
|
-
* Aborts this flow, abandoning all remaining tasks. If there are
|
|
2005
|
-
* listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the
|
|
2006
|
-
* offending {@code error}, otherwise, the {@code error} will be rethrown to the
|
|
2007
|
-
* global error handler.
|
|
2008
|
-
* @param {*} error Object describing the error that caused the flow to
|
|
2009
|
-
* abort; usually either an Error or string value.
|
|
2010
|
-
* @private
|
|
2011
|
-
*/
|
|
2012
|
-
promise.ControlFlow.prototype.abortNow_ = function(error) {
|
|
2013
|
-
this.activeFrame_ = null;
|
|
2014
|
-
this.cancelShutdown_();
|
|
2015
|
-
this.cancelEventLoop_();
|
|
2016
|
-
this.cancelHold_();
|
|
2017
|
-
|
|
2018
|
-
var listeners = this.listeners(
|
|
2019
|
-
promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
|
|
2020
|
-
if (!listeners.length) {
|
|
2021
|
-
throwException(error);
|
|
2022
|
-
} else {
|
|
2023
|
-
this.emit(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, error);
|
|
2024
|
-
}
|
|
2025
|
-
};
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
/**
|
|
2029
|
-
* Wraps a function to execute as a cancellable micro task.
|
|
2030
|
-
* @final
|
|
2031
|
-
*/
|
|
2032
|
-
var MicroTask = goog.defineClass(null, {
|
|
2033
|
-
/**
|
|
2034
|
-
* @param {function(this: THIS)} fn The function to run as a micro task.
|
|
2035
|
-
* @param {THIS=} opt_scope The scope to run the function in.
|
|
2036
|
-
* @template THIS
|
|
2037
|
-
*/
|
|
2038
|
-
constructor: function(fn, opt_scope) {
|
|
2039
|
-
/** @private {boolean} */
|
|
2040
|
-
this.cancelled_ = false;
|
|
2041
|
-
asyncRun(function() {
|
|
2042
|
-
if (!this.cancelled_) {
|
|
2043
|
-
fn.call(opt_scope);
|
|
2044
|
-
}
|
|
2045
|
-
}, this);
|
|
2046
|
-
},
|
|
2047
|
-
|
|
2048
|
-
/**
|
|
2049
|
-
* Cancels the execution of this task. Note: this will not prevent the task
|
|
2050
|
-
* timer from firing, just the invocation of the wrapped function.
|
|
2051
|
-
*/
|
|
2052
|
-
cancel: function() {
|
|
2053
|
-
this.cancelled_ = true;
|
|
2054
|
-
}
|
|
2055
|
-
});
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
/**
|
|
2059
|
-
* An execution frame within a {@link webdriver.promise.ControlFlow}. Each
|
|
2060
|
-
* frame represents the execution context for either a
|
|
2061
|
-
* {@link webdriver.Task} or a callback on a
|
|
2062
|
-
* {@link webdriver.promise.Promise}.
|
|
2063
|
-
*
|
|
2064
|
-
* Each frame may contain sub-frames. If child N is a sub-frame, then the
|
|
2065
|
-
* items queued within it are given priority over child N+1.
|
|
2066
|
-
*
|
|
2067
|
-
* @unrestricted
|
|
2068
|
-
* @final
|
|
2069
|
-
* @private
|
|
2070
|
-
*/
|
|
2071
|
-
var Frame = goog.defineClass(EventEmitter, {
|
|
2072
|
-
/**
|
|
2073
|
-
* @param {!promise.ControlFlow} flow The flow this instance belongs to.
|
|
2074
|
-
*/
|
|
2075
|
-
constructor: function(flow) {
|
|
2076
|
-
EventEmitter.call(this);
|
|
2077
|
-
goog.getUid(this);
|
|
2078
|
-
|
|
2079
|
-
/** @private {!promise.ControlFlow} */
|
|
2080
|
-
this.flow_ = flow;
|
|
2081
|
-
|
|
2082
|
-
/** @private {Frame} */
|
|
2083
|
-
this.parent_ = null;
|
|
2084
|
-
|
|
2085
|
-
/** @private {Array<!(Frame|Task)>} */
|
|
2086
|
-
this.children_ = null;
|
|
2087
|
-
|
|
2088
|
-
/** @private {(Frame|Task)} */
|
|
2089
|
-
this.lastInsertedChild_ = null;
|
|
2090
|
-
|
|
2091
|
-
/**
|
|
2092
|
-
* The task currently being executed within this frame.
|
|
2093
|
-
* @private {Task}
|
|
2094
|
-
*/
|
|
2095
|
-
this.pendingTask_ = null;
|
|
2096
|
-
|
|
2097
|
-
/**
|
|
2098
|
-
* Whether this frame is currently locked. A locked frame represents an
|
|
2099
|
-
* executed function that has scheduled all of its tasks.
|
|
2100
|
-
*
|
|
2101
|
-
* Once a frame becomes locked, any new frames which are added as children
|
|
2102
|
-
* represent interrupts (such as a {@link webdriver.promise.Promise}
|
|
2103
|
-
* callback) whose tasks must be given priority over those already scheduled
|
|
2104
|
-
* within this frame. For example:
|
|
2105
|
-
*
|
|
2106
|
-
* var flow = promise.controlFlow();
|
|
2107
|
-
* flow.execute('start here', goog.nullFunction).then(function() {
|
|
2108
|
-
* flow.execute('this should execute 2nd', goog.nullFunction);
|
|
2109
|
-
* });
|
|
2110
|
-
* flow.execute('this should execute last', goog.nullFunction);
|
|
2111
|
-
*
|
|
2112
|
-
* @private {boolean}
|
|
2113
|
-
*/
|
|
2114
|
-
this.isLocked_ = false;
|
|
2115
|
-
|
|
2116
|
-
/**
|
|
2117
|
-
* Whether this frame's completion is blocked on the resolution of a promise
|
|
2118
|
-
* returned by its main function.
|
|
2119
|
-
* @private
|
|
2120
|
-
*/
|
|
2121
|
-
this.isBlocked_ = false;
|
|
2122
|
-
|
|
2123
|
-
/**
|
|
2124
|
-
* Whether this frame represents a pending callback attached to a
|
|
2125
|
-
* {@link webdriver.promise.Promise}.
|
|
2126
|
-
* @private {boolean}
|
|
2127
|
-
*/
|
|
2128
|
-
this.pendingCallback = false;
|
|
2129
|
-
|
|
2130
|
-
/**
|
|
2131
|
-
* Whether there are pending unhandled rejections detected within this frame.
|
|
2132
|
-
* @private {boolean}
|
|
2133
|
-
*/
|
|
2134
|
-
this.pendingRejection = false;
|
|
2135
|
-
|
|
2136
|
-
/** @private {promise.CancellationError} */
|
|
2137
|
-
this.cancellationError_ = null;
|
|
2138
|
-
},
|
|
2139
|
-
|
|
2140
|
-
statics: {
|
|
2141
|
-
/** @const */
|
|
2142
|
-
CLOSE_EVENT: 'close',
|
|
2143
|
-
|
|
2144
|
-
/** @const */
|
|
2145
|
-
ERROR_EVENT: 'error',
|
|
2146
|
-
|
|
2147
|
-
/**
|
|
2148
|
-
* @param {!promise.CancellationError} error The cancellation error.
|
|
2149
|
-
* @param {!(Frame|Task)} child The child to cancel.
|
|
2150
|
-
* @private
|
|
2151
|
-
*/
|
|
2152
|
-
cancelChild_: function(error, child) {
|
|
2153
|
-
if (child instanceof Frame) {
|
|
2154
|
-
child.cancelRemainingTasks(error);
|
|
2155
|
-
} else {
|
|
2156
|
-
child.promise.callbacks_ = null;
|
|
2157
|
-
child.cancel(error);
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
},
|
|
2161
|
-
|
|
2162
|
-
/** @return {Frame} This frame's parent, if any. */
|
|
2163
|
-
getParent: function() {
|
|
2164
|
-
return this.parent_;
|
|
2165
|
-
},
|
|
2166
|
-
|
|
2167
|
-
/** @param {Frame} parent This frame's new parent. */
|
|
2168
|
-
setParent: function(parent) {
|
|
2169
|
-
this.parent_ = parent;
|
|
2170
|
-
},
|
|
2171
|
-
|
|
2172
|
-
/** @return {!Frame} The root of this frame's tree. */
|
|
2173
|
-
getRoot: function() {
|
|
2174
|
-
var root = this;
|
|
2175
|
-
while (root.parent_) {
|
|
2176
|
-
root = root.parent_;
|
|
2177
|
-
}
|
|
2178
|
-
return root;
|
|
2179
|
-
},
|
|
2180
|
-
|
|
2181
|
-
/**
|
|
2182
|
-
* Aborts the execution of this frame, cancelling all outstanding tasks
|
|
2183
|
-
* scheduled within this frame.
|
|
2184
|
-
*
|
|
2185
|
-
* @param {*} error The error that triggered this abortion.
|
|
2186
|
-
*/
|
|
2187
|
-
abort: function(error) {
|
|
2188
|
-
this.cancellationError_ = promise.CancellationError.wrap(
|
|
2189
|
-
error, 'Task discarded due to a previous task failure');
|
|
2190
|
-
this.cancelRemainingTasks(this.cancellationError_);
|
|
2191
|
-
if (!this.pendingCallback) {
|
|
2192
|
-
this.emit(Frame.ERROR_EVENT, error);
|
|
2193
|
-
}
|
|
2194
|
-
},
|
|
2195
|
-
|
|
2196
|
-
/**
|
|
2197
|
-
* Marks all of the tasks that are descendants of this frame in the execution
|
|
2198
|
-
* tree as cancelled. This is necessary for callbacks scheduled asynchronous.
|
|
2199
|
-
* For example:
|
|
2200
|
-
*
|
|
2201
|
-
* var someResult;
|
|
2202
|
-
* promise.createFlow(function(flow) {
|
|
2203
|
-
* someResult = flow.execute(function() {});
|
|
2204
|
-
* throw Error();
|
|
2205
|
-
* }).thenCatch(function(err) {
|
|
2206
|
-
* console.log('flow failed: ' + err);
|
|
2207
|
-
* someResult.then(function() {
|
|
2208
|
-
* console.log('task succeeded!');
|
|
2209
|
-
* }, function(err) {
|
|
2210
|
-
* console.log('task failed! ' + err);
|
|
2211
|
-
* });
|
|
2212
|
-
* });
|
|
2213
|
-
* // flow failed: Error: boom
|
|
2214
|
-
* // task failed! CancelledTaskError: Task discarded due to a previous
|
|
2215
|
-
* // task failure: Error: boom
|
|
2216
|
-
*
|
|
2217
|
-
* @param {!promise.CancellationError} reason The cancellation reason.
|
|
2218
|
-
*/
|
|
2219
|
-
cancelRemainingTasks: function(reason) {
|
|
2220
|
-
if (this.children_) {
|
|
2221
|
-
this.children_.forEach(function(child) {
|
|
2222
|
-
Frame.cancelChild_(reason, child);
|
|
2223
|
-
});
|
|
2224
|
-
}
|
|
2225
|
-
},
|
|
2226
|
-
|
|
2227
|
-
/**
|
|
2228
|
-
* @return {Task} The task currently executing
|
|
2229
|
-
* within this frame, if any.
|
|
2230
|
-
*/
|
|
2231
|
-
getPendingTask: function() {
|
|
2232
|
-
return this.pendingTask_;
|
|
2233
|
-
},
|
|
2234
|
-
|
|
2235
|
-
/**
|
|
2236
|
-
* @param {Task} task The task currently
|
|
2237
|
-
* executing within this frame, if any.
|
|
2238
|
-
*/
|
|
2239
|
-
setPendingTask: function(task) {
|
|
2240
|
-
this.pendingTask_ = task;
|
|
2241
|
-
},
|
|
2242
|
-
|
|
2243
|
-
/**
|
|
2244
|
-
* @return {boolean} Whether this frame is empty (has no scheduled tasks or
|
|
2245
|
-
* pending callback frames).
|
|
2246
|
-
*/
|
|
2247
|
-
isEmpty: function() {
|
|
2248
|
-
return !this.children_ || !this.children_.length;
|
|
2249
|
-
},
|
|
2250
|
-
|
|
2251
|
-
/**
|
|
2252
|
-
* Adds a new node to this frame.
|
|
2253
|
-
* @param {!(Frame|Task)} node The node to insert.
|
|
2254
|
-
*/
|
|
2255
|
-
addChild: function(node) {
|
|
2256
|
-
if (this.cancellationError_) {
|
|
2257
|
-
Frame.cancelChild_(this.cancellationError_, node);
|
|
2258
|
-
return; // Child will never run, no point keeping a reference.
|
|
2259
|
-
}
|
|
2260
|
-
|
|
2261
|
-
if (!this.children_) {
|
|
2262
|
-
this.children_ = [];
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
|
-
node.setParent(this);
|
|
2266
|
-
if (this.isLocked_ && node instanceof Frame) {
|
|
2267
|
-
var index = 0;
|
|
2268
|
-
if (this.lastInsertedChild_ instanceof Frame) {
|
|
2269
|
-
index = this.children_.indexOf(this.lastInsertedChild_);
|
|
2270
|
-
// If the last inserted child into a locked frame is a pending callback,
|
|
2271
|
-
// it is an interrupt and the new interrupt must come after it. Otherwise,
|
|
2272
|
-
// we have our first interrupt for this frame and it shoudl go before the
|
|
2273
|
-
// last inserted child.
|
|
2274
|
-
index += (this.lastInsertedChild_.pendingCallback) ? 1 : -1;
|
|
2275
|
-
}
|
|
2276
|
-
this.children_.splice(Math.max(index, 0), 0, node);
|
|
2277
|
-
this.lastInsertedChild_ = node;
|
|
2278
|
-
return;
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
this.lastInsertedChild_ = node;
|
|
2282
|
-
this.children_.push(node);
|
|
2283
|
-
},
|
|
2284
|
-
|
|
2285
|
-
/**
|
|
2286
|
-
* @return {(Frame|Task)} This frame's fist child.
|
|
2287
|
-
*/
|
|
2288
|
-
getFirstChild: function() {
|
|
2289
|
-
this.isLocked_ = true;
|
|
2290
|
-
return this.children_ && this.children_[0];
|
|
2291
|
-
},
|
|
2292
|
-
|
|
2293
|
-
/**
|
|
2294
|
-
* Removes a child from this frame.
|
|
2295
|
-
* @param {!(Frame|Task)} child The child to remove.
|
|
2296
|
-
*/
|
|
2297
|
-
removeChild: function(child) {
|
|
2298
|
-
goog.asserts.assert(child.parent_ === this, 'not a child of this frame');
|
|
2299
|
-
goog.asserts.assert(this.children_ !== null, 'frame has no children!');
|
|
2300
|
-
var index = this.children_.indexOf(child);
|
|
2301
|
-
child.setParent(null);
|
|
2302
|
-
this.children_.splice(index, 1);
|
|
2303
|
-
if (this.lastInsertedChild_ === child) {
|
|
2304
|
-
this.lastInsertedChild_ = this.children_[index - 1] || null;
|
|
2305
|
-
}
|
|
2306
|
-
if (!this.children_.length) {
|
|
2307
|
-
this.children_ = null;
|
|
2308
|
-
}
|
|
2309
|
-
},
|
|
2310
|
-
|
|
2311
|
-
/** @override */
|
|
2312
|
-
toString: function() {
|
|
2313
|
-
return 'Frame::' + goog.getUid(this);
|
|
2314
|
-
}
|
|
2315
|
-
});
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
/**
|
|
2319
|
-
* A task to be executed by a {@link webdriver.promise.ControlFlow}.
|
|
2320
|
-
*
|
|
2321
|
-
* @unrestricted
|
|
2322
|
-
* @final
|
|
2323
|
-
*/
|
|
2324
|
-
var Task = goog.defineClass(promise.Deferred, {
|
|
2325
|
-
/**
|
|
2326
|
-
* @param {!promise.ControlFlow} flow The flow this instances belongs
|
|
2327
|
-
* to.
|
|
2328
|
-
* @param {function(): (T|!promise.Promise<T>)} fn The function to
|
|
2329
|
-
* call when the task executes. If it returns a
|
|
2330
|
-
* {@link webdriver.promise.Promise}, the flow will wait for it to be
|
|
2331
|
-
* resolved before starting the next task.
|
|
2332
|
-
* @param {string} description A description of the task for debugging.
|
|
2333
|
-
* @constructor
|
|
2334
|
-
* @extends {promise.Deferred<T>}
|
|
2335
|
-
* @template T
|
|
2336
|
-
*/
|
|
2337
|
-
constructor: function(flow, fn, description) {
|
|
2338
|
-
Task.base(this, 'constructor', flow);
|
|
2339
|
-
goog.getUid(this);
|
|
2340
|
-
|
|
2341
|
-
/**
|
|
2342
|
-
* @type {function(): (T|!promise.Promise<T>)}
|
|
2343
|
-
*/
|
|
2344
|
-
this.execute = fn;
|
|
2345
|
-
|
|
2346
|
-
/** @private {string} */
|
|
2347
|
-
this.description_ = description;
|
|
2348
|
-
|
|
2349
|
-
/** @private {Frame} */
|
|
2350
|
-
this.parent_ = null;
|
|
2351
|
-
|
|
2352
|
-
/** @private {Frame} */
|
|
2353
|
-
this.frame_ = null;
|
|
2354
|
-
},
|
|
2355
|
-
|
|
2356
|
-
/**
|
|
2357
|
-
* @return {Frame} frame The frame used to run this task's
|
|
2358
|
-
* {@link #execute} method.
|
|
2359
|
-
*/
|
|
2360
|
-
getFrame: function() {
|
|
2361
|
-
return this.frame_;
|
|
2362
|
-
},
|
|
2363
|
-
|
|
2364
|
-
/**
|
|
2365
|
-
* @param {Frame} frame The frame used to run this task's
|
|
2366
|
-
* {@link #execute} method.
|
|
2367
|
-
*/
|
|
2368
|
-
setFrame: function(frame) {
|
|
2369
|
-
this.frame_ = frame;
|
|
2370
|
-
},
|
|
2371
|
-
|
|
2372
|
-
/**
|
|
2373
|
-
* @param {Frame} frame The frame this task is scheduled in.
|
|
2374
|
-
*/
|
|
2375
|
-
setParent: function(frame) {
|
|
2376
|
-
goog.asserts.assert(goog.isNull(this.parent_) || goog.isNull(frame),
|
|
2377
|
-
'parent already set');
|
|
2378
|
-
this.parent_ = frame;
|
|
2379
|
-
},
|
|
2380
|
-
|
|
2381
|
-
/** @return {string} This task's description. */
|
|
2382
|
-
getDescription: function() {
|
|
2383
|
-
return this.description_;
|
|
2384
|
-
},
|
|
2385
|
-
|
|
2386
|
-
/** @override */
|
|
2387
|
-
toString: function() {
|
|
2388
|
-
return 'Task::' + goog.getUid(this) + '<' + this.description_ + '>';
|
|
2389
|
-
}
|
|
2390
|
-
});
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
/**
|
|
2394
|
-
* Manages a callback attached to a {@link webdriver.promise.Promise}. When the
|
|
2395
|
-
* promise is resolved, this callback will invoke the appropriate callback
|
|
2396
|
-
* function based on the promise's resolved value.
|
|
2397
|
-
*
|
|
2398
|
-
* @unrestricted
|
|
2399
|
-
* @final
|
|
2400
|
-
*/
|
|
2401
|
-
var Callback = goog.defineClass(promise.Deferred, {
|
|
2402
|
-
/**
|
|
2403
|
-
* @param {!promise.Promise} parent The promise this callback is attached to.
|
|
2404
|
-
* @param {(function(T): (IThenable<R>|R)|null|undefined)} callback
|
|
2405
|
-
* The fulfillment callback.
|
|
2406
|
-
* @param {(function(*): (IThenable<R>|R)|null|undefined)} errback
|
|
2407
|
-
* The rejection callback.
|
|
2408
|
-
* @param {string} name The callback name.
|
|
2409
|
-
* @param {!Function} fn The function to use as the top of the stack when
|
|
2410
|
-
* recording the callback's creation point.
|
|
2411
|
-
* @extends {promise.Deferred<R>}
|
|
2412
|
-
* @template T, R
|
|
2413
|
-
*/
|
|
2414
|
-
constructor: function(parent, callback, errback, name, fn) {
|
|
2415
|
-
Callback.base(this, 'constructor', parent.flow_);
|
|
2416
|
-
|
|
2417
|
-
/** @private {(function(T): (IThenable<R>|R)|null|undefined)} */
|
|
2418
|
-
this.callback_ = callback;
|
|
2419
|
-
|
|
2420
|
-
/** @private {(function(*): (IThenable<R>|R)|null|undefined)} */
|
|
2421
|
-
this.errback_ = errback;
|
|
2422
|
-
|
|
2423
|
-
/** @private {!Frame} */
|
|
2424
|
-
this.frame_ = new Frame(parent.flow_);
|
|
2425
|
-
this.frame_.pendingCallback = true;
|
|
2426
|
-
|
|
2427
|
-
this.promise.parent_ = parent;
|
|
2428
|
-
if (promise.LONG_STACK_TRACES) {
|
|
2429
|
-
this.promise.stack_ = promise.captureStackTrace('Promise', name, fn);
|
|
2430
|
-
}
|
|
2431
|
-
},
|
|
2432
|
-
|
|
2433
|
-
/**
|
|
2434
|
-
* Called by the parent promise when it has been resolved.
|
|
2435
|
-
* @param {!PromiseState} state The parent's new state.
|
|
2436
|
-
* @param {*} value The parent's new value.
|
|
2437
|
-
*/
|
|
2438
|
-
notify: function(state, value) {
|
|
2439
|
-
var callback = this.callback_;
|
|
2440
|
-
var fallback = this.fulfill;
|
|
2441
|
-
if (state === PromiseState.REJECTED) {
|
|
2442
|
-
callback = this.errback_;
|
|
2443
|
-
fallback = this.reject;
|
|
2444
|
-
}
|
|
2445
|
-
|
|
2446
|
-
this.frame_.pendingCallback = false;
|
|
2447
|
-
if (goog.isFunction(callback)) {
|
|
2448
|
-
this.frame_.flow_.runInFrame_(
|
|
2449
|
-
this.frame_,
|
|
2450
|
-
goog.bind(callback, undefined, value),
|
|
2451
|
-
this.fulfill, this.reject);
|
|
2452
|
-
} else {
|
|
2453
|
-
if (this.frame_.getParent()) {
|
|
2454
|
-
this.frame_.getParent().removeChild(this.frame_);
|
|
2455
|
-
}
|
|
2456
|
-
fallback(value);
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
});
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
/**
|
|
2464
|
-
* The default flow to use if no others are active.
|
|
2465
|
-
* @type {!promise.ControlFlow}
|
|
2466
|
-
*/
|
|
2467
|
-
var defaultFlow = new promise.ControlFlow();
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
/**
|
|
2471
|
-
* A stack of active control flows, with the top of the stack used to schedule
|
|
2472
|
-
* commands. When there are multiple flows on the stack, the flow at index N
|
|
2473
|
-
* represents a callback triggered within a task owned by the flow at index
|
|
2474
|
-
* N-1.
|
|
2475
|
-
* @type {!Array<!promise.ControlFlow>}
|
|
2476
|
-
*/
|
|
2477
|
-
var activeFlows = [];
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
/**
|
|
2481
|
-
* Changes the default flow to use when no others are active.
|
|
2482
|
-
* @param {!promise.ControlFlow} flow The new default flow.
|
|
2483
|
-
* @throws {Error} If the default flow is not currently active.
|
|
2484
|
-
*/
|
|
2485
|
-
promise.setDefaultFlow = function(flow) {
|
|
2486
|
-
if (activeFlows.length) {
|
|
2487
|
-
throw Error('You may only change the default flow while it is active');
|
|
2488
|
-
}
|
|
2489
|
-
defaultFlow = flow;
|
|
2490
|
-
};
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
/**
|
|
2494
|
-
* @return {!promise.ControlFlow} The currently active control flow.
|
|
2495
|
-
*/
|
|
2496
|
-
promise.controlFlow = function() {
|
|
2497
|
-
return /** @type {!promise.ControlFlow} */ (
|
|
2498
|
-
Arrays.peek(activeFlows) || defaultFlow);
|
|
2499
|
-
};
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
/**
|
|
2503
|
-
* Creates a new control flow. The provided callback will be invoked as the
|
|
2504
|
-
* first task within the new flow, with the flow as its sole argument. Returns
|
|
2505
|
-
* a promise that resolves to the callback result.
|
|
2506
|
-
* @param {function(!promise.ControlFlow)} callback The entry point
|
|
2507
|
-
* to the newly created flow.
|
|
2508
|
-
* @return {!promise.Promise} A promise that resolves to the callback
|
|
2509
|
-
* result.
|
|
2510
|
-
*/
|
|
2511
|
-
promise.createFlow = function(callback) {
|
|
2512
|
-
var flow = new promise.ControlFlow;
|
|
2513
|
-
return flow.execute(function() {
|
|
2514
|
-
return callback(flow);
|
|
2515
|
-
});
|
|
2516
|
-
};
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
/**
|
|
2520
|
-
* Tests is a function is a generator.
|
|
2521
|
-
* @param {!Function} fn The function to test.
|
|
2522
|
-
* @return {boolean} Whether the function is a generator.
|
|
2523
|
-
*/
|
|
2524
|
-
promise.isGenerator = function(fn) {
|
|
2525
|
-
return fn.constructor.name === 'GeneratorFunction';
|
|
2526
|
-
};
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
/**
|
|
2530
|
-
* Consumes a {@code GeneratorFunction}. Each time the generator yields a
|
|
2531
|
-
* promise, this function will wait for it to be fulfilled before feeding the
|
|
2532
|
-
* fulfilled value back into {@code next}. Likewise, if a yielded promise is
|
|
2533
|
-
* rejected, the rejection error will be passed to {@code throw}.
|
|
2534
|
-
*
|
|
2535
|
-
* __Example 1:__ the Fibonacci Sequence.
|
|
2536
|
-
*
|
|
2537
|
-
* promise.consume(function* fibonacci() {
|
|
2538
|
-
* var n1 = 1, n2 = 1;
|
|
2539
|
-
* for (var i = 0; i < 4; ++i) {
|
|
2540
|
-
* var tmp = yield n1 + n2;
|
|
2541
|
-
* n1 = n2;
|
|
2542
|
-
* n2 = tmp;
|
|
2543
|
-
* }
|
|
2544
|
-
* return n1 + n2;
|
|
2545
|
-
* }).then(function(result) {
|
|
2546
|
-
* console.log(result); // 13
|
|
2547
|
-
* });
|
|
2548
|
-
*
|
|
2549
|
-
* __Example 2:__ a generator that throws.
|
|
2550
|
-
*
|
|
2551
|
-
* promise.consume(function* () {
|
|
2552
|
-
* yield promise.delayed(250).then(function() {
|
|
2553
|
-
* throw Error('boom');
|
|
2554
|
-
* });
|
|
2555
|
-
* }).thenCatch(function(e) {
|
|
2556
|
-
* console.log(e.toString()); // Error: boom
|
|
2557
|
-
* });
|
|
2558
|
-
*
|
|
2559
|
-
* @param {!Function} generatorFn The generator function to execute.
|
|
2560
|
-
* @param {Object=} opt_self The object to use as "this" when invoking the
|
|
2561
|
-
* initial generator.
|
|
2562
|
-
* @param {...*} var_args Any arguments to pass to the initial generator.
|
|
2563
|
-
* @return {!promise.Promise<?>} A promise that will resolve to the
|
|
2564
|
-
* generator's final result.
|
|
2565
|
-
* @throws {TypeError} If the given function is not a generator.
|
|
2566
|
-
*/
|
|
2567
|
-
promise.consume = function(generatorFn, opt_self, var_args) {
|
|
2568
|
-
if (!promise.isGenerator(generatorFn)) {
|
|
2569
|
-
throw new TypeError('Input is not a GeneratorFunction: ' +
|
|
2570
|
-
generatorFn.constructor.name);
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
var deferred = promise.defer();
|
|
2574
|
-
var generator = generatorFn.apply(opt_self, Arrays.slice(arguments, 2));
|
|
2575
|
-
callNext();
|
|
2576
|
-
return deferred.promise;
|
|
2577
|
-
|
|
2578
|
-
/** @param {*=} opt_value . */
|
|
2579
|
-
function callNext(opt_value) {
|
|
2580
|
-
pump(generator.next, opt_value);
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
/** @param {*=} opt_error . */
|
|
2584
|
-
function callThrow(opt_error) {
|
|
2585
|
-
// Dictionary lookup required because Closure compiler's built-in
|
|
2586
|
-
// externs does not include GeneratorFunction.prototype.throw.
|
|
2587
|
-
pump(generator['throw'], opt_error);
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
function pump(fn, opt_arg) {
|
|
2591
|
-
if (!deferred.isPending()) {
|
|
2592
|
-
return; // Defererd was cancelled; silently abort.
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
try {
|
|
2596
|
-
var result = fn.call(generator, opt_arg);
|
|
2597
|
-
} catch (ex) {
|
|
2598
|
-
deferred.reject(ex);
|
|
2599
|
-
return;
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
|
-
if (result.done) {
|
|
2603
|
-
deferred.fulfill(result.value);
|
|
2604
|
-
return;
|
|
2605
|
-
}
|
|
2606
|
-
|
|
2607
|
-
promise.asap(result.value, callNext, callThrow);
|
|
2608
|
-
}
|
|
2609
|
-
};
|