1 | /* |
2 | * Copyright (C) 2013, 2014 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 "CanvasRenderingContext2D.h" |
33 | #include "Chrome.h" |
34 | #include "ChromeClient.h" |
35 | #include "Document.h" |
36 | #include "Frame.h" |
37 | #include "FrameSnapshotting.h" |
38 | #include "HTMLCanvasElement.h" |
39 | #include "ImageBitmapRenderingContext.h" |
40 | #include "ImageBuffer.h" |
41 | #include "InspectorController.h" |
42 | #include "InspectorInstrumentation.h" |
43 | #include "IntRect.h" |
44 | #include "JSCanvasRenderingContext2D.h" |
45 | #include "JSExecState.h" |
46 | #include "JSHTMLCanvasElement.h" |
47 | #include "JSImageBitmapRenderingContext.h" |
48 | #include "JSNode.h" |
49 | #include "JSOffscreenCanvas.h" |
50 | #include "Node.h" |
51 | #include "OffscreenCanvas.h" |
52 | #include "Page.h" |
53 | #include "ScriptableDocumentParser.h" |
54 | #include "Settings.h" |
55 | #include <JavaScriptCore/ConsoleMessage.h> |
56 | #include <JavaScriptCore/JSCInlines.h> |
57 | #include <JavaScriptCore/ScriptArguments.h> |
58 | #include <JavaScriptCore/ScriptCallStack.h> |
59 | #include <JavaScriptCore/ScriptCallStackFactory.h> |
60 | #include <wtf/text/WTFString.h> |
61 | |
62 | #if ENABLE(WEBGL) |
63 | #include "JSWebGLRenderingContext.h" |
64 | #include "WebGLRenderingContext.h" |
65 | #endif |
66 | |
67 | #if ENABLE(WEBGL2) |
68 | #include "JSWebGL2RenderingContext.h" |
69 | #include "WebGL2RenderingContext.h" |
70 | #endif |
71 | |
72 | namespace WebCore { |
73 | using namespace Inspector; |
74 | |
75 | PageConsoleClient::PageConsoleClient(Page& page) |
76 | : m_page(page) |
77 | { |
78 | } |
79 | |
80 | PageConsoleClient::~PageConsoleClient() = default; |
81 | |
82 | static int muteCount = 0; |
83 | static bool printExceptions = false; |
84 | |
85 | bool PageConsoleClient::shouldPrintExceptions() |
86 | { |
87 | return printExceptions; |
88 | } |
89 | |
90 | void PageConsoleClient::setShouldPrintExceptions(bool print) |
91 | { |
92 | printExceptions = print; |
93 | } |
94 | |
95 | void PageConsoleClient::mute() |
96 | { |
97 | muteCount++; |
98 | } |
99 | |
100 | void PageConsoleClient::unmute() |
101 | { |
102 | ASSERT(muteCount > 0); |
103 | muteCount--; |
104 | } |
105 | |
106 | void PageConsoleClient::addMessage(std::unique_ptr<Inspector::ConsoleMessage>&& consoleMessage) |
107 | { |
108 | if (consoleMessage->source() != MessageSource::CSS && consoleMessage->type() != MessageType::Image && !m_page.usesEphemeralSession()) { |
109 | m_page.chrome().client().addMessageToConsole(consoleMessage->source(), consoleMessage->level(), consoleMessage->message(), consoleMessage->line(), consoleMessage->column(), consoleMessage->url()); |
110 | |
111 | if (m_page.settings().logsPageMessagesToSystemConsoleEnabled() || shouldPrintExceptions()) |
112 | ConsoleClient::printConsoleMessage(MessageSource::ConsoleAPI, MessageType::Log, consoleMessage->level(), consoleMessage->message(), consoleMessage->url(), consoleMessage->line(), consoleMessage->column()); |
113 | } |
114 | |
115 | InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(consoleMessage)); |
116 | } |
117 | |
118 | void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier, Document* document) |
119 | { |
120 | String url; |
121 | unsigned line = 0; |
122 | unsigned column = 0; |
123 | if (document) |
124 | document->getParserLocation(url, line, column); |
125 | |
126 | addMessage(source, level, message, url, line, column, 0, JSExecState::currentState(), requestIdentifier); |
127 | } |
128 | |
129 | void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, Ref<ScriptCallStack>&& callStack) |
130 | { |
131 | addMessage(source, level, message, String(), 0, 0, WTFMove(callStack), 0); |
132 | } |
133 | |
134 | 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) |
135 | { |
136 | if (muteCount && source != MessageSource::ConsoleAPI) |
137 | return; |
138 | |
139 | std::unique_ptr<Inspector::ConsoleMessage> message; |
140 | |
141 | if (callStack) |
142 | message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier); |
143 | else |
144 | message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, suggestedURL, suggestedLineNumber, suggestedColumnNumber, state, requestIdentifier); |
145 | |
146 | addMessage(WTFMove(message)); |
147 | } |
148 | |
149 | |
150 | void PageConsoleClient::messageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::ExecState* exec, Ref<Inspector::ScriptArguments>&& arguments) |
151 | { |
152 | String messageText; |
153 | bool gotMessage = arguments->getFirstArgumentAsString(messageText); |
154 | |
155 | auto message = std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, type, level, messageText, arguments.copyRef(), exec); |
156 | |
157 | String url = message->url(); |
158 | unsigned lineNumber = message->line(); |
159 | unsigned columnNumber = message->column(); |
160 | |
161 | InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(message)); |
162 | |
163 | if (m_page.usesEphemeralSession()) |
164 | return; |
165 | |
166 | if (gotMessage) |
167 | m_page.chrome().client().addMessageToConsole(MessageSource::ConsoleAPI, level, messageText, lineNumber, columnNumber, url); |
168 | |
169 | if (m_page.settings().logsPageMessagesToSystemConsoleEnabled() || PageConsoleClient::shouldPrintExceptions()) |
170 | ConsoleClient::printConsoleMessageWithArguments(MessageSource::ConsoleAPI, type, level, exec, WTFMove(arguments)); |
171 | } |
172 | |
173 | void PageConsoleClient::count(JSC::ExecState* exec, Ref<ScriptArguments>&& arguments) |
174 | { |
175 | InspectorInstrumentation::consoleCount(m_page, exec, WTFMove(arguments)); |
176 | } |
177 | |
178 | void PageConsoleClient::profile(JSC::ExecState* exec, const String& title) |
179 | { |
180 | // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler |
181 | InspectorInstrumentation::startProfiling(m_page, exec, title); |
182 | } |
183 | |
184 | void PageConsoleClient::profileEnd(JSC::ExecState* exec, const String& title) |
185 | { |
186 | // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler |
187 | InspectorInstrumentation::stopProfiling(m_page, exec, title); |
188 | } |
189 | |
190 | void PageConsoleClient::takeHeapSnapshot(JSC::ExecState*, const String& title) |
191 | { |
192 | InspectorInstrumentation::takeHeapSnapshot(m_page.mainFrame(), title); |
193 | } |
194 | |
195 | void PageConsoleClient::time(JSC::ExecState*, const String& title) |
196 | { |
197 | InspectorInstrumentation::startConsoleTiming(m_page.mainFrame(), title); |
198 | } |
199 | |
200 | void PageConsoleClient::timeEnd(JSC::ExecState* exec, const String& title) |
201 | { |
202 | InspectorInstrumentation::stopConsoleTiming(m_page.mainFrame(), title, createScriptCallStackForConsole(exec, 1)); |
203 | } |
204 | |
205 | void PageConsoleClient::timeStamp(JSC::ExecState*, Ref<ScriptArguments>&& arguments) |
206 | { |
207 | InspectorInstrumentation::consoleTimeStamp(m_page.mainFrame(), WTFMove(arguments)); |
208 | } |
209 | |
210 | static JSC::JSObject* objectArgumentAt(ScriptArguments& arguments, unsigned index) |
211 | { |
212 | return arguments.argumentCount() > index ? arguments.argumentAt(index).getObject() : nullptr; |
213 | } |
214 | |
215 | static CanvasRenderingContext* canvasRenderingContext(JSC::VM& vm, ScriptArguments& arguments) |
216 | { |
217 | auto* target = objectArgumentAt(arguments, 0); |
218 | if (!target) |
219 | return nullptr; |
220 | |
221 | if (auto* canvas = JSHTMLCanvasElement::toWrapped(vm, target)) |
222 | return canvas->renderingContext(); |
223 | if (auto* canvas = JSOffscreenCanvas::toWrapped(vm, target)) |
224 | return canvas->renderingContext(); |
225 | if (auto* context = JSCanvasRenderingContext2D::toWrapped(vm, target)) |
226 | return context; |
227 | if (auto* context = JSImageBitmapRenderingContext::toWrapped(vm, target)) |
228 | return context; |
229 | #if ENABLE(WEBGL) |
230 | if (auto* context = JSWebGLRenderingContext::toWrapped(vm, target)) |
231 | return context; |
232 | #endif |
233 | #if ENABLE(WEBGL2) |
234 | if (auto* context = JSWebGL2RenderingContext::toWrapped(vm, target)) |
235 | return context; |
236 | #endif |
237 | return nullptr; |
238 | } |
239 | |
240 | void PageConsoleClient::record(JSC::ExecState* state, Ref<ScriptArguments>&& arguments) |
241 | { |
242 | if (auto* context = canvasRenderingContext(state->vm(), arguments)) |
243 | InspectorInstrumentation::consoleStartRecordingCanvas(*context, *state, objectArgumentAt(arguments, 1)); |
244 | } |
245 | |
246 | void PageConsoleClient::recordEnd(JSC::ExecState* state, Ref<ScriptArguments>&& arguments) |
247 | { |
248 | if (auto* context = canvasRenderingContext(state->vm(), arguments)) |
249 | InspectorInstrumentation::didFinishRecordingCanvasFrame(*context, true); |
250 | } |
251 | |
252 | void PageConsoleClient::screenshot(JSC::ExecState* state, Ref<ScriptArguments>&& arguments) |
253 | { |
254 | FAST_RETURN_IF_NO_FRONTENDS(void()); |
255 | |
256 | Frame& frame = m_page.mainFrame(); |
257 | |
258 | std::unique_ptr<ImageBuffer> snapshot; |
259 | |
260 | auto* target = objectArgumentAt(arguments, 0); |
261 | if (target) { |
262 | auto* node = JSNode::toWrapped(state->vm(), target); |
263 | if (!node) |
264 | return; |
265 | |
266 | snapshot = WebCore::snapshotNode(frame, *node); |
267 | } else { |
268 | // If no target is provided, capture an image of the viewport. |
269 | IntRect imageRect(IntPoint::zero(), frame.view()->sizeForVisibleContent()); |
270 | snapshot = WebCore::snapshotFrameRect(frame, imageRect, SnapshotOptionsInViewCoordinates); |
271 | } |
272 | |
273 | if (!snapshot) { |
274 | addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Error, "Could not capture screenshot"_s , arguments.copyRef())); |
275 | return; |
276 | } |
277 | |
278 | String dataURL = snapshot->toDataURL("image/png"_s , WTF::nullopt, PreserveResolution::Yes); |
279 | if (dataURL.isEmpty()) { |
280 | addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Error, "Could not capture screenshot"_s , arguments.copyRef())); |
281 | return; |
282 | } |
283 | |
284 | if (target) { |
285 | // Log the argument before sending the image for it. |
286 | String messageText; |
287 | arguments->getFirstArgumentAsString(messageText); |
288 | addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Log, messageText, arguments.copyRef())); |
289 | } |
290 | |
291 | addMessage(std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Image, MessageLevel::Log, dataURL)); |
292 | } |
293 | |
294 | } // namespace WebCore |
295 | |