<!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>[242992] trunk</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/242992">242992</a></dd>
<dt>Author</dt> <dd>drousso@apple.com</dd>
<dt>Date</dt> <dd>2019-03-15 01:12:21 -0700 (Fri, 15 Mar 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: provide a way to capture a screenshot of a node from within the page
https://bugs.webkit.org/show_bug.cgi?id=194279
<rdar://problem/10731573>

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

Add `console.screenshot` functionality, which displays a screenshot of a given object (if
able) within Web Inspector's Console tab. From there, it can be viewed and saved.

Currently, `console.screenshot` will
 - capture an image of a `Node` (if provided)
 - capture an image of the viewport if nothing is provided

* inspector/protocol/Console.json:
Add `Image` enum value to `ConsoleMessage` type.
* runtime/ConsoleTypes.h:
* inspector/ConsoleMessage.h:
* inspector/ConsoleMessage.cpp:
(Inspector::messageTypeValue):

* runtime/ConsoleClient.h:
* runtime/ConsoleObject.cpp:
(JSC::ConsoleObject::finishCreation):
(JSC::consoleProtoFuncScreenshot): Added.

* inspector/JSGlobalObjectConsoleClient.h:
* inspector/JSGlobalObjectConsoleClient.cpp:
(Inspector::JSGlobalObjectConsoleClient::screenshot): Added.

Source/WebCore:

Test: inspector/console/console-screenshot.html

Add `console.screenshot` functionality, which displays a screenshot of a given object (if
able) within Web Inspector's Console tab. From there, it can be viewed and saved.

Currently, `console.screenshot` will
 - capture an image of a `Node` (if provided)
 - capture an image of the viewport if nothing is provided

* page/PageConsoleClient.h:
* page/PageConsoleClient.cpp:
(WebCore::PageConsoleClient::addMessage):
(WebCore::PageConsoleClient::screenshot): Added.

* workers/WorkerConsoleClient.h:
* workers/WorkerConsoleClient.cpp:
(WebCore::WorkerConsoleClient::screenshot): Added.
* worklets/WorkletConsoleClient.h:
* worklets/WorkletConsoleClient.cpp:
(WebCore::WorkletConsoleClient::screenshot): Added.

* inspector/CommandLineAPIModuleSource.js:
(CommandLineAPIImpl.prototype.screenshot): Added.

* inspector/InspectorInstrumentation.h:

Source/WebInspectorUI:

Add `console.screenshot` functionality, which displays a screenshot of a given object (if
able) within Web Inspector's Console tab. From there, it can be viewed and saved.

Currently, `console.screenshot` will
 - capture an image of a `Node` (if provided)
 - capture an image of the viewport if nothing is provided

* UserInterface/Models/ConsoleMessage.js:
(WI.ConsoleMessage):
* UserInterface/Views/ConsoleCommandView.js:
(WI.ConsoleCommandView.prototype.render):
* UserInterface/Views/ConsoleMessageView.js:
(WI.ConsoleMessageView.prototype.render):
(WI.ConsoleMessageView.prototype.toClipboardString):
(WI.ConsoleMessageView.prototype._appendMessageTextAndArguments):
(WI.ConsoleMessageView.prototype._appendSavedResultIndex):
(WI.ConsoleMessageView.prototype._appendStackTrace):
(WI.ConsoleMessageView.prototype._makeExpandable):
(WI.ConsoleMessageView.prototype._handleContextMenu): Added.
* UserInterface/Views/ConsoleMessageView.css:
(.console-user-command.special-user-log > .console-message-body): Added.
(.console-message-body): Added.
(.console-message-body > span): Added.
(.console-message-body > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider)): Added.
(.console-message-body > .console-image): Added.
(.console-message-body > .show-grid): Added.
(.console-error-level .console-message-body): Added.
(.console-warning-level .console-message-body): Added.
(.console-log-level.console-image-container::before): Added.
(.console-user-command > .console-message-body): Added.
(.console-warning-level .console-message-body): Added.
(.console-error-level .console-message-body): Added.
(.console-user-command > .console-message-body): Added.
(.console-user-command.special-user-log > .console-message-text): Deleted.
(.console-message-text): Deleted.
(.console-message-text > span): Deleted.
(.console-message-text > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider)): Deleted.
(.console-error-level .console-message-text): Deleted.
(.console-warning-level .console-message-text): Deleted.
(.console-user-command > .console-message-text): Deleted.
(.console-warning-level .console-message-text): Deleted.
(.console-error-level .console-message-text): Deleted.
(.console-user-command > .console-message-text): Deleted.
* UserInterface/Views/LogContentView.css:
(.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-text .highlighted): Added.
(.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-body .highlighted): Deleted.
Renamed variables/classes to be more semantically correct when the content is an image.
 - `_messageTextElement` to `_messageBodyElement` (JS)
 - `.console-message-text` to `.console-message-body` (CSS)

* UserInterface/Controllers/JavaScriptLogViewController.js:
(WI.JavaScriptLogViewController.prototype.renderPendingMessages):

* UserInterface/Views/Main.css:
(:matches(img, canvas).show-grid):
(@media (prefers-color-scheme: dark) :matches(img, canvas).show-grid):

* UserInterface/Base/FileUtilities.js:
(WI.FileUtilities.screenshotString): Added.

* UserInterface/Models/NativeFunctionParameters.js:
* UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js:

* UserInterface/Images/ConsoleImage.svg: Copied from UserInterface/Images/Canvas.svg.
* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* js/console.html:
* js/console-expected.txt:
* inspector/console/console-screenshot.html: Added.
* inspector/console/console-screenshot-expected.txt: Added.
* http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsinspectordomcrossdomaininspectednodeaccessexpectedtxt">trunk/LayoutTests/http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsconsoleexpectedtxt">trunk/LayoutTests/js/console-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsconsolehtml">trunk/LayoutTests/js/console.html</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorConsoleMessagecpp">trunk/Source/JavaScriptCore/inspector/ConsoleMessage.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorConsoleMessageh">trunk/Source/JavaScriptCore/inspector/ConsoleMessage.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorJSGlobalObjectConsoleClientcpp">trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorJSGlobalObjectConsoleClienth">trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorprotocolConsolejson">trunk/Source/JavaScriptCore/inspector/protocol/Console.json</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeConsoleClienth">trunk/Source/JavaScriptCore/runtime/ConsoleClient.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeConsoleObjectcpp">trunk/Source/JavaScriptCore/runtime/ConsoleObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeConsoleTypesh">trunk/Source/JavaScriptCore/runtime/ConsoleTypes.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreinspectorCommandLineAPIModuleSourcejs">trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js</a></li>
<li><a href="#trunkSourceWebCoreinspectorInspectorInstrumentationh">trunk/Source/WebCore/inspector/InspectorInstrumentation.h</a></li>
<li><a href="#trunkSourceWebCorepagePageConsoleClientcpp">trunk/Source/WebCore/page/PageConsoleClient.cpp</a></li>
<li><a href="#trunkSourceWebCorepagePageConsoleClienth">trunk/Source/WebCore/page/PageConsoleClient.h</a></li>
<li><a href="#trunkSourceWebCoreworkersWorkerConsoleClientcpp">trunk/Source/WebCore/workers/WorkerConsoleClient.cpp</a></li>
<li><a href="#trunkSourceWebCoreworkersWorkerConsoleClienth">trunk/Source/WebCore/workers/WorkerConsoleClient.h</a></li>
<li><a href="#trunkSourceWebCoreworkletsWorkletConsoleClientcpp">trunk/Source/WebCore/worklets/WorkletConsoleClient.cpp</a></li>
<li><a href="#trunkSourceWebCoreworkletsWorkletConsoleClienth">trunk/Source/WebCore/worklets/WorkletConsoleClient.h</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUILocalizationsenlprojlocalizedStringsjs">trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseFileUtilitiesjs">trunk/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersJavaScriptLogViewControllerjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptLogViewController.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersJavaScriptRuntimeCompletionProviderjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsConsoleMessagejs">trunk/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsNativeFunctionParametersjs">trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsConsoleCommandViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleCommandView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsConsoleMessageViewcss">trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsConsoleMessageViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsContextMenuUtilitiesjs">trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsLogContentViewcss">trunk/Source/WebInspectorUI/UserInterface/Views/LogContentView.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsMaincss">trunk/Source/WebInspectorUI/UserInterface/Views/Main.css</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorconsoleconsolescreenshotexpectedtxt">trunk/LayoutTests/inspector/console/console-screenshot-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorconsoleconsolescreenshothtml">trunk/LayoutTests/inspector/console/console-screenshot.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceImagesConsoleImagesvg">trunk/Source/WebInspectorUI/UserInterface/Images/ConsoleImage.svg</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/LayoutTests/ChangeLog 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2019-03-15  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: provide a way to capture a screenshot of a node from within the page
+        https://bugs.webkit.org/show_bug.cgi?id=194279
+        <rdar://problem/10731573>
+
+        Reviewed by Joseph Pecoraro.
+
+        * js/console.html:
+        * js/console-expected.txt:
+        * inspector/console/console-screenshot.html: Added.
+        * inspector/console/console-screenshot-expected.txt: Added.
+        * http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt:
+
</ins><span class="cx"> 2019-03-14  Sihui Liu  <sihui_liu@apple.com>
</span><span class="cx"> 
</span><span class="cx">         IndexedDB: re-enable some leak tests
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsinspectordomcrossdomaininspectednodeaccessexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt       2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/LayoutTests/http/tests/inspector/dom/cross-domain-inspected-node-access-expected.txt  2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><del>-CONSOLE MESSAGE: line 43: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
-CONSOLE MESSAGE: line 43: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
</del><ins>+CONSOLE MESSAGE: line 44: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 44: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
</ins><span class="cx"> Test that code evaluated in the main frame cannot access $0 that resolves to a node in a frame from a different domain. Bug 105423.
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorconsoleconsolescreenshotexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/console/console-screenshot-expected.txt (0 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/console/console-screenshot-expected.txt                              (rev 0)
+++ trunk/LayoutTests/inspector/console/console-screenshot-expected.txt 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -0,0 +1,23 @@
</span><ins>+CONSOLE MESSAGE: [object HTMLDivElement]
+CONSOLE MESSAGE: [object HTMLDivElement]
+CONSOLE MESSAGE: Could not capture screenshot
+Tests for the console.screenshot API.
+
+
+== Running test suite: console.screenshot
+-- Running test case: console.screenshot.SingleNode
+PASS: The added message should be an image.
+PASS: The image should be a 2x2 red square.
+
+-- Running test case: console.screenshot.MultipleNodes
+PASS: The added message should be an image.
+PASS: The image should be a 2x2 blue square.
+
+-- Running test case: console.screenshot.DetachedNode
+PASS: Could not capture screenshot
+
+-- Running test case: console.screenshot.NoArguments
+PASS: The added message should be an image.
+PASS: The image width should be greater than 2px.
+PASS: The image height should be greater than 2px.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorconsoleconsolescreenshothtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/console/console-screenshot.html (0 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/console/console-screenshot.html                              (rev 0)
+++ trunk/LayoutTests/inspector/console/console-screenshot.html 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -0,0 +1,122 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+
+function createDetachedTest3()
+{
+    let div = document.createElement("div");
+    div.id = "test3";
+    return div;
+}
+
+function test()
+{
+    InspectorTest.debug();
+
+    // 2x2 red square
+    const redSquareDataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1j/M/AAEQMDEwgAgQAHxcCAmtAm/sAAAAASUVORK5CYII=";
+
+    // 2x2 blue square
+    const blueSquareDataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABRJREFUCB1jZGD4/58BCJhABAgAAB0ZAgJSPDJ6AAAAAElFTkSuQmCC";
+
+    // 2x2 green square
+    const greenSquareDataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAAXNSR0IArs4c6QAAABNJREFUCB1jZGhg+M8ABEwgAgQAFigBgxp1H6oAAAAASUVORK5CYII=";
+
+    let suite = InspectorTest.createAsyncSuite("console.screenshot");
+
+    function addTest({name, expression, imageMessageAddedCallback, shouldError}) {
+        suite.addTestCase({
+            name,
+            test(resolve, reject) {
+                let listener = WI.consoleManager.addEventListener(WI.ConsoleManager.Event.MessageAdded, async (event) => {
+                    let {message} = event.data;
+
+                    let isError = message.level === WI.ConsoleMessage.MessageLevel.Error;
+                    if (isError || message.type === WI.ConsoleMessage.MessageType.Image) {
+                        WI.consoleManager.removeEventListener(WI.ConsoleManager.Event.MessageAdded, listener);
+
+                        if (isError)
+                            InspectorTest.expectThat(shouldError, message.messageText);
+                        else
+                            InspectorTest.expectEqual(message.type, WI.ConsoleMessage.MessageType.Image, "The added message should be an image.");
+
+                        if (imageMessageAddedCallback)
+                            await imageMessageAddedCallback(message);
+
+                        resolve();
+                        return;
+                    }
+                });
+
+                InspectorTest.evaluateInPage(expression)
+                .catch(reject);
+            },
+        });
+    }
+
+    addTest({
+        name: "console.screenshot.SingleNode",
+        expression: `console.screenshot(document.querySelector("#test1"))`,
+        imageMessageAddedCallback(message) {
+            InspectorTest.expectEqual(message.messageText, redSquareDataURL, "The image should be a 2x2 red square.");
+        },
+    });
+
+    addTest({
+        name: "console.screenshot.MultipleNodes", 
+        expression: `console.screenshot(document.querySelector("#test2"), document.querySelector("#test1"))`,
+        imageMessageAddedCallback(message) {
+            InspectorTest.expectEqual(message.messageText, blueSquareDataURL, "The image should be a 2x2 blue square.");
+        },
+    });
+
+    addTest({
+        name: "console.screenshot.DetachedNode",  
+        expression: `console.screenshot(createDetachedTest3())`,
+        shouldError: true,
+    });
+
+    addTest({
+        name: "console.screenshot.NoArguments", 
+        expression: `console.screenshot()`,
+        async imageMessageAddedCallback(message) {
+            InspectorTest.assert(message.messageText !== redSquareDataURL, "The imag should not be a 2x2 red square.");
+            InspectorTest.assert(message.messageText !== blueSquareDataURL, "The imag should not be a 2x2 blue square.");
+            InspectorTest.assert(message.messageText !== greenSquareDataURL, "The imag should not be a 2x2 green square.");
+
+            let img = await WI.ImageUtilities.promisifyLoad(message.messageText);
+            InspectorTest.expectGreaterThan(img.width, 2, "The image width should be greater than 2px.");
+            InspectorTest.expectGreaterThan(img.height, 2, "The image height should be greater than 2px.");
+        },
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Tests for the console.screenshot API.</p>
+    <div id="test1"></div>
+    <div id="test2"></div>
+    <div id="test3"></div>
+    <style>
+    #test1 {
+        width: 2px;
+        height: 2px;
+        background-color: red;
+    }
+    #test2 {
+        width: 2px;
+        height: 2px;
+        background-color: blue;
+    }
+    #test3 {
+        width: 2px;
+        height: 2px;
+        background-color: green;
+    }
+    </style>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsjsconsoleexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/console-expected.txt (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/console-expected.txt        2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/LayoutTests/js/console-expected.txt   2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -177,6 +177,13 @@
</span><span class="cx"> PASS descriptor.writable is true
</span><span class="cx"> PASS descriptor.enumerable is true
</span><span class="cx"> 
</span><ins>+console.screenshot
+PASS typeof console.screenshot is "function"
+PASS console.screenshot.length is 0
+PASS descriptor.configurable is true
+PASS descriptor.writable is true
+PASS descriptor.enumerable is true
+
</ins><span class="cx"> PASS Object.getOwnPropertyNames(console).length is enumerablePropertyCount
</span><span class="cx"> 
</span><span class="cx"> fuzzing of target for console.record
</span><span class="lines">@@ -218,6 +225,18 @@
</span><span class="cx"> PASS console.record(window.canvas) did not throw exception.
</span><span class="cx"> PASS console.record(window.canvas) did not throw exception.
</span><span class="cx"> PASS console.record(window.canvas) did not throw exception.
</span><ins>+
+fuzzing of target for console.screenshot
+PASS console.screenshot() did not throw exception.
+PASS console.screenshot(undefined) did not throw exception.
+PASS console.screenshot(null) did not throw exception.
+PASS console.screenshot(1) did not throw exception.
+PASS console.screenshot("test") did not throw exception.
+PASS console.screenshot([]) did not throw exception.
+PASS console.screenshot({}) did not throw exception.
+PASS console.screenshot(window) did not throw exception.
+PASS console.screenshot(console) did not throw exception.
+
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestsjsconsolehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/console.html (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/console.html        2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/LayoutTests/js/console.html   2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> debug("");
</span><span class="cx"> shouldBe("Object.getOwnPropertyNames(console).length", "enumerablePropertyCount");
</span><span class="cx"> 
</span><del>-const consoleRecordArguments = [
</del><ins>+const fuzzingArguments = [
</ins><span class="cx">   `undefined`,
</span><span class="cx">  `null`,
</span><span class="cx">  `1`,
</span><span class="lines">@@ -61,18 +61,18 @@
</span><span class="cx"> debug("");
</span><span class="cx"> debug("fuzzing of target for console.record");
</span><span class="cx"> shouldNotThrow(`console.record()`);
</span><del>-for (let argument of consoleRecordArguments)
</del><ins>+for (let argument of fuzzingArguments)
</ins><span class="cx">   shouldNotThrow(`console.record(${argument})`);
</span><span class="cx"> 
</span><span class="cx"> debug("");
</span><span class="cx"> debug("fuzzing of options for console.record");
</span><del>-for (let argument of consoleRecordArguments)
</del><ins>+for (let argument of fuzzingArguments)
</ins><span class="cx">   shouldNotThrow(`console.record({}, ${argument})`);
</span><span class="cx"> 
</span><span class="cx"> debug("");
</span><span class="cx"> debug("fuzzing of target for console.recordEnd");
</span><span class="cx"> shouldNotThrow(`console.recordEnd()`);
</span><del>-for (let argument of consoleRecordArguments)
</del><ins>+for (let argument of fuzzingArguments)
</ins><span class="cx">   shouldNotThrow(`console.recordEnd(${argument})`);
</span><span class="cx"> 
</span><span class="cx"> debug("");
</span><span class="lines">@@ -84,6 +84,14 @@
</span><span class="cx"> shouldNotThrow(`console.record(window.canvas)`);
</span><span class="cx"> shouldNotThrow(`console.record(window.canvas)`);
</span><span class="cx"> 
</span><ins>+debug("");
+debug("fuzzing of target for console.screenshot");
+shouldNotThrow(`console.screenshot()`);
+for (let argument of fuzzingArguments)
+    shouldNotThrow(`console.screenshot(${argument})`);
+
+debug("");
+
</ins><span class="cx"> </script>
</span><span class="cx"> <script src="../resources/js-test-post.js"></script>
</span><span class="cx"> <canvas id="canvas"></canvas>
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/ChangeLog       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -1,3 +1,34 @@
</span><ins>+2019-03-15  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: provide a way to capture a screenshot of a node from within the page
+        https://bugs.webkit.org/show_bug.cgi?id=194279
+        <rdar://problem/10731573>
+
+        Reviewed by Joseph Pecoraro.
+
+        Add `console.screenshot` functionality, which displays a screenshot of a given object (if
+        able) within Web Inspector's Console tab. From there, it can be viewed and saved.
+
+        Currently, `console.screenshot` will
+         - capture an image of a `Node` (if provided)
+         - capture an image of the viewport if nothing is provided
+
+        * inspector/protocol/Console.json:
+        Add `Image` enum value to `ConsoleMessage` type.
+        * runtime/ConsoleTypes.h:
+        * inspector/ConsoleMessage.h:
+        * inspector/ConsoleMessage.cpp:
+        (Inspector::messageTypeValue):
+
+        * runtime/ConsoleClient.h:
+        * runtime/ConsoleObject.cpp:
+        (JSC::ConsoleObject::finishCreation):
+        (JSC::consoleProtoFuncScreenshot): Added.
+
+        * inspector/JSGlobalObjectConsoleClient.h:
+        * inspector/JSGlobalObjectConsoleClient.cpp:
+        (Inspector::JSGlobalObjectConsoleClient::screenshot): Added.
+
</ins><span class="cx"> 2019-03-14  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [JSC] Retain PrivateName of Symbol before passing it to operations potentially incurring GC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorConsoleMessagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/ConsoleMessage.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/ConsoleMessage.cpp 2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/inspector/ConsoleMessage.cpp    2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -191,6 +191,7 @@
</span><span class="cx">     case MessageType::Timing: return Protocol::Console::ConsoleMessage::Type::Timing;
</span><span class="cx">     case MessageType::Profile: return Protocol::Console::ConsoleMessage::Type::Profile;
</span><span class="cx">     case MessageType::ProfileEnd: return Protocol::Console::ConsoleMessage::Type::ProfileEnd;
</span><ins>+    case MessageType::Image: return Protocol::Console::ConsoleMessage::Type::Image;
</ins><span class="cx">     }
</span><span class="cx">     return Protocol::Console::ConsoleMessage::Type::Log;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorConsoleMessageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/ConsoleMessage.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/ConsoleMessage.h   2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/inspector/ConsoleMessage.h      2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -55,7 +55,7 @@
</span><span class="cx">     ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, unsigned long requestIdentifier = 0);
</span><span class="cx">     ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, const String& url, unsigned line, unsigned column, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0);
</span><span class="cx">     ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, Ref<ScriptCallStack>&&, unsigned long requestIdentifier = 0);
</span><del>-    ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, Ref<ScriptArguments>&&, JSC::ExecState*, unsigned long requestIdentifier = 0);
</del><ins>+    ConsoleMessage(MessageSource, MessageType, MessageLevel, const String& message, Ref<ScriptArguments>&&, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0);
</ins><span class="cx">     ConsoleMessage(MessageSource, MessageType, MessageLevel, Vector<JSONLogValue>&&, JSC::ExecState*, unsigned long requestIdentifier = 0);
</span><span class="cx">     ~ConsoleMessage();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorJSGlobalObjectConsoleClientcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -168,6 +168,11 @@
</span><span class="cx"> void JSGlobalObjectConsoleClient::record(ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> void JSGlobalObjectConsoleClient::recordEnd(ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> 
</span><ins>+void JSGlobalObjectConsoleClient::screenshot(ExecState*, Ref<ScriptArguments>&&)
+{
+    warnUnimplemented("console.screenshot"_s);
+}
+
</ins><span class="cx"> void JSGlobalObjectConsoleClient::warnUnimplemented(const String& method)
</span><span class="cx"> {
</span><span class="cx">     String message = method + " is currently ignored in JavaScript context inspection.";
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorJSGlobalObjectConsoleClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.h      2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.h 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -55,6 +55,7 @@
</span><span class="cx">     void timeStamp(JSC::ExecState*, Ref<ScriptArguments>&&) override;
</span><span class="cx">     void record(JSC::ExecState*, Ref<ScriptArguments>&&) override;
</span><span class="cx">     void recordEnd(JSC::ExecState*, Ref<ScriptArguments>&&) override;
</span><ins>+    void screenshot(JSC::ExecState*, Ref<ScriptArguments>&&) override;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     void warnUnimplemented(const String& method);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorprotocolConsolejson"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/protocol/Console.json (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/protocol/Console.json      2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/inspector/protocol/Console.json 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx">                 { "name": "source", "$ref": "ChannelSource"},
</span><span class="cx">                 { "name": "level", "type": "string", "enum": ["log", "info", "warning", "error", "debug"], "description": "Message severity." },
</span><span class="cx">                 { "name": "text", "type": "string", "description": "Message text." },
</span><del>-                { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "timing", "profile", "profileEnd"], "description": "Console message type." },
</del><ins>+                { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "timing", "profile", "profileEnd", "image"], "description": "Console message type." },
</ins><span class="cx">                 { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
</span><span class="cx">                 { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." },
</span><span class="cx">                 { "name": "column", "type": "integer", "optional": true, "description": "Column number on the line in the resource that generated this message." },
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeConsoleClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ConsoleClient.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ConsoleClient.h      2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/runtime/ConsoleClient.h 2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -64,6 +64,7 @@
</span><span class="cx">     virtual void timeStamp(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
</span><span class="cx">     virtual void record(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
</span><span class="cx">     virtual void recordEnd(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
</span><ins>+    virtual void screenshot(ExecState*, Ref<Inspector::ScriptArguments>&&) = 0;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeConsoleObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ConsoleObject.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ConsoleObject.cpp    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/runtime/ConsoleObject.cpp       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -59,6 +59,7 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL consoleProtoFuncGroupEnd(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecord(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL consoleProtoFuncRecordEnd(ExecState*);
</span><ins>+static EncodedJSValue JSC_HOST_CALL consoleProtoFuncScreenshot(ExecState*);
</ins><span class="cx"> 
</span><span class="cx"> const ClassInfo ConsoleObject::s_info = { "Console", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ConsoleObject) };
</span><span class="cx"> 
</span><span class="lines">@@ -99,6 +100,7 @@
</span><span class="cx">     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("groupEnd", consoleProtoFuncGroupEnd, static_cast<unsigned>(PropertyAttribute::None), 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("record", consoleProtoFuncRecord, static_cast<unsigned>(PropertyAttribute::None), 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("recordEnd", consoleProtoFuncRecordEnd, static_cast<unsigned>(PropertyAttribute::None), 0);
</span><ins>+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("screenshot", consoleProtoFuncScreenshot, static_cast<unsigned>(PropertyAttribute::None), 0);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
</span><span class="lines">@@ -391,4 +393,14 @@
</span><span class="cx">     return JSValue::encode(jsUndefined());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static EncodedJSValue JSC_HOST_CALL consoleProtoFuncScreenshot(ExecState* exec)
+{
+    ConsoleClient* client = exec->lexicalGlobalObject()->consoleClient();
+    if (!client)
+        return JSValue::encode(jsUndefined());
+
+    client->screenshot(exec, Inspector::createScriptArguments(exec, 0));
+    return JSValue::encode(jsUndefined());
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeConsoleTypesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ConsoleTypes.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ConsoleTypes.h       2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/JavaScriptCore/runtime/ConsoleTypes.h  2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx">     Timing,
</span><span class="cx">     Profile,
</span><span class="cx">     ProfileEnd,
</span><ins>+    Image,
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> enum class MessageLevel : uint8_t {
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/ChangeLog      2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2019-03-15  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: provide a way to capture a screenshot of a node from within the page
+        https://bugs.webkit.org/show_bug.cgi?id=194279
+        <rdar://problem/10731573>
+
+        Reviewed by Joseph Pecoraro.
+
+        Test: inspector/console/console-screenshot.html
+
+        Add `console.screenshot` functionality, which displays a screenshot of a given object (if
+        able) within Web Inspector's Console tab. From there, it can be viewed and saved.
+
+        Currently, `console.screenshot` will
+         - capture an image of a `Node` (if provided)
+         - capture an image of the viewport if nothing is provided
+
+        * page/PageConsoleClient.h:
+        * page/PageConsoleClient.cpp:
+        (WebCore::PageConsoleClient::addMessage):
+        (WebCore::PageConsoleClient::screenshot): Added.
+
+        * workers/WorkerConsoleClient.h:
+        * workers/WorkerConsoleClient.cpp:
+        (WebCore::WorkerConsoleClient::screenshot): Added.
+        * worklets/WorkletConsoleClient.h:
+        * worklets/WorkletConsoleClient.cpp:
+        (WebCore::WorkletConsoleClient::screenshot): Added.
+
+        * inspector/CommandLineAPIModuleSource.js:
+        (CommandLineAPIImpl.prototype.screenshot): Added.
+
+        * inspector/InspectorInstrumentation.h:
+
</ins><span class="cx"> 2019-03-14  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [JSC] Retain PrivateName of Symbol before passing it to operations potentially incurring GC
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectorCommandLineAPIModuleSourcejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -83,6 +83,7 @@
</span><span class="cx">     "profile",
</span><span class="cx">     "profileEnd",
</span><span class="cx">     "queryObjects",
</span><ins>+    "screenshot",
</ins><span class="cx">     "table",
</span><span class="cx">     "unmonitorEvents",
</span><span class="cx">     "values",
</span><span class="lines">@@ -200,6 +201,11 @@
</span><span class="cx">         return inspectedWindow.console.table.apply(inspectedWindow.console, arguments)
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    screenshot: function()
+    {
+        return inspectedWindow.console.screenshot.apply(inspectedWindow.console, arguments)
+    },
+
</ins><span class="cx">     /**
</span><span class="cx">      * @param {Object} object
</span><span class="cx">      * @param {Array.<string>|string=} types
</span></span></pre></div>
<a id="trunkSourceWebCoreinspectorInspectorInstrumentationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/inspector/InspectorInstrumentation.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/inspector/InspectorInstrumentation.h        2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/inspector/InspectorInstrumentation.h   2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx"> 
</span><span class="cx"> struct WebSocketFrame;
</span><span class="cx"> 
</span><del>-#define FAST_RETURN_IF_NO_FRONTENDS(value) if (LIKELY(!hasFrontends())) return value;
</del><ins>+#define FAST_RETURN_IF_NO_FRONTENDS(value) if (LIKELY(!InspectorInstrumentation::hasFrontends())) return value;
</ins><span class="cx"> 
</span><span class="cx"> class InspectorInstrumentation {
</span><span class="cx"> public:
</span></span></pre></div>
<a id="trunkSourceWebCorepagePageConsoleClientcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/PageConsoleClient.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/PageConsoleClient.cpp  2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/page/PageConsoleClient.cpp     2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -34,15 +34,20 @@
</span><span class="cx"> #include "ChromeClient.h"
</span><span class="cx"> #include "Document.h"
</span><span class="cx"> #include "Frame.h"
</span><ins>+#include "FrameSnapshotting.h"
</ins><span class="cx"> #include "HTMLCanvasElement.h"
</span><span class="cx"> #include "ImageBitmapRenderingContext.h"
</span><ins>+#include "ImageBuffer.h"
</ins><span class="cx"> #include "InspectorController.h"
</span><span class="cx"> #include "InspectorInstrumentation.h"
</span><ins>+#include "IntRect.h"
</ins><span class="cx"> #include "JSCanvasRenderingContext2D.h"
</span><span class="cx"> #include "JSExecState.h"
</span><span class="cx"> #include "JSHTMLCanvasElement.h"
</span><span class="cx"> #include "JSImageBitmapRenderingContext.h"
</span><ins>+#include "JSNode.h"
</ins><span class="cx"> #include "JSOffscreenCanvas.h"
</span><ins>+#include "Node.h"
</ins><span class="cx"> #include "OffscreenCanvas.h"
</span><span class="cx"> #include "Page.h"
</span><span class="cx"> #include "ScriptableDocumentParser.h"
</span><span class="lines">@@ -119,7 +124,7 @@
</span><span class="cx"> 
</span><span class="cx"> void PageConsoleClient::addMessage(std::unique_ptr<Inspector::ConsoleMessage>&& consoleMessage)
</span><span class="cx"> {
</span><del>-    if (consoleMessage->source() != MessageSource::CSS && !m_page.usesEphemeralSession()) {
</del><ins>+    if (consoleMessage->source() != MessageSource::CSS && consoleMessage->type() != MessageType::Image && !m_page.usesEphemeralSession()) {
</ins><span class="cx">         m_page.chrome().client().addMessageToConsole(consoleMessage->source(), consoleMessage->level(), consoleMessage->message(), consoleMessage->line(), consoleMessage->column(), consoleMessage->url());
</span><span class="cx"> 
</span><span class="cx">         if (m_page.settings().logsPageMessagesToSystemConsoleEnabled() || shouldPrintExceptions())
</span><span class="lines">@@ -258,4 +263,46 @@
</span><span class="cx">         InspectorInstrumentation::didFinishRecordingCanvasFrame(*context, true);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void PageConsoleClient::screenshot(JSC::ExecState* state, Ref<ScriptArguments>&& arguments)
+{
+    FAST_RETURN_IF_NO_FRONTENDS(void());
+
+    Frame& frame = m_page.mainFrame();
+
+    std::unique_ptr<ImageBuffer> snapshot;
+
+    auto* target = objectArgumentAt(arguments, 0);
+    if (target) {
+        auto* node = JSNode::toWrapped(state->vm(), target);
+        if (!node)
+            return;
+
+        snapshot = WebCore::snapshotNode(frame, *node);
+    } else {
+        // If no target is provided, capture an image of the viewport.
+        IntRect imageRect(IntPoint::zero(), frame.view()->sizeForVisibleContent());
+        snapshot = WebCore::snapshotFrameRect(frame, imageRect, SnapshotOptionsInViewCoordinates);
+    }
+
+    if (!snapshot) {
+        addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Error, "Could not capture screenshot"_s, arguments.copyRef()));
+        return;
+    }
+
+    String dataURL = snapshot->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
+    if (dataURL.isEmpty()) {
+        addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Error, "Could not capture screenshot"_s, arguments.copyRef()));
+        return;
+    }
+
+    if (target) {
+        // Log the argument before sending the image for it.
+        String messageText;
+        arguments->getFirstArgumentAsString(messageText);
+        addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Log, messageText, arguments.copyRef()));
+    }
+
+    addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Image, MessageLevel::Log, dataURL));
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorepagePageConsoleClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/PageConsoleClient.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/PageConsoleClient.h    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/page/PageConsoleClient.h       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -76,6 +76,7 @@
</span><span class="cx">     void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><span class="cx">     void record(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><span class="cx">     void recordEnd(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><ins>+    void screenshot(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     Page& m_page;
</span></span></pre></div>
<a id="trunkSourceWebCoreworkersWorkerConsoleClientcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/workers/WorkerConsoleClient.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/workers/WorkerConsoleClient.cpp     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/workers/WorkerConsoleClient.cpp        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -76,4 +76,6 @@
</span><span class="cx"> void WorkerConsoleClient::record(JSC::ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> void WorkerConsoleClient::recordEnd(JSC::ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> 
</span><ins>+void WorkerConsoleClient::screenshot(JSC::ExecState*, Ref<ScriptArguments>&&) { }
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreworkersWorkerConsoleClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/workers/WorkerConsoleClient.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/workers/WorkerConsoleClient.h       2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/workers/WorkerConsoleClient.h  2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx">     void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><span class="cx">     void record(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><span class="cx">     void recordEnd(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</span><ins>+    void screenshot(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override;
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     WorkerGlobalScope& m_workerGlobalScope;
</span></span></pre></div>
<a id="trunkSourceWebCoreworkletsWorkletConsoleClientcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/worklets/WorkletConsoleClient.cpp (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/worklets/WorkletConsoleClient.cpp   2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/worklets/WorkletConsoleClient.cpp      2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -66,5 +66,7 @@
</span><span class="cx"> void WorkletConsoleClient::record(JSC::ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> void WorkletConsoleClient::recordEnd(JSC::ExecState*, Ref<ScriptArguments>&&) { }
</span><span class="cx"> 
</span><ins>+void WorkletConsoleClient::screenshot(JSC::ExecState*, Ref<ScriptArguments>&&) { }
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreworkletsWorkletConsoleClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/worklets/WorkletConsoleClient.h (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/worklets/WorkletConsoleClient.h     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebCore/worklets/WorkletConsoleClient.h        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -54,6 +54,7 @@
</span><span class="cx">     void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) final;
</span><span class="cx">     void record(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) final;
</span><span class="cx">     void recordEnd(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) final;
</span><ins>+    void screenshot(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) final;
</ins><span class="cx"> 
</span><span class="cx">     WorkletGlobalScope& m_workletGlobalScope;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/ChangeLog       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -1,3 +1,77 @@
</span><ins>+2019-03-15  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: provide a way to capture a screenshot of a node from within the page
+        https://bugs.webkit.org/show_bug.cgi?id=194279
+        <rdar://problem/10731573>
+
+        Reviewed by Joseph Pecoraro.
+
+        Add `console.screenshot` functionality, which displays a screenshot of a given object (if
+        able) within Web Inspector's Console tab. From there, it can be viewed and saved.
+
+        Currently, `console.screenshot` will
+         - capture an image of a `Node` (if provided)
+         - capture an image of the viewport if nothing is provided
+
+        * UserInterface/Models/ConsoleMessage.js:
+        (WI.ConsoleMessage):
+        * UserInterface/Views/ConsoleCommandView.js:
+        (WI.ConsoleCommandView.prototype.render):
+        * UserInterface/Views/ConsoleMessageView.js:
+        (WI.ConsoleMessageView.prototype.render):
+        (WI.ConsoleMessageView.prototype.toClipboardString):
+        (WI.ConsoleMessageView.prototype._appendMessageTextAndArguments):
+        (WI.ConsoleMessageView.prototype._appendSavedResultIndex):
+        (WI.ConsoleMessageView.prototype._appendStackTrace):
+        (WI.ConsoleMessageView.prototype._makeExpandable):
+        (WI.ConsoleMessageView.prototype._handleContextMenu): Added.
+        * UserInterface/Views/ConsoleMessageView.css:
+        (.console-user-command.special-user-log > .console-message-body): Added.
+        (.console-message-body): Added.
+        (.console-message-body > span): Added.
+        (.console-message-body > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider)): Added.
+        (.console-message-body > .console-image): Added.
+        (.console-message-body > .show-grid): Added.
+        (.console-error-level .console-message-body): Added.
+        (.console-warning-level .console-message-body): Added.
+        (.console-log-level.console-image-container::before): Added.
+        (.console-user-command > .console-message-body): Added.
+        (.console-warning-level .console-message-body): Added.
+        (.console-error-level .console-message-body): Added.
+        (.console-user-command > .console-message-body): Added.
+        (.console-user-command.special-user-log > .console-message-text): Deleted.
+        (.console-message-text): Deleted.
+        (.console-message-text > span): Deleted.
+        (.console-message-text > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider)): Deleted.
+        (.console-error-level .console-message-text): Deleted.
+        (.console-warning-level .console-message-text): Deleted.
+        (.console-user-command > .console-message-text): Deleted.
+        (.console-warning-level .console-message-text): Deleted.
+        (.console-error-level .console-message-text): Deleted.
+        (.console-user-command > .console-message-text): Deleted.
+        * UserInterface/Views/LogContentView.css:
+        (.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-text .highlighted): Added.
+        (.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-body .highlighted): Deleted.
+        Renamed variables/classes to be more semantically correct when the content is an image.
+         - `_messageTextElement` to `_messageBodyElement` (JS)
+         - `.console-message-text` to `.console-message-body` (CSS)
+
+        * UserInterface/Controllers/JavaScriptLogViewController.js:
+        (WI.JavaScriptLogViewController.prototype.renderPendingMessages):
+
+        * UserInterface/Views/Main.css:
+        (:matches(img, canvas).show-grid):
+        (@media (prefers-color-scheme: dark) :matches(img, canvas).show-grid):
+
+        * UserInterface/Base/FileUtilities.js:
+        (WI.FileUtilities.screenshotString): Added.
+
+        * UserInterface/Models/NativeFunctionParameters.js:
+        * UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js:
+
+        * UserInterface/Images/ConsoleImage.svg: Copied from UserInterface/Images/Canvas.svg.
+        * Localizations/en.lproj/localizedStrings.js:
+
</ins><span class="cx"> 2019-03-14  Nikita Vasilyev  <nvasilyev@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Styles: Jump to effective property button doesn't hide after overridden property become effective
</span></span></pre></div>
<a id="trunkSourceWebInspectorUILocalizationsenlprojlocalizedStringsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js   2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js      2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -864,6 +864,7 @@
</span><span class="cx"> localizedStrings["Samples"] = "Samples";
</span><span class="cx"> localizedStrings["Save %d"] = "Save %d";
</span><span class="cx"> localizedStrings["Save File"] = "Save File";
</span><ins>+localizedStrings["Save Image"] = "Save Image";
</ins><span class="cx"> localizedStrings["Save Selected"] = "Save Selected";
</span><span class="cx"> localizedStrings["Save configuration"] = "Save configuration";
</span><span class="cx"> localizedStrings["Saved States"] = "Saved States";
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseFileUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js  2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/FileUtilities.js     2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -24,6 +24,20 @@
</span><span class="cx">  */
</span><span class="cx"> 
</span><span class="cx"> WI.FileUtilities = class FileUtilities {
</span><ins>+    static screenshotString()
+    {
+        let date = new Date;
+        let values = [
+            date.getFullYear(),
+            Number.zeroPad(date.getMonth() + 1, 2),
+            Number.zeroPad(date.getDate(), 2),
+            Number.zeroPad(date.getHours(), 2),
+            Number.zeroPad(date.getMinutes(), 2),
+            Number.zeroPad(date.getSeconds(), 2),
+        ];
+        return WI.UIString("Screen Shot %s-%s-%s at %s.%s.%s").format(...values);
+    }
+
</ins><span class="cx">     static save(saveData, forceSaveAs)
</span><span class="cx">     {
</span><span class="cx">         console.assert(saveData);
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersJavaScriptLogViewControllerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptLogViewController.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptLogViewController.js     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptLogViewController.js        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -323,7 +323,7 @@
</span><span class="cx"> 
</span><span class="cx">         this._currentSessionOrGroup = savedCurrentConsoleGroup;
</span><span class="cx"> 
</span><del>-        if (wasScrolledToBottom || lastMessageView instanceof WI.ConsoleCommandView || lastMessageView.message.type === WI.ConsoleMessage.MessageType.Result)
</del><ins>+        if (wasScrolledToBottom || lastMessageView instanceof WI.ConsoleCommandView || lastMessageView.message.type === WI.ConsoleMessage.MessageType.Result || lastMessageView.message.type === WI.ConsoleMessage.MessageType.Image)
</ins><span class="cx">             this.scrollToBottom();
</span><span class="cx"> 
</span><span class="cx">         WI.quickConsole.needsLayout();
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersJavaScriptRuntimeCompletionProviderjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/JavaScriptRuntimeCompletionProvider.js        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -319,6 +319,7 @@
</span><span class="cx">     "profile",
</span><span class="cx">     "profileEnd",
</span><span class="cx">     "queryObjects",
</span><ins>+    "screenshot",
</ins><span class="cx">     "table",
</span><span class="cx">     "unmonitorEvents",
</span><span class="cx">     "values",
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceImagesConsoleImagesvg"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Images/ConsoleImage.svg (0 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Images/ConsoleImage.svg                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Images/ConsoleImage.svg   2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -0,0 +1,6 @@
</span><ins>+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright © 2017 Apple Inc. All rights reserved. -->
+<svg xmlns="http://www.w3.org/2000/svg" id="root" version="1.1" viewBox="0 0 16 16">
+    <rect x="1.5" y="2.5" width="13" height="11" fill="none" stroke="currentColor"/>
+    <polygon points="11 8 9 11 6 7 3 11 3 12 13 12 13 11 11 8"/>
+</svg>
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsConsoleMessagejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js       2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ConsoleMessage.js  2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -27,10 +27,11 @@
</span><span class="cx"> {
</span><span class="cx">     constructor(target, source, level, message, type, url, line, column, repeatCount, parameters, callFrames, request)
</span><span class="cx">     {
</span><ins>+        console.assert(target instanceof WI.Target);
</ins><span class="cx">         console.assert(typeof source === "string");
</span><span class="cx">         console.assert(typeof level === "string");
</span><span class="cx">         console.assert(typeof message === "string");
</span><del>-        console.assert(target instanceof WI.Target);
</del><ins>+        console.assert(!type || Object.values(WI.ConsoleMessage.MessageType).includes(type));
</ins><span class="cx">         console.assert(!parameters || parameters.every((x) => x instanceof WI.RemoteObject));
</span><span class="cx"> 
</span><span class="cx">         this._target = target;
</span><span class="lines">@@ -127,6 +128,7 @@
</span><span class="cx">     Timing: "timing",
</span><span class="cx">     Profile: "profile",
</span><span class="cx">     ProfileEnd: "profileEnd",
</span><ins>+    Image: "image",
</ins><span class="cx">     Result: "result", // Frontend Only.
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsNativeFunctionParametersjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js     2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/NativeFunctionParameters.js        2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -175,6 +175,7 @@
</span><span class="cx">         profileEnd: "name",
</span><span class="cx">         record: "object, [options]",
</span><span class="cx">         recordEnd: "object",
</span><ins>+        screenshot: "[node]",
</ins><span class="cx">         table: "data, [columns]",
</span><span class="cx">         takeHeapSnapshot: "[label]",
</span><span class="cx">         time: "name = \"default\"",
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsConsoleCommandViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleCommandView.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleCommandView.js    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleCommandView.js       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -49,7 +49,7 @@
</span><span class="cx">             this._element.classList.add(this._className);
</span><span class="cx"> 
</span><span class="cx">         this._formattedCommandElement = this._element.appendChild(document.createElement("span"));
</span><del>-        this._formattedCommandElement.classList.add("console-message-text");
</del><ins>+        this._formattedCommandElement.classList.add("console-message-body");
</ins><span class="cx">         this._formattedCommandElement.textContent = this._commandText;
</span><span class="cx"> 
</span><span class="cx">         // FIXME: <https://webkit.org/b/143545> Web Inspector: LogContentView should use higher level objects
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsConsoleMessageViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css   2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.css      2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx">     min-height: 21px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-user-command.special-user-log > .console-message-text {
</del><ins>+.console-user-command.special-user-log > .console-message-body {
</ins><span class="cx">     padding: 0 6px 1px;
</span><span class="cx">     border-radius: 3px;
</span><span class="cx">     border: 1px solid transparent;
</span><span class="lines">@@ -59,18 +59,29 @@
</span><span class="cx">     border-radius: 7px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-message-text {
</del><ins>+.console-message-body {
</ins><span class="cx">     white-space: pre-wrap;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-message-text > span {
</del><ins>+.console-message-body > span {
</ins><span class="cx">     -webkit-user-select: text;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-message-text > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider) {
</del><ins>+.console-message-body > span > :matches(.console-message-enclosed, .console-message-preview, .console-message-preview-divider) {
</ins><span class="cx">     -webkit-user-select: none;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.console-message-body > .console-image {
+    max-width: 500px;
+    max-height: 500px;
+    box-shadow: 1px 2px 6px hsla(0, 0%, 0%, 0.58);
+}
+
+.console-message-body > .show-grid {
+    /* Prevents the light blue highlight from being visible in the checkerboard. */
+    --checkerboard-light-square: white;
+}
+
</ins><span class="cx"> .console-message.expandable .console-top-level-message::before {
</span><span class="cx">     display: inline-block;
</span><span class="cx"> 
</span><span class="lines">@@ -160,7 +171,7 @@
</span><span class="cx">     border-color: hsl(0, 100%, 92%);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-error-level .console-message-text {
</del><ins>+.console-error-level .console-message-body {
</ins><span class="cx">     color: hsl(0, 75%, 45%);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -169,7 +180,7 @@
</span><span class="cx">     border-color: hsl(40, 100%, 90%);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-warning-level .console-message-text {
</del><ins>+.console-warning-level .console-message-body {
</ins><span class="cx">     color: hsl(30, 90%, 35%);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -182,6 +193,10 @@
</span><span class="cx">     content: url(../Images/Log.svg);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.console-log-level.console-image-container::before {
+    content: url(../Images/ConsoleImage.svg);
+}
+
</ins><span class="cx"> .console-info-level::before {
</span><span class="cx">     content: url(../Images/Info.svg);
</span><span class="cx"> }
</span><span class="lines">@@ -212,7 +227,7 @@
</span><span class="cx">     padding-top: 1px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.console-user-command > .console-message-text {
</del><ins>+.console-user-command > .console-message-body {
</ins><span class="cx">     color: hsl(209, 100%, 50%);
</span><span class="cx">     -webkit-user-select: text;
</span><span class="cx"> }
</span><span class="lines">@@ -290,15 +305,15 @@
</span><span class="cx">         background-color: unset;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    .console-warning-level .console-message-text {
</del><ins>+    .console-warning-level .console-message-body {
</ins><span class="cx">         color: hsl(53, 80%, 55%);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    .console-error-level .console-message-text {
</del><ins>+    .console-error-level .console-message-body {
</ins><span class="cx">         color: hsl(10, 100%, 70%);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    .console-user-command > .console-message-text {
</del><ins>+    .console-user-command > .console-message-body {
</ins><span class="cx">         color: hsl(209, 100%, 70%);
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsConsoleMessageViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js    2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ConsoleMessageView.js       2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -88,10 +88,9 @@
</span><span class="cx">         // FIXME: The location link should include stack trace information.
</span><span class="cx">         this._appendLocationLink();
</span><span class="cx"> 
</span><del>-        this._messageTextElement = this._element.appendChild(document.createElement("span"));
-        this._messageTextElement.classList.add("console-top-level-message");
-        this._messageTextElement.classList.add("console-message-text");
-        this._appendMessageTextAndArguments(this._messageTextElement);
</del><ins>+        this._messageBodyElement = this._element.appendChild(document.createElement("span"));
+        this._messageBodyElement.classList.add("console-top-level-message", "console-message-body");
+        this._appendMessageTextAndArguments(this._messageBodyElement);
</ins><span class="cx">         this._appendSavedResultIndex();
</span><span class="cx"> 
</span><span class="cx">         this._appendExtraParameters();
</span><span class="lines">@@ -98,6 +97,11 @@
</span><span class="cx">         this._appendStackTrace();
</span><span class="cx"> 
</span><span class="cx">         this._renderRepeatCount();
</span><ins>+
+        if (this._message.type === WI.ConsoleMessage.MessageType.Image) {
+            this._element.classList.add("console-image-container");
+            this._element.addEventListener("contextmenu", this._handleContextMenu.bind(this));
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     get element()
</span><span class="lines">@@ -197,7 +201,7 @@
</span><span class="cx"> 
</span><span class="cx">     toClipboardString(isPrefixOptional)
</span><span class="cx">     {
</span><del>-        let clipboardString = this._messageTextElement.innerText.removeWordBreakCharacters();
</del><ins>+        let clipboardString = this._messageBodyElement.innerText.removeWordBreakCharacters();
</ins><span class="cx">         if (this._message.savedResultIndex)
</span><span class="cx">             clipboardString = clipboardString.replace(/\s*=\s*(\$\d+)$/, "");
</span><span class="cx"> 
</span><span class="lines">@@ -284,6 +288,20 @@
</span><span class="cx">                 this._extraParameters = null;
</span><span class="cx">                 break;
</span><span class="cx"> 
</span><ins>+            case WI.ConsoleMessage.MessageType.Image: {
+                let img = element.appendChild(document.createElement("img"));
+                img.classList.add("console-image", "show-grid");
+                img.src = this._message.messageText;
+                img.setAttribute("filename", WI.FileUtilities.screenshotString() + ".png");
+                img.addEventListener("load", (event) => {
+                    if (img.width >= img.height)
+                        img.width = img.width / window.devicePixelRatio;
+                    else
+                        img.height = img.height / window.devicePixelRatio;
+                });
+                break;
+            }
+
</ins><span class="cx">             default:
</span><span class="cx">                 var args = this._message.parameters || [this._message.messageText];
</span><span class="cx">                 this._appendFormattedArguments(element, args);
</span><span class="lines">@@ -313,7 +331,7 @@
</span><span class="cx">         if (this._objectTree)
</span><span class="cx">             this._objectTree.appendTitleSuffix(savedVariableElement);
</span><span class="cx">         else
</span><del>-            this._messageTextElement.appendChild(savedVariableElement);
</del><ins>+            this._messageBodyElement.appendChild(savedVariableElement);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _appendLocationLink()
</span><span class="lines">@@ -403,7 +421,7 @@
</span><span class="cx">             this.expand();
</span><span class="cx"> 
</span><span class="cx">         this._stackTraceElement = this._element.appendChild(document.createElement("div"));
</span><del>-        this._stackTraceElement.classList.add("console-message-text", "console-message-stack-trace-container");
</del><ins>+        this._stackTraceElement.classList.add("console-message-body", "console-message-stack-trace-container");
</ins><span class="cx"> 
</span><span class="cx">         var callFramesElement = new WI.StackTraceView(this._message.stackTrace).element;
</span><span class="cx">         this._stackTraceElement.appendChild(callFramesElement);
</span><span class="lines">@@ -920,6 +938,26 @@
</span><span class="cx">         this._element.classList.add("expandable");
</span><span class="cx"> 
</span><span class="cx">         this._boundClickHandler = this.toggle.bind(this);
</span><del>-        this._messageTextElement.addEventListener("click", this._boundClickHandler);
</del><ins>+        this._messageBodyElement.addEventListener("click", this._boundClickHandler);
</ins><span class="cx">     }
</span><ins>+
+    _handleContextMenu(event)
+    {
+        let image = event.target.closest(".console-image");
+        if (!image)
+            return;
+
+        let contextMenu = WI.ContextMenu.createFromEvent(event);
+
+        contextMenu.appendItem(WI.UIString("Save Image"), () => {
+            const forceSaveAs = true;
+            WI.FileUtilities.save({
+                url: encodeURI("web-inspector:///" + image.getAttribute("filename")),
+                content: parseDataURL(this._message.messageText).data,
+                base64Encoded: true,
+            }, forceSaveAs);
+        });
+
+        contextMenu.appendSeparator();
+    }
</ins><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsContextMenuUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js  2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js     2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -250,18 +250,8 @@
</span><span class="cx">                     return;
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                let date = new Date;
-                let values = [
-                    date.getFullYear(),
-                    Number.zeroPad(date.getMonth() + 1, 2),
-                    Number.zeroPad(date.getDate(), 2),
-                    Number.zeroPad(date.getHours(), 2),
-                    Number.zeroPad(date.getMinutes(), 2),
-                    Number.zeroPad(date.getSeconds(), 2),
-                ];
-                let filename = WI.UIString("Screen Shot %s-%s-%s at %s.%s.%s").format(...values);
</del><span class="cx">                 WI.FileUtilities.save({
</span><del>-                    url: encodeURI(`web-inspector:///${filename}.png`),
</del><ins>+                    url: encodeURI(`web-inspector:///${WI.FileUtilities.screenshotString()}.png`),
</ins><span class="cx">                     content: parseDataURL(dataURL).data,
</span><span class="cx">                     base64Encoded: true,
</span><span class="cx">                 });
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsLogContentViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/LogContentView.css (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/LogContentView.css       2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/LogContentView.css  2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -226,7 +226,7 @@
</span><span class="cx">     background-color: hsla(53, 83%, 53%, 0.75);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-text .highlighted {
</del><ins>+.search-in-progress .console-item:not(.filtered-out-by-search).special-user-log .console-message-body .highlighted {
</ins><span class="cx">     color: var(--selected-foreground-color);
</span><span class="cx">     background-color: var(--selected-background-color-highlight);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsMaincss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Main.css (242991 => 242992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/Main.css 2019-03-15 05:56:24 UTC (rev 242991)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Main.css    2019-03-15 08:12:21 UTC (rev 242992)
</span><span class="lines">@@ -411,12 +411,16 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> :matches(img, canvas).show-grid {
</span><del>-    background-image: linear-gradient(315deg, transparent 75%, hsl(0, 0%, 95%) 75%),
-                      linear-gradient(45deg, transparent 75%, hsl(0, 0%, 95%) 75%),
-                      linear-gradient(315deg, hsl(0, 0%, 95%) 25%, transparent 25%),
-                      linear-gradient(45deg, hsl(0, 0%, 95%) 25%, transparent 25%);
</del><ins>+    background-color: var(--checkerboard-light-square);
+    background-image: linear-gradient(315deg, transparent 75%, var(--checkerboard-dark-square) 75%),
+                      linear-gradient(45deg, transparent 75%, var(--checkerboard-dark-square) 75%),
+                      linear-gradient(315deg, var(--checkerboard-dark-square) 25%, transparent 25%),
+                      linear-gradient(45deg, var(--checkerboard-dark-square) 25%, transparent 25%);
</ins><span class="cx">     background-size: 20px 20px;
</span><span class="cx">     background-position: 10px 10px, 10px 0px, 0 0, 0 10px;
</span><ins>+
+    --checkerboard-light-square: transparent;
+    --checkerboard-dark-square: hsl(0, 0%, 95%);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .device-settings-content {
</span><span class="lines">@@ -479,8 +483,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     :matches(img, canvas).show-grid {
</span><del>-        background-color: white;
</del><ins>+        --checkerboard-light-square: white;
</ins><span class="cx">         --checkerboard-dark-square: hsl(0, 0%, 80%);
</span><del>-        background-image: linear-gradient(315deg, transparent 75%, var(--checkerboard-dark-square) 75%), linear-gradient(45deg, transparent 75%, var(--checkerboard-dark-square) 75%), linear-gradient(315deg, var(--checkerboard-dark-square) 25%, transparent 25%), linear-gradient(45deg, var(--checkerboard-dark-square) 25%, transparent 25%);
</del><span class="cx">     }
</span><span class="cx"> }
</span></span></pre>
</div>
</div>

</body>
</html>