1/*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "WebChromeClient.h"
29
30#include "COMPropertyBag.h"
31#include "COMVariantSetter.h"
32#include "DOMCoreClasses.h"
33#include "WebElementPropertyBag.h"
34#include "WebFrame.h"
35#include "WebHistory.h"
36#include "WebMutableURLRequest.h"
37#include "WebDesktopNotificationsDelegate.h"
38#include "WebSecurityOrigin.h"
39#include "WebView.h"
40#include <WebCore/BString.h>
41#include <WebCore/Console.h>
42#include <WebCore/ContextMenu.h>
43#include <WebCore/Cursor.h>
44#include <WebCore/FileChooser.h>
45#include <WebCore/FileIconLoader.h>
46#include <WebCore/FloatRect.h>
47#include <WebCore/Frame.h>
48#include <WebCore/FrameLoadRequest.h>
49#include <WebCore/FrameView.h>
50#include <WebCore/FullScreenController.h>
51#include <WebCore/HTMLNames.h>
52#include <WebCore/Icon.h>
53#include <WebCore/LocalWindowsContext.h>
54#include <WebCore/LocalizedStrings.h>
55#include <WebCore/NavigationAction.h>
56#include <WebCore/NotImplemented.h>
57#include <WebCore/Page.h>
58#include <WebCore/SecurityOrigin.h>
59#include <WebCore/PopupMenuWin.h>
60#include <WebCore/SearchPopupMenuWin.h>
61#include <WebCore/WindowFeatures.h>
62#include <wchar.h>
63
64#if USE(ACCELERATED_COMPOSITING)
65#include <WebCore/GraphicsLayer.h>
66#endif
67
68using namespace WebCore;
69
70// When you call GetOpenFileName, if the size of the buffer is too small,
71// MSDN says that the first two bytes of the buffer contain the required size for the file selection, in bytes or characters
72// So we can assume the required size can't be more than the maximum value for a short.
73static const size_t maxFilePathsListSize = USHRT_MAX;
74
75WebChromeClient::WebChromeClient(WebView* webView)
76    : m_webView(webView)
77#if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
78    , m_notificationsDelegate(new WebDesktopNotificationsDelegate(webView))
79#endif
80{
81}
82
83void WebChromeClient::chromeDestroyed()
84{
85    delete this;
86}
87
88void WebChromeClient::setWindowRect(const FloatRect& r)
89{
90    IWebUIDelegate* uiDelegate = 0;
91    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
92        RECT rect = IntRect(r);
93        uiDelegate->setFrame(m_webView, &rect);
94        uiDelegate->Release();
95    }
96}
97
98FloatRect WebChromeClient::windowRect()
99{
100    IWebUIDelegate* uiDelegate = 0;
101    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
102        RECT rect;
103        HRESULT retval = uiDelegate->webViewFrame(m_webView, &rect);
104
105        uiDelegate->Release();
106
107        if (SUCCEEDED(retval))
108            return rect;
109    }
110
111    return FloatRect();
112}
113
114FloatRect WebChromeClient::pageRect()
115{
116    RECT rect;
117    m_webView->frameRect(&rect);
118    return rect;
119}
120
121void WebChromeClient::focus()
122{
123    IWebUIDelegate* uiDelegate = 0;
124    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
125        uiDelegate->webViewFocus(m_webView);
126        uiDelegate->Release();
127    }
128    // Normally this would happen on a timer, but JS might need to know this earlier, so we'll update here.
129    m_webView->updateActiveState();
130}
131
132void WebChromeClient::unfocus()
133{
134    IWebUIDelegate* uiDelegate = 0;
135    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
136        uiDelegate->webViewUnfocus(m_webView);
137        uiDelegate->Release();
138    }
139    // Normally this would happen on a timer, but JS might need to know this earlier, so we'll update here.
140    m_webView->updateActiveState();
141}
142
143bool WebChromeClient::canTakeFocus(FocusDirection direction)
144{
145    IWebUIDelegate* uiDelegate = 0;
146    BOOL bForward = (direction == FocusDirectionForward) ? TRUE : FALSE;
147    BOOL result = FALSE;
148    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
149        uiDelegate->canTakeFocus(m_webView, bForward, &result);
150        uiDelegate->Release();
151    }
152
153    return !!result;
154}
155
156void WebChromeClient::takeFocus(FocusDirection direction)
157{
158    IWebUIDelegate* uiDelegate = 0;
159    BOOL bForward = (direction == FocusDirectionForward) ? TRUE : FALSE;
160    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
161        uiDelegate->takeFocus(m_webView, bForward);
162        uiDelegate->Release();
163    }
164}
165
166void WebChromeClient::focusedNodeChanged(Node*)
167{
168}
169
170void WebChromeClient::focusedFrameChanged(Frame*)
171{
172}
173
174static COMPtr<IPropertyBag> createWindowFeaturesPropertyBag(const WindowFeatures& features)
175{
176    HashMap<String, COMVariant> map;
177    if (features.xSet)
178        map.set(WebWindowFeaturesXKey, features.x);
179    if (features.ySet)
180        map.set(WebWindowFeaturesYKey, features.y);
181    if (features.widthSet)
182        map.set(WebWindowFeaturesWidthKey, features.width);
183    if (features.heightSet)
184        map.set(WebWindowFeaturesHeightKey, features.height);
185    map.set(WebWindowFeaturesMenuBarVisibleKey, features.menuBarVisible);
186    map.set(WebWindowFeaturesStatusBarVisibleKey, features.statusBarVisible);
187    map.set(WebWindowFeaturesToolBarVisibleKey, features.toolBarVisible);
188    map.set(WebWindowFeaturesScrollbarsVisibleKey, features.scrollbarsVisible);
189    map.set(WebWindowFeaturesResizableKey, features.resizable);
190    map.set(WebWindowFeaturesFullscreenKey, features.fullscreen);
191    map.set(WebWindowFeaturesDialogKey, features.dialog);
192
193    return COMPtr<IPropertyBag>(AdoptCOM, COMPropertyBag<COMVariant>::adopt(map));
194}
195
196Page* WebChromeClient::createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures& features, const NavigationAction&)
197{
198    COMPtr<IWebUIDelegate> delegate = uiDelegate();
199    if (!delegate)
200        return 0;
201
202    // Just create a blank request because createWindow() is only required to create window but not to load URL.
203    COMPtr<IWebMutableURLRequest> request(AdoptCOM, WebMutableURLRequest::createInstance());
204
205    COMPtr<IWebUIDelegatePrivate2> delegatePrivate(Query, delegate);
206    if (delegatePrivate) {
207        COMPtr<IWebView> newWebView;
208        HRESULT hr = delegatePrivate->createWebViewWithRequest(m_webView, request.get(), createWindowFeaturesPropertyBag(features).get(), &newWebView);
209
210        if (SUCCEEDED(hr) && newWebView)
211            return core(newWebView.get());
212
213        // If the delegate doesn't implement the IWebUIDelegatePrivate2 version of the call, fall back
214        // to the old versions (even if they support the IWebUIDelegatePrivate2 interface).
215        if (hr != E_NOTIMPL)
216            return 0;
217    }
218
219    COMPtr<IWebView> newWebView;
220
221    if (features.dialog) {
222        if (FAILED(delegate->createModalDialog(m_webView, request.get(), &newWebView)))
223            return 0;
224    } else if (FAILED(delegate->createWebViewWithRequest(m_webView, request.get(), &newWebView)))
225        return 0;
226
227    return newWebView ? core(newWebView.get()) : 0;
228}
229
230void WebChromeClient::show()
231{
232    IWebUIDelegate* uiDelegate = 0;
233    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
234        uiDelegate->webViewShow(m_webView);
235        uiDelegate->Release();
236    }
237}
238
239bool WebChromeClient::canRunModal()
240{
241    BOOL result = FALSE;
242    if (COMPtr<IWebUIDelegate> delegate = uiDelegate())
243        delegate->canRunModal(m_webView, &result);
244    return result;
245}
246
247void WebChromeClient::runModal()
248{
249    if (COMPtr<IWebUIDelegate> delegate = uiDelegate())
250        delegate->runModal(m_webView);
251}
252
253void WebChromeClient::setToolbarsVisible(bool visible)
254{
255    IWebUIDelegate* uiDelegate = 0;
256    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
257        uiDelegate->setToolbarsVisible(m_webView, visible);
258        uiDelegate->Release();
259    }
260}
261
262bool WebChromeClient::toolbarsVisible()
263{
264    BOOL result = false;
265    IWebUIDelegate* uiDelegate = 0;
266    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
267        uiDelegate->webViewAreToolbarsVisible(m_webView, &result);
268        uiDelegate->Release();
269    }
270    return result != false;
271}
272
273void WebChromeClient::setStatusbarVisible(bool visible)
274{
275    IWebUIDelegate* uiDelegate = 0;
276    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
277        uiDelegate->setStatusBarVisible(m_webView, visible);
278        uiDelegate->Release();
279    }
280}
281
282bool WebChromeClient::statusbarVisible()
283{
284    BOOL result = false;
285    IWebUIDelegate* uiDelegate = 0;
286    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
287        uiDelegate->webViewIsStatusBarVisible(m_webView, &result);
288        uiDelegate->Release();
289    }
290    return result != false;
291}
292
293void WebChromeClient::setScrollbarsVisible(bool b)
294{
295    WebFrame* webFrame = m_webView->topLevelFrame();
296    if (webFrame)
297        webFrame->setAllowsScrolling(b);
298}
299
300bool WebChromeClient::scrollbarsVisible()
301{
302    WebFrame* webFrame = m_webView->topLevelFrame();
303    BOOL b = false;
304    if (webFrame)
305        webFrame->allowsScrolling(&b);
306
307    return !!b;
308}
309
310void WebChromeClient::setMenubarVisible(bool visible)
311{
312    COMPtr<IWebUIDelegate> delegate = uiDelegate();
313    if (!delegate)
314        return;
315    delegate->setMenuBarVisible(m_webView, visible);
316}
317
318bool WebChromeClient::menubarVisible()
319{
320    COMPtr<IWebUIDelegate> delegate = uiDelegate();
321    if (!delegate)
322        return true;
323    BOOL result = true;
324    delegate->isMenuBarVisible(m_webView, &result);
325    return result;
326}
327
328void WebChromeClient::setResizable(bool resizable)
329{
330    IWebUIDelegate* uiDelegate = 0;
331    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
332        uiDelegate->setResizable(m_webView, resizable);
333        uiDelegate->Release();
334    }
335}
336
337void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& url)
338{
339    UNUSED_PARAM(columnNumber);
340
341    COMPtr<IWebUIDelegate> uiDelegate;
342    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
343        COMPtr<IWebUIDelegatePrivate> uiPrivate;
344        if (SUCCEEDED(uiDelegate->QueryInterface(IID_IWebUIDelegatePrivate, (void**)&uiPrivate)))
345            uiPrivate->webViewAddMessageToConsole(m_webView, BString(message), lineNumber, BString(url), true);
346    }
347}
348
349bool WebChromeClient::canRunBeforeUnloadConfirmPanel()
350{
351    IWebUIDelegate* ui;
352    if (SUCCEEDED(m_webView->uiDelegate(&ui)) && ui) {
353        ui->Release();
354        return true;
355    }
356    return false;
357}
358
359bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
360{
361    BOOL result = TRUE;
362    IWebUIDelegate* ui;
363    if (SUCCEEDED(m_webView->uiDelegate(&ui)) && ui) {
364        WebFrame* webFrame = kit(frame);
365        ui->runBeforeUnloadConfirmPanelWithMessage(m_webView, BString(message), webFrame, &result);
366        ui->Release();
367    }
368    return !!result;
369}
370
371void WebChromeClient::closeWindowSoon()
372{
373    // We need to remove the parent WebView from WebViewSets here, before it actually
374    // closes, to make sure that JavaScript code that executes before it closes
375    // can't find it. Otherwise, window.open will select a closed WebView instead of
376    // opening a new one <rdar://problem/3572585>.
377
378    // We also need to stop the load to prevent further parsing or JavaScript execution
379    // after the window has torn down <rdar://problem/4161660>.
380
381    // FIXME: This code assumes that the UI delegate will respond to a webViewClose
382    // message by actually closing the WebView. Safari guarantees this behavior, but other apps might not.
383    // This approach is an inherent limitation of not making a close execute immediately
384    // after a call to window.close.
385
386    m_webView->setGroupName(0);
387    m_webView->stopLoading(0);
388    m_webView->closeWindowSoon();
389}
390
391void WebChromeClient::runJavaScriptAlert(Frame*, const String& message)
392{
393    COMPtr<IWebUIDelegate> ui;
394    if (SUCCEEDED(m_webView->uiDelegate(&ui)))
395        ui->runJavaScriptAlertPanelWithMessage(m_webView, BString(message));
396}
397
398bool WebChromeClient::runJavaScriptConfirm(Frame*, const String& message)
399{
400    BOOL result = FALSE;
401    COMPtr<IWebUIDelegate> ui;
402    if (SUCCEEDED(m_webView->uiDelegate(&ui)))
403        ui->runJavaScriptConfirmPanelWithMessage(m_webView, BString(message), &result);
404    return !!result;
405}
406
407bool WebChromeClient::runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result)
408{
409    COMPtr<IWebUIDelegate> ui;
410    if (FAILED(m_webView->uiDelegate(&ui)))
411        return false;
412
413    TimerBase::fireTimersInNestedEventLoop();
414
415    BString resultBSTR;
416    if (FAILED(ui->runJavaScriptTextInputPanelWithPrompt(m_webView, BString(message), BString(defaultValue), &resultBSTR)))
417        return false;
418
419    if (!resultBSTR)
420        return false;
421
422    result = String(resultBSTR, SysStringLen(resultBSTR));
423    return true;
424}
425
426void WebChromeClient::setStatusbarText(const String& statusText)
427{
428    COMPtr<IWebUIDelegate> uiDelegate;
429    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
430        uiDelegate->setStatusText(m_webView, BString(statusText));
431    }
432}
433
434bool WebChromeClient::shouldInterruptJavaScript()
435{
436    COMPtr<IWebUIDelegate> uiDelegate;
437    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
438        COMPtr<IWebUIDelegatePrivate> uiPrivate;
439        if (SUCCEEDED(uiDelegate->QueryInterface(IID_IWebUIDelegatePrivate, (void**)&uiPrivate))) {
440            BOOL result;
441            if (SUCCEEDED(uiPrivate->webViewShouldInterruptJavaScript(m_webView, &result)))
442                return !!result;
443        }
444    }
445    return false;
446}
447
448KeyboardUIMode WebChromeClient::keyboardUIMode()
449{
450    BOOL enabled = FALSE;
451    IWebPreferences* preferences;
452    if (SUCCEEDED(m_webView->preferences(&preferences)))
453        preferences->tabsToLinks(&enabled);
454
455    return enabled ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
456}
457
458IntRect WebChromeClient::windowResizerRect() const
459{
460    return IntRect();
461}
462
463void WebChromeClient::invalidateRootView(const IntRect& windowRect, bool immediate)
464{
465    ASSERT(core(m_webView->topLevelFrame()));
466    m_webView->repaint(windowRect, false /*contentChanged*/, immediate, false /*repaintContentOnly*/);
467}
468
469void WebChromeClient::invalidateContentsAndRootView(const IntRect& windowRect, bool immediate)
470{
471    ASSERT(core(m_webView->topLevelFrame()));
472    m_webView->repaint(windowRect, true /*contentChanged*/, immediate /*immediate*/, false /*repaintContentOnly*/);
473}
474
475void WebChromeClient::invalidateContentsForSlowScroll(const IntRect& windowRect, bool immediate)
476{
477    ASSERT(core(m_webView->topLevelFrame()));
478    m_webView->repaint(windowRect, true /*contentChanged*/, immediate, true /*repaintContentOnly*/);
479}
480
481void WebChromeClient::scroll(const IntSize& delta, const IntRect& scrollViewRect, const IntRect& clipRect)
482{
483    ASSERT(core(m_webView->topLevelFrame()));
484
485    m_webView->scrollBackingStore(core(m_webView->topLevelFrame())->view(), delta.width(), delta.height(), scrollViewRect, clipRect);
486}
487
488IntRect WebChromeClient::rootViewToScreen(const IntRect& rect) const
489{
490    HWND viewWindow;
491    if (FAILED(m_webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
492        return rect;
493
494    // Find the top left corner of the Widget's containing window in screen coords,
495    // and adjust the result rect's position by this amount.
496    POINT topLeft = {0, 0};
497    IntRect result = rect;
498    ::ClientToScreen(viewWindow, &topLeft);
499    result.move(topLeft.x, topLeft.y);
500
501    return result;
502}
503
504IntPoint WebChromeClient::screenToRootView(const IntPoint& point) const
505{
506    POINT result = point;
507
508    HWND viewWindow;
509    if (FAILED(m_webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
510        return point;
511
512    ::ScreenToClient(viewWindow, &result);
513
514    return result;
515}
516
517PlatformPageClient WebChromeClient::platformPageClient() const
518{
519    HWND viewWindow;
520    if (FAILED(m_webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
521        return 0;
522    return viewWindow;
523}
524
525void WebChromeClient::contentsSizeChanged(Frame*, const IntSize&) const
526{
527    notImplemented();
528}
529
530void WebChromeClient::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags)
531{
532    COMPtr<IWebUIDelegate> uiDelegate;
533    if (FAILED(m_webView->uiDelegate(&uiDelegate)))
534        return;
535
536    COMPtr<WebElementPropertyBag> element;
537    element.adoptRef(WebElementPropertyBag::createInstance(result));
538
539    uiDelegate->mouseDidMoveOverElement(m_webView, element.get(), modifierFlags);
540}
541
542bool WebChromeClient::shouldUnavailablePluginMessageBeButton(RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
543{
544    if (pluginUnavailabilityReason != RenderEmbeddedObject::PluginMissing)
545        return false;
546
547    COMPtr<IWebUIDelegate> uiDelegate;
548    if (FAILED(m_webView->uiDelegate(&uiDelegate)))
549        return false;
550
551    // If the UI delegate implements IWebUIDelegatePrivate3,
552    // which contains didPressMissingPluginButton, then the message should be a button.
553    COMPtr<IWebUIDelegatePrivate3> uiDelegatePrivate3(Query, uiDelegate);
554    return uiDelegatePrivate3;
555}
556
557void WebChromeClient::unavailablePluginButtonClicked(Element* element, RenderEmbeddedObject::PluginUnavailabilityReason pluginUnavailabilityReason) const
558{
559    ASSERT_UNUSED(pluginUnavailabilityReason, pluginUnavailabilityReason == RenderEmbeddedObject::PluginMissing);
560
561    COMPtr<IWebUIDelegate> uiDelegate;
562    if (FAILED(m_webView->uiDelegate(&uiDelegate)))
563        return;
564
565    COMPtr<IWebUIDelegatePrivate3> uiDelegatePrivate3(Query, uiDelegate);
566    if (!uiDelegatePrivate3)
567        return;
568
569    COMPtr<IDOMElement> e(AdoptCOM, DOMElement::createInstance(element));
570    uiDelegatePrivate3->didPressMissingPluginButton(e.get());
571}
572
573void WebChromeClient::setToolTip(const String& toolTip, TextDirection)
574{
575    m_webView->setToolTip(toolTip);
576}
577
578void WebChromeClient::print(Frame* frame)
579{
580    COMPtr<IWebUIDelegate> uiDelegate;
581    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate)))
582        uiDelegate->printFrame(m_webView, kit(frame));
583}
584
585#if ENABLE(SQL_DATABASE)
586void WebChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseIdentifier, DatabaseDetails)
587{
588    COMPtr<WebSecurityOrigin> origin(AdoptCOM, WebSecurityOrigin::createInstance(frame->document()->securityOrigin()));
589    COMPtr<IWebUIDelegate> uiDelegate;
590    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
591        COMPtr<IWebUIDelegatePrivate> uiDelegatePrivate(Query, uiDelegate);
592        if (uiDelegatePrivate)
593            uiDelegatePrivate->exceededDatabaseQuota(m_webView, kit(frame), origin.get(), BString(databaseIdentifier));
594        else {
595            // FIXME: remove this workaround once shipping Safari has the necessary delegate implemented.
596            WCHAR path[MAX_PATH];
597            HMODULE safariHandle = GetModuleHandleW(L"Safari.exe");
598            if (!safariHandle)
599                return;
600            GetModuleFileName(safariHandle, path, WTF_ARRAY_LENGTH(path));
601            DWORD handle;
602            DWORD versionSize = GetFileVersionInfoSize(path, &handle);
603            if (!versionSize)
604                return;
605            Vector<char> data(versionSize);
606            if (!GetFileVersionInfo(path, 0, versionSize, data.data()))
607                return;
608
609            LPCTSTR productVersion;
610            UINT productVersionLength;
611            if (!VerQueryValueW(data.data(), L"\\StringFileInfo\\040904b0\\ProductVersion", (void**)&productVersion, &productVersionLength))
612                return;
613            if (wcsncmp(L"3.1", productVersion, productVersionLength) > 0) {
614                const unsigned long long defaultQuota = 5 * 1024 * 1024; // 5 megabytes should hopefully be enough to test storage support.
615                origin->setQuota(defaultQuota);
616            }
617        }
618    }
619}
620#endif
621
622// FIXME: Move this include to the top of the file with the other includes.
623#include "ApplicationCacheStorage.h"
624
625void WebChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
626{
627    // FIXME: Free some space.
628    notImplemented();
629}
630
631void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t)
632{
633    notImplemented();
634}
635
636void WebChromeClient::populateVisitedLinks()
637{
638    COMPtr<IWebHistoryDelegate> historyDelegate;
639    m_webView->historyDelegate(&historyDelegate);
640    if (historyDelegate) {
641        historyDelegate->populateVisitedLinksForWebView(m_webView);
642        return;
643    }
644
645    WebHistory* history = WebHistory::sharedHistory();
646    if (!history)
647        return;
648    history->addVisitedLinksToPageGroup(m_webView->page()->group());
649}
650
651void WebChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
652{
653    RefPtr<FileChooser> fileChooser = prpFileChooser;
654
655    HWND viewWindow;
656    if (FAILED(m_webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
657        return;
658
659    bool multiFile = fileChooser->settings().allowsMultipleFiles;
660    Vector<WCHAR> fileBuf(multiFile ? maxFilePathsListSize : MAX_PATH);
661
662    OPENFILENAME ofn;
663
664    memset(&ofn, 0, sizeof(ofn));
665
666    // Need to zero out the first char of fileBuf so GetOpenFileName doesn't think it's an initialization string
667    fileBuf[0] = '\0';
668
669    ofn.lStructSize = sizeof(ofn);
670    ofn.hwndOwner = viewWindow;
671    String allFiles = allFilesText();
672    allFiles.append(L"\0*.*\0\0", 6);
673    ofn.lpstrFilter = allFiles.charactersWithNullTermination();
674    ofn.lpstrFile = fileBuf.data();
675    ofn.nMaxFile = fileBuf.size();
676    String dialogTitle = uploadFileText();
677    ofn.lpstrTitle = dialogTitle.charactersWithNullTermination();
678    ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;
679    if (multiFile)
680        ofn.Flags = ofn.Flags | OFN_ALLOWMULTISELECT;
681
682    if (GetOpenFileName(&ofn)) {
683        WCHAR* files = fileBuf.data();
684        Vector<String> fileList;
685        String file(files);
686        if (multiFile) {
687            while (!file.isEmpty()) {
688                // When using the OFN_EXPLORER flag, the file list is null delimited.
689                // When you create a String from a ptr to this list, it will use strlen to look for the null character.
690                // Then we find the next file path string by using the length of the string we just created.
691                WCHAR* nextFilePtr = files + file.length() + 1;
692                String nextFile(nextFilePtr);
693                // If multiple files are selected, there will be a directory name first, which we don't want to add to the vector.
694                // We know a single file was selected if there is only one filename in the list.
695                // In that case, we don't want to skip adding the first (and only) name.
696                if (files != fileBuf.data() || nextFile.isEmpty())
697                    fileList.append(file);
698                files = nextFilePtr;
699                file = nextFile;
700            }
701        } else
702            fileList.append(file);
703        ASSERT(fileList.size());
704        fileChooser->chooseFiles(fileList);
705    }
706    // FIXME: Show some sort of error if too many files are selected and the buffer is too small.  For now, this will fail silently.
707}
708
709void WebChromeClient::loadIconForFiles(const Vector<WTF::String>& filenames, WebCore::FileIconLoader* loader)
710{
711    loader->notifyFinished(Icon::createIconForFiles(filenames));
712}
713
714void WebChromeClient::setCursor(const Cursor& cursor)
715{
716    HCURSOR platformCursor = cursor.platformCursor()->nativeCursor();
717    if (!platformCursor)
718        return;
719
720    bool shouldSetCursor = true;
721    if (COMPtr<IWebUIDelegate> delegate = uiDelegate()) {
722        COMPtr<IWebUIDelegatePrivate> delegatePrivate(Query, delegate);
723        if (delegatePrivate) {
724            if (SUCCEEDED(delegatePrivate->webViewSetCursor(m_webView, reinterpret_cast<OLE_HANDLE>(platformCursor))))
725                shouldSetCursor = false;
726        }
727    }
728
729    if (shouldSetCursor)
730        ::SetCursor(platformCursor);
731
732    setLastSetCursorToCurrentCursor();
733}
734
735void WebChromeClient::setCursorHiddenUntilMouseMoves(bool)
736{
737    notImplemented();
738}
739
740void WebChromeClient::setLastSetCursorToCurrentCursor()
741{
742    m_webView->setLastCursor(::GetCursor());
743}
744
745#if USE(ACCELERATED_COMPOSITING)
746void WebChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* graphicsLayer)
747{
748    m_webView->setRootChildLayer(graphicsLayer);
749}
750
751void WebChromeClient::scheduleCompositingLayerFlush()
752{
753    m_webView->flushPendingGraphicsLayerChangesSoon();
754}
755#endif
756
757#if PLATFORM(WIN) && USE(AVFOUNDATION)
758WebCore::GraphicsDeviceAdapter* WebChromeClient::graphicsDeviceAdapter() const
759{
760    return m_webView->graphicsDeviceAdapter();
761}
762#endif
763
764COMPtr<IWebUIDelegate> WebChromeClient::uiDelegate()
765{
766    COMPtr<IWebUIDelegate> delegate;
767    m_webView->uiDelegate(&delegate);
768    return delegate;
769}
770
771#if ENABLE(VIDEO)
772
773bool WebChromeClient::supportsFullscreenForNode(const Node* node)
774{
775    return node->hasTagName(HTMLNames::videoTag);
776}
777
778void WebChromeClient::enterFullscreenForNode(Node* node)
779{
780    m_webView->enterFullscreenForNode(node);
781}
782
783void WebChromeClient::exitFullscreenForNode(Node*)
784{
785    m_webView->exitFullscreen();
786}
787
788#endif
789
790bool WebChromeClient::selectItemWritingDirectionIsNatural()
791{
792    return true;
793}
794
795bool WebChromeClient::selectItemAlignmentFollowsMenuWritingDirection()
796{
797    return false;
798}
799
800bool WebChromeClient::hasOpenedPopup() const
801{
802    notImplemented();
803    return false;
804}
805
806PassRefPtr<PopupMenu> WebChromeClient::createPopupMenu(PopupMenuClient* client) const
807{
808    return adoptRef(new PopupMenuWin(client));
809}
810
811PassRefPtr<SearchPopupMenu> WebChromeClient::createSearchPopupMenu(PopupMenuClient* client) const
812{
813    return adoptRef(new SearchPopupMenuWin(client));
814}
815
816#if ENABLE(FULLSCREEN_API)
817bool WebChromeClient::supportsFullScreenForElement(const Element* element, bool requestingKeyboardAccess)
818{
819    COMPtr<IWebUIDelegate> uiDelegate;
820    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
821        COMPtr<IWebUIDelegatePrivate4> uiDelegatePrivate4(Query, uiDelegate);
822        BOOL supports = FALSE;
823        COMPtr<IDOMElement> domElement(AdoptCOM, DOMElement::createInstance(const_cast<Element*>(element)));
824
825        if (uiDelegatePrivate4 && SUCCEEDED(uiDelegatePrivate4->supportsFullScreenForElement(domElement.get(), requestingKeyboardAccess, &supports)))
826            return supports;
827    }
828
829    return m_webView->supportsFullScreenForElement(element, requestingKeyboardAccess);
830}
831
832void WebChromeClient::enterFullScreenForElement(Element* element)
833{
834    COMPtr<IWebUIDelegate> uiDelegate;
835    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
836        COMPtr<IWebUIDelegatePrivate4> uiDelegatePrivate4(Query, uiDelegate);
837        COMPtr<IDOMElement> domElement(AdoptCOM, DOMElement::createInstance(element));
838        if (uiDelegatePrivate4 && SUCCEEDED(uiDelegatePrivate4->enterFullScreenForElement(domElement.get())))
839            return;
840    }
841
842    m_webView->setFullScreenElement(element);
843    m_webView->fullScreenController()->enterFullScreen();
844}
845
846void WebChromeClient::exitFullScreenForElement(Element* element)
847{
848    COMPtr<IWebUIDelegate> uiDelegate;
849    if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) {
850        COMPtr<IWebUIDelegatePrivate4> uiDelegatePrivate4(Query, uiDelegate);
851        COMPtr<IDOMElement> domElement(AdoptCOM, DOMElement::createInstance(element));
852        if (uiDelegatePrivate4 && SUCCEEDED(uiDelegatePrivate4->exitFullScreenForElement(domElement.get())))
853            return;
854    }
855
856    ASSERT(element == m_webView->fullScreenElement());
857    m_webView->fullScreenController()->exitFullScreen();
858}
859
860void WebChromeClient::AXStartFrameLoad()
861{
862    COMPtr<IAccessibilityDelegate> delegate;
863    m_webView->accessibilityDelegate(&delegate);
864    if (delegate)
865        delegate->fireFrameLoadStartedEvents();
866}
867
868void WebChromeClient::AXFinishFrameLoad()
869{
870    COMPtr<IAccessibilityDelegate> delegate;
871    m_webView->accessibilityDelegate(&delegate);
872    if (delegate)
873        delegate->fireFrameLoadFinishedEvents();
874}
875
876#endif
877