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