1 | /* |
2 | * Copyright (C) 2006-2017 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) |
4 | * Copyright (C) 2012, Samsung Electronics. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | #include "Chrome.h" |
24 | |
25 | #include "ChromeClient.h" |
26 | #include "DOMWindow.h" |
27 | #include "Document.h" |
28 | #include "DocumentType.h" |
29 | #include "FileChooser.h" |
30 | #include "FileIconLoader.h" |
31 | #include "FileList.h" |
32 | #include "FloatRect.h" |
33 | #include "Frame.h" |
34 | #include "FrameLoaderClient.h" |
35 | #include "FrameTree.h" |
36 | #include "Geolocation.h" |
37 | #include "HTMLFormElement.h" |
38 | #include "HTMLInputElement.h" |
39 | #include "HTMLNames.h" |
40 | #include "HitTestResult.h" |
41 | #include "Icon.h" |
42 | #include "InspectorInstrumentation.h" |
43 | #include "Page.h" |
44 | #include "PageGroupLoadDeferrer.h" |
45 | #include "PopupOpeningObserver.h" |
46 | #include "RenderObject.h" |
47 | #include "ResourceHandle.h" |
48 | #include "Settings.h" |
49 | #include "ShareData.h" |
50 | #include "StorageNamespace.h" |
51 | #include "WindowFeatures.h" |
52 | #include <JavaScriptCore/VM.h> |
53 | #include <wtf/SetForScope.h> |
54 | #include <wtf/Vector.h> |
55 | |
56 | #if ENABLE(INPUT_TYPE_COLOR) |
57 | #include "ColorChooser.h" |
58 | #endif |
59 | |
60 | #if ENABLE(DATALIST_ELEMENT) |
61 | #include "DataListSuggestionPicker.h" |
62 | #endif |
63 | |
64 | #if PLATFORM(MAC) && ENABLE(GRAPHICS_CONTEXT_3D) |
65 | #include "GraphicsContext3DManager.h" |
66 | #endif |
67 | |
68 | namespace WebCore { |
69 | |
70 | using namespace HTMLNames; |
71 | |
72 | Chrome::Chrome(Page& page, ChromeClient& client) |
73 | : m_page(page) |
74 | , m_client(client) |
75 | { |
76 | } |
77 | |
78 | Chrome::~Chrome() |
79 | { |
80 | m_client.chromeDestroyed(); |
81 | } |
82 | |
83 | void Chrome::invalidateRootView(const IntRect& updateRect) |
84 | { |
85 | m_client.invalidateRootView(updateRect); |
86 | } |
87 | |
88 | void Chrome::invalidateContentsAndRootView(const IntRect& updateRect) |
89 | { |
90 | m_client.invalidateContentsAndRootView(updateRect); |
91 | } |
92 | |
93 | void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect) |
94 | { |
95 | m_client.invalidateContentsForSlowScroll(updateRect); |
96 | } |
97 | |
98 | void Chrome::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) |
99 | { |
100 | m_client.scroll(scrollDelta, rectToScroll, clipRect); |
101 | InspectorInstrumentation::didScroll(m_page); |
102 | } |
103 | |
104 | IntPoint Chrome::screenToRootView(const IntPoint& point) const |
105 | { |
106 | return m_client.screenToRootView(point); |
107 | } |
108 | |
109 | IntRect Chrome::rootViewToScreen(const IntRect& rect) const |
110 | { |
111 | return m_client.rootViewToScreen(rect); |
112 | } |
113 | |
114 | IntPoint Chrome::accessibilityScreenToRootView(const IntPoint& point) const |
115 | { |
116 | return m_client.accessibilityScreenToRootView(point); |
117 | } |
118 | |
119 | IntRect Chrome::rootViewToAccessibilityScreen(const IntRect& rect) const |
120 | { |
121 | return m_client.rootViewToAccessibilityScreen(rect); |
122 | } |
123 | |
124 | PlatformPageClient Chrome::platformPageClient() const |
125 | { |
126 | return m_client.platformPageClient(); |
127 | } |
128 | |
129 | void Chrome::contentsSizeChanged(Frame& frame, const IntSize& size) const |
130 | { |
131 | m_client.contentsSizeChanged(frame, size); |
132 | } |
133 | |
134 | void Chrome::scrollRectIntoView(const IntRect& rect) const |
135 | { |
136 | m_client.scrollRectIntoView(rect); |
137 | } |
138 | |
139 | void Chrome::setWindowRect(const FloatRect& rect) const |
140 | { |
141 | m_client.setWindowRect(rect); |
142 | } |
143 | |
144 | FloatRect Chrome::windowRect() const |
145 | { |
146 | return m_client.windowRect(); |
147 | } |
148 | |
149 | FloatRect Chrome::() const |
150 | { |
151 | return m_client.pageRect(); |
152 | } |
153 | |
154 | void Chrome::focus() const |
155 | { |
156 | m_client.focus(); |
157 | } |
158 | |
159 | void Chrome::unfocus() const |
160 | { |
161 | m_client.unfocus(); |
162 | } |
163 | |
164 | bool Chrome::canTakeFocus(FocusDirection direction) const |
165 | { |
166 | return m_client.canTakeFocus(direction); |
167 | } |
168 | |
169 | void Chrome::takeFocus(FocusDirection direction) const |
170 | { |
171 | m_client.takeFocus(direction); |
172 | } |
173 | |
174 | void Chrome::focusedElementChanged(Element* element) const |
175 | { |
176 | m_client.focusedElementChanged(element); |
177 | } |
178 | |
179 | void Chrome::focusedFrameChanged(Frame* frame) const |
180 | { |
181 | m_client.focusedFrameChanged(frame); |
182 | } |
183 | |
184 | Page* Chrome::createWindow(Frame& frame, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction& action) const |
185 | { |
186 | Page* newPage = m_client.createWindow(frame, request, features, action); |
187 | if (!newPage) |
188 | return nullptr; |
189 | |
190 | if (auto* oldSessionStorage = m_page.sessionStorage(false)) |
191 | newPage->setSessionStorage(oldSessionStorage->copy(newPage)); |
192 | if (auto* oldEphemeralLocalStorage = m_page.ephemeralLocalStorage(false)) |
193 | newPage->setEphemeralLocalStorage(oldEphemeralLocalStorage->copy(newPage)); |
194 | |
195 | return newPage; |
196 | } |
197 | |
198 | void Chrome::show() const |
199 | { |
200 | m_client.show(); |
201 | } |
202 | |
203 | bool Chrome::canRunModal() const |
204 | { |
205 | return m_client.canRunModal(); |
206 | } |
207 | |
208 | void Chrome::runModal() const |
209 | { |
210 | // Defer callbacks in all the other pages in this group, so we don't try to run JavaScript |
211 | // in a way that could interact with this view. |
212 | PageGroupLoadDeferrer deferrer(m_page, false); |
213 | |
214 | // JavaScript that runs within the nested event loop must not be run in the context of the |
215 | // script that called showModalDialog. Null out entryScope to break the connection. |
216 | SetForScope<JSC::VMEntryScope*> entryScopeNullifier { m_page.mainFrame().document()->vm().entryScope, nullptr }; |
217 | |
218 | TimerBase::fireTimersInNestedEventLoop(); |
219 | m_client.runModal(); |
220 | } |
221 | |
222 | void Chrome::setToolbarsVisible(bool b) const |
223 | { |
224 | m_client.setToolbarsVisible(b); |
225 | } |
226 | |
227 | bool Chrome::toolbarsVisible() const |
228 | { |
229 | return m_client.toolbarsVisible(); |
230 | } |
231 | |
232 | void Chrome::setStatusbarVisible(bool b) const |
233 | { |
234 | m_client.setStatusbarVisible(b); |
235 | } |
236 | |
237 | bool Chrome::statusbarVisible() const |
238 | { |
239 | return m_client.statusbarVisible(); |
240 | } |
241 | |
242 | void Chrome::setScrollbarsVisible(bool b) const |
243 | { |
244 | m_client.setScrollbarsVisible(b); |
245 | } |
246 | |
247 | bool Chrome::scrollbarsVisible() const |
248 | { |
249 | return m_client.scrollbarsVisible(); |
250 | } |
251 | |
252 | void Chrome::(bool b) const |
253 | { |
254 | m_client.setMenubarVisible(b); |
255 | } |
256 | |
257 | bool Chrome::() const |
258 | { |
259 | return m_client.menubarVisible(); |
260 | } |
261 | |
262 | void Chrome::setResizable(bool b) const |
263 | { |
264 | m_client.setResizable(b); |
265 | } |
266 | |
267 | bool Chrome::canRunBeforeUnloadConfirmPanel() |
268 | { |
269 | return m_client.canRunBeforeUnloadConfirmPanel(); |
270 | } |
271 | |
272 | bool Chrome::runBeforeUnloadConfirmPanel(const String& message, Frame& frame) |
273 | { |
274 | // Defer loads in case the client method runs a new event loop that would |
275 | // otherwise cause the load to continue while we're in the middle of executing JavaScript. |
276 | PageGroupLoadDeferrer deferrer(m_page, true); |
277 | |
278 | return m_client.runBeforeUnloadConfirmPanel(message, frame); |
279 | } |
280 | |
281 | void Chrome::closeWindowSoon() |
282 | { |
283 | m_client.closeWindowSoon(); |
284 | } |
285 | |
286 | void Chrome::runJavaScriptAlert(Frame& frame, const String& message) |
287 | { |
288 | // Defer loads in case the client method runs a new event loop that would |
289 | // otherwise cause the load to continue while we're in the middle of executing JavaScript. |
290 | PageGroupLoadDeferrer deferrer(m_page, true); |
291 | |
292 | notifyPopupOpeningObservers(); |
293 | String displayMessage = frame.displayStringModifiedByEncoding(message); |
294 | |
295 | m_client.runJavaScriptAlert(frame, displayMessage); |
296 | } |
297 | |
298 | bool Chrome::runJavaScriptConfirm(Frame& frame, const String& message) |
299 | { |
300 | // Defer loads in case the client method runs a new event loop that would |
301 | // otherwise cause the load to continue while we're in the middle of executing JavaScript. |
302 | PageGroupLoadDeferrer deferrer(m_page, true); |
303 | |
304 | notifyPopupOpeningObservers(); |
305 | return m_client.runJavaScriptConfirm(frame, frame.displayStringModifiedByEncoding(message)); |
306 | } |
307 | |
308 | bool Chrome::runJavaScriptPrompt(Frame& frame, const String& prompt, const String& defaultValue, String& result) |
309 | { |
310 | // Defer loads in case the client method runs a new event loop that would |
311 | // otherwise cause the load to continue while we're in the middle of executing JavaScript. |
312 | PageGroupLoadDeferrer deferrer(m_page, true); |
313 | |
314 | notifyPopupOpeningObservers(); |
315 | String displayPrompt = frame.displayStringModifiedByEncoding(prompt); |
316 | |
317 | bool ok = m_client.runJavaScriptPrompt(frame, displayPrompt, frame.displayStringModifiedByEncoding(defaultValue), result); |
318 | if (ok) |
319 | result = frame.displayStringModifiedByEncoding(result); |
320 | |
321 | return ok; |
322 | } |
323 | |
324 | void Chrome::setStatusbarText(Frame& frame, const String& status) |
325 | { |
326 | m_client.setStatusbarText(frame.displayStringModifiedByEncoding(status)); |
327 | } |
328 | |
329 | void Chrome::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags) |
330 | { |
331 | if (result.innerNode() && result.innerNode()->document().isDNSPrefetchEnabled()) |
332 | m_page.mainFrame().loader().client().prefetchDNS(result.absoluteLinkURL().host().toString()); |
333 | m_client.mouseDidMoveOverElement(result, modifierFlags); |
334 | |
335 | InspectorInstrumentation::mouseDidMoveOverElement(m_page, result, modifierFlags); |
336 | } |
337 | |
338 | void Chrome::setToolTip(const HitTestResult& result) |
339 | { |
340 | // First priority is a potential toolTip representing a spelling or grammar error |
341 | TextDirection toolTipDirection; |
342 | String toolTip = result.spellingToolTip(toolTipDirection); |
343 | |
344 | // Next priority is a toolTip from a URL beneath the mouse (if preference is set to show those). |
345 | if (toolTip.isEmpty() && m_page.settings().showsURLsInToolTips()) { |
346 | if (Element* element = result.innerNonSharedElement()) { |
347 | // Get tooltip representing form action, if relevant |
348 | if (is<HTMLInputElement>(*element)) { |
349 | HTMLInputElement& input = downcast<HTMLInputElement>(*element); |
350 | if (input.isSubmitButton()) { |
351 | if (HTMLFormElement* form = input.form()) { |
352 | toolTip = form->action(); |
353 | if (form->renderer()) |
354 | toolTipDirection = form->renderer()->style().direction(); |
355 | else |
356 | toolTipDirection = TextDirection::LTR; |
357 | } |
358 | } |
359 | } |
360 | } |
361 | |
362 | // Get tooltip representing link's URL |
363 | if (toolTip.isEmpty()) { |
364 | // FIXME: Need to pass this URL through userVisibleString once that's in WebCore |
365 | toolTip = result.absoluteLinkURL().string(); |
366 | // URL always display as LTR. |
367 | toolTipDirection = TextDirection::LTR; |
368 | } |
369 | } |
370 | |
371 | // Next we'll consider a tooltip for element with "title" attribute |
372 | if (toolTip.isEmpty()) |
373 | toolTip = result.title(toolTipDirection); |
374 | |
375 | if (toolTip.isEmpty() && m_page.settings().showsToolTipOverTruncatedText()) |
376 | toolTip = result.innerTextIfTruncated(toolTipDirection); |
377 | |
378 | // Lastly, for <input type="file"> that allow multiple files, we'll consider a tooltip for the selected filenames |
379 | if (toolTip.isEmpty()) { |
380 | if (Element* element = result.innerNonSharedElement()) { |
381 | if (is<HTMLInputElement>(*element)) { |
382 | toolTip = downcast<HTMLInputElement>(*element).defaultToolTip(); |
383 | |
384 | // FIXME: We should obtain text direction of tooltip from |
385 | // ChromeClient or platform. As of October 2011, all client |
386 | // implementations don't use text direction information for |
387 | // ChromeClient::setToolTip. We'll work on tooltip text |
388 | // direction during bidi cleanup in form inputs. |
389 | toolTipDirection = TextDirection::LTR; |
390 | } |
391 | } |
392 | } |
393 | |
394 | m_client.setToolTip(toolTip, toolTipDirection); |
395 | } |
396 | |
397 | bool Chrome::print(Frame& frame) |
398 | { |
399 | // FIXME: This should have PageGroupLoadDeferrer, like runModal() or runJavaScriptAlert(), because it's no different from those. |
400 | |
401 | if (frame.document()->isSandboxed(SandboxModals)) { |
402 | frame.document()->domWindow()->printErrorMessage("Use of window.print is not allowed in a sandboxed frame when the allow-modals flag is not set." ); |
403 | return false; |
404 | } |
405 | |
406 | m_client.print(frame); |
407 | return true; |
408 | } |
409 | |
410 | void Chrome::enableSuddenTermination() |
411 | { |
412 | m_client.enableSuddenTermination(); |
413 | } |
414 | |
415 | void Chrome::disableSuddenTermination() |
416 | { |
417 | m_client.disableSuddenTermination(); |
418 | } |
419 | |
420 | #if ENABLE(INPUT_TYPE_COLOR) |
421 | |
422 | std::unique_ptr<ColorChooser> Chrome::createColorChooser(ColorChooserClient& client, const Color& initialColor) |
423 | { |
424 | #if PLATFORM(IOS_FAMILY) |
425 | return nullptr; |
426 | #endif |
427 | notifyPopupOpeningObservers(); |
428 | return m_client.createColorChooser(client, initialColor); |
429 | } |
430 | |
431 | #endif |
432 | |
433 | #if ENABLE(DATALIST_ELEMENT) |
434 | |
435 | std::unique_ptr<DataListSuggestionPicker> Chrome::createDataListSuggestionPicker(DataListSuggestionsClient& client) |
436 | { |
437 | notifyPopupOpeningObservers(); |
438 | return m_client.createDataListSuggestionPicker(client); |
439 | } |
440 | |
441 | #endif |
442 | |
443 | void Chrome::runOpenPanel(Frame& frame, FileChooser& fileChooser) |
444 | { |
445 | notifyPopupOpeningObservers(); |
446 | m_client.runOpenPanel(frame, fileChooser); |
447 | } |
448 | |
449 | void Chrome::showShareSheet(ShareDataWithParsedURL& shareData, CompletionHandler<void(bool)>&& callback) |
450 | { |
451 | m_client.showShareSheet(shareData, WTFMove(callback)); |
452 | } |
453 | |
454 | void Chrome::loadIconForFiles(const Vector<String>& filenames, FileIconLoader& loader) |
455 | { |
456 | m_client.loadIconForFiles(filenames, loader); |
457 | } |
458 | |
459 | FloatSize Chrome::screenSize() const |
460 | { |
461 | return m_client.screenSize(); |
462 | } |
463 | |
464 | FloatSize Chrome::availableScreenSize() const |
465 | { |
466 | return m_client.availableScreenSize(); |
467 | } |
468 | |
469 | FloatSize Chrome::overrideScreenSize() const |
470 | { |
471 | return m_client.overrideScreenSize(); |
472 | } |
473 | |
474 | void Chrome::dispatchDisabledAdaptationsDidChange(const OptionSet<DisabledAdaptations>& disabledAdaptations) const |
475 | { |
476 | m_client.dispatchDisabledAdaptationsDidChange(disabledAdaptations); |
477 | } |
478 | |
479 | void Chrome::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const |
480 | { |
481 | #if PLATFORM(IOS_FAMILY) |
482 | if (m_isDispatchViewportDataDidChangeSuppressed) |
483 | return; |
484 | #endif |
485 | m_client.dispatchViewportPropertiesDidChange(arguments); |
486 | } |
487 | |
488 | void Chrome::setCursor(const Cursor& cursor) |
489 | { |
490 | #if ENABLE(CURSOR_SUPPORT) |
491 | m_client.setCursor(cursor); |
492 | #else |
493 | UNUSED_PARAM(cursor); |
494 | #endif |
495 | } |
496 | |
497 | void Chrome::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves) |
498 | { |
499 | #if ENABLE(CURSOR_SUPPORT) |
500 | m_client.setCursorHiddenUntilMouseMoves(hiddenUntilMouseMoves); |
501 | #else |
502 | UNUSED_PARAM(hiddenUntilMouseMoves); |
503 | #endif |
504 | } |
505 | |
506 | PlatformDisplayID Chrome::displayID() const |
507 | { |
508 | return m_displayID; |
509 | } |
510 | |
511 | void Chrome::windowScreenDidChange(PlatformDisplayID displayID) |
512 | { |
513 | if (displayID == m_displayID) |
514 | return; |
515 | |
516 | m_displayID = displayID; |
517 | |
518 | for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
519 | if (frame->document()) |
520 | frame->document()->windowScreenDidChange(displayID); |
521 | } |
522 | |
523 | #if PLATFORM(MAC) && ENABLE(GRAPHICS_CONTEXT_3D) |
524 | GraphicsContext3DManager::sharedManager().screenDidChange(displayID, this); |
525 | #endif |
526 | } |
527 | |
528 | #if ENABLE(DASHBOARD_SUPPORT) |
529 | void ChromeClient::annotatedRegionsChanged() |
530 | { |
531 | } |
532 | #endif |
533 | |
534 | bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&) |
535 | { |
536 | return false; |
537 | } |
538 | |
539 | String ChromeClient::generateReplacementFile(const String&) |
540 | { |
541 | ASSERT_NOT_REACHED(); |
542 | return String(); |
543 | } |
544 | |
545 | bool Chrome::selectItemWritingDirectionIsNatural() |
546 | { |
547 | return m_client.selectItemWritingDirectionIsNatural(); |
548 | } |
549 | |
550 | bool Chrome::() |
551 | { |
552 | return m_client.selectItemAlignmentFollowsMenuWritingDirection(); |
553 | } |
554 | |
555 | RefPtr<PopupMenu> Chrome::(PopupMenuClient& client) const |
556 | { |
557 | notifyPopupOpeningObservers(); |
558 | return m_client.createPopupMenu(client); |
559 | } |
560 | |
561 | RefPtr<SearchPopupMenu> Chrome::(PopupMenuClient& client) const |
562 | { |
563 | notifyPopupOpeningObservers(); |
564 | return m_client.createSearchPopupMenu(client); |
565 | } |
566 | |
567 | bool Chrome::requiresFullscreenForVideoPlayback() |
568 | { |
569 | return m_client.requiresFullscreenForVideoPlayback(); |
570 | } |
571 | |
572 | void Chrome::didReceiveDocType(Frame& frame) |
573 | { |
574 | #if !PLATFORM(IOS_FAMILY) |
575 | UNUSED_PARAM(frame); |
576 | #else |
577 | if (!frame.isMainFrame()) |
578 | return; |
579 | |
580 | auto* doctype = frame.document()->doctype(); |
581 | m_client.didReceiveMobileDocType(doctype && doctype->publicId().containsIgnoringASCIICase("xhtml mobile" )); |
582 | #endif |
583 | } |
584 | |
585 | void Chrome::(PopupOpeningObserver& observer) |
586 | { |
587 | m_popupOpeningObservers.append(&observer); |
588 | } |
589 | |
590 | void Chrome::(PopupOpeningObserver& observer) |
591 | { |
592 | bool removed = m_popupOpeningObservers.removeFirst(&observer); |
593 | ASSERT_UNUSED(removed, removed); |
594 | } |
595 | |
596 | void Chrome::() const |
597 | { |
598 | const Vector<PopupOpeningObserver*> observers(m_popupOpeningObservers); |
599 | for (auto& observer : observers) |
600 | observer->willOpenPopup(); |
601 | } |
602 | |
603 | } // namespace WebCore |
604 | |