1/*
2 * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "Chrome.h"
23
24#include "ChromeClient.h"
25#include "DNS.h"
26#include "Document.h"
27#include "FileList.h"
28#include "FloatRect.h"
29#include "Frame.h"
30#include "FrameTree.h"
31#include "Geolocation.h"
32#include "HTMLFormElement.h"
33#include "HTMLInputElement.h"
34#include "HTMLNames.h"
35#include "HitTestResult.h"
36#include "InspectorInstrumentation.h"
37#include "Page.h"
38#include "PageGroupLoadDeferrer.h"
39#include "RenderObject.h"
40#include "ResourceHandle.h"
41#include "ScriptController.h"
42#include "SecurityOrigin.h"
43#include "Settings.h"
44#include "WindowFeatures.h"
45#include <wtf/PassRefPtr.h>
46#include <wtf/RefPtr.h>
47#include <wtf/Vector.h>
48
49#if ENABLE(DOM_STORAGE)
50#include "StorageNamespace.h"
51#endif
52
53namespace WebCore {
54
55using namespace HTMLNames;
56using namespace std;
57
58Chrome::Chrome(Page* page, ChromeClient* client)
59    : m_page(page)
60    , m_client(client)
61{
62    ASSERT(m_client);
63}
64
65Chrome::~Chrome()
66{
67    m_client->chromeDestroyed();
68}
69
70void Chrome::invalidateWindow(const IntRect& updateRect, bool immediate)
71{
72    m_client->invalidateWindow(updateRect, immediate);
73}
74
75void Chrome::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate)
76{
77    m_client->invalidateContentsAndWindow(updateRect, immediate);
78}
79
80void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
81{
82    m_client->invalidateContentsForSlowScroll(updateRect, immediate);
83}
84
85void Chrome::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
86{
87    m_client->scroll(scrollDelta, rectToScroll, clipRect);
88}
89
90#if ENABLE(TILED_BACKING_STORE)
91void Chrome::delegatedScrollRequested(const IntPoint& scrollPoint)
92{
93    m_client->delegatedScrollRequested(scrollPoint);
94}
95#endif
96
97IntPoint Chrome::screenToWindow(const IntPoint& point) const
98{
99    return m_client->screenToWindow(point);
100}
101
102IntRect Chrome::windowToScreen(const IntRect& rect) const
103{
104    return m_client->windowToScreen(rect);
105}
106
107PlatformPageClient Chrome::platformPageClient() const
108{
109    return m_client->platformPageClient();
110}
111
112void Chrome::contentsSizeChanged(Frame* frame, const IntSize& size) const
113{
114    m_client->contentsSizeChanged(frame, size);
115}
116
117void Chrome::scrollRectIntoView(const IntRect& rect) const
118{
119    // FIXME: The unused ScrollView* argument can and should be removed from ChromeClient::scrollRectIntoView.
120    m_client->scrollRectIntoView(rect, 0);
121}
122
123void Chrome::scrollbarsModeDidChange() const
124{
125    m_client->scrollbarsModeDidChange();
126}
127
128void Chrome::setWindowRect(const FloatRect& rect) const
129{
130    m_client->setWindowRect(rect);
131}
132
133FloatRect Chrome::windowRect() const
134{
135    return m_client->windowRect();
136}
137
138FloatRect Chrome::pageRect() const
139{
140    return m_client->pageRect();
141}
142
143float Chrome::scaleFactor()
144{
145    return m_client->scaleFactor();
146}
147
148void Chrome::focus() const
149{
150    m_client->focus();
151}
152
153void Chrome::unfocus() const
154{
155    m_client->unfocus();
156}
157
158bool Chrome::canTakeFocus(FocusDirection direction) const
159{
160    return m_client->canTakeFocus(direction);
161}
162
163void Chrome::takeFocus(FocusDirection direction) const
164{
165    m_client->takeFocus(direction);
166}
167
168void Chrome::focusedNodeChanged(Node* node) const
169{
170    m_client->focusedNodeChanged(node);
171}
172
173void Chrome::focusedFrameChanged(Frame* frame) const
174{
175    m_client->focusedFrameChanged(frame);
176}
177
178Page* Chrome::createWindow(Frame* frame, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction& action) const
179{
180    Page* newPage = m_client->createWindow(frame, request, features, action);
181
182#if ENABLE(DOM_STORAGE)
183    if (newPage) {
184        if (StorageNamespace* oldSessionStorage = m_page->sessionStorage(false))
185            newPage->setSessionStorage(oldSessionStorage->copy());
186    }
187#endif
188
189    return newPage;
190}
191
192void Chrome::show() const
193{
194    m_client->show();
195}
196
197bool Chrome::canRunModal() const
198{
199    return m_client->canRunModal();
200}
201
202bool Chrome::canRunModalNow() const
203{
204    // If loads are blocked, we can't run modal because the contents
205    // of the modal dialog will never show up!
206    return canRunModal() && !ResourceHandle::loadsBlocked();
207}
208
209void Chrome::runModal() const
210{
211    // Defer callbacks in all the other pages in this group, so we don't try to run JavaScript
212    // in a way that could interact with this view.
213    PageGroupLoadDeferrer deferrer(m_page, false);
214
215    TimerBase::fireTimersInNestedEventLoop();
216    m_client->runModal();
217}
218
219void Chrome::setToolbarsVisible(bool b) const
220{
221    m_client->setToolbarsVisible(b);
222}
223
224bool Chrome::toolbarsVisible() const
225{
226    return m_client->toolbarsVisible();
227}
228
229void Chrome::setStatusbarVisible(bool b) const
230{
231    m_client->setStatusbarVisible(b);
232}
233
234bool Chrome::statusbarVisible() const
235{
236    return m_client->statusbarVisible();
237}
238
239void Chrome::setScrollbarsVisible(bool b) const
240{
241    m_client->setScrollbarsVisible(b);
242}
243
244bool Chrome::scrollbarsVisible() const
245{
246    return m_client->scrollbarsVisible();
247}
248
249void Chrome::setMenubarVisible(bool b) const
250{
251    m_client->setMenubarVisible(b);
252}
253
254bool Chrome::menubarVisible() const
255{
256    return m_client->menubarVisible();
257}
258
259void Chrome::setResizable(bool b) const
260{
261    m_client->setResizable(b);
262}
263
264bool Chrome::canRunBeforeUnloadConfirmPanel()
265{
266    return m_client->canRunBeforeUnloadConfirmPanel();
267}
268
269bool Chrome::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
270{
271    // Defer loads in case the client method runs a new event loop that would
272    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
273    PageGroupLoadDeferrer deferrer(m_page, true);
274
275    return m_client->runBeforeUnloadConfirmPanel(message, frame);
276}
277
278void Chrome::closeWindowSoon()
279{
280    m_client->closeWindowSoon();
281}
282
283static inline void willRunModalDialog(const Frame* frame, const ChromeClient::DialogType& dialogType, const ChromeClient* client)
284{
285    if (frame->loader()->pageDismissalEventBeingDispatched())
286        client->willRunModalDialogDuringPageDismissal(dialogType);
287}
288
289void Chrome::runJavaScriptAlert(Frame* frame, const String& message)
290{
291    willRunModalDialog(frame, ChromeClient::AlertDialog, m_client);
292
293    // Defer loads in case the client method runs a new event loop that would
294    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
295    PageGroupLoadDeferrer deferrer(m_page, true);
296
297    ASSERT(frame);
298    m_client->runJavaScriptAlert(frame, frame->displayStringModifiedByEncoding(message));
299}
300
301bool Chrome::runJavaScriptConfirm(Frame* frame, const String& message)
302{
303    willRunModalDialog(frame, ChromeClient::ConfirmDialog, m_client);
304
305    // Defer loads in case the client method runs a new event loop that would
306    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
307    PageGroupLoadDeferrer deferrer(m_page, true);
308
309    ASSERT(frame);
310    return m_client->runJavaScriptConfirm(frame, frame->displayStringModifiedByEncoding(message));
311}
312
313bool Chrome::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultValue, String& result)
314{
315    willRunModalDialog(frame, ChromeClient::PromptDialog, m_client);
316
317    // Defer loads in case the client method runs a new event loop that would
318    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
319    PageGroupLoadDeferrer deferrer(m_page, true);
320
321    ASSERT(frame);
322    bool ok = m_client->runJavaScriptPrompt(frame, frame->displayStringModifiedByEncoding(prompt), frame->displayStringModifiedByEncoding(defaultValue), result);
323
324    if (ok)
325        result = frame->displayStringModifiedByEncoding(result);
326
327    return ok;
328}
329
330void Chrome::setStatusbarText(Frame* frame, const String& status)
331{
332    ASSERT(frame);
333    m_client->setStatusbarText(frame->displayStringModifiedByEncoding(status));
334}
335
336bool Chrome::shouldInterruptJavaScript()
337{
338    // Defer loads in case the client method runs a new event loop that would
339    // otherwise cause the load to continue while we're in the middle of executing JavaScript.
340    PageGroupLoadDeferrer deferrer(m_page, true);
341
342    return m_client->shouldInterruptJavaScript();
343}
344
345#if ENABLE(REGISTER_PROTOCOL_HANDLER)
346void Chrome::registerProtocolHandler(const String& scheme, const String& baseURL, const String& url, const String& title)
347{
348    m_client->registerProtocolHandler(scheme, baseURL, url, title);
349}
350#endif
351
352IntRect Chrome::windowResizerRect() const
353{
354    return m_client->windowResizerRect();
355}
356
357void Chrome::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags)
358{
359    if (result.innerNode()) {
360        Document* document = result.innerNode()->document();
361        if (document && document->isDNSPrefetchEnabled())
362            ResourceHandle::prepareForURL(result.absoluteLinkURL());
363    }
364    m_client->mouseDidMoveOverElement(result, modifierFlags);
365
366    InspectorInstrumentation::mouseDidMoveOverElement(m_page, result, modifierFlags);
367}
368
369void Chrome::setToolTip(const HitTestResult& result)
370{
371    // First priority is a potential toolTip representing a spelling or grammar error
372    TextDirection toolTipDirection;
373    String toolTip = result.spellingToolTip(toolTipDirection);
374
375    // Next priority is a toolTip from a URL beneath the mouse (if preference is set to show those).
376    if (toolTip.isEmpty() && m_page->settings()->showsURLsInToolTips()) {
377        if (Node* node = result.innerNonSharedNode()) {
378            // Get tooltip representing form action, if relevant
379            if (node->hasTagName(inputTag)) {
380                HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
381                if (input->isSubmitButton())
382                    if (HTMLFormElement* form = input->form()) {
383                        toolTip = form->action();
384                        if (form->renderer())
385                            toolTipDirection = form->renderer()->style()->direction();
386                        else
387                            toolTipDirection = LTR;
388                    }
389            }
390        }
391
392        // Get tooltip representing link's URL
393        if (toolTip.isEmpty()) {
394            // FIXME: Need to pass this URL through userVisibleString once that's in WebCore
395            toolTip = result.absoluteLinkURL().string();
396            // URL always display as LTR.
397            toolTipDirection = LTR;
398        }
399    }
400
401    // Next we'll consider a tooltip for element with "title" attribute
402    if (toolTip.isEmpty())
403        toolTip = result.title(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 (Node* node = result.innerNonSharedNode()) {
408            if (node->hasTagName(inputTag)) {
409                HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
410                if (input->isFileUpload()) {
411                    FileList* files = input->files();
412                    unsigned listSize = files->length();
413                    if (listSize > 1) {
414                        Vector<UChar> names;
415                        for (size_t i = 0; i < listSize; ++i) {
416                            append(names, files->item(i)->fileName());
417                            if (i != listSize - 1)
418                                names.append('\n');
419                        }
420                        toolTip = String::adopt(names);
421                        // filename always display as LTR.
422                        toolTipDirection = LTR;
423                    }
424                }
425            }
426        }
427    }
428
429    m_client->setToolTip(toolTip, toolTipDirection);
430}
431
432void Chrome::print(Frame* frame)
433{
434    m_client->print(frame);
435}
436
437void Chrome::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
438{
439    m_client->requestGeolocationPermissionForFrame(frame, geolocation);
440}
441
442void Chrome::cancelGeolocationPermissionRequestForFrame(Frame* frame, Geolocation* geolocation)
443{
444    m_client->cancelGeolocationPermissionRequestForFrame(frame, geolocation);
445}
446
447#if ENABLE(DIRECTORY_UPLOAD)
448void Chrome::enumerateChosenDirectory(const String& path, FileChooser* fileChooser)
449{
450    m_client->enumerateChosenDirectory(path, fileChooser);
451}
452#endif
453
454void Chrome::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileChooser)
455{
456    m_client->runOpenPanel(frame, fileChooser);
457}
458
459void Chrome::chooseIconForFiles(const Vector<String>& filenames, FileChooser* fileChooser)
460{
461    m_client->chooseIconForFiles(filenames, fileChooser);
462}
463
464void Chrome::dispatchViewportDataDidChange(const ViewportArguments& arguments) const
465{
466    m_client->dispatchViewportDataDidChange(arguments);
467}
468
469void Chrome::setCursor(const Cursor& cursor)
470{
471    m_client->setCursor(cursor);
472}
473
474#if ENABLE(REQUEST_ANIMATION_FRAME)
475void Chrome::scheduleAnimation()
476{
477    m_client->scheduleAnimation();
478}
479#endif
480
481#if ENABLE(NOTIFICATIONS)
482NotificationPresenter* Chrome::notificationPresenter() const
483{
484    return m_client->notificationPresenter();
485}
486#endif
487
488// --------
489
490#if ENABLE(DASHBOARD_SUPPORT)
491void ChromeClient::dashboardRegionsChanged()
492{
493}
494#endif
495
496void ChromeClient::populateVisitedLinks()
497{
498}
499
500FloatRect ChromeClient::customHighlightRect(Node*, const AtomicString&, const FloatRect&)
501{
502    return FloatRect();
503}
504
505void ChromeClient::paintCustomHighlight(Node*, const AtomicString&, const FloatRect&, const FloatRect&, bool, bool)
506{
507}
508
509bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&)
510{
511    return false;
512}
513
514String ChromeClient::generateReplacementFile(const String&)
515{
516    ASSERT_NOT_REACHED();
517    return String();
518}
519
520bool ChromeClient::paintCustomScrollbar(GraphicsContext*, const FloatRect&, ScrollbarControlSize,
521                                        ScrollbarControlState, ScrollbarPart, bool,
522                                        float, float, ScrollbarControlPartMask)
523{
524    return false;
525}
526
527bool ChromeClient::paintCustomScrollCorner(GraphicsContext*, const FloatRect&)
528{
529    return false;
530}
531
532bool ChromeClient::paintCustomOverhangArea(GraphicsContext*, const IntRect&, const IntRect&, const IntRect&)
533{
534    return false;
535}
536
537bool Chrome::selectItemWritingDirectionIsNatural()
538{
539    return m_client->selectItemWritingDirectionIsNatural();
540}
541
542bool Chrome::selectItemAlignmentFollowsMenuWritingDirection()
543{
544    return m_client->selectItemAlignmentFollowsMenuWritingDirection();
545}
546
547PassRefPtr<PopupMenu> Chrome::createPopupMenu(PopupMenuClient* client) const
548{
549    return m_client->createPopupMenu(client);
550}
551
552PassRefPtr<SearchPopupMenu> Chrome::createSearchPopupMenu(PopupMenuClient* client) const
553{
554    return m_client->createSearchPopupMenu(client);
555}
556
557#if ENABLE(CONTEXT_MENUS)
558void Chrome::showContextMenu()
559{
560    m_client->showContextMenu();
561}
562#endif
563
564bool Chrome::requiresFullscreenForVideoPlayback()
565{
566    return m_client->requiresFullscreenForVideoPlayback();
567}
568
569void Chrome::willRunModalHTMLDialog(const Frame* frame) const
570{
571    willRunModalDialog(frame, ChromeClient::HTMLDialog, m_client);
572}
573
574} // namespace WebCore
575