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 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 "BeforeUnloadEvent.h" 34 #include "CSSComputedStyleDeclaration.h" 35 #include "CSSMutableStyleDeclaration.h" 36 #include "CSSProperty.h" 37 #include "CSSPropertyNames.h" 38 #include "CachedCSSStyleSheet.h" 39 #include "Chrome.h" 40 #include "DOMWindow.h" 41 #include "DocLoader.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 "HTMLDocument.h" 53 #include "HTMLFormControlElement.h" 54 #include "HTMLFormElement.h" 55 #include "HTMLFrameElementBase.h" 56 #include "HTMLNames.h" 57 #include "HTMLTableCellElement.h" 58 #include "HitTestResult.h" 59 #include "Logging.h" 60 #include "MediaFeatureNames.h" 61 #include "Navigator.h" 62 #include "NodeList.h" 63 #include "Page.h" 64 #include "PageGroup.h" 65 #include "RegularExpression.h" 66 #include "RenderPart.h" 67 #include "RenderTableCell.h" 68 #include "RenderTextControl.h" 69 #include "RenderTheme.h" 70 #include "RenderView.h" 71 #include "ScriptController.h" 72 #include "ScriptSourceCode.h" 73 #include "ScriptValue.h" 74 #include "Settings.h" 75 #include "TextIterator.h" 76 #include "TextResourceDecoder.h" 77 #include "UserContentURLPattern.h" 78 #include "XMLNSNames.h" 79 #include "XMLNames.h" 80 #include "htmlediting.h" 81 #include "markup.h" 82 #include "npruntime_impl.h" 83 #include "visible_units.h" 84 #include <wtf/RefCountedLeakCounter.h> 85 #include <wtf/StdLibExtras.h> 86 87 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) 88 #import <Carbon/Carbon.h> 89 #endif 90 91 #if USE(JSC) 92 #include "JSDOMWindowShell.h" 93 #include "runtime_root.h" 94 #endif 95 96 #if ENABLE(SVG) 97 #include "SVGDocument.h" 98 #include "SVGDocumentExtensions.h" 99 #include "SVGNames.h" 100 #include "XLinkNames.h" 101 #endif 102 103 #if PLATFORM(ANDROID) 104 #include "WebViewCore.h" 105 #endif 106 107 #if ENABLE(WML) 108 #include "WMLNames.h" 109 #endif 110 111 #if ENABLE(MATHML) 112 #include "MathMLNames.h" 113 #endif 114 115 using namespace std; 116 117 namespace WebCore { 118 119 using namespace HTMLNames; 120 121 #ifndef NDEBUG 122 static WTF::RefCountedLeakCounter frameCounter("Frame"); 123 #endif 124 125 static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement) 126 { 127 if (!ownerElement) 128 return 0; 129 return ownerElement->document()->frame(); 130 } 131 132 Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient) 133 : m_page(page) 134 , m_treeNode(this, parentFromOwnerElement(ownerElement)) 135 , m_loader(this, frameLoaderClient) 136 , m_redirectScheduler(this) 137 , m_ownerElement(ownerElement) 138 , m_script(this) 139 , m_selectionGranularity(CharacterGranularity) 140 , m_selectionController(this) 141 , m_editor(this) 142 , m_eventHandler(this) 143 , m_animationController(this) 144 , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired) 145 #if ENABLE(ORIENTATION_EVENTS) 146 , m_orientation(0) 147 #endif 148 , m_highlightTextMatches(false) 149 , m_inViewSourceMode(false) 150 , m_needsReapplyStyles(false) 151 , m_isDisconnected(false) 152 , m_excludeFromTextSearch(false) 153 { 154 Frame* parent = parentFromOwnerElement(ownerElement); 155 m_zoomFactor = parent ? parent->m_zoomFactor : 1.0f; 156 157 AtomicString::init(); 158 HTMLNames::init(); 159 QualifiedName::init(); 160 MediaFeatureNames::init(); 161 162 #if ENABLE(SVG) 163 SVGNames::init(); 164 XLinkNames::init(); 165 #endif 166 167 #if ENABLE(WML) 168 WMLNames::init(); 169 #endif 170 171 #if ENABLE(MATHML) 172 MathMLNames::init(); 173 #endif 174 175 XMLNSNames::init(); 176 XMLNames::init(); 177 178 if (!ownerElement) 179 page->setMainFrame(this); 180 else { 181 page->incrementFrameCount(); 182 // Make sure we will not end up with two frames referencing the same owner element. 183 ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement)); 184 ownerElement->m_contentFrame = this; 185 } 186 187 #ifndef NDEBUG 188 frameCounter.increment(); 189 #endif 190 } 191 192 Frame::~Frame() 193 { 194 setView(0); 195 loader()->cancelAndClear(); 196 197 // FIXME: We should not be doing all this work inside the destructor 198 199 ASSERT(!m_lifeSupportTimer.isActive()); 200 201 #ifndef NDEBUG 202 frameCounter.decrement(); 203 #endif 204 205 disconnectOwnerElement(); 206 207 if (m_domWindow) 208 m_domWindow->disconnectFrame(); 209 script()->clearWindowShell(); 210 211 HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end(); 212 for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it) 213 (*it)->disconnectFrame(); 214 215 if (m_view) { 216 m_view->hide(); 217 m_view->clearFrame(); 218 } 219 220 ASSERT(!m_lifeSupportTimer.isActive()); 221 } 222 223 void Frame::init() 224 { 225 m_loader.init(); 226 } 227 228 FrameLoader* Frame::loader() const 229 { 230 return &m_loader; 231 } 232 233 RedirectScheduler* Frame::redirectScheduler() const 234 { 235 return &m_redirectScheduler; 236 } 237 238 FrameView* Frame::view() const 239 { 240 return m_view.get(); 241 } 242 243 void Frame::setView(PassRefPtr<FrameView> view) 244 { 245 // We the custom scroll bars as early as possible to prevent m_doc->detach() 246 // from messing with the view such that its scroll bars won't be torn down. 247 // FIXME: We should revisit this. 248 if (m_view) 249 m_view->detachCustomScrollbars(); 250 251 // Detach the document now, so any onUnload handlers get run - if 252 // we wait until the view is destroyed, then things won't be 253 // hooked up enough for some JavaScript calls to work. 254 if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) { 255 // FIXME: We don't call willRemove here. Why is that OK? 256 m_doc->detach(); 257 if (m_view) 258 m_view->unscheduleRelayout(); 259 } 260 eventHandler()->clear(); 261 262 m_view = view; 263 264 // Only one form submission is allowed per view of a part. 265 // Since this part may be getting reused as a result of being 266 // pulled from the back/forward cache, reset this flag. 267 loader()->resetMultipleFormSubmissionProtection(); 268 } 269 270 ScriptController* Frame::script() 271 { 272 return &m_script; 273 } 274 275 Document* Frame::document() const 276 { 277 return m_doc.get(); 278 } 279 280 void Frame::setDocument(PassRefPtr<Document> newDoc) 281 { 282 if (m_doc && m_doc->attached() && !m_doc->inPageCache()) { 283 // FIXME: We don't call willRemove here. Why is that OK? 284 m_doc->detach(); 285 } 286 287 m_doc = newDoc; 288 if (m_doc && selection()->isFocusedAndActive()) 289 setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive()); 290 291 if (m_doc && !m_doc->attached()) 292 m_doc->attach(); 293 294 // Update the cached 'document' property, which is now stale. 295 m_script.updateDocument(); 296 } 297 298 #if ENABLE(ORIENTATION_EVENTS) 299 void Frame::sendOrientationChangeEvent(int orientation) 300 { 301 m_orientation = orientation; 302 if (Document* doc = document()) 303 doc->dispatchWindowEvent(Event::create(eventNames().orientationchangeEvent, false, false)); 304 } 305 #endif // ENABLE(ORIENTATION_EVENTS) 306 307 Settings* Frame::settings() const 308 { 309 return m_page ? m_page->settings() : 0; 310 } 311 312 String Frame::selectedText() const 313 { 314 return plainText(selection()->toNormalizedRange().get()); 315 } 316 317 IntRect Frame::firstRectForRange(Range* range) const 318 { 319 int extraWidthToEndOfLine = 0; 320 ExceptionCode ec = 0; 321 ASSERT(range->startContainer(ec)); 322 ASSERT(range->endContainer(ec)); 323 324 InlineBox* startInlineBox; 325 int startCaretOffset; 326 range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset); 327 328 RenderObject* startRenderer = range->startContainer(ec)->renderer(); 329 IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine); 330 if (startCaretRect != IntRect()) 331 startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox(); 332 333 InlineBox* endInlineBox; 334 int endCaretOffset; 335 range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset); 336 337 RenderObject* endRenderer = range->endContainer(ec)->renderer(); 338 IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset); 339 if (endCaretRect != IntRect()) 340 endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox(); 341 342 if (startCaretRect.y() == endCaretRect.y()) { 343 // start and end are on the same line 344 return IntRect(min(startCaretRect.x(), endCaretRect.x()), 345 startCaretRect.y(), 346 abs(endCaretRect.x() - startCaretRect.x()), 347 max(startCaretRect.height(), endCaretRect.height())); 348 } 349 350 // start and end aren't on the same line, so go from start to the end of its line 351 return IntRect(startCaretRect.x(), 352 startCaretRect.y(), 353 startCaretRect.width() + extraWidthToEndOfLine, 354 startCaretRect.height()); 355 } 356 357 SelectionController* Frame::selection() const 358 { 359 return &m_selectionController; 360 } 361 362 Editor* Frame::editor() const 363 { 364 return &m_editor; 365 } 366 367 TextGranularity Frame::selectionGranularity() const 368 { 369 return m_selectionGranularity; 370 } 371 372 void Frame::setSelectionGranularity(TextGranularity granularity) 373 { 374 m_selectionGranularity = granularity; 375 } 376 377 SelectionController* Frame::dragCaretController() const 378 { 379 return m_page->dragCaretController(); 380 } 381 382 383 AnimationController* Frame::animation() const 384 { 385 return &m_animationController; 386 } 387 388 static RegularExpression* createRegExpForLabels(const Vector<String>& labels) 389 { 390 // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being 391 // the same across calls. We can't do that. 392 393 DEFINE_STATIC_LOCAL(RegularExpression, wordRegExp, ("\\w", TextCaseSensitive)); 394 String pattern("("); 395 unsigned int numLabels = labels.size(); 396 unsigned int i; 397 for (i = 0; i < numLabels; i++) { 398 String label = labels[i]; 399 400 bool startsWithWordChar = false; 401 bool endsWithWordChar = false; 402 if (label.length()) { 403 startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0; 404 endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0; 405 } 406 407 if (i) 408 pattern.append("|"); 409 // Search for word boundaries only if label starts/ends with "word characters". 410 // If we always searched for word boundaries, this wouldn't work for languages 411 // such as Japanese. 412 if (startsWithWordChar) 413 pattern.append("\\b"); 414 pattern.append(label); 415 if (endsWithWordChar) 416 pattern.append("\\b"); 417 } 418 pattern.append(")"); 419 return new RegularExpression(pattern, TextCaseInsensitive); 420 } 421 422 String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell, size_t* resultDistanceFromStartOfCell) 423 { 424 RenderObject* cellRenderer = cell->renderer(); 425 426 if (cellRenderer && cellRenderer->isTableCell()) { 427 RenderTableCell* tableCellRenderer = toRenderTableCell(cellRenderer); 428 RenderTableCell* cellAboveRenderer = tableCellRenderer->table()->cellAbove(tableCellRenderer); 429 430 if (cellAboveRenderer) { 431 HTMLTableCellElement* aboveCell = 432 static_cast<HTMLTableCellElement*>(cellAboveRenderer->node()); 433 434 if (aboveCell) { 435 // search within the above cell we found for a match 436 size_t lengthSearched = 0; 437 for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) { 438 if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { 439 // For each text chunk, run the regexp 440 String nodeString = n->nodeValue(); 441 int pos = regExp->searchRev(nodeString); 442 if (pos >= 0) { 443 if (resultDistanceFromStartOfCell) 444 *resultDistanceFromStartOfCell = lengthSearched; 445 return nodeString.substring(pos, regExp->matchedLength()); 446 } 447 lengthSearched += nodeString.length(); 448 } 449 } 450 } 451 } 452 } 453 // Any reason in practice to search all cells in that are above cell? 454 if (resultDistanceFromStartOfCell) 455 *resultDistanceFromStartOfCell = notFound; 456 return String(); 457 } 458 459 String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) 460 { 461 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); 462 // We stop searching after we've seen this many chars 463 const unsigned int charsSearchedThreshold = 500; 464 // This is the absolute max we search. We allow a little more slop than 465 // charsSearchedThreshold, to make it more likely that we'll search whole nodes. 466 const unsigned int maxCharsSearched = 600; 467 // If the starting element is within a table, the cell that contains it 468 HTMLTableCellElement* startingTableCell = 0; 469 bool searchedCellAbove = false; 470 471 if (resultDistance) 472 *resultDistance = notFound; 473 if (resultIsInCellAbove) 474 *resultIsInCellAbove = false; 475 476 // walk backwards in the node tree, until another element, or form, or end of tree 477 int unsigned lengthSearched = 0; 478 Node* n; 479 for (n = element->traversePreviousNode(); 480 n && lengthSearched < charsSearchedThreshold; 481 n = n->traversePreviousNode()) 482 { 483 if (n->hasTagName(formTag) 484 || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())) 485 { 486 // We hit another form element or the start of the form - bail out 487 break; 488 } else if (n->hasTagName(tdTag) && !startingTableCell) { 489 startingTableCell = static_cast<HTMLTableCellElement*>(n); 490 } else if (n->hasTagName(trTag) && startingTableCell) { 491 String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); 492 if (!result.isEmpty()) { 493 if (resultIsInCellAbove) 494 *resultIsInCellAbove = true; 495 return result; 496 } 497 searchedCellAbove = true; 498 } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { 499 // For each text chunk, run the regexp 500 String nodeString = n->nodeValue(); 501 // add 100 for slop, to make it more likely that we'll search whole nodes 502 if (lengthSearched + nodeString.length() > maxCharsSearched) 503 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); 504 int pos = regExp->searchRev(nodeString); 505 if (pos >= 0) { 506 if (resultDistance) 507 *resultDistance = lengthSearched; 508 return nodeString.substring(pos, regExp->matchedLength()); 509 } 510 lengthSearched += nodeString.length(); 511 } 512 } 513 514 // If we started in a cell, but bailed because we found the start of the form or the 515 // previous element, we still might need to search the row above us for a label. 516 if (startingTableCell && !searchedCellAbove) { 517 String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); 518 if (!result.isEmpty()) { 519 if (resultIsInCellAbove) 520 *resultIsInCellAbove = true; 521 return result; 522 } 523 } 524 return String(); 525 } 526 527 static String matchLabelsAgainstString(const Vector<String>& labels, const String& stringToMatch) 528 { 529 if (stringToMatch.isEmpty()) 530 return String(); 531 532 String mutableStringToMatch = stringToMatch; 533 534 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2" 535 replace(mutableStringToMatch, RegularExpression("\\d", TextCaseSensitive), " "); 536 mutableStringToMatch.replace('_', ' '); 537 538 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); 539 // Use the largest match we can find in the whole string 540 int pos; 541 int length; 542 int bestPos = -1; 543 int bestLength = -1; 544 int start = 0; 545 do { 546 pos = regExp->match(mutableStringToMatch, start); 547 if (pos != -1) { 548 length = regExp->matchedLength(); 549 if (length >= bestLength) { 550 bestPos = pos; 551 bestLength = length; 552 } 553 start = pos + 1; 554 } 555 } while (pos != -1); 556 557 if (bestPos != -1) 558 return mutableStringToMatch.substring(bestPos, bestLength); 559 return String(); 560 } 561 562 String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element) 563 { 564 // Match against the name element, then against the id element if no match is found for the name element. 565 // See 7538330 for one popular site that benefits from the id element check. 566 // FIXME: This code is mirrored in FrameMac.mm. It would be nice to make the Mac code call the platform-agnostic 567 // code, which would require converting the NSArray of NSStrings to a Vector of Strings somewhere along the way. 568 String resultFromNameAttribute = matchLabelsAgainstString(labels, element->getAttribute(nameAttr)); 569 if (!resultFromNameAttribute.isEmpty()) 570 return resultFromNameAttribute; 571 572 return matchLabelsAgainstString(labels, element->getAttribute(idAttr)); 573 } 574 575 const VisibleSelection& Frame::mark() const 576 { 577 return m_mark; 578 } 579 580 void Frame::setMark(const VisibleSelection& s) 581 { 582 ASSERT(!s.base().node() || s.base().node()->document() == document()); 583 ASSERT(!s.extent().node() || s.extent().node()->document() == document()); 584 ASSERT(!s.start().node() || s.start().node()->document() == document()); 585 ASSERT(!s.end().node() || s.end().node()->document() == document()); 586 587 m_mark = s; 588 } 589 590 void Frame::notifyRendererOfSelectionChange(bool userTriggered) 591 { 592 RenderObject* renderer = 0; 593 if (selection()->rootEditableElement()) 594 renderer = selection()->rootEditableElement()->shadowAncestorNode()->renderer(); 595 596 // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed 597 if (renderer && renderer->isTextControl()) 598 toRenderTextControl(renderer)->selectionChanged(userTriggered); 599 } 600 601 // Helper function that tells whether a particular node is an element that has an entire 602 // Frame and FrameView, a <frame>, <iframe>, or <object>. 603 static bool isFrameElement(const Node *n) 604 { 605 if (!n) 606 return false; 607 RenderObject *renderer = n->renderer(); 608 if (!renderer || !renderer->isWidget()) 609 return false; 610 Widget* widget = toRenderWidget(renderer)->widget(); 611 return widget && widget->isFrameView(); 612 } 613 614 void Frame::setFocusedNodeIfNeeded() 615 { 616 if (selection()->isNone() || !selection()->isFocused()) 617 return; 618 619 bool caretBrowsing = settings() && settings()->caretBrowsingEnabled(); 620 if (caretBrowsing) { 621 Node* anchor = enclosingAnchorElement(selection()->base()); 622 if (anchor) { 623 page()->focusController()->setFocusedNode(anchor, this); 624 return; 625 } 626 } 627 628 Node* target = selection()->rootEditableElement(); 629 if (target) { 630 RenderObject* renderer = target->renderer(); 631 632 // Walk up the render tree to search for a node to focus. 633 // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields. 634 while (renderer) { 635 // We don't want to set focus on a subframe when selecting in a parent frame, 636 // so add the !isFrameElement check here. There's probably a better way to make this 637 // work in the long term, but this is the safest fix at this time. 638 if (target && target->isMouseFocusable() && !isFrameElement(target)) { 639 page()->focusController()->setFocusedNode(target, this); 640 return; 641 } 642 renderer = renderer->parent(); 643 if (renderer) 644 target = renderer->node(); 645 } 646 document()->setFocusedNode(0); 647 } 648 649 if (caretBrowsing) 650 page()->focusController()->setFocusedNode(0, this); 651 } 652 653 void Frame::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const 654 { 655 #if ENABLE(TEXT_CARET) 656 SelectionController* dragCaretController = m_page->dragCaretController(); 657 ASSERT(dragCaretController->selection().isCaret()); 658 if (dragCaretController->selection().start().node()->document()->frame() == this) 659 dragCaretController->paintCaret(p, tx, ty, clipRect); 660 #endif 661 } 662 663 float Frame::zoomFactor() const 664 { 665 return m_zoomFactor; 666 } 667 668 bool Frame::isZoomFactorTextOnly() const 669 { 670 return m_page->settings()->zoomsTextOnly(); 671 } 672 673 bool Frame::shouldApplyTextZoom() const 674 { 675 if (m_zoomFactor == 1.0f || !isZoomFactorTextOnly()) 676 return false; 677 #if ENABLE(SVG) 678 if (m_doc->isSVGDocument()) 679 return false; 680 #endif 681 return true; 682 } 683 684 bool Frame::shouldApplyPageZoom() const 685 { 686 if (m_zoomFactor == 1.0f || isZoomFactorTextOnly()) 687 return false; 688 #if ENABLE(SVG) 689 if (m_doc->isSVGDocument()) 690 return false; 691 #endif 692 return true; 693 } 694 695 void Frame::setZoomFactor(float percent, bool isTextOnly) 696 { 697 if (m_zoomFactor == percent && isZoomFactorTextOnly() == isTextOnly) 698 return; 699 700 #if ENABLE(SVG) 701 // SVG doesn't care if the zoom factor is text only. It will always apply a 702 // zoom to the whole SVG. 703 if (m_doc->isSVGDocument()) { 704 if (!static_cast<SVGDocument*>(m_doc.get())->zoomAndPanEnabled()) 705 return; 706 m_zoomFactor = percent; 707 m_page->settings()->setZoomsTextOnly(true); // We do this to avoid doing any scaling of CSS pixels, since the SVG has its own notion of zoom. 708 if (m_doc->renderer()) 709 m_doc->renderer()->repaint(); 710 return; 711 } 712 #endif 713 714 if (!isTextOnly) { 715 // Update the scroll position when doing a full page zoom, so the content stays in relatively the same position. 716 IntPoint scrollPosition = view()->scrollPosition(); 717 float percentDifference = (percent / m_zoomFactor); 718 view()->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference)); 719 } 720 721 m_zoomFactor = percent; 722 m_page->settings()->setZoomsTextOnly(isTextOnly); 723 724 m_doc->recalcStyle(Node::Force); 725 726 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 727 child->setZoomFactor(m_zoomFactor, isTextOnly); 728 729 if (m_doc->renderer() && m_doc->renderer()->needsLayout() && view()->didFirstLayout()) 730 view()->layout(); 731 } 732 733 void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize) 734 { 735 m_doc->setPrinting(printing); 736 view()->setMediaType(printing ? "print" : "screen"); 737 m_doc->updateStyleSelector(); 738 view()->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize); 739 740 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) 741 child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize); 742 } 743 744 void Frame::setJSStatusBarText(const String& text) 745 { 746 ASSERT(m_doc); // Client calls shouldn't be made when the frame is in inconsistent state. 747 m_kjsStatusBarText = text; 748 if (m_page) 749 m_page->chrome()->setStatusbarText(this, m_kjsStatusBarText); 750 } 751 752 void Frame::setJSDefaultStatusBarText(const String& text) 753 { 754 ASSERT(m_doc); // Client calls shouldn't be made when the frame is in inconsistent state. 755 m_kjsDefaultStatusBarText = text; 756 if (m_page) 757 m_page->chrome()->setStatusbarText(this, m_kjsDefaultStatusBarText); 758 } 759 760 String Frame::jsStatusBarText() const 761 { 762 return m_kjsStatusBarText; 763 } 764 765 String Frame::jsDefaultStatusBarText() const 766 { 767 return m_kjsDefaultStatusBarText; 768 } 769 770 void Frame::setNeedsReapplyStyles() 771 { 772 // When the frame is not showing web content, it doesn't make sense to apply styles. 773 // If we tried, we'd end up doing things with the document, but the document, if one 774 // exists, is not currently shown and should be in the page cache. 775 if (!m_loader.client()->hasHTMLView()) 776 return; 777 778 if (m_needsReapplyStyles) 779 return; 780 781 m_needsReapplyStyles = true; 782 783 // FrameView's "layout" timer includes reapplyStyles, so despite its 784 // name, it's what we want to call here. 785 if (view()) 786 view()->scheduleRelayout(); 787 } 788 789 bool Frame::needsReapplyStyles() const 790 { 791 return m_needsReapplyStyles; 792 } 793 794 void Frame::reapplyStyles() 795 { 796 m_needsReapplyStyles = false; 797 798 // FIXME: This call doesn't really make sense in a function called reapplyStyles. 799 // We should probably eventually move it into its own function. 800 m_doc->docLoader()->setAutoLoadImages(m_page && m_page->settings()->loadsImagesAutomatically()); 801 802 // FIXME: It's not entirely clear why the following is needed. 803 // The document automatically does this as required when you set the style sheet. 804 // But we had problems when this code was removed. Details are in 805 // <http://bugs.webkit.org/show_bug.cgi?id=8079>. 806 m_doc->updateStyleSelector(); 807 } 808 809 void Frame::injectUserScripts(UserScriptInjectionTime injectionTime) 810 { 811 if (!m_page) 812 return; 813 814 // Walk the hashtable. Inject by world. 815 const UserScriptMap* userScripts = m_page->group().userScripts(); 816 if (!userScripts) 817 return; 818 UserScriptMap::const_iterator end = userScripts->end(); 819 for (UserScriptMap::const_iterator it = userScripts->begin(); it != end; ++it) 820 injectUserScriptsForWorld(it->first.get(), *it->second, injectionTime); 821 } 822 823 void Frame::injectUserScriptsForWorld(DOMWrapperWorld* world, const UserScriptVector& userScripts, UserScriptInjectionTime injectionTime) 824 { 825 if (userScripts.isEmpty()) 826 return; 827 828 Document* doc = document(); 829 if (!doc) 830 return; 831 832 Vector<ScriptSourceCode> sourceCode; 833 unsigned count = userScripts.size(); 834 for (unsigned i = 0; i < count; ++i) { 835 UserScript* script = userScripts[i].get(); 836 if (script->injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(doc->url(), script->whitelist(), script->blacklist())) 837 m_script.evaluateInWorld(ScriptSourceCode(script->source(), script->url()), world); 838 } 839 } 840 841 bool Frame::shouldChangeSelection(const VisibleSelection& newSelection) const 842 { 843 return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false); 844 } 845 846 bool Frame::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const 847 { 848 return editor()->client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), 849 affinity, stillSelecting); 850 } 851 852 bool Frame::shouldDeleteSelection(const VisibleSelection& selection) const 853 { 854 return editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get()); 855 } 856 857 bool Frame::isContentEditable() const 858 { 859 if (m_editor.clientIsEditable()) 860 return true; 861 return m_doc->inDesignMode(); 862 } 863 864 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) 865 const short enableRomanKeyboardsOnly = -23; 866 #endif 867 void Frame::setUseSecureKeyboardEntry(bool enable) 868 { 869 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) 870 if (enable == IsSecureEventInputEnabled()) 871 return; 872 if (enable) { 873 EnableSecureEventInput(); 874 #ifdef BUILDING_ON_TIGER 875 KeyScript(enableRomanKeyboardsOnly); 876 #else 877 // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is 878 // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated 879 // after focusing a node. 880 CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); 881 TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources); 882 CFRelease(inputSources); 883 #endif 884 } else { 885 DisableSecureEventInput(); 886 #ifdef BUILDING_ON_TIGER 887 KeyScript(smKeyEnableKybds); 888 #else 889 TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag); 890 #endif 891 } 892 #endif 893 } 894 895 void Frame::updateSecureKeyboardEntryIfActive() 896 { 897 if (selection()->isFocusedAndActive()) 898 setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive()); 899 } 900 901 CSSMutableStyleDeclaration *Frame::typingStyle() const 902 { 903 return m_typingStyle.get(); 904 } 905 906 void Frame::setTypingStyle(CSSMutableStyleDeclaration *style) 907 { 908 m_typingStyle = style; 909 } 910 911 void Frame::clearTypingStyle() 912 { 913 m_typingStyle = 0; 914 } 915 916 void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction) 917 { 918 if (!style || !style->length()) { 919 clearTypingStyle(); 920 return; 921 } 922 923 // Calculate the current typing style. 924 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable(); 925 if (typingStyle()) { 926 typingStyle()->merge(mutableStyle.get()); 927 mutableStyle = typingStyle(); 928 } 929 930 RefPtr<CSSValue> unicodeBidi; 931 RefPtr<CSSValue> direction; 932 if (editingAction == EditActionSetWritingDirection) { 933 unicodeBidi = mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 934 direction = mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 935 } 936 937 Node* node = selection()->selection().visibleStart().deepEquivalent().node(); 938 computedStyle(node)->diff(mutableStyle.get()); 939 940 if (editingAction == EditActionSetWritingDirection && unicodeBidi) { 941 ASSERT(unicodeBidi->isPrimitiveValue()); 942 mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent()); 943 if (direction) { 944 ASSERT(direction->isPrimitiveValue()); 945 mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); 946 } 947 } 948 949 // Handle block styles, substracting these from the typing style. 950 RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties(); 951 blockStyle->diff(mutableStyle.get()); 952 if (blockStyle->length() > 0) 953 applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction)); 954 955 // Set the remaining style as the typing style. 956 m_typingStyle = mutableStyle.release(); 957 } 958 959 String Frame::selectionStartStylePropertyValue(int stylePropertyID) const 960 { 961 Node *nodeToRemove; 962 RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove); 963 if (!selectionStyle) 964 return String(); 965 966 String value = selectionStyle->getPropertyValue(stylePropertyID); 967 968 if (nodeToRemove) { 969 ExceptionCode ec = 0; 970 nodeToRemove->remove(ec); 971 ASSERT(!ec); 972 } 973 974 return value; 975 } 976 977 PassRefPtr<CSSComputedStyleDeclaration> Frame::selectionComputedStyle(Node*& nodeToRemove) const 978 { 979 nodeToRemove = 0; 980 981 if (selection()->isNone()) 982 return 0; 983 984 RefPtr<Range> range(selection()->toNormalizedRange()); 985 Position pos = range->editingStartPosition(); 986 987 Element *elem = pos.element(); 988 if (!elem) 989 return 0; 990 991 RefPtr<Element> styleElement = elem; 992 ExceptionCode ec = 0; 993 994 if (m_typingStyle) { 995 styleElement = document()->createElement(spanTag, false); 996 997 styleElement->setAttribute(styleAttr, m_typingStyle->cssText().impl(), ec); 998 ASSERT(!ec); 999 1000 styleElement->appendChild(document()->createEditingTextNode(""), ec); 1001 ASSERT(!ec); 1002 1003 if (elem->renderer() && elem->renderer()->canHaveChildren()) { 1004 elem->appendChild(styleElement, ec); 1005 } else { 1006 Node *parent = elem->parent(); 1007 Node *next = elem->nextSibling(); 1008 1009 if (next) 1010 parent->insertBefore(styleElement, next, ec); 1011 else 1012 parent->appendChild(styleElement, ec); 1013 } 1014 ASSERT(!ec); 1015 1016 nodeToRemove = styleElement.get(); 1017 } 1018 1019 return computedStyle(styleElement.release()); 1020 } 1021 1022 void Frame::textFieldDidBeginEditing(Element* e) 1023 { 1024 if (editor()->client()) 1025 editor()->client()->textFieldDidBeginEditing(e); 1026 } 1027 1028 void Frame::textFieldDidEndEditing(Element* e) 1029 { 1030 if (editor()->client()) 1031 editor()->client()->textFieldDidEndEditing(e); 1032 } 1033 1034 void Frame::textDidChangeInTextField(Element* e) 1035 { 1036 if (editor()->client()) 1037 editor()->client()->textDidChangeInTextField(e); 1038 } 1039 1040 bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke) 1041 { 1042 if (editor()->client()) 1043 return editor()->client()->doTextFieldCommandFromEvent(e, ke); 1044 1045 return false; 1046 } 1047 1048 void Frame::textWillBeDeletedInTextField(Element* input) 1049 { 1050 if (editor()->client()) 1051 editor()->client()->textWillBeDeletedInTextField(input); 1052 } 1053 1054 void Frame::textDidChangeInTextArea(Element* e) 1055 { 1056 if (editor()->client()) 1057 editor()->client()->textDidChangeInTextArea(e); 1058 } 1059 1060 void Frame::applyEditingStyleToBodyElement() const 1061 { 1062 RefPtr<NodeList> list = m_doc->getElementsByTagName("body"); 1063 unsigned len = list->length(); 1064 for (unsigned i = 0; i < len; i++) 1065 applyEditingStyleToElement(static_cast<Element*>(list->item(i))); 1066 } 1067 1068 void Frame::removeEditingStyleFromBodyElement() const 1069 { 1070 RefPtr<NodeList> list = m_doc->getElementsByTagName("body"); 1071 unsigned len = list->length(); 1072 for (unsigned i = 0; i < len; i++) 1073 removeEditingStyleFromElement(static_cast<Element*>(list->item(i))); 1074 } 1075 1076 void Frame::applyEditingStyleToElement(Element* element) const 1077 { 1078 if (!element) 1079 return; 1080 1081 CSSStyleDeclaration* style = element->style(); 1082 ASSERT(style); 1083 1084 ExceptionCode ec = 0; 1085 style->setProperty(CSSPropertyWordWrap, "break-word", false, ec); 1086 ASSERT(!ec); 1087 style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec); 1088 ASSERT(!ec); 1089 style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec); 1090 ASSERT(!ec); 1091 } 1092 1093 void Frame::removeEditingStyleFromElement(Element*) const 1094 { 1095 } 1096 1097 #ifndef NDEBUG 1098 static HashSet<Frame*>& keepAliveSet() 1099 { 1100 DEFINE_STATIC_LOCAL(HashSet<Frame*>, staticKeepAliveSet, ()); 1101 return staticKeepAliveSet; 1102 } 1103 #endif 1104 1105 void Frame::keepAlive() 1106 { 1107 if (m_lifeSupportTimer.isActive()) 1108 return; 1109 #ifndef NDEBUG 1110 keepAliveSet().add(this); 1111 #endif 1112 ref(); 1113 m_lifeSupportTimer.startOneShot(0); 1114 } 1115 1116 #ifndef NDEBUG 1117 void Frame::cancelAllKeepAlive() 1118 { 1119 HashSet<Frame*>::iterator end = keepAliveSet().end(); 1120 for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) { 1121 Frame* frame = *it; 1122 frame->m_lifeSupportTimer.stop(); 1123 frame->deref(); 1124 } 1125 keepAliveSet().clear(); 1126 } 1127 #endif 1128 1129 void Frame::lifeSupportTimerFired(Timer<Frame>*) 1130 { 1131 #ifndef NDEBUG 1132 keepAliveSet().remove(this); 1133 #endif 1134 deref(); 1135 } 1136 1137 void Frame::clearDOMWindow() 1138 { 1139 if (m_domWindow) { 1140 m_liveFormerWindows.add(m_domWindow.get()); 1141 m_domWindow->clear(); 1142 } 1143 m_domWindow = 0; 1144 } 1145 1146 RenderView* Frame::contentRenderer() const 1147 { 1148 Document* doc = document(); 1149 if (!doc) 1150 return 0; 1151 RenderObject* object = doc->renderer(); 1152 if (!object) 1153 return 0; 1154 ASSERT(object->isRenderView()); 1155 return toRenderView(object); 1156 } 1157 1158 HTMLFrameOwnerElement* Frame::ownerElement() const 1159 { 1160 return m_ownerElement; 1161 } 1162 1163 RenderPart* Frame::ownerRenderer() const 1164 { 1165 HTMLFrameOwnerElement* ownerElement = m_ownerElement; 1166 if (!ownerElement) 1167 return 0; 1168 RenderObject* object = ownerElement->renderer(); 1169 if (!object) 1170 return 0; 1171 // FIXME: If <object> is ever fixed to disassociate itself from frames 1172 // that it has started but canceled, then this can turn into an ASSERT 1173 // since m_ownerElement would be 0 when the load is canceled. 1174 // https://bugs.webkit.org/show_bug.cgi?id=18585 1175 if (!object->isRenderPart()) 1176 return 0; 1177 return toRenderPart(object); 1178 } 1179 1180 bool Frame::isDisconnected() const 1181 { 1182 return m_isDisconnected; 1183 } 1184 1185 void Frame::setIsDisconnected(bool isDisconnected) 1186 { 1187 m_isDisconnected = isDisconnected; 1188 } 1189 1190 bool Frame::excludeFromTextSearch() const 1191 { 1192 return m_excludeFromTextSearch; 1193 } 1194 1195 void Frame::setExcludeFromTextSearch(bool exclude) 1196 { 1197 m_excludeFromTextSearch = exclude; 1198 } 1199 1200 // returns FloatRect because going through IntRect would truncate any floats 1201 FloatRect Frame::selectionBounds(bool clipToVisibleContent) const 1202 { 1203 RenderView* root = contentRenderer(); 1204 FrameView* view = m_view.get(); 1205 if (!root || !view) 1206 return IntRect(); 1207 1208 IntRect selectionRect = root->selectionBounds(clipToVisibleContent); 1209 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect; 1210 } 1211 1212 void Frame::selectionTextRects(Vector<FloatRect>& rects, SelectionRectRespectTransforms respectTransforms, bool clipToVisibleContent) const 1213 { 1214 RenderView* root = contentRenderer(); 1215 if (!root) 1216 return; 1217 1218 RefPtr<Range> selectedRange = selection()->toNormalizedRange(); 1219 1220 FloatRect visibleContentRect = m_view->visibleContentRect(); 1221 1222 // FIMXE: we are appending empty rects to the list for those that fall outside visibleContentRect. 1223 // We may not want to do that. 1224 if (respectTransforms) { 1225 Vector<FloatQuad> quads; 1226 selectedRange->textQuads(quads, true); 1227 1228 unsigned size = quads.size(); 1229 for (unsigned i = 0; i < size; ++i) { 1230 IntRect currRect = quads[i].enclosingBoundingBox(); 1231 if (clipToVisibleContent) 1232 rects.append(intersection(currRect, visibleContentRect)); 1233 else 1234 rects.append(currRect); 1235 } 1236 } else { 1237 Vector<IntRect> intRects; 1238 selectedRange->textRects(intRects, true); 1239 1240 unsigned size = intRects.size(); 1241 for (unsigned i = 0; i < size; ++i) { 1242 if (clipToVisibleContent) 1243 rects.append(intersection(intRects[i], visibleContentRect)); 1244 else 1245 rects.append(intRects[i]); 1246 } 1247 } 1248 } 1249 1250 // Scans logically forward from "start", including any child frames 1251 static HTMLFormElement *scanForForm(Node *start) 1252 { 1253 Node *n; 1254 for (n = start; n; n = n->traverseNextNode()) { 1255 if (n->hasTagName(formTag)) 1256 return static_cast<HTMLFormElement*>(n); 1257 else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement()) 1258 return static_cast<HTMLFormControlElement*>(n)->form(); 1259 else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) { 1260 Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument(); 1261 if (HTMLFormElement *frameResult = scanForForm(childDoc)) 1262 return frameResult; 1263 } 1264 } 1265 return 0; 1266 } 1267 1268 // We look for either the form containing the current focus, or for one immediately after it 1269 HTMLFormElement *Frame::currentForm() const 1270 { 1271 // start looking either at the active (first responder) node, or where the selection is 1272 Node *start = m_doc ? m_doc->focusedNode() : 0; 1273 if (!start) 1274 start = selection()->start().node(); 1275 1276 // try walking up the node tree to find a form element 1277 Node *n; 1278 for (n = start; n; n = n->parentNode()) { 1279 if (n->hasTagName(formTag)) 1280 return static_cast<HTMLFormElement*>(n); 1281 else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement()) 1282 return static_cast<HTMLFormControlElement*>(n)->form(); 1283 } 1284 1285 // try walking forward in the node tree to find a form element 1286 return start ? scanForForm(start) : 0; 1287 } 1288 1289 void Frame::revealSelection(const ScrollAlignment& alignment, bool revealExtent) 1290 { 1291 IntRect rect; 1292 1293 switch (selection()->selectionType()) { 1294 case VisibleSelection::NoSelection: 1295 return; 1296 case VisibleSelection::CaretSelection: 1297 rect = selection()->absoluteCaretBounds(); 1298 break; 1299 case VisibleSelection::RangeSelection: 1300 rect = revealExtent ? VisiblePosition(selection()->extent()).absoluteCaretBounds() : enclosingIntRect(selectionBounds(false)); 1301 break; 1302 } 1303 1304 Position start = selection()->start(); 1305 ASSERT(start.node()); 1306 if (start.node() && start.node()->renderer()) { 1307 // FIXME: This code only handles scrolling the startContainer's layer, but 1308 // the selection rect could intersect more than just that. 1309 // See <rdar://problem/4799899>. 1310 if (RenderLayer* layer = start.node()->renderer()->enclosingLayer()) 1311 layer->scrollRectToVisible(rect, false, alignment, alignment); 1312 } 1313 } 1314 1315 Frame* Frame::frameForWidget(const Widget* widget) 1316 { 1317 ASSERT_ARG(widget, widget); 1318 1319 if (RenderWidget* renderer = RenderWidget::find(widget)) 1320 if (Node* node = renderer->node()) 1321 return node->document()->frame(); 1322 1323 // Assume all widgets are either a FrameView or owned by a RenderWidget. 1324 // FIXME: That assumption is not right for scroll bars! 1325 ASSERT(widget->isFrameView()); 1326 return static_cast<const FrameView*>(widget)->frame(); 1327 } 1328 1329 void Frame::clearTimers(FrameView *view, Document *document) 1330 { 1331 if (view) { 1332 view->unscheduleRelayout(); 1333 if (view->frame()) { 1334 view->frame()->animation()->suspendAnimations(document); 1335 view->frame()->eventHandler()->stopAutoscrollTimer(); 1336 } 1337 } 1338 } 1339 1340 void Frame::clearTimers() 1341 { 1342 clearTimers(m_view.get(), document()); 1343 } 1344 1345 RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const 1346 { 1347 nodeToRemove = 0; 1348 1349 if (selection()->isNone()) 1350 return 0; 1351 1352 Position pos = selection()->selection().visibleStart().deepEquivalent(); 1353 if (!pos.isCandidate()) 1354 return 0; 1355 Node *node = pos.node(); 1356 if (!node) 1357 return 0; 1358 1359 if (!m_typingStyle) 1360 return node->renderer()->style(); 1361 1362 RefPtr<Element> styleElement = document()->createElement(spanTag, false); 1363 1364 ExceptionCode ec = 0; 1365 String styleText = m_typingStyle->cssText() + " display: inline"; 1366 styleElement->setAttribute(styleAttr, styleText.impl(), ec); 1367 ASSERT(!ec); 1368 1369 styleElement->appendChild(document()->createEditingTextNode(""), ec); 1370 ASSERT(!ec); 1371 1372 node->parentNode()->appendChild(styleElement, ec); 1373 ASSERT(!ec); 1374 1375 nodeToRemove = styleElement.get(); 1376 return styleElement->renderer() ? styleElement->renderer()->style() : 0; 1377 } 1378 1379 void Frame::setSelectionFromNone() 1380 { 1381 // Put a caret inside the body if the entire frame is editable (either the 1382 // entire WebView is editable or designMode is on for this document). 1383 Document *doc = document(); 1384 bool caretBrowsing = settings() && settings()->caretBrowsingEnabled(); 1385 if (!selection()->isNone() || !(isContentEditable() || caretBrowsing)) 1386 return; 1387 1388 Node* node = doc->documentElement(); 1389 while (node && !node->hasTagName(bodyTag)) 1390 node = node->traverseNextNode(); 1391 if (node) 1392 selection()->setSelection(VisibleSelection(Position(node, 0), DOWNSTREAM)); 1393 } 1394 1395 bool Frame::inViewSourceMode() const 1396 { 1397 return m_inViewSourceMode; 1398 } 1399 1400 void Frame::setInViewSourceMode(bool mode) 1401 { 1402 m_inViewSourceMode = mode; 1403 } 1404 1405 // Searches from the beginning of the document if nothing is selected. 1406 bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection) 1407 { 1408 if (target.isEmpty()) 1409 return false; 1410 1411 if (excludeFromTextSearch()) 1412 return false; 1413 1414 // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge 1415 // is used depends on whether we're searching forward or backward, and whether startInSelection is set. 1416 RefPtr<Range> searchRange(rangeOfContents(document())); 1417 VisibleSelection selection = this->selection()->selection(); 1418 1419 if (forward) 1420 setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd()); 1421 else 1422 setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart()); 1423 1424 Node* shadowTreeRoot = selection.shadowTreeRootNode(); 1425 if (shadowTreeRoot) { 1426 ExceptionCode ec = 0; 1427 if (forward) 1428 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); 1429 else 1430 searchRange->setStart(shadowTreeRoot, 0, ec); 1431 } 1432 1433 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag)); 1434 // If we started in the selection and the found range exactly matches the existing selection, find again. 1435 // Build a selection with the found range to remove collapsed whitespace. 1436 // Compare ranges instead of selection objects to ignore the way that the current selection was made. 1437 if (startInSelection && *VisibleSelection(resultRange.get()).toNormalizedRange() == *selection.toNormalizedRange()) { 1438 searchRange = rangeOfContents(document()); 1439 if (forward) 1440 setStart(searchRange.get(), selection.visibleEnd()); 1441 else 1442 setEnd(searchRange.get(), selection.visibleStart()); 1443 1444 if (shadowTreeRoot) { 1445 ExceptionCode ec = 0; 1446 if (forward) 1447 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); 1448 else 1449 searchRange->setStart(shadowTreeRoot, 0, ec); 1450 } 1451 1452 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag); 1453 } 1454 1455 ExceptionCode exception = 0; 1456 1457 // If nothing was found in the shadow tree, search in main content following the shadow tree. 1458 if (resultRange->collapsed(exception) && shadowTreeRoot) { 1459 searchRange = rangeOfContents(document()); 1460 if (forward) 1461 searchRange->setStartAfter(shadowTreeRoot->shadowParentNode(), exception); 1462 else 1463 searchRange->setEndBefore(shadowTreeRoot->shadowParentNode(), exception); 1464 1465 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag); 1466 } 1467 1468 if (!editor()->insideVisibleArea(resultRange.get())) { 1469 resultRange = editor()->nextVisibleRange(resultRange.get(), target, forward, caseFlag, wrapFlag); 1470 if (!resultRange) 1471 return false; 1472 } 1473 1474 // If we didn't find anything and we're wrapping, search again in the entire document (this will 1475 // redundantly re-search the area already searched in some cases). 1476 if (resultRange->collapsed(exception) && wrapFlag) { 1477 searchRange = rangeOfContents(document()); 1478 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag); 1479 // We used to return false here if we ended up with the same range that we started with 1480 // (e.g., the selection was already the only instance of this text). But we decided that 1481 // this should be a success case instead, so we'll just fall through in that case. 1482 } 1483 1484 if (resultRange->collapsed(exception)) 1485 return false; 1486 1487 this->selection()->setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM)); 1488 revealSelection(); 1489 return true; 1490 } 1491 1492 unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit) 1493 { 1494 if (target.isEmpty()) 1495 return 0; 1496 1497 RefPtr<Range> searchRange(rangeOfContents(document())); 1498 1499 ExceptionCode exception = 0; 1500 unsigned matchCount = 0; 1501 do { 1502 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag)); 1503 if (resultRange->collapsed(exception)) { 1504 if (!resultRange->startContainer()->isInShadowTree()) 1505 break; 1506 1507 searchRange = rangeOfContents(document()); 1508 searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception); 1509 continue; 1510 } 1511 1512 // Only treat the result as a match if it is visible 1513 if (editor()->insideVisibleArea(resultRange.get())) { 1514 ++matchCount; 1515 document()->addMarker(resultRange.get(), DocumentMarker::TextMatch); 1516 } 1517 1518 // Stop looking if we hit the specified limit. A limit of 0 means no limit. 1519 if (limit > 0 && matchCount >= limit) 1520 break; 1521 1522 // Set the new start for the search range to be the end of the previous 1523 // result range. There is no need to use a VisiblePosition here, 1524 // since findPlainText will use a TextIterator to go over the visible 1525 // text nodes. 1526 searchRange->setStart(resultRange->endContainer(exception), resultRange->endOffset(exception), exception); 1527 1528 Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); 1529 if (searchRange->collapsed(exception) && shadowTreeRoot) 1530 searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception); 1531 } while (true); 1532 1533 // Do a "fake" paint in order to execute the code that computes the rendered rect for 1534 // each text match. 1535 Document* doc = document(); 1536 if (m_view && contentRenderer()) { 1537 doc->updateLayout(); // Ensure layout is up to date. 1538 IntRect visibleRect = m_view->visibleContentRect(); 1539 if (!visibleRect.isEmpty()) { 1540 GraphicsContext context((PlatformGraphicsContext*)0); 1541 context.setPaintingDisabled(true); 1542 m_view->paintContents(&context, visibleRect); 1543 } 1544 } 1545 1546 return matchCount; 1547 } 1548 1549 bool Frame::markedTextMatchesAreHighlighted() const 1550 { 1551 return m_highlightTextMatches; 1552 } 1553 1554 void Frame::setMarkedTextMatchesAreHighlighted(bool flag) 1555 { 1556 if (flag == m_highlightTextMatches) 1557 return; 1558 1559 m_highlightTextMatches = flag; 1560 document()->repaintMarkers(DocumentMarker::TextMatch); 1561 } 1562 1563 FrameTree* Frame::tree() const 1564 { 1565 return &m_treeNode; 1566 } 1567 1568 void Frame::setDOMWindow(DOMWindow* domWindow) 1569 { 1570 if (m_domWindow) { 1571 m_liveFormerWindows.add(m_domWindow.get()); 1572 m_domWindow->clear(); 1573 } 1574 m_domWindow = domWindow; 1575 } 1576 1577 DOMWindow* Frame::domWindow() const 1578 { 1579 if (!m_domWindow) 1580 m_domWindow = DOMWindow::create(const_cast<Frame*>(this)); 1581 1582 return m_domWindow.get(); 1583 } 1584 1585 void Frame::clearFormerDOMWindow(DOMWindow* window) 1586 { 1587 m_liveFormerWindows.remove(window); 1588 } 1589 1590 Page* Frame::page() const 1591 { 1592 return m_page; 1593 } 1594 1595 void Frame::detachFromPage() 1596 { 1597 m_page = 0; 1598 } 1599 1600 EventHandler* Frame::eventHandler() const 1601 { 1602 return &m_eventHandler; 1603 } 1604 1605 void Frame::pageDestroyed() 1606 { 1607 if (Frame* parent = tree()->parent()) 1608 parent->loader()->checkLoadComplete(); 1609 1610 // FIXME: It's unclear as to why this is called more than once, but it is, 1611 // so page() could be NULL. 1612 if (page() && page()->focusController()->focusedFrame() == this) 1613 page()->focusController()->setFocusedFrame(0); 1614 1615 script()->clearWindowShell(); 1616 script()->clearScriptObjects(); 1617 script()->updatePlatformScriptObjects(); 1618 1619 detachFromPage(); 1620 } 1621 1622 void Frame::disconnectOwnerElement() 1623 { 1624 if (m_ownerElement) { 1625 if (Document* doc = document()) 1626 doc->clearAXObjectCache(); 1627 m_ownerElement->m_contentFrame = 0; 1628 if (m_page) 1629 m_page->decrementFrameCount(); 1630 } 1631 m_ownerElement = 0; 1632 } 1633 1634 String Frame::documentTypeString() const 1635 { 1636 if (DocumentType* doctype = document()->doctype()) 1637 return createMarkup(doctype); 1638 1639 return String(); 1640 } 1641 1642 void Frame::focusWindow() 1643 { 1644 if (!page()) 1645 return; 1646 1647 // If we're a top level window, bring the window to the front. 1648 if (!tree()->parent()) 1649 #ifdef ANDROID_USER_GESTURE 1650 // FrameLoader::isProcessingUserGesture() will be false when a 1651 // different frame tries to focus this frame through javascript. 1652 page()->chrome()->focus(m_loader.isProcessingUserGesture()); 1653 #else 1654 page()->chrome()->focus(); 1655 #endif 1656 1657 eventHandler()->focusDocumentView(); 1658 } 1659 1660 void Frame::unfocusWindow() 1661 { 1662 if (!page()) 1663 return; 1664 1665 // If we're a top level window, deactivate the window. 1666 if (!tree()->parent()) 1667 page()->chrome()->unfocus(); 1668 } 1669 1670 bool Frame::shouldClose() 1671 { 1672 Chrome* chrome = page() ? page()->chrome() : 0; 1673 if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel()) 1674 return true; 1675 1676 if (!m_domWindow) 1677 return true; 1678 1679 RefPtr<Document> doc = document(); 1680 HTMLElement* body = doc->body(); 1681 if (!body) 1682 return true; 1683 1684 RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); 1685 m_domWindow->dispatchEvent(beforeUnloadEvent.get(), m_domWindow->document()); 1686 1687 if (!beforeUnloadEvent->defaultPrevented()) 1688 doc->defaultEventHandler(beforeUnloadEvent.get()); 1689 if (beforeUnloadEvent->result().isNull()) 1690 return true; 1691 1692 String text = doc->displayStringModifiedByEncoding(beforeUnloadEvent->result()); 1693 return chrome->runBeforeUnloadConfirmPanel(text, this); 1694 } 1695 1696 void Frame::scheduleClose() 1697 { 1698 if (!shouldClose()) 1699 return; 1700 1701 Chrome* chrome = page() ? page()->chrome() : 0; 1702 if (chrome) 1703 chrome->closeWindowSoon(); 1704 } 1705 1706 void Frame::respondToChangedSelection(const VisibleSelection& oldSelection, bool closeTyping) 1707 { 1708 bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled(); 1709 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled(); 1710 if (isContinuousSpellCheckingEnabled) { 1711 VisibleSelection newAdjacentWords; 1712 VisibleSelection newSelectedSentence; 1713 bool caretBrowsing = settings() && settings()->caretBrowsingEnabled(); 1714 if (selection()->selection().isContentEditable() || caretBrowsing) { 1715 VisiblePosition newStart(selection()->selection().visibleStart()); 1716 newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary)); 1717 if (isContinuousGrammarCheckingEnabled) 1718 newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart)); 1719 } 1720 1721 // When typing we check spelling elsewhere, so don't redo it here. 1722 // If this is a change in selection resulting from a delete operation, 1723 // oldSelection may no longer be in the document. 1724 if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) { 1725 VisiblePosition oldStart(oldSelection.visibleStart()); 1726 VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary)); 1727 if (oldAdjacentWords != newAdjacentWords) { 1728 if (isContinuousGrammarCheckingEnabled) { 1729 VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart)); 1730 editor()->markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence); 1731 } else 1732 editor()->markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords); 1733 } 1734 } 1735 1736 // This only erases markers that are in the first unit (word or sentence) of the selection. 1737 // Perhaps peculiar, but it matches AppKit. 1738 if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange()) 1739 document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling); 1740 if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange()) 1741 document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar); 1742 } 1743 1744 // When continuous spell checking is off, existing markers disappear after the selection changes. 1745 if (!isContinuousSpellCheckingEnabled) 1746 document()->removeMarkers(DocumentMarker::Spelling); 1747 if (!isContinuousGrammarCheckingEnabled) 1748 document()->removeMarkers(DocumentMarker::Grammar); 1749 1750 editor()->respondToChangedSelection(oldSelection); 1751 } 1752 1753 VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) 1754 { 1755 HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true); 1756 Node* node = result.innerNode(); 1757 if (!node) 1758 return VisiblePosition(); 1759 RenderObject* renderer = node->renderer(); 1760 if (!renderer) 1761 return VisiblePosition(); 1762 VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint()); 1763 if (visiblePos.isNull()) 1764 visiblePos = VisiblePosition(Position(node, 0)); 1765 return visiblePos; 1766 } 1767 1768 Document* Frame::documentAtPoint(const IntPoint& point) 1769 { 1770 if (!view()) 1771 return 0; 1772 1773 IntPoint pt = view()->windowToContents(point); 1774 HitTestResult result = HitTestResult(pt); 1775 1776 if (contentRenderer()) 1777 result = eventHandler()->hitTestResultAtPoint(pt, false); 1778 return result.innerNode() ? result.innerNode()->document() : 0; 1779 } 1780 1781 void Frame::createView(const IntSize& viewportSize, 1782 const Color& backgroundColor, bool transparent, 1783 const IntSize& fixedLayoutSize, bool useFixedLayout, 1784 ScrollbarMode horizontalScrollbarMode, ScrollbarMode verticalScrollbarMode) 1785 { 1786 ASSERT(this); 1787 ASSERT(m_page); 1788 1789 bool isMainFrame = this == m_page->mainFrame(); 1790 1791 if (isMainFrame && view()) 1792 view()->setParentVisible(false); 1793 1794 setView(0); 1795 1796 RefPtr<FrameView> frameView; 1797 if (isMainFrame) { 1798 frameView = FrameView::create(this, viewportSize); 1799 frameView->setFixedLayoutSize(fixedLayoutSize); 1800 frameView->setUseFixedLayout(useFixedLayout); 1801 } else 1802 frameView = FrameView::create(this); 1803 1804 frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode); 1805 1806 setView(frameView); 1807 1808 if (backgroundColor.isValid()) 1809 frameView->updateBackgroundRecursively(backgroundColor, transparent); 1810 1811 if (isMainFrame) 1812 frameView->setParentVisible(true); 1813 1814 if (ownerRenderer()) 1815 ownerRenderer()->setWidget(frameView); 1816 1817 if (HTMLFrameOwnerElement* owner = ownerElement()) 1818 view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); 1819 } 1820 1821 } // namespace WebCore 1822