Frame.cpp 58.5 KB
Newer Older
darin's avatar
darin committed
1
/*
darin's avatar
darin committed
2 3 4 5 6 7
 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
 *                     1999 Lars Knoll <knoll@kde.org>
 *                     1999 Antti Koivisto <koivisto@kde.org>
 *                     2000 Simon Hausmann <hausmann@kde.org>
 *                     2000 Stefan Schimanski <1Stein@gmx.de>
 *                     2001 George Staikos <staikos@kde.org>
8
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
ap's avatar
ap committed
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>
darin's avatar
darin committed
12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
ddkilzer's avatar
ddkilzer committed
25 26
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
darin's avatar
darin committed
27 28
 */

mjs's avatar
mjs committed
29
#include "config.h"
mjs's avatar
mjs committed
30
#include "Frame.h"
darin's avatar
darin committed
31

darin's avatar
darin committed
32
#include "ApplyStyleCommand.h"
33
#include "BeforeUnloadEvent.h"
darin's avatar
darin committed
34
#include "CSSComputedStyleDeclaration.h"
darin@apple.com's avatar
darin@apple.com committed
35
#include "CSSMutableStyleDeclaration.h"
36
#include "CSSProperty.h"
37
#include "CSSPropertyNames.h"
38
#include "CachedCSSStyleSheet.h"
darin's avatar
darin committed
39
#include "DOMWindow.h"
40
#include "DocLoader.h"
mjs's avatar
mjs committed
41
#include "DocumentType.h"
darin's avatar
darin committed
42
#include "EditingText.h"
mjs's avatar
mjs committed
43
#include "EditorClient.h"
darin's avatar
darin committed
44
#include "EventNames.h"
darin@apple.com's avatar
darin@apple.com committed
45
#include "FloatQuad.h"
46
#include "FocusController.h"
darin's avatar
darin committed
47
#include "FrameLoader.h"
48
#include "FrameLoaderClient.h"
darin's avatar
darin committed
49
#include "FrameView.h"
darin's avatar
darin committed
50
#include "GraphicsContext.h"
darin's avatar
darin committed
51
#include "HTMLDocument.h"
52
#include "HTMLFormControlElement.h"
darin's avatar
darin committed
53
#include "HTMLFormElement.h"
darin's avatar
darin committed
54
#include "HTMLFrameElementBase.h"
darin's avatar
darin committed
55
#include "HTMLNames.h"
mjs's avatar
mjs committed
56
#include "HTMLTableCellElement.h"
weinig@apple.com's avatar
weinig@apple.com committed
57
#include "HitTestResult.h"
ggaren's avatar
ggaren committed
58
#include "Logging.h"
weinig's avatar
 
weinig committed
59
#include "MediaFeatureNames.h"
60
#include "Navigator.h"
darin's avatar
darin committed
61
#include "NodeList.h"
lweintraub's avatar
lweintraub committed
62
#include "Page.h"
63
#include "PageGroup.h"
mjs's avatar
mjs committed
64
#include "RegularExpression.h"
65
#include "RenderPart.h"
mjs's avatar
mjs committed
66
#include "RenderTableCell.h"
darin's avatar
darin committed
67
#include "RenderTextControl.h"
weinig's avatar
weinig committed
68
#include "RenderTheme.h"
69
#include "RenderView.h"
70
#include "ScriptController.h"
71 72
#include "ScriptSourceCode.h"
#include "ScriptValue.h"
ggaren's avatar
ggaren committed
73
#include "Settings.h"
darin's avatar
darin committed
74
#include "TextIterator.h"
darin's avatar
darin committed
75
#include "TextResourceDecoder.h"
76
#include "UserContentURLPattern.h"
darin's avatar
darin committed
77
#include "XMLNames.h"
78
#include "htmlediting.h"
79
#include "markup.h"
andersca@apple.com's avatar
andersca@apple.com committed
80
#include "npruntime_impl.h"
darin's avatar
darin committed
81
#include "visible_units.h"
82
#include <wtf/RefCountedLeakCounter.h>
83
#include <wtf/StdLibExtras.h>
84

85 86 87 88
#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && PLATFORM(DARWIN))
#import <Carbon/Carbon.h>
#endif

89 90 91 92 93
#if USE(JSC)
#include "JSDOMWindowShell.h"
#include "runtime_root.h"
#endif

