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