Frame.cpp 64.4 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>
darin's avatar
darin committed
8
 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
ap's avatar
ap committed
9
 * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
mjs's avatar
mjs committed
10
 * Copyright (C) 2007 Trolltech ASA
darin's avatar
darin committed
11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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
24 25
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
darin's avatar
darin committed
26 27
 */

mjs's avatar
mjs committed
28
#include "config.h"
mjs's avatar
mjs committed
29
#include "Frame.h"
darin's avatar
darin committed
30
#include "FramePrivate.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"
35
#include "CSSProperty.h"
36
#include "CSSPropertyNames.h"
37
#include "CachedCSSStyleSheet.h"
darin's avatar
darin committed
38
#include "DOMWindow.h"
39
#include "DocLoader.h"
mjs's avatar
mjs committed
40
#include "DocumentType.h"
darin's avatar
darin committed
41
#include "EditingText.h"
mjs's avatar
mjs committed
42
#include "EditorClient.h"
darin's avatar
darin committed
43
#include "EventNames.h"
adele's avatar
adele committed
44
#include "FocusController.h"
darin's avatar
darin committed
45
#include "FrameLoader.h"
darin's avatar
darin committed
46
#include "FrameView.h"
darin's avatar
darin committed
47
#include "GraphicsContext.h"
oliver's avatar
oliver committed
48
#include "HitTestResult.h"
darin's avatar
darin committed
49
#include "HTMLDocument.h"
darin's avatar
darin committed
50
#include "HTMLFormElement.h"
darin's avatar
darin committed
51
#include "HTMLFrameElementBase.h"
darin's avatar
darin committed
52
#include "HTMLGenericFormElement.h"
darin's avatar
darin committed
53
#include "HTMLNames.h"
mjs's avatar
mjs committed
54
#include "HTMLTableCellElement.h"
ggaren's avatar
ggaren committed
55
#include "Logging.h"
weinig's avatar
 
weinig committed
56
#include "MediaFeatureNames.h"
darin's avatar
darin committed
57
#include "NodeList.h"
lweintraub's avatar
lweintraub committed
58
#include "Page.h"
mjs's avatar
mjs committed
59
#include "RegularExpression.h"
60
#include "RenderPart.h"
mjs's avatar
mjs committed
61
#include "RenderTableCell.h"
darin's avatar
darin committed
62
#include "RenderTextControl.h"
weinig's avatar
weinig committed
63
#include "RenderTheme.h"
64
#include "RenderView.h"
ggaren's avatar
ggaren committed
65
#include "Settings.h"
ggaren's avatar
ggaren committed
66
#include "SystemTime.h"
darin's avatar
darin committed
67
#include "TextIterator.h"
darin's avatar
darin committed
68
#include "TextResourceDecoder.h"
darin's avatar
darin committed
69
#include "XMLNames.h"
mjs's avatar
mjs committed
70 71 72
#include "bindings/NP_jsobject.h"
#include "bindings/npruntime_impl.h"
#include "bindings/runtime_root.h"
darin's avatar
darin committed
73 74 75
#include "kjs_proxy.h"
#include "kjs_window.h"
#include "visible_units.h"
76

mjs's avatar
mjs committed
77
#if ENABLE(SVG)
oliver's avatar
oliver committed
78 79
#include "SVGDocument.h"
#include "SVGDocumentExtensions.h"
darin's avatar
darin committed
80 81 82 83
#include "SVGNames.h"
#include "XLinkNames.h"
#endif

darin's avatar
darin committed
84
using namespace std;
darin's avatar
darin committed
85 86

using KJS::JSLock;
darin's avatar
darin committed
87 88 89

namespace WebCore {

darin's avatar
darin committed
90
using namespace EventNames;
91
using namespace HTMLNames;
darin's avatar
darin committed
92

aroben's avatar
aroben committed
93
double Frame::s_currentPaintTimeStamp = 0.0;
kocienda's avatar
kocienda committed
94

95
class UserStyleSheetLoader : public CachedResourceClient {
darin's avatar
darin committed
96
public:
andersca's avatar
andersca committed
97 98 99
    UserStyleSheetLoader(PassRefPtr<Document> document, const String& url)
        : m_document(document)
        , m_cachedSheet(m_document->docLoader()->requestUserCSSStyleSheet(url, ""))
darin's avatar
darin committed
100
    {
adele@apple.com's avatar
adele@apple.com committed
101 102
        if (m_cachedSheet) {
            m_document->addPendingSheet();
adele@apple.com's avatar
adele@apple.com committed
103
            m_cachedSheet->ref(this);
adele@apple.com's avatar
adele@apple.com committed
104
        }
darin's avatar
darin committed
105
    }
darin's avatar
darin committed
106
    ~UserStyleSheetLoader()
darin's avatar
darin committed
107
    {
adele@apple.com's avatar
adele@apple.com committed
108 109 110 111 112
        if (m_cachedSheet) {
            if (!m_cachedSheet->isLoaded())
                m_document->removePendingSheet();        
            m_cachedSheet->deref(this);
        }
darin's avatar
darin committed
113
    }
darin's avatar
darin committed
114
private:
ap's avatar
ap committed
115
    virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& sheet)
darin's avatar
darin committed
116
    {
bdakin's avatar
bdakin committed
117
        m_document->removePendingSheet();
andersca's avatar
andersca committed
118 119
        if (Frame* frame = m_document->frame())
            frame->setUserStyleSheet(sheet);
darin's avatar
darin committed
120
    }
andersca's avatar
andersca committed
121
    RefPtr<Document> m_document;
darin's avatar
darin committed
122 123
    CachedCSSStyleSheet* m_cachedSheet;
};
darin's avatar
darin committed
124