mjs's avatar
mjs committed
94
#if ENABLE(SVG)
oliver's avatar
oliver committed
95 96
#include "SVGDocument.h"
#include "SVGDocumentExtensions.h"
darin's avatar
darin committed
97 98 99 100
#include "SVGNames.h"
#include "XLinkNames.h"
#endif

101 102 103 104
#if ENABLE(WML)
#include "WMLNames.h"
#endif

105 106 107 108
#if ENABLE(MATHML)
#include "MathMLNames.h"
#endif

darin's avatar
darin committed
109
using namespace std;
darin's avatar
darin committed
110

darin's avatar
darin committed
111 112
namespace WebCore {

113
using namespace HTMLNames;
darin's avatar
darin committed
114

115
#ifndef NDEBUG
116
static WTF::RefCountedLeakCounter frameCounter("Frame");
darin's avatar
darin committed
117
#endif
mjs's avatar
mjs committed
118

darin's avatar
darin committed
119
static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
mjs's avatar
mjs committed
120
{
andersca's avatar
andersca committed
121
    if (!ownerElement)
darin's avatar
darin committed
122
        return 0;
andersca's avatar
andersca committed
123
    return ownerElement->document()->frame();
darin's avatar
darin committed
124 125
}

126
Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
127 128 129
    : m_page(page)
    , m_treeNode(this, parentFromOwnerElement(ownerElement))
    , m_loader(this, frameLoaderClient)
130
    , m_redirectScheduler(this)
131 132 133 134 135 136 137 138 139
    , m_ownerElement(ownerElement)
    , m_script(this)
    , m_selectionGranularity(CharacterGranularity)
    , m_selectionController(this)
    , m_caretBlinkTimer(this, &Frame::caretBlinkTimerFired)
    , m_editor(this)
    , m_eventHandler(this)
    , m_animationController(this)
    , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired)
140 141 142
#if ENABLE(ORIENTATION_EVENTS)
    , m_orientation(0)
#endif
143 144 145 146 147 148 149
    , m_caretVisible(false)
    , m_caretPaint(true)
    , m_highlightTextMatches(false)
    , m_inViewSourceMode(false)
    , m_needsReapplyStyles(false)
    , m_isDisconnected(false)
    , m_excludeFromTextSearch(false)
darin's avatar
darin committed
150
{
151 152 153
    Frame* parent = parentFromOwnerElement(ownerElement);
    m_zoomFactor = parent ? parent->m_zoomFactor : 1.0f;

darin's avatar
darin committed
154 155 156
    AtomicString::init();
    HTMLNames::init();
    QualifiedName::init();
157
    MediaFeatureNames::init();
darin's avatar
darin committed
158

mjs's avatar
mjs committed
159
#if ENABLE(SVG)
darin's avatar
darin committed
160 161 162 163
    SVGNames::init();
    XLinkNames::init();
#endif

164 165 166 167
#if ENABLE(WML)
    WMLNames::init();
#endif

168 169 170 171
#if ENABLE(MATHML)
    MathMLNames::init();
#endif

zimmermann's avatar
zimmermann committed
172 173
    XMLNames::init();

darin's avatar
darin committed
174 175 176 177
    if (!ownerElement)
        page->setMainFrame(this);
    else {
        page->incrementFrameCount();
mitz@apple.com's avatar
mitz@apple.com committed
178
        // Make sure we will not end up with two frames referencing the same owner element.
179
        ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement));
darin's avatar
darin committed
180
        ownerElement->m_contentFrame = this;
darin's avatar
darin committed
181
    }
andersca's avatar
andersca committed
182

darin's avatar
darin committed
183
#ifndef NDEBUG
184
    frameCounter.increment();
mjs's avatar
mjs committed
185 186 187
#endif
}

mjs's avatar
mjs committed
188
Frame::~Frame()
darin's avatar
darin committed
189
{
mjs's avatar
mjs committed
190 191
    setView(0);
    loader()->cancelAndClear();
192

aroben's avatar
aroben committed
193 194
    // FIXME: We should not be doing all this work inside the destructor

195
    ASSERT(!m_lifeSupportTimer.isActive());
mjs's avatar
mjs committed
196

darin's avatar
darin committed
197
#ifndef NDEBUG
198
    frameCounter.decrement();
mjs's avatar
mjs committed
199 200
#endif

andersca's avatar
andersca committed
201
    disconnectOwnerElement();
202

203 204
    if (m_domWindow)
        m_domWindow->disconnectFrame();
205
    script()->clearWindowShell();
206

207 208
    HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end();
    for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it)
209
        (*it)->disconnectFrame();
