<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[193210] branches/safari-601-branch</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/193210">193210</a></dd>
<dt>Author</dt> <dd>timothy@apple.com</dd>
<dt>Date</dt> <dd>2015-12-03 10:57:59 -0800 (Thu, 03 Dec 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/189761">r189761</a>. rdar://problem/23221163</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#branchessafari601branchLayoutTestsChangeLog">branches/safari-601-branch/LayoutTests/ChangeLog</a></li>
<li><a href="#branchessafari601branchLayoutTestsinspectorprotocolinspectorbackendinvocationreturnvalueexpectedtxt">branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value-expected.txt</a></li>
<li><a href="#branchessafari601branchLayoutTestsinspectorprotocolinspectorbackendinvocationreturnvaluehtml">branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value.html</a></li>
<li><a href="#branchessafari601branchSourceWebInspectorUIChangeLog">branches/safari-601-branch/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#branchessafari601branchSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs">branches/safari-601-branch/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchessafari601branchLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-601-branch/LayoutTests/ChangeLog (193209 => 193210)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-601-branch/LayoutTests/ChangeLog        2015-12-03 18:57:52 UTC (rev 193209)
+++ branches/safari-601-branch/LayoutTests/ChangeLog        2015-12-03 18:57:59 UTC (rev 193210)
</span><span class="lines">@@ -1,5 +1,21 @@
</span><span class="cx"> 2015-12-02 Timothy Hatcher <timothy@apple.com>
</span><span class="cx">
</span><ins>+ Merge r189761. rdar://problem/23221163
+
+ 2015-09-14 Brian Burg <bburg@apple.com>
+
+ Web Inspector: backend command promises are not rejected when a protocol error occurs
+ https://bugs.webkit.org/show_bug.cgi?id=141403
+
+ Reviewed by Joseph Pecoraro.
+
+ Expand coverage of an existing protocol layer test to cover success and failure modes.
+
+ * inspector/protocol/inspector-backend-invocation-return-value-expected.txt:
+ * inspector/protocol/inspector-backend-invocation-return-value.html:
+
+2015-12-02 Timothy Hatcher <timothy@apple.com>
+
</ins><span class="cx"> Merge r189415. rdar://problem/23221163
</span><span class="cx">
</span><span class="cx"> 2015-09-04 Joseph Pecoraro <pecoraro@apple.com>
</span></span></pre></div>
<a id="branchessafari601branchLayoutTestsinspectorprotocolinspectorbackendinvocationreturnvalueexpectedtxt"></a>
<div class="modfile"><h4>Modified: branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value-expected.txt (193209 => 193210)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value-expected.txt        2015-12-03 18:57:52 UTC (rev 193209)
+++ branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value-expected.txt        2015-12-03 18:57:59 UTC (rev 193210)
</span><span class="lines">@@ -1,5 +1,41 @@
</span><span class="cx"> Testing the inspector backend's return values when invoking a protocol command in various ways.
</span><span class="cx">
</span><del>-PASS: RuntimeAgent.evaluate should not return a promise when invoked with a function as the last parameter.
-PASS: RuntimeAgent.evaluate should return a Promise when invoked without a callback.
</del><span class="cx">
</span><ins>+== Running test suite: Protocol.BackendInvocationReturnValues
+-- Running test case: ResolveCommandPromiseOnSuccess
+PASS: A backend command should return a Promise when invoked without a callback.
+PASS: A successful command invocation's promise should be resolved.
+
+-- Running test case: RejectCommandPromiseWithInvalidArguments
+ERROR: Protocol Error: Invalid type of argument 'expression' for command 'Runtime.evaluate' call. It must be 'string' but it is 'number'.
+PASS: A backend command should return a Promise when invoked without a callback.
+PASS: An invalid command invocation's promise should be rejected.
+
+-- Running test case: RejectCommandPromiseWithMissingArguments
+ERROR: Protocol Error: Invalid number of arguments for command 'Runtime.evaluate'.
+PASS: A backend command should return a Promise when invoked without a callback.
+PASS: An invalid command invocation's promise should be rejected.
+
+-- Running test case: ReturnNothingIfCallback
+PASS: A backend command should not have a return value when invoked with a callback.
+
+-- Running test case: InvokeCallbackWithResultOnSuccess
+PASS: A backend command should not return anything when invoked with a callback.
+PASS: A backend command should always invoke its callback asynchronously.
+PASS: A successful command should invoke the callback with a 'null' first parameter.
+PASS: A successful command should invoke the callback with one or more result parameters.
+
+-- Running test case: InvokeCallbackWithErrorForInvalidArguments
+ERROR: Protocol Error: Invalid type of argument 'expression' for command 'Runtime.evaluate' call. It must be 'string' but it is 'number'.
+PASS: A backend command should not return anything when invoked with a callback.
+PASS: A backend command should always invoke its callback asynchronously.
+PASS: A failed command should invoke the callback with a string error message as its first parameter.
+PASS: A failed command should invoke the callback with only an error parameter.
+
+-- Running test case: InvokeCallbackWithErrorForMissingArguments
+ERROR: Protocol Error: Invalid number of arguments for command 'Runtime.evaluate'.
+PASS: A backend command should not return anything when invoked with a callback.
+PASS: A backend command should always invoke its callback asynchronously.
+PASS: A failed command should invoke the callback with a string error message as its first parameter.
+PASS: A failed command should invoke the callback with only an error parameter.
+
</ins></span></pre></div>
<a id="branchessafari601branchLayoutTestsinspectorprotocolinspectorbackendinvocationreturnvaluehtml"></a>
<div class="modfile"><h4>Modified: branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value.html (193209 => 193210)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value.html        2015-12-03 18:57:52 UTC (rev 193209)
+++ branches/safari-601-branch/LayoutTests/inspector/protocol/inspector-backend-invocation-return-value.html        2015-12-03 18:57:59 UTC (rev 193210)
</span><span class="lines">@@ -5,14 +5,139 @@
</span><span class="cx"> <script>
</span><span class="cx"> function test()
</span><span class="cx"> {
</span><del>- var dummyCallback = function(){};
- var invokeCallbackResult = RuntimeAgent.evaluate("41", dummyCallback);
- InspectorTest.expectThat(!(invokeCallbackResult instanceof Promise), "RuntimeAgent.evaluate should not return a promise when invoked with a function as the last parameter.");
</del><ins>+ let suite = InspectorTest.createAsyncSuite("Protocol.BackendInvocationReturnValues");
+ let dummyCallback = () => {};
</ins><span class="cx">
</span><del>- var invokePromiseResult = RuntimeAgent.evaluate("42");
- InspectorTest.expectThat(invokePromiseResult instanceof Promise, "RuntimeAgent.evaluate should return a Promise when invoked without a callback.");
</del><ins>+ // Test behavior for promise-based callbacks.
</ins><span class="cx">
</span><del>- InspectorTest.completeTest();
</del><ins>+ suite.addTestCase({
+ name: "ResolveCommandPromiseOnSuccess",
+ description: "Backend command's returned promise should be resolved if the command is successful.",
+ test: (resolve, reject) => {
+ let returnValue = RuntimeAgent.evaluate("42");
+
+ InspectorTest.expectThat(returnValue instanceof Promise, "A backend command should return a Promise when invoked without a callback.");
+ // If a promise wasn't returned, we can't test the rest so just die.
+ if (!(returnValue instanceof Promise))
+ reject();
+
+ returnValue.then(function resolved(result) {
+ InspectorTest.log("PASS: A successful command invocation's promise should be resolved.");
+ resolve();
+ }, function rejected(result) {
+ InspectorTest.log("FAIL: A successful command invocation's promise should be resolved.");
+ reject();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "RejectCommandPromiseWithInvalidArguments",
+ description: "Backend command's returned promise should be rejected if the command has invalid arguments.",
+ test: (resolve, reject) => {
+ let result = RuntimeAgent.evaluate(42);
+
+ InspectorTest.expectThat(result instanceof Promise, "A backend command should return a Promise when invoked without a callback.");
+ // If a promise wasn't returned, we can't test the rest so just die.
+ if (!(result instanceof Promise))
+ reject();
+
+ result.then(function resolved(result) {
+ InspectorTest.log("FAIL: An invalid command invocation's promise should be rejected.");
+ reject();
+ }, function rejected(result) {
+ InspectorTest.log("PASS: An invalid command invocation's promise should be rejected.");
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "RejectCommandPromiseWithMissingArguments",
+ description: "Backend command's returned promise should be rejected if the command lacks required arguments.",
+ test: (resolve, reject) => {
+ let result = RuntimeAgent.evaluate();
+
+ InspectorTest.expectThat(result instanceof Promise, "A backend command should return a Promise when invoked without a callback.");
+ // If a promise wasn't returned, we can't test the rest so just die.
+ if (!(result instanceof Promise))
+ reject();
+
+ result.then(function resolved(result) {
+ InspectorTest.log("FAIL: An invalid command invocation's promise should be rejected.");
+ reject();
+ }, function rejected(result) {
+ InspectorTest.log("PASS: An invalid command invocation's promise should be rejected.");
+ resolve();
+ });
+ }
+ });
+
+ // Test behavior for function-based callbacks.
+
+ suite.addTestCase({
+ name: "ReturnNothingIfCallback",
+ description: "Backend commands should not return anything if a callback is supplied.",
+ test: (resolve, reject) => {
+ let returnValue = RuntimeAgent.evaluate("41", dummyCallback);
+ InspectorTest.expectThat(returnValue === undefined, "A backend command should not have a return value when invoked with a callback.");
+ resolve();
+ }
+ });
+
+ suite.addTestCase({
+ name: "InvokeCallbackWithResultOnSuccess",
+ description: "Backend command callback should be invoked with a result if the command is successful.",
+ test: (resolve, reject) => {
+ let initialState = 1;
+ let returnValue = RuntimeAgent.evaluate("42", function(error, result) {
+ InspectorTest.expectThat(error === null, "A successful command should invoke the callback with a 'null' first parameter.");
+ InspectorTest.expectThat(arguments.length > 1, "A successful command should invoke the callback with one or more result parameters.");
+ initialState++;
+ resolve();
+ });
+
+ InspectorTest.expectThat(returnValue === undefined, "A backend command should not return anything when invoked with a callback.");
+ InspectorTest.expectThat(initialState === 1, "A backend command should always invoke its callback asynchronously.");
+ }
+ });
+
+ suite.addTestCase({
+ name: "InvokeCallbackWithErrorForInvalidArguments",
+ description: "Backend command callback should be invoked with an error if the command has invalid arguments.",
+ test: (resolve, reject) => {
+ let initialState = 1;
+ let returnValue = RuntimeAgent.evaluate(42, function callback(error) {
+ InspectorTest.expectThat(typeof error === "string", "A failed command should invoke the callback with a string error message as its first parameter.");
+ InspectorTest.expectThat(arguments.length === 1, "A failed command should invoke the callback with only an error parameter.");
+ initialState++;
+ resolve();
+ });
+
+ InspectorTest.expectThat(returnValue === undefined, "A backend command should not return anything when invoked with a callback.");
+ InspectorTest.expectThat(initialState === 1, "A backend command should always invoke its callback asynchronously.");
+ }
+ });
+
+ suite.addTestCase({
+ name: "InvokeCallbackWithErrorForMissingArguments",
+ description: "Backend command callback should be invoked with an error if the command lacks required arguments.",
+ test: (resolve, reject) => {
+ let initialState = 1;
+ let returnValue = RuntimeAgent.evaluate(function callback(error) {
+ InspectorTest.expectThat(typeof error === "string", "A failed command should invoke the callback with a string error message as its first parameter.");
+ InspectorTest.expectThat(arguments.length === 1, "A failed command should invoke the callback with only an error parameter.");
+
+ initialState++;
+ resolve();
+ });
+
+ InspectorTest.expectThat(returnValue === undefined, "A backend command should not return anything when invoked with a callback.");
+ InspectorTest.expectThat(initialState === 1, "A backend command should always invoke its callback asynchronously.");
+ }
+ });
+
+ suite.runTestCasesAndFinish();
</ins><span class="cx"> }
</span><span class="cx"> </script>
</span><span class="cx"> </head>
</span></span></pre></div>
<a id="branchessafari601branchSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-601-branch/Source/WebInspectorUI/ChangeLog (193209 => 193210)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-601-branch/Source/WebInspectorUI/ChangeLog        2015-12-03 18:57:52 UTC (rev 193209)
+++ branches/safari-601-branch/Source/WebInspectorUI/ChangeLog        2015-12-03 18:57:59 UTC (rev 193210)
</span><span class="lines">@@ -1,5 +1,25 @@
</span><span class="cx"> 2015-12-02 Timothy Hatcher <timothy@apple.com>
</span><span class="cx">
</span><ins>+ Merge r189761. rdar://problem/23221163
+
+ 2015-09-14 Brian Burg <bburg@apple.com>
+
+ Web Inspector: backend command promises are not rejected when a protocol error occurs
+ https://bugs.webkit.org/show_bug.cgi?id=141403
+
+ Reviewed by Joseph Pecoraro.
+
+ Fix a few corner cases for how InspectorBackend delivers command failures.
+
+ * UserInterface/Protocol/InspectorBackend.js:
+ (InspectorBackend.Command.prototype.deliverFailure): Added.
+ (InspectorBackend.Command.prototype._invokeWithArguments):
+
+ If argument-checking fails, return a rejected promise or invoke the supplied callback
+ on a zero-delay setTimeout to ensure that the reply is asynchronous.
+
+2015-12-02 Timothy Hatcher <timothy@apple.com>
+
</ins><span class="cx"> Merge r189759. rdar://problem/23221163
</span><span class="cx">
</span><span class="cx"> 2015-09-14 Brian Burg <bburg@apple.com>
</span></span></pre></div>
<a id="branchessafari601branchSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs"></a>
<div class="modfile"><h4>Modified: branches/safari-601-branch/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js (193209 => 193210)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-601-branch/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2015-12-03 18:57:52 UTC (rev 193209)
+++ branches/safari-601-branch/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2015-12-03 18:57:59 UTC (rev 193210)
</span><span class="lines">@@ -475,35 +475,35 @@
</span><span class="cx"> var commandArguments = Array.from(arguments);
</span><span class="cx"> var callback = typeof commandArguments.lastValue === "function" ? commandArguments.pop() : null;
</span><span class="cx">
</span><ins>+ function deliverFailure(message) {
+ console.error(`Protocol Error: ${message}`);
+ if (callback)
+ setTimeout(callback.bind(null, message), 0);
+ else
+ return Promise.reject(new Error(message));
+ }
+
</ins><span class="cx"> var parameters = {};
</span><span class="cx"> for (var parameter of instance.callSignature) {
</span><span class="cx"> var parameterName = parameter["name"];
</span><span class="cx"> var typeName = parameter["type"];
</span><span class="cx"> var optionalFlag = parameter["optional"];
</span><span class="cx">
</span><del>- if (!commandArguments.length && !optionalFlag) {
- console.error("Protocol Error: Invalid number of arguments for method '" + instance.qualifiedName + "' call. It must have the following arguments '" + JSON.stringify(instance.callSignature) + "'.");
- return;
- }
</del><ins>+ if (!commandArguments.length && !optionalFlag)
+ return deliverFailure(`Invalid number of arguments for command '${instance.qualifiedName}'.`);
</ins><span class="cx">
</span><span class="cx"> var value = commandArguments.shift();
</span><span class="cx"> if (optionalFlag && value === undefined)
</span><span class="cx"> continue;
</span><span class="cx">
</span><del>- if (typeof value !== typeName) {
- console.error("Protocol Error: Invalid type of argument '" + parameterName + "' for method '" + instance.qualifiedName + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
- return;
- }
</del><ins>+ if (typeof value !== typeName)
+ return deliverFailure(`Invalid type of argument '${parameterName}' for command '${instance.qualifiedName}' call. It must be '${typeName}' but it is '${typeof value}'.`);
</ins><span class="cx">
</span><span class="cx"> parameters[parameterName] = value;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if (commandArguments.length === 1 && !callback) {
- if (commandArguments[0] !== undefined) {
- console.error("Protocol Error: Optional callback argument for method '" + instance.qualifiedName + "' call must be a function but its type is '" + typeof args[0] + "'.");
- return;
- }
- }
</del><ins>+ if (!callback && commandArguments.length === 1 && commandArguments[0] !== undefined)
+ return deliverFailure(`Protocol Error: Optional callback argument for command '${instance.qualifiedName}' call must be a function but its type is '${typeof args[0]}'.`);
</ins><span class="cx">
</span><span class="cx"> if (callback)
</span><span class="cx"> instance._backend._sendCommandToBackendWithCallback(instance, parameters, callback);
</span></span></pre>
</div>
</div>
</body>
</html>