darin's avatar
darin committed
125
#ifndef NDEBUG
ggaren's avatar
ggaren committed
126 127
WTFLogChannel LogWebCoreFrameLeaks =  { 0x00000000, "", WTFLogChannelOn };

mjs's avatar
mjs committed
128 129
struct FrameCounter { 
    static int count; 
ggaren's avatar
ggaren committed
130 131 132 133 134
    ~FrameCounter() 
    { 
        if (count)
            LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count);
    }
mjs's avatar
mjs committed
135 136 137
};
int FrameCounter::count = 0;
static FrameCounter frameCounter;
darin's avatar
darin committed
138
#endif
mjs's avatar
mjs committed
139

darin's avatar
darin committed
140
static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
mjs's avatar
mjs committed
141
{
andersca's avatar
andersca committed
142
    if (!ownerElement)
darin's avatar
darin committed
143
        return 0;
andersca's avatar
andersca committed
144
    return ownerElement->document()->frame();
darin's avatar
darin committed
145 146
}

darin's avatar
darin committed
147
Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient) 
ggaren's avatar
ggaren committed
148
    : d(new FramePrivate(page, parentFromOwnerElement(ownerElement), this, ownerElement, frameLoaderClient))
darin's avatar
darin committed
149 150 151 152 153
{
    AtomicString::init();
    EventNames::init();
    HTMLNames::init();
    QualifiedName::init();
154
    MediaFeatureNames::init();
darin's avatar
darin committed
155

mjs's avatar
mjs committed
156
#if ENABLE(SVG)
darin's avatar
darin committed
157 158 159 160
    SVGNames::init();
    XLinkNames::init();
#endif

zimmermann's avatar
zimmermann committed
161 162
    XMLNames::init();

darin's avatar
darin committed
163 164 165 166 167 168 169
    if (!ownerElement)
        page->setMainFrame(this);
    else {
        // FIXME: Frames were originally created with a refcount of 1.
        // Leave this ref call here until we can straighten that out.
        ref();
        page->incrementFrameCount();
darin's avatar
darin committed
170
        ownerElement->m_contentFrame = this;
darin's avatar
darin committed
171
    }
andersca's avatar
andersca committed
172

darin's avatar
darin committed
173
#ifndef NDEBUG
mjs's avatar
mjs committed
174 175 176 177
    ++FrameCounter::count;
#endif
}

mjs's avatar
mjs committed
178
Frame::~Frame()
darin's avatar
darin committed
179
{
mjs's avatar
mjs committed
180 181 182 183 184 185 186 187 188
    setView(0);
    loader()->clearRecordedFormValues();

#if PLATFORM(MAC)
    setBridge(0);
#endif
    
    loader()->cancelAndClear();
    
aroben's avatar
aroben committed
189 190
    // FIXME: We should not be doing all this work inside the destructor

darin's avatar
darin committed
191
    ASSERT(!d->m_lifeSupportTimer.isActive());
mjs's avatar
mjs committed
192

darin's avatar
darin committed
193
#ifndef NDEBUG
mjs's avatar
mjs committed
194 195 196
    --FrameCounter::count;
#endif

ggaren's avatar
ggaren committed
197
    if (d->m_jscript && d->m_jscript->haveInterpreter())
ggaren@apple.com's avatar
ggaren@apple.com committed
198
        static_cast<KJS::Window*>(d->m_jscript->globalObject())->disconnectFrame();
darin's avatar
darin committed
199

andersca's avatar
andersca committed
200 201
    disconnectOwnerElement();
    
ggaren's avatar
ggaren committed
202 203 204
    if (d->m_domWindow)
        d->m_domWindow->disconnectFrame();
            
darin's avatar
darin committed
205 206
    if (d->m_view) {
        d->m_view->hide();
darin's avatar
darin committed
207
        d->m_view->clearFrame();
darin's avatar
darin committed
208
    }
darin's avatar
darin committed
209
  
darin's avatar
darin committed
210
    ASSERT(!d->m_lifeSupportTimer.isActive());
mjs's avatar
mjs committed
211

darin's avatar
darin committed
212 213 214
    delete d->m_userStyleSheetLoader;
    delete d;
    d = 0;
darin's avatar
darin committed
215 216
}