210

211 212 213
    if (m_view) {
        m_view->hide();
        m_view->clearFrame();
darin's avatar
darin committed
214
    }
215

216
    ASSERT(!m_lifeSupportTimer.isActive());
darin's avatar
darin committed
217 218
}

mjs's avatar
mjs committed
219 220
void Frame::init()
{
221
    m_loader.init();
mjs's avatar
mjs committed
222 223
}

darin's avatar
darin committed
224
FrameLoader* Frame::loader() const
ggaren's avatar
ggaren committed
225
{
226
    return &m_loader;
ggaren's avatar
ggaren committed
227 228
}

229 230 231 232 233
RedirectScheduler* Frame::redirectScheduler() const
{
    return &m_redirectScheduler;
}

234
FrameView* Frame::view() const
darin's avatar
darin committed
235
{
236
    return m_view.get();
237 238
}

darin@apple.com's avatar
darin@apple.com committed
239
void Frame::setView(PassRefPtr<FrameView> view)
240
{
andersca@apple.com's avatar
andersca@apple.com committed
241 242 243 244 245 246
    // We the custom scroll bars as early as possible to prevent m_doc->detach()
    // from messing with the view such that its scroll bars won't be torn down.
    // FIXME: We should revisit this.
    if (m_view)
        m_view->detachCustomScrollbars();

aroben's avatar
aroben committed
247 248 249
    // Detach the document now, so any onUnload handlers get run - if
    // we wait until the view is destroyed, then things won't be
    // hooked up enough for some JavaScript calls to work.
250
    if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) {
darin's avatar
darin committed
251
        // FIXME: We don't call willRemove here. Why is that OK?
252 253 254
        m_doc->detach();
        if (m_view)
            m_view->unscheduleRelayout();
bdakin's avatar
bdakin committed
255
    }
adele's avatar
adele committed
256
    eventHandler()->clear();
aroben's avatar
aroben committed
257

258
    m_view = view;
kmccullo's avatar
kmccullo committed
259 260 261 262 263

    // Only one form submission is allowed per view of a part.
    // Since this part may be getting reused as a result of being
    // pulled from the back/forward cache, reset this flag.
    loader()->resetMultipleFormSubmissionProtection();
darin's avatar
darin committed
264 265
}

darin@apple.com's avatar
darin@apple.com committed
266
ScriptController* Frame::script()
ggaren's avatar
ggaren committed
267
{
268
    return &m_script;
mjs's avatar
mjs committed
269 270
}

271
Document* Frame::document() const
darin's avatar
darin committed
272
{
273
    return m_doc.get();
darin's avatar
darin committed
274 275
}

276
void Frame::setDocument(PassRefPtr<Document> newDoc)
277
{
278
    if (m_doc && m_doc->attached() && !m_doc->inPageCache()) {
darin's avatar
darin committed
279
        // FIXME: We don't call willRemove here. Why is that OK?
280
        m_doc->detach();
darin's avatar
darin committed
281
    }
beidson's avatar
beidson committed
282

283 284 285
    m_doc = newDoc;
    if (m_doc && selection()->isFocusedAndActive())
        setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
286

287 288
    if (m_doc && !m_doc->attached())
        m_doc->attach();
289

mjs@apple.com's avatar
mjs@apple.com committed
290
    // Update the cached 'document' property, which is now stale.
291
    m_script.updateDocument();
292 293
}

294 295 296 297 298 299 300 301 302
#if ENABLE(ORIENTATION_EVENTS)
void Frame::sendOrientationChangeEvent(int orientation)
{
    m_orientation = orientation;
    if (Document* doc = document())
        doc->dispatchWindowEvent(eventNames().orientationchangeEvent, false, false);
}
#endif // ENABLE(ORIENTATION_EVENTS)
    
ggaren's avatar
ggaren committed
303
Settings* Frame::settings() const
darin's avatar
darin committed
304
{
305
    return m_page ? m_page->settings() : 0;
darin's avatar
darin committed
306 307
}

308
String Frame::selectedText() const
darin's avatar
darin committed
309
{
eric@webkit.org's avatar
eric@webkit.org committed
310
    return plainText(selection()->toNormalizedRange().get());
eseidel's avatar
eseidel committed
311
}
darin's avatar
darin committed
312

