<!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>[199680] trunk/Source/WebKit2</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/199680">199680</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2016-04-18 10:46:01 -0700 (Mon, 18 Apr 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Automation: provide detailed error messages when an automation command fails
https://bugs.webkit.org/show_bug.cgi?id=156635
&lt;rdar://problem/25754051&gt;

Reviewed by Darin Adler and Timothy Hatcher.

Fill in lots of missing error message details now that the remote end knows
how to parse error strings with predefined error names and details.

* UIProcess/Automation/WebAutomationSession.cpp:
Rearrange the error message macros. Make it possible to pass a ErrorMessage
variable or just the enum value name and get out an encoded error message
that optionally includes a free-form details string. The predefined error
name and the details string are joined together with a semicolon.

(WebKit::WebAutomationSession::getBrowsingContext):
(WebKit::WebAutomationSession::createBrowsingContext):
(WebKit::WebAutomationSession::closeBrowsingContext):
(WebKit::WebAutomationSession::switchToBrowsingContext):
(WebKit::WebAutomationSession::resizeWindowOfBrowsingContext):
(WebKit::WebAutomationSession::moveWindowOfBrowsingContext):
(WebKit::WebAutomationSession::navigateBrowsingContext):
(WebKit::WebAutomationSession::goBackInBrowsingContext):
(WebKit::WebAutomationSession::goForwardInBrowsingContext):
(WebKit::WebAutomationSession::reloadBrowsingContext):
(WebKit::WebAutomationSession::evaluateJavaScriptFunction):
(WebKit::WebAutomationSession::didEvaluateJavaScriptFunction):
(WebKit::WebAutomationSession::resolveChildFrameHandle):
(WebKit::WebAutomationSession::didResolveChildFrame):
(WebKit::WebAutomationSession::resolveParentFrameHandle):
(WebKit::WebAutomationSession::didResolveParentFrame):
(WebKit::WebAutomationSession::computeElementLayout):
(WebKit::WebAutomationSession::didComputeElementLayout):
(WebKit::WebAutomationSession::isShowingJavaScriptDialog):
(WebKit::WebAutomationSession::dismissCurrentJavaScriptDialog):
(WebKit::WebAutomationSession::acceptCurrentJavaScriptDialog):
(WebKit::WebAutomationSession::messageOfCurrentJavaScriptDialog):
(WebKit::WebAutomationSession::setUserInputForCurrentJavaScriptPrompt):
(WebKit::WebAutomationSession::getAllCookies):
(WebKit::WebAutomationSession::didGetCookiesForFrame):
(WebKit::WebAutomationSession::deleteSingleCookie):
(WebKit::WebAutomationSession::didDeleteCookie):
(WebKit::WebAutomationSession::addSingleCookie):
(WebKit::WebAutomationSession::deleteAllCookies):
(WebKit::WebAutomationSession::performMouseInteraction):
(WebKit::WebAutomationSession::performKeyboardInteractions):
(WebKit::WebAutomationSession::takeScreenshot):
(WebKit::WebAutomationSession::didTakeScreenshot):
Audit almost all early exits and provide a relevant error details message
if the error itself is ambiguous. Make sure to format asynchronous errors.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAutomationWebAutomationSessioncpp">trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (199679 => 199680)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2016-04-18 17:30:36 UTC (rev 199679)
+++ trunk/Source/WebKit2/ChangeLog        2016-04-18 17:46:01 UTC (rev 199680)
</span><span class="lines">@@ -1,3 +1,56 @@
</span><ins>+2016-04-18  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Automation: provide detailed error messages when an automation command fails
+        https://bugs.webkit.org/show_bug.cgi?id=156635
+        &lt;rdar://problem/25754051&gt;
+
+        Reviewed by Darin Adler and Timothy Hatcher.
+
+        Fill in lots of missing error message details now that the remote end knows
+        how to parse error strings with predefined error names and details.
+
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        Rearrange the error message macros. Make it possible to pass a ErrorMessage
+        variable or just the enum value name and get out an encoded error message
+        that optionally includes a free-form details string. The predefined error
+        name and the details string are joined together with a semicolon.
+
+        (WebKit::WebAutomationSession::getBrowsingContext):
+        (WebKit::WebAutomationSession::createBrowsingContext):
+        (WebKit::WebAutomationSession::closeBrowsingContext):
+        (WebKit::WebAutomationSession::switchToBrowsingContext):
+        (WebKit::WebAutomationSession::resizeWindowOfBrowsingContext):
+        (WebKit::WebAutomationSession::moveWindowOfBrowsingContext):
+        (WebKit::WebAutomationSession::navigateBrowsingContext):
+        (WebKit::WebAutomationSession::goBackInBrowsingContext):
+        (WebKit::WebAutomationSession::goForwardInBrowsingContext):
+        (WebKit::WebAutomationSession::reloadBrowsingContext):
+        (WebKit::WebAutomationSession::evaluateJavaScriptFunction):
+        (WebKit::WebAutomationSession::didEvaluateJavaScriptFunction):
+        (WebKit::WebAutomationSession::resolveChildFrameHandle):
+        (WebKit::WebAutomationSession::didResolveChildFrame):
+        (WebKit::WebAutomationSession::resolveParentFrameHandle):
+        (WebKit::WebAutomationSession::didResolveParentFrame):
+        (WebKit::WebAutomationSession::computeElementLayout):
+        (WebKit::WebAutomationSession::didComputeElementLayout):
+        (WebKit::WebAutomationSession::isShowingJavaScriptDialog):
+        (WebKit::WebAutomationSession::dismissCurrentJavaScriptDialog):
+        (WebKit::WebAutomationSession::acceptCurrentJavaScriptDialog):
+        (WebKit::WebAutomationSession::messageOfCurrentJavaScriptDialog):
+        (WebKit::WebAutomationSession::setUserInputForCurrentJavaScriptPrompt):
+        (WebKit::WebAutomationSession::getAllCookies):
+        (WebKit::WebAutomationSession::didGetCookiesForFrame):
+        (WebKit::WebAutomationSession::deleteSingleCookie):
+        (WebKit::WebAutomationSession::didDeleteCookie):
+        (WebKit::WebAutomationSession::addSingleCookie):
+        (WebKit::WebAutomationSession::deleteAllCookies):
+        (WebKit::WebAutomationSession::performMouseInteraction):
+        (WebKit::WebAutomationSession::performKeyboardInteractions):
+        (WebKit::WebAutomationSession::takeScreenshot):
+        (WebKit::WebAutomationSession::didTakeScreenshot):
+        Audit almost all early exits and provide a relevant error details message
+        if the error itself is ambiguous. Make sure to format asynchronous errors.
+
</ins><span class="cx"> 2016-04-18  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         CSP: Remove stubs for dynamically-added favicons (via link rel=&quot;icon&quot;)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAutomationWebAutomationSessioncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp (199679 => 199680)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp        2016-04-18 17:30:36 UTC (rev 199679)
+++ trunk/Source/WebKit2/UIProcess/Automation/WebAutomationSession.cpp        2016-04-18 17:46:01 UTC (rev 199680)
</span><span class="lines">@@ -38,16 +38,36 @@
</span><span class="cx"> #include &lt;WebCore/UUID.h&gt;
</span><span class="cx"> #include &lt;algorithm&gt;
</span><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><ins>+#include &lt;wtf/text/StringConcatenate.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> using namespace Inspector;
</span><span class="cx"> 
</span><del>-#define FAIL_WITH_PREDEFINED_ERROR_MESSAGE(messageName) \
</del><ins>+static const char* const errorNameAndDetailsSeparator = &quot;;&quot;;
+
+// Make sure the predefined error name is valid, otherwise use InternalError.
+#define VALIDATED_ERROR_MESSAGE(errorString) Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::ErrorMessage&gt;(errorString).valueOr(Inspector::Protocol::Automation::ErrorMessage::InternalError)
+
+// If the error name is incorrect for these macros, it will be a compile-time error.
+#define STRING_FOR_PREDEFINED_ERROR_NAME(errorName) Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::errorName)
+#define STRING_FOR_PREDEFINED_ERROR_NAME_AND_DETAILS(errorName, detailsString) makeString(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::errorName), errorNameAndDetailsSeparator, detailsString)
+
+// If the error message is not a predefined error, InternalError will be used instead.
+#define STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorMessage) Inspector::Protocol::AutomationHelpers::getEnumConstantValue(VALIDATED_ERROR_MESSAGE(errorMessage))
+#define STRING_FOR_PREDEFINED_ERROR_MESSAGE_AND_DETAILS(errorMessage, detailsString) makeString(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(VALIDATED_ERROR_MESSAGE(errorMessage)), errorNameAndDetailsSeparator, detailsString)
+
+// Convenience macros for filling in the error string of synchronous commands in bailout branches.
+#define FAIL_WITH_PREDEFINED_ERROR(errorName) \
</ins><span class="cx"> do { \
</span><del>-    auto enumValue = Inspector::Protocol::Automation::ErrorMessage::messageName; \
-    errorString = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(enumValue); \
</del><ins>+    errorString = STRING_FOR_PREDEFINED_ERROR_NAME(errorName); \
</ins><span class="cx">     return; \
</span><span class="cx"> } while (false)
</span><span class="cx"> 
</span><ins>+#define FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(errorName, detailsString) \
+do { \
+    errorString = STRING_FOR_PREDEFINED_ERROR_NAME_AND_DETAILS(errorName, detailsString); \
+    return; \
+} while (false)
+
</ins><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><span class="cx"> WebAutomationSession::WebAutomationSession()
</span><span class="lines">@@ -237,7 +257,7 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     context = buildBrowsingContextForPage(*page);
</span><span class="cx"> }
</span><span class="lines">@@ -246,11 +266,11 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, &quot;The remote session could not request a new browsing context.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = m_client-&gt;didRequestNewWindow(this);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, &quot;The remote session failed to create a new browsing context.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     m_activeBrowsingContextHandle = *handle = handleForWebPageProxy(*page);
</span><span class="cx"> }
</span><span class="lines">@@ -259,7 +279,7 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (handle == m_activeBrowsingContextHandle)
</span><span class="cx">         m_activeBrowsingContextHandle = emptyString();
</span><span class="lines">@@ -271,11 +291,11 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* frame = webFrameProxyForHandle(optionalFrameHandle ? *optionalFrameHandle : emptyString(), *page);
</span><span class="cx">     if (!frame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(FrameNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     // FIXME: We don't need to track this in WK2. Remove in a follow up.
</span><span class="cx">     m_activeBrowsingContextHandle = browsingContextHandle;
</span><span class="lines">@@ -286,86 +306,86 @@
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::resizeWindowOfBrowsingContext(Inspector::ErrorString&amp; errorString, const String&amp; handle, const Inspector::InspectorObject&amp; sizeObject)
</span><span class="cx"> {
</span><del>-    // FIXME &lt;rdar://problem/25094106&gt;: Specify what parameter was missing or invalid and how.
-    // This requires some changes to the other end's error handling. Right now it looks for an
-    // exact error message match. We could stuff this into the 'data' field on error object.
</del><span class="cx">     float width;
</span><span class="cx">     if (!sizeObject.getDouble(WTF::ASCIILiteral(&quot;width&quot;), width))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The 'width' parameter was not found or invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     float height;
</span><span class="cx">     if (!sizeObject.getDouble(WTF::ASCIILiteral(&quot;height&quot;), height))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The 'height' parameter was not found or invalid.&quot;);
</ins><span class="cx"> 
</span><del>-    if (width &lt; 0 || height &lt; 0)
-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+    if (width &lt; 0)
+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The 'width' parameter had an invalid value.&quot;);
</ins><span class="cx"> 
</span><ins>+    if (height &lt; 0)
+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The 'height' parameter had an invalid value.&quot;);
+
</ins><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::FloatRect originalFrame;
</span><span class="cx">     page-&gt;getWindowFrame(originalFrame);
</span><del>-    
</del><ins>+
</ins><span class="cx">     WebCore::FloatRect newFrame = WebCore::FloatRect(originalFrame.location(), WebCore::FloatSize(width, height));
</span><span class="cx">     if (newFrame == originalFrame)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     page-&gt;setWindowFrame(newFrame);
</span><del>-    
</del><ins>+
</ins><span class="cx">     // If nothing changed at all, it's probably fair to report that something went wrong.
</span><span class="cx">     // (We can't assume that the requested frame size will be honored exactly, however.)
</span><span class="cx">     WebCore::FloatRect updatedFrame;
</span><span class="cx">     page-&gt;getWindowFrame(updatedFrame);
</span><span class="cx">     if (originalFrame == updatedFrame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, &quot;The window size was expected to have changed, but did not.&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::moveWindowOfBrowsingContext(Inspector::ErrorString&amp; errorString, const String&amp; handle, const Inspector::InspectorObject&amp; positionObject)
</span><span class="cx"> {
</span><del>-    // FIXME &lt;rdar://problem/25094106&gt;: Specify what parameter was missing or invalid and how.
-    // This requires some changes to the other end's error handling. Right now it looks for an
-    // exact error message match. We could stuff this into the 'data' field on error object.
</del><span class="cx">     float x;
</span><span class="cx">     if (!positionObject.getDouble(WTF::ASCIILiteral(&quot;x&quot;), x))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The 'x' parameter was not found or invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     float y;
</span><span class="cx">     if (!positionObject.getDouble(WTF::ASCIILiteral(&quot;y&quot;), y))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The 'y' parameter was not found or invalid.&quot;);
</ins><span class="cx"> 
</span><del>-    if (x &lt; 0 || y &lt; 0)
-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+    if (x &lt; 0)
+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The 'x' parameter had an invalid value.&quot;);
</ins><span class="cx"> 
</span><ins>+    if (y &lt; 0)
+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The 'y' parameter had an invalid value.&quot;);
+
</ins><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::FloatRect originalFrame;
</span><span class="cx">     page-&gt;getWindowFrame(originalFrame);
</span><del>-    
</del><ins>+
</ins><span class="cx">     WebCore::FloatRect newFrame = WebCore::FloatRect(WebCore::FloatPoint(x, y), originalFrame.size());
</span><span class="cx">     if (newFrame == originalFrame)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     page-&gt;setWindowFrame(newFrame);
</span><del>-    
</del><ins>+
</ins><span class="cx">     // If nothing changed at all, it's probably fair to report that something went wrong.
</span><span class="cx">     // (We can't assume that the requested frame size will be honored exactly, however.)
</span><span class="cx">     WebCore::FloatRect updatedFrame;
</span><span class="cx">     page-&gt;getWindowFrame(updatedFrame);
</span><span class="cx">     if (originalFrame == updatedFrame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, &quot;The window position was expected to have changed, but did not.&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::navigateBrowsingContext(Inspector::ErrorString&amp; errorString, const String&amp; handle, const String&amp; url, Ref&lt;NavigateBrowsingContextCallback&gt;&amp;&amp; callback)
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page-&gt;pageID()))
</span><del>-        callback-&gt;sendFailure(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::Timeout));
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
</ins><span class="cx">     m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page-&gt;pageID(), WTFMove(callback));
</span><span class="cx"> 
</span><span class="cx">     page-&gt;loadRequest(WebCore::URL(WebCore::URL(), url));
</span><span class="lines">@@ -375,10 +395,10 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page-&gt;pageID()))
</span><del>-        callback-&gt;sendFailure(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::Timeout));
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
</ins><span class="cx">     m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page-&gt;pageID(), WTFMove(callback));
</span><span class="cx"> 
</span><span class="cx">     page-&gt;goBack();
</span><span class="lines">@@ -388,10 +408,10 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page-&gt;pageID()))
</span><del>-        callback-&gt;sendFailure(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::Timeout));
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
</ins><span class="cx">     m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page-&gt;pageID(), WTFMove(callback));
</span><span class="cx"> 
</span><span class="cx">     page-&gt;goForward();
</span><span class="lines">@@ -401,10 +421,10 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (auto callback = m_pendingNavigationInBrowsingContextCallbacksPerPage.take(page-&gt;pageID()))
</span><del>-        callback-&gt;sendFailure(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::Timeout));
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
</ins><span class="cx">     m_pendingNavigationInBrowsingContextCallbacksPerPage.set(page-&gt;pageID(), WTFMove(callback));
</span><span class="cx"> 
</span><span class="cx">     const bool reloadFromOrigin = false;
</span><span class="lines">@@ -422,11 +442,11 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* frame = webFrameProxyForHandle(optionalFrameHandle ? *optionalFrameHandle : emptyString(), *page);
</span><span class="cx">     if (!frame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(FrameNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     Vector&lt;String&gt; argumentsVector;
</span><span class="cx">     argumentsVector.reserveCapacity(arguments.length());
</span><span class="lines">@@ -452,25 +472,24 @@
</span><span class="cx">     if (!callback)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    if (!errorType.isEmpty()) {
-        // FIXME: We should send both the errorType and result, since result has the specific exception message.
-        callback-&gt;sendFailure(errorType);
-    } else
</del><ins>+    if (!errorType.isEmpty())
+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE_AND_DETAILS(errorType, result));
+    else
</ins><span class="cx">         callback-&gt;sendSuccess(result);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebAutomationSession::resolveChildFrameHandle(Inspector::ErrorString&amp; errorString, const String&amp; browsingContextHandle, const String* optionalFrameHandle, const int* optionalOrdinal, const String* optionalName, const String* optionalNodeHandle, Ref&lt;ResolveChildFrameHandleCallback&gt;&amp;&amp; callback)
</span><span class="cx"> {
</span><span class="cx">     if (!optionalOrdinal &amp;&amp; !optionalName &amp;&amp; !optionalNodeHandle)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;Command must specify a child frame by ordinal, name, or element handle.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* frame = webFrameProxyForHandle(optionalFrameHandle ? *optionalFrameHandle : emptyString(), *page);
</span><span class="cx">     if (!frame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(FrameNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextResolveFrameCallbackID++;
</span><span class="cx">     m_resolveChildFrameHandleCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -500,7 +519,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty())
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">     else
</span><span class="cx">         callback-&gt;sendSuccess(handleForWebFrameID(frameID));
</span><span class="cx"> }
</span><span class="lines">@@ -509,11 +528,11 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* frame = webFrameProxyForHandle(frameHandle, *page);
</span><span class="cx">     if (!frame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(FrameNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextResolveParentFrameCallbackID++;
</span><span class="cx">     m_resolveParentFrameHandleCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -528,7 +547,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty())
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">     else
</span><span class="cx">         callback-&gt;sendSuccess(handleForWebFrameID(frameID));
</span><span class="cx"> }
</span><span class="lines">@@ -537,11 +556,11 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* frame = webFrameProxyForHandle(frameHandle, *page);
</span><span class="cx">     if (!frame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(FrameNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextComputeElementLayoutCallbackID++;
</span><span class="cx">     m_computeElementLayoutCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -559,7 +578,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty()) {
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -585,11 +604,11 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(InternalError);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     *result = m_client-&gt;isShowingJavaScriptDialogOnPage(this, page);
</span><span class="cx"> }
</span><span class="lines">@@ -598,14 +617,14 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(InternalError);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_client-&gt;isShowingJavaScriptDialogOnPage(this, page))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NoJavaScriptDialog);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(NoJavaScriptDialog);
</ins><span class="cx"> 
</span><span class="cx">     m_client-&gt;dismissCurrentJavaScriptDialogOnPage(this, page);
</span><span class="cx"> }
</span><span class="lines">@@ -614,14 +633,14 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(InternalError);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_client-&gt;isShowingJavaScriptDialogOnPage(this, page))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NoJavaScriptDialog);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(NoJavaScriptDialog);
</ins><span class="cx"> 
</span><span class="cx">     m_client-&gt;acceptCurrentJavaScriptDialogOnPage(this, page);
</span><span class="cx"> }
</span><span class="lines">@@ -630,14 +649,14 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(InternalError);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_client-&gt;isShowingJavaScriptDialogOnPage(this, page))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NoJavaScriptDialog);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(NoJavaScriptDialog);
</ins><span class="cx"> 
</span><span class="cx">     *text = m_client-&gt;messageOfCurrentJavaScriptDialogOnPage(this, page);
</span><span class="cx"> }
</span><span class="lines">@@ -646,14 +665,14 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_client);
</span><span class="cx">     if (!m_client)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InternalError);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(InternalError);
</ins><span class="cx"> 
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (!m_client-&gt;isShowingJavaScriptDialogOnPage(this, page))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NoJavaScriptDialog);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(NoJavaScriptDialog);
</ins><span class="cx"> 
</span><span class="cx">     m_client-&gt;setUserInputForCurrentJavaScriptPromptOnPage(this, page, promptValue);
</span><span class="cx"> }
</span><span class="lines">@@ -662,12 +681,12 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* mainFrame = page-&gt;mainFrame();
</span><span class="cx">     ASSERT(mainFrame);
</span><span class="cx">     if (!mainFrame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextGetCookiesCallbackID++;
</span><span class="cx">     m_getCookieCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -707,7 +726,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty()) {
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -718,12 +737,12 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebFrameProxy* mainFrame = page-&gt;mainFrame();
</span><span class="cx">     ASSERT(mainFrame);
</span><span class="cx">     if (!mainFrame)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(FrameNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextDeleteCookieCallbackID++;
</span><span class="cx">     m_deleteCookieCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -738,7 +757,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty()) {
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -749,7 +768,7 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::URL activeURL = WebCore::URL(WebCore::URL(), page-&gt;pageLoadState().activeURL());
</span><span class="cx">     ASSERT(activeURL.isValid());
</span><span class="lines">@@ -757,14 +776,14 @@
</span><span class="cx">     WebCore::Cookie cookie;
</span><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getString(WTF::ASCIILiteral(&quot;name&quot;), cookie.name))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'name' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getString(WTF::ASCIILiteral(&quot;value&quot;), cookie.value))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'value' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     String domain;
</span><span class="cx">     if (!cookieObject.getString(WTF::ASCIILiteral(&quot;domain&quot;), domain))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'domain' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     // Inherit the domain/host from the main frame's URL if it is not explicitly set.
</span><span class="cx">     if (domain.isEmpty())
</span><span class="lines">@@ -773,22 +792,22 @@
</span><span class="cx">     cookie.domain = domain;
</span><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getString(WTF::ASCIILiteral(&quot;path&quot;), cookie.path))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'path' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     double expires;
</span><span class="cx">     if (!cookieObject.getDouble(WTF::ASCIILiteral(&quot;expires&quot;), expires))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'expires' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     cookie.expires = expires * 1000.0;
</span><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getBoolean(WTF::ASCIILiteral(&quot;secure&quot;), cookie.secure))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'secure' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getBoolean(WTF::ASCIILiteral(&quot;session&quot;), cookie.session))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'session' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     if (!cookieObject.getBoolean(WTF::ASCIILiteral(&quot;httpOnly&quot;), cookie.httpOnly))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'httpOnly' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     WebCookieManagerProxy* cookieManager = m_processPool-&gt;supplement&lt;WebCookieManagerProxy&gt;();
</span><span class="cx">     cookieManager-&gt;addCookie(cookie, activeURL.host());
</span><span class="lines">@@ -800,7 +819,7 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(browsingContextHandle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::URL activeURL = WebCore::URL(WebCore::URL(), page-&gt;pageLoadState().activeURL());
</span><span class="cx">     ASSERT(activeURL.isValid());
</span><span class="lines">@@ -830,22 +849,19 @@
</span><span class="cx"> void WebAutomationSession::performMouseInteraction(Inspector::ErrorString&amp; errorString, const String&amp; handle, const Inspector::InspectorObject&amp; requestedPositionObject, const String&amp; mouseButtonString, const String&amp; mouseInteractionString, const Inspector::InspectorArray&amp; keyModifierStrings, RefPtr&lt;Inspector::Protocol::Automation::Point&gt;&amp; updatedPositionObject)
</span><span class="cx"> {
</span><span class="cx"> #if !USE(APPKIT)
</span><del>-    FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NotImplemented);
</del><ins>+    FAIL_WITH_PREDEFINED_ERROR(NotImplemented);
</ins><span class="cx"> #else
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><del>-    // FIXME &lt;rdar://problem/25094106&gt;: Specify what parameter was missing or invalid and how.
-    // This requires some changes to the other end's error handling. Right now it looks for an
-    // exact error message match. We could stuff this into the 'data' field on error object.
</del><span class="cx">     float x;
</span><span class="cx">     if (!requestedPositionObject.getDouble(WTF::ASCIILiteral(&quot;x&quot;), x))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'x' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     float y;
</span><span class="cx">     if (!requestedPositionObject.getDouble(WTF::ASCIILiteral(&quot;y&quot;), y))
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;The parameter 'y' was not found.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     WebCore::FloatRect windowFrame;
</span><span class="cx">     page-&gt;getWindowFrame(windowFrame);
</span><span class="lines">@@ -857,21 +873,21 @@
</span><span class="cx"> 
</span><span class="cx">     auto parsedInteraction = Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::MouseInteraction&gt;(mouseInteractionString);
</span><span class="cx">     if (!parsedInteraction)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The parameter 'interaction' is invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     auto parsedButton = Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::MouseButton&gt;(mouseButtonString);
</span><span class="cx">     if (!parsedButton)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The parameter 'button' is invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     WebEvent::Modifiers keyModifiers = (WebEvent::Modifiers)0;
</span><span class="cx">     for (auto it = keyModifierStrings.begin(); it != keyModifierStrings.end(); ++it) {
</span><span class="cx">         String modifierString;
</span><span class="cx">         if (!it-&gt;get()-&gt;asString(modifierString))
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The parameter 'modifiers' is invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">         auto parsedModifier = Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::KeyModifier&gt;(modifierString);
</span><span class="cx">         if (!parsedModifier)
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;A modifier in the 'modifiers' array is invalid.&quot;);
</ins><span class="cx">         WebEvent::Modifiers enumValue = protocolModifierToWebEventModifier(parsedModifier.value());
</span><span class="cx">         keyModifiers = (WebEvent::Modifiers)(enumValue | keyModifiers);
</span><span class="cx">     }
</span><span class="lines">@@ -888,14 +904,14 @@
</span><span class="cx"> void WebAutomationSession::performKeyboardInteractions(ErrorString&amp; errorString, const String&amp; handle, const Inspector::InspectorArray&amp; interactions)
</span><span class="cx"> {
</span><span class="cx"> #if !USE(APPKIT)
</span><del>-    FAIL_WITH_PREDEFINED_ERROR_MESSAGE(NotImplemented);
</del><ins>+    FAIL_WITH_PREDEFINED_ERROR(NotImplemented);
</ins><span class="cx"> #else
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     if (!interactions.length())
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;The parameter 'interactions' was not found or empty.&quot;);
</ins><span class="cx"> 
</span><span class="cx">     // Validate all of the parameters before performing any interactions with the browsing context under test.
</span><span class="cx">     Vector&lt;std::function&lt;void()&gt;&gt; actionsToPerform;
</span><span class="lines">@@ -904,21 +920,21 @@
</span><span class="cx">     for (auto interaction : interactions) {
</span><span class="cx">         RefPtr&lt;InspectorObject&gt; interactionObject;
</span><span class="cx">         if (!interaction-&gt;asObject(interactionObject))
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;An interaction in the 'interactions' parameter was invalid.&quot;);
</ins><span class="cx"> 
</span><span class="cx">         String interactionTypeString;
</span><span class="cx">         if (!interactionObject-&gt;getString(ASCIILiteral(&quot;type&quot;), interactionTypeString))
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;An interaction in the 'interactions' parameter is missing the 'type' key.&quot;);
</ins><span class="cx">         auto interactionType = Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::KeyboardInteractionType&gt;(interactionTypeString);
</span><span class="cx">         if (!interactionType)
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;An interaction in the 'interactions' parameter has an invalid 'type' key.&quot;);
</ins><span class="cx"> 
</span><span class="cx">         String virtualKeyString;
</span><span class="cx">         bool foundVirtualKey = interactionObject-&gt;getString(ASCIILiteral(&quot;key&quot;), virtualKeyString);
</span><span class="cx">         if (foundVirtualKey) {
</span><span class="cx">             auto virtualKey = Inspector::Protocol::AutomationHelpers::parseEnumValueFromString&lt;Inspector::Protocol::Automation::VirtualKey&gt;(virtualKeyString);
</span><span class="cx">             if (!virtualKey)
</span><del>-                FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+                FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;An interaction in the 'interactions' parameter has an invalid 'key' value.&quot;);
</ins><span class="cx"> 
</span><span class="cx">             actionsToPerform.uncheckedAppend([this, page, interactionType, virtualKey] {
</span><span class="cx">                 platformSimulateKeyStroke(*page, interactionType.value(), virtualKey.value());
</span><span class="lines">@@ -932,7 +948,7 @@
</span><span class="cx">             case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress:
</span><span class="cx">             case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease:
</span><span class="cx">                 // 'KeyPress' and 'KeyRelease' are meant for a virtual key and are not supported for a string (sequence of codepoints).
</span><del>-                FAIL_WITH_PREDEFINED_ERROR_MESSAGE(InvalidParameter);
</del><ins>+                FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, &quot;An interaction in the 'interactions' parameter has an invalid 'key' value.&quot;);
</ins><span class="cx"> 
</span><span class="cx">             case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey:
</span><span class="cx">                 actionsToPerform.uncheckedAppend([this, page, keySequence] {
</span><span class="lines">@@ -943,7 +959,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (!foundVirtualKey &amp;&amp; !foundKeySequence)
</span><del>-            FAIL_WITH_PREDEFINED_ERROR_MESSAGE(MissingParameter);
</del><ins>+            FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(MissingParameter, &quot;An interaction in the 'interactions' parameter is missing both 'key' and 'text'. One must be provided.&quot;);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ASSERT(actionsToPerform.size());
</span><span class="lines">@@ -957,7 +973,7 @@
</span><span class="cx"> {
</span><span class="cx">     WebPageProxy* page = webPageProxyForHandle(handle);
</span><span class="cx">     if (!page)
</span><del>-        FAIL_WITH_PREDEFINED_ERROR_MESSAGE(WindowNotFound);
</del><ins>+        FAIL_WITH_PREDEFINED_ERROR(WindowNotFound);
</ins><span class="cx"> 
</span><span class="cx">     uint64_t callbackID = m_nextScreenshotCallbackID++;
</span><span class="cx">     m_screenshotCallbacks.set(callbackID, WTFMove(callback));
</span><span class="lines">@@ -972,13 +988,13 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     if (!errorType.isEmpty()) {
</span><del>-        callback-&gt;sendFailure(errorType);
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorType));
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     String base64EncodedData = platformGetBase64EncodedPNGData(imageDataHandle);
</span><span class="cx">     if (base64EncodedData.isEmpty()) {
</span><del>-        callback-&gt;sendFailure(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InternalError));
</del><ins>+        callback-&gt;sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(InternalError));
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>