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
68namespace WebCore {
69
70using namespace HTMLNames;
71
72Chrome::Chrome(Page& page, ChromeClient& client)
73 : m_page(page)
74 , m_client(client)
75{
76}
77
78Chrome::~Chrome()
79{
80 m_client.chromeDestroyed();
81}
82
83void Chrome::invalidateRootView(const IntRect& updateRect)
84{
85 m_client.invalidateRootView(updateRect);
86}
87
88void Chrome::invalidateContentsAndRootView(const IntRect& updateRect)
89{
90 m_client.invalidateContentsAndRootView(updateRect);
91}
92
93void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect)
94{
95 m_client.invalidateContentsForSlowScroll(updateRect);
96}
97
98void 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
104IntPoint Chrome::screenToRootView(const IntPoint& point) const
105{
106 return m_client.screenToRootView(point);
107}
108
109IntRect Chrome::rootViewToScreen(const IntRect& rect) const
110{
111 return m_client.rootViewToScreen(rect);
112}
113
114IntPoint Chrome::accessibilityScreenToRootView(const IntPoint& point) const
115{
116 return m_client.accessibilityScreenToRootView(point);
117}
118
119IntRect Chrome::rootViewToAccessibilityScreen(const IntRect& rect) const
120{
121 return m_client.rootViewToAccessibilityScreen(rect);
122}
123
124PlatformPageClient Chrome::platformPageClient() const
125{
126 return m_client.platformPageClient();
127}
128
129void Chrome::contentsSizeChanged(Frame& frame, const IntSize& size) const
130{
131 m_client.contentsSizeChanged(frame, size);
132}
133
134void Chrome::scrollRectIntoView(const IntRect& rect) const
135{
136 m_client.scrollRectIntoView(rect);
137}
138
139void Chrome::setWindowRect(const FloatRect& rect) const
140{
141 m_client.setWindowRect(rect);
142}
143
144FloatRect Chrome::windowRect() const
145{
146 return m_client.windowRect();
147}
148
149FloatRect Chrome::pageRect() const
150{
151 return m_client.pageRect();
152}
153
154void Chrome::focus() const
155{
156 m_client.focus();
157}
158
159void Chrome::unfocus() const
160{
161 m_client.unfocus();
162}
163
164bool Chrome::canTakeFocus(FocusDirection direction) const
165{
166 return m_client.canTakeFocus(direction);
167}
168
169void Chrome::takeFocus(FocusDirection direction) const
170{
171 m_client.takeFocus(direction);
172}
173
174void Chrome::focusedElementChanged(Element* element) const
175{
176 m_client.focusedElementChanged(element);
177}
178
179void Chrome::focusedFrameChanged(Frame* frame) const
180{
181 m_client.focusedFrameChanged(frame);
182}
183
184Page* 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
198void Chrome::show() const
199{
200 m_client.show();
201}
202
203bool Chrome::canRunModal() const
204{
205 return m_client.canRunModal();
206}
207
208void 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
222void Chrome::setToolbarsVisible(bool b) const
223{
224 m_client.setToolbarsVisible(b);
225}
226
227bool Chrome::toolbarsVisible() const
228{
229 return m_client.toolbarsVisible();
230}
231
232void Chrome::setStatusbarVisible(bool b) const
233{
234 m_client.setStatusbarVisible(b);
235}
236
237bool Chrome::statusbarVisible() const
238{
239 return m_client.statusbarVisible();
240}
241
242void Chrome::setScrollbarsVisible(bool b) const
243{
244 m_client.setScrollbarsVisible(b);
245}
246
247bool Chrome::scrollbarsVisible() const
248{
249 return m_client.scrollbarsVisible();
250}
251
252void Chrome::setMenubarVisible(bool b) const
253{
254 m_client.setMenubarVisible(b);
255}
256
257bool Chrome::menubarVisible() const
258{
259 return m_client.menubarVisible();
260}
261
262void Chrome::setResizable(bool b) const
263{
264 m_client.setResizable(b);
265}
266
267bool Chrome::canRunBeforeUnloadConfirmPanel()
268{
269 return m_client.canRunBeforeUnloadConfirmPanel();
270}
271
272bool 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
281void Chrome::closeWindowSoon()
282{
283 m_client.closeWindowSoon();
284}
285
286void 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
298bool 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
308bool 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
324void Chrome::setStatusbarText(Frame& frame, const String& status)
325{
326 m_client.setStatusbarText(frame.displayStringModifiedByEncoding(status));
327}
328
329void 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
338void 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
397bool 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
410void Chrome::enableSuddenTermination()
411{
412 m_client.enableSuddenTermination();
413}
414
415void Chrome::disableSuddenTermination()
416{
417 m_client.disableSuddenTermination();
418}
419
420#if ENABLE(INPUT_TYPE_COLOR)
421
422std::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
435std::unique_ptr<DataListSuggestionPicker> Chrome::createDataListSuggestionPicker(DataListSuggestionsClient& client)
436{
437 notifyPopupOpeningObservers();
438 return m_client.createDataListSuggestionPicker(client);
439}
440
441#endif
442
443void Chrome::runOpenPanel(Frame& frame, FileChooser& fileChooser)
444{
445 notifyPopupOpeningObservers();
446 m_client.runOpenPanel(frame, fileChooser);
447}
448
449void Chrome::showShareSheet(ShareDataWithParsedURL& shareData, CompletionHandler<void(bool)>&& callback)
450{
451 m_client.showShareSheet(shareData, WTFMove(callback));
452}
453
454void Chrome::loadIconForFiles(const Vector<String>& filenames, FileIconLoader& loader)
455{
456 m_client.loadIconForFiles(filenames, loader);
457}
458
459FloatSize Chrome::screenSize() const
460{
461 return m_client.screenSize();
462}
463
464FloatSize Chrome::availableScreenSize() const
465{
466 return m_client.availableScreenSize();
467}
468
469FloatSize Chrome::overrideScreenSize() const
470{
471 return m_client.overrideScreenSize();
472}
473
474void Chrome::dispatchDisabledAdaptationsDidChange(const OptionSet<DisabledAdaptations>& disabledAdaptations) const
475{
476 m_client.dispatchDisabledAdaptationsDidChange(disabledAdaptations);
477}
478
479void 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
488void 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
497void 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
506PlatformDisplayID Chrome::displayID() const
507{
508 return m_displayID;
509}
510
511void 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
528bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&)
529{
530 return false;
531}
532
533String ChromeClient::generateReplacementFile(const String&)
534{
535 ASSERT_NOT_REACHED();
536 return String();
537}
538
539bool Chrome::selectItemWritingDirectionIsNatural()
540{
541 return m_client.selectItemWritingDirectionIsNatural();
542}
543
544bool Chrome::selectItemAlignmentFollowsMenuWritingDirection()
545{
546 return m_client.selectItemAlignmentFollowsMenuWritingDirection();
547}
548
549RefPtr<PopupMenu> Chrome::createPopupMenu(PopupMenuClient& client) const
550{
551 notifyPopupOpeningObservers();
552 return m_client.createPopupMenu(client);
553}
554
555RefPtr<SearchPopupMenu> Chrome::createSearchPopupMenu(PopupMenuClient& client) const
556{
557 notifyPopupOpeningObservers();
558 return m_client.createSearchPopupMenu(client);
559}
560
561bool Chrome::requiresFullscreenForVideoPlayback()
562{
563 return m_client.requiresFullscreenForVideoPlayback();
564}
565
566void Chrome::didReceiveDocType(Frame& frame)
567{
568#if !PLATFORM(IOS_FAMILY)
569 UNUSED_PARAM(frame);
570#else
571 if (!frame.isMainFrame())
572 return;
573
574 auto* doctype = frame.document()->doctype();
575 m_client.didReceiveMobileDocType(doctype && doctype->publicId().containsIgnoringASCIICase("xhtml mobile"));
576#endif
577}
578
579void Chrome::registerPopupOpeningObserver(PopupOpeningObserver& observer)
580{
581 m_popupOpeningObservers.append(&observer);
582}
583
584void Chrome::unregisterPopupOpeningObserver(PopupOpeningObserver& observer)
585{
586 bool removed = m_popupOpeningObservers.removeFirst(&observer);
587 ASSERT_UNUSED(removed, removed);
588}
589
590void Chrome::notifyPopupOpeningObservers() const
591{
592 const Vector<PopupOpeningObserver*> observers(m_popupOpeningObservers);
593 for (auto& observer : observers)
594 observer->willOpenPopup();
595}
596
597} // namespace WebCore
598