1 /* 2 * Copyright (C) 1998, 1999 Torben Weis <weis (at) kde.org> 3 * 1999 Lars Knoll <knoll (at) kde.org> 4 * 1999 Antti Koivisto <koivisto (at) kde.org> 5 * 2000 Simon Hausmann <hausmann (at) kde.org> 6 * 2000 Stefan Schimanski <1Stein (at) gmx.de> 7 * 2001 George Staikos <staikos (at) kde.org> 8 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 9 * Copyright (C) 2005 Alexey Proskuryakov <ap (at) nypop.com> 10 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 11 * Copyright (C) 2008 Eric Seidel <eric (at) webkit.org> 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Library General Public License for more details. 22 * 23 * You should have received a copy of the GNU Library General Public License 24 * along with this library; see the file COPYING.LIB. If not, write to 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26 * Boston, MA 02110-1301, USA. 27 */ 28 29 #include "config.h" 30 #include "Frame.h" 31 32 #include "ApplyStyleCommand.h" 33 #include "CSSComputedStyleDeclaration.h" 34 #include "CSSMutableStyleDeclaration.h" 35 #include "CSSProperty.h" 36 #include "CSSPropertyNames.h" 37 #include "CachedCSSStyleSheet.h" 38 #include "Chrome.h" 39 #include "ChromeClient.h" 40 #include "DOMWindow.h" 41 #include "CachedResourceLoader.h" 42 #include "DocumentType.h" 43 #include "EditingText.h" 44 #include "EditorClient.h" 45 #include "EventNames.h" 46 #include "FloatQuad.h" 47 #include "FocusController.h" 48 #include "FrameLoader.h" 49 #include "FrameLoaderClient.h" 50 #include "FrameView.h" 51 #include "GraphicsContext.h" 52 #include "GraphicsLayer.h" 53 #include "HTMLDocument.h" 54 #include "HTMLFormControlElement.h" 55 #include "HTMLFormElement.h" 56 #include "HTMLFrameElementBase.h" 57 #include "HTMLNames.h" 58 #include "HTMLTableCellElement.h" 59 #include "HitTestResult.h" 60 #include "Logging.h" 61 #include "MediaFeatureNames.h" 62 #include "Navigator.h" 63 #include "NodeList.h" 64 #include "Page.h" 65 #include "PageGroup.h" 66 #include "RegularExpression.h" 67 #include "RenderLayer.h" 68 #include "RenderPart.h" 69 #include "RenderTableCell.h" 70 #include "RenderTextControl.h" 71 #include "RenderTheme.h" 72 #include "RenderView.h" 73 #include "ScriptController.h" 74 #include "ScriptSourceCode.h" 75 #include "ScriptValue.h" 76 #include "Settings.h" 77 #include "TextIterator.h" 78 #include "TextResourceDecoder.h" 79 #include "UserContentURLPattern.h" 80 #include "UserTypingGestureIndicator.h" 81 #include "XMLNSNames.h" 82 #include "XMLNames.h" 83 #include "htmlediting.h" 84 #include "markup.h" 85 #include "npruntime_impl.h" 86 #include "visible_units.h" 87 #include <wtf/RefCountedLeakCounter.h> 88 #include <wtf/StdLibExtras.h> 89 90 #if USE(ACCELERATED_COMPOSITING) 91 #include "RenderLayerCompositor.h" 92 #endif 93 94 #if USE(JSC) 95 #include "JSDOMWindowShell.h" 96 #include "runtime_root.h" 97 #endif 98 99 #include "MathMLNames.h" 100 #include "SVGNames.h" 101 #include "XLinkNames.h" 102 103 #if ENABLE(SVG) 104 #include "SVGDocument.h" 105 #include "SVGDocumentExtensions.h" 106 #endif 107 108 #if ENABLE(TILED_BACKING_STORE) 109 #include "TiledBackingStore.h" 110 #endif 111 112 #if ENABLE(WML) 113 #include "WMLNames.h" 114 #endif 115 116 using namespace std; 117 118 namespace WebCore { 119 120 using namespace HTMLNames; 121 122 #ifndef NDEBUG 123 static WTF::RefCountedLeakCounter frameCounter("Frame"); 124 #endif 125 126 static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement) 127 { 128 if (!ownerElement) 129 return 0; 130 return ownerElement->document()->frame(); 131 } 132 133 static inline float parentPageZoomFactor(Frame* frame) 134 { 135 Frame* parent = frame->tree()->parent(); 136 if (!parent) 137 return 1; 138 return parent->pageZoomFactor(); 139 } 140 141 static inline float parentTextZoomFactor(Frame* frame) 142 { 143 Frame* parent = frame->tree()->parent(); 144 if (!parent) 145 return 1; 146 return parent->textZoomFactor(); 147 } 148 149 inline Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient) 150 : m_page(page) 151 , m_treeNode(this, parentFromOwnerElement(ownerElement)) 152 , m_loader(this, frameLoaderClient) 153 , m_navigationScheduler(this) 154 , m_ownerElement(ownerElement) 155 , m_script(this) 156 , m_editor(this) 157 , m_selectionController(this) 158 , m_eventHandler(this) 159 , m_animationController(this) 160 , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired) 161 , m_pageZoomFactor(parentPageZoomFactor(this)) 162 , m_textZoomFactor(parentTextZoomFactor(this)) 163 , m_pageScaleFactor(1) 164 #if ENABLE(ORIENTATION_EVENTS) 165 , m_orientation(0) 166 #endif 167 , m_inViewSourceMode(false) 168 , m_isDisconnected(false) 169 , m_excludeFromTextSearch(false) 170 #if PLATFORM(ANDROID) 171 // Temporary hack for http://b/5188895 172 , m_isDocumentUpToDate(true) 173 #endif 174 { 175 ASSERT(page); 176 AtomicString::init(); 177 HTMLNames::init(); 178 QualifiedName::init(); 179 MediaFeatureNames::init(); 180 SVGNames::init(); 181 XLinkNames::init(); 182 MathMLNames::init(); 183 XMLNSNames::init(); 184 XMLNames::init(); 185 186 #if ENABLE(WML) 187 WMLNames::init(); 188 #endif 189 190 if (!ownerElement) { 191 #if ENABLE(TILED_BACKING_STORE) 192 // Top level frame only for now. 193 setTiledBackingStoreEnabled(page->settings()->tiledBackingStoreEnabled()); 194 #endif 195 } else { 196 page->incrementFrameCount(); 197 198 // Make sure we will not end up with two frames referencing the same owner element. 199 Frame*& contentFrameSlot = ownerElement->m_contentFrame; 200 ASSERT(!contentFrameSlot || contentFrameSlot->ownerElement() != ownerElement); 201 contentFrameSlot = this; 202 } 203 204 #ifndef NDEBUG 205 frameCounter.increment(); 206 #endif 207 } 208 209 PassRefPtr<Frame> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) 210 { 211 RefPtr<Frame> frame = adoptRef(new Frame(page, ownerElement, client)); 212 if (!ownerElement) 213 page->setMainFrame(frame); 214 return frame.release(); 215 } 216 217 Frame::~Frame() 218 { 219 setView(0); 220 loader()->cancelAndClear(); 221 222 // FIXME: We should not be doing all this work inside the destructor 223 224 ASSERT(!m_lifeSupportTimer.isActive()); 225 226 #ifndef NDEBUG 227 frameCounter.decrement(); 228 #endif 229 230 disconnectOwnerElement(); 231 232 if (m_domWindow) 233 m_domWindow->disconnectFrame(); 234 script()->clearWindowShell(); 235 236 HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end(); 237 for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it) 238 (*it)->disconnectFrame(); 239 240 HashSet<FrameDestructionObserver*>::iterator stop = m_destructionObservers.end(); 241 for (HashSet<FrameDestructionObserver*>::iterator it = m_destructionObservers.begin(); it != stop; ++it) 242 (*it)->frameDestroyed(); 243 244 if (m_view) { 245 m_view->hide(); 246 m_view->clearFrame(); 247 } 248 249 ASSERT(!m_lifeSupportTimer.isActive()); 250 } 251 252 void Frame::addDestructionObserver(FrameDestructionObserver* observer) 253 { 254 m_destructionObservers.add(observer); 255 } 256 257 void Frame::removeDestructionObserver(FrameDestructionObserver* observer) 258 { 259 m_destructionObservers.remove(observer); 260 } 261 262 void Frame::setView(PassRefPtr<FrameView> view) 263 { 264 // We the custom scroll bars as early as possible to prevent m_doc->detach() 265 // from messing with the view such that its scroll bars won't be torn down. 266 // FIXME: We should revisit this. 267 if (m_view) 268 m_view->detachCustomScrollbars(); 269 270 // Detach the document now, so any onUnload handlers get run - if 271 // we wait until the view is destroyed, then things won't be 272 // hooked up enough for some JavaScript calls to work. 273 if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) { 274 // FIXME: We don't call willRemove here. Why is that OK? 275 m_doc->detach(); 276 } 277 278 if (m_view) 279 m_view->unscheduleRelayout(); 280 281 eventHandler()->clear(); 282 283 m_view = view; 284 285 // Only one form submission is allowed per view of a part. 286 // Since this part may be getting reused as a result of being 287 // pulled from the back/forward cache, reset this flag. 288 loader()->resetMultipleFormSubmissionProtection(); 289 290 #if ENABLE(TILED_BACKING_STORE) 291 if (m_view && tiledBackingStore()) 292 m_view->setPaintsEntireContents(true); 293 #endif 294 } 295 296 void Frame::setDocument(PassRefPtr<Document> newDoc) 297 { 298 ASSERT(!newDoc || newDoc->frame()); 299 if (m_doc && m_doc->attached() && !m_doc->inPageCache()) { 300 // FIXME: We don't call willRemove here. Why is that OK? 301 m_doc->detach(); 302 } 303 304 m_doc = newDoc; 305 #if PLATFORM(ANDROID) 306 // Temporary hack for http://b/5188895 307 m_isDocumentUpToDate = true; 308 #endif 309 selection()->updateSecureKeyboardEntryIfActive(); 310 311 if (m_doc && !m_doc->attached()) 312 m_doc->attach(); 313 314 // Update the cached 'document' property, which is now stale. 315 m_script.updateDocument(); 316 317 if (m_page) 318 m_page->updateViewportArguments(); 319 } 320 321 #if ENABLE(ORIENTATION_EVENTS) 322 void Frame::sendOrientationChangeEvent(int orientation) 323 { 324 m_orientation = orientation; 325 if (Document* doc = document()) 326 doc->dispatchWindowEvent(Event::create(eventNames().orientationchangeEvent, false, false)); 327 } 328 #endif // ENABLE(ORIENTATION_EVENTS) 329 330 Settings* Frame::settings() const 331 { 332 return m_page ? m_page->settings() : 0; 333 } 334 335 static RegularExpression* createRegExpForLabels(const Vector<String>& labels) 336 { 337 // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being 338 // the same across calls. We can't do that. 339 340 DEFINE_STATIC_LOCAL(RegularExpression, wordRegExp, ("\\w", TextCaseSensitive)); 341 String pattern("("); 342 unsigned int numLabels = labels.size(); 343 unsigned int i; 344 for (i = 0; i < numLabels; i++) { 345 String label = labels[i]; 346 347 bool startsWithWordChar = false; 348 bool endsWithWordChar = false; 349 if (label.length()) { 350 startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0; 351 endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0; 352 } 353 354 if (i) 355 pattern.append("|"); 356 // Search for word boundaries only if label starts/ends with "word characters". 357 // If we always searched for word boundaries, this wouldn't work for languages 358 // such as Japanese. 359 if (startsWithWordChar) 360 pattern.append("\\b"); 361 pattern.append(label); 362 if (endsWithWordChar) 363 pattern.append("\\b"); 364 } 365 pattern.append(")"); 366 return new RegularExpression(pattern, TextCaseInsensitive); 367 } 368 369 String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell, size_t* resultDistanceFromStartOfCell) 370 { 371 HTMLTableCellElement* aboveCell = cell->cellAbove(); 372 if (aboveCell) { 373 // search within the above cell we found for a match 374 size_t lengthSearched = 0; 375 for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) { 376 if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { 377 // For each text chunk, run the regexp 378 String nodeString = n->nodeValue(); 379 int pos = regExp->searchRev(nodeString); 380 if (pos >= 0) { 381 if (resultDistanceFromStartOfCell) 382 *resultDistanceFromStartOfCell = lengthSearched; 383 return nodeString.substring(pos, regExp->matchedLength()); 384 } 385 lengthSearched += nodeString.length(); 386 } 387 } 388 } 389 390 // Any reason in practice to search all cells in that are above cell? 391 if (resultDistanceFromStartOfCell) 392 *resultDistanceFromStartOfCell = notFound; 393 return String(); 394 } 395 396 String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) 397 { 398 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); 399 // We stop searching after we've seen this many chars 400 const unsigned int charsSearchedThreshold = 500; 401 // This is the absolute max we search. We allow a little more slop than 402 // charsSearchedThreshold, to make it more likely that we'll search whole nodes. 403 const unsigned int maxCharsSearched = 600; 404 // If the starting element is within a table, the cell that contains it 405 HTMLTableCellElement* startingTableCell = 0; 406 bool searchedCellAbove = false; 407 408 if (resultDistance) 409 *resultDistance = notFound; 410 if (resultIsInCellAbove) 411 *resultIsInCellAbove = false; 412 413 // walk backwards in the node tree, until another element, or form, or end of tree 414 int unsigned lengthSearched = 0; 415 Node* n; 416 for (n = element->traversePreviousNode(); 417 n && lengthSearched < charsSearchedThreshold; 418 n = n->traversePreviousNode()) 419 { 420 if (n->hasTagName(formTag) 421 || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())) 422 { 423 // We hit another form element or the start of the form - bail out 424 break; 425 } else if (n->hasTagName(tdTag) && !startingTableCell) { 426 startingTableCell = static_cast<HTMLTableCellElement*>(n); 427 } else if (n->hasTagName(trTag) && startingTableCell) { 428 String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); 429 if (!result.isEmpty()) { 430 if (resultIsInCellAbove) 431 *resultIsInCellAbove = true; 432 return result; 433 } 434 searchedCellAbove = true; 435 } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { 436 // For each text chunk, run the regexp 437 String nodeString = n->nodeValue(); 438 // add 100 for slop, to make it more likely that we'll search whole nodes 439 if (lengthSearched + nodeString.length() > maxCharsSearched) 440 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); 441 int pos = regExp->searchRev(nodeString); 442 if (pos >= 0) { 443 if (resultDistance) 444 *resultDistance = lengthSearched; 445 return nodeString.substring(pos, regExp->matchedLength()); 446 } 447 lengthSearched += nodeString.length(); 448 } 449 } 450 451 // If we started in a cell, but bailed because we found the start of the form or the 452 // previous element, we still might need to search the row above us for a label. 453 if (startingTableCell && !searchedCellAbove) { 454 String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); 455 if (!result.isEmpty()) { 456 if (resultIsInCellAbove) 457 *resultIsInCellAbove = true; 458 return result; 459 } 460 } 461 return String(); 462 } 463 464 static String matchLabelsAgainstString(const Vector<String>& labels, const String& stringToMatch) 465 { 466 if (stringToMatch.isEmpty()) 467 return String(); 468 469 String mutableStringToMatch = stringToMatch; 470 471 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2" 472 replace(mutableStringToMatch, RegularExpression("\\d", TextCaseSensitive), " "); 473 mutableStringToMatch.replace('_', ' '); 474 475 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); 476 // Use the largest match we can find in the whole string 477 int pos; 478 int length; 479 int bestPos = -1; 480 int bestLength = -1; 481 int start = 0; 482 do { 483 pos = regExp->match(mutableStringToMatch, start); 484 if (pos != -1) { 485 length = regExp->matchedLength(); 486 if (length >= bestLength) { 487 bestPos = pos; 488 bestLength = length; 489 } 490 start = pos + 1; 491 } 492 } while (pos != -1); 493 494 if (bestPos != -1) 495 return mutableStringToMatch.substring(bestPos, bestLength); 496 return String(); 497 } 498 499 String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element) 500 { 501 // Match against the name element, then against the id element if no match is found for the name element. 502 // See 7538330 for one popular site that benefits from the id element check. 503 // FIXME: This code is mirrored in FrameMac.mm. It would be nice to make the Mac code call the platform-agnostic 504 // code, which would require converting the NSArray of NSStrings to a Vector of Strings somewhere along the way. 505 String resultFromNameAttribute = matchLabelsAgainstString(labels, element->getAttribute(nameAttr)); 506 if (!resultFromNameAttribute.isEmpty()) 507 return resultFromNameAttribute; 508 509 return matchLabelsAgainstString(labels, element->getAttribute(idAttr)); 510 } 511 512 void Frame::setPrinting(bool printing, const FloatSize& pageSize, float maximumShrinkRatio, AdjustViewSizeOrNot shouldAdjustViewSize) 513 { 514 m_doc->setPrinting(printing); 515 view()->adjustMediaTypeForPrinting(printing); 516 517 m_doc->styleSelectorChanged(RecalcStyleImmediately); 518 view()->forceLayoutForPagination(pageSize, maximumShrinkRatio, shouldAdjustViewSize); 519 520 // Subframes of the one we're printing don't lay out to the page size. 521 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 522 child->setPrinting(printing, IntSize(), 0, shouldAdjustViewSize); 523 } 524 525 void Frame::injectUserScripts(UserScriptInjectionTime injectionTime) 526 { 527 if (!m_page) 528 return; 529 530 if (loader()->stateMachine()->creatingInitialEmptyDocument() && !settings()->shouldInjectUserScriptsInInitialEmptyDocument()) 531 return; 532 533 // Walk the hashtable. Inject by world. 534 const UserScriptMap* userScripts = m_page->group().userScripts(); 535 if (!userScripts) 536 return; 537 UserScriptMap::const_iterator end = userScripts->end(); 538 for (UserScriptMap::const_iterator it = userScripts->begin(); it != end; ++it) 539 injectUserScriptsForWorld(it->first.get(), *it->second, injectionTime); 540 } 541 542 void Frame::injectUserScriptsForWorld(DOMWrapperWorld* world, const UserScriptVector& userScripts, UserScriptInjectionTime injectionTime) 543 { 544 if (userScripts.isEmpty()) 545 return; 546 547 Document* doc = document(); 548 if (!doc) 549 return; 550 551 Vector<ScriptSourceCode> sourceCode; 552 unsigned count = userScripts.size(); 553 for (unsigned i = 0; i < count; ++i) { 554 UserScript* script = userScripts[i].get(); 555 if (script->injectedFrames() == InjectInTopFrameOnly && ownerElement()) 556 continue; 557 558 if (script->injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(doc->url(), script->whitelist(), script->blacklist())) 559 m_script.evaluateInWorld(ScriptSourceCode(script->source(), script->url()), world); 560 } 561 } 562 563 #ifndef NDEBUG 564 static HashSet<Frame*>& keepAliveSet() 565 { 566 DEFINE_STATIC_LOCAL(HashSet<Frame*>, staticKeepAliveSet, ()); 567 return staticKeepAliveSet; 568 } 569 #endif 570 571 void Frame::keepAlive() 572 { 573 if (m_lifeSupportTimer.isActive()) 574 return; 575 #ifndef NDEBUG 576 keepAliveSet().add(this); 577 #endif 578 ref(); 579 m_lifeSupportTimer.startOneShot(0); 580 } 581 582 #ifndef NDEBUG 583 void Frame::cancelAllKeepAlive() 584 { 585 HashSet<Frame*>::iterator end = keepAliveSet().end(); 586 for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) { 587 Frame* frame = *it; 588 frame->m_lifeSupportTimer.stop(); 589 frame->deref(); 590 } 591 keepAliveSet().clear(); 592 } 593 #endif 594 595 void Frame::lifeSupportTimerFired(Timer<Frame>*) 596 { 597 #ifndef NDEBUG 598 keepAliveSet().remove(this); 599 #endif 600 deref(); 601 } 602 603 void Frame::clearDOMWindow() 604 { 605 if (m_domWindow) { 606 m_liveFormerWindows.add(m_domWindow.get()); 607 m_domWindow->clear(); 608 } 609 m_domWindow = 0; 610 } 611 612 RenderView* Frame::contentRenderer() const 613 { 614 Document* doc = document(); 615 if (!doc) 616 return 0; 617 RenderObject* object = doc->renderer(); 618 if (!object) 619 return 0; 620 ASSERT(object->isRenderView()); 621 return toRenderView(object); 622 } 623 624 RenderPart* Frame::ownerRenderer() const 625 { 626 HTMLFrameOwnerElement* ownerElement = m_ownerElement; 627 if (!ownerElement) 628 return 0; 629 RenderObject* object = ownerElement->renderer(); 630 if (!object) 631 return 0; 632 // FIXME: If <object> is ever fixed to disassociate itself from frames 633 // that it has started but canceled, then this can turn into an ASSERT 634 // since m_ownerElement would be 0 when the load is canceled. 635 // https://bugs.webkit.org/show_bug.cgi?id=18585 636 if (!object->isRenderPart()) 637 return 0; 638 return toRenderPart(object); 639 } 640 641 Frame* Frame::frameForWidget(const Widget* widget) 642 { 643 ASSERT_ARG(widget, widget); 644 645 if (RenderWidget* renderer = RenderWidget::find(widget)) 646 if (Node* node = renderer->node()) 647 return node->document()->frame(); 648 649 // Assume all widgets are either a FrameView or owned by a RenderWidget. 650 // FIXME: That assumption is not right for scroll bars! 651 ASSERT(widget->isFrameView()); 652 return static_cast<const FrameView*>(widget)->frame(); 653 } 654 655 void Frame::clearTimers(FrameView *view, Document *document) 656 { 657 if (view) { 658 view->unscheduleRelayout(); 659 if (view->frame()) { 660 view->frame()->animation()->suspendAnimationsForDocument(document); 661 view->frame()->eventHandler()->stopAutoscrollTimer(); 662 } 663 } 664 } 665 666 void Frame::clearTimers() 667 { 668 clearTimers(m_view.get(), document()); 669 } 670 671 void Frame::setDOMWindow(DOMWindow* domWindow) 672 { 673 if (m_domWindow) { 674 m_liveFormerWindows.add(m_domWindow.get()); 675 m_domWindow->clear(); 676 } 677 m_domWindow = domWindow; 678 } 679 680 DOMWindow* Frame::domWindow() const 681 { 682 if (!m_domWindow) 683 m_domWindow = DOMWindow::create(const_cast<Frame*>(this)); 684 685 return m_domWindow.get(); 686 } 687 688 void Frame::clearFormerDOMWindow(DOMWindow* window) 689 { 690 m_liveFormerWindows.remove(window); 691 } 692 693 void Frame::pageDestroyed() 694 { 695 if (Frame* parent = tree()->parent()) 696 parent->loader()->checkLoadComplete(); 697 698 if (m_domWindow) { 699 m_domWindow->resetGeolocation(); 700 m_domWindow->pageDestroyed(); 701 } 702 703 // FIXME: It's unclear as to why this is called more than once, but it is, 704 // so page() could be NULL. 705 if (page() && page()->focusController()->focusedFrame() == this) 706 page()->focusController()->setFocusedFrame(0); 707 708 script()->clearWindowShell(); 709 script()->clearScriptObjects(); 710 script()->updatePlatformScriptObjects(); 711 712 detachFromPage(); 713 } 714 715 void Frame::disconnectOwnerElement() 716 { 717 if (m_ownerElement) { 718 if (Document* doc = document()) 719 doc->clearAXObjectCache(); 720 m_ownerElement->m_contentFrame = 0; 721 if (m_page) 722 m_page->decrementFrameCount(); 723 } 724 m_ownerElement = 0; 725 } 726 727 // The frame is moved in DOM, potentially to another page. 728 void Frame::transferChildFrameToNewDocument() 729 { 730 ASSERT(m_ownerElement); 731 Frame* newParent = m_ownerElement->document()->frame(); 732 ASSERT(newParent); 733 bool didTransfer = false; 734 735 // Switch page. 736 Page* newPage = newParent->page(); 737 Page* oldPage = m_page; 738 if (m_page != newPage) { 739 if (m_page) { 740 if (m_page->focusController()->focusedFrame() == this) 741 m_page->focusController()->setFocusedFrame(0); 742 743 m_page->decrementFrameCount(); 744 } 745 746 // FIXME: We should ideally allow existing Geolocation activities to continue 747 // when the Geolocation's iframe is reparented. 748 // See https://bugs.webkit.org/show_bug.cgi?id=55577 749 // and https://bugs.webkit.org/show_bug.cgi?id=52877 750 if (m_domWindow) 751 m_domWindow->resetGeolocation(); 752 753 m_page = newPage; 754 755 if (newPage) 756 newPage->incrementFrameCount(); 757 758 didTransfer = true; 759 } 760 761 // Update the frame tree. 762 didTransfer = newParent->tree()->transferChild(this) || didTransfer; 763 764 // Avoid unnecessary calls to client and frame subtree if the frame ended 765 // up on the same page and under the same parent frame. 766 if (didTransfer) { 767 // Let external clients update themselves. 768 loader()->client()->didTransferChildFrameToNewDocument(oldPage); 769 770 // Update resource tracking now that frame could be in a different page. 771 if (oldPage != newPage) 772 loader()->transferLoadingResourcesFromPage(oldPage); 773 774 // Do the same for all the children. 775 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 776 child->transferChildFrameToNewDocument(); 777 } 778 } 779 780 String Frame::documentTypeString() const 781 { 782 if (DocumentType* doctype = document()->doctype()) 783 return createMarkup(doctype); 784 785 return String(); 786 } 787 788 VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) 789 { 790 HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true); 791 Node* node = result.innerNode(); 792 if (!node) 793 return VisiblePosition(); 794 RenderObject* renderer = node->renderer(); 795 if (!renderer) 796 return VisiblePosition(); 797 VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint()); 798 if (visiblePos.isNull()) 799 visiblePos = firstPositionInOrBeforeNode(node); 800 return visiblePos; 801 } 802 803 Document* Frame::documentAtPoint(const IntPoint& point) 804 { 805 if (!view()) 806 return 0; 807 808 IntPoint pt = view()->windowToContents(point); 809 HitTestResult result = HitTestResult(pt); 810 811 if (contentRenderer()) 812 result = eventHandler()->hitTestResultAtPoint(pt, false); 813 return result.innerNode() ? result.innerNode()->document() : 0; 814 } 815 816 PassRefPtr<Range> Frame::rangeForPoint(const IntPoint& framePoint) 817 { 818 VisiblePosition position = visiblePositionForPoint(framePoint); 819 if (position.isNull()) 820 return 0; 821 822 VisiblePosition previous = position.previous(); 823 if (previous.isNotNull()) { 824 RefPtr<Range> previousCharacterRange = makeRange(previous, position); 825 IntRect rect = editor()->firstRectForRange(previousCharacterRange.get()); 826 if (rect.contains(framePoint)) 827 return previousCharacterRange.release(); 828 } 829 830 VisiblePosition next = position.next(); 831 if (next.isNotNull()) { 832 RefPtr<Range> nextCharacterRange = makeRange(position, next); 833 IntRect rect = editor()->firstRectForRange(nextCharacterRange.get()); 834 if (rect.contains(framePoint)) 835 return nextCharacterRange.release(); 836 } 837 838 return 0; 839 } 840 841 void Frame::createView(const IntSize& viewportSize, 842 const Color& backgroundColor, bool transparent, 843 const IntSize& fixedLayoutSize, bool useFixedLayout, 844 ScrollbarMode horizontalScrollbarMode, bool horizontalLock, 845 ScrollbarMode verticalScrollbarMode, bool verticalLock) 846 { 847 ASSERT(this); 848 ASSERT(m_page); 849 850 bool isMainFrame = this == m_page->mainFrame(); 851 852 if (isMainFrame && view()) 853 view()->setParentVisible(false); 854 855 setView(0); 856 857 RefPtr<FrameView> frameView; 858 if (isMainFrame) { 859 frameView = FrameView::create(this, viewportSize); 860 frameView->setFixedLayoutSize(fixedLayoutSize); 861 frameView->setUseFixedLayout(useFixedLayout); 862 } else 863 frameView = FrameView::create(this); 864 865 frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode, horizontalLock, verticalLock); 866 867 setView(frameView); 868 869 if (backgroundColor.isValid()) 870 frameView->updateBackgroundRecursively(backgroundColor, transparent); 871 872 if (isMainFrame) 873 frameView->setParentVisible(true); 874 875 if (ownerRenderer()) 876 ownerRenderer()->setWidget(frameView); 877 878 if (HTMLFrameOwnerElement* owner = ownerElement()) 879 view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); 880 } 881 882 #if ENABLE(TILED_BACKING_STORE) 883 void Frame::setTiledBackingStoreEnabled(bool enabled) 884 { 885 if (!enabled) { 886 m_tiledBackingStore.clear(); 887 return; 888 } 889 if (m_tiledBackingStore) 890 return; 891 m_tiledBackingStore.set(new TiledBackingStore(this)); 892 if (m_view) 893 m_view->setPaintsEntireContents(true); 894 } 895 896 void Frame::tiledBackingStorePaintBegin() 897 { 898 if (!m_view) 899 return; 900 m_view->updateLayoutAndStyleIfNeededRecursive(); 901 m_view->flushDeferredRepaints(); 902 } 903 904 void Frame::tiledBackingStorePaint(GraphicsContext* context, const IntRect& rect) 905 { 906 if (!m_view) 907 return; 908 m_view->paintContents(context, rect); 909 } 910 911 void Frame::tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea) 912 { 913 if (!m_page || !m_view) 914 return; 915 unsigned size = paintedArea.size(); 916 // Request repaint from the system 917 for (int n = 0; n < size; ++n) 918 m_page->chrome()->invalidateContentsAndWindow(m_view->contentsToWindow(paintedArea[n]), false); 919 } 920 921 IntRect Frame::tiledBackingStoreContentsRect() 922 { 923 if (!m_view) 924 return IntRect(); 925 return IntRect(IntPoint(), m_view->contentsSize()); 926 } 927 928 IntRect Frame::tiledBackingStoreVisibleRect() 929 { 930 if (!m_page) 931 return IntRect(); 932 return m_page->chrome()->client()->visibleRectForTiledBackingStore(); 933 } 934 935 Color Frame::tiledBackingStoreBackgroundColor() const 936 { 937 if (!m_view) 938 return Color(); 939 return m_view->baseBackgroundColor(); 940 } 941 #endif 942 943 String Frame::layerTreeAsText(bool showDebugInfo) const 944 { 945 #if USE(ACCELERATED_COMPOSITING) 946 document()->updateLayout(); 947 948 if (!contentRenderer()) 949 return String(); 950 951 return contentRenderer()->compositor()->layerTreeAsText(showDebugInfo); 952 #else 953 return String(); 954 #endif 955 } 956 957 void Frame::setPageZoomFactor(float factor) 958 { 959 setPageAndTextZoomFactors(factor, m_textZoomFactor); 960 } 961 962 void Frame::setTextZoomFactor(float factor) 963 { 964 setPageAndTextZoomFactors(m_pageZoomFactor, factor); 965 } 966 967 void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor) 968 { 969 if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) 970 return; 971 972 Page* page = this->page(); 973 if (!page) 974 return; 975 976 Document* document = this->document(); 977 if (!document) 978 return; 979 980 m_editor.dismissCorrectionPanelAsIgnored(); 981 982 #if ENABLE(SVG) 983 // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents. 984 // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG WG clarification. 985 if (document->isSVGDocument()) { 986 if (!static_cast<SVGDocument*>(document)->zoomAndPanEnabled()) 987 return; 988 if (document->renderer()) 989 document->renderer()->setNeedsLayout(true); 990 } 991 #endif 992 993 if (m_pageZoomFactor != pageZoomFactor) { 994 if (FrameView* view = this->view()) { 995 // Update the scroll position when doing a full page zoom, so the content stays in relatively the same position. 996 IntPoint scrollPosition = view->scrollPosition(); 997 float percentDifference = (pageZoomFactor / m_pageZoomFactor); 998 view->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference)); 999 } 1000 } 1001 1002 m_pageZoomFactor = pageZoomFactor; 1003 m_textZoomFactor = textZoomFactor; 1004 1005 document->recalcStyle(Node::Force); 1006 1007 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 1008 child->setPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor); 1009 1010 if (FrameView* view = this->view()) { 1011 if (document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout()) 1012 view->layout(); 1013 } 1014 } 1015 1016 #if USE(ACCELERATED_COMPOSITING) 1017 void Frame::updateContentsScale(float scale) 1018 { 1019 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 1020 child->updateContentsScale(scale); 1021 1022 RenderView* root = contentRenderer(); 1023 if (root && root->compositor()) 1024 root->compositor()->updateContentsScale(scale); 1025 } 1026 #endif 1027 1028 void Frame::scalePage(float scale, const IntPoint& origin) 1029 { 1030 Document* document = this->document(); 1031 if (!document) 1032 return; 1033 1034 if (scale != m_pageScaleFactor) { 1035 m_pageScaleFactor = scale; 1036 1037 if (document->renderer()) 1038 document->renderer()->setNeedsLayout(true); 1039 1040 document->recalcStyle(Node::Force); 1041 1042 #if USE(ACCELERATED_COMPOSITING) 1043 updateContentsScale(scale); 1044 #endif 1045 } 1046 1047 if (FrameView* view = this->view()) { 1048 if (document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout()) 1049 view->layout(); 1050 view->setScrollPosition(origin); 1051 } 1052 } 1053 1054 } // namespace WebCore 1055