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