#include "config.h"
#include "Frame.h"
#include "FramePrivate.h"
#include "ApplyStyleCommand.h"
#include "BeforeUnloadEvent.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
#include "CachedCSSStyleSheet.h"
#include "DOMWindow.h"
#include "DocLoader.h"
#include "DocumentType.h"
#include "EditingText.h"
#include "EditorClient.h"
#include "EventNames.h"
#include "FocusController.h"
#include "FrameLoader.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "HTMLDocument.h"
#include "HTMLFormElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLGenericFormElement.h"
#include "HTMLNames.h"
#include "HTMLTableCellElement.h"
#include "Cache.h"
#include "HTMLTokenizer.h"
#include "SystemMemory.h"
#if ENABLE(HW_COMP)
#include "LCLayer.h"
#endif
#include "Logging.h"
#include "MediaFeatureNames.h"
#include "NodeList.h"
#include "Page.h"
#include "RegularExpression.h"
#include "RenderPart.h"
#include "RenderTableCell.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "Settings.h"
#include "SystemTime.h"
#include "TextIterator.h"
#include "TextResourceDecoder.h"
#include "XMLNames.h"
#include "bindings/NP_jsobject.h"
#include "bindings/npruntime_impl.h"
#include "bindings/runtime_root.h"
#include "kjs_proxy.h"
#include "kjs_window.h"
#include "visible_units.h"
#if FRAME_LOADS_USER_STYLESHEET
#include "UserStyleSheetLoader.h"
#endif
#if ENABLE(SVG)
#include "SVGDocument.h"
#include "SVGDocumentExtensions.h"
#include "SVGNames.h"
#include "XLinkNames.h"
#endif
using namespace std;
using KJS::JSLock;
namespace WebCore {
using namespace EventNames;
using namespace HTMLNames;
const int SCROLL_FREQUENCY = 1000/60;
double Frame::s_currentPaintTimeStamp = 0.0;
#ifndef NDEBUG
WTFLogChannel LogWebCoreFrameLeaks = { 0x00000000, "", WTFLogChannelOn };
struct FrameCounter {
static int count;
~FrameCounter()
{
if (count)
LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count);
}
};
int FrameCounter::count = 0;
static FrameCounter frameCounter;
#endif
static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
{
if (!ownerElement)
return 0;
return ownerElement->document()->frame();
}
Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
: d(new FramePrivate(page, parentFromOwnerElement(ownerElement), this, ownerElement, frameLoaderClient))
{
AtomicString::init();
EventNames::init();
HTMLNames::init();
QualifiedName::init();
MediaFeatureNames::init();
#if ENABLE(SVG)
SVGNames::init();
XLinkNames::init();
#endif
XMLNames::init();
if (!ownerElement)
page->setMainFrame(this);
else {
ref();
page->incrementFrameCount();
ownerElement->m_contentFrame = this;
}
#ifndef NDEBUG
++FrameCounter::count;
#endif
}
Frame::~Frame()
{
setView(0);
loader()->clearRecordedFormValues();
#if PLATFORM(MAC)
setBridge(0);
#endif
loader()->cancelAndClear();
ASSERT(!d->m_lifeSupportTimer.isActive());
#ifndef NDEBUG
--FrameCounter::count;
#endif
if (d->m_jscript && d->m_jscript->haveGlobalObject())
static_cast<KJS::Window*>(d->m_jscript->globalObject())->disconnectFrame();
disconnectOwnerElement();
if (d->m_domWindow)
d->m_domWindow->disconnectFrame();
if (d->m_view) {
d->m_view->hide();
d->m_view->clearFrame();
}
ASSERT(!d->m_lifeSupportTimer.isActive());
#if FRAME_LOADS_USER_STYLESHEET
delete d->m_userStyleSheetLoader;
#endif
delete d;
d = 0;
}
void Frame::init()
{
d->m_loader->init();
}
FrameLoader* Frame::loader() const
{
return d->m_loader;
}
FrameView* Frame::view() const
{
return d->m_view.get();
}
void Frame::setView(FrameView* view)
{
if (!view && d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
d->m_doc->detach();
if (d->m_view) {
d->m_view->unscheduleRelayout();
#if ENABLE(HW_COMP)
d->m_view->unscheduleLayerUpdate();
#endif // ENABLE(HW_COMP)
}
}
eventHandler()->clear();
d->m_view = view;
loader()->resetMultipleFormSubmissionProtection();
}
KJSProxy *Frame::scriptProxy()
{
if (!d->m_jscript)
d->m_jscript = new KJSProxy(this);
return d->m_jscript;
}
Document *Frame::document() const
{
if (d)
return d->m_doc.get();
return 0;
}
void Frame::setDocument(PassRefPtr<Document> newDoc)
{
if (d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
d->m_doc->detach();
}
d->m_doc = newDoc;
if (d->m_doc && selectionController()->isFocusedAndActive())
setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
if (d->m_doc && !d->m_doc->attached())
d->m_doc->attach();
if (d->m_jscript)
d->m_jscript->clearDocumentWrapper();
#if ENABLE(SVG)
if (d->m_doc && !d->m_userStyleSheet.isNull() && !d->m_needsReapplyStyles && view())
#else
if (d->m_doc && !d->m_userStyleSheet.isNull())
#endif
setNeedsReapplyStyles();
}
Settings* Frame::settings() const
{
return d->m_page ? d->m_page->settings() : 0;
}
String Frame::selectedText() const
{
return plainText(selectionController()->toRange().get());
}
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()) {
return IntRect(min(startCaretRect.x(), endCaretRect.x()),
startCaretRect.y(),
abs(endCaretRect.x() - startCaretRect.x()),
max(startCaretRect.height(), endCaretRect.height()));
}
return IntRect(startCaretRect.x(),
startCaretRect.y(),
startCaretRect.width() + extraWidthToEndOfLine,
startCaretRect.height());
}
FloatQuad Frame::firstQuadForRange(Range* range) const
{
int extraWidthToEndOfLine = 0;
ExceptionCode ec = 0;
ASSERT(range->startContainer(ec));
ASSERT(range->endContainer(ec));
FloatQuad startCaretQuad = range->startContainer(ec)->renderer()->absoluteCaretQuad(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine);
ASSERT(!ec);
FloatQuad endCaretQuad = range->endContainer(ec)->renderer()->absoluteCaretQuad(range->endOffset(ec), UPSTREAM);
ASSERT(!ec);
FloatRect startCaretRect = startCaretQuad.boundingBox();
FloatRect endCaretRect = endCaretQuad.boundingBox();
if (startCaretRect.y() == endCaretRect.y()) {
return FloatQuad(FloatRect(min(startCaretRect.x(), endCaretRect.x()),
startCaretRect.y(),
fabsf(endCaretRect.x() - startCaretRect.x()),
max(startCaretRect.height(), endCaretRect.height())));
}
return FloatQuad(FloatRect(startCaretRect.x(),
startCaretRect.y(),
startCaretRect.width() + extraWidthToEndOfLine,
startCaretRect.height()));
}
SelectionController* Frame::selectionController() const
{
return &d->m_selectionController;
}
Editor* Frame::editor() const
{
return &d->m_editor;
}
TextGranularity Frame::selectionGranularity() const
{
return d->m_selectionGranularity;
}
void Frame::setSelectionGranularity(TextGranularity granularity) const
{
d->m_selectionGranularity = granularity;
}
SelectionController* Frame::dragCaretController() const
{
return d->m_page->dragCaretController();
}
AnimationController* Frame::animationController() const
{
return &d->m_animationController;
}
static RegularExpression *createRegExpForLabels(const Vector<String>& labels)
{
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("|");
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) {
for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
DeprecatedString nodeString = n->nodeValue().deprecatedString();
int pos = regExp->searchRev(nodeString);
if (pos >= 0)
return nodeString.mid(pos, regExp->matchedLength());
}
}
}
}
}
return String();
}
String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
{
OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
const unsigned int charsSearchedThreshold = 500;
const unsigned int maxCharsSearched = 600;
HTMLTableCellElement* startingTableCell = 0;
bool searchedCellAbove = false;
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()))
{
break;
} else if (n->hasTagName(tdTag) && !startingTableCell) {
startingTableCell = static_cast<HTMLTableCellElement*>(n);
} else if (n->hasTagName(trTag) && startingTableCell) {
String result = searchForLabelsAboveCell(regExp.get(), startingTableCell);
if (!result.isEmpty())
return result;
searchedCellAbove = true;
} else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
DeprecatedString nodeString = n->nodeValue().deprecatedString();
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 (startingTableCell && !searchedCellAbove) {
return searchForLabelsAboveCell(regExp.get(), startingTableCell);
}
return String();
}
String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
{
DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
name.replace(RegularExpression("\\d"), " ");
name.replace('_', ' ');
OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
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();
}
const Selection& Frame::mark() const
{
return d->m_mark;
}
void Frame::setMark(const Selection& s)
{
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());
d->m_mark = s;
}
void Frame::notifyRendererOfSelectionChange(bool userTriggered)
{
RenderObject* renderer = 0;
if (selectionController()->rootEditableElement())
renderer = selectionController()->rootEditableElement()->shadowAncestorNode()->renderer();
if (renderer && (renderer->isTextArea() || renderer->isTextField()))
static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
}
void Frame::invalidateSelection()
{
selectionController()->setNeedsLayout();
selectionLayoutChanged();
}
void Frame::setCaretVisible(bool flag)
{
if (d->m_caretVisible == flag)
return;
clearCaretRectIfNeeded();
d->m_caretVisible = flag;
selectionLayoutChanged();
}
void Frame::clearCaretRectIfNeeded()
{
if (d->m_caretPaint) {
d->m_caretPaint = false;
selectionController()->invalidateCaretRect();
}
}
static bool isFrameElement(const Node *n)
{
if (!n)
return false;
RenderObject *renderer = n->renderer();
if (!renderer || !renderer->isWidget())
return false;
Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
return widget && widget->isFrameView();
}
void Frame::setFocusedNodeIfNeeded()
{
if (!document() || selectionController()->isNone() || !selectionController()->isFocusedAndActive())
return;
Node* target = selectionController()->rootEditableElement();
if (target) {
RenderObject* renderer = target->renderer();
while (renderer) {
if (target && target->isMouseFocusable() && !isFrameElement(target)) {
page()->focusController()->setFocusedNode(target, this);
return;
}
renderer = renderer->parent();
if (renderer)
target = renderer->element();
}
document()->setFocusedNode(0);
}
}
void Frame::selectionLayoutChanged()
{
if (!renderer())
return;
RenderView* canvas = static_cast<RenderView*>(renderer());
Selection selection = selectionController()->selection();
if (!selection.isRange())
canvas->clearSelection();
else {
Position startPos = selection.start();
if (startPos.downstream().isCandidate())
startPos = startPos.downstream();
Position endPos = selection.end();
if (endPos.upstream().isCandidate())
endPos = endPos.upstream();
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());
}
}
notifySelectionLayoutChanged();
}
void Frame::caretBlinkTimerFired(Timer<Frame>*)
{
}
void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const
{
}
void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const
{
SelectionController* dragCaretController = d->m_page->dragCaretController();
ASSERT(dragCaretController->selection().isCaret());
if (dragCaretController->selection().start().node()->document()->frame() == this)
dragCaretController->paintCaret(p, rect);
}
void Frame::setCaretColor(const Color &color)
{
if (d->m_caretColor != color) {
d->m_caretColor = color;
if (d->m_caretVisible &&
d->m_caretBlinks &&
(page() && page()->selection().isCaret())) {
selectionController()->invalidateCaretRect();
}
}
}
int Frame::zoomFactor() const
{
return d->m_zoomFactor;
}
void Frame::clearSelection()
{
selectionController()->setSelection(Selection());
}
void Frame::setCaretBlinks(bool flag)
{
if (d->m_caretBlinks == flag)
return;
clearCaretRectIfNeeded();
if (flag)
setFocusedNodeIfNeeded();
d->m_caretBlinks = flag;
selectionLayoutChanged();
}
void Frame::scrollOverflowLayer(RenderLayer *layer, const IntRect &visibleRect, const IntRect &exposeRect) {
if (!layer && !layer->renderer())
return;
if (!visibleRect.intersects(exposeRect)) {
int x = layer->scrollXOffset();
int eleft = exposeRect.x();
int eright = eleft + exposeRect.width();
int clientWidth = layer->renderer()->clientWidth();
int scrollWidth = layer->renderer()->scrollWidth();
if (eleft <= 0)
x = max(0, x + eleft - (clientWidth / 2));
else if (eright >= clientWidth)
x = min(scrollWidth - clientWidth, x + (clientWidth / 2));
int y = layer->scrollYOffset();
int etop = exposeRect.y();
int ebottom = etop + exposeRect.height();
int clientHeight = layer->renderer()->clientHeight();
int scrollHeight = layer->renderer()->scrollHeight();
if (etop <= 0)
y = max(0, y + etop - (clientHeight / 2));
else if (ebottom >= clientHeight)
y = min(scrollHeight - clientHeight, y + (clientHeight / 2));
layer->scrollToOffset(x, y);
invalidateSelection();
}
}
void Frame::setSingleLineSelectionBehavior(bool b)
{
d->m_singleLineSelectionBehavior = b;
}
bool Frame::singleLineSelectionBehavior() const
{
return d->m_singleLineSelectionBehavior;
}
void Frame::setZoomFactor(int percent)
{
if (d->m_zoomFactor == percent)
return;
#if ENABLE(SVG)
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;
}
#endif
d->m_zoomFactor = percent;
if (d->m_doc)
d->m_doc->recalcStyle(Node::Force);
for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
child->setZoomFactor(d->m_zoomFactor);
if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout())
view()->layout();
}
void Frame::overflowAutoScrollTimerFired(Timer<Frame>*)
{
if (!eventHandler()->mousePressed() || checkOverflowScroll(PerformOverflowScroll) == OverflowScrollNone)
stopOverflowAutoScroll();
}
void Frame::startOverflowAutoScroll(const IntPoint &mousePosition)
{
d->m_overflowAutoScrollPos = mousePosition;
if (d->m_overflowAutoScrollTimer.isActive())
return;
if (checkOverflowScroll(DoNotPerformOverflowScroll) != OverflowScrollNone) {
d->m_overflowAutoScrollTimer.startRepeating(SCROLL_FREQUENCY);
d->m_overflowAutoScrollDelta = 3;
}
}
void Frame::stopOverflowAutoScroll()
{
if (d->m_overflowAutoScrollTimer.isActive()) {
d->m_overflowAutoScrollTimer.stop();
}
}
int Frame::checkOverflowScroll(OverflowScrollAction action)
{
Position extent = selectionController()->selection().extent();
if (extent.isNull())
return OverflowScrollNone;
RenderObject *renderer = extent.node()->renderer();
if (!renderer)
return OverflowScrollNone;
FrameView *v = view();
if (!v)
return OverflowScrollNone;
RenderBlock *cb = renderer->containingBlock();
if (!cb || !cb->hasOverflowClip())
return OverflowScrollNone;
RenderLayer *layer = cb->layer();
assert(layer);
IntRect visibleRect = IntRect(v->contentsX(), v->contentsY(), v->visibleWidth(), v->visibleHeight());
IntPoint pos = d->m_overflowAutoScrollPos;
if (visibleRect.contains(pos.x(), pos.y()))
return OverflowScrollNone;
int scrollType = 0;
int deltaX = 0;
int deltaY = 0;
IntPoint selectionPos;
static const int scrollBoundsAdjustment = 3;
if (pos.x() < visibleRect.x()) {
scrollType |= OverflowScrollLeft;
if (action == PerformOverflowScroll) {
deltaX -= (int)d->m_overflowAutoScrollDelta;
selectionPos.setX(v->contentsX() - scrollBoundsAdjustment);
}
}
else if (pos.x() > visibleRect.right()) {
scrollType |= OverflowScrollRight;
if (action == PerformOverflowScroll) {
deltaX += (int)d->m_overflowAutoScrollDelta;
selectionPos.setX(v->contentsX() + v->visibleWidth() + scrollBoundsAdjustment);
}
}
if (pos.y() < visibleRect.y()) {
scrollType |= OverflowScrollUp;
if (action == PerformOverflowScroll) {
deltaY -= (int)d->m_overflowAutoScrollDelta;
selectionPos.setY(v->contentsY() - scrollBoundsAdjustment);
}
}
else if (pos.y() > visibleRect.bottom()) {
scrollType |= OverflowScrollDown;
if (action == PerformOverflowScroll) {
deltaY += (int)d->m_overflowAutoScrollDelta;
selectionPos.setY(v->contentsY() + v->visibleHeight() + scrollBoundsAdjustment);
}
}
if (action == PerformOverflowScroll && (deltaX || deltaY)) {
layer->scrollToOffset(layer->scrollXOffset() + deltaX, layer->scrollYOffset() + deltaY);
VisiblePosition visiblePos(renderer->positionForCoordinates(selectionPos.x(), selectionPos.y()));
if (visiblePos.isNotNull()) {
Selection sel = selectionController()->selection();
sel.setExtent(visiblePos);
if (d->m_selectionGranularity != CharacterGranularity) {
sel.expandUsingGranularity(d->m_selectionGranularity);
}
if (shouldChangeSelection(sel)) {
selectionController()->setSelection(sel);
}
}
d->m_overflowAutoScrollDelta *= 1.02; }
return scrollType;
}
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);
}
void Frame::setJSStatusBarText(const String& text)
{
d->m_kjsStatusBarText = text;
if (d->m_page)
d->m_page->chrome()->setStatusbarText(this, d->m_kjsStatusBarText);
}
void Frame::setJSDefaultStatusBarText(const String& text)
{
d->m_kjsDefaultStatusBarText = text;
if (d->m_page)
d->m_page->chrome()->setStatusbarText(this, d->m_kjsDefaultStatusBarText);
}
String Frame::jsStatusBarText() const
{
return d->m_kjsStatusBarText;
}
String Frame::jsDefaultStatusBarText() const
{
return d->m_kjsDefaultStatusBarText;
}
void Frame::setNeedsReapplyStyles()
{
if (d->m_needsReapplyStyles)
return;
d->m_needsReapplyStyles = true;
if (view())
view()->invalidate();
}
bool Frame::needsReapplyStyles() const
{
return d->m_needsReapplyStyles;
}
void Frame::reapplyStyles()
{
d->m_needsReapplyStyles = false;
if (d->m_doc)
d->m_doc->docLoader()->setAutoLoadImages(d->m_page && d->m_page->settings()->loadsImagesAutomatically());
#if FRAME_LOADS_USER_STYLESHEET
setUserStyleSheet(d->m_userStyleSheet, false);
#endif
if (d->m_doc)
d->m_doc->updateStyleSelector();
}
bool Frame::shouldChangeSelection(const Selection& newSelection) const
{
if (embeddedEditingMode())
return true;
return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false);
}
bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
{
if (embeddedEditingMode())
return true;
return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
affinity, stillSelecting);
}
bool Frame::shouldDeleteSelection(const Selection& selection) const
{
if (embeddedEditingMode())
return true;
return editor()->client()->shouldDeleteRange(selection.toRange().get());
}
bool Frame::isContentEditable() const
{
if (embeddedEditingMode())
return true;
if (d->m_editor.clientIsEditable())
return true;
if (!d->m_doc)
return false;
return d->m_doc->inDesignMode();
}
#if !PLATFORM(MAC)
void Frame::setUseSecureKeyboardEntry(bool)
{
}
#endif
void Frame::updateSecureKeyboardEntryIfActive()
{
if (selectionController()->isFocusedAndActive())
setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
}
CSSMutableStyleDeclaration *Frame::typingStyle() const
{
return d->m_typingStyle.get();
}
void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
{
d->m_typingStyle = style;
}
void Frame::clearTypingStyle()
{
d->m_typingStyle = 0;
}
void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
{
if (!style || style->length() == 0) {
clearTypingStyle();
return;
}
RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
if (typingStyle()) {
typingStyle()->merge(mutableStyle.get());
mutableStyle = typingStyle();
}
Node *node = selectionController()->selection().visibleStart().deepEquivalent().node();
CSSComputedStyleDeclaration computedStyle(node);
computedStyle.diff(mutableStyle.get());
RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
blockStyle->diff(mutableStyle.get());
if (document() && blockStyle->length() > 0)
applyCommand(new ApplyStyleCommand(document(), blockStyle.get(), editingAction));
d->m_typingStyle = mutableStyle.release();
}
String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
{
Node *nodeToRemove;
RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
if (!selectionStyle)
return String();
String value = selectionStyle->getPropertyValue(stylePropertyID);
if (nodeToRemove) {
ExceptionCode ec = 0;
nodeToRemove->remove(ec);
ASSERT(ec == 0);
}
return value;
}
CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) const
{
nodeToRemove = 0;
if (!document())
return 0;
if (selectionController()->isNone())
return 0;
RefPtr<Range> range(selectionController()->toRange());
Position pos = range->editingStartPosition();
Element *elem = pos.element();
if (!elem)
return 0;
RefPtr<Element> styleElement = elem;
ExceptionCode ec = 0;
if (d->m_typingStyle) {
styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
ASSERT(ec == 0);
styleElement->setAttribute(styleAttr, d->m_typingStyle->cssText().impl(), ec);
ASSERT(ec == 0);
styleElement->appendChild(document()->createEditingTextNode(""), ec);
ASSERT(ec == 0);
if (elem->renderer() && elem->renderer()->canHaveChildren()) {
elem->appendChild(styleElement, ec);
} else {
Node *parent = elem->parent();
Node *next = elem->nextSibling();
if (next) {
parent->insertBefore(styleElement, next, ec);
} else {
parent->appendChild(styleElement, ec);
}
}
ASSERT(ec == 0);
nodeToRemove = styleElement.get();
}
return new CSSComputedStyleDeclaration(styleElement);
}
void Frame::formElementDidSetValue(Element* anElement)
{
if (editor()->client())
editor()->client()->formElementDidSetValue(anElement);
}
void Frame::formElementDidFocus(Element* anElement)
{
if (editor()->client())
editor()->client()->formElementDidFocus(anElement);
}
void Frame::formElementDidBlur(Element* anElement)
{
if (editor()->client())
editor()->client()->formElementDidBlur(anElement);
}
void Frame::textFieldDidBeginEditing(Element* e)
{
if (editor()->client())
editor()->client()->textFieldDidBeginEditing(e);
}
void Frame::textFieldDidEndEditing(Element* e)
{
if (editor()->client())
editor()->client()->textFieldDidEndEditing(e);
}
void Frame::textDidChangeInTextField(Element* e)
{
if (editor()->client())
editor()->client()->textDidChangeInTextField(e);
}
bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
{
if (editor()->client())
return editor()->client()->doTextFieldCommandFromEvent(e, ke);
return false;
}
void Frame::textWillBeDeletedInTextField(Element* input)
{
if (editor()->client())
editor()->client()->textWillBeDeletedInTextField(input);
}
void Frame::textDidChangeInTextArea(Element* e)
{
if (editor()->client())
editor()->client()->textDidChangeInTextArea(e);
}
void Frame::applyEditingStyleToBodyElement() const
{
if (!d->m_doc)
return;
RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
unsigned len = list->length();
for (unsigned i = 0; i < len; i++) {
applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
}
}
void Frame::removeEditingStyleFromBodyElement() const
{
if (!d->m_doc)
return;
RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
unsigned len = list->length();
for (unsigned i = 0; i < len; i++) {
removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
}
}
void Frame::applyEditingStyleToElement(Element* element) const
{
if (!element)
return;
CSSStyleDeclaration* style = element->style();
ASSERT(style);
ExceptionCode ec = 0;
style->setProperty(CSS_PROP_WORD_WRAP, "break-word", false, ec);
ASSERT(ec == 0);
style->setProperty(CSS_PROP__WEBKIT_NBSP_MODE, "space", false, ec);
ASSERT(ec == 0);
style->setProperty(CSS_PROP__WEBKIT_LINE_BREAK, "after-white-space", false, ec);
ASSERT(ec == 0);
}
void Frame::removeEditingStyleFromElement(Element*) const
{
}
#ifndef NDEBUG
static HashSet<Frame*>& keepAliveSet()
{
static HashSet<Frame*> staticKeepAliveSet;
return staticKeepAliveSet;
}
#endif
void Frame::keepAlive()
{
if (d->m_lifeSupportTimer.isActive())
return;
#ifndef NDEBUG
keepAliveSet().add(this);
#endif
ref();
d->m_lifeSupportTimer.startOneShot(0);
}
#ifndef NDEBUG
void Frame::cancelAllKeepAlive()
{
HashSet<Frame*>::iterator end = keepAliveSet().end();
for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
Frame* frame = *it;
frame->d->m_lifeSupportTimer.stop();
frame->deref();
}
keepAliveSet().clear();
}
#endif
void Frame::lifeSupportTimerFired(Timer<Frame>*)
{
#ifndef NDEBUG
keepAliveSet().remove(this);
#endif
deref();
}
KJS::Bindings::RootObject* Frame::bindingRootObject()
{
if (!scriptProxy()->isEnabled())
return 0;
if (!d->m_bindingRootObject) {
JSLock lock;
d->m_bindingRootObject = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject());
}
return d->m_bindingRootObject.get();
}
PassRefPtr<KJS::Bindings::RootObject> Frame::createRootObject(void* nativeHandle, KJS::JSGlobalObject* globalObject)
{
RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
if (it != d->m_rootObjects.end())
return it->second;
RefPtr<KJS::Bindings::RootObject> rootObject = KJS::Bindings::RootObject::create(nativeHandle, globalObject);
d->m_rootObjects.set(nativeHandle, rootObject);
return rootObject.release();
}
#if USE(NPOBJECT)
NPObject* Frame::windowScriptNPObject()
{
if (!d->m_windowScriptNPObject) {
if (scriptProxy()->isEnabled()) {
KJS::JSLock lock;
KJS::JSObject* win = KJS::Window::retrieveWindow(this);
ASSERT(win);
KJS::Bindings::RootObject* root = bindingRootObject();
d->m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root);
} else {
d->m_windowScriptNPObject = _NPN_CreateNoScriptObject();
}
}
return d->m_windowScriptNPObject;
}
#endif
void Frame::clearScriptProxy()
{
if (d->m_jscript)
d->m_jscript->clear();
}
void Frame::clearDOMWindow()
{
if (d->m_domWindow)
d->m_domWindow->clear();
}
void Frame::cleanupScriptObjectsForPlugin(void* nativeHandle)
{
RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
if (it == d->m_rootObjects.end())
return;
it->second->invalidate();
d->m_rootObjects.remove(it);
}
void Frame::clearScriptObjects()
{
JSLock lock;
RootObjectMap::const_iterator end = d->m_rootObjects.end();
for (RootObjectMap::const_iterator it = d->m_rootObjects.begin(); it != end; ++it)
it->second->invalidate();
d->m_rootObjects.clear();
if (d->m_bindingRootObject) {
d->m_bindingRootObject->invalidate();
d->m_bindingRootObject = 0;
}
#if USE(NPOBJECT)
if (d->m_windowScriptNPObject) {
_NPN_DeallocateObject(d->m_windowScriptNPObject);
d->m_windowScriptNPObject = 0;
}
#endif
clearPlatformScriptObjects();
}
RenderObject *Frame::renderer() const
{
Document *doc = document();
return doc ? doc->renderer() : 0;
}
HTMLFrameOwnerElement* Frame::ownerElement() const
{
return d->m_ownerElement;
}
RenderPart* Frame::ownerRenderer()
{
HTMLFrameOwnerElement* ownerElement = d->m_ownerElement;
if (!ownerElement)
return 0;
return static_cast<RenderPart*>(ownerElement->renderer());
}
FloatRect Frame::selectionRect(bool clipToVisibleContent) const
{
RenderView *root = static_cast<RenderView*>(renderer());
if (!root)
return IntRect();
IntRect selectionRect = root->selectionRect(clipToVisibleContent);
return clipToVisibleContent ? intersection(selectionRect, d->m_view->visibleContentRect()) : selectionRect;
}
void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
{
RenderView *root = static_cast<RenderView*>(renderer());
if (!root)
return;
RefPtr<Range> selectedRange = selectionController()->toRange();
Vector<IntRect> intRects;
selectedRange->addLineBoxRects(intRects, true);
unsigned size = intRects.size();
FloatRect visibleContentRect = d->m_view->visibleContentRect();
for (unsigned i = 0; i < size; ++i)
if (clipToVisibleContent)
rects.append(intersection(intRects[i], visibleContentRect));
else
rects.append(intRects[i]);
}
bool Frame::isFrameSet() const
{
Document* document = d->m_doc.get();
if (!document || !document->isHTMLDocument())
return false;
Node *body = static_cast<HTMLDocument*>(document)->body();
return body && body->renderer() && body->hasTagName(framesetTag);
}
static HTMLFormElement *scanForForm(Node *start)
{
Node *n;
for (n = start; n; n = n->traverseNextNode()) {
if (n->hasTagName(formTag))
return static_cast<HTMLFormElement*>(n);
else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement())
return static_cast<HTMLGenericFormElement*>(n)->form();
else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
if (HTMLFormElement *frameResult = scanForForm(childDoc))
return frameResult;
}
}
return 0;
}
HTMLFormElement *Frame::currentForm() const
{
Node *start = d->m_doc ? d->m_doc->focusedNode() : 0;
if (!start)
start = selectionController()->start().node();
Node *n;
for (n = start; n; n = n->parentNode()) {
if (n->hasTagName(formTag))
return static_cast<HTMLFormElement*>(n);
else if (n->isHTMLElement()
&& static_cast<HTMLElement*>(n)->isGenericFormElement())
return static_cast<HTMLGenericFormElement*>(n)->form();
}
return start ? scanForForm(start) : 0;
}
void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
{
IntRect rect;
switch (selectionController()->state()) {
case Selection::NONE:
return;
case Selection::CARET:
rect = selectionController()->caretRect();
break;
case Selection::RANGE:
rect = enclosingIntRect(selectionRect(false));
break;
}
Position start = selectionController()->start();
ASSERT(start.node());
if (start.node() && start.node()->renderer()) {
if (RenderLayer *layer = start.node()->renderer()->enclosingLayer()) {
layer->setAdjustForPurpleCaretWhenScrolling(true);
layer->scrollRectToVisible(rect, alignment, alignment);
layer->setAdjustForPurpleCaretWhenScrolling(false);
notifySelectionLayoutChanged();
}
}
}
void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
{
if (selectionController()->isNone())
return;
Position extent = selectionController()->extent();
if (extent.node() && extent.node()->renderer()) {
IntRect extentRect = VisiblePosition(extent).caretRect();
RenderLayer* layer = extent.node()->renderer()->enclosingLayer();
if (layer)
layer->scrollRectToVisible(extentRect, alignment, alignment);
}
}
void Frame::paint(GraphicsContext* p, const IntRect& rect)
{
#ifndef NDEBUG
bool fillWithRed;
if (!document() || document()->printing())
fillWithRed = false; else if (document()->ownerElement())
fillWithRed = false; else if (view() && view()->isTransparent())
fillWithRed = false; else if (d->m_paintRestriction == PaintRestrictionSelectionOnly || d->m_paintRestriction == PaintRestrictionSelectionOnlyBlackText)
fillWithRed = false; else if (d->m_elementToDraw)
fillWithRed = false; else
fillWithRed = true;
if (fillWithRed)
p->fillRect(rect, Color(0xFF, 0, 0));
#endif
bool isTopLevelPainter = !s_currentPaintTimeStamp;
if (isTopLevelPainter)
s_currentPaintTimeStamp = currentTime();
if (renderer()) {
if (!(d->m_view && !d->m_view->needsLayout())) {
p->fillRect(rect, Color::white);
return;
}
ASSERT(d->m_view && !d->m_view->needsLayout());
ASSERT(!d->m_isPainting);
d->m_isPainting = true;
RenderObject *eltRenderer = d->m_elementToDraw ? d->m_elementToDraw->renderer() : 0;
if (d->m_paintRestriction == PaintRestrictionNone)
renderer()->document()->invalidateRenderedRectsForMarkersInRect(rect);
renderer()->layer()->paint(p, rect, d->m_paintRestriction, eltRenderer);
d->m_isPainting = false;
d->m_view->didPaint();
const unsigned memoryReserve = 2 * 1024 * 1024;
bool safeMemory = hasEnoughMemoryFor(memoryReserve);
if (!safeMemory && isTopLevelPainter)
cache()->pruneLiveResources(true);
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer()->document()->dashboardRegionsDirty())
renderer()->view()->frameView()->updateDashboardRegions();
#endif
#if ENABLE(TOUCH_EVENTS)
if (document()->touchEventListeners().size() > 0)
document()->setTouchEventListenersDirty(true);
#endif
} else
LOG_ERROR("called Frame::paint with nil renderer");
if (isTopLevelPainter)
s_currentPaintTimeStamp = 0;
}
void Frame::setPaintRestriction(PaintRestriction pr)
{
d->m_paintRestriction = pr;
}
bool Frame::isPainting() const
{
return d->m_isPainting;
}
void Frame::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
{
RenderView *root = static_cast<RenderView*>(document()->renderer());
if (root) {
GraphicsContext context((PlatformGraphicsContext*)0);
root->setTruncatedAt((int)floorf(oldBottom));
IntRect dirtyRect(0, (int)floorf(oldTop), root->docWidth(), (int)ceilf(oldBottom - oldTop));
root->layer()->paint(&context, dirtyRect);
*newBottom = root->bestTruncatedAt();
if (*newBottom == 0)
*newBottom = oldBottom;
} else
*newBottom = oldBottom;
}
Frame* Frame::frameForWidget(const Widget* widget)
{
ASSERT_ARG(widget, widget);
if (RenderWidget* renderer = RenderWidget::find(widget))
if (Node* node = renderer->node())
return node->document()->frame();
ASSERT(widget->isFrameView());
return static_cast<const FrameView*>(widget)->frame();
}
void Frame::forceLayout(bool allowSubtree)
{
FrameView *v = d->m_view.get();
if (v) {
v->layout(allowSubtree);
#if ENABLE(IPHONE_PPT)
if (v->frame() && v->frame()->page() && v->frame()->page()->mainFrame())
v->frame()->page()->mainFrame()->didForcedLayout();
#endif
}
}
void Frame::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth, bool adjustViewSize)
{
RenderView *root = static_cast<RenderView*>(document()->renderer());
if (root) {
int pageW = (int)ceilf(minPageWidth);
root->setWidth(pageW);
root->setNeedsLayoutAndPrefWidthsRecalc();
forceLayout();
int rightmostPos = root->rightmostPosition();
if (rightmostPos > minPageWidth) {
pageW = min(rightmostPos, (int)ceilf(maxPageWidth));
root->setWidth(pageW);
root->setNeedsLayoutAndPrefWidthsRecalc();
forceLayout();
}
}
if (adjustViewSize && view())
view()->adjustViewSize();
}
void Frame::sendResizeEvent()
{
if (Document* doc = document())
doc->dispatchWindowEvent(EventNames::resizeEvent, false, false);
}
void Frame::sendOrientationChangeEvent()
{
if (Document* doc = document())
doc->dispatchWindowEvent(EventNames::orientationchangeEvent, false, false);
}
void Frame::sendScrollEvent()
{
FrameView* v = d->m_view.get();
if (!v)
return;
v->setWasScrolledByUser(true);
Document* doc = document();
if (!doc)
return;
doc->dispatchHTMLEvent(scrollEvent, true, false);
}
void Frame::clearTimers(FrameView *view, Document *document)
{
if (view) {
view->unscheduleRelayout();
#if ENABLE(HW_COMP)
view->unscheduleLayerUpdate();
#endif // ENABLE(HW_COMP)
if (view->frame()) {
if (document && document->renderer() && document->renderer()->hasLayer())
document->renderer()->layer()->suspendMarquees();
view->frame()->animationController()->suspendAnimations(document);
}
}
}
void Frame::clearTimers()
{
clearTimers(d->m_view.get(), document());
}
RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
{
nodeToRemove = 0;
if (!document())
return 0;
if (selectionController()->isNone())
return 0;
Position pos = selectionController()->selection().visibleStart().deepEquivalent();
if (!pos.isCandidate())
return 0;
Node *node = pos.node();
if (!node)
return 0;
if (!d->m_typingStyle)
return node->renderer()->style();
ExceptionCode ec = 0;
RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
ASSERT(ec == 0);
String styleText = d->m_typingStyle->cssText() + " display: inline";
styleElement->setAttribute(styleAttr, styleText.impl(), ec);
ASSERT(ec == 0);
styleElement->appendChild(document()->createEditingTextNode(""), ec);
ASSERT(ec == 0);
node->parentNode()->appendChild(styleElement, ec);
ASSERT(ec == 0);
nodeToRemove = styleElement.get();
return styleElement->renderer() ? styleElement->renderer()->style() : 0;
}
void Frame::setSelectionFromNone()
{
Document *doc = document();
if (!doc || !(selectionController()->isNone() || isStartOfDocument(VisiblePosition(selectionController()->selection().start(), selectionController()->selection().affinity())))
|| !isContentEditable())
return;
Node* node = doc->documentElement();
while (node && !node->hasTagName(bodyTag))
node = node->traverseNextNode();
if (node)
selectionController()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
}
bool Frame::inViewSourceMode() const
{
return d->m_inViewSourceMode;
}
void Frame::setInViewSourceMode(bool mode) const
{
d->m_inViewSourceMode = mode;
}
UChar Frame::backslashAsCurrencySymbol() const
{
Document *doc = document();
if (!doc)
return '\\';
TextResourceDecoder *decoder = doc->decoder();
if (!decoder)
return '\\';
return decoder->encoding().backslashAsCurrencySymbol();
}
static bool isInShadowTree(Node* node)
{
for (Node* n = node; n; n = n->parentNode())
if (n->isShadowNode())
return true;
return false;
}
bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
{
if (target.isEmpty() || !document())
return false;
RefPtr<Range> searchRange(rangeOfContents(document()));
Selection selection(selectionController()->selection());
Node* selectionBaseNode = selection.base().node();
bool selectionIsInMainContent = selectionBaseNode && !isInShadowTree(selectionBaseNode);
if (selectionIsInMainContent) {
if (forward)
setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
else
setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
}
RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
if (startInSelection && selectionIsInMainContent && *Selection(resultRange.get()).toRange() == *selection.toRange()) {
searchRange = rangeOfContents(document());
if (forward)
setStart(searchRange.get(), selection.visibleEnd());
else
setEnd(searchRange.get(), selection.visibleStart());
resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
}
int exception = 0;
if (resultRange->collapsed(exception) && wrapFlag) {
searchRange = rangeOfContents(document());
resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
}
if (resultRange->collapsed(exception))
return false;
selectionController()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
revealSelection();
return true;
}
unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
{
if (target.isEmpty() || !document())
return 0;
RefPtr<Range> searchRange(rangeOfContents(document()));
int exception = 0;
unsigned matchCount = 0;
do {
RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
if (resultRange->collapsed(exception))
break;
VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
break;
++matchCount;
document()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
if (limit > 0 && matchCount >= limit)
break;
setStart(searchRange.get(), newStart);
} while (true);
Document* doc = document();
if (doc && d->m_view && renderer()) {
doc->updateLayout(); IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
GraphicsContext context((PlatformGraphicsContext*)0);
context.setPaintingDisabled(true);
paint(&context, visibleRect);
}
return matchCount;
}
bool Frame::markedTextMatchesAreHighlighted() const
{
return d->m_highlightTextMatches;
}
void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
{
if (flag == d->m_highlightTextMatches || !document())
return;
d->m_highlightTextMatches = flag;
document()->repaintMarkers(DocumentMarker::TextMatch);
}
FrameTree* Frame::tree() const
{
return &d->m_treeNode;
}
DOMWindow* Frame::domWindow() const
{
if (!d->m_domWindow)
d->m_domWindow = new DOMWindow(const_cast<Frame*>(this));
return d->m_domWindow.get();
}
Page* Frame::page() const
{
return d->m_page;
}
EventHandler* Frame::eventHandler() const
{
return &d->m_eventHandler;
}
void Frame::pageDestroyed()
{
if (Frame* parent = tree()->parent())
parent->loader()->checkLoadComplete();
if (d->m_page && d->m_page->focusController()->focusedFrame() == this)
d->m_page->focusController()->setFocusedFrame(0);
clearObservedContentModifiers();
if (d->m_jscript && d->m_jscript->haveGlobalObject())
if (KJS::Window* w = KJS::Window::retrieveWindow(this))
w->disconnectFrame();
clearScriptObjects();
d->m_page = 0;
}
void Frame::disconnectOwnerElement()
{
if (d->m_ownerElement) {
d->m_ownerElement->m_contentFrame = 0;
if (d->m_page)
d->m_page->decrementFrameCount();
}
d->m_ownerElement = 0;
}
String Frame::documentTypeString() const
{
if (Document *doc = document())
if (DocumentType *doctype = doc->realDocType())
return doctype->toString();
return String();
}
bool Frame::prohibitsScrolling() const
{
return d->m_prohibitsScrolling;
}
void Frame::setProhibitsScrolling(bool prohibit)
{
d->m_prohibitsScrolling = prohibit;
}
void Frame::focusWindow()
{
if (!page())
return;
if (!tree()->parent())
page()->chrome()->focus(d->m_loader->userGestureHint());
eventHandler()->focusDocumentView();
}
void Frame::unfocusWindow()
{
if (!page())
return;
if (!tree()->parent())
page()->chrome()->unfocus();
}
bool Frame::shouldClose()
{
Chrome* chrome = page() ? page()->chrome() : 0;
if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
return true;
RefPtr<Document> doc = document();
if (!doc)
return true;
HTMLElement* body = doc->body();
if (!body)
return true;
RefPtr<BeforeUnloadEvent> beforeUnloadEvent = new BeforeUnloadEvent;
beforeUnloadEvent->setTarget(doc);
doc->handleWindowEvent(beforeUnloadEvent.get(), false);
if (!beforeUnloadEvent->defaultPrevented() && doc)
doc->defaultEventHandler(beforeUnloadEvent.get());
if (beforeUnloadEvent->result().isNull())
return true;
String text = beforeUnloadEvent->result();
text.replace('\\', backslashAsCurrencySymbol());
return chrome->runBeforeUnloadConfirmPanel(text, this);
}
void Frame::scheduleClose()
{
if (!shouldClose())
return;
Chrome* chrome = page() ? page()->chrome() : 0;
if (chrome)
chrome->closeWindowSoon();
}
void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
{
if (document()) {
bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
if (isContinuousSpellCheckingEnabled) {
Selection newAdjacentWords;
Selection newSelectedSentence;
if (selectionController()->selection().isContentEditable()) {
VisiblePosition newStart(selectionController()->selection().visibleStart());
newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
if (isContinuousGrammarCheckingEnabled)
newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
}
if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
VisiblePosition oldStart(oldSelection.visibleStart());
Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
if (oldAdjacentWords != newAdjacentWords) {
editor()->markMisspellings(oldAdjacentWords);
if (isContinuousGrammarCheckingEnabled) {
Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
if (oldSelectedSentence != newSelectedSentence)
editor()->markBadGrammar(oldSelectedSentence);
}
}
}
if (RefPtr<Range> wordRange = newAdjacentWords.toRange())
document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
if (RefPtr<Range> sentenceRange = newSelectedSentence.toRange())
document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
}
if (!isContinuousSpellCheckingEnabled)
document()->removeMarkers(DocumentMarker::Spelling);
if (!isContinuousGrammarCheckingEnabled)
document()->removeMarkers(DocumentMarker::Grammar);
}
editor()->respondToChangedSelection(oldSelection);
}
VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
{
HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true);
Node* node = result.innerNode();
if (!node)
return VisiblePosition();
RenderObject* renderer = node->renderer();
if (!renderer)
return VisiblePosition();
VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
if (visiblePos.isNull())
visiblePos = VisiblePosition(Position(node, 0));
return visiblePos;
}
Document* Frame::documentAtPoint(const IntPoint& point)
{
if (!view())
return 0;
IntPoint pt = view()->windowToContents(point);
HitTestResult result = HitTestResult(pt);
if (renderer())
result = eventHandler()->hitTestResultAtPoint(pt, false);
return result.innerNode() ? result.innerNode()->document() : 0;
}
#if ENABLE(HW_COMP)
bool Frame::has3DContent() const
{
if (renderer()) {
RenderLayer* layer = renderer()->layer();
return (layer && layer->has3DContent());
}
return false;
}
float Frame::frameScale() const
{
return d->m_frameScale;
}
void Frame::setFrameScale(float newScale)
{
for (Frame* frame = this; frame; frame = frame->tree()->traverseNext(this)) {
if (newScale != frame->d->m_frameScale) {
frame->d->m_frameScale = newScale;
if (frame->renderer()) {
RenderLayer* layer = frame->renderer()->layer();
if (layer) {
layer->updatePageZoomFactor(newScale);
}
}
}
}
}
#endif
FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement* ownerElement,
FrameLoaderClient* frameLoaderClient)
: m_page(page)
, m_treeNode(thisFrame, parent)
, m_ownerElement(ownerElement)
, m_jscript(0)
, m_zoomFactor(parent ? parent->d->m_zoomFactor : 100)
, m_selectionGranularity(CharacterGranularity)
, m_selectionController(thisFrame)
, m_caretBlinkTimer(thisFrame, &Frame::caretBlinkTimerFired)
, m_editor(thisFrame)
, m_eventHandler(thisFrame)
, m_animationController(thisFrame)
, m_caretVisible(false)
, m_caretBlinks(true)
, m_caretPaint(true)
, m_isPainting(false)
, m_overflowAutoScrollTimer(thisFrame, &Frame::overflowAutoScrollTimerFired)
, m_layoutInterval(cLayoutScheduleThreshold)
, m_maxParseDuration(tokenizerTimeDelay)
, m_savedPausedTimeouts(0)
, m_viewportArguments()
, m_embeddedEditingMode(false)
#if ENABLE(IPHONE_PPT)
, m_parseCount(0)
, m_layoutCount(0)
, m_forcedLayoutCount(0)
, m_parseDuration(0.0)
, m_layoutDuration(0.0)
#endif
, m_lifeSupportTimer(thisFrame, &Frame::lifeSupportTimerFired)
, m_loader(new FrameLoader(thisFrame, frameLoaderClient))
, m_paintRestriction(PaintRestrictionNone)
, m_highlightTextMatches(false)
, m_inViewSourceMode(false)
, frameCount(0)
, m_prohibitsScrolling(false)
, m_needsReapplyStyles(false)
#if ENABLE(HW_COMP)
, m_frameScale(1.0f)
#endif
#if FRAME_LOADS_USER_STYLESHEET
, m_userStyleSheetLoader(0)
#endif
#if PLATFORM(MAC)
, m_windowScriptObject(nil)
, m_bridge(nil)
#endif
{
}
FramePrivate::~FramePrivate()
{
delete m_jscript;
delete m_loader;
if (m_savedPausedTimeouts)
delete m_savedPausedTimeouts;
}
void Frame::setLayoutInterval(int interval)
{
d->m_layoutInterval = interval < cLayoutScheduleThreshold ? cLayoutScheduleThreshold : interval;
}
int Frame::layoutInterval()
{
return d->m_layoutInterval;
}
void Frame::setMaxParseDuration(double duration)
{
d->m_maxParseDuration = duration;
}
double Frame::maxParseDuration()
{
return d->m_maxParseDuration;
}
void Frame::clearSavedTimeouts()
{
if (d->m_savedPausedTimeouts != 0) {
delete d->m_savedPausedTimeouts;
d->m_savedPausedTimeouts = 0;
}
}
void Frame::pauseTimeoutsAndSave()
{
animationController()->suspendAnimations(document());
if (d->m_savedPausedTimeouts != 0)
return;
KJS::Window* window = KJS::Window::retrieveWindow(this);
JSLock lock;
if (window) {
d->m_savedPausedTimeouts = window->pauseTimeouts();
}
}
void Frame::resumeSavedTimeouts()
{
animationController()->resumeAnimations(document());
KJS::Window* window = KJS::Window::retrieveWindow(this);
if (window)
window->resumeTimeouts(d->m_savedPausedTimeouts);
clearSavedTimeouts();
}
void Frame::invalidateOwnerRendererLayoutIfNeeded()
{
if (settings() && settings()->flatFrameSetLayoutEnabled() && ownerRenderer())
ownerRenderer()->setNeedsLayout(true, true);
}
void Frame::setEmbeddedEditingMode(bool b)
{
d->m_embeddedEditingMode = b;
}
bool Frame::embeddedEditingMode() const
{
return d->m_embeddedEditingMode;
}
}