1 /*
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, 2008, 2009 Apple Inc. All rights reserved.
9 * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
10 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
11 * Copyright (C) 2008 Eric Seidel <eric@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
parentFromOwnerElement(HTMLFrameOwnerElement * ownerElement)125 static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
126 {
127 if (!ownerElement)
128 return 0;
129 return ownerElement->document()->frame();
130 }
131
Frame(Page * page,HTMLFrameOwnerElement * ownerElement,FrameLoaderClient * frameLoaderClient)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
~Frame()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
init()223 void Frame::init()
224 {
225 m_loader.init();
226 }
227
loader() const228 FrameLoader* Frame::loader() const
229 {
230 return &m_loader;
231 }
232
redirectScheduler() const233 RedirectScheduler* Frame::redirectScheduler() const
234 {
235 return &m_redirectScheduler;
236 }
237
view() const238 FrameView* Frame::view() const
239 {
240 return m_view.get();
241 }
242
setView(PassRefPtr<FrameView> view)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
script()270 ScriptController* Frame::script()
271 {
272 return &m_script;
273 }
274
document() const275 Document* Frame::document() const
276 {
277 return m_doc.get();
278 }
279
setDocument(PassRefPtr<Document> newDoc)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)
sendOrientationChangeEvent(int orientation)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
settings() const307 Settings* Frame::settings() const
308 {
309 return m_page ? m_page->settings() : 0;
310 }
311
selectedText() const312 String Frame::selectedText() const
313 {
314 return plainText(selection()->toNormalizedRange().get());
315 }
316
firstRectForRange(Range * range) const317 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
selection() const357 SelectionController* Frame::selection() const
358 {
359 return &m_selectionController;
360 }
361
editor() const362 Editor* Frame::editor() const
363 {
364 return &m_editor;
365 }
366
selectionGranularity() const367 TextGranularity Frame::selectionGranularity() const
368 {
369 return m_selectionGranularity;
370 }
371
setSelectionGranularity(TextGranularity granularity)372 void Frame::setSelectionGranularity(TextGranularity granularity)
373 {
374 m_selectionGranularity = granularity;
375 }
376
dragCaretController() const377 SelectionController* Frame::dragCaretController() const
378 {
379 return m_page->dragCaretController();
380 }
381
382
animation() const383 AnimationController* Frame::animation() const
384 {
385 return &m_animationController;
386 }
387
createRegExpForLabels(const Vector<String> & labels)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
searchForLabelsAboveCell(RegularExpression * regExp,HTMLTableCellElement * cell,size_t * resultDistanceFromStartOfCell)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
searchForLabelsBeforeElement(const Vector<String> & labels,Element * element,size_t * resultDistance,bool * resultIsInCellAbove)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
matchLabelsAgainstString(const Vector<String> & labels,const String & stringToMatch)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
matchLabelsAgainstElement(const Vector<String> & labels,Element * element)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
mark() const575 const VisibleSelection& Frame::mark() const
576 {
577 return m_mark;
578 }
579
setMark(const VisibleSelection & s)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
notifyRendererOfSelectionChange(bool userTriggered)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>.
isFrameElement(const Node * n)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
setFocusedNodeIfNeeded()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
paintDragCaret(GraphicsContext * p,int tx,int ty,const IntRect & clipRect) const653 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
zoomFactor() const663 float Frame::zoomFactor() const
664 {
665 return m_zoomFactor;
666 }
667
isZoomFactorTextOnly() const668 bool Frame::isZoomFactorTextOnly() const
669 {
670 return m_page->settings()->zoomsTextOnly();
671 }
672
shouldApplyTextZoom() const673 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
shouldApplyPageZoom() const684 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
setZoomFactor(float percent,bool isTextOnly)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
setPrinting(bool printing,float minPageWidth,float maxPageWidth,bool adjustViewSize)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
setJSStatusBarText(const String & text)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
setJSDefaultStatusBarText(const String & text)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
jsStatusBarText() const760 String Frame::jsStatusBarText() const
761 {
762 return m_kjsStatusBarText;
763 }
764
jsDefaultStatusBarText() const765 String Frame::jsDefaultStatusBarText() const
766 {
767 return m_kjsDefaultStatusBarText;
768 }
769
setNeedsReapplyStyles()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
needsReapplyStyles() const789 bool Frame::needsReapplyStyles() const
790 {
791 return m_needsReapplyStyles;
792 }
793
reapplyStyles()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
injectUserScripts(UserScriptInjectionTime injectionTime)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
injectUserScriptsForWorld(DOMWrapperWorld * world,const UserScriptVector & userScripts,UserScriptInjectionTime injectionTime)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
shouldChangeSelection(const VisibleSelection & newSelection) const841 bool Frame::shouldChangeSelection(const VisibleSelection& newSelection) const
842 {
843 return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false);
844 }
845
shouldChangeSelection(const VisibleSelection & oldSelection,const VisibleSelection & newSelection,EAffinity affinity,bool stillSelecting) const846 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
shouldDeleteSelection(const VisibleSelection & selection) const852 bool Frame::shouldDeleteSelection(const VisibleSelection& selection) const
853 {
854 return editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
855 }
856
isContentEditable() const857 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
setUseSecureKeyboardEntry(bool enable)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
updateSecureKeyboardEntryIfActive()895 void Frame::updateSecureKeyboardEntryIfActive()
896 {
897 if (selection()->isFocusedAndActive())
898 setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
899 }
900
typingStyle() const901 CSSMutableStyleDeclaration *Frame::typingStyle() const
902 {
903 return m_typingStyle.get();
904 }
905
setTypingStyle(CSSMutableStyleDeclaration * style)906 void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
907 {
908 m_typingStyle = style;
909 }
910
clearTypingStyle()911 void Frame::clearTypingStyle()
912 {
913 m_typingStyle = 0;
914 }
915
computeAndSetTypingStyle(CSSStyleDeclaration * style,EditAction editingAction)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
selectionStartStylePropertyValue(int stylePropertyID) const959 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
selectionComputedStyle(Node * & nodeToRemove) const977 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
textFieldDidBeginEditing(Element * e)1022 void Frame::textFieldDidBeginEditing(Element* e)
1023 {
1024 if (editor()->client())
1025 editor()->client()->textFieldDidBeginEditing(e);
1026 }
1027
textFieldDidEndEditing(Element * e)1028 void Frame::textFieldDidEndEditing(Element* e)
1029 {
1030 if (editor()->client())
1031 editor()->client()->textFieldDidEndEditing(e);
1032 }
1033
textDidChangeInTextField(Element * e)1034 void Frame::textDidChangeInTextField(Element* e)
1035 {
1036 if (editor()->client())
1037 editor()->client()->textDidChangeInTextField(e);
1038 }
1039
doTextFieldCommandFromEvent(Element * e,KeyboardEvent * ke)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
textWillBeDeletedInTextField(Element * input)1048 void Frame::textWillBeDeletedInTextField(Element* input)
1049 {
1050 if (editor()->client())
1051 editor()->client()->textWillBeDeletedInTextField(input);
1052 }
1053
textDidChangeInTextArea(Element * e)1054 void Frame::textDidChangeInTextArea(Element* e)
1055 {
1056 if (editor()->client())
1057 editor()->client()->textDidChangeInTextArea(e);
1058 }
1059
applyEditingStyleToBodyElement() const1060 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
removeEditingStyleFromBodyElement() const1068 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
applyEditingStyleToElement(Element * element) const1076 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
removeEditingStyleFromElement(Element *) const1093 void Frame::removeEditingStyleFromElement(Element*) const
1094 {
1095 }
1096
1097 #ifndef NDEBUG
keepAliveSet()1098 static HashSet<Frame*>& keepAliveSet()
1099 {
1100 DEFINE_STATIC_LOCAL(HashSet<Frame*>, staticKeepAliveSet, ());
1101 return staticKeepAliveSet;
1102 }
1103 #endif
1104
keepAlive()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
cancelAllKeepAlive()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
lifeSupportTimerFired(Timer<Frame> *)1129 void Frame::lifeSupportTimerFired(Timer<Frame>*)
1130 {
1131 #ifndef NDEBUG
1132 keepAliveSet().remove(this);
1133 #endif
1134 deref();
1135 }
1136
clearDOMWindow()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
contentRenderer() const1146 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
ownerElement() const1158 HTMLFrameOwnerElement* Frame::ownerElement() const
1159 {
1160 return m_ownerElement;
1161 }
1162
ownerRenderer() const1163 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
isDisconnected() const1180 bool Frame::isDisconnected() const
1181 {
1182 return m_isDisconnected;
1183 }
1184
setIsDisconnected(bool isDisconnected)1185 void Frame::setIsDisconnected(bool isDisconnected)
1186 {
1187 m_isDisconnected = isDisconnected;
1188 }
1189
excludeFromTextSearch() const1190 bool Frame::excludeFromTextSearch() const
1191 {
1192 return m_excludeFromTextSearch;
1193 }
1194
setExcludeFromTextSearch(bool exclude)1195 void Frame::setExcludeFromTextSearch(bool exclude)
1196 {
1197 m_excludeFromTextSearch = exclude;
1198 }
1199
1200 // returns FloatRect because going through IntRect would truncate any floats
selectionBounds(bool clipToVisibleContent) const1201 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
selectionTextRects(Vector<FloatRect> & rects,SelectionRectRespectTransforms respectTransforms,bool clipToVisibleContent) const1212 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
scanForForm(Node * start)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
currentForm() const1269 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
revealSelection(const ScrollAlignment & alignment,bool revealExtent)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
frameForWidget(const Widget * widget)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
clearTimers(FrameView * view,Document * document)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
clearTimers()1340 void Frame::clearTimers()
1341 {
1342 clearTimers(m_view.get(), document());
1343 }
1344
styleForSelectionStart(Node * & nodeToRemove) const1345 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
setSelectionFromNone()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
inViewSourceMode() const1395 bool Frame::inViewSourceMode() const
1396 {
1397 return m_inViewSourceMode;
1398 }
1399
setInViewSourceMode(bool mode)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.
findString(const String & target,bool forward,bool caseFlag,bool wrapFlag,bool startInSelection)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
markAllMatchesForText(const String & target,bool caseFlag,unsigned limit)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
markedTextMatchesAreHighlighted() const1549 bool Frame::markedTextMatchesAreHighlighted() const
1550 {
1551 return m_highlightTextMatches;
1552 }
1553
setMarkedTextMatchesAreHighlighted(bool flag)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
tree() const1563 FrameTree* Frame::tree() const
1564 {
1565 return &m_treeNode;
1566 }
1567
setDOMWindow(DOMWindow * domWindow)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
domWindow() const1577 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
clearFormerDOMWindow(DOMWindow * window)1585 void Frame::clearFormerDOMWindow(DOMWindow* window)
1586 {
1587 m_liveFormerWindows.remove(window);
1588 }
1589
page() const1590 Page* Frame::page() const
1591 {
1592 return m_page;
1593 }
1594
detachFromPage()1595 void Frame::detachFromPage()
1596 {
1597 m_page = 0;
1598 }
1599
eventHandler() const1600 EventHandler* Frame::eventHandler() const
1601 {
1602 return &m_eventHandler;
1603 }
1604
pageDestroyed()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
disconnectOwnerElement()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
documentTypeString() const1634 String Frame::documentTypeString() const
1635 {
1636 if (DocumentType* doctype = document()->doctype())
1637 return createMarkup(doctype);
1638
1639 return String();
1640 }
1641
focusWindow()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
unfocusWindow()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
shouldClose()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
scheduleClose()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
respondToChangedSelection(const VisibleSelection & oldSelection,bool closeTyping)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
visiblePositionForPoint(const IntPoint & framePoint)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
documentAtPoint(const IntPoint & point)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
createView(const IntSize & viewportSize,const Color & backgroundColor,bool transparent,const IntSize & fixedLayoutSize,bool useFixedLayout,ScrollbarMode horizontalScrollbarMode,ScrollbarMode verticalScrollbarMode)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