mjs's avatar
mjs committed
217 218 219 220 221
void Frame::init()
{
    d->m_loader->init();
}

darin's avatar
darin committed
222
FrameLoader* Frame::loader() const
ggaren's avatar
ggaren committed
223
{
darin's avatar
darin committed
224
    return d->m_loader;
ggaren's avatar
ggaren committed
225 226
}

227
FrameView* Frame::view() const
darin's avatar
darin committed
228
{
229 230 231 232 233
    return d->m_view.get();
}

void Frame::setView(FrameView* view)
{
aroben's avatar
aroben committed
234 235 236
    // 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.
darin's avatar
darin committed
237
    if (!view && d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
darin's avatar
darin committed
238
        // FIXME: We don't call willRemove here. Why is that OK?
aroben's avatar
aroben committed
239
        d->m_doc->detach();
bdakin's avatar
bdakin committed
240 241 242
        if (d->m_view)
            d->m_view->unscheduleRelayout();
    }
adele's avatar
adele committed
243
    eventHandler()->clear();
aroben's avatar
aroben committed
244

245
    d->m_view = view;
kmccullo's avatar
kmccullo committed
246 247 248 249 250

    // 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
251 252
}

darin's avatar
darin committed
253
KJSProxy *Frame::scriptProxy()
ggaren's avatar
ggaren committed
254
{
ggaren's avatar
ggaren committed
255 256
    Settings* settings = this->settings();
    if (!settings || !settings->isJavaScriptEnabled())
ggaren's avatar
ggaren committed
257 258 259 260 261 262
        return 0;

    if (!d->m_jscript)
        d->m_jscript = new KJSProxy(this);

    return d->m_jscript;
mjs's avatar
mjs committed
263 264
}

darin's avatar
darin committed
265
Document *Frame::document() const
darin's avatar
darin committed
266
{
darin's avatar
darin committed
267 268
    if (d)
        return d->m_doc.get();
darin's avatar
darin committed
269 270 271
    return 0;
}

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

    d->m_doc = newDoc;
    if (d->m_doc && d->m_isActive)
        setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
        
    if (d->m_doc && !d->m_doc->attached())
        d->m_doc->attach();
antti's avatar
antti committed
285
    
ggaren's avatar
ggaren committed
286 287 288
    // Remove the cached 'document' property, which is now stale.
    if (d->m_jscript)
        d->m_jscript->clearDocumentWrapper();
289 290
}

ggaren's avatar
ggaren committed
291
Settings* Frame::settings() const
darin's avatar
darin committed
292
{
ggaren's avatar
ggaren committed
293
    return d->m_page ? d->m_page->settings() : 0;
darin's avatar
darin committed
294 295
}

darin's avatar
darin committed
296
void Frame::setUserStyleSheetLocation(const KURL& url)
darin's avatar
darin committed
297
{
darin's avatar
darin committed
298 299 300
    delete d->m_userStyleSheetLoader;
    d->m_userStyleSheetLoader = 0;
    if (d->m_doc && d->m_doc->docLoader())
andersca's avatar
andersca committed
301
        d->m_userStyleSheetLoader = new UserStyleSheetLoader(d->m_doc, url.url());
darin's avatar
darin committed
302 303
}

304
void Frame::setUserStyleSheet(const String& styleSheet)
darin's avatar
darin committed
305
{
darin's avatar
darin committed
306 307 308 309
    delete d->m_userStyleSheetLoader;
    d->m_userStyleSheetLoader = 0;
    if (d->m_doc)
        d->m_doc->setUserStyleSheet(styleSheet);
darin's avatar
darin committed
310 311
}

312
String Frame::selectedText() const
darin's avatar
darin committed
313
{
justing's avatar
justing committed
314
    return plainText(selectionController()->toRange().get());
eseidel's avatar
eseidel committed
315
}
darin's avatar
darin committed
316

