1 | /* |
2 | * Copyright (C) 2007-2017 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2008 Matt Lilek <[email protected]> |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
15 | * its contributors may be used to endorse or promote products derived |
16 | * from this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
19 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
21 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | */ |
29 | |
30 | #include "config.h" |
31 | #include "InspectorFrontendHost.h" |
32 | |
33 | #include "CertificateInfo.h" |
34 | #include "ContextMenu.h" |
35 | #include "ContextMenuController.h" |
36 | #include "ContextMenuItem.h" |
37 | #include "ContextMenuProvider.h" |
38 | #include "DOMWrapperWorld.h" |
39 | #include "Document.h" |
40 | #include "Editor.h" |
41 | #include "Event.h" |
42 | #include "FloatRect.h" |
43 | #include "FocusController.h" |
44 | #include "Frame.h" |
45 | #include "HitTestResult.h" |
46 | #include "InspectorController.h" |
47 | #include "InspectorFrontendClient.h" |
48 | #include "JSDOMConvertInterface.h" |
49 | #include "JSDOMExceptionHandling.h" |
50 | #include "JSExecState.h" |
51 | #include "JSInspectorFrontendHost.h" |
52 | #include "MouseEvent.h" |
53 | #include "Node.h" |
54 | #include "Page.h" |
55 | #include "Pasteboard.h" |
56 | #include "ScriptState.h" |
57 | #include "UserGestureIndicator.h" |
58 | #include <JavaScriptCore/ScriptFunctionCall.h> |
59 | #include <pal/system/Sound.h> |
60 | #include <wtf/StdLibExtras.h> |
61 | #include <wtf/text/Base64.h> |
62 | |
63 | namespace WebCore { |
64 | |
65 | using namespace Inspector; |
66 | |
67 | #if ENABLE(CONTEXT_MENUS) |
68 | class : public ContextMenuProvider { |
69 | public: |
70 | static Ref<FrontendMenuProvider> (InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items) |
71 | { |
72 | return adoptRef(*new FrontendMenuProvider(frontendHost, frontendApiObject, items)); |
73 | } |
74 | |
75 | void () |
76 | { |
77 | m_frontendApiObject = { }; |
78 | m_frontendHost = nullptr; |
79 | } |
80 | |
81 | private: |
82 | (InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items) |
83 | : m_frontendHost(frontendHost) |
84 | , m_frontendApiObject(frontendApiObject) |
85 | , m_items(items) |
86 | { |
87 | } |
88 | |
89 | virtual () |
90 | { |
91 | contextMenuCleared(); |
92 | } |
93 | |
94 | void (ContextMenu* ) override |
95 | { |
96 | for (auto& item : m_items) |
97 | menu->appendItem(item); |
98 | } |
99 | |
100 | void (ContextMenuAction action, const String&) override |
101 | { |
102 | if (m_frontendHost) { |
103 | UserGestureIndicator gestureIndicator(ProcessingUserGesture); |
104 | int itemNumber = action - ContextMenuItemBaseCustomTag; |
105 | |
106 | Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected" , WebCore::functionCallHandlerFromAnyThread); |
107 | function.appendArgument(itemNumber); |
108 | function.call(); |
109 | } |
110 | } |
111 | |
112 | void () override |
113 | { |
114 | if (m_frontendHost) { |
115 | Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared" , WebCore::functionCallHandlerFromAnyThread); |
116 | function.call(); |
117 | |
118 | m_frontendHost->m_menuProvider = nullptr; |
119 | } |
120 | m_items.clear(); |
121 | } |
122 | |
123 | InspectorFrontendHost* ; |
124 | Deprecated::ScriptObject ; |
125 | Vector<ContextMenuItem> ; |
126 | }; |
127 | #endif |
128 | |
129 | InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage) |
130 | : m_client(client) |
131 | , m_frontendPage(frontendPage) |
132 | #if ENABLE(CONTEXT_MENUS) |
133 | , m_menuProvider(nullptr) |
134 | #endif |
135 | { |
136 | } |
137 | |
138 | InspectorFrontendHost::~InspectorFrontendHost() |
139 | { |
140 | ASSERT(!m_client); |
141 | } |
142 | |
143 | void InspectorFrontendHost::disconnectClient() |
144 | { |
145 | m_client = nullptr; |
146 | #if ENABLE(CONTEXT_MENUS) |
147 | if (m_menuProvider) |
148 | m_menuProvider->disconnect(); |
149 | #endif |
150 | m_frontendPage = nullptr; |
151 | } |
152 | |
153 | void InspectorFrontendHost::addSelfToGlobalObjectInWorld(DOMWrapperWorld& world) |
154 | { |
155 | auto& state = *execStateFromPage(world, m_frontendPage); |
156 | auto& vm = state.vm(); |
157 | JSC::JSLockHolder lock(vm); |
158 | auto scope = DECLARE_CATCH_SCOPE(vm); |
159 | |
160 | auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()); |
161 | globalObject.putDirect(vm, JSC::Identifier::fromString(&vm, "InspectorFrontendHost" ), toJS<IDLInterface<InspectorFrontendHost>>(state, globalObject, *this)); |
162 | if (UNLIKELY(scope.exception())) |
163 | reportException(&state, scope.exception()); |
164 | } |
165 | |
166 | void InspectorFrontendHost::loaded() |
167 | { |
168 | if (m_client) |
169 | m_client->frontendLoaded(); |
170 | } |
171 | |
172 | void InspectorFrontendHost::requestSetDockSide(const String& side) |
173 | { |
174 | if (!m_client) |
175 | return; |
176 | if (side == "undocked" ) |
177 | m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Undocked); |
178 | else if (side == "right" ) |
179 | m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Right); |
180 | else if (side == "left" ) |
181 | m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Left); |
182 | else if (side == "bottom" ) |
183 | m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Bottom); |
184 | } |
185 | |
186 | void InspectorFrontendHost::closeWindow() |
187 | { |
188 | if (m_client) { |
189 | m_client->closeWindow(); |
190 | disconnectClient(); // Disconnect from client. |
191 | } |
192 | } |
193 | |
194 | void InspectorFrontendHost::reopen() |
195 | { |
196 | if (m_client) |
197 | m_client->reopen(); |
198 | } |
199 | |
200 | void InspectorFrontendHost::bringToFront() |
201 | { |
202 | if (m_client) |
203 | m_client->bringToFront(); |
204 | } |
205 | |
206 | void InspectorFrontendHost::inspectedURLChanged(const String& newURL) |
207 | { |
208 | if (m_client) |
209 | m_client->inspectedURLChanged(newURL); |
210 | } |
211 | |
212 | void InspectorFrontendHost::setZoomFactor(float zoom) |
213 | { |
214 | if (m_frontendPage) |
215 | m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1); |
216 | } |
217 | |
218 | float InspectorFrontendHost::zoomFactor() |
219 | { |
220 | if (m_frontendPage) |
221 | return m_frontendPage->mainFrame().pageZoomFactor(); |
222 | |
223 | return 1.0; |
224 | } |
225 | |
226 | String InspectorFrontendHost::userInterfaceLayoutDirection() |
227 | { |
228 | if (m_client && m_client->userInterfaceLayoutDirection() == UserInterfaceLayoutDirection::RTL) |
229 | return "rtl"_s ; |
230 | |
231 | return "ltr"_s ; |
232 | } |
233 | |
234 | void InspectorFrontendHost::setAttachedWindowHeight(unsigned height) |
235 | { |
236 | if (m_client) |
237 | m_client->changeAttachedWindowHeight(height); |
238 | } |
239 | |
240 | void InspectorFrontendHost::setAttachedWindowWidth(unsigned width) |
241 | { |
242 | if (m_client) |
243 | m_client->changeAttachedWindowWidth(width); |
244 | } |
245 | |
246 | void InspectorFrontendHost::setSheetRect(float x, float y, unsigned width, unsigned height) |
247 | { |
248 | if (m_client) |
249 | m_client->changeSheetRect(FloatRect(x, y, width, height)); |
250 | } |
251 | |
252 | void InspectorFrontendHost::startWindowDrag() |
253 | { |
254 | if (m_client) |
255 | m_client->startWindowDrag(); |
256 | } |
257 | |
258 | void InspectorFrontendHost::moveWindowBy(float x, float y) const |
259 | { |
260 | if (m_client) |
261 | m_client->moveWindowBy(x, y); |
262 | } |
263 | |
264 | bool InspectorFrontendHost::isRemote() const |
265 | { |
266 | return m_client ? m_client->isRemote() : false; |
267 | } |
268 | |
269 | String InspectorFrontendHost::localizedStringsURL() |
270 | { |
271 | return m_client ? m_client->localizedStringsURL() : String(); |
272 | } |
273 | |
274 | String InspectorFrontendHost::backendCommandsURL() |
275 | { |
276 | return m_client ? m_client->backendCommandsURL() : String(); |
277 | } |
278 | |
279 | String InspectorFrontendHost::debuggableType() |
280 | { |
281 | return m_client ? m_client->debuggableType() : String(); |
282 | } |
283 | |
284 | unsigned InspectorFrontendHost::inspectionLevel() |
285 | { |
286 | return m_client ? m_client->inspectionLevel() : 1; |
287 | } |
288 | |
289 | String InspectorFrontendHost::platform() |
290 | { |
291 | #if PLATFORM(MAC) || PLATFORM(IOS_FAMILY) |
292 | return "mac"_s ; |
293 | #elif OS(WINDOWS) |
294 | return "windows"_s ; |
295 | #elif OS(LINUX) |
296 | return "linux"_s ; |
297 | #elif OS(FREEBSD) |
298 | return "freebsd"_s ; |
299 | #elif OS(OPENBSD) |
300 | return "openbsd"_s ; |
301 | #else |
302 | return "unknown"_s ; |
303 | #endif |
304 | } |
305 | |
306 | String InspectorFrontendHost::port() |
307 | { |
308 | #if PLATFORM(GTK) |
309 | return "gtk"_s ; |
310 | #else |
311 | return "unknown"_s ; |
312 | #endif |
313 | } |
314 | |
315 | void InspectorFrontendHost::copyText(const String& text) |
316 | { |
317 | Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace); |
318 | } |
319 | |
320 | void InspectorFrontendHost::killText(const String& text, bool shouldPrependToKillRing, bool shouldStartNewSequence) |
321 | { |
322 | if (!m_frontendPage) |
323 | return; |
324 | |
325 | Editor& editor = m_frontendPage->focusController().focusedOrMainFrame().editor(); |
326 | editor.setStartNewKillRingSequence(shouldStartNewSequence); |
327 | Editor::KillRingInsertionMode insertionMode = shouldPrependToKillRing ? Editor::KillRingInsertionMode::PrependText : Editor::KillRingInsertionMode::AppendText; |
328 | editor.addTextToKillRing(text, insertionMode); |
329 | } |
330 | |
331 | void InspectorFrontendHost::openInNewTab(const String& url) |
332 | { |
333 | if (WTF::protocolIsJavaScript(url)) |
334 | return; |
335 | |
336 | if (m_client) |
337 | m_client->openInNewTab(url); |
338 | } |
339 | |
340 | bool InspectorFrontendHost::canSave() |
341 | { |
342 | if (m_client) |
343 | return m_client->canSave(); |
344 | return false; |
345 | } |
346 | |
347 | void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs) |
348 | { |
349 | if (m_client) |
350 | m_client->save(url, content, base64Encoded, forceSaveAs); |
351 | } |
352 | |
353 | void InspectorFrontendHost::append(const String& url, const String& content) |
354 | { |
355 | if (m_client) |
356 | m_client->append(url, content); |
357 | } |
358 | |
359 | void InspectorFrontendHost::close(const String&) |
360 | { |
361 | } |
362 | |
363 | void InspectorFrontendHost::sendMessageToBackend(const String& message) |
364 | { |
365 | if (m_client) |
366 | m_client->sendMessageToBackend(message); |
367 | } |
368 | |
369 | #if ENABLE(CONTEXT_MENUS) |
370 | |
371 | static void (Vector<InspectorFrontendHost::ContextMenuItem>&& items, ContextMenu& ) |
372 | { |
373 | for (auto& item : items) { |
374 | if (item.type == "separator" ) { |
375 | menu.appendItem({ SeparatorType, ContextMenuItemTagNoAction, { } }); |
376 | continue; |
377 | } |
378 | |
379 | if (item.type == "subMenu" && item.subItems) { |
380 | ContextMenu ; |
381 | populateContextMenu(WTFMove(*item.subItems), subMenu); |
382 | |
383 | menu.appendItem({ SubmenuType, ContextMenuItemTagNoAction, item.label, &subMenu }); |
384 | continue; |
385 | } |
386 | |
387 | auto type = item.type == "checkbox" ? CheckableActionType : ActionType; |
388 | auto action = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + item.id.valueOr(0)); |
389 | ContextMenuItem = { type, action, item.label }; |
390 | if (item.enabled) |
391 | menuItem.setEnabled(*item.enabled); |
392 | if (item.checked) |
393 | menuItem.setChecked(*item.checked); |
394 | menu.appendItem(menuItem); |
395 | } |
396 | } |
397 | #endif |
398 | |
399 | void InspectorFrontendHost::(Event& event, Vector<ContextMenuItem>&& items) |
400 | { |
401 | #if ENABLE(CONTEXT_MENUS) |
402 | ASSERT(m_frontendPage); |
403 | |
404 | auto& state = *execStateFromPage(debuggerWorld(), m_frontendPage); |
405 | auto value = state.lexicalGlobalObject()->get(&state, JSC::Identifier::fromString(&state.vm(), "InspectorFrontendAPI" )); |
406 | ASSERT(value); |
407 | ASSERT(value.isObject()); |
408 | auto* frontendAPIObject = asObject(value); |
409 | |
410 | ContextMenu ; |
411 | populateContextMenu(WTFMove(items), menu); |
412 | |
413 | auto = FrontendMenuProvider::create(this, { &state, frontendAPIObject }, menu.items()); |
414 | m_menuProvider = menuProvider.ptr(); |
415 | m_frontendPage->contextMenuController().showContextMenu(event, menuProvider); |
416 | #else |
417 | UNUSED_PARAM(event); |
418 | UNUSED_PARAM(items); |
419 | #endif |
420 | } |
421 | |
422 | void InspectorFrontendHost::(Event& event) |
423 | { |
424 | #if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS) |
425 | if (!is<MouseEvent>(event)) |
426 | return; |
427 | |
428 | auto& mouseEvent = downcast<MouseEvent>(event); |
429 | auto& frame = *downcast<Node>(mouseEvent.target())->document().frame(); |
430 | m_frontendPage->contextMenuController().showContextMenuAt(frame, roundedIntPoint(mouseEvent.absoluteLocation())); |
431 | #else |
432 | UNUSED_PARAM(event); |
433 | #endif |
434 | } |
435 | |
436 | bool InspectorFrontendHost::isUnderTest() |
437 | { |
438 | return m_client && m_client->isUnderTest(); |
439 | } |
440 | |
441 | void InspectorFrontendHost::unbufferedLog(const String& message) |
442 | { |
443 | // This is used only for debugging inspector tests. |
444 | WTFLogAlways("%s" , message.utf8().data()); |
445 | } |
446 | |
447 | void InspectorFrontendHost::beep() |
448 | { |
449 | PAL::systemBeep(); |
450 | } |
451 | |
452 | void InspectorFrontendHost::inspectInspector() |
453 | { |
454 | if (m_frontendPage) |
455 | m_frontendPage->inspectorController().show(); |
456 | } |
457 | |
458 | bool InspectorFrontendHost::isBeingInspected() |
459 | { |
460 | if (!m_frontendPage) |
461 | return false; |
462 | |
463 | InspectorController& inspectorController = m_frontendPage->inspectorController(); |
464 | return inspectorController.hasLocalFrontend() || inspectorController.hasRemoteFrontend(); |
465 | } |
466 | |
467 | bool InspectorFrontendHost::supportsShowCertificate() const |
468 | { |
469 | #if PLATFORM(COCOA) |
470 | return true; |
471 | #else |
472 | return false; |
473 | #endif |
474 | } |
475 | |
476 | bool InspectorFrontendHost::showCertificate(const String& serializedCertificate) |
477 | { |
478 | if (!m_client) |
479 | return false; |
480 | |
481 | Vector<uint8_t> data; |
482 | if (!base64Decode(serializedCertificate, data)) |
483 | return false; |
484 | |
485 | CertificateInfo certificateInfo; |
486 | WTF::Persistence::Decoder decoder(data.data(), data.size()); |
487 | if (!decoder.decode(certificateInfo)) |
488 | return false; |
489 | |
490 | if (certificateInfo.isEmpty()) |
491 | return false; |
492 | |
493 | m_client->showCertificate(certificateInfo); |
494 | return true; |
495 | } |
496 | |
497 | } // namespace WebCore |
498 | |