oliver's avatar
oliver committed
313 314 315 316 317 318
IntRect Frame::firstRectForRange(Range* range) const
{
    int extraWidthToEndOfLine = 0;
    ExceptionCode ec = 0;
    ASSERT(range->startContainer(ec));
    ASSERT(range->endContainer(ec));
319

mitz@apple.com's avatar
mitz@apple.com committed
320 321 322
    InlineBox* startInlineBox;
    int startCaretOffset;
    range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
323 324 325 326 327

    RenderObject* startRenderer = range->startContainer(ec)->renderer();
    IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
    if (startCaretRect != IntRect())
        startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
mitz@apple.com's avatar
mitz@apple.com committed
328 329 330 331

    InlineBox* endInlineBox;
    int endCaretOffset;
    range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
332 333 334 335 336

    RenderObject* endRenderer = range->endContainer(ec)->renderer();
    IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset);
    if (endCaretRect != IntRect())
        endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
mitz@apple.com's avatar
mitz@apple.com committed
337

oliver's avatar
oliver committed
338 339
    if (startCaretRect.y() == endCaretRect.y()) {
        // start and end are on the same line
340 341
        return IntRect(min(startCaretRect.x(), endCaretRect.x()),
                       startCaretRect.y(),
oliver's avatar
oliver committed
342
                       abs(endCaretRect.x() - startCaretRect.x()),
oliver's avatar
oliver committed
343
                       max(startCaretRect.height(), endCaretRect.height()));
oliver's avatar
oliver committed
344
    }
345

oliver's avatar
oliver committed
346
    // start and end aren't on the same line, so go from start to the end of its line
347
    return IntRect(startCaretRect.x(),
oliver's avatar
oliver committed
348 349 350 351 352
                   startCaretRect.y(),
                   startCaretRect.width() + extraWidthToEndOfLine,
                   startCaretRect.height());
}

darin@apple.com's avatar
darin@apple.com committed
353
SelectionController* Frame::selection() const
kocienda's avatar
kocienda committed
354
{
355
    return &m_selectionController;
kocienda's avatar
kocienda committed
356 357
}

aliceli1's avatar
aliceli1 committed
358 359
Editor* Frame::editor() const
{
360
    return &m_editor;
aliceli1's avatar
aliceli1 committed
361 362
}

darin's avatar
darin committed
363
TextGranularity Frame::selectionGranularity() const
364
{
365
    return m_selectionGranularity;
366 367
}

368
void Frame::setSelectionGranularity(TextGranularity granularity)
harrison's avatar
harrison committed
369
{
370
    m_selectionGranularity = granularity;
harrison's avatar
harrison committed
371 372
}

justing's avatar
justing committed
373
SelectionController* Frame::dragCaretController() const
cblu's avatar
cblu committed
374
{
375
    return m_page->dragCaretController();
cblu's avatar
cblu committed
376 377
}

mjs's avatar
mjs committed
378

darin@apple.com's avatar
darin@apple.com committed
379
AnimationController* Frame::animation() const
380
{
381
    return &m_animationController;
382 383
}

384
static RegularExpression* createRegExpForLabels(const Vector<String>& labels)
mjs's avatar
mjs committed
385 386 387 388
{
    // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
    // the same across calls.  We can't do that.

389
    DEFINE_STATIC_LOCAL(RegularExpression, wordRegExp, ("\\w", TextCaseSensitive));
390
    String pattern("(");
mjs's avatar
mjs committed
391 392 393
    unsigned int numLabels = labels.size();
    unsigned int i;
    for (i = 0; i < numLabels; i++) {
394
        String label = labels[i];
mjs's avatar
mjs committed
395 396 397

        bool startsWithWordChar = false;
        bool endsWithWordChar = false;
398
        if (label.length()) {
399 400
            startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0;
            endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0;
mjs's avatar
mjs committed
401
        }
402 403

        if (i)
mjs's avatar
mjs committed
404 405 406 407
            pattern.append("|");
        // Search for word boundaries only if label starts/ends with "word characters".
        // If we always searched for word boundaries, this wouldn't work for languages
        // such as Japanese.
408
        if (startsWithWordChar)
mjs's avatar
mjs committed
409 410
            pattern.append("\\b");
        pattern.append(label);
411
        if (endsWithWordChar)
mjs's avatar
mjs committed
412 413 414
            pattern.append("\\b");
    }
    pattern.append(")");
415
    return new RegularExpression(pattern, TextCaseInsensitive);
mjs's avatar
mjs committed
416 417 418 419
}