oliver's avatar
oliver committed
317 318 319 320 321 322 323 324 325 326 327 328 329
IntRect Frame::firstRectForRange(Range* range) const
{
    int extraWidthToEndOfLine = 0;
    ExceptionCode ec = 0;
    ASSERT(range->startContainer(ec));
    ASSERT(range->endContainer(ec));
    IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine);
    ASSERT(!ec);
    IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(range->endOffset(ec), UPSTREAM);
    ASSERT(!ec);
    
    if (startCaretRect.y() == endCaretRect.y()) {
        // start and end are on the same line
oliver's avatar
oliver committed
330
        return IntRect(min(startCaretRect.x(), endCaretRect.x()), 
oliver's avatar
oliver committed
331 332
                       startCaretRect.y(), 
                       abs(endCaretRect.x() - startCaretRect.x()),
oliver's avatar
oliver committed
333
                       max(startCaretRect.height(), endCaretRect.height()));
oliver's avatar
oliver committed
334 335 336 337 338 339 340 341 342
    }
    
    // start and end aren't on the same line, so go from start to the end of its line
    return IntRect(startCaretRect.x(), 
                   startCaretRect.y(),
                   startCaretRect.width() + extraWidthToEndOfLine,
                   startCaretRect.height());
}

justing's avatar
justing committed
343
SelectionController* Frame::selectionController() const
kocienda's avatar
kocienda committed
344
{
thatcher's avatar
thatcher committed
345
    return &d->m_selectionController;
kocienda's avatar
kocienda committed
346 347
}

aliceli1's avatar
aliceli1 committed
348 349
Editor* Frame::editor() const
{
thatcher's avatar
thatcher committed
350
    return &d->m_editor;
aliceli1's avatar
aliceli1 committed
351 352
}

darin's avatar
darin committed
353
TextGranularity Frame::selectionGranularity() const
354 355 356 357
{
    return d->m_selectionGranularity;
}

darin's avatar
darin committed
358
void Frame::setSelectionGranularity(TextGranularity granularity) const
harrison's avatar
harrison committed
359 360 361 362
{
    d->m_selectionGranularity = granularity;
}

justing's avatar
justing committed
363
SelectionController* Frame::dragCaretController() const
cblu's avatar
cblu committed
364
{
justing's avatar
justing committed
365
    return d->m_page->dragCaretController();
cblu's avatar
cblu committed
366 367
}

mjs's avatar
mjs committed
368

369 370 371 372 373
AnimationController* Frame::animationController() const
{
    return &d->m_animationController;
}

mjs's avatar
mjs committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
// Either get cached regexp or build one that matches any of the labels.
// The regexp we build is of the form:  (STR1|STR2|STRN)
static RegularExpression *regExpForLabels(const Vector<String>& labels)
{
    // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
    // the same across calls.  We can't do that.

    static RegularExpression wordRegExp = RegularExpression("\\w");
    DeprecatedString pattern("(");
    unsigned int numLabels = labels.size();
    unsigned int i;
    for (i = 0; i < numLabels; i++) {
        DeprecatedString label = labels[i].deprecatedString();

        bool startsWithWordChar = false;
        bool endsWithWordChar = false;
        if (label.length() != 0) {
            startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
            endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
        }
        
        if (i != 0)
            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.
        if (startsWithWordChar) {
            pattern.append("\\b");
        }
        pattern.append(label);
        if (endsWithWordChar) {
            pattern.append("\\b");
        }
    }
    pattern.append(")");
    return new RegularExpression(pattern, false);
}

String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
{
    RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());

    if (cellRenderer && cellRenderer->isTableCell()) {
        RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);

        if (cellAboveRenderer) {
            HTMLTableCellElement* aboveCell =
                static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());

            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
                        DeprecatedString nodeString = n->nodeValue().deprecatedString();
                        int pos = regExp->searchRev(nodeString);
                        if (pos >= 0)
                            return nodeString.mid(pos, regExp->matchedLength());
                    }
                }
            }
        }
    }
    // Any reason in practice to search all cells in that are above cell?
    return String();
}

String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
{
    RegularExpression* regExp = regExpForLabels(labels);
    // 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)
            || (n->isHTMLElement()
                && static_cast<HTMLElement*>(n)->isGenericFormElement()))
        {
            // 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) {
            String result = searchForLabelsAboveCell(regExp, startingTableCell);
            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
            DeprecatedString nodeString = n->nodeValue().deprecatedString();
            // 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)
                return nodeString.mid(pos, regExp->matchedLength());
            else
                lengthSearched += nodeString.length();
        }
    }

    // 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.
    if (startingTableCell && !searchedCellAbove) {
         return searchForLabelsAboveCell(regExp, startingTableCell);
    }
    return String();
}

String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
{
    DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
    // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
darin@apple.com's avatar
darin@apple.com committed
499
    name.replace(RegularExpression("\\d"), " ");
mjs's avatar
mjs committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    name.replace('_', ' ');
    
    RegularExpression* regExp = regExpForLabels(labels);
    // 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 {
        pos = regExp->search(name, start);
        if (pos != -1) {
            length = regExp->matchedLength();
            if (length >= bestLength) {
                bestPos = pos;
                bestLength = length;
            }
            start = pos+1;
        }
    } while (pos != -1);

    if (bestPos != -1)
        return name.mid(bestPos, bestLength);
    return String();
}

