2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 * 1999 Lars Knoll <knoll@kde.org>
4 * 1999 Antti Koivisto <koivisto@kde.org>
5 * 2000 Simon Hausmann <hausmann@kde.org>
6 * 2000 Stefan Schimanski <1Stein@gmx.de>
7 * 2001 George Staikos <staikos@kde.org>
8 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
9 * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
10 * Copyright (C) 2007 Trolltech ASA
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
30 #include "FramePrivate.h"
32 #include "ApplyStyleCommand.h"
33 #include "BeforeUnloadEvent.h"
34 #include "CSSComputedStyleDeclaration.h"
35 #include "CSSProperty.h"
36 #include "CSSPropertyNames.h"
37 #include "CachedCSSStyleSheet.h"
38 #include "DOMWindow.h"
39 #include "DocLoader.h"
40 #include "DocumentType.h"
41 #include "EditingText.h"
42 #include "EditorClient.h"
43 #include "EventNames.h"
44 #include "FocusController.h"
45 #include "FrameLoader.h"
46 #include "FrameView.h"
47 #include "GraphicsContext.h"
48 #include "HTMLDocument.h"
49 #include "HTMLFormElement.h"
50 #include "HTMLFrameElementBase.h"
51 #include "HTMLGenericFormElement.h"
52 #include "HTMLNames.h"
53 #include "HTMLTableCellElement.h"
54 #include "HitTestResult.h"
55 #include "JSDOMWindow.h"
57 #include "MediaFeatureNames.h"
58 #include "NP_jsobject.h"
59 #include "Navigator.h"
62 #include "RegularExpression.h"
63 #include "RenderPart.h"
64 #include "RenderTableCell.h"
65 #include "RenderTextControl.h"
66 #include "RenderTheme.h"
67 #include "RenderView.h"
69 #include "SystemTime.h"
70 #include "TextIterator.h"
71 #include "TextResourceDecoder.h"
73 #include "kjs_proxy.h"
74 #include "npruntime_impl.h"
75 #include "runtime_root.h"
76 #include "visible_units.h"
78 #if FRAME_LOADS_USER_STYLESHEET
79 #include "UserStyleSheetLoader.h"
83 #include "SVGDocument.h"
84 #include "SVGDocumentExtensions.h"
86 #include "XLinkNames.h"
95 using namespace EventNames;
96 using namespace HTMLNames;
98 double Frame::s_currentPaintTimeStamp = 0.0;
101 WTFLogChannel LogWebCoreFrameLeaks = { 0x00000000, "", WTFLogChannelOn };
103 struct FrameCounter {
108 LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count);
111 int FrameCounter::count = 0;
112 static FrameCounter frameCounter;
115 static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
119 return ownerElement->document()->frame();
122 Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
123 : RefCounted<Frame>(0)
124 , d(new FramePrivate(page, parentFromOwnerElement(ownerElement), this, ownerElement, frameLoaderClient))
126 AtomicString::init();
129 QualifiedName::init();
130 MediaFeatureNames::init();
140 static bool initializedJavaJSBindings;
141 if (!initializedJavaJSBindings) {
142 initializedJavaJSBindings = true;
143 initJavaJSBindings();
148 page->setMainFrame(this);
150 // FIXME: It's bad to have a ref() here but not in the !ownerElement case.
151 // We need to straighten this out.
153 page->incrementFrameCount();
154 ownerElement->m_contentFrame = this;
158 ++FrameCounter::count;
165 loader()->clearRecordedFormValues();
166 loader()->cancelAndClear();
168 // FIXME: We should not be doing all this work inside the destructor
170 ASSERT(!d->m_lifeSupportTimer.isActive());
173 --FrameCounter::count;
176 if (d->m_jscript && d->m_jscript->haveGlobalObject())
177 static_cast<JSDOMWindow*>(d->m_jscript->globalObject())->disconnectFrame();
179 disconnectOwnerElement();
182 d->m_domWindow->disconnectFrame();
186 d->m_view->clearFrame();
189 ASSERT(!d->m_lifeSupportTimer.isActive());
191 #if FRAME_LOADS_USER_STYLESHEET
192 delete d->m_userStyleSheetLoader;
204 FrameLoader* Frame::loader() const
209 FrameView* Frame::view() const
211 return d->m_view.get();
214 void Frame::setView(FrameView* view)
216 // Detach the document now, so any onUnload handlers get run - if
217 // we wait until the view is destroyed, then things won't be
218 // hooked up enough for some JavaScript calls to work.
219 if (!view && d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
220 // FIXME: We don't call willRemove here. Why is that OK?
223 d->m_view->unscheduleRelayout();
225 eventHandler()->clear();
229 // Only one form submission is allowed per view of a part.
230 // Since this part may be getting reused as a result of being
231 // pulled from the back/forward cache, reset this flag.
232 loader()->resetMultipleFormSubmissionProtection();
235 KJSProxy *Frame::scriptProxy()
238 d->m_jscript = new KJSProxy(this);
242 Document *Frame::document() const
245 return d->m_doc.get();
249 void Frame::setDocument(PassRefPtr<Document> newDoc)
251 if (d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
252 // FIXME: We don't call willRemove here. Why is that OK?
257 if (d->m_doc && selectionController()->isFocusedAndActive())
258 setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
260 if (d->m_doc && !d->m_doc->attached())
263 // Remove the cached 'document' property, which is now stale.
265 d->m_jscript->clearDocumentWrapper();
268 Settings* Frame::settings() const
270 return d->m_page ? d->m_page->settings() : 0;
273 String Frame::selectedText() const
275 return plainText(selectionController()->toRange().get());
278 IntRect Frame::firstRectForRange(Range* range) const
280 int extraWidthToEndOfLine = 0;
281 ExceptionCode ec = 0;
282 ASSERT(range->startContainer(ec));
283 ASSERT(range->endContainer(ec));
284 IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine);
286 IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(range->endOffset(ec), UPSTREAM);
289 if (startCaretRect.y() == endCaretRect.y()) {
290 // start and end are on the same line
291 return IntRect(min(startCaretRect.x(), endCaretRect.x()),
293 abs(endCaretRect.x() - startCaretRect.x()),
294 max(startCaretRect.height(), endCaretRect.height()));
297 // start and end aren't on the same line, so go from start to the end of its line
298 return IntRect(startCaretRect.x(),
300 startCaretRect.width() + extraWidthToEndOfLine,
301 startCaretRect.height());
304 SelectionController* Frame::selectionController() const
306 return &d->m_selectionController;
309 Editor* Frame::editor() const
314 TextGranularity Frame::selectionGranularity() const
316 return d->m_selectionGranularity;
319 void Frame::setSelectionGranularity(TextGranularity granularity) const
321 d->m_selectionGranularity = granularity;
324 SelectionController* Frame::dragCaretController() const
326 return d->m_page->dragCaretController();
330 AnimationController* Frame::animationController() const
332 return &d->m_animationController;
335 static RegularExpression* createRegExpForLabels(const Vector<String>& labels)
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.
340 static RegularExpression wordRegExp = RegularExpression("\\w");
342 unsigned int numLabels = labels.size();
344 for (i = 0; i < numLabels; i++) {
345 String label = labels[i];
347 bool startsWithWordChar = false;
348 bool endsWithWordChar = false;
349 if (label.length() != 0) {
350 startsWithWordChar = wordRegExp.search(label.substring(0, 1)) >= 0;
351 endsWithWordChar = wordRegExp.search(label.substring(label.length() - 1, 1)) >= 0;
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
359 if (startsWithWordChar) {
360 pattern.append("\\b");
362 pattern.append(label);
363 if (endsWithWordChar) {
364 pattern.append("\\b");
368 return new RegularExpression(pattern, false);
371 String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
373 RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
375 if (cellRenderer && cellRenderer->isTableCell()) {
376 RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
378 if (cellAboveRenderer) {
379 HTMLTableCellElement* aboveCell =
380 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
383 // search within the above cell we found for a match
384 for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
385 if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
386 // For each text chunk, run the regexp
387 String nodeString = n->nodeValue();
388 int pos = regExp->searchRev(nodeString);
390 return nodeString.substring(pos, regExp->matchedLength());
396 // Any reason in practice to search all cells in that are above cell?
400 String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
402 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
403 // We stop searching after we've seen this many chars
404 const unsigned int charsSearchedThreshold = 500;
405 // This is the absolute max we search. We allow a little more slop than
406 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
407 const unsigned int maxCharsSearched = 600;
408 // If the starting element is within a table, the cell that contains it
409 HTMLTableCellElement* startingTableCell = 0;
410 bool searchedCellAbove = false;
412 // walk backwards in the node tree, until another element, or form, or end of tree
413 int unsigned lengthSearched = 0;
415 for (n = element->traversePreviousNode();
416 n && lengthSearched < charsSearchedThreshold;
417 n = n->traversePreviousNode())
419 if (n->hasTagName(formTag)
420 || (n->isHTMLElement()
421 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
423 // We hit another form element or the start of the form - bail out
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);
429 if (!result.isEmpty())
431 searchedCellAbove = true;
432 } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
433 // For each text chunk, run the regexp
434 String nodeString = n->nodeValue();
435 // add 100 for slop, to make it more likely that we'll search whole nodes
436 if (lengthSearched + nodeString.length() > maxCharsSearched)
437 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
438 int pos = regExp->searchRev(nodeString);
440 return nodeString.substring(pos, regExp->matchedLength());
441 lengthSearched += nodeString.length();
445 // If we started in a cell, but bailed because we found the start of the form or the
446 // previous element, we still might need to search the row above us for a label.
447 if (startingTableCell && !searchedCellAbove) {
448 return searchForLabelsAboveCell(regExp.get(), startingTableCell);
453 String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
455 String name = element->getAttribute(nameAttr);
456 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
457 replace(name, RegularExpression("\\d"), " ");
458 name.replace('_', ' ');
460 OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
461 // Use the largest match we can find in the whole name string
468 pos = regExp->search(name, start);
470 length = regExp->matchedLength();
471 if (length >= bestLength) {
480 return name.substring(bestPos, bestLength);
484 const Selection& Frame::mark() const
489 void Frame::setMark(const Selection& s)
491 ASSERT(!s.base().node() || s.base().node()->document() == document());
492 ASSERT(!s.extent().node() || s.extent().node()->document() == document());
493 ASSERT(!s.start().node() || s.start().node()->document() == document());
494 ASSERT(!s.end().node() || s.end().node()->document() == document());
499 void Frame::notifyRendererOfSelectionChange(bool userTriggered)
501 RenderObject* renderer = 0;
502 if (selectionController()->rootEditableElement())
503 renderer = selectionController()->rootEditableElement()->shadowAncestorNode()->renderer();
505 // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
506 if (renderer && (renderer->isTextArea() || renderer->isTextField()))
507 static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
510 void Frame::invalidateSelection()
512 selectionController()->setNeedsLayout();
513 selectionLayoutChanged();
516 void Frame::setCaretVisible(bool flag)
518 if (d->m_caretVisible == flag)
520 clearCaretRectIfNeeded();
521 d->m_caretVisible = flag;
522 selectionLayoutChanged();
525 void Frame::clearCaretRectIfNeeded()
527 if (d->m_caretPaint) {
528 d->m_caretPaint = false;
529 selectionController()->invalidateCaretRect();
533 // Helper function that tells whether a particular node is an element that has an entire
534 // Frame and FrameView, a <frame>, <iframe>, or <object>.
535 static bool isFrameElement(const Node *n)
539 RenderObject *renderer = n->renderer();
540 if (!renderer || !renderer->isWidget())
542 Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
543 return widget && widget->isFrameView();
546 void Frame::setFocusedNodeIfNeeded()
548 if (!document() || selectionController()->isNone() || !selectionController()->isFocusedAndActive())
551 Node* target = selectionController()->rootEditableElement();
553 RenderObject* renderer = target->renderer();
555 // Walk up the render tree to search for a node to focus.
556 // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
558 // We don't want to set focus on a subframe when selecting in a parent frame,
559 // so add the !isFrameElement check here. There's probably a better way to make this
560 // work in the long term, but this is the safest fix at this time.
561 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
562 page()->focusController()->setFocusedNode(target, this);
565 renderer = renderer->parent();
567 target = renderer->element();
569 document()->setFocusedNode(0);
573 void Frame::selectionLayoutChanged()
575 bool caretRectChanged = selectionController()->recomputeCaretRect();
577 bool shouldBlink = d->m_caretVisible
578 && selectionController()->isCaret() && selectionController()->isContentEditable();
580 // If the caret moved, stop the blink timer so we can restart with a
581 // black caret in the new location.
582 if (caretRectChanged || !shouldBlink)
583 d->m_caretBlinkTimer.stop();
585 // Start blinking with a black caret. Be sure not to restart if we're
586 // already blinking in the right location.
587 if (shouldBlink && !d->m_caretBlinkTimer.isActive()) {
588 d->m_caretBlinkTimer.startRepeating(theme()->caretBlinkFrequency());
589 if (!d->m_caretPaint) {
590 d->m_caretPaint = true;
591 selectionController()->invalidateCaretRect();
597 RenderView* canvas = static_cast<RenderView*>(renderer());
599 Selection selection = selectionController()->selection();
601 if (!selection.isRange())
602 canvas->clearSelection();
604 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
605 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
606 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
607 // and will fill the gap before 'bar'.
608 Position startPos = selection.visibleStart().deepEquivalent();
609 if (startPos.downstream().isCandidate())
610 startPos = startPos.downstream();
611 Position endPos = selection.visibleEnd().deepEquivalent();
612 if (endPos.upstream().isCandidate())
613 endPos = endPos.upstream();
615 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
616 // because we don't yet notify the SelectionController of text removal.
617 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
618 RenderObject *startRenderer = startPos.node()->renderer();
619 RenderObject *endRenderer = endPos.node()->renderer();
620 canvas->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
625 void Frame::caretBlinkTimerFired(Timer<Frame>*)
627 ASSERT(d->m_caretVisible);
628 ASSERT(selectionController()->isCaret());
629 bool caretPaint = d->m_caretPaint;
630 if (selectionController()->isCaretBlinkingSuspended() && caretPaint)
632 d->m_caretPaint = !caretPaint;
633 selectionController()->invalidateCaretRect();
636 void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const
638 if (d->m_caretPaint && d->m_caretVisible)
639 selectionController()->paintCaret(p, rect);
642 void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const
644 SelectionController* dragCaretController = d->m_page->dragCaretController();
645 ASSERT(dragCaretController->selection().isCaret());
646 if (dragCaretController->selection().start().node()->document()->frame() == this)
647 dragCaretController->paintCaret(p, rect);
650 float Frame::zoomFactor() const
652 return d->m_zoomFactor;
655 bool Frame::isZoomFactorTextOnly() const
657 return d->m_zoomFactorIsTextOnly;
660 bool Frame::shouldApplyTextZoom() const
662 if (d->m_zoomFactor == 1.0f || !d->m_zoomFactorIsTextOnly)
664 if (d->m_doc && d->m_doc->isSVGDocument())
669 bool Frame::shouldApplyPageZoom() const
671 if (d->m_zoomFactor == 1.0f || d->m_zoomFactorIsTextOnly)
673 if (d->m_doc && d->m_doc->isSVGDocument())
678 void Frame::setZoomFactor(float percent, bool isTextOnly)
680 if (d->m_zoomFactor == percent && d->m_zoomFactorIsTextOnly == isTextOnly)
684 // SVG doesn't care if the zoom factor is text only. It will always apply a
685 // zoom to the whole SVG.
686 if (d->m_doc && d->m_doc->isSVGDocument()) {
687 if (!static_cast<SVGDocument*>(d->m_doc.get())->zoomAndPanEnabled())
689 d->m_zoomFactor = percent;
690 d->m_zoomFactorIsTextOnly = true; // We do this to avoid doing any scaling of CSS pixels, since the SVG has its own notion of zoom.
691 if (d->m_doc->renderer())
692 d->m_doc->renderer()->repaint();
697 d->m_zoomFactor = percent;
698 d->m_zoomFactorIsTextOnly = isTextOnly;
701 d->m_doc->recalcStyle(Node::Force);
703 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
704 child->setZoomFactor(d->m_zoomFactor, isTextOnly);
706 if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout())
710 void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
715 d->m_doc->setPrinting(printing);
716 view()->setMediaType(printing ? "print" : "screen");
717 d->m_doc->updateStyleSelector();
718 forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
720 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
721 child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
724 void Frame::setJSStatusBarText(const String& text)
726 d->m_kjsStatusBarText = text;
728 d->m_page->chrome()->setStatusbarText(this, d->m_kjsStatusBarText);
731 void Frame::setJSDefaultStatusBarText(const String& text)
733 d->m_kjsDefaultStatusBarText = text;
735 d->m_page->chrome()->setStatusbarText(this, d->m_kjsDefaultStatusBarText);
738 String Frame::jsStatusBarText() const
740 return d->m_kjsStatusBarText;
743 String Frame::jsDefaultStatusBarText() const
745 return d->m_kjsDefaultStatusBarText;
748 void Frame::setNeedsReapplyStyles()
750 if (d->m_needsReapplyStyles)
753 d->m_needsReapplyStyles = true;
755 // Invalidate the FrameView so that FrameView::layout will get called,
756 // which calls reapplyStyles.
757 view()->invalidate();
760 bool Frame::needsReapplyStyles() const
762 return d->m_needsReapplyStyles;
765 void Frame::reapplyStyles()
767 d->m_needsReapplyStyles = false;
769 // FIXME: This call doesn't really make sense in a method called
770 // "reapplyStyles". We should probably eventually move it into its own
773 d->m_doc->docLoader()->setAutoLoadImages(d->m_page && d->m_page->settings()->loadsImagesAutomatically());
775 #if FRAME_LOADS_USER_STYLESHEET
776 const KURL userStyleSheetLocation = d->m_page ? d->m_page->settings()->userStyleSheetLocation() : KURL();
777 if (!userStyleSheetLocation.isEmpty())
778 setUserStyleSheetLocation(userStyleSheetLocation);
780 setUserStyleSheet(String());
783 // FIXME: It's not entirely clear why the following is needed.
784 // The document automatically does this as required when you set the style sheet.
785 // But we had problems when this code was removed. Details are in
786 // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
788 d->m_doc->updateStyleSelector();
791 bool Frame::shouldChangeSelection(const Selection& newSelection) const
793 return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false);
796 bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
798 return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
799 affinity, stillSelecting);
802 bool Frame::shouldDeleteSelection(const Selection& selection) const
804 return editor()->client()->shouldDeleteRange(selection.toRange().get());
807 bool Frame::isContentEditable() const
809 if (d->m_editor.clientIsEditable())
813 return d->m_doc->inDesignMode();
818 void Frame::setUseSecureKeyboardEntry(bool)
824 void Frame::updateSecureKeyboardEntryIfActive()
826 if (selectionController()->isFocusedAndActive())
827 setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
830 CSSMutableStyleDeclaration *Frame::typingStyle() const
832 return d->m_typingStyle.get();
835 void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
837 d->m_typingStyle = style;
840 void Frame::clearTypingStyle()
842 d->m_typingStyle = 0;
845 void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
847 if (!style || style->length() == 0) {
852 // Calculate the current typing style.
853 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
855 typingStyle()->merge(mutableStyle.get());
856 mutableStyle = typingStyle();
859 Node *node = selectionController()->selection().visibleStart().deepEquivalent().node();
860 CSSComputedStyleDeclaration computedStyle(node);
861 computedStyle.diff(mutableStyle.get());
863 // Handle block styles, substracting these from the typing style.
864 RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
865 blockStyle->diff(mutableStyle.get());
866 if (document() && blockStyle->length() > 0)
867 applyCommand(new ApplyStyleCommand(document(), blockStyle.get(), editingAction));
869 // Set the remaining style as the typing style.
870 d->m_typingStyle = mutableStyle.release();
873 String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
876 RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
880 String value = selectionStyle->getPropertyValue(stylePropertyID);
883 ExceptionCode ec = 0;
884 nodeToRemove->remove(ec);
891 CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) const
898 if (selectionController()->isNone())
901 RefPtr<Range> range(selectionController()->toRange());
902 Position pos = range->editingStartPosition();
904 Element *elem = pos.element();
908 RefPtr<Element> styleElement = elem;
909 ExceptionCode ec = 0;
911 if (d->m_typingStyle) {
912 styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
915 styleElement->setAttribute(styleAttr, d->m_typingStyle->cssText().impl(), ec);
918 styleElement->appendChild(document()->createEditingTextNode(""), ec);
921 if (elem->renderer() && elem->renderer()->canHaveChildren()) {
922 elem->appendChild(styleElement, ec);
924 Node *parent = elem->parent();
925 Node *next = elem->nextSibling();
928 parent->insertBefore(styleElement, next, ec);
930 parent->appendChild(styleElement, ec);
935 nodeToRemove = styleElement.get();
938 return new CSSComputedStyleDeclaration(styleElement);
941 void Frame::textFieldDidBeginEditing(Element* e)
943 if (editor()->client())
944 editor()->client()->textFieldDidBeginEditing(e);
947 void Frame::textFieldDidEndEditing(Element* e)
949 if (editor()->client())
950 editor()->client()->textFieldDidEndEditing(e);
953 void Frame::textDidChangeInTextField(Element* e)
955 if (editor()->client())
956 editor()->client()->textDidChangeInTextField(e);
959 bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
961 if (editor()->client())
962 return editor()->client()->doTextFieldCommandFromEvent(e, ke);
967 void Frame::textWillBeDeletedInTextField(Element* input)
969 if (editor()->client())
970 editor()->client()->textWillBeDeletedInTextField(input);
973 void Frame::textDidChangeInTextArea(Element* e)
975 if (editor()->client())
976 editor()->client()->textDidChangeInTextArea(e);
979 void Frame::applyEditingStyleToBodyElement() const
984 RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
985 unsigned len = list->length();
986 for (unsigned i = 0; i < len; i++) {
987 applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
991 void Frame::removeEditingStyleFromBodyElement() const
996 RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
997 unsigned len = list->length();
998 for (unsigned i = 0; i < len; i++) {
999 removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
1003 void Frame::applyEditingStyleToElement(Element* element) const
1008 CSSStyleDeclaration* style = element->style();
1011 ExceptionCode ec = 0;
1012 style->setProperty(CSS_PROP_WORD_WRAP, "break-word", false, ec);
1014 style->setProperty(CSS_PROP__WEBKIT_NBSP_MODE, "space", false, ec);
1016 style->setProperty(CSS_PROP__WEBKIT_LINE_BREAK, "after-white-space", false, ec);
1020 void Frame::removeEditingStyleFromElement(Element*) const
1025 static HashSet<Frame*>& keepAliveSet()
1027 static HashSet<Frame*> staticKeepAliveSet;
1028 return staticKeepAliveSet;
1032 void Frame::keepAlive()
1034 if (d->m_lifeSupportTimer.isActive())
1037 keepAliveSet().add(this);
1040 d->m_lifeSupportTimer.startOneShot(0);
1044 void Frame::cancelAllKeepAlive()
1046 HashSet<Frame*>::iterator end = keepAliveSet().end();
1047 for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
1049 frame->d->m_lifeSupportTimer.stop();
1052 keepAliveSet().clear();
1056 void Frame::lifeSupportTimerFired(Timer<Frame>*)
1059 keepAliveSet().remove(this);
1064 KJS::Bindings::RootObject* Frame::bindingRootObject()
1066 if (!scriptProxy()->isEnabled())
1069 if (!d->m_bindingRootObject) {
1071 d->m_bindingRootObject = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject());
1073 return d->m_bindingRootObject.get();
1076 PassRefPtr<KJS::Bindings::RootObject> Frame::createRootObject(void* nativeHandle, KJS::JSGlobalObject* globalObject)
1078 RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
1079 if (it != d->m_rootObjects.end())
1082 RefPtr<KJS::Bindings::RootObject> rootObject = KJS::Bindings::RootObject::create(nativeHandle, globalObject);
1084 d->m_rootObjects.set(nativeHandle, rootObject);
1085 return rootObject.release();
1088 #if ENABLE(NETSCAPE_PLUGIN_API)
1089 NPObject* Frame::windowScriptNPObject()
1091 if (!d->m_windowScriptNPObject) {
1092 if (scriptProxy()->isEnabled()) {
1093 // JavaScript is enabled, so there is a JavaScript window object. Return an NPObject bound to the window
1096 KJS::JSObject* win = toJSDOMWindow(this);
1098 KJS::Bindings::RootObject* root = bindingRootObject();
1099 d->m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
1101 // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1102 // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1103 d->m_windowScriptNPObject = _NPN_CreateNoScriptObject();
1107 return d->m_windowScriptNPObject;
1111 void Frame::clearScriptProxy()
1114 d->m_jscript->clear();
1117 void Frame::clearDOMWindow()
1120 d->m_domWindow->clear();
1123 void Frame::cleanupScriptObjectsForPlugin(void* nativeHandle)
1125 RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
1127 if (it == d->m_rootObjects.end())
1130 it->second->invalidate();
1131 d->m_rootObjects.remove(it);
1134 void Frame::clearScriptObjects()
1138 RootObjectMap::const_iterator end = d->m_rootObjects.end();
1139 for (RootObjectMap::const_iterator it = d->m_rootObjects.begin(); it != end; ++it)
1140 it->second->invalidate();
1142 d->m_rootObjects.clear();
1144 if (d->m_bindingRootObject) {
1145 d->m_bindingRootObject->invalidate();
1146 d->m_bindingRootObject = 0;
1149 #if ENABLE(NETSCAPE_PLUGIN_API)
1150 if (d->m_windowScriptNPObject) {
1151 // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
1152 // script object properly.
1153 // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
1154 _NPN_DeallocateObject(d->m_windowScriptNPObject);
1155 d->m_windowScriptNPObject = 0;
1159 clearPlatformScriptObjects();
1162 RenderObject *Frame::renderer() const
1164 Document *doc = document();
1165 return doc ? doc->renderer() : 0;
1168 HTMLFrameOwnerElement* Frame::ownerElement() const
1170 return d->m_ownerElement;
1173 RenderPart* Frame::ownerRenderer()
1175 HTMLFrameOwnerElement* ownerElement = d->m_ownerElement;
1178 return static_cast<RenderPart*>(ownerElement->renderer());
1181 // returns FloatRect because going through IntRect would truncate any floats
1182 FloatRect Frame::selectionRect(bool clipToVisibleContent) const
1184 RenderView *root = static_cast<RenderView*>(renderer());
1188 IntRect selectionRect = root->selectionRect(clipToVisibleContent);
1189 return clipToVisibleContent ? intersection(selectionRect, d->m_view->visibleContentRect()) : selectionRect;
1192 void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
1194 RenderView *root = static_cast<RenderView*>(renderer());
1198 RefPtr<Range> selectedRange = selectionController()->toRange();
1200 Vector<IntRect> intRects;
1201 selectedRange->addLineBoxRects(intRects, true);
1203 unsigned size = intRects.size();
1204 FloatRect visibleContentRect = d->m_view->visibleContentRect();
1205 for (unsigned i = 0; i < size; ++i)
1206 if (clipToVisibleContent)
1207 rects.append(intersection(intRects[i], visibleContentRect));
1209 rects.append(intRects[i]);
1213 bool Frame::isFrameSet() const
1215 Document* document = d->m_doc.get();
1216 if (!document || !document->isHTMLDocument())
1218 Node *body = static_cast<HTMLDocument*>(document)->body();
1219 return body && body->renderer() && body->hasTagName(framesetTag);
1222 // Scans logically forward from "start", including any child frames
1223 static HTMLFormElement *scanForForm(Node *start)
1226 for (n = start; n; n = n->traverseNextNode()) {
1227 if (n->hasTagName(formTag))
1228 return static_cast<HTMLFormElement*>(n);
1229 else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement())
1230 return static_cast<HTMLGenericFormElement*>(n)->form();
1231 else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
1232 Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
1233 if (HTMLFormElement *frameResult = scanForForm(childDoc))
1240 // We look for either the form containing the current focus, or for one immediately after it
1241 HTMLFormElement *Frame::currentForm() const
1243 // start looking either at the active (first responder) node, or where the selection is
1244 Node *start = d->m_doc ? d->m_doc->focusedNode() : 0;
1246 start = selectionController()->start().node();
1248 // try walking up the node tree to find a form element
1250 for (n = start; n; n = n->parentNode()) {
1251 if (n->hasTagName(formTag))
1252 return static_cast<HTMLFormElement*>(n);
1253 else if (n->isHTMLElement()
1254 && static_cast<HTMLElement*>(n)->isGenericFormElement())
1255 return static_cast<HTMLGenericFormElement*>(n)->form();
1258 // try walking forward in the node tree to find a form element
1259 return start ? scanForForm(start) : 0;
1262 // FIXME: should this go in SelectionController?
1263 void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
1267 switch (selectionController()->state()) {
1268 case Selection::NONE:
1271 case Selection::CARET:
1272 rect = selectionController()->caretRect();
1275 case Selection::RANGE:
1276 rect = enclosingIntRect(selectionRect(false));
1280 Position start = selectionController()->start();
1282 ASSERT(start.node());
1283 if (start.node() && start.node()->renderer()) {
1284 // FIXME: This code only handles scrolling the startContainer's layer, but
1285 // the selection rect could intersect more than just that.
1286 // See <rdar://problem/4799899>.
1287 if (RenderLayer *layer = start.node()->renderer()->enclosingLayer())
1288 layer->scrollRectToVisible(rect, alignment, alignment);
1292 void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
1294 if (selectionController()->isNone())
1297 Position extent = selectionController()->extent();
1298 if (extent.node() && extent.node()->renderer()) {
1299 IntRect extentRect = VisiblePosition(extent).caretRect();
1300 RenderLayer* layer = extent.node()->renderer()->enclosingLayer();
1302 layer->scrollRectToVisible(extentRect, alignment, alignment);
1306 // FIXME: why is this here instead of on the FrameView?
1307 void Frame::paint(GraphicsContext* p, const IntRect& rect)
1311 if (!document() || document()->printing())
1312 fillWithRed = false; // Printing, don't fill with red (can't remember why).
1313 else if (document()->ownerElement())
1314 fillWithRed = false; // Subframe, don't fill with red.
1315 else if (view() && view()->isTransparent())
1316 fillWithRed = false; // Transparent, don't fill with red.
1317 else if (d->m_paintRestriction == PaintRestrictionSelectionOnly || d->m_paintRestriction == PaintRestrictionSelectionOnlyBlackText)
1318 fillWithRed = false; // Selections are transparent, don't fill with red.
1319 else if (d->m_elementToDraw)
1320 fillWithRed = false; // Element images are transparent, don't fill with red.
1325 p->fillRect(rect, Color(0xFF, 0, 0));
1328 bool isTopLevelPainter = !s_currentPaintTimeStamp;
1329 if (isTopLevelPainter)
1330 s_currentPaintTimeStamp = currentTime();
1333 ASSERT(d->m_view && !d->m_view->needsLayout());
1334 ASSERT(!d->m_isPainting);
1336 d->m_isPainting = true;
1338 // d->m_elementToDraw is used to draw only one element
1339 RenderObject *eltRenderer = d->m_elementToDraw ? d->m_elementToDraw->renderer() : 0;
1340 if (d->m_paintRestriction == PaintRestrictionNone)
1341 renderer()->document()->invalidateRenderedRectsForMarkersInRect(rect);
1342 renderer()->layer()->paint(p, rect, d->m_paintRestriction, eltRenderer);
1344 d->m_isPainting = false;
1346 // Regions may have changed as a result of the visibility/z-index of element changing.
1347 if (renderer()->document()->dashboardRegionsDirty())
1348 renderer()->view()->frameView()->updateDashboardRegions();
1350 LOG_ERROR("called Frame::paint with nil renderer");
1352 if (isTopLevelPainter)
1353 s_currentPaintTimeStamp = 0;
1356 void Frame::setPaintRestriction(PaintRestriction pr)
1358 d->m_paintRestriction = pr;
1361 bool Frame::isPainting() const
1363 return d->m_isPainting;
1366 void Frame::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
1368 RenderView *root = static_cast<RenderView*>(document()->renderer());
1370 // Use a context with painting disabled.
1371 GraphicsContext context((PlatformGraphicsContext*)0);
1372 root->setTruncatedAt((int)floorf(oldBottom));
1373 IntRect dirtyRect(0, (int)floorf(oldTop), root->docWidth(), (int)ceilf(oldBottom - oldTop));
1374 root->layer()->paint(&context, dirtyRect);
1375 *newBottom = root->bestTruncatedAt();
1376 if (*newBottom == 0)
1377 *newBottom = oldBottom;
1379 *newBottom = oldBottom;
1382 Frame* Frame::frameForWidget(const Widget* widget)
1384 ASSERT_ARG(widget, widget);
1386 if (RenderWidget* renderer = RenderWidget::find(widget))
1387 if (Node* node = renderer->node())
1388 return node->document()->frame();
1390 // Assume all widgets are either a FrameView or owned by a RenderWidget.
1391 // FIXME: That assumption is not right for scroll bars!
1392 ASSERT(widget->isFrameView());
1393 return static_cast<const FrameView*>(widget)->frame();
1396 void Frame::forceLayout(bool allowSubtree)
1398 FrameView *v = d->m_view.get();
1400 v->layout(allowSubtree);
1401 // We cannot unschedule a pending relayout, since the force can be called with
1402 // a tiny rectangle from a drawRect update. By unscheduling we in effect
1403 // "validate" and stop the necessary full repaint from occurring. Basically any basic
1404 // append/remove DHTML is broken by this call. For now, I have removed the optimization
1405 // until we have a better invalidation stategy. -dwh
1406 //v->unscheduleRelayout();
1410 void Frame::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth, bool adjustViewSize)
1412 // Dumping externalRepresentation(m_frame->renderer()).ascii() is a good trick to see
1413 // the state of things before and after the layout
1414 RenderView *root = static_cast<RenderView*>(document()->renderer());
1416 // This magic is basically copied from khtmlview::print
1417 int pageW = (int)ceilf(minPageWidth);
1418 root->setWidth(pageW);
1419 root->setNeedsLayoutAndPrefWidthsRecalc();
1422 // If we don't fit in the minimum page width, we'll lay out again. If we don't fit in the
1423 // maximum page width, we will lay out to the maximum page width and clip extra content.
1424 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
1425 // implementation should not do this!
1426 int rightmostPos = root->rightmostPosition();
1427 if (rightmostPos > minPageWidth) {
1428 pageW = min(rightmostPos, (int)ceilf(maxPageWidth));
1429 root->setWidth(pageW);
1430 root->setNeedsLayoutAndPrefWidthsRecalc();
1435 if (adjustViewSize && view())
1436 view()->adjustViewSize();
1439 void Frame::sendResizeEvent()
1441 if (Document* doc = document())
1442 doc->dispatchWindowEvent(EventNames::resizeEvent, false, false);
1445 void Frame::sendScrollEvent()
1447 FrameView* v = d->m_view.get();
1450 v->setWasScrolledByUser(true);
1451 Document* doc = document();
1454 doc->dispatchHTMLEvent(scrollEvent, true, false);
1457 void Frame::clearTimers(FrameView *view)
1460 view->unscheduleRelayout();
1461 if (view->frame()) {
1462 Document* document = view->frame()->document();
1463 if (document && document->renderer() && document->renderer()->hasLayer())
1464 document->renderer()->layer()->suspendMarquees();
1465 view->frame()->animationController()->suspendAnimations();
1470 void Frame::clearTimers()
1472 clearTimers(d->m_view.get());
1475 RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
1481 if (selectionController()->isNone())
1484 Position pos = selectionController()->selection().visibleStart().deepEquivalent();
1485 if (!pos.isCandidate())
1487 Node *node = pos.node();
1491 if (!d->m_typingStyle)
1492 return node->renderer()->style();
1494 ExceptionCode ec = 0;
1495 RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
1498 String styleText = d->m_typingStyle->cssText() + " display: inline";
1499 styleElement->setAttribute(styleAttr, styleText.impl(), ec);
1502 styleElement->appendChild(document()->createEditingTextNode(""), ec);
1505 node->parentNode()->appendChild(styleElement, ec);
1508 nodeToRemove = styleElement.get();
1509 return styleElement->renderer() ? styleElement->renderer()->style() : 0;
1512 void Frame::setSelectionFromNone()
1514 // Put a caret inside the body if the entire frame is editable (either the
1515 // entire WebView is editable or designMode is on for this document).
1516 Document *doc = document();
1517 if (!doc || !selectionController()->isNone() || !isContentEditable())
1520 Node* node = doc->documentElement();
1521 while (node && !node->hasTagName(bodyTag))
1522 node = node->traverseNextNode();
1524 selectionController()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
1527 bool Frame::inViewSourceMode() const
1529 return d->m_inViewSourceMode;
1532 void Frame::setInViewSourceMode(bool mode) const
1534 d->m_inViewSourceMode = mode;
1537 UChar Frame::backslashAsCurrencySymbol() const
1539 Document *doc = document();
1542 TextResourceDecoder *decoder = doc->decoder();
1546 return decoder->encoding().backslashAsCurrencySymbol();
1549 static bool isInShadowTree(Node* node)
1551 for (Node* n = node; n; n = n->parentNode())
1552 if (n->isShadowNode())
1557 // Searches from the beginning of the document if nothing is selected.
1558 bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
1560 if (target.isEmpty() || !document())
1563 // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
1564 // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
1565 RefPtr<Range> searchRange(rangeOfContents(document()));
1566 Selection selection(selectionController()->selection());
1567 Node* selectionBaseNode = selection.base().node();
1569 // FIXME 3099526: We don't search in the shadow trees (e.g. text fields and textareas), though we'd like to
1570 // someday. If we don't explicitly skip them here, we'll miss hits in the regular content.
1571 bool selectionIsInMainContent = selectionBaseNode && !isInShadowTree(selectionBaseNode);
1573 if (selectionIsInMainContent) {
1575 setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
1577 setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
1579 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
1580 // If we started in the selection and the found range exactly matches the existing selection, find again.
1581 // Build a selection with the found range to remove collapsed whitespace.
1582 // Compare ranges instead of selection objects to ignore the way that the current selection was made.
1583 if (startInSelection && selectionIsInMainContent && *Selection(resultRange.get()).toRange() == *selection.toRange()) {
1584 searchRange = rangeOfContents(document());
1586 setStart(searchRange.get(), selection.visibleEnd());
1588 setEnd(searchRange.get(), selection.visibleStart());
1589 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
1594 // If we didn't find anything and we're wrapping, search again in the entire document (this will
1595 // redundantly re-search the area already searched in some cases).
1596 if (resultRange->collapsed(exception) && wrapFlag) {
1597 searchRange = rangeOfContents(document());
1598 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
1599 // We used to return false here if we ended up with the same range that we started with
1600 // (e.g., the selection was already the only instance of this text). But we decided that
1601 // this should be a success case instead, so we'll just fall through in that case.
1604 if (resultRange->collapsed(exception))
1607 selectionController()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
1612 unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
1614 if (target.isEmpty() || !document())
1617 RefPtr<Range> searchRange(rangeOfContents(document()));
1620 unsigned matchCount = 0;
1622 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
1623 if (resultRange->collapsed(exception))
1626 // A non-collapsed result range can in some funky whitespace cases still not
1627 // advance the range's start position (4509328). Break to avoid infinite loop.
1628 VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
1629 if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
1634 document()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
1636 // Stop looking if we hit the specified limit. A limit of 0 means no limit.
1637 if (limit > 0 && matchCount >= limit)
1640 setStart(searchRange.get(), newStart);
1643 // Do a "fake" paint in order to execute the code that computes the rendered rect for
1645 Document* doc = document();
1646 if (doc && d->m_view && renderer()) {
1647 doc->updateLayout(); // Ensure layout is up to date.
1648 IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
1649 GraphicsContext context((PlatformGraphicsContext*)0);
1650 context.setPaintingDisabled(true);
1651 paint(&context, visibleRect);
1657 bool Frame::markedTextMatchesAreHighlighted() const
1659 return d->m_highlightTextMatches;
1662 void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
1664 if (flag == d->m_highlightTextMatches || !document())
1667 d->m_highlightTextMatches = flag;
1668 document()->repaintMarkers(DocumentMarker::TextMatch);
1671 FrameTree* Frame::tree() const
1673 return &d->m_treeNode;
1676 DOMWindow* Frame::domWindow() const
1678 if (!d->m_domWindow)
1679 d->m_domWindow = DOMWindow::create(const_cast<Frame*>(this));
1681 return d->m_domWindow.get();
1684 Page* Frame::page() const
1689 EventHandler* Frame::eventHandler() const
1691 return &d->m_eventHandler;
1694 void Frame::pageDestroyed()
1696 if (Frame* parent = tree()->parent())
1697 parent->loader()->checkLoadComplete();
1699 if (d->m_page && d->m_page->focusController()->focusedFrame() == this)
1700 d->m_page->focusController()->setFocusedFrame(0);
1702 // This will stop any JS timers
1703 if (d->m_jscript && d->m_jscript->haveGlobalObject())
1704 if (JSDOMWindow* w = toJSDOMWindow(this))
1705 w->disconnectFrame();
1707 clearScriptObjects();
1712 void Frame::disconnectOwnerElement()
1714 if (d->m_ownerElement) {
1715 if (Document* doc = document())
1716 doc->clearAXObjectCache();
1717 d->m_ownerElement->m_contentFrame = 0;
1719 d->m_page->decrementFrameCount();
1721 d->m_ownerElement = 0;
1724 String Frame::documentTypeString() const
1726 if (Document *doc = document())
1727 if (DocumentType *doctype = doc->doctype())
1728 return doctype->toString();
1733 bool Frame::prohibitsScrolling() const
1735 return d->m_prohibitsScrolling;
1738 void Frame::setProhibitsScrolling(bool prohibit)
1740 d->m_prohibitsScrolling = prohibit;
1743 void Frame::focusWindow()
1748 // If we're a top level window, bring the window to the front.
1749 if (!tree()->parent())
1750 page()->chrome()->focus();
1752 eventHandler()->focusDocumentView();
1755 void Frame::unfocusWindow()
1760 // If we're a top level window, deactivate the window.
1761 if (!tree()->parent())
1762 page()->chrome()->unfocus();
1765 bool Frame::shouldClose()
1767 Chrome* chrome = page() ? page()->chrome() : 0;
1768 if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
1771 RefPtr<Document> doc = document();
1774 HTMLElement* body = doc->body();
1778 RefPtr<BeforeUnloadEvent> beforeUnloadEvent = new BeforeUnloadEvent;
1779 beforeUnloadEvent->setTarget(doc);
1780 doc->handleWindowEvent(beforeUnloadEvent.get(), false);
1782 if (!beforeUnloadEvent->defaultPrevented() && doc)
1783 doc->defaultEventHandler(beforeUnloadEvent.get());
1784 if (beforeUnloadEvent->result().isNull())
1787 String text = beforeUnloadEvent->result();
1788 text.replace('\\', backslashAsCurrencySymbol());
1790 return chrome->runBeforeUnloadConfirmPanel(text, this);
1793 void Frame::scheduleClose()
1798 Chrome* chrome = page() ? page()->chrome() : 0;
1800 chrome->closeWindowSoon();
1803 void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
1806 bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
1807 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
1808 if (isContinuousSpellCheckingEnabled) {
1809 Selection newAdjacentWords;
1810 Selection newSelectedSentence;
1811 if (selectionController()->selection().isContentEditable()) {
1812 VisiblePosition newStart(selectionController()->selection().visibleStart());
1813 newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
1814 if (isContinuousGrammarCheckingEnabled)
1815 newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
1818 // When typing we check spelling elsewhere, so don't redo it here.
1819 // If this is a change in selection resulting from a delete operation,
1820 // oldSelection may no longer be in the document.
1821 if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
1822 VisiblePosition oldStart(oldSelection.visibleStart());
1823 Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
1824 if (oldAdjacentWords != newAdjacentWords) {
1825 editor()->markMisspellings(oldAdjacentWords);
1826 if (isContinuousGrammarCheckingEnabled) {
1827 Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
1828 if (oldSelectedSentence != newSelectedSentence)
1829 editor()->markBadGrammar(oldSelectedSentence);
1834 // This only erases markers that are in the first unit (word or sentence) of the selection.
1835 // Perhaps peculiar, but it matches AppKit.
1836 if (RefPtr<Range> wordRange = newAdjacentWords.toRange())
1837 document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
1838 if (RefPtr<Range> sentenceRange = newSelectedSentence.toRange())
1839 document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
1842 // When continuous spell checking is off, existing markers disappear after the selection changes.
1843 if (!isContinuousSpellCheckingEnabled)
1844 document()->removeMarkers(DocumentMarker::Spelling);
1845 if (!isContinuousGrammarCheckingEnabled)
1846 document()->removeMarkers(DocumentMarker::Grammar);
1849 editor()->respondToChangedSelection(oldSelection);
1852 VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
1854 HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true);
1855 Node* node = result.innerNode();
1857 return VisiblePosition();
1858 RenderObject* renderer = node->renderer();
1860 return VisiblePosition();
1861 VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
1862 if (visiblePos.isNull())
1863 visiblePos = VisiblePosition(Position(node, 0));
1867 Document* Frame::documentAtPoint(const IntPoint& point)
1872 IntPoint pt = view()->windowToContents(point);
1873 HitTestResult result = HitTestResult(pt);
1876 result = eventHandler()->hitTestResultAtPoint(pt, false);
1877 return result.innerNode() ? result.innerNode()->document() : 0;
1880 FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement* ownerElement,
1881 FrameLoaderClient* frameLoaderClient)
1883 , m_treeNode(thisFrame, parent)
1884 , m_ownerElement(ownerElement)
1886 , m_zoomFactor(parent ? parent->d->m_zoomFactor : 1.0f)
1887 , m_zoomFactorIsTextOnly(parent ? parent->d->m_zoomFactorIsTextOnly : true)
1888 , m_selectionGranularity(CharacterGranularity)
1889 , m_selectionController(thisFrame)
1890 , m_caretBlinkTimer(thisFrame, &Frame::caretBlinkTimerFired)
1891 , m_editor(thisFrame)
1892 , m_eventHandler(thisFrame)
1893 , m_animationController(thisFrame)
1894 , m_caretVisible(false)
1895 , m_caretPaint(true)
1896 , m_isPainting(false)
1897 , m_lifeSupportTimer(thisFrame, &Frame::lifeSupportTimerFired)
1898 , m_loader(new FrameLoader(thisFrame, frameLoaderClient))
1899 , m_paintRestriction(PaintRestrictionNone)
1900 , m_highlightTextMatches(false)
1901 , m_inViewSourceMode(false)
1903 , m_prohibitsScrolling(false)
1904 , m_needsReapplyStyles(false)
1905 #if ENABLE(NETSCAPE_PLUGIN_API)
1906 , m_windowScriptNPObject(0)
1908 #if FRAME_LOADS_USER_STYLESHEET
1909 , m_userStyleSheetLoader(0)
1912 , m_windowScriptObject(nil)
1917 FramePrivate::~FramePrivate()
1923 } // namespace WebCore