WebKit Bugzilla
Attachment 356471 Details for
Bug 192171
: Web Inspector: Audit: tests should support async operations
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-192171.patch (text/plain), 62.34 KB, created by
Devin Rousso
on 2018-12-03 22:28:42 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Devin Rousso
Created:
2018-12-03 22:28:42 PST
Size:
62.34 KB
patch
obsolete
>diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 95f706d818f182c3d38ed90188d37f55cc6e0f51..eb46d5e275d2d59b11c81c469eaf70530100791f 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,43 @@ >+2018-12-03 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: tests should support async operations >+ https://bugs.webkit.org/show_bug.cgi?id=192171 >+ <rdar://problem/46423562> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ Add `awaitPromise` command for executing a callback when a Promise gets settled. >+ >+ Drive-by: allow `wasThrown` to be optional, instead of expecting it to always have a value. >+ >+ * inspector/protocol/Runtime.json: >+ >+ * inspector/InjectedScriptSource.js: >+ (InjectedScript.prototype.awaitPromise): Added. >+ >+ * inspector/InjectedScript.h: >+ * inspector/InjectedScript.cpp: >+ (Inspector::InjectedScript::evaluate): >+ (Inspector::InjectedScript::awaitPromise): Added. >+ (Inspector::InjectedScript::callFunctionOn): >+ (Inspector::InjectedScript::evaluateOnCallFrame): >+ >+ * inspector/InjectedScriptBase.h: >+ * inspector/InjectedScriptBase.cpp: >+ (Inspector::InjectedScriptBase::makeEvalCall): >+ (Inspector::InjectedScriptBase::makeAsyncCall): Added. >+ (Inspector::InjcetedScriptBase::checkCallResult): Added. >+ (Inspector::InjcetedScriptBase::checkAsyncCallResult): Added. >+ >+ * inspector/agents/InspectorRuntimeAgent.h: >+ * inspector/agents/InspectorRuntimeAgent.cpp: >+ (Inspector::InspectorRuntimeAgent::evaluate): >+ (Inspector::InspectorRuntimeAgent::awaitPromise): >+ (Inspector::InspectorRuntimeAgent::callFunctionOn): >+ >+ * inspector/agents/InspectorDebuggerAgent.cpp: >+ (Inspector::InspectorDebuggerAgent::evaluateOnCallFrame): >+ > 2018-12-03 Ryan Haddad <ryanhaddad@apple.com> > > Unreviewed, rolling out r238833. >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 256b67205b62c7913a40af2e508ae6dbbedf3bad..2bd78286a9dc71de22a79271e7be46746cbda6e9 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,16 @@ >+2018-12-03 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: tests should support async operations >+ https://bugs.webkit.org/show_bug.cgi?id=192171 >+ <rdar://problem/46423562> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ * page/Settings.yaml: >+ * dom/ScriptExecutionContext.cpp: >+ (ScriptExecutionContext::reportUnhandledPromiseRejection): >+ Add setting for muting the "Unhandled Promise Rejection" console message. >+ > 2018-12-03 Keith Rollin <krollin@apple.com> > > Add .xcfilelist files >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index a8672f0e4ea916ffe55d12df13f8ccaf8b516af5..ab6c1add26d68bd11d7a0fd78e22fd5837237800 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,31 @@ >+2018-12-03 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: tests should support async operations >+ https://bugs.webkit.org/show_bug.cgi?id=192171 >+ <rdar://problem/46423562> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ * UserInterface/Controllers/RuntimeManager.js: >+ (WI.RuntimeManager.supportsAwaitPromise): Added. >+ >+ * UserInterface/Models/AuditTestCase.js: >+ (WI.AuditTestCase.prototype.async run.async parseResponse.checkResultProperty.addErrorForValueType): Deleted. >+ (WI.AuditTestCase.prototype.async run.async parseResponse.checkResultProperty): Deleted. >+ (WI.AuditTestCase.prototype.async run.async parseResponse.async resultArrayForEach): Deleted. >+ (WI.AuditTestCase.prototype.async run.async parseResponse): Added. >+ (WI.AuditTestCase.prototype.async run): >+ (WI.AuditTestCase.prototype.async run.checkResultProperty.addErrorForValueType): Deleted. >+ (WI.AuditTestCase.prototype.async run.checkResultProperty): Deleted. >+ (WI.AuditTestCase.prototype.async run.async resultArrayForEach): Deleted. >+ >+ * UserInterface/Models/AuditTestCaseResult.js: >+ (WI.AuditTestCaseResult.async fromPayload): >+ (WI.AuditTestCaseResult.prototype.toJSON): >+ >+ * UserInterface/Views/AuditTestCaseContentView.js: >+ (WI.AuditTestCaseContentView.prototype.layout): >+ > 2018-12-03 Devin Rousso <drousso@apple.com> > > Web Inspector: Audit: "Add Default Audits" shown when there are no filter results >diff --git a/Source/JavaScriptCore/inspector/InjectedScript.cpp b/Source/JavaScriptCore/inspector/InjectedScript.cpp >index 2c9a4b2f4c31f8d2ca82eda8a6588fc63e619016..c03ffeb2e4484f75d9b8fc4cc33a6a00865a2b0e 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScript.cpp >+++ b/Source/JavaScriptCore/inspector/InjectedScript.cpp >@@ -54,7 +54,7 @@ InjectedScript::~InjectedScript() > { > } > >-void InjectedScript::evaluate(ErrorString& errorString, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown, std::optional<int>& savedResultIndex) >+void InjectedScript::evaluate(ErrorString& errorString, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) > { > Deprecated::ScriptFunctionCall function(injectedScriptObject(), "evaluate"_s, inspectorEnvironment()->functionCallHandler()); > function.appendArgument(expression); >@@ -66,7 +66,17 @@ void InjectedScript::evaluate(ErrorString& errorString, const String& expression > makeEvalCall(errorString, function, result, wasThrown, savedResultIndex); > } > >-void InjectedScript::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown) >+void InjectedScript::awaitPromise(const String& promiseObjectId, bool returnByValue, bool generatePreview, bool saveResult, AsyncCallCallback&& callback) >+{ >+ Deprecated::ScriptFunctionCall function(injectedScriptObject(), "awaitPromise"_s, inspectorEnvironment()->functionCallHandler()); >+ function.appendArgument(promiseObjectId); >+ function.appendArgument(returnByValue); >+ function.appendArgument(generatePreview); >+ function.appendArgument(saveResult); >+ makeAsyncCall(function, WTFMove(callback)); >+} >+ >+void InjectedScript::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown) > { > Deprecated::ScriptFunctionCall function(injectedScriptObject(), "callFunctionOn"_s, inspectorEnvironment()->functionCallHandler()); > function.appendArgument(objectId); >@@ -74,12 +84,13 @@ void InjectedScript::callFunctionOn(ErrorString& errorString, const String& obje > function.appendArgument(arguments); > function.appendArgument(returnByValue); > function.appendArgument(generatePreview); >- >- std::optional<int> unused; >- makeEvalCall(errorString, function, result, wasThrown, unused); >+ >+ std::optional<int> savedResultIndex; >+ makeEvalCall(errorString, function, result, wasThrown, savedResultIndex); >+ ASSERT(!savedResultIndex); > } > >-void InjectedScript::evaluateOnCallFrame(ErrorString& errorString, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown, std::optional<int>& savedResultIndex) >+void InjectedScript::evaluateOnCallFrame(ErrorString& errorString, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) > { > Deprecated::ScriptFunctionCall function(injectedScriptObject(), "evaluateOnCallFrame"_s, inspectorEnvironment()->functionCallHandler()); > function.appendArgument(callFrames); >diff --git a/Source/JavaScriptCore/inspector/InjectedScript.h b/Source/JavaScriptCore/inspector/InjectedScript.h >index 6d32b97cbfe1c66875a399a8ea9785bf03cbab95..4de9d745dce5b3dcf59bc1c53aeb6c3f8cc0a34f 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScript.h >+++ b/Source/JavaScriptCore/inspector/InjectedScript.h >@@ -33,6 +33,7 @@ > > #include "InjectedScriptBase.h" > #include <wtf/Forward.h> >+#include <wtf/Function.h> > #include <wtf/RefPtr.h> > > namespace Deprecated { >@@ -50,9 +51,10 @@ public: > InjectedScript(Deprecated::ScriptObject, InspectorEnvironment*); > virtual ~InjectedScript(); > >- void evaluate(ErrorString&, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown, std::optional<int>& savedResultIndex); >- void evaluateOnCallFrame(ErrorString&, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown, std::optional<int>& savedResultIndex); >- void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, bool& wasThrown); >+ void evaluate(ErrorString&, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex); >+ void awaitPromise(const String& promiseObjectId, bool returnByValue, bool generatePreview, bool saveResult, AsyncCallCallback&&); >+ void evaluateOnCallFrame(ErrorString&, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex); >+ void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown); > void getFunctionDetails(ErrorString&, const String& functionId, RefPtr<Protocol::Debugger::FunctionDetails>& result); > void functionDetails(ErrorString&, JSC::JSValue, RefPtr<Protocol::Debugger::FunctionDetails>& result); > void getPreview(ErrorString&, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>& result); >diff --git a/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp b/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >index c5dbc73237891efe9c87351ac7213e96e744889a..e30c6014174c81383089b0bc05276957ff8a23ca 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >+++ b/Source/JavaScriptCore/inspector/InjectedScriptBase.cpp >@@ -35,6 +35,9 @@ > #include "DebuggerEvalEnabler.h" > #include "JSCInlines.h" > #include "JSGlobalObject.h" >+#include "JSLock.h" >+#include "JSNativeStdFunction.h" >+#include "NativeStdFunctionCell.h" > #include "ScriptFunctionCall.h" > #include <wtf/JSONValues.h> > #include <wtf/text/WTFString.h> >@@ -93,9 +96,53 @@ Ref<JSON::Value> InjectedScriptBase::makeCall(Deprecated::ScriptFunctionCall& fu > return resultJSONValue.releaseNonNull(); > } > >-void InjectedScriptBase::makeEvalCall(ErrorString& errorString, Deprecated::ScriptFunctionCall& function, RefPtr<Protocol::Runtime::RemoteObject>& out_resultObject, bool& out_wasThrown, std::optional<int>& out_savedResultIndex) >+void InjectedScriptBase::makeEvalCall(ErrorString& errorString, Deprecated::ScriptFunctionCall& function, RefPtr<Protocol::Runtime::RemoteObject>& out_resultObject, std::optional<bool>& out_wasThrown, std::optional<int>& out_savedResultIndex) >+{ >+ checkCallResult(errorString, makeCall(function), out_resultObject, out_wasThrown, out_savedResultIndex); >+} >+ >+void InjectedScriptBase::makeAsyncCall(Deprecated::ScriptFunctionCall& function, AsyncCallCallback&& callback) >+{ >+ if (hasNoValue() || !hasAccessToInspectedScriptState()) { >+ checkAsyncCallResult(JSON::Value::null(), callback); >+ return; >+ } >+ >+ auto* scriptState = m_injectedScriptObject.scriptState(); >+ JSC::VM& vm = scriptState->vm(); >+ >+ JSC::JSNativeStdFunction* jsFunction; >+ >+ { >+ JSC::JSLockHolder locker(vm); >+ >+ jsFunction = JSC::JSNativeStdFunction::create(vm, scriptState->lexicalGlobalObject(), 1, String(), [&, callback = WTFMove(callback)] (JSC::ExecState* exec) { >+ if (!exec) >+ checkAsyncCallResult(JSON::Value::create("Exception while making a call."), callback); >+ if (auto resultJSONValue = toInspectorValue(*exec, exec->argument(0))) >+ checkAsyncCallResult(resultJSONValue, callback); >+ else >+ checkAsyncCallResult(JSON::Value::create(String::format("Object has too long reference chain (must not be longer than %d)", JSON::Value::maxDepth)), callback); >+ return JSC::JSValue::encode(JSC::jsUndefined()); >+ }); >+ } >+ >+ function.appendArgument(JSC::JSValue(jsFunction)); >+ >+ bool hadException = false; >+ auto resultJSValue = callFunctionWithEvalEnabled(function, hadException); >+ ASSERT_UNUSED(resultJSValue, resultJSValue.isUndefined()); >+ >+ ASSERT(!hadException); >+ if (hadException) { >+ // Since `callback` is moved above, we can't call it if there's an exception while trying to >+ // execute the `JSNativeStdFunction` inside InjectedScriptSource.js. >+ jsFunction->nativeStdFunctionCell()->function()(nullptr); >+ } >+} >+ >+void InjectedScriptBase::checkCallResult(ErrorString& errorString, RefPtr<JSON::Value> result, RefPtr<Protocol::Runtime::RemoteObject>& out_resultObject, std::optional<bool>& out_wasThrown, std::optional<int>& out_savedResultIndex) > { >- RefPtr<JSON::Value> result = makeCall(function); > if (!result) { > errorString = "Internal error: result value is empty"_s; > return; >@@ -126,12 +173,26 @@ void InjectedScriptBase::makeEvalCall(ErrorString& errorString, Deprecated::Scri > } > > out_resultObject = BindingTraits<Protocol::Runtime::RemoteObject>::runtimeCast(resultObject); >- out_wasThrown = wasThrown; >+ >+ if (wasThrown) >+ out_wasThrown = wasThrown; > > int savedResultIndex; > if (resultTuple->getInteger("savedResultIndex"_s, savedResultIndex)) > out_savedResultIndex = savedResultIndex; > } > >+void InjectedScriptBase::checkAsyncCallResult(RefPtr<JSON::Value> result, const AsyncCallCallback& callback) >+{ >+ ErrorString errorString; >+ RefPtr<Protocol::Runtime::RemoteObject> resultObject; >+ std::optional<bool> wasThrown; >+ std::optional<int> savedResultIndex; >+ >+ checkCallResult(errorString, result, resultObject, wasThrown, savedResultIndex); >+ >+ callback(errorString, WTFMove(resultObject), wasThrown, savedResultIndex); >+} >+ > } // namespace Inspector > >diff --git a/Source/JavaScriptCore/inspector/InjectedScriptBase.h b/Source/JavaScriptCore/inspector/InjectedScriptBase.h >index 3e7e7a3009eba81760e3b16d948150aa9d2fa730..447a4288f9977cf2cb20aefdefe2cb52b02c3a78 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScriptBase.h >+++ b/Source/JavaScriptCore/inspector/InjectedScriptBase.h >@@ -35,6 +35,7 @@ > #include "InspectorProtocolObjects.h" > #include "ScriptObject.h" > #include <wtf/Forward.h> >+#include <wtf/Function.h> > #include <wtf/RefPtr.h> > > namespace Deprecated { >@@ -44,6 +45,7 @@ class ScriptFunctionCall; > namespace Inspector { > > typedef String ErrorString; >+typedef WTF::Function<void(ErrorString&, RefPtr<Protocol::Runtime::RemoteObject>&&, std::optional<bool>&, std::optional<int>&)> AsyncCallCallback; > > class JS_EXPORT_PRIVATE InjectedScriptBase { > public: >@@ -64,9 +66,13 @@ protected: > const Deprecated::ScriptObject& injectedScriptObject() const; > JSC::JSValue callFunctionWithEvalEnabled(Deprecated::ScriptFunctionCall&, bool& hadException) const; > Ref<JSON::Value> makeCall(Deprecated::ScriptFunctionCall&); >- void makeEvalCall(ErrorString&, Deprecated::ScriptFunctionCall&, RefPtr<Protocol::Runtime::RemoteObject>& resultObject, bool& wasThrown, std::optional<int>& savedResultIndex); >+ void makeEvalCall(ErrorString&, Deprecated::ScriptFunctionCall&, RefPtr<Protocol::Runtime::RemoteObject>& resultObject, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex); >+ void makeAsyncCall(Deprecated::ScriptFunctionCall&, AsyncCallCallback&&); > > private: >+ void checkCallResult(ErrorString&, RefPtr<JSON::Value> result, RefPtr<Protocol::Runtime::RemoteObject>& resultObject, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex); >+ void checkAsyncCallResult(RefPtr<JSON::Value> result, const AsyncCallCallback&); >+ > String m_name; > Deprecated::ScriptObject m_injectedScriptObject; > InspectorEnvironment* m_environment { nullptr }; >diff --git a/Source/JavaScriptCore/inspector/InjectedScriptSource.js b/Source/JavaScriptCore/inspector/InjectedScriptSource.js >index d6b75f1fecd79af702f9ed58000423238343668a..b5adc6f99b3f2d887b735ebd8ee45e111e3bf6c0 100644 >--- a/Source/JavaScriptCore/inspector/InjectedScriptSource.js >+++ b/Source/JavaScriptCore/inspector/InjectedScriptSource.js >@@ -108,6 +108,43 @@ let InjectedScript = class InjectedScript > return this._evaluateAndWrap(InjectedScriptHost.evaluateWithScopeExtension, InjectedScriptHost, expression, objectGroup, false, injectCommandLineAPI, returnByValue, generatePreview, saveResult); > } > >+ awaitPromise(promiseObjectId, returnByValue, generatePreview, saveResult, callback) >+ { >+ let parsedPromiseObjectId = this._parseObjectId(promiseObjectId); >+ let promiseObject = this._objectForId(parsedPromiseObjectId); >+ let promiseObjectGroupName = this._idToObjectGroupName[parsedPromiseObjectId.id]; >+ >+ if (!isDefined(promiseObject)) { >+ callback("Could not find object with given id"); >+ return; >+ } >+ >+ if (!(promiseObject instanceof Promise)) { >+ callback("Object with given id is not a Promise"); >+ return; >+ } >+ >+ let resolve = (value) => { >+ let returnObject = { >+ wasThrown: false, >+ result: RemoteObject.create(value, promiseObjectGroupName, returnByValue, generatePreview), >+ }; >+ >+ if (saveResult) { >+ this._savedResultIndex = 0; >+ this._saveResult(returnObject.result); >+ if (this._savedResultIndex) >+ returnObject.savedResultIndex = this._savedResultIndex; >+ } >+ >+ callback(returnObject); >+ }; >+ let reject = (reason) => { >+ callback(this._createThrownValue(reason, promiseObjectGroupName)); >+ }; >+ promiseObject.then(resolve, reject); >+ } >+ > evaluateOnCallFrame(topCallFrame, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, saveResult) > { > let callFrame = this._callFrameForId(topCallFrame, callFrameId); >diff --git a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp >index b487a22010ec51f290501aa9336c9dfc1f315c90..f9910071742281e6b1dfe5df8a7b321996c4467b 100644 >--- a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp >+++ b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp >@@ -827,7 +827,7 @@ void InspectorDebuggerAgent::setPauseOnAssertions(ErrorString&, bool enabled) > m_pauseOnAssertionFailures = enabled; > } > >-void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& outWasThrown, std::optional<int>& savedResultIndex) >+void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) > { > if (!m_currentCallStack) { > errorString = "Not paused"_s; >@@ -848,11 +848,9 @@ void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const > muteConsole(); > } > >- bool wasThrown; > injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack.get(), callFrameId, expression, > objectGroup ? *objectGroup : emptyString(), includeCommandLineAPI && *includeCommandLineAPI, returnByValue && *returnByValue, generatePreview && *generatePreview, saveResult && *saveResult, > result, wasThrown, savedResultIndex); >- outWasThrown = wasThrown; > > if (pauseAndMute) { > unmuteConsole(); >diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp >index e869a6172d3677ff2992ef0e478593225c354cb0..627975b71339c559369205ceed8a903320bebe61 100644 >--- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp >+++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp >@@ -111,7 +111,7 @@ void InspectorRuntimeAgent::parse(ErrorString&, const String& expression, Protoc > } > } > >-void InspectorRuntimeAgent::evaluate(ErrorString& errorString, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& outWasThrown, std::optional<int>& savedResultIndex) >+void InspectorRuntimeAgent::evaluate(ErrorString& errorString, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) > { > InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId); > if (injectedScript.hasNoValue()) >@@ -123,9 +123,7 @@ void InspectorRuntimeAgent::evaluate(ErrorString& errorString, const String& exp > if (asBool(doNotPauseOnExceptionsAndMuteConsole)) > muteConsole(); > >- bool wasThrown; > injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : String(), asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), asBool(saveResult), result, wasThrown, savedResultIndex); >- outWasThrown = wasThrown; > > if (asBool(doNotPauseOnExceptionsAndMuteConsole)) { > unmuteConsole(); >@@ -133,7 +131,23 @@ void InspectorRuntimeAgent::evaluate(ErrorString& errorString, const String& exp > } > } > >-void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& outWasThrown) >+void InspectorRuntimeAgent::awaitPromise(const String& promiseObjectId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, Ref<AwaitPromiseCallback>&& callback) >+{ >+ InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(promiseObjectId); >+ if (injectedScript.hasNoValue()) { >+ callback->sendFailure("Could not find InjectedScript for promiseObjectId"_s); >+ return; >+ } >+ >+ injectedScript.awaitPromise(promiseObjectId, asBool(returnByValue), asBool(generatePreview), asBool(saveResult), [callback = WTFMove(callback)] (ErrorString& errorString, RefPtr<Protocol::Runtime::RemoteObject>&& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) { >+ if (!errorString.isEmpty()) >+ callback->sendFailure(errorString); >+ else >+ callback->sendSuccess(WTFMove(result), wasThrown, savedResultIndex); >+ }); >+} >+ >+void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown) > { > InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); > if (injectedScript.hasNoValue()) { >@@ -151,12 +165,8 @@ void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const Strin > if (asBool(doNotPauseOnExceptionsAndMuteConsole)) > muteConsole(); > >- bool wasThrown; >- > injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), asBool(generatePreview), result, wasThrown); > >- outWasThrown = wasThrown; >- > if (asBool(doNotPauseOnExceptionsAndMuteConsole)) { > unmuteConsole(); > setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState); >diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h >index 29927b1f4971dfb510041c35a727af220fde01f3..ab6d7fc856bbf8f62250243cfd5c2e8231884b0c 100644 >--- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h >+++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h >@@ -59,6 +59,7 @@ public: > void disable(ErrorString&) override { m_enabled = false; } > void parse(ErrorString&, const String& expression, Protocol::Runtime::SyntaxErrorType* result, std::optional<String>& message, RefPtr<Protocol::Runtime::ErrorRange>&) final; > void evaluate(ErrorString&, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown, std::optional<int>& savedResultIndex) final; >+ void awaitPromise(const String& promiseObjectId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, Ref<AwaitPromiseCallback>&&) final; > void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, std::optional<bool>& wasThrown) final; > void releaseObject(ErrorString&, const ErrorString& objectId) final; > void getPreview(ErrorString&, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>&) final; >diff --git a/Source/JavaScriptCore/inspector/protocol/Runtime.json b/Source/JavaScriptCore/inspector/protocol/Runtime.json >index 33d59a44c6f2f40850e1c0a589bd4bda444c1528..42f0745aa1c21b0f98f8e48ac2238e874abab1df 100644 >--- a/Source/JavaScriptCore/inspector/protocol/Runtime.json >+++ b/Source/JavaScriptCore/inspector/protocol/Runtime.json >@@ -225,6 +225,22 @@ > { "name": "savedResultIndex", "type": "integer", "optional": true, "description": "If the result was saved, this is the $n index that can be used to access the value." } > ] > }, >+ { >+ "name": "awaitPromise", >+ "description": "Calls the async callback when the promise with the given ID gets settled.", >+ "parameters": [ >+ { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, >+ { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, >+ { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, >+ { "name": "saveResult", "type": "boolean", "optional": true, "description": "Whether the resulting value should be considered for saving in the $n history." } >+ ], >+ "returns": [ >+ { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, >+ { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, >+ { "name": "savedResultIndex", "type": "integer", "optional": true, "description": "If the result was saved, this is the $n index that can be used to access the value." } >+ ], >+ "async": true >+ }, > { > "name": "callFunctionOn", > "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object.", >diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp >index b3cfba90a8e17e952b0aa76b02258393c230891e..3cfeb0579426e275fa52334988a4096dbc776ccd 100644 >--- a/Source/WebCore/dom/ScriptExecutionContext.cpp >+++ b/Source/WebCore/dom/ScriptExecutionContext.cpp >@@ -39,6 +39,7 @@ > #include "JSDOMWindow.h" > #include "MessagePort.h" > #include "Navigator.h" >+#include "Page.h" > #include "PublicURLManager.h" > #include "RejectedPromiseTracker.h" > #include "ResourceRequest.h" >@@ -392,6 +393,14 @@ void ScriptExecutionContext::reportException(const String& errorMessage, int lin > > void ScriptExecutionContext::reportUnhandledPromiseRejection(JSC::ExecState& state, JSC::JSPromise& promise, RefPtr<Inspector::ScriptCallStack>&& callStack) > { >+ Page* page = nullptr; >+ if (is<Document>(this)) >+ page = downcast<Document>(this)->page(); >+ // FIXME: allow Workers to mute unhandled promise rejection messages. >+ >+ if (page && !page->settings().unhandledPromiseRejectionToConsoleEnabled()) >+ return; >+ > JSC::VM& vm = state.vm(); > auto scope = DECLARE_CATCH_SCOPE(vm); > >diff --git a/Source/WebCore/page/Settings.yaml b/Source/WebCore/page/Settings.yaml >index 4177fa7490462c3055813580d7bc45379d95b876..6daec2ae5b79e2f2353e664d3629835121e512ed 100644 >--- a/Source/WebCore/page/Settings.yaml >+++ b/Source/WebCore/page/Settings.yaml >@@ -224,6 +224,8 @@ webGLEnabled: > initial: false > webGLErrorsToConsoleEnabled: > initial: true >+unhandledPromiseRejectionToConsoleEnabled: >+ initial: true > forceSoftwareWebGLRendering: > initial: false > forceWebGLUsesLowPower: >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js b/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js >index 5a14542aee9b9f4eb8727370eeb720a5140653fb..a71beeced20727d700dcf6913602d7be70c9f446 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/RuntimeManager.js >@@ -34,6 +34,14 @@ WI.RuntimeManager = class RuntimeManager extends WI.Object > WI.Frame.addEventListener(WI.Frame.Event.ExecutionContextsCleared, this._frameExecutionContextsCleared, this); > } > >+ // Static >+ >+ static supportsAwaitPromise() >+ { >+ // COMPATIBILITY (iOS 12): Runtime.awaitPromise did not exist >+ return !!InspectorBackend.domains.Runtime.awaitPromise; >+ } >+ > // Target > > initializeTarget(target) >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >index e42d538685f42ede71db7aeddf982cfcc682a34c..a6daef2582bc4e9463c2eb453b15f93a3c7048c8 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >@@ -112,13 +112,9 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase > doNotPauseOnExceptionsAndMuteConsole: true, > }; > >- try { >- metadata.startTimestamp = new Date; >- let evaluateResponse = await RuntimeAgent.evaluate.invoke(evaluateArguments); >- metadata.endTimestamp = new Date; >- >- let remoteObject = WI.RemoteObject.fromPayload(evaluateResponse.result, WI.mainTarget); >- if (evaluateResponse.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error")) >+ async function parseResponse(response) { >+ let remoteObject = WI.RemoteObject.fromPayload(response.result, WI.mainTarget); >+ if (response.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error")) > addError(remoteObject.description); > else if (remoteObject.type === "boolean") > setLevel(remoteObject.value ? WI.AuditTestCaseResult.Level.Pass : WI.AuditTestCaseResult.Level.Fail); >@@ -234,6 +230,27 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase > }); > } else > addError(WI.UIString("Return value is not an object, string, or boolean")); >+ } >+ >+ try { >+ metadata.startTimestamp = new Date; >+ let response = await RuntimeAgent.evaluate.invoke(evaluateArguments); >+ metadata.endTimestamp = new Date; >+ >+ if (response.result.type === "object" && response.result.className === "Promise") { >+ if (WI.RuntimeManager.supportsAwaitPromise()) { >+ metadata.asyncTimestamp = metadata.endTimestamp; >+ response = await RuntimeAgent.awaitPromise(response.result.objectId); >+ metadata.endTimestamp = new Date; >+ } else { >+ response = null; >+ addError(WI.UIString("Async audits are not supported.")); >+ setLevel(WI.AuditTestCaseResult.Level.Unsupported); >+ } >+ } >+ >+ if (response) >+ await parseResponse(response); > } catch (error) { > metadata.endTimestamp = new Date; > addError(error.message); >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >index 63d0409e0f131e0c7b45d970c1683018b7048ca3..23dd9cd83828826b84aa3b3ef4aea210c5ab74a6 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >@@ -80,6 +80,7 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > metadata = {}; > else { > metadata.startTimestamp = typeof metadata.startTimestamp === "string" ? new Date(metadata.startTimestamp) : null; >+ metadata.asyncTimestamp = typeof metadata.asyncTimestamp === "string" ? new Date(metadata.asyncTimestamp) : null; > metadata.endTimestamp = typeof metadata.endTimestamp === "string" ? new Date(metadata.endTimestamp) : null; > metadata.url = typeof metadata.url === "string" ? metadata.url : null; > } >@@ -112,6 +113,8 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > options.metadata = {}; > if (metadata.startTimestamp && !isNaN(metadata.startTimestamp)) > options.metadata.startTimestamp = metadata.startTimestamp; >+ if (metadata.asyncTimestamp && !isNaN(metadata.asyncTimestamp)) >+ options.metadata.asyncTimestamp = metadata.asyncTimestamp; > if (metadata.endTimestamp && !isNaN(metadata.endTimestamp)) > options.metadata.endTimestamp = metadata.endTimestamp; > if (metadata.url) >@@ -177,6 +180,8 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > let metadata = {}; > if (this._metadata.startTimestamp && !isNaN(this._metadata.startTimestamp)) > metadata.startTimestamp = this._metadata.startTimestamp; >+ if (this._metadata.asyncTimestamp && !isNaN(this._metadata.asyncTimestamp)) >+ metadata.asyncTimestamp = this._metadata.asyncTimestamp; > if (this._metadata.endTimestamp && !isNaN(this._metadata.endTimestamp)) > metadata.endTimestamp = this._metadata.endTimestamp; > if (this._metadata.url) >diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >index 10882ebdfeac9c6bc571f44fcc49458735fd6c5d..1a8d7fd97eb7f6088351472461e7dfb82b623232 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >@@ -104,9 +104,19 @@ WI.AuditTestCaseContentView = class AuditTestCaseContentView extends WI.AuditTes > timeElement.textContent = metadata.startTimestamp.toLocaleString(); > > if (metadata.endTimestamp) { >+ let totalDuration = Number.secondsToString((metadata.endTimestamp - metadata.startTimestamp) / 1000); >+ > let durationElement = this._metadataElement.appendChild(document.createElement("span")); > durationElement.classList.add("duration"); >- durationElement.textContent = Number.secondsToString((metadata.endTimestamp - metadata.startTimestamp) / 1000); >+ durationElement.textContent = totalDuration; >+ >+ if (metadata.asyncTimestamp) { >+ let evalDuration = Number.secondsToString((metadata.asyncTimestamp - metadata.startTimestamp) / 1000); >+ let asyncDuration = Number.secondsToString((metadata.endTimestamp - metadata.asyncTimestamp) / 1000); >+ >+ durationElement.classList.add("async"); >+ durationElement.title = WI.UIString("%s eval\n%s async").format(evalDuration, asyncDuration); >+ } > } > } > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 4b867b8e837724d7f98c360e8a6e5a7719eb0ef9..c0529029ca0899fa725009685decb7becab6052c 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,27 @@ >+2018-12-03 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: tests should support async operations >+ https://bugs.webkit.org/show_bug.cgi?id=192171 >+ <rdar://problem/46423562> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ * inspector/audit/resources/audit-utilities.js: >+ (TestPage.registerInitializer.InspectorTest.Audit.addFunctionlessTest): >+ (TestPage.registerInitializer.InspectorTest.Audit.addStringTest): >+ (TestPage.registerInitializer.InspectorTest.Audit.addObjectTest): >+ (TestPage.registerInitializer.InspectorTest.Audit.addPromiseTest): Added. >+ * inspector/audit/basic-expected.txt: >+ * inspector/audit/basic.html: >+ >+ * inspector/model/auditTestCaseResult-expected.txt: >+ * inspector/model/auditTestCaseResult.html: >+ * inspector/model/auditTestGroupResult-expected.txt: >+ * inspector/model/auditTestGroupResult.html: >+ >+ * inspector/runtime/awaitPromise-expected.txt: Added. >+ * inspector/runtime/awaitPromise.html: Added. >+ > 2018-12-03 Ryan Haddad <ryanhaddad@apple.com> > > [css-grid] Crash on debug changing the style of a positioned element >diff --git a/LayoutTests/inspector/audit/basic-expected.txt b/LayoutTests/inspector/audit/basic-expected.txt >index 4246c2982c5361dd192389f0669d071554f259f4..b1a054918319423245a03eaf2281839d820f24d8 100644 >--- a/LayoutTests/inspector/audit/basic-expected.txt >+++ b/LayoutTests/inspector/audit/basic-expected.txt >@@ -50,23 +50,51 @@ PASS: Result should be "error". > Testing value `{"level":"unsupported"}`... > PASS: Result should be "unsupported". > >+-- Running test case: Audit.Basic.Promise.Boolean.True >+Testing value `new Promise((resolve, reject) => resolve(true))`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Promise.String.Pass >+Testing value `new Promise((resolve, reject) => resolve("pass"))`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Promise.Object.Pass >+Testing value `new Promise((resolve, reject) => resolve({level: "pass"}))`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Async.Boolean.True >+Testing value `true`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Async.String.Pass >+Testing value `"pass"`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Async.Object.Pass >+Testing value `{"level":"pass"}`... >+PASS: Result should be "pass". >+ >+-- Running test case: Audit.Basic.Timeout.Pass >+Testing value `new Promise((resolve, reject) => setTimeout(resolve, 0, "pass"))`... >+PASS: Result should be "pass". >+ > -- Running test case: Audit.Basic.Error.Undefined >-Testing... >+Testing value `undefined`... > PASS: Result should be "error". > errors: >- - TypeError: eval(undefined) is not a function. (In 'eval(undefined)()', 'eval(undefined)' is undefined) >+ - Return value is not an object, string, or boolean > > -- Running test case: Audit.Basic.Error.Null >-Testing... >+Testing value `null`... > PASS: Result should be "error". > errors: >- - TypeError: eval(null) is not a function. (In 'eval(null)()', 'eval(null)' is null) >+ - Return value is not an object, string, or boolean > > -- Running test case: Audit.Basic.Error.Number >-Testing... >+Testing value `42`... > PASS: Result should be "error". > errors: >- - TypeError: eval(42) is not a function. (In 'eval(42)()', 'eval(42)' is 42) >+ - Return value is not an object, string, or boolean > > -- Running test case: Audit.Basic.Error.String > Testing value `"foo"`... >@@ -81,8 +109,20 @@ PASS: Result should be "error". > - Missing result level > > -- Running test case: Audit.Basic.Error.Variable >-Testing... >+Testing value `INVALID`... > PASS: Result should be "error". > errors: > - ReferenceError: Can't find variable: INVALID > >+-- Running test case: Audit.Basic.Error.Promise.Resolved >+Testing value `new Promise((resolve, reject) => setTimeout(resolve, 0))`... >+PASS: Result should be "error". >+ errors: >+ - Return value is not an object, string, or boolean >+ >+-- Running test case: Audit.Basic.Error.Promise.Rejected >+Testing value `new Promise((resolve, reject) => setTimeout(reject, 0, "rejected"))`... >+PASS: Result should be "error". >+ errors: >+ - rejected >+ >diff --git a/LayoutTests/inspector/audit/basic.html b/LayoutTests/inspector/audit/basic.html >index 085a04adc6ae5736e82c373906c4cd5a9fe82362..f31ebf44de18066a9f20c8c48c4c4e9cff4bf86c 100644 >--- a/LayoutTests/inspector/audit/basic.html >+++ b/LayoutTests/inspector/audit/basic.html >@@ -4,6 +4,9 @@ > <script src="../../http/tests/inspector/resources/inspector-test.js"></script> > <script src="resources/audit-utilities.js"></script> > <script> >+if (window.internals) >+ window.internals.settings.setUnhandledPromiseRejectionToConsoleEnabled(false); >+ > function test() > { > let suite = InspectorTest.Audit.createSuite("Audit.Basic"); >@@ -23,12 +26,24 @@ function test() > InspectorTest.Audit.addObjectTest("Audit.Basic.Object.Error", {level: WI.AuditTestCaseResult.Level.Error}, WI.AuditTestCaseResult.Level.Error); > InspectorTest.Audit.addObjectTest("Audit.Basic.Object.Unsupported", {level: WI.AuditTestCaseResult.Level.Unsupported}, WI.AuditTestCaseResult.Level.Unsupported); > >- InspectorTest.Audit.addTest("Audit.Basic.Error.Undefined", undefined, WI.AuditTestCaseResult.Level.Error); >- InspectorTest.Audit.addTest("Audit.Basic.Error.Null", null, WI.AuditTestCaseResult.Level.Error); >- InspectorTest.Audit.addTest("Audit.Basic.Error.Number", 42, WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Promise.Boolean.True", `resolve(true)`, WI.AuditTestCaseResult.Level.Pass); >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Promise.String.Pass", `resolve("${WI.AuditTestCaseResult.Level.Pass}")`, WI.AuditTestCaseResult.Level.Pass); >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Promise.Object.Pass", `resolve({level: "${WI.AuditTestCaseResult.Level.Pass}"})`, WI.AuditTestCaseResult.Level.Pass); >+ >+ InspectorTest.Audit.addFunctionlessTest("Audit.Basic.Async.Boolean.True", true, WI.AuditTestCaseResult.Level.Pass, {async: true}); >+ InspectorTest.Audit.addStringTest("Audit.Basic.Async.String.Pass", WI.AuditTestCaseResult.Level.Pass, WI.AuditTestCaseResult.Level.Pass, {async: true}); >+ InspectorTest.Audit.addObjectTest("Audit.Basic.Async.Object.Pass", {level: WI.AuditTestCaseResult.Level.Pass}, WI.AuditTestCaseResult.Level.Pass, {async: true}); >+ >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Timeout.Pass", `setTimeout(resolve, 0, "${WI.AuditTestCaseResult.Level.Pass}")`, WI.AuditTestCaseResult.Level.Pass); >+ >+ InspectorTest.Audit.addFunctionlessTest("Audit.Basic.Error.Undefined", undefined, WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addFunctionlessTest("Audit.Basic.Error.Null", null, WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addFunctionlessTest("Audit.Basic.Error.Number", 42, WI.AuditTestCaseResult.Level.Error); > InspectorTest.Audit.addStringTest("Audit.Basic.Error.String", "foo", WI.AuditTestCaseResult.Level.Error); > InspectorTest.Audit.addObjectTest("Audit.Basic.Error.Object", {}, WI.AuditTestCaseResult.Level.Error); >- InspectorTest.Audit.addTest("Audit.Basic.Error.Variable", "INVALID", WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addFunctionlessTest("Audit.Basic.Error.Variable", "INVALID", WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Error.Promise.Resolved", `setTimeout(resolve, 0)`, WI.AuditTestCaseResult.Level.Error); >+ InspectorTest.Audit.addPromiseTest("Audit.Basic.Error.Promise.Rejected", `setTimeout(reject, 0, "rejected")`, WI.AuditTestCaseResult.Level.Error); > > suite.runTestCasesAndFinish(); > } >diff --git a/LayoutTests/inspector/audit/resources/audit-utilities.js b/LayoutTests/inspector/audit/resources/audit-utilities.js >index 136b59d5559f34b8c5029c0300782dc145b1cc8d..10f114c82e00597205a57cfb5e5bffe4ad218fdd 100644 >--- a/LayoutTests/inspector/audit/resources/audit-utilities.js >+++ b/LayoutTests/inspector/audit/resources/audit-utilities.js >@@ -69,18 +69,22 @@ TestPage.registerInitializer(() => { > }); > }; > >- InspectorTest.Audit.addFunctionlessTest = function(name, test, level) { >- InspectorTest.Audit.addTest(name, `function() { return ${test} }`, level, { >+ InspectorTest.Audit.addFunctionlessTest = function(name, test, level, options = {}) { >+ InspectorTest.Audit.addTest(name, (options.async ? "async " : "") + `function() { return ${test} }`, level, { > beforeStart: ` value \`${test}\``, > }); > }; > >- InspectorTest.Audit.addStringTest = function(name, test, level) { >- InspectorTest.Audit.addFunctionlessTest(name, `"${test}"`, level); >+ InspectorTest.Audit.addStringTest = function(name, test, level, options = {}) { >+ InspectorTest.Audit.addFunctionlessTest(name, `"${test}"`, level, options); > }; > >- InspectorTest.Audit.addObjectTest = function(name, test, level) { >- InspectorTest.Audit.addFunctionlessTest(name, JSON.stringify(test), level); >+ InspectorTest.Audit.addObjectTest = function(name, test, level, options = {}) { >+ InspectorTest.Audit.addFunctionlessTest(name, JSON.stringify(test), level, options); >+ }; >+ >+ InspectorTest.Audit.addPromiseTest = function(name, test, level, options = {}) { >+ InspectorTest.Audit.addFunctionlessTest(name, `new Promise((resolve, reject) => ${test})`, level, options); > }; > > InspectorTest.Audit.addDOMSelectorTest = function(name, test, level) { >diff --git a/LayoutTests/inspector/model/auditTestCaseResult-expected.txt b/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >index cbed0eb141fb40af27549a35c92faf29d0c57f9b..814ad23f65b0c449df5a5f1979296c8ffaaaa07b 100644 >--- a/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >+++ b/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >@@ -55,7 +55,8 @@ null > }, > "metadata": { > "startTimestamp": "0001-01-01T00:00:00.000Z", >- "endTimestamp": "0002-01-01T00:00:00.000Z", >+ "asyncTimestamp": "0002-01-01T00:00:00.000Z", >+ "endTimestamp": "0003-01-01T00:00:00.000Z", > "url": "validWithValidSubOptionals test result url" > } > } >diff --git a/LayoutTests/inspector/model/auditTestCaseResult.html b/LayoutTests/inspector/model/auditTestCaseResult.html >index fd44fc7808cd5bde461a57de0ad8d57b407f38d8..4ee3165c20482cde4a138412755e86dba3f81803 100644 >--- a/LayoutTests/inspector/model/auditTestCaseResult.html >+++ b/LayoutTests/inspector/model/auditTestCaseResult.html >@@ -71,6 +71,7 @@ function test() > }, > metadata: { > startTimestamp: null, >+ asyncTimestamp: null, > endTimestamp: null, > url: null, > }, >@@ -90,7 +91,8 @@ function test() > }, > metadata: { > startTimestamp: "1", >- endTimestamp: "2", >+ asyncTimestamp: "2", >+ endTimestamp: "3", > url: "validWithValidSubOptionals test result url", > }, > }, >diff --git a/LayoutTests/inspector/model/auditTestGroupResult-expected.txt b/LayoutTests/inspector/model/auditTestGroupResult-expected.txt >index 35b465e6218ec6b8aa1f077997bff16529d5e9fa..1b732d909f821ddd6e9cee22bd5e1b1ba318be9a 100644 >--- a/LayoutTests/inspector/model/auditTestGroupResult-expected.txt >+++ b/LayoutTests/inspector/model/auditTestGroupResult-expected.txt >@@ -70,7 +70,8 @@ null > }, > "metadata": { > "startTimestamp": "0001-01-01T00:00:00.000Z", >- "endTimestamp": "0002-01-01T00:00:00.000Z", >+ "asyncTimestamp": "0002-01-01T00:00:00.000Z", >+ "endTimestamp": "0003-01-01T00:00:00.000Z", > "url": "validWithValidOptionals test result url" > } > } >@@ -106,7 +107,8 @@ null > }, > "metadata": { > "startTimestamp": "0001-01-01T00:00:00.000Z", >- "endTimestamp": "0002-01-01T00:00:00.000Z", >+ "asyncTimestamp": "0002-01-01T00:00:00.000Z", >+ "endTimestamp": "0003-01-01T00:00:00.000Z", > "url": "validNested nested test result url" > } > } >@@ -129,8 +131,9 @@ null > ] > }, > "metadata": { >- "startTimestamp": "0003-01-01T00:00:00.000Z", >- "endTimestamp": "0004-01-01T00:00:00.000Z", >+ "startTimestamp": "0004-01-01T00:00:00.000Z", >+ "asyncTimestamp": "0005-01-01T00:00:00.000Z", >+ "endTimestamp": "0006-01-01T00:00:00.000Z", > "url": "validNested test result url" > } > } >diff --git a/LayoutTests/inspector/model/auditTestGroupResult.html b/LayoutTests/inspector/model/auditTestGroupResult.html >index 9767049c6a25d756f5b1418ecc9a5311c4a7bfff..5f32fcb95fe0c079218328b372d717139d0caf2d 100644 >--- a/LayoutTests/inspector/model/auditTestGroupResult.html >+++ b/LayoutTests/inspector/model/auditTestGroupResult.html >@@ -107,7 +107,8 @@ function test() > }, > metadata: { > startTimestamp: "1", >- endTimestamp: "2", >+ asyncTimestamp: "2", >+ endTimestamp: "3", > url: "validWithValidOptionals test result url", > }, > }, >@@ -138,7 +139,8 @@ function test() > }, > metadata: { > startTimestamp: "1", >- endTimestamp: "2", >+ asyncTimestamp: "2", >+ endTimestamp: "3", > url: "validNested nested test result url", > }, > }, >@@ -155,8 +157,9 @@ function test() > errors: ["validNested test result error"], > }, > metadata: { >- startTimestamp: "3", >- endTimestamp: "4", >+ startTimestamp: "4", >+ asyncTimestamp: "5", >+ endTimestamp: "6", > url: "validNested test result url", > }, > }, >diff --git a/LayoutTests/inspector/runtime/awaitPromise-expected.txt b/LayoutTests/inspector/runtime/awaitPromise-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..390a4dea7e6ebf96b900ca6bd5c00d58c3b53c71 >--- /dev/null >+++ b/LayoutTests/inspector/runtime/awaitPromise-expected.txt >@@ -0,0 +1,60 @@ >+Tests functionality of Runtime.awaitPromise. >+ >+ >+== Running test suite: Runtime.awaitPromise >+-- Running test case: Runtime.awaitPromise.Resolve.Undefined >+PASS: The resolved value should be undefined >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Null >+PASS: The resolved value should be null >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Boolean >+PASS: The resolved value should be true >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Number >+PASS: The resolved value should be 42 >+ >+-- Running test case: Runtime.awaitPromise.Resolve.String >+PASS: The resolved value should be "foo" >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Array >+PASS: The resolved value should be [0,1] >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Object >+PASS: The resolved value should be {"a":1,"b":2} >+ >+-- Running test case: Runtime.awaitPromise.Resolve.Chain >+PASS: The resolved value should be 3. >+ >+-- Running test case: Runtime.awaitPromise.Reject.Undefined >+PASS: The rejected value should be undefined >+ >+-- Running test case: Runtime.awaitPromise.Reject.Null >+PASS: The rejected value should be null >+ >+-- Running test case: Runtime.awaitPromise.Reject.Boolean >+PASS: The rejected value should be true >+ >+-- Running test case: Runtime.awaitPromise.Reject.Number >+PASS: The rejected value should be 42 >+ >+-- Running test case: Runtime.awaitPromise.Reject.String >+PASS: The rejected value should be "foo" >+ >+-- Running test case: Runtime.awaitPromise.Reject.Array >+PASS: The rejected value should be [0,1] >+ >+-- Running test case: Runtime.awaitPromise.Reject.Object >+PASS: The rejected value should be {"a":1,"b":2} >+ >+-- Running test case: Runtime.awaitPromise.Reject.Chain >+PASS: The rejected value should be 3. >+ >+-- Running test case: Runtime.awaitPromise.Error.NonPromiseObjectId >+PASS: Should produce an error. >+Error: Error: Object with given id is not a Promise >+ >+-- Running test case: Runtime.awaitPromise.Error.InvalidPromiseObjectId >+PASS: Should produce an error. >+Error: Could not find InjectedScript for promiseObjectId >+ >diff --git a/LayoutTests/inspector/runtime/awaitPromise.html b/LayoutTests/inspector/runtime/awaitPromise.html >new file mode 100644 >index 0000000000000000000000000000000000000000..9d2fbf45c74e748d9d116d5a69c05b7ced0c2f43 >--- /dev/null >+++ b/LayoutTests/inspector/runtime/awaitPromise.html >@@ -0,0 +1,128 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/inspector-test.js"></script> >+<script src="resources/audit-utilities.js"></script> >+<script> >+if (window.internals) >+ window.internals.settings.setUnhandledPromiseRejectionToConsoleEnabled(false); >+ >+function test() >+{ >+ let savedResultCount = 0; >+ >+ let suite = InspectorTest.createAsyncSuite("Runtime.awaitPromise"); >+ >+ function addTest(name, expression, options = {}, callback) { >+ suite.addTestCase({ >+ name, >+ async test() { >+ let evaluateResponse = await RuntimeAgent.evaluate(expression); >+ InspectorTest.assert(evaluateResponse.result.type === "object"); >+ InspectorTest.assert(evaluateResponse.result.className === "Promise"); >+ >+ let awaitPromiseResponse = await RuntimeAgent.awaitPromise(evaluateResponse.result.objectId, options.returnByValue, options.generatePreview, options.saveResult); >+ >+ if (!awaitPromiseResponse.wasThrown && options.saveResult) >+ InspectorTest.assert(++savedResultCount === awaitPromiseResponse.savedResultIndex, "savedResultIndex should match."); >+ >+ await callback(WI.RemoteObject.fromPayload(awaitPromiseResponse.result), awaitPromiseResponse.wasThrown, awaitPromiseResponse.savedResultIndex); >+ }, >+ }); >+ } >+ >+ function addResolveTest(name, value, options = {}) { >+ let expression = `new Promise((resolve, reject) => setTimeout(resolve, 0, ${JSON.stringify(value)}))`; >+ addTest(name, expression, options, async (remoteObject, wasThrown) => { >+ InspectorTest.assert(!wasThrown, "There should be no error."); >+ if (options.returnByValue) { >+ if (value && typeof value === "object") >+ InspectorTest.expectShallowEqual(remoteObject.value, value, "The resolved value should be " + JSON.stringify(value)); >+ else >+ InspectorTest.expectEqual(remoteObject.value, value, "The resolved value should be " + JSON.stringify(value)); >+ } else { >+ InspectorTest.expectEqual(remoteObject.type, value.type, "The type should be " + value.type); >+ InspectorTest.expectEqual(remoteObject.subtype, value.subtype, "The subtype should be " + value.subtype); >+ InspectorTest.expectEqual(remoteObject.description, value.description, "The description should be " + value.description); >+ } >+ }); >+ } >+ >+ function addRejectTest(name, value, options = {}) { >+ let expression = `new Promise((resolve, reject) => setTimeout(reject, 0, ${JSON.stringify(value)}))`; >+ addTest(name, expression, options, async (remoteObject, wasThrown) => { >+ InspectorTest.assert(wasThrown, "There should be an error."); >+ if (value && typeof value === "object") { >+ let propertyDescriptors = await new Promise((resolve) => remoteObject.getPropertyDescriptorsAsObject(resolve)); >+ let properties = Array.isArray(value) ? [] : {}; >+ for (let key in value) >+ properties[key] = propertyDescriptors[key].value.value; >+ InspectorTest.expectShallowEqual(properties, value, "The rejected value should be " + JSON.stringify(value)); >+ } else >+ InspectorTest.expectEqual(remoteObject.value, value, "The rejected value should be " + JSON.stringify(value)); >+ }); >+ } >+ >+ addResolveTest("Runtime.awaitPromise.Resolve.Undefined", undefined, {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.Null", null, {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.Boolean", true, {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.Number", 42, {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.String", "foo", {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.Array", [0, 1], {returnByValue: true, saveResult: true}); >+ addResolveTest("Runtime.awaitPromise.Resolve.Object", {a: 1, b: 2}, {returnByValue: true, saveResult: true}); >+ >+ addTest("Runtime.awaitPromise.Resolve.Chain", `Promise.resolve(1).then(() => 2).then(() => 3)`, {returnByValue: true, saveResult: true}, async (remoteObject, wasThrown) => { >+ InspectorTest.assert(!wasThrown, "There should be no error."); >+ InspectorTest.expectEqual(remoteObject.value, 3, "The resolved value should be 3."); >+ }); >+ >+ addRejectTest("Runtime.awaitPromise.Reject.Undefined", undefined); >+ addRejectTest("Runtime.awaitPromise.Reject.Null", null); >+ addRejectTest("Runtime.awaitPromise.Reject.Boolean", true); >+ addRejectTest("Runtime.awaitPromise.Reject.Number", 42); >+ addRejectTest("Runtime.awaitPromise.Reject.String", "foo"); >+ addRejectTest("Runtime.awaitPromise.Reject.Array", [0, 1]); >+ addRejectTest("Runtime.awaitPromise.Reject.Object", {a: 1, b: 2}); >+ >+ addTest("Runtime.awaitPromise.Reject.Chain", `Promise.reject(1).catch(() => Promise.reject(2)).catch(() => Promise.reject(3))`, {}, async (remoteObject, wasThrown) => { >+ InspectorTest.assert(wasThrown, "There should be an error."); >+ InspectorTest.expectEqual(remoteObject.value, 3, "The rejected value should be 3."); >+ }); >+ >+ suite.addTestCase({ >+ name: "Runtime.awaitPromise.Error.NonPromiseObjectId", >+ test(resolve, reject) { >+ RuntimeAgent.evaluate("window") >+ .then((response) => RuntimeAgent.awaitPromise(response.result.objectId)) >+ .then((response) => { >+ InspectorTest.fail("Should not be able to call awaitPromise for a non-Promise object."); >+ resolve(); >+ }) >+ .catch((error) => { >+ InspectorTest.expectThat(error, "Should produce an error."); >+ InspectorTest.log("Error: " + error); >+ resolve(); >+ }); >+ }, >+ }); >+ >+ suite.addTestCase({ >+ name: "Runtime.awaitPromise.Error.InvalidPromiseObjectId", >+ test(resolve, reject) { >+ const promiseObjectId = "DOES_NOT_EXIST"; >+ RuntimeAgent.awaitPromise(promiseObjectId, (error) => { >+ InspectorTest.expectThat(error, "Should produce an error."); >+ InspectorTest.log("Error: " + error); >+ resolve(); >+ }); >+ }, >+ }); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+ <p>Tests functionality of Runtime.awaitPromise.</p> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 192171
:
356034
|
356045
|
356051
|
356056
|
356073
|
356077
|
356096
|
356160
|
356164
|
356166
|
356183
|
356200
|
356218
|
356220
|
356228
|
356229
|
356286
|
356453
|
356457
|
356470
| 356471