String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
{
darin@apple.com's avatar
darin@apple.com committed
420
    RenderObject* cellRenderer = cell->renderer();
mjs's avatar
mjs committed
421 422

    if (cellRenderer && cellRenderer->isTableCell()) {
darin@apple.com's avatar
darin@apple.com committed
423 424
        RenderTableCell* tableCellRenderer = toRenderTableCell(cellRenderer);
        RenderTableCell* cellAboveRenderer = tableCellRenderer->table()->cellAbove(tableCellRenderer);
mjs's avatar
mjs committed
425 426 427

        if (cellAboveRenderer) {
            HTMLTableCellElement* aboveCell =
428
                static_cast<HTMLTableCellElement*>(cellAboveRenderer->node());
mjs's avatar
mjs committed
429 430 431 432 433 434

            if (aboveCell) {
                // search within the above cell we found for a match
                for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
                    if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
                        // For each text chunk, run the regexp
435
                        String nodeString = n->nodeValue();
mjs's avatar
mjs committed
436 437
                        int pos = regExp->searchRev(nodeString);
                        if (pos >= 0)
438
                            return nodeString.substring(pos, regExp->matchedLength());
mjs's avatar
mjs committed
439 440 441 442 443 444 445 446 447 448 449
                    }
                }
            }
        }
    }
    // Any reason in practice to search all cells in that are above cell?
    return String();
}

String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
{
450
    OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
mjs's avatar
mjs committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    // We stop searching after we've seen this many chars
    const unsigned int charsSearchedThreshold = 500;
    // This is the absolute max we search.  We allow a little more slop than
    // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
    const unsigned int maxCharsSearched = 600;
    // If the starting element is within a table, the cell that contains it
    HTMLTableCellElement* startingTableCell = 0;
    bool searchedCellAbove = false;

    // walk backwards in the node tree, until another element, or form, or end of tree
    int unsigned lengthSearched = 0;
    Node* n;
    for (n = element->traversePreviousNode();
         n && lengthSearched < charsSearchedThreshold;
         n = n->traversePreviousNode())
    {
        if (n->hasTagName(formTag)
468
            || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement()))
mjs's avatar
mjs committed
469 470 471 472 473 474
        {
            // We hit another form element or the start of the form - bail out
            break;
        } else if (n->hasTagName(tdTag) && !startingTableCell) {
            startingTableCell = static_cast<HTMLTableCellElement*>(n);
        } else if (n->hasTagName(trTag) && startingTableCell) {
475
            String result = searchForLabelsAboveCell(regExp.get(), startingTableCell);
mjs's avatar
mjs committed
476 477 478 479 480
            if (!result.isEmpty())
                return result;
            searchedCellAbove = true;
        } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
            // For each text chunk, run the regexp
481
            String nodeString = n->nodeValue();
mjs's avatar
mjs committed
482 483 484 485 486
            // add 100 for slop, to make it more likely that we'll search whole nodes
            if (lengthSearched + nodeString.length() > maxCharsSearched)
                nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
            int pos = regExp->searchRev(nodeString);
            if (pos >= 0)
487 488
                return nodeString.substring(pos, regExp->matchedLength());
            lengthSearched += nodeString.length();
mjs's avatar
mjs committed
489 490 491 492 493
        }
    }

    // If we started in a cell, but bailed because we found the start of the form or the
    // previous element, we still might need to search the row above us for a label.
494
    if (startingTableCell && !searchedCellAbove)
495
         return searchForLabelsAboveCell(regExp.get(), startingTableCell);
mjs's avatar
mjs committed
496 497 498 499 500
    return String();
}

String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
{
501
    String name = element->getAttribute(nameAttr);
mitz@apple.com's avatar
mitz@apple.com committed
502 503 504
    if (name.isEmpty())
        return String();

mjs's avatar
mjs committed
505
    // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
506
    replace(name, RegularExpression("\\d", TextCaseSensitive), " ");
mjs's avatar
mjs committed
507
    name.replace('_', ' ');
508

509
    OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
mjs's avatar
mjs committed
510 511 512 513 514 515 516
    // Use the largest match we can find in the whole name string
    int pos;
    int length;
    int bestPos = -1;
    int bestLength = -1;
    int start = 0;
    do {
517
        pos = regExp->match(name, start);
mjs's avatar
mjs committed
518 519 520 521 522 523
        if (pos != -1) {
            length = regExp->matchedLength();
            if (length >= bestLength) {
                bestPos = pos;
                bestLength = length;
            }
mitz@apple.com's avatar
mitz@apple.com committed
524
            start = pos + 1;
mjs's avatar
mjs committed
525 526 527 528
        }
    } while (pos != -1);

    if (bestPos != -1)
529
        return name.substring(bestPos, bestLength);
mjs's avatar
mjs committed
530 531 532
    return String();
}

533
const VisibleSelection& Frame::mark() const
darin's avatar
darin committed
534
{
535
    return m_mark;
darin's avatar
darin committed
536 537
}

538
void Frame::setMark(const VisibleSelection& s)
darin's avatar
darin committed
539
{
darin's avatar
darin committed
540 541 542 543 544
    ASSERT(!s.base().node() || s.base().node()->document() == document());
    ASSERT(!s.extent().node() || s.extent().node()->document() == document());
    ASSERT(!s.start().node() || s.start().node()->document() == document());
    ASSERT(!s.end().node() || s.end().node()->document() == document());

545
    m_mark = s;
darin's avatar
darin committed
546 547
}

adele's avatar
adele committed
548 549 550
void Frame::notifyRendererOfSelectionChange(bool userTriggered)
{
    RenderObject* renderer = 0;
darin@apple.com's avatar
darin@apple.com committed
551 552
    if (selection()->rootEditableElement())
        renderer = selection()->rootEditableElement()->shadowAncestorNode()->renderer();
adele's avatar
adele committed
553 554

    // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
555 556
    if (renderer && renderer->isTextControl())
        toRenderTextControl(renderer)->selectionChanged(userTriggered);
adele's avatar
adele committed
557 558
}

mjs's avatar
mjs committed
559
void Frame::invalidateSelection()
kocienda's avatar
kocienda committed
560
{
darin@apple.com's avatar
darin@apple.com committed
561
    selection()->setNeedsLayout();
kocienda's avatar
kocienda committed
562
    selectionLayoutChanged();
kocienda's avatar
kocienda committed
563 564
}

mjs's avatar
mjs committed
565
void Frame::setCaretVisible(bool flag)
kocienda's avatar
kocienda committed
566
{
567
    if (m_caretVisible == flag)
kocienda's avatar
kocienda committed
568
        return;
kocienda's avatar
kocienda committed
569
    clearCaretRectIfNeeded();
570
    m_caretVisible = flag;
kocienda's avatar
kocienda committed
571
    selectionLayoutChanged();
darin's avatar
darin committed
572 573
}

mjs's avatar
mjs committed
574
void Frame::clearCaretRectIfNeeded()
kocienda's avatar
kocienda committed
575
{
abarth@webkit.org's avatar
abarth@webkit.org committed
576
#if ENABLE(TEXT_CARET)
577 578
    if (m_caretPaint) {
        m_caretPaint = false;
darin@apple.com's avatar
darin@apple.com committed
579
        selection()->invalidateCaretRect();
darin's avatar
darin committed
580
    }
abarth@webkit.org's avatar
abarth@webkit.org committed
581
#endif
kocienda's avatar
kocienda committed
582 583
}

darin's avatar
darin committed
584
// Helper function that tells whether a particular node is an element that has an entire
585
// Frame and FrameView, a <frame>, <iframe>, or <object>.
darin's avatar
darin committed
586
static bool isFrameElement(const Node *n)
darin's avatar
darin committed
587 588 589 590 591 592
{
    if (!n)
        return false;
    RenderObject *renderer = n->renderer();
    if (!renderer || !renderer->isWidget())
        return false;
593
    Widget* widget = toRenderWidget(renderer)->widget();
darin's avatar
darin committed
594
    return widget && widget->isFrameView();
darin's avatar
darin committed
595 596
}

ggaren's avatar
ggaren committed
597
void Frame::setFocusedNodeIfNeeded()
kocienda's avatar
Tests:  
kocienda committed
598
{
hyatt@apple.com's avatar
hyatt@apple.com committed
599
    if (selection()->isNone() || !selection()->isFocused())
kocienda's avatar
Tests:  
kocienda committed
600 601
        return;

602 603 604 605 606 607 608 609 610
    bool caretBrowsing = settings() && settings()->caretBrowsingEnabled();
    if (caretBrowsing) {
        Node* anchor = enclosingAnchorElement(selection()->base());
        if (anchor) {
            page()->focusController()->setFocusedNode(anchor, this);
            return;
        }
    }

darin@apple.com's avatar
darin@apple.com committed
611
    Node* target = selection()->rootEditableElement();
kocienda's avatar
Tests:  
kocienda committed
612
    if (target) {
adele's avatar
adele committed
613 614 615 616 617
        RenderObject* renderer = target->renderer();

        // Walk up the render tree to search for a node to focus.
        // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
        while (renderer) {
darin's avatar
darin committed
618
            // We don't want to set focus on a subframe when selecting in a parent frame,
mjs's avatar
mjs committed
619
            // so add the !isFrameElement check here. There's probably a better way to make this
darin's avatar
darin committed
620
            // work in the long term, but this is the safest fix at this time.
darin's avatar
darin committed
621
            if (target && target->isMouseFocusable() && !isFrameElement(target)) {
justing's avatar
justing committed
622
                page()->focusController()->setFocusedNode(target, this);
kocienda's avatar
kocienda committed
623 624
                return;
            }
adele's avatar
adele committed
625 626
            renderer = renderer->parent();
            if (renderer)
627
                target = renderer->node();
kocienda's avatar
kocienda committed
628
        }
ggaren's avatar
ggaren committed
629
        document()->setFocusedNode(0);
kocienda's avatar
Tests:  
kocienda committed
630
    }
631 632 633

    if (caretBrowsing)
        page()->focusController()->setFocusedNode(0, this);
kocienda's avatar
Tests:  
kocienda committed
634 635
}

mjs's avatar
mjs committed
636
void Frame::selectionLayoutChanged()
kocienda's avatar
kocienda committed
637
{
darin@apple.com's avatar
darin@apple.com committed
638
    bool caretRectChanged = selection()->recomputeCaretRect();
639

abarth@webkit.org's avatar
abarth@webkit.org committed
640
#if ENABLE(TEXT_CARET)
641
    bool caretBrowsing = settings() && settings()->caretBrowsingEnabled();
642
    bool shouldBlink = m_caretVisible
643
        && selection()->isCaret() && (selection()->isContentEditable() || caretBrowsing);
kocienda's avatar
kocienda committed
644

645 646 647
    // If the caret moved, stop the blink timer so we can restart with a
    // black caret in the new location.
    if (caretRectChanged || !shouldBlink)
648
        m_caretBlinkTimer.stop();
649 650 651

    // Start blinking with a black caret. Be sure not to restart if we're
    // already blinking in the right location.
652
    if (shouldBlink && !m_caretBlinkTimer.isActive()) {
653
        if (double blinkInterval = page()->theme()->caretBlinkInterval())
654
            m_caretBlinkTimer.startRepeating(blinkInterval);
655

656 657
        if (!m_caretPaint) {
            m_caretPaint = true;
darin@apple.com's avatar
darin@apple.com committed
658
            selection()->invalidateCaretRect();
darin's avatar
darin committed
659
        }
kocienda's avatar
kocienda committed
660
    }
abarth@webkit.org's avatar
abarth@webkit.org committed
661 662 663 664
#else
    if (!caretRectChanged)
        return;
#endif
kocienda's avatar
kocienda committed
665

666 667
    RenderView* view = contentRenderer();
    if (!view)
ap@webkit.org's avatar
ap@webkit.org committed
668 669
        return;

670
    VisibleSelection selection = this->selection()->selection();
671

ap@webkit.org's avatar
ap@webkit.org committed
672
    if (!selection.isRange())
673
        view->clearSelection();
ap@webkit.org's avatar
ap@webkit.org committed
674 675 676 677 678
    else {
        // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
        // Example: foo <a>bar</a>.  Imagine that a line wrap occurs after 'foo', and that 'bar' is selected.   If we pass [foo, 3]
        // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
        // and will fill the gap before 'bar'.
justin.garcia@apple.com's avatar
justin.garcia@apple.com committed
679
        Position startPos = selection.start();
ap@webkit.org's avatar
ap@webkit.org committed
680 681
        if (startPos.downstream().isCandidate())
            startPos = startPos.downstream();
justin.garcia@apple.com's avatar
justin.garcia@apple.com committed
682
        Position endPos = selection.end();
ap@webkit.org's avatar
ap@webkit.org committed
683 684
        if (endPos.upstream().isCandidate())
            endPos = endPos.upstream();
685

ap@webkit.org's avatar
ap@webkit.org committed
686 687 688 689 690
        // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
        // because we don't yet notify the SelectionController of text removal.
        if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
            RenderObject *startRenderer = startPos.node()->renderer();
            RenderObject *endRenderer = endPos.node()->renderer();
eric@webkit.org's avatar
eric@webkit.org committed
691
            view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
ap@webkit.org's avatar
ap@webkit.org committed
692 693
        }
    }
kocienda's avatar
kocienda committed
694 695
}

darin's avatar
darin committed
696
void Frame::caretBlinkTimerFired(Timer<Frame>*)
kocienda's avatar
kocienda committed
697
{
abarth@webkit.org's avatar
abarth@webkit.org committed
698
#if ENABLE(TEXT_CARET)
699
    ASSERT(m_caretVisible);
darin@apple.com's avatar
darin@apple.com committed
700
    ASSERT(selection()->isCaret());
701
    bool caretPaint = m_caretPaint;
darin@apple.com's avatar
darin@apple.com committed
702
    if (selection()->isCaretBlinkingSuspended() && caretPaint)
darin's avatar
darin committed
703
        return;
704
    m_caretPaint = !caretPaint;
darin@apple.com's avatar
darin@apple.com committed
705
    selection()->invalidateCaretRect();
abarth@webkit.org's avatar
abarth@webkit.org committed
706
#endif
kocienda's avatar
kocienda committed
707 708
}

709
void Frame::paintCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
kocienda's avatar
kocienda committed
710
{
abarth@webkit.org's avatar
abarth@webkit.org committed
711
#if ENABLE(TEXT_CARET)
712
    if (m_caretPaint && m_caretVisible)
713
        selection()->paintCaret(p, tx, ty, clipRect);
abarth@webkit.org's avatar
abarth@webkit.org committed
714
#endif
darin's avatar
darin committed
715 716
}

717
void Frame::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
cblu's avatar
cblu committed
718
{
abarth@webkit.org's avatar
abarth@webkit.org committed
719
#if ENABLE(TEXT_CARET)
720
    SelectionController* dragCaretController = m_page->dragCaretController();
ggaren's avatar
ggaren committed
721
    ASSERT(dragCaretController->selection().isCaret());
justing's avatar
justing committed
722
    if (dragCaretController->selection().start().node()->document()->frame() == this)
723
        dragCaretController->paintCaret(p, tx, ty, clipRect);
abarth@webkit.org's avatar
abarth@webkit.org committed
724
#endif
cblu's avatar
cblu committed
725 726
}

hyatt@apple.com's avatar
hyatt@apple.com committed
727
float Frame::zoomFactor() const
darin's avatar
darin committed
728
{
729
    return m_zoomFactor;
darin's avatar
darin committed
730 731
}

hyatt@apple.com's avatar
hyatt@apple.com committed
732 733
bool Frame::isZoomFactorTextOnly() const
{
734
    return m_page->settings()->zoomsTextOnly();
hyatt@apple.com's avatar
hyatt@apple.com committed
735 736
}

737 738
bool Frame::shouldApplyTextZoom() const
{
739
    if (m_zoomFactor == 1.0f || !isZoomFactorTextOnly())
740
        return false;
741
#if ENABLE(SVG)
ap@webkit.org's avatar
ap@webkit.org committed
742
    if (m_doc->isSVGDocument())
743
        return false;
744
#endif
745 746 747 748 749
    return true;
}

bool Frame::shouldApplyPageZoom() const
{
750
    if (m_zoomFactor == 1.0f || isZoomFactorTextOnly())
751
        return false;
752
#if ENABLE(SVG)
ap@webkit.org's avatar
ap@webkit.org committed
753
    if (m_doc->isSVGDocument())
754
        return false;
755
#endif
756 757 758
    return true;
}

hyatt@apple.com's avatar
hyatt@apple.com committed
759
void Frame::setZoomFactor(float percent, bool isTextOnly)
760
{
761
    if (m_zoomFactor == percent && isZoomFactorTextOnly() == isTextOnly)
hyatt@apple.com's avatar
hyatt@apple.com committed
762
        return;
mjs's avatar
mjs committed
763

weinig's avatar
weinig committed
764
#if ENABLE(SVG)
765
    // SVG doesn't care if the zoom factor is text only.  It will always apply a
hyatt@apple.com's avatar
hyatt@apple.com committed
766
    // zoom to the whole SVG.
ap@webkit.org's avatar
ap@webkit.org committed
767
    if (m_doc->isSVGDocument()) {
768
        if (!static_cast<SVGDocument*>(m_doc.get())->zoomAndPanEnabled())
oliver's avatar
oliver committed
769
            return;
770 771 772 773
        m_zoomFactor = percent;
        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.
        if (m_doc->renderer())
            m_doc->renderer()->repaint();
oliver's avatar
oliver committed
774
        return;