darin's avatar
darin committed
526
const Selection& Frame::mark() const
darin's avatar
darin committed
527 528 529 530
{
    return d->m_mark;
}

darin's avatar
darin committed
531
void Frame::setMark(const Selection& s)
darin's avatar
darin committed
532
{
darin's avatar
darin committed
533 534 535 536 537
    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());

darin's avatar
darin committed
538 539 540
    d->m_mark = s;
}

adele's avatar
adele committed
541 542 543
void Frame::notifyRendererOfSelectionChange(bool userTriggered)
{
    RenderObject* renderer = 0;
justing's avatar
justing committed
544 545
    if (selectionController()->rootEditableElement())
        renderer = selectionController()->rootEditableElement()->shadowAncestorNode()->renderer();
adele's avatar
adele committed
546 547 548

    // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
    if (renderer && (renderer->isTextArea() || renderer->isTextField()))
darin's avatar
darin committed
549
        static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
adele's avatar
adele committed
550 551
}

mjs's avatar
mjs committed
552
void Frame::invalidateSelection()
kocienda's avatar
kocienda committed
553
{
justing's avatar
justing committed
554
    selectionController()->setNeedsLayout();
kocienda's avatar
kocienda committed
555
    selectionLayoutChanged();
kocienda's avatar
kocienda committed
556 557
}

mjs's avatar
mjs committed
558
void Frame::setCaretVisible(bool flag)
kocienda's avatar
kocienda committed
559 560 561
{
    if (d->m_caretVisible == flag)
        return;
kocienda's avatar
kocienda committed
562
    clearCaretRectIfNeeded();
kocienda's avatar
kocienda committed
563
    d->m_caretVisible = flag;
kocienda's avatar
kocienda committed
564
    selectionLayoutChanged();
darin's avatar
darin committed
565 566
}

mjs's avatar
mjs committed
567
void Frame::clearCaretRectIfNeeded()
kocienda's avatar
kocienda committed
568 569 570
{
    if (d->m_caretPaint) {
        d->m_caretPaint = false;
571
        selectionController()->invalidateCaretRect();
darin's avatar
darin committed
572
    }
kocienda's avatar
kocienda committed
573 574
}

darin's avatar
darin committed
575
// Helper function that tells whether a particular node is an element that has an entire
576
// Frame and FrameView, a <frame>, <iframe>, or <object>.
darin's avatar
darin committed
577
static bool isFrameElement(const Node *n)
darin's avatar
darin committed
578 579 580 581 582 583
{
    if (!n)
        return false;
    RenderObject *renderer = n->renderer();
    if (!renderer || !renderer->isWidget())
        return false;
584
    Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
darin's avatar
darin committed
585
    return widget && widget->isFrameView();
darin's avatar
darin committed
586 587
}

ggaren's avatar
ggaren committed
588
void Frame::setFocusedNodeIfNeeded()
kocienda's avatar
Tests:  
kocienda committed
589
{
justing's avatar
justing committed
590
    if (!document() || selectionController()->isNone() || !d->m_isActive)
kocienda's avatar
Tests:  
kocienda committed
591 592
        return;

justing's avatar
justing committed
593
    Node* target = selectionController()->rootEditableElement();
kocienda's avatar
Tests:  
kocienda committed
594
    if (target) {
adele's avatar
adele committed
595 596 597 598 599
        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
600
            // We don't want to set focus on a subframe when selecting in a parent frame,
mjs's avatar
mjs committed
601
            // so add the !isFrameElement check here. There's probably a better way to make this
darin's avatar
darin committed
602
            // work in the long term, but this is the safest fix at this time.
darin's avatar
darin committed
603
            if (target && target->isMouseFocusable() && !isFrameElement(target)) {
justing's avatar
justing committed
604
                page()->focusController()->setFocusedNode(target, this);
kocienda's avatar
kocienda committed
605 606
                return;
            }
adele's avatar
adele committed
607 608 609
            renderer = renderer->parent();
            if (renderer)
                target = renderer->element();
kocienda's avatar
kocienda committed
610
        }
ggaren's avatar
ggaren committed
611
        document()->setFocusedNode(0);
kocienda's avatar
Tests:  
kocienda committed
612 613 614
    }
}

mjs's avatar
mjs committed
615
void Frame::selectionLayoutChanged()
kocienda's avatar
kocienda committed
616
{
617 618 619 620
    bool caretRectChanged = selectionController()->recomputeCaretRect();

    bool shouldBlink = d->m_caretVisible
        && selectionController()->isCaret() && selectionController()->isContentEditable();
kocienda's avatar
kocienda committed
621

622 623 624 625 626 627 628 629
    // If the caret moved, stop the blink timer so we can restart with a
    // black caret in the new location.
    if (caretRectChanged || !shouldBlink)
        d->m_caretBlinkTimer.stop();

    // Start blinking with a black caret. Be sure not to restart if we're
    // already blinking in the right location.
    if (shouldBlink && !d->m_caretBlinkTimer.isActive()) {
alp's avatar
alp committed
630
        d->m_caretBlinkTimer.startRepeating(theme()->caretBlinkFrequency());
darin's avatar
darin committed
631 632 633 634
        if (!d->m_caretPaint) {
            d->m_caretPaint = true;
            selectionController()->invalidateCaretRect();
        }
kocienda's avatar
kocienda committed
635 636
    }

ap@webkit.org's avatar
ap@webkit.org committed
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
    if (!renderer())
        return;
    RenderView* canvas = static_cast<RenderView*>(renderer());

    Selection selection = selectionController()->selection();
        
    if (!selection.isRange())
        canvas->clearSelection();
    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'.
        Position startPos = selection.visibleStart().deepEquivalent();
        if (startPos.downstream().isCandidate())
            startPos = startPos.downstream();
        Position endPos = selection.visibleEnd().deepEquivalent();
        if (endPos.upstream().isCandidate())
            endPos = endPos.upstream();
        
        // 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();
            canvas->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
        }
    }
kocienda's avatar
kocienda committed
665 666
}

darin's avatar
darin committed
667
void Frame::caretBlinkTimerFired(Timer<Frame>*)
kocienda's avatar
kocienda committed
668
{
669 670
    ASSERT(d->m_caretVisible);
    ASSERT(selectionController()->isCaret());
darin's avatar
darin committed
671
    bool caretPaint = d->m_caretPaint;
darin's avatar
darin committed
672
    if (selectionController()->isCaretBlinkingSuspended() && caretPaint)
darin's avatar
darin committed
673 674
        return;
    d->m_caretPaint = !caretPaint;
675
    selectionController()->invalidateCaretRect();
kocienda's avatar
kocienda committed
676 677
}

678
void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const
kocienda's avatar
kocienda committed
679
{
justing's avatar
justing committed
680
    if (d->m_caretPaint && d->m_caretVisible)
justing's avatar
justing committed
681
        selectionController()->paintCaret(p, rect);
darin's avatar
darin committed
682 683
}

684
void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const
cblu's avatar
cblu committed
685
{
justing's avatar
justing committed
686
    SelectionController* dragCaretController = d->m_page->dragCaretController();
ggaren's avatar
ggaren committed
687
    ASSERT(dragCaretController->selection().isCaret());
justing's avatar
justing committed
688 689
    if (dragCaretController->selection().start().node()->document()->frame() == this)
        dragCaretController->paintCaret(p, rect);
cblu's avatar
cblu committed
690 691
}

mjs's avatar
mjs committed
692
int Frame::zoomFactor() const
darin's avatar
darin committed
693 694 695 696
{
  return d->m_zoomFactor;
}

mjs's avatar
mjs committed
697
void Frame::setZoomFactor(int percent)
darin's avatar
darin committed
698
{  
mjs's avatar
mjs committed
699 700 701
  if (d->m_zoomFactor == percent)
      return;

weinig's avatar
weinig committed
702
#if ENABLE(SVG)
oliver's avatar
oliver committed
703 704 705 706 707 708 709
    if (d->m_doc && d->m_doc->isSVGDocument()) {
        if (!static_cast<SVGDocument*>(d->m_doc.get())->zoomAndPanEnabled())
            return;
        d->m_zoomFactor = percent;
        if (d->m_doc->renderer())
            d->m_doc->renderer()->repaint();
        return;
weinig's avatar
weinig committed
710 711
    }
#endif
oliver's avatar
oliver committed
712 713
  d->m_zoomFactor = percent;
  if (d->m_doc)
darin's avatar
darin committed
714
      d->m_doc->recalcStyle(Node::Force);
darin's avatar
darin committed
715

mjs's avatar
mjs committed
716
  for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
mjs's avatar
mjs committed
717
      child->setZoomFactor(d->m_zoomFactor);
darin's avatar
darin committed
718

719
  if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout())
mjs's avatar
mjs committed
720
      view()->layout();
darin's avatar
darin committed
721 722
}

hyatt's avatar
hyatt committed
723 724 725 726 727 728 729 730 731 732 733 734 735 736
void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
{
    if (!d->m_doc)
        return;

    d->m_doc->setPrinting(printing);
    view()->setMediaType(printing ? "print" : "screen");
    d->m_doc->updateStyleSelector();
    forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);

    for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
        child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
}

darin's avatar
darin committed
737
void Frame::setJSStatusBarText(const String& text)
darin's avatar
darin committed
738
{
darin's avatar
darin committed
739
    d->m_kjsStatusBarText = text;
andersca's avatar
andersca committed
740 741
    if (d->m_page)
        d->m_page->chrome()->setStatusbarText(this, d->m_kjsStatusBarText);
darin's avatar
darin committed
742 743
}

darin's avatar
darin committed
744
void Frame::setJSDefaultStatusBarText(const String& text)
darin's avatar
darin committed
745
{
darin's avatar
darin committed
746
    d->m_kjsDefaultStatusBarText = text;
andersca's avatar
andersca committed
747 748
    if (d->m_page)
        d->m_page->chrome()->setStatusbarText(this, d->m_kjsDefaultStatusBarText);
darin's avatar
darin committed
749 750
}

darin's avatar
darin committed
751
String Frame::jsStatusBarText() const
darin's avatar
darin committed
752 753 754 755
{
    return d->m_kjsStatusBarText;
}

darin's avatar
darin committed
756
String Frame::jsDefaultStatusBarText() const
darin's avatar
darin committed
757 758 759 760
{
   return d->m_kjsDefaultStatusBarText;
}

761
void Frame::setNeedsReapplyStyles()
darin's avatar
darin committed
762
{
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
    if (d->m_needsReapplyStyles)
        return;

    d->m_needsReapplyStyles = true;

    // Invalidate the FrameView so that FrameView::layout will get called,
    // which calls reapplyStyles.
    view()->invalidate();
}

bool Frame::needsReapplyStyles() const
{
    return d->m_needsReapplyStyles;
}

void Frame::reapplyStyles()
{
    d->m_needsReapplyStyles = false;

    // FIXME: This call doesn't really make sense in a method called
    // "reapplyStyles". We should probably eventually move it into its own
    // method.
darin's avatar
darin committed
785
    if (d->m_doc)
ggaren's avatar
ggaren committed
786
        d->m_doc->docLoader()->setAutoLoadImages(d->m_page && d->m_page->settings()->loadsImagesAutomatically());
787
        
ggaren's avatar
ggaren committed
788
    const KURL userStyleSheetLocation = d->m_page ? d->m_page->settings()->userStyleSheetLocation() : KURL();
789 790 791 792
    if (!userStyleSheetLocation.isEmpty())
        setUserStyleSheetLocation(userStyleSheetLocation);
    else
        setUserStyleSheet(String());
darin's avatar
darin committed
793 794 795 796

    // FIXME: It's not entirely clear why the following is needed.
    // The document automatically does this as required when you set the style sheet.
    // But we had problems when this code was removed. Details are in
darin's avatar
darin committed
797
    // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
darin's avatar
darin committed
798 799
    if (d->m_doc)
        d->m_doc->updateStyleSelector();
darin's avatar
darin committed
800 801
}

justing's avatar
justing committed
802
bool Frame::shouldChangeSelection(const Selection& newSelection) const
darin's avatar
darin committed
803
{
justing's avatar
justing committed
804
    return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false);
darin's avatar
darin committed
805 806
}

mjs's avatar
mjs committed
807
bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
lweintraub's avatar
lweintraub committed
808
{
mjs's avatar
mjs committed
809 810 811 812 813 814 815
    return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
                                                         affinity, stillSelecting);
}

bool Frame::shouldDeleteSelection(const Selection& selection) const
{
    return editor()->client()->shouldDeleteRange(selection.toRange().get());
lweintraub's avatar
lweintraub committed
816 817
}

mjs's avatar
mjs committed
818
bool Frame::isContentEditable() const 
kocienda's avatar
Tests:  
kocienda committed
819
{
mjs's avatar
mjs committed
820 821
    if (d->m_editor.clientIsEditable())
        return true;
adele's avatar
adele committed
822 823 824
    if (!d->m_doc)
        return false;
    return d->m_doc->inDesignMode();
kocienda's avatar
Tests:  
kocienda committed
825 826
}

mjs's avatar
mjs committed
827
#if !PLATFORM(MAC)
adele's avatar
adele committed
828

darin's avatar
darin committed
829
void Frame::setUseSecureKeyboardEntry(bool)
adele's avatar
adele committed
830 831 832
{
}

mjs's avatar
mjs committed
833
#endif
adele's avatar
adele committed
834

beidson's avatar
beidson committed
835
void Frame::updateSecureKeyboardEntryIfActive()
darin's avatar
darin committed
836 837
{
    if (d->m_isActive)
beidson's avatar
beidson committed
838
        setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
darin's avatar
darin committed
839 840
}

darin's avatar
darin committed
841
CSSMutableStyleDeclaration *Frame::typingStyle() const
kocienda's avatar
kocienda committed
842
{
darin's avatar
darin committed
843
    return d->m_typingStyle.get();
kocienda's avatar
kocienda committed
844 845
}

darin's avatar
darin committed
846
void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
kocienda's avatar
kocienda committed
847 848 849 850
{
    d->m_typingStyle = style;
}

mjs's avatar
mjs committed
851
void Frame::clearTypingStyle()
kocienda's avatar
kocienda committed
852
{
darin's avatar
darin committed
853
    d->m_typingStyle = 0;
kocienda's avatar
kocienda committed
854 855
}

darin's avatar
darin committed
856
void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
kocienda's avatar
kocienda committed
857 858 859 860 861 862 863
{
    if (!style || style->length() == 0) {
        clearTypingStyle();
        return;
    }

    // Calculate the current typing style.
darin's avatar
darin committed
864
    RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
kocienda's avatar
kocienda committed
865
    if (typingStyle()) {
darin's avatar
darin committed
866
        typingStyle()->merge(mutableStyle.get());
kocienda's avatar
kocienda committed
867 868
        mutableStyle = typingStyle();
    }
harrison's avatar
harrison committed
869

justing's avatar
justing committed
870
    Node *node = selectionController()->selection().visibleStart().deepEquivalent().node();
darin's avatar
darin committed
871
    CSSComputedStyleDeclaration computedStyle(node);
darin's avatar
darin committed
872
    computedStyle.diff(mutableStyle.get());
kocienda's avatar
kocienda committed
873 874
    
    // Handle block styles, substracting these from the typing style.
darin's avatar
darin committed
875
    RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
darin's avatar
darin committed
876
    blockStyle->diff(mutableStyle.get());
darin's avatar
darin committed
877 878
    if (document() && blockStyle->length() > 0)
        applyCommand(new ApplyStyleCommand(document(), blockStyle.get(), editingAction));
kocienda's avatar
kocienda committed
879 880
    
    // Set the remaining style as the typing style.
darin's avatar
darin committed
881
    d->m_typingStyle = mutableStyle.release();
kocienda's avatar
kocienda committed
882 883
}

884
static void updateState(CSSMutableStyleDeclaration *desiredStyle, CSSComputedStyleDeclaration *computedStyle, bool& atStart, Frame::TriState& state)
darin's avatar
darin committed
885
{
darin's avatar
darin committed
886 887
    DeprecatedValueListConstIterator<CSSProperty> end;
    for (DeprecatedValueListConstIterator<CSSProperty> it = desiredStyle->valuesIterator(); it != end; ++it) {
darin's avatar
darin committed
888
        int propertyID = (*it).id();
darin's avatar
darin committed
889 890
        String desiredProperty = desiredStyle->getPropertyValue(propertyID);
        String computedProperty = computedStyle->getPropertyValue(propertyID);
mjs's avatar
mjs committed
891 892
        Frame::TriState propertyState = equalIgnoringCase(desiredProperty, computedProperty)
            ? Frame::trueTriState : Frame::falseTriState;
darin's avatar
darin committed
893 894 895 896
        if (atStart) {
            state = propertyState;
            atStart = false;
        } else if (state != propertyState) {
mjs's avatar
mjs committed
897
            state = Frame::mixedTriState;
darin's avatar
darin committed
898 899 900 901 902
            break;
        }
    }
}

darin's avatar
darin committed
903
Frame::TriState Frame::selectionHasStyle(CSSStyleDeclaration *style) const
darin's avatar
darin committed
904 905 906 907
{
    bool atStart = true;
    TriState state = falseTriState;

darin's avatar
darin committed
908
    RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
darin's avatar
darin committed
909

justing's avatar
justing committed
910
    if (!selectionController()->isRange()) {
darin's avatar
darin committed
911 912
        Node* nodeToRemove;
        RefPtr<CSSComputedStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
darin's avatar
darin committed
913 914
        if (!selectionStyle)
            return falseTriState;
darin's avatar
darin committed
915
        updateState(mutableStyle.get(), selectionStyle.get(), atStart, state);
darin's avatar
darin committed
916
        if (nodeToRemove) {
darin's avatar
darin committed
917 918
            ExceptionCode ec = 0;
            nodeToRemove->remove(ec);
ggaren's avatar
ggaren committed
919
            ASSERT(ec == 0);
darin's avatar
darin committed
920 921
        }
    } else {
justing's avatar
justing committed
922
        for (Node* node = selectionController()->start().node(); node; node = node->traverseNextNode()) {
darin's avatar
darin committed
923
            RefPtr<CSSComputedStyleDeclaration> computedStyle = new CSSComputedStyleDeclaration(node);
darin's avatar
darin committed
924 925
            if (computedStyle)
                updateState(mutableStyle.get(), computedStyle.get(), atStart, state);
darin's avatar
darin committed
926 927
            if (state == mixedTriState)
                break;
justing's avatar
justing committed
928
            if (node == selectionController()->end().node())