Clean up some includes to make the build a bit faster: DOMPromise
[WebKit-https.git] / Source / WebCore / page / PageConsoleClient.cpp
1 /*
2  * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "PageConsoleClient.h"
31
32 #include "CachedImage.h"
33 #include "CanvasRenderingContext2D.h"
34 #include "Chrome.h"
35 #include "ChromeClient.h"
36 #include "Document.h"
37 #include "ElementChildIterator.h"
38 #include "Frame.h"
39 #include "FrameSnapshotting.h"
40 #include "HTMLCanvasElement.h"
41 #include "HTMLImageElement.h"
42 #include "HTMLPictureElement.h"
43 #include "HTMLVideoElement.h"
44 #include "Image.h"
45 #include "ImageBitmap.h"
46 #include "ImageBitmapRenderingContext.h"
47 #include "ImageBuffer.h"
48 #include "ImageData.h"
49 #include "InspectorController.h"
50 #include "InspectorInstrumentation.h"
51 #include "IntRect.h"
52 #include "JSCanvasRenderingContext2D.h"
53 #include "JSExecState.h"
54 #include "JSHTMLCanvasElement.h"
55 #include "JSImageBitmap.h"
56 #include "JSImageBitmapRenderingContext.h"
57 #include "JSImageData.h"
58 #include "JSNode.h"
59 #include "JSOffscreenCanvas.h"
60 #include "Node.h"
61 #include "OffscreenCanvas.h"
62 #include "Page.h"
63 #include "ScriptableDocumentParser.h"
64 #include "Settings.h"
65 #include <JavaScriptCore/ConsoleMessage.h>
66 #include <JavaScriptCore/JSCInlines.h>
67 #include <JavaScriptCore/ScriptArguments.h>
68 #include <JavaScriptCore/ScriptCallStack.h>
69 #include <JavaScriptCore/ScriptCallStackFactory.h>
70 #include <JavaScriptCore/StrongInlines.h>
71 #include <wtf/text/WTFString.h>
72
73 #if ENABLE(WEBGL)
74 #include "JSWebGLRenderingContext.h"
75 #include "WebGLRenderingContext.h"
76 #endif
77
78 #if ENABLE(WEBGL2)
79 #include "JSWebGL2RenderingContext.h"
80 #include "WebGL2RenderingContext.h"
81 #endif
82
83 namespace WebCore {
84 using namespace Inspector;
85
86 PageConsoleClient::PageConsoleClient(Page& page)
87     : m_page(page)
88 {
89 }
90
91 PageConsoleClient::~PageConsoleClient() = default;
92
93 static int muteCount = 0;
94 static bool printExceptions = false;
95
96 bool PageConsoleClient::shouldPrintExceptions()
97 {
98     return printExceptions;
99 }
100
101 void PageConsoleClient::setShouldPrintExceptions(bool print)
102 {
103     printExceptions = print;
104 }
105
106 void PageConsoleClient::mute()
107 {
108     muteCount++;
109 }
110
111 void PageConsoleClient::unmute()
112 {
113     ASSERT(muteCount > 0);
114     muteCount--;
115 }
116
117 void PageConsoleClient::addMessage(std::unique_ptr<Inspector::ConsoleMessage>&& consoleMessage)
118 {
119     if (!m_page.usesEphemeralSession()) {
120         String message;
121         if (consoleMessage->type() == MessageType::Image) {
122             ASSERT(consoleMessage->arguments());
123             consoleMessage->arguments()->getFirstArgumentAsString(message);
124         } else
125             message = consoleMessage->message();
126         m_page.chrome().client().addMessageToConsole(consoleMessage->source(), consoleMessage->level(), message, consoleMessage->line(), consoleMessage->column(), consoleMessage->url());
127
128         if (UNLIKELY(m_page.settings().logsPageMessagesToSystemConsoleEnabled() || shouldPrintExceptions())) {
129             if (consoleMessage->type() == MessageType::Image) {
130                 ASSERT(consoleMessage->arguments());
131                 ConsoleClient::printConsoleMessageWithArguments(MessageSource::ConsoleAPI, MessageType::Log, consoleMessage->level(), consoleMessage->arguments()->globalState(), *consoleMessage->arguments());
132             } else
133                 ConsoleClient::printConsoleMessage(MessageSource::ConsoleAPI, MessageType::Log, consoleMessage->level(), consoleMessage->message(), consoleMessage->url(), consoleMessage->line(), consoleMessage->column());
134         }
135     }
136
137     InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(consoleMessage));
138 }
139
140 void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier, Document* document)
141 {
142     String url;
143     unsigned line = 0;
144     unsigned column = 0;
145     if (document)
146         document->getParserLocation(url, line, column);
147
148     addMessage(source, level, message, url, line, column, 0, JSExecState::currentState(), requestIdentifier);
149 }
150
151 void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, Ref<ScriptCallStack>&& callStack)
152 {
153     addMessage(source, level, message, String(), 0, 0, WTFMove(callStack), 0);
154 }
155
156 void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& suggestedURL, unsigned suggestedLineNumber, unsigned suggestedColumnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::ExecState* state, unsigned long requestIdentifier)
157 {
158     if (muteCount && source != MessageSource::ConsoleAPI)
159         return;
160
161     std::unique_ptr<Inspector::ConsoleMessage> message;
162
163     if (callStack)
164         message = makeUnique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier);
165     else
166         message = makeUnique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, suggestedURL, suggestedLineNumber, suggestedColumnNumber, state, requestIdentifier);
167
168     addMessage(WTFMove(message));
169 }
170
171
172 void PageConsoleClient::messageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::ExecState* exec, Ref<Inspector::ScriptArguments>&& arguments)
173 {
174     String messageText;
175     bool gotMessage = arguments->getFirstArgumentAsString(messageText);
176
177     auto message = makeUnique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, type, level, messageText, arguments.copyRef(), exec);
178
179     String url = message->url();
180     unsigned lineNumber = message->line();
181     unsigned columnNumber = message->column();
182
183     InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(message));
184
185     if (m_page.usesEphemeralSession())
186         return;
187
188     if (gotMessage)
189         m_page.chrome().client().addMessageToConsole(MessageSource::ConsoleAPI, level, messageText, lineNumber, columnNumber, url);
190
191     if (m_page.settings().logsPageMessagesToSystemConsoleEnabled() || PageConsoleClient::shouldPrintExceptions())
192         ConsoleClient::printConsoleMessageWithArguments(MessageSource::ConsoleAPI, type, level, exec, WTFMove(arguments));
193 }
194
195 void PageConsoleClient::count(JSC::ExecState* exec, const String& label)
196 {
197     InspectorInstrumentation::consoleCount(m_page, exec, label);
198 }
199
200 void PageConsoleClient::countReset(JSC::ExecState* exec, const String& label)
201 {
202     InspectorInstrumentation::consoleCountReset(m_page, exec, label);
203 }
204
205 void PageConsoleClient::profile(JSC::ExecState* exec, const String& title)
206 {
207     // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler
208     InspectorInstrumentation::startProfiling(m_page, exec, title);
209 }
210
211 void PageConsoleClient::profileEnd(JSC::ExecState* exec, const String& title)
212 {
213     // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler
214     InspectorInstrumentation::stopProfiling(m_page, exec, title);
215 }
216
217 void PageConsoleClient::takeHeapSnapshot(JSC::ExecState*, const String& title)
218 {
219     InspectorInstrumentation::takeHeapSnapshot(m_page.mainFrame(), title);
220 }
221
222 void PageConsoleClient::time(JSC::ExecState* exec, const String& label)
223 {
224     InspectorInstrumentation::startConsoleTiming(m_page.mainFrame(), exec, label);
225 }
226
227 void PageConsoleClient::timeLog(JSC::ExecState* exec, const String& label, Ref<ScriptArguments>&& arguments)
228 {
229     InspectorInstrumentation::logConsoleTiming(m_page.mainFrame(), exec, label, WTFMove(arguments));
230 }
231
232 void PageConsoleClient::timeEnd(JSC::ExecState* exec, const String& label)
233 {
234     InspectorInstrumentation::stopConsoleTiming(m_page.mainFrame(), exec, label);
235 }
236
237 void PageConsoleClient::timeStamp(JSC::ExecState*, Ref<ScriptArguments>&& arguments)
238 {
239     InspectorInstrumentation::consoleTimeStamp(m_page.mainFrame(), WTFMove(arguments));
240 }
241
242 static JSC::JSObject* objectArgumentAt(ScriptArguments& arguments, unsigned index)
243 {
244     return arguments.argumentCount() > index ? arguments.argumentAt(index).getObject() : nullptr;
245 }
246
247 static CanvasRenderingContext* canvasRenderingContext(JSC::VM& vm, JSC::JSValue target)
248 {
249     if (auto* canvas = JSHTMLCanvasElement::toWrapped(vm, target))
250         return canvas->renderingContext();
251     if (auto* canvas = JSOffscreenCanvas::toWrapped(vm, target))
252         return canvas->renderingContext();
253     if (auto* context = JSCanvasRenderingContext2D::toWrapped(vm, target))
254         return context;
255     if (auto* context = JSImageBitmapRenderingContext::toWrapped(vm, target))
256         return context;
257 #if ENABLE(WEBGL)
258     if (auto* context = JSWebGLRenderingContext::toWrapped(vm, target))
259         return context;
260 #endif
261 #if ENABLE(WEBGL2)
262     if (auto* context = JSWebGL2RenderingContext::toWrapped(vm, target))
263         return context;
264 #endif
265     return nullptr;
266 }
267
268 void PageConsoleClient::record(JSC::ExecState* state, Ref<ScriptArguments>&& arguments)
269 {
270     if (auto* target = objectArgumentAt(arguments, 0)) {
271         if (auto* context = canvasRenderingContext(state->vm(), target))
272             InspectorInstrumentation::consoleStartRecordingCanvas(*context, *state, objectArgumentAt(arguments, 1));
273     }
274 }
275
276 void PageConsoleClient::recordEnd(JSC::ExecState* state, Ref<ScriptArguments>&& arguments)
277 {
278     if (auto* target = objectArgumentAt(arguments, 0)) {
279         if (auto* context = canvasRenderingContext(state->vm(), target))
280             InspectorInstrumentation::didFinishRecordingCanvasFrame(*context, true);
281     }
282 }
283
284 static Optional<String> snapshotCanvas(HTMLCanvasElement& canvasElement, CanvasRenderingContext& canvasRenderingContext)
285 {
286 #if ENABLE(WEBGL)
287     if (is<WebGLRenderingContextBase>(canvasRenderingContext))
288         downcast<WebGLRenderingContextBase>(canvasRenderingContext).setPreventBufferClearForInspector(true);
289 #endif
290
291     auto result = canvasElement.toDataURL("image/png"_s);
292
293 #if ENABLE(WEBGL)
294     if (is<WebGLRenderingContextBase>(canvasRenderingContext))
295         downcast<WebGLRenderingContextBase>(canvasRenderingContext).setPreventBufferClearForInspector(false);
296 #endif
297
298     if (!result.hasException())
299         return result.releaseReturnValue().string;
300
301     return WTF::nullopt;
302 }
303
304 void PageConsoleClient::screenshot(JSC::ExecState* state, Ref<ScriptArguments>&& arguments)
305 {
306     JSC::VM& vm = state->vm();
307     String dataURL;
308     JSC::JSValue target;
309
310     if (arguments->argumentCount()) {
311         auto possibleTarget = arguments->argumentAt(0);
312
313         if (auto* node = JSNode::toWrapped(vm, possibleTarget)) {
314             target = possibleTarget;
315             if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
316                 std::unique_ptr<ImageBuffer> snapshot;
317
318                 // Only try to do something special for subclasses of Node if they're detached from the DOM tree.
319                 if (!node->document().contains(node)) {
320                     auto snapshotImageElement = [&snapshot] (HTMLImageElement& imageElement) {
321                         if (auto* cachedImage = imageElement.cachedImage()) {
322                             auto* image = cachedImage->image();
323                             if (image && image != &Image::nullImage()) {
324                                 snapshot = ImageBuffer::create(image->size(), RenderingMode::Unaccelerated);
325                                 snapshot->context().drawImage(*image, FloatPoint(0, 0));
326                             }
327                         }
328                     };
329
330                     if (is<HTMLImageElement>(node))
331                         snapshotImageElement(downcast<HTMLImageElement>(*node));
332                     else if (is<HTMLPictureElement>(node)) {
333                         if (auto* firstImage = childrenOfType<HTMLImageElement>(downcast<HTMLPictureElement>(*node)).first())
334                             snapshotImageElement(*firstImage);
335                     }
336 #if ENABLE(VIDEO)
337                     else if (is<HTMLVideoElement>(node)) {
338                         auto& videoElement = downcast<HTMLVideoElement>(*node);
339                         unsigned videoWidth = videoElement.videoWidth();
340                         unsigned videoHeight = videoElement.videoHeight();
341                         snapshot = ImageBuffer::create(FloatSize(videoWidth, videoHeight), RenderingMode::Unaccelerated);
342                         videoElement.paintCurrentFrameInContext(snapshot->context(), FloatRect(0, 0, videoWidth, videoHeight));
343                     }
344 #endif
345                     else if (is<HTMLCanvasElement>(node)) {
346                         auto& canvasElement = downcast<HTMLCanvasElement>(*node);
347                         if (auto* canvasRenderingContext = canvasElement.renderingContext()) {
348                             if (auto result = snapshotCanvas(canvasElement, *canvasRenderingContext))
349                                 dataURL = result.value();
350                         }
351                     }
352                 }
353
354                 if (dataURL.isEmpty()) {
355                     if (!snapshot)
356                         snapshot = WebCore::snapshotNode(m_page.mainFrame(), *node);
357
358                     if (snapshot)
359                         dataURL = snapshot->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
360                 }
361             }
362         } else if (auto* imageData = JSImageData::toWrapped(vm, possibleTarget)) {
363             target = possibleTarget;
364             if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
365                 auto sourceSize = imageData->size();
366                 if (auto imageBuffer = ImageBuffer::create(sourceSize, RenderingMode::Unaccelerated)) {
367                     IntRect sourceRect(IntPoint(), sourceSize);
368                     imageBuffer->putByteArray(*imageData->data(), AlphaPremultiplication::Unpremultiplied, sourceSize, sourceRect, IntPoint());
369                     dataURL = imageBuffer->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
370                 }
371             }
372         } else if (auto* imageBitmap = JSImageBitmap::toWrapped(vm, possibleTarget)) {
373             target = possibleTarget;
374             if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
375                 if (auto* imageBuffer = imageBitmap->buffer())
376                     dataURL = imageBuffer->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
377             }
378         } else if (auto* context = canvasRenderingContext(vm, possibleTarget)) {
379             auto& canvas = context->canvasBase();
380             if (is<HTMLCanvasElement>(canvas)) {
381                 target = possibleTarget;
382                 if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
383                     if (auto result = snapshotCanvas(downcast<HTMLCanvasElement>(canvas), *context))
384                         dataURL = result.value();
385                 }
386             }
387
388             // FIXME: <https://webkit.org/b/180833> Web Inspector: support OffscreenCanvas for Canvas related operations
389         } else {
390             String base64;
391             if (possibleTarget.getString(state, base64) && base64.startsWithIgnoringASCIICase("data:"_s) && base64.length() > 5) {
392                 target = possibleTarget;
393                 dataURL = base64;
394             }
395         }
396     }
397
398     if (UNLIKELY(InspectorInstrumentation::hasFrontends())) {
399         if (!target) {
400             // If no target is provided, capture an image of the viewport.
401             IntRect imageRect(IntPoint::zero(), m_page.mainFrame().view()->sizeForVisibleContent());
402             if (auto snapshot = WebCore::snapshotFrameRect(m_page.mainFrame(), imageRect, SnapshotOptionsInViewCoordinates))
403                 dataURL = snapshot->toDataURL("image/png"_s, WTF::nullopt, PreserveResolution::Yes);
404         }
405
406         if (dataURL.isEmpty()) {
407             addMessage(makeUnique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Image, MessageLevel::Error, "Could not capture screenshot"_s, WTFMove(arguments)));
408             return;
409         }
410     }
411
412     Vector<JSC::Strong<JSC::Unknown>> adjustedArguments;
413     adjustedArguments.append({ vm, target ? target : JSC::jsNontrivialString(vm, "Viewport"_s) });
414     for (size_t i = (!target ? 0 : 1); i < arguments->argumentCount(); ++i)
415         adjustedArguments.append({ vm, arguments->argumentAt(i) });
416     arguments = ScriptArguments::create(*state, WTFMove(adjustedArguments));
417     addMessage(makeUnique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Image, MessageLevel::Log, dataURL, WTFMove(arguments)));
418 }
419
420 } // namespace WebCore