diff options
author | Feng Qian <> | 2009-04-10 18:11:29 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-10 18:11:29 -0700 |
commit | 8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch) | |
tree | 181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/html | |
parent | 7ed56f225e0ade046e1c2178977f72b2d896f196 (diff) | |
download | external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2 |
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/html')
53 files changed, 1528 insertions, 1584 deletions
diff --git a/WebCore/html/CanvasGradient.cpp b/WebCore/html/CanvasGradient.cpp index 693d8f7..fd48194 100644 --- a/WebCore/html/CanvasGradient.cpp +++ b/WebCore/html/CanvasGradient.cpp @@ -34,11 +34,13 @@ namespace WebCore { CanvasGradient::CanvasGradient(const FloatPoint& p0, const FloatPoint& p1) : m_gradient(Gradient::create(p0, p1)) + , m_dashbardCompatibilityMode(false) { } CanvasGradient::CanvasGradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1) : m_gradient(Gradient::create(p0, r0, p1, r1)) + , m_dashbardCompatibilityMode(false) { } @@ -51,7 +53,8 @@ void CanvasGradient::addColorStop(float value, const String& color, ExceptionCod RGBA32 rgba = 0; if (!CSSParser::parseColor(rgba, color)) { - ec = SYNTAX_ERR; + if (!m_dashbardCompatibilityMode) + ec = SYNTAX_ERR; return; } diff --git a/WebCore/html/CanvasGradient.h b/WebCore/html/CanvasGradient.h index 3b81dbd..0a77652 100644 --- a/WebCore/html/CanvasGradient.h +++ b/WebCore/html/CanvasGradient.h @@ -54,11 +54,16 @@ namespace WebCore { void getColor(float value, float* r, float* g, float* b, float* a) const { m_gradient->getColor(value, r, g, b, a); } +#if ENABLE(DASHBOARD_SUPPORT) + void setDashboardCompatibilityMode() { m_dashbardCompatibilityMode = true; } +#endif + private: CanvasGradient(const FloatPoint& p0, const FloatPoint& p1); CanvasGradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1); RefPtr<Gradient> m_gradient; + bool m_dashbardCompatibilityMode; }; } //namespace diff --git a/WebCore/html/CanvasRenderingContext2D.cpp b/WebCore/html/CanvasRenderingContext2D.cpp index 373301a..82680bd 100644 --- a/WebCore/html/CanvasRenderingContext2D.cpp +++ b/WebCore/html/CanvasRenderingContext2D.cpp @@ -357,7 +357,7 @@ void CanvasRenderingContext2D::scale(float sx, float sy) return; TransformationMatrix newTransform = state().m_transform; - newTransform.scale(sx, sy); + newTransform.scaleNonUniform(sx, sy); if (!newTransform.isInvertible()) { state().m_invertibleCTM = false; return; @@ -365,7 +365,7 @@ void CanvasRenderingContext2D::scale(float sx, float sy) state().m_transform = newTransform; c->scale(FloatSize(sx, sy)); - m_path.transform(TransformationMatrix().scale(1.0/sx, 1.0/sy)); + m_path.transform(TransformationMatrix().scaleNonUniform(1.0/sx, 1.0/sy)); } void CanvasRenderingContext2D::rotate(float angleInRadians) @@ -867,7 +867,6 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur, GraphicsContext* dc = drawingContext(); if (!dc) return; - // FIXME: Do this through platform-independent GraphicsContext API. #if PLATFORM(CG) const CGFloat components[5] = { c, m, y, k, a }; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK(); @@ -875,6 +874,8 @@ void CanvasRenderingContext2D::setShadow(float width, float height, float blur, CGColorSpaceRelease(colorSpace); CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor); CGColorRelease(shadowColor); +#else + dc->setShadow(IntSize(width, -height), blur, Color(c, m, y, k, a)); #endif } @@ -1078,6 +1079,15 @@ void CanvasRenderingContext2D::setCompositeOperation(const String& operation) setGlobalCompositeOperation(operation); } +void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const +{ +#if ENABLE(DASHBOARD_SUPPORT) + if (Settings* settings = m_canvas->document()->settings()) + if (settings->usesDashboardBackwardCompatibilityMode()) + gradient->setDashboardCompatibilityMode(); +#endif +} + PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec) { if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { @@ -1085,7 +1095,9 @@ PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float return 0; } - return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); + PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); + prepareGradientForDashboard(gradient.get()); + return gradient; } PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec) @@ -1095,7 +1107,9 @@ PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float ec = NOT_SUPPORTED_ERR; return 0; } - return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); + PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); + prepareGradientForDashboard(gradient.get()); + return gradient; } PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image, @@ -1422,6 +1436,7 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_canvas->height())); } +#if PLATFORM(CG) CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get(); if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) { // FIXME: The rect is not big enough for miters on stroked text. @@ -1451,6 +1466,7 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo return; } +#endif c->setTextDrawingMode(fill ? cTextFill : cTextStroke); c->drawBidiText(font, textRun, location); diff --git a/WebCore/html/CanvasRenderingContext2D.h b/WebCore/html/CanvasRenderingContext2D.h index 8d05b92..d74659e 100644 --- a/WebCore/html/CanvasRenderingContext2D.h +++ b/WebCore/html/CanvasRenderingContext2D.h @@ -251,7 +251,8 @@ namespace WebCore { #if ENABLE(DASHBOARD_SUPPORT) void clearPathForDashboardBackwardCompatibilityMode(); #endif - + + void prepareGradientForDashboard(CanvasGradient* gradient) const; void checkOrigin(const KURL&); HTMLCanvasElement* m_canvas; diff --git a/WebCore/html/CanvasStyle.cpp b/WebCore/html/CanvasStyle.cpp index d82643f..0aaaab2 100644 --- a/WebCore/html/CanvasStyle.cpp +++ b/WebCore/html/CanvasStyle.cpp @@ -78,12 +78,21 @@ CanvasStyle::CanvasStyle(float grayLevel, float alpha) } CanvasStyle::CanvasStyle(float r, float g, float b, float a) - : m_type(RGBA), m_alpha(a), m_red(r), m_green(g), m_blue(b) + : m_type(RGBA) + , m_alpha(a) + , m_red(r) + , m_green(g) + , m_blue(b) { } CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a) - : m_type(CMYKA), m_alpha(a), m_cyan(c), m_magenta(m), m_yellow(y), m_black(k) + : m_type(CMYKA) + , m_alpha(a) + , m_cyan(c) + , m_magenta(m) + , m_yellow(y) + , m_black(k) { } @@ -146,10 +155,10 @@ void CanvasStyle::applyStrokeColor(GraphicsContext* context) clr.setCmykF(m_cyan, m_magenta, m_yellow, m_black, m_alpha); currentPen.setColor(clr); context->platformContext()->setPen(currentPen); -#elif PLATFORM(CAIRO) - notImplemented(); #elif PLATFORM(SGL) context->setCMYKAStrokeColor(m_cyan, m_magenta, m_yellow, m_black, m_alpha); +#else + context->setStrokeColor(Color(m_cyan, m_magenta, m_yellow, m_black, m_alpha)); #endif break; } @@ -211,6 +220,8 @@ void CanvasStyle::applyFillColor(GraphicsContext* context) context->platformContext()->setBrush(currentBrush); #elif PLATFORM(SGL) context->setCMYKAFillColor(m_cyan, m_magenta, m_yellow, m_black, m_alpha); +#else + context->setFillColor(Color(m_cyan, m_magenta, m_yellow, m_black, m_alpha)); #endif break; } diff --git a/WebCore/html/HTMLAnchorElement.cpp b/WebCore/html/HTMLAnchorElement.cpp index 4122168..c6b2a95 100644 --- a/WebCore/html/HTMLAnchorElement.cpp +++ b/WebCore/html/HTMLAnchorElement.cpp @@ -38,7 +38,8 @@ #include "KeyboardEvent.h" #include "MouseEvent.h" #include "MutationEvent.h" -#include "RenderFlow.h" +#include "Page.h" +#include "RenderBox.h" #include "RenderImage.h" #include "ResourceRequest.h" #include "SelectionController.h" @@ -107,18 +108,14 @@ bool HTMLAnchorElement::isKeyboardFocusable(KeyboardEvent* event) const if (!document()->frame()->eventHandler()->tabsToLinks(event)) return false; - if (!renderer() || !renderer()->isBox()) + if (!renderer() || !renderer()->isBoxModelObject()) return false; // Before calling absoluteRects, check for the common case where the renderer - // or one of the continuations is non-empty, since this is a faster check and - // almost always returns true. - RenderBox* box = toRenderBox(renderer()); + // is non-empty, since this is a faster check and almost always returns true. + RenderBoxModelObject* box = toRenderBoxModelObject(renderer()); if (!box->borderBoundingBox().isEmpty()) return true; - for (RenderFlow* r = box->virtualContinuation(); r; r = r->continuation()) - if (!r->borderBoundingBox().isEmpty()) - return true; Vector<IntRect> rects; FloatPoint absPos = renderer()->localToAbsolute(); @@ -203,7 +200,7 @@ void HTMLAnchorElement::defaultEventHandler(Event* evt) if (evt->target()->toNode()->hasTagName(imgTag)) { HTMLImageElement* img = static_cast<HTMLImageElement*>(evt->target()->toNode()); if (img && img->isServerMap()) { - RenderImage* r = static_cast<RenderImage*>(img->renderer()); + RenderImage* r = toRenderImage(img->renderer()); if (r && e) { // FIXME: broken with transforms FloatPoint absPos = r->localToAbsolute(); @@ -434,7 +431,8 @@ void HTMLAnchorElement::setType(const AtomicString& value) String HTMLAnchorElement::hash() const { - return "#" + href().ref(); + String ref = href().ref(); + return ref.isEmpty() ? "" : "#" + ref; } String HTMLAnchorElement::host() const @@ -467,7 +465,8 @@ String HTMLAnchorElement::protocol() const String HTMLAnchorElement::search() const { - return href().query(); + String query = href().query(); + return query.isEmpty() ? "" : "?" + query; } String HTMLAnchorElement::text() const diff --git a/WebCore/html/HTMLAppletElement.cpp b/WebCore/html/HTMLAppletElement.cpp index 9d7ab6a..de8e1cf 100644 --- a/WebCore/html/HTMLAppletElement.cpp +++ b/WebCore/html/HTMLAppletElement.cpp @@ -99,9 +99,12 @@ void HTMLAppletElement::removedFromDocument() HTMLPlugInElement::removedFromDocument(); } -bool HTMLAppletElement::rendererIsNeeded(RenderStyle*) +bool HTMLAppletElement::rendererIsNeeded(RenderStyle* style) { - return !getAttribute(codeAttr).isNull(); + if (getAttribute(codeAttr).isNull()) + return false; + + return HTMLPlugInElement::rendererIsNeeded(style); } RenderObject* HTMLAppletElement::createRenderer(RenderArena*, RenderStyle* style) @@ -112,9 +115,13 @@ RenderObject* HTMLAppletElement::createRenderer(RenderArena*, RenderStyle* style HashMap<String, String> args; args.set("code", getAttribute(codeAttr)); + const AtomicString& codeBase = getAttribute(codebaseAttr); - if(!codeBase.isNull()) + if (!codeBase.isNull()) args.set("codeBase", codeBase); + else + args.set("codeBase", document()->baseURL().baseAsString()); + const AtomicString& name = getAttribute(document()->isHTMLDocument() ? nameAttr : idAttr); if (!name.isNull()) args.set("name", name); diff --git a/WebCore/html/HTMLAreaElement.cpp b/WebCore/html/HTMLAreaElement.cpp index 7a3c9e0..9db50b7 100644 --- a/WebCore/html/HTMLAreaElement.cpp +++ b/WebCore/html/HTMLAreaElement.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,6 +27,7 @@ #include "HTMLNames.h" #include "HitTestResult.h" #include "Length.h" +#include "Path.h" #include "RenderObject.h" using namespace std; @@ -35,8 +36,8 @@ namespace WebCore { using namespace HTMLNames; -HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document *doc) - : HTMLAnchorElement(tagName, doc) +HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document* document) + : HTMLAnchorElement(tagName, document) , m_coords(0) , m_coordsLen(0) , m_lastSize(-1, -1) @@ -73,11 +74,11 @@ void HTMLAreaElement::parseMappedAttribute(MappedAttribute *attr) bool HTMLAreaElement::mapMouseEvent(int x, int y, const IntSize& size, HitTestResult& result) { if (m_lastSize != size) { - region = getRegion(size); + m_region.set(new Path(getRegion(size))); m_lastSize = size; } - if (!region.contains(IntPoint(x, y))) + if (!m_region->contains(IntPoint(x, y))) return false; result.setInnerNode(this); @@ -150,32 +151,32 @@ Path HTMLAreaElement::getRegion(const IntSize& size) const return path; } -String HTMLAreaElement::accessKey() const +const AtomicString& HTMLAreaElement::accessKey() const { return getAttribute(accesskeyAttr); } -void HTMLAreaElement::setAccessKey(const String& value) +void HTMLAreaElement::setAccessKey(const AtomicString& value) { setAttribute(accesskeyAttr, value); } -String HTMLAreaElement::alt() const +const AtomicString& HTMLAreaElement::alt() const { return getAttribute(altAttr); } -void HTMLAreaElement::setAlt(const String& value) +void HTMLAreaElement::setAlt(const AtomicString& value) { setAttribute(altAttr, value); } -String HTMLAreaElement::coords() const +const AtomicString& HTMLAreaElement::coords() const { return getAttribute(coordsAttr); } -void HTMLAreaElement::setCoords(const String& value) +void HTMLAreaElement::setCoords(const AtomicString& value) { setAttribute(coordsAttr, value); } @@ -185,7 +186,7 @@ KURL HTMLAreaElement::href() const return document()->completeURL(getAttribute(hrefAttr)); } -void HTMLAreaElement::setHref(const String& value) +void HTMLAreaElement::setHref(const AtomicString& value) { setAttribute(hrefAttr, value); } @@ -200,12 +201,12 @@ void HTMLAreaElement::setNoHref(bool noHref) setAttribute(nohrefAttr, noHref ? "" : 0); } -String HTMLAreaElement::shape() const +const AtomicString& HTMLAreaElement::shape() const { return getAttribute(shapeAttr); } -void HTMLAreaElement::setShape(const String& value) +void HTMLAreaElement::setShape(const AtomicString& value) { setAttribute(shapeAttr, value); } @@ -220,7 +221,7 @@ String HTMLAreaElement::target() const return getAttribute(targetAttr); } -void HTMLAreaElement::setTarget(const String& value) +void HTMLAreaElement::setTarget(const AtomicString& value) { setAttribute(targetAttr, value); } diff --git a/WebCore/html/HTMLAreaElement.h b/WebCore/html/HTMLAreaElement.h index 7de5832..19533b1 100644 --- a/WebCore/html/HTMLAreaElement.h +++ b/WebCore/html/HTMLAreaElement.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,11 +25,11 @@ #include "HTMLAnchorElement.h" #include "IntSize.h" -#include "Path.h" namespace WebCore { class HitTestResult; +class Path; class HTMLAreaElement : public HTMLAnchorElement { public: @@ -47,33 +47,34 @@ public: virtual IntRect getRect(RenderObject*) const; - String accessKey() const; - void setAccessKey(const String&); + const AtomicString& accessKey() const; + void setAccessKey(const AtomicString&); - String alt() const; - void setAlt(const String&); + const AtomicString& alt() const; + void setAlt(const AtomicString&); - String coords() const; - void setCoords(const String&); + const AtomicString& coords() const; + void setCoords(const AtomicString&); KURL href() const; - void setHref(const String&); + void setHref(const AtomicString&); bool noHref() const; void setNoHref(bool); - String shape() const; - void setShape(const String&); + const AtomicString& shape() const; + void setShape(const AtomicString&); virtual bool isFocusable() const; virtual String target() const; - void setTarget(const String&); + void setTarget(const AtomicString&); private: enum Shape { Default, Poly, Rect, Circle, Unknown }; Path getRegion(const IntSize&) const; - Path region; + + OwnPtr<Path> m_region; Length* m_coords; int m_coordsLen; IntSize m_lastSize; diff --git a/WebCore/html/HTMLCanvasElement.cpp b/WebCore/html/HTMLCanvasElement.cpp index 7481d96..1cd2796 100644 --- a/WebCore/html/HTMLCanvasElement.cpp +++ b/WebCore/html/HTMLCanvasElement.cpp @@ -280,7 +280,7 @@ TransformationMatrix HTMLCanvasElement::baseTransform() const IntSize size = convertLogicalToDevice(unscaledSize); TransformationMatrix transform; if (size.width() && size.height()) - transform.scale(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); + transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); transform.multiply(m_imageBuffer->baseTransform()); return transform; } diff --git a/WebCore/html/HTMLCanvasElement.idl b/WebCore/html/HTMLCanvasElement.idl index e457c15..bf69ac0 100644 --- a/WebCore/html/HTMLCanvasElement.idl +++ b/WebCore/html/HTMLCanvasElement.idl @@ -38,7 +38,7 @@ module html { raises(DOMException); #if !defined(LANGUAGE_OBJECTIVE_C) - DOMObject getContext(in DOMString contextId); + [V8Custom] DOMObject getContext(in DOMString contextId); #endif }; diff --git a/WebCore/html/HTMLDocument.cpp b/WebCore/html/HTMLDocument.cpp index a987c76..01f151c 100644 --- a/WebCore/html/HTMLDocument.cpp +++ b/WebCore/html/HTMLDocument.cpp @@ -433,5 +433,11 @@ void HTMLDocument::clear() // We've long had a comment saying that IE doesn't support this. // But I do see it in the documentation for Mozilla. } - + +bool HTMLDocument::isFrameSet() const +{ + HTMLElement* bodyElement = body(); + return bodyElement && bodyElement->renderer() && bodyElement->hasTagName(framesetTag); +} + } diff --git a/WebCore/html/HTMLDocument.h b/WebCore/html/HTMLDocument.h index de84c70..ab5da50 100644 --- a/WebCore/html/HTMLDocument.h +++ b/WebCore/html/HTMLDocument.h @@ -88,6 +88,7 @@ protected: private: virtual bool isHTMLDocument() const { return true; } + virtual bool isFrameSet() const; virtual Tokenizer* createTokenizer(); virtual void determineParseMode(); diff --git a/WebCore/html/HTMLElement.cpp b/WebCore/html/HTMLElement.cpp index 4394bc8..906f847 100644 --- a/WebCore/html/HTMLElement.cpp +++ b/WebCore/html/HTMLElement.cpp @@ -96,23 +96,6 @@ int HTMLElement::tagPriority() const return 1; } -PassRefPtr<Node> HTMLElement::cloneNode(bool deep) -{ - RefPtr<HTMLElement> clone = HTMLElementFactory::createHTMLElement(tagQName(), document(), 0, false); - if (!clone) - return 0; - - if (namedAttrMap) - clone->attributes()->setAttributes(*namedAttrMap); - - clone->copyNonAttributeProperties(this); - - if (deep) - cloneChildNodes(clone.get()); - - return clone.release(); -} - bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const { if (attrName == alignAttr || @@ -947,8 +930,18 @@ bool HTMLElement::inEitherTagList(const Node* newChild) if (newChild->isHTMLElement()) { const HTMLElement* child = static_cast<const HTMLElement*>(newChild); - if (inlineTagList()->contains(child->tagQName().localName().impl())) + if (inlineTagList()->contains(child->tagQName().localName().impl())) { +#if PLATFORM(MAC) + if (child->tagQName().localName() == styleTag) { + // Leopard Mail doesn't expect <style> to be in the body of the document, so don't allow it in that case. + // See <rdar://problem/6621310> + Settings* settings = newChild->document() ? newChild->document()->settings() : 0; + if (settings && settings->needsLeopardMailQuirks()) + return false; + } +#endif return true; + } if (blockTagList()->contains(child->tagQName().localName().impl())) return true; return !isRecognizedTagName(child->tagQName()); // Accept custom html tags @@ -999,7 +992,7 @@ bool HTMLElement::rendererIsNeeded(RenderStyle *style) if (settings && settings->isJavaScriptEnabled()) return false; } - return (document()->documentElement() == this) || (style->display() != NONE); + return StyledElement::rendererIsNeeded(style); } RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style) diff --git a/WebCore/html/HTMLElement.h b/WebCore/html/HTMLElement.h index 3d84e8b..60152cd 100644 --- a/WebCore/html/HTMLElement.h +++ b/WebCore/html/HTMLElement.h @@ -45,8 +45,6 @@ public: virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; virtual void parseMappedAttribute(MappedAttribute*); - virtual PassRefPtr<Node> cloneNode(bool deep); - PassRefPtr<HTMLCollection> children(); String id() const; diff --git a/WebCore/html/HTMLElementFactory.cpp b/WebCore/html/HTMLElementFactory.cpp deleted file mode 100644 index 16c2b99..0000000 --- a/WebCore/html/HTMLElementFactory.cpp +++ /dev/null @@ -1,510 +0,0 @@ -/* - * This file is part of the HTML DOM implementation for KDE. - * - * Copyright (C) 2005 Apple Computer, Inc. - * - * 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 - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "HTMLElementFactory.h" - -#include "HTMLAnchorElement.h" -#include "HTMLAppletElement.h" -#include "HTMLAreaElement.h" -#include "HTMLAudioElement.h" -#include "HTMLBRElement.h" -#include "HTMLBaseElement.h" -#include "HTMLBaseFontElement.h" -#include "HTMLBlockquoteElement.h" -#include "HTMLBodyElement.h" -#include "HTMLButtonElement.h" -#include "HTMLCanvasElement.h" -#include "HTMLDListElement.h" -#include "HTMLDirectoryElement.h" -#include "HTMLDivElement.h" -#include "HTMLDocument.h" -#include "HTMLEmbedElement.h" -#include "HTMLFieldSetElement.h" -#include "HTMLFontElement.h" -#include "HTMLFormElement.h" -#include "HTMLFrameElement.h" -#include "HTMLFrameSetElement.h" -#include "HTMLHRElement.h" -#include "HTMLHeadElement.h" -#include "HTMLHeadingElement.h" -#include "HTMLHtmlElement.h" -#include "HTMLIFrameElement.h" -#include "HTMLImageElement.h" -#include "HTMLIsIndexElement.h" -#include "HTMLKeygenElement.h" -#include "HTMLLIElement.h" -#include "HTMLLabelElement.h" -#include "HTMLLegendElement.h" -#include "HTMLLinkElement.h" -#include "HTMLMapElement.h" -#include "HTMLMarqueeElement.h" -#include "HTMLMenuElement.h" -#include "HTMLMetaElement.h" -#include "HTMLModElement.h" -#include "HTMLNames.h" -#include "HTMLOListElement.h" -#include "HTMLObjectElement.h" -#include "HTMLOptGroupElement.h" -#include "HTMLOptionElement.h" -#include "HTMLParagraphElement.h" -#include "HTMLParamElement.h" -#include "HTMLPreElement.h" -#include "HTMLQuoteElement.h" -#include "HTMLScriptElement.h" -#include "HTMLSelectElement.h" -#include "HTMLSourceElement.h" -#include "HTMLStyleElement.h" -#include "HTMLTableCaptionElement.h" -#include "HTMLTableCellElement.h" -#include "HTMLTableColElement.h" -#include "HTMLTableElement.h" -#include "HTMLTableRowElement.h" -#include "HTMLTableSectionElement.h" -#include "HTMLTextAreaElement.h" -#include "HTMLTitleElement.h" -#include "HTMLUListElement.h" -#include "HTMLVideoElement.h" - -namespace WebCore { - -using namespace HTMLNames; - -typedef PassRefPtr<HTMLElement> (*ConstructorFunc)(const QualifiedName& tagName, Document*, HTMLFormElement*, bool createdByParser); -typedef HashMap<AtomicStringImpl*, ConstructorFunc> FunctionMap; -static FunctionMap* gFunctionMap; - -static PassRefPtr<HTMLElement> htmlConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLHtmlElement(htmlTag, doc); -} - -static PassRefPtr<HTMLElement> headConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLHeadElement(headTag, doc); -} - -static PassRefPtr<HTMLElement> bodyConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLBodyElement(bodyTag, doc); -} - -static PassRefPtr<HTMLElement> baseConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLBaseElement(baseTag, doc); -} - -static PassRefPtr<HTMLElement> linkConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLLinkElement(linkTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> metaConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLMetaElement(metaTag, doc); -} - -static PassRefPtr<HTMLElement> styleConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLStyleElement(styleTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> titleConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTitleElement(titleTag, doc); -} - -static PassRefPtr<HTMLElement> frameConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLFrameElement(frameTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> framesetConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLFrameSetElement(framesetTag, doc); -} - -static PassRefPtr<HTMLElement> iframeConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLIFrameElement(iframeTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> formConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLFormElement(formTag, doc); -} - -static PassRefPtr<HTMLElement> buttonConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLButtonElement(buttonTag, doc, form); -} - -static PassRefPtr<HTMLElement> inputConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLInputElement(inputTag, doc, form); -} - -static PassRefPtr<HTMLElement> isindexConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLIsIndexElement(isindexTag, doc, form); -} - -static PassRefPtr<HTMLElement> fieldsetConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLFieldSetElement(fieldsetTag, doc, form); -} - -static PassRefPtr<HTMLElement> keygenConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLKeygenElement(keygenTag, doc, form); -} - -static PassRefPtr<HTMLElement> labelConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLLabelElement(labelTag, doc); -} - -static PassRefPtr<HTMLElement> legendConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLLegendElement(legendTag, doc, form); -} - -static PassRefPtr<HTMLElement> optgroupConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLOptGroupElement(optgroupTag, doc, form); -} - -static PassRefPtr<HTMLElement> optionConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLOptionElement(optionTag, doc, form); -} - -static PassRefPtr<HTMLElement> selectConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLSelectElement(selectTag, doc, form); -} - -static PassRefPtr<HTMLElement> textareaConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLTextAreaElement(textareaTag, doc, form); -} - -static PassRefPtr<HTMLElement> dlConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLDListElement(dlTag, doc); -} - -static PassRefPtr<HTMLElement> ulConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLUListElement(ulTag, doc); -} - -static PassRefPtr<HTMLElement> olConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLOListElement(olTag, doc); -} - -static PassRefPtr<HTMLElement> dirConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLDirectoryElement(dirTag, doc); -} - -static PassRefPtr<HTMLElement> menuConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLMenuElement(menuTag, doc); -} - -static PassRefPtr<HTMLElement> liConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLLIElement(liTag, doc); -} - -static PassRefPtr<HTMLElement> blockquoteConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLBlockquoteElement(blockquoteTag, doc); -} - -static PassRefPtr<HTMLElement> divConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLDivElement(divTag, doc); -} - -static PassRefPtr<HTMLElement> headingConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLHeadingElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> hrConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLHRElement(hrTag, doc); -} - -static PassRefPtr<HTMLElement> paragraphConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLParagraphElement(pTag, doc); -} - -static PassRefPtr<HTMLElement> preConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLPreElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> basefontConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLBaseFontElement(basefontTag, doc); -} - -static PassRefPtr<HTMLElement> fontConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLFontElement(fontTag, doc); -} - -static PassRefPtr<HTMLElement> modConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLModElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> anchorConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLAnchorElement(aTag, doc); -} - -static PassRefPtr<HTMLElement> imageConstructor(const QualifiedName&, Document* doc, HTMLFormElement* form, bool) -{ - return new HTMLImageElement(imgTag, doc, form); -} - -static PassRefPtr<HTMLElement> mapConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLMapElement(mapTag, doc); -} - -static PassRefPtr<HTMLElement> areaConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLAreaElement(areaTag, doc); -} - -static PassRefPtr<HTMLElement> canvasConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLCanvasElement(canvasTag, doc); -} - -static PassRefPtr<HTMLElement> appletConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLAppletElement(appletTag, doc); -} - -static PassRefPtr<HTMLElement> embedConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLEmbedElement(embedTag, doc); -} - -static PassRefPtr<HTMLElement> objectConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLObjectElement(objectTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> paramConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLParamElement(paramTag, doc); -} - -static PassRefPtr<HTMLElement> scriptConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool createdByParser) -{ - return new HTMLScriptElement(scriptTag, doc, createdByParser); -} - -static PassRefPtr<HTMLElement> tableConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableElement(tableTag, doc); -} - -static PassRefPtr<HTMLElement> tableCaptionConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableCaptionElement(captionTag, doc); -} - -static PassRefPtr<HTMLElement> tableColConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableColElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> tableRowConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableRowElement(trTag, doc); -} - -static PassRefPtr<HTMLElement> tableCellConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableCellElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> tableSectionConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLTableSectionElement(tagName, doc); -} - -static PassRefPtr<HTMLElement> brConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLBRElement(brTag, doc); -} - -static PassRefPtr<HTMLElement> quoteConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - doc->setUsesBeforeAfterRules(true); - return new HTMLQuoteElement(qTag, doc); -} - -static PassRefPtr<HTMLElement> marqueeConstructor(const QualifiedName&, Document* doc, HTMLFormElement*, bool) -{ - return new HTMLMarqueeElement(marqueeTag, doc); -} - -#if ENABLE(VIDEO) -static PassRefPtr<HTMLElement> audioConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - if (!MediaPlayer::isAvailable()) - return new HTMLElement(tagName, doc); - return new HTMLAudioElement(audioTag, doc); -} - -static PassRefPtr<HTMLElement> videoConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - if (!MediaPlayer::isAvailable()) - return new HTMLElement(tagName, doc); - return new HTMLVideoElement(videoTag, doc); -} - -static PassRefPtr<HTMLElement> sourceConstructor(const QualifiedName& tagName, Document* doc, HTMLFormElement*, bool) -{ - if (!MediaPlayer::isAvailable()) - return new HTMLElement(tagName, doc); - return new HTMLSourceElement(sourceTag, doc); -} -#endif - -static void addTag(const QualifiedName& tag, ConstructorFunc func) -{ - gFunctionMap->set(tag.localName().impl(), func); -} - -static void createFunctionMap() -{ - // Create the table. - gFunctionMap = new FunctionMap; - - // Populate it with constructor functions. - addTag(aTag, anchorConstructor); - addTag(appletTag, appletConstructor); - addTag(areaTag, areaConstructor); - addTag(baseTag, baseConstructor); - addTag(basefontTag, basefontConstructor); - addTag(blockquoteTag, blockquoteConstructor); - addTag(bodyTag, bodyConstructor); - addTag(brTag, brConstructor); - addTag(buttonTag, buttonConstructor); - addTag(canvasTag, canvasConstructor); - addTag(captionTag, tableCaptionConstructor); - addTag(colTag, tableColConstructor); - addTag(colgroupTag, tableColConstructor); - addTag(delTag, modConstructor); - addTag(dirTag, dirConstructor); - addTag(divTag, divConstructor); - addTag(dlTag, dlConstructor); - addTag(embedTag, embedConstructor); - addTag(fieldsetTag, fieldsetConstructor); - addTag(fontTag, fontConstructor); - addTag(formTag, formConstructor); - addTag(frameTag, frameConstructor); - addTag(framesetTag, framesetConstructor); - addTag(h1Tag, headingConstructor); - addTag(h2Tag, headingConstructor); - addTag(h3Tag, headingConstructor); - addTag(h4Tag, headingConstructor); - addTag(h5Tag, headingConstructor); - addTag(h6Tag, headingConstructor); - addTag(headTag, headConstructor); - addTag(hrTag, hrConstructor); - addTag(htmlTag, htmlConstructor); - addTag(iframeTag, iframeConstructor); - addTag(imageTag, imageConstructor); - addTag(imgTag, imageConstructor); - addTag(inputTag, inputConstructor); - addTag(insTag, modConstructor); - addTag(isindexTag, isindexConstructor); - addTag(keygenTag, keygenConstructor); - addTag(labelTag, labelConstructor); - addTag(legendTag, legendConstructor); - addTag(liTag, liConstructor); - addTag(linkTag, linkConstructor); - addTag(listingTag, preConstructor); - addTag(mapTag, mapConstructor); - addTag(marqueeTag, marqueeConstructor); - addTag(menuTag, menuConstructor); - addTag(metaTag, metaConstructor); - addTag(objectTag, objectConstructor); - addTag(olTag, olConstructor); - addTag(optgroupTag, optgroupConstructor); - addTag(optionTag, optionConstructor); - addTag(pTag, paragraphConstructor); - addTag(paramTag, paramConstructor); - addTag(preTag, preConstructor); - addTag(qTag, quoteConstructor); - addTag(scriptTag, scriptConstructor); - addTag(selectTag, selectConstructor); - addTag(styleTag, styleConstructor); - addTag(tableTag, tableConstructor); - addTag(tbodyTag, tableSectionConstructor); - addTag(tdTag, tableCellConstructor); - addTag(textareaTag, textareaConstructor); - addTag(tfootTag, tableSectionConstructor); - addTag(thTag, tableCellConstructor); - addTag(theadTag, tableSectionConstructor); - addTag(titleTag, titleConstructor); - addTag(trTag, tableRowConstructor); - addTag(ulTag, ulConstructor); - addTag(xmpTag, preConstructor); -#if ENABLE(VIDEO) - addTag(audioTag, audioConstructor); - addTag(sourceTag, sourceConstructor); - addTag(videoTag, videoConstructor); -#endif -} - -PassRefPtr<HTMLElement> HTMLElementFactory::createHTMLElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form, bool createdByParser) -{ - if (!doc) - return 0; // Don't allow elements to ever be made without having a doc. - - if (!gFunctionMap) - createFunctionMap(); - - ConstructorFunc func = gFunctionMap->get(tagName.localName().impl()); - if (func) - return func(tagName, doc, form, createdByParser); - - // elements with no special representation in the DOM - return new HTMLElement(tagName, doc); -} - -} - diff --git a/WebCore/html/HTMLElementFactory.h b/WebCore/html/HTMLElementFactory.h deleted file mode 100644 index 539d070..0000000 --- a/WebCore/html/HTMLElementFactory.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the HTML DOM implementation for KDE. - * - * Copyright (C) 2005, 2006 Apple Computer, Inc. - * - * 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 - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef HTMLElementFactory_h -#define HTMLElementFactory_h - -#include <wtf/Forward.h> - -namespace WebCore { - -class AtomicString; -class Document; -class Element; -class HTMLElement; -class HTMLFormElement; -class QualifiedName; - -// The idea behind this class is that there will eventually be a mapping from namespace URIs to ElementFactories that can dispense elements. -// In a compound document world, the generic createElement function (will end up being virtual) will be called. -class HTMLElementFactory { -public: - PassRefPtr<Element> createElement(const QualifiedName&, Document*, bool createdByParser = true); - static PassRefPtr<HTMLElement> createHTMLElement(const QualifiedName& tagName, Document*, HTMLFormElement* = 0, bool createdByParser = true); -}; - -} - -#endif diff --git a/WebCore/html/HTMLEmbedElement.cpp b/WebCore/html/HTMLEmbedElement.cpp index 1af7147..f467849 100644 --- a/WebCore/html/HTMLEmbedElement.cpp +++ b/WebCore/html/HTMLEmbedElement.cpp @@ -137,7 +137,7 @@ bool HTMLEmbedElement::rendererIsNeeded(RenderStyle* style) return false; } - return true; + return HTMLPlugInElement::rendererIsNeeded(style); } RenderObject* HTMLEmbedElement::createRenderer(RenderArena* arena, RenderStyle*) @@ -164,7 +164,7 @@ void HTMLEmbedElement::attach() m_imageLoader->updateFromElement(); if (renderer()) - static_cast<RenderImage*>(renderer())->setCachedImage(m_imageLoader->image()); + toRenderImage(renderer())->setCachedImage(m_imageLoader->image()); } } diff --git a/WebCore/html/HTMLFormControlElement.cpp b/WebCore/html/HTMLFormControlElement.cpp index 0295f39..5238ad5 100644 --- a/WebCore/html/HTMLFormControlElement.cpp +++ b/WebCore/html/HTMLFormControlElement.cpp @@ -34,6 +34,7 @@ #include "HTMLNames.h" #include "HTMLParser.h" #include "HTMLTokenizer.h" +#include "RenderBox.h" #include "RenderTheme.h" namespace WebCore { @@ -98,8 +99,12 @@ void HTMLFormControlElement::attach() // Focus the element if it should honour its autofocus attribute. // We have to determine if the element is a TextArea/Input/Button/Select, // if input type hidden ignore autofocus. So if disabled or readonly. + bool isInputTypeHidden = false; + if (hasTagName(inputTag)) + isInputTypeHidden = static_cast<HTMLInputElement*>(this)->isInputTypeHidden(); + if (autofocus() && renderer() && !document()->ignoreAutofocus() && !isReadOnlyControl() && - ((hasTagName(inputTag) && !isInputTypeHidden()) || hasTagName(selectTag) || + ((hasTagName(inputTag) && !isInputTypeHidden) || hasTagName(selectTag) || hasTagName(buttonTag) || hasTagName(textareaTag))) focus(); } diff --git a/WebCore/html/HTMLFormControlElement.h b/WebCore/html/HTMLFormControlElement.h index 0d1b31a..7430df7 100644 --- a/WebCore/html/HTMLFormControlElement.h +++ b/WebCore/html/HTMLFormControlElement.h @@ -45,7 +45,7 @@ public: virtual const AtomicString& type() const = 0; - virtual bool isControl() const { return true; } + virtual bool isTextControl() const { return false; } virtual bool isEnabled() const { return !disabled(); } virtual void parseMappedAttribute(MappedAttribute*); diff --git a/WebCore/html/HTMLFormElement.cpp b/WebCore/html/HTMLFormElement.cpp index afc9c1e..df4e541 100644 --- a/WebCore/html/HTMLFormElement.cpp +++ b/WebCore/html/HTMLFormElement.cpp @@ -44,6 +44,7 @@ #include "MIMETypeRegistry.h" #include "Page.h" #include "RenderTextControl.h" +#include <wtf/CurrentTime.h> #include <wtf/RandomNumber.h> #include <limits> @@ -61,6 +62,14 @@ namespace WebCore { using namespace HTMLNames; +static int64_t generateFormDataIdentifier() +{ + // Initialize to the current time to reduce the likelihood of generating + // identifiers that overlap with those from past/future browser sessions. + static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0); + return ++nextIdentifier; +} + HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc) : HTMLElement(tagName, doc) , m_elementAliases(0) @@ -116,7 +125,7 @@ void HTMLFormElement::removedFromDocument() void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture) { - EventTargetNode* targetNode = event->target()->toNode(); + Node* targetNode = event->target()->toNode(); if (!useCapture && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) { event->stopPropagation(); return; @@ -202,7 +211,8 @@ PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary) if (!path.isEmpty()) { if (Page* page = document()->page()) { String generatedFileName; - if (shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName)) + shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName); + if (shouldGenerateFile) fileName = generatedFileName; } } @@ -240,6 +250,8 @@ PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary) m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true); result->appendData(encodedData.data(), encodedData.size()); + + result->setIdentifier(generateFormDataIdentifier()); return result; } @@ -625,9 +637,11 @@ void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLFormControlElement* if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element) return; + InputElement* inputElement = toInputElement(element); + ASSERT_UNUSED(inputElement, inputElement); + ASSERT(inputElement->isChecked()); ASSERT(element->isRadioButton()); - ASSERT(element->isChecked()); - + m_nameToCheckedRadioButtonMap->remove(it); if (m_nameToCheckedRadioButtonMap->isEmpty()) m_nameToCheckedRadioButtonMap.clear(); diff --git a/WebCore/html/HTMLImageElement.cpp b/WebCore/html/HTMLImageElement.cpp index 599cbee..c4e7608 100644 --- a/WebCore/html/HTMLImageElement.cpp +++ b/WebCore/html/HTMLImageElement.cpp @@ -80,7 +80,7 @@ void HTMLImageElement::parseMappedAttribute(MappedAttribute* attr) const QualifiedName& attrName = attr->name(); if (attrName == altAttr) { if (renderer() && renderer()->isImage()) - static_cast<RenderImage*>(renderer())->updateAltText(); + toRenderImage(renderer())->updateAltText(); } else if (attrName == srcAttr) m_imageLoader.updateFromElementIgnoringPreviousError(); else if (attrName == widthAttr) @@ -166,7 +166,7 @@ void HTMLImageElement::attach() HTMLElement::attach(); if (renderer() && renderer()->isImage()) { - RenderImage* imageObj = static_cast<RenderImage*>(renderer()); + RenderImage* imageObj = toRenderImage(renderer()); if (imageObj->hasImage()) return; imageObj->setCachedImage(m_imageLoader.image()); diff --git a/WebCore/html/HTMLImageLoader.cpp b/WebCore/html/HTMLImageLoader.cpp index 22e3abc..5dac8bf 100644 --- a/WebCore/html/HTMLImageLoader.cpp +++ b/WebCore/html/HTMLImageLoader.cpp @@ -42,10 +42,7 @@ HTMLImageLoader::~HTMLImageLoader() void HTMLImageLoader::dispatchLoadEvent() { - if (!haveFiredLoadEvent() && image()) { - setHaveFiredLoadEvent(true); - element()->dispatchEventForType(image()->errorOccurred() ? eventNames().errorEvent : eventNames().loadEvent, false, false); - } + element()->dispatchEventForType(image()->errorOccurred() ? eventNames().errorEvent : eventNames().loadEvent, false, false); } String HTMLImageLoader::sourceURI(const AtomicString& attr) const diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp index c451680..103b740 100644 --- a/WebCore/html/HTMLInputElement.cpp +++ b/WebCore/html/HTMLInputElement.cpp @@ -53,9 +53,6 @@ #include "RenderTextControlSingleLine.h" #include "RenderTheme.h" #include "TextEvent.h" -#if USE(LOW_BANDWIDTH_DISPLAY) -#include "FrameLoader.h" -#endif #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS #include "WebViewCore.h" #endif @@ -447,7 +444,7 @@ int HTMLInputElement::selectionStart() const return m_data.cachedSelectionStart(); if (!renderer()) return 0; - return static_cast<RenderTextControl*>(renderer())->selectionStart(); + return toRenderTextControl(renderer())->selectionStart(); } int HTMLInputElement::selectionEnd() const @@ -458,7 +455,7 @@ int HTMLInputElement::selectionEnd() const return m_data.cachedSelectionEnd(); if (!renderer()) return 0; - return static_cast<RenderTextControl*>(renderer())->selectionEnd(); + return toRenderTextControl(renderer())->selectionEnd(); } void HTMLInputElement::setSelectionStart(int start) @@ -467,7 +464,7 @@ void HTMLInputElement::setSelectionStart(int start) return; if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->setSelectionStart(start); + toRenderTextControl(renderer())->setSelectionStart(start); } void HTMLInputElement::setSelectionEnd(int end) @@ -476,7 +473,7 @@ void HTMLInputElement::setSelectionEnd(int end) return; if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end); + toRenderTextControl(renderer())->setSelectionEnd(end); } void HTMLInputElement::select() @@ -485,7 +482,7 @@ void HTMLInputElement::select() return; if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->select(); + toRenderTextControl(renderer())->select(); } void HTMLInputElement::setSelectionRange(int start, int end) @@ -578,7 +575,7 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr) InputElement::parseSizeAttribute(m_data, attr); else if (attr->name() == altAttr) { if (renderer() && inputType() == IMAGE) - static_cast<RenderImage*>(renderer())->updateAltText(); + toRenderImage(renderer())->updateAltText(); } else if (attr->name() == srcAttr) { if (renderer() && inputType() == IMAGE) { if (!m_imageLoader) @@ -644,13 +641,6 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr) bool HTMLInputElement::rendererIsNeeded(RenderStyle *style) { -#if USE(LOW_BANDWIDTH_DISPLAY) - if (document()->inLowBandwidthDisplay()) { - document()->frame()->loader()->needToSwitchOutLowBandwidthDisplay(); - return false; - } -#endif - switch (inputType()) { case BUTTON: case CHECKBOX: @@ -715,7 +705,7 @@ void HTMLInputElement::attach() m_imageLoader.set(new HTMLImageLoader(this)); m_imageLoader->updateFromElement(); if (renderer()) { - RenderImage* imageObj = static_cast<RenderImage*>(renderer()); + RenderImage* imageObj = toRenderImage(renderer()); imageObj->setCachedImage(m_imageLoader->image()); // If we have no image at all because we have no src attribute, set @@ -956,17 +946,17 @@ void HTMLInputElement::setValue(const String& value) if (inputType() == FILE && !value.isEmpty()) return; - if (isTextField()) - InputElement::updatePlaceholderVisibility(m_data, document()); - setValueMatchesRenderer(false); if (storesValueSeparateFromAttribute()) { if (inputType() == FILE) m_fileList->clear(); else { m_data.setValue(constrainValue(value)); - if (isTextField() && inDocument()) - document()->updateRendering(); + if (isTextField()) { + InputElement::updatePlaceholderVisibility(m_data, document()); + if (inDocument()) + document()->updateRendering(); + } } if (renderer()) renderer()->updateFromElement(); @@ -1113,6 +1103,9 @@ void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data) void HTMLInputElement::defaultEventHandler(Event* evt) { + // FIXME: It would be better to refactor this for the different types of input element. + // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific. + bool clickDefaultFormButton = false; if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n") @@ -1128,26 +1121,36 @@ void HTMLInputElement::defaultEventHandler(Event* evt) m_yPos = 0; } else { // FIXME: This doesn't work correctly with transforms. + // FIXME: pageX/pageY need adjusting for pageZoomFactor(). Use actualPageLocation()? IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute()); m_xPos = me->pageX() - absOffset.x(); m_yPos = me->pageY() - absOffset.y(); } } - if (isTextField() && evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame() - && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { + if (isTextField() + && evt->type() == eventNames().keydownEvent + && evt->isKeyboardEvent() + && focused() + && document()->frame() + && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { evt->setDefaultHandled(); return; } - if (inputType() == RADIO && evt->isMouseEvent() - && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { + if (inputType() == RADIO + && evt->isMouseEvent() + && evt->type() == eventNames().clickEvent + && static_cast<MouseEvent*>(evt)->button() == LeftButton) { evt->setDefaultHandled(); return; } - // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields - if (!clickDefaultFormButton) { + // Call the base event handler before any of our own event handling for almost all events in text fields. + // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. + bool callBaseClassEarly = isTextField() && !clickDefaultFormButton + && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent); + if (callBaseClassEarly) { HTMLFormControlElementWithState::defaultEventHandler(evt); if (evt->defaultHandled()) return; @@ -1246,7 +1249,8 @@ void HTMLInputElement::defaultEventHandler(Event* evt) case SUBMIT: case RADIO: setActive(true, true); - // No setDefaultHandled() - IE dispatches a keypress in this case. + // No setDefaultHandled(), because IE dispatches a keypress in this case + // and the caller will only dispatch a keypress if we don't call setDefaultHandled. return; default: break; @@ -1337,12 +1341,12 @@ void HTMLInputElement::defaultEventHandler(Event* evt) } // Fire onChange for text fields. RenderObject* r = renderer(); - if (r && r->isTextField() && r->isEdited()) { + if (r && r->isTextField() && toRenderTextControl(r)->isEdited()) { onChange(); // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it. r = renderer(); - if (r) - r->setEdited(false); + if (r && r->isTextField()) + toRenderTextControl(r)->setEdited(false); } // Form may never have been present, or may have been destroyed by the change event. if (form()) @@ -1357,20 +1361,11 @@ void HTMLInputElement::defaultEventHandler(Event* evt) if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent)) static_cast<RenderTextControlSingleLine*>(renderer())->forwardEvent(evt); - if (inputType() == RANGE && renderer()) { - RenderSlider* slider = static_cast<RenderSlider*>(renderer()); - if (evt->isMouseEvent() && evt->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { - MouseEvent* mEvt = static_cast<MouseEvent*>(evt); - if (!slider->mouseEventIsInThumb(mEvt)) { - IntPoint eventOffset(mEvt->offsetX(), mEvt->offsetY()); - if (mEvt->target() != this) // Does this ever happen now? Was added for <video> controls - eventOffset = roundedIntPoint(renderer()->absoluteToLocal(FloatPoint(mEvt->pageX(), mEvt->pageY()), false, true)); - slider->setValueForPosition(slider->positionForOffset(eventOffset)); - } - } - if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent()) - slider->forwardEvent(evt); - } + if (inputType() == RANGE && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())) + static_cast<RenderSlider*>(renderer())->forwardEvent(evt); + + if (!callBaseClassEarly && !evt->defaultHandled()) + HTMLFormControlElementWithState::defaultEventHandler(evt); } bool HTMLInputElement::isURLAttribute(Attribute *attr) const @@ -1453,6 +1448,16 @@ void HTMLInputElement::setMaxLength(int _maxLength) setAttribute(maxlengthAttr, String::number(_maxLength)); } +bool HTMLInputElement::multiple() const +{ + return !getAttribute(multipleAttr).isNull(); +} + +void HTMLInputElement::setMultiple(bool multiple) +{ + setAttribute(multipleAttr, multiple ? "" : 0); +} + void HTMLInputElement::setSize(unsigned _size) { setAttribute(sizeAttr, String::number(_size)); @@ -1537,11 +1542,11 @@ void HTMLInputElement::onSearch() dispatchEventForType(eventNames().searchEvent, true, false); } -Selection HTMLInputElement::selection() const +VisibleSelection HTMLInputElement::selection() const { if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1) - return Selection(); - return static_cast<RenderTextControl*>(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd()); + return VisibleSelection(); + return toRenderTextControl(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd()); } void HTMLInputElement::documentDidBecomeActive() diff --git a/WebCore/html/HTMLInputElement.h b/WebCore/html/HTMLInputElement.h index fd7ce03..5b6a5d6 100644 --- a/WebCore/html/HTMLInputElement.h +++ b/WebCore/html/HTMLInputElement.h @@ -33,7 +33,7 @@ namespace WebCore { class FileList; class HTMLImageLoader; class KURL; -class Selection; +class VisibleSelection; class HTMLInputElement : public HTMLFormControlElementWithState, public InputElement { public: @@ -188,10 +188,13 @@ public: int maxLength() const; void setMaxLength(int); + bool multiple() const; + void setMultiple(bool); + String useMap() const; void setUseMap(const String&); - bool isAutofilled() const { return m_autofilled; } + virtual bool isAutofilled() const { return m_autofilled; } void setAutofilled(bool value = true); FileList* files(); @@ -200,7 +203,7 @@ public: void addSearchResult(); void onSearch(); - Selection selection() const; + VisibleSelection selection() const; virtual String constrainValue(const String& proposedValue) const; diff --git a/WebCore/html/HTMLInputElement.idl b/WebCore/html/HTMLInputElement.idl index a9b6d2c..0734e6a 100644 --- a/WebCore/html/HTMLInputElement.idl +++ b/WebCore/html/HTMLInputElement.idl @@ -21,7 +21,6 @@ module html { interface [ - CustomGetOwnPropertySlot, GenerateConstructor, InterfaceUUID=8f388ea3-1c31-4cca-8edd-449d14e222e1, ImplementationUUID=aeb56b87-a90e-4d9d-a4d5-7eec3687c338 @@ -37,6 +36,7 @@ module html { attribute boolean disabled; attribute boolean autofocus; attribute long maxLength; + attribute boolean multiple; attribute [ConvertNullToNullString] DOMString name; attribute boolean readOnly; #if defined(LANGUAGE_OBJECTIVE_C) @@ -58,9 +58,9 @@ module html { // WinIE & FireFox extension: - attribute [CustomGetter] long selectionStart; - attribute [CustomGetter] long selectionEnd; - void setSelectionRange(in long start, in long end); + attribute [Custom] long selectionStart; + attribute [Custom] long selectionEnd; + [Custom] void setSelectionRange(in long start, in long end); #if defined(LANGUAGE_OBJECTIVE_C) // Objective-C extension: diff --git a/WebCore/html/HTMLLegendElement.cpp b/WebCore/html/HTMLLegendElement.cpp index 9f7e645..4092643 100644 --- a/WebCore/html/HTMLLegendElement.cpp +++ b/WebCore/html/HTMLLegendElement.cpp @@ -26,7 +26,6 @@ #include "HTMLLegendElement.h" #include "HTMLNames.h" -#include "RenderLegend.h" #include <wtf/StdLibExtras.h> namespace WebCore { @@ -48,14 +47,6 @@ bool HTMLLegendElement::isFocusable() const return HTMLElement::isFocusable(); } -RenderObject* HTMLLegendElement::createRenderer(RenderArena* arena, RenderStyle* style) -{ - if (style->contentData()) - return RenderObject::createObject(this, style); - - return new (arena) RenderLegend(this); -} - const AtomicString& HTMLLegendElement::type() const { DEFINE_STATIC_LOCAL(const AtomicString, legend, ("legend")); diff --git a/WebCore/html/HTMLLegendElement.h b/WebCore/html/HTMLLegendElement.h index b2ee51a..6d01dce 100644 --- a/WebCore/html/HTMLLegendElement.h +++ b/WebCore/html/HTMLLegendElement.h @@ -34,7 +34,6 @@ public: virtual ~HTMLLegendElement(); virtual bool isFocusable() const; - virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); virtual const AtomicString& type() const; virtual void accessKeyAction(bool sendToAnyElement); diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp index 8d9cabb..df0ed04 100644 --- a/WebCore/html/HTMLMediaElement.cpp +++ b/WebCore/html/HTMLMediaElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,12 +28,15 @@ #if ENABLE(VIDEO) #include "HTMLMediaElement.h" +#include "ContentType.h" #include "CSSHelper.h" #include "CSSPropertyNames.h" #include "CSSValueKeywords.h" #include "Event.h" #include "EventNames.h" #include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoader.h" #include "HTMLDocument.h" #include "HTMLNames.h" #include "HTMLSourceElement.h" @@ -45,10 +48,18 @@ #include "MIMETypeRegistry.h" #include "MediaPlayer.h" #include "Page.h" +#include "ProgressEvent.h" #include "RenderVideo.h" +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "RenderPartObject.h" +#endif #include "TimeRanges.h" +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "Widget.h" +#endif #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> +#include <limits> using namespace std; @@ -61,27 +72,37 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired) , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) + , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) + , m_playbackRate(1.0f) , m_defaultPlaybackRate(1.0f) - , m_networkState(EMPTY) - , m_readyState(DATA_UNAVAILABLE) - , m_begun(false) - , m_loadedFirstFrame(false) - , m_autoplaying(true) - , m_currentLoop(0) + , m_networkState(NETWORK_EMPTY) + , m_readyState(HAVE_NOTHING) , m_volume(1.0f) - , m_muted(false) - , m_paused(true) - , m_seeking(false) , m_currentTimeDuringSeek(0) , m_previousProgress(0) , m_previousProgressTime(numeric_limits<double>::max()) + , m_lastTimeUpdateEventWallTime(0) + , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max()) + , m_loadState(WaitingForSource) + , m_currentSourceNode(0) + , m_player(0) + , m_restrictions(NoRestrictions) + , m_processingMediaPlayerCallback(0) + , m_processingLoad(false) + , m_delayingTheLoadEvent(false) + , m_haveFiredLoadedData(false) + , m_inActiveDocument(true) + , m_autoplaying(true) + , m_muted(false) + , m_paused(true) + , m_seeking(false) , m_sentStalledEvent(false) - , m_bufferingRate(0) - , m_loadNestingLevel(0) - , m_terminateLoadBelowNestingLevel(0) + , m_sentEndEvent(false) , m_pausedInternal(false) - , m_inActiveDocument(true) - , m_player(0) + , m_sendProgressEvents(true) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + , m_needWidgetUpdate(false) +#endif { document()->registerForDocumentActivationCallbacks(this); document()->registerForMediaVolumeCallbacks(this); @@ -104,11 +125,12 @@ void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) const QualifiedName& attrName = attr->name(); if (attrName == srcAttr) { - // 3.14.9.2. - // change to src attribute triggers load() - if (inDocument() && m_networkState == EMPTY) + // don't have a src or any <source> children, trigger load + if (inDocument() && m_loadState == WaitingForSource) scheduleLoad(); - } if (attrName == controlsAttr) { + } +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (attrName == controlsAttr) { if (!isVideo() && attached() && (controls() != (renderer() != 0))) { detach(); attach(); @@ -116,16 +138,30 @@ void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) if (renderer()) renderer()->updateFromElement(); } +#endif } bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style) { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + UNUSED_PARAM(style); + Frame* frame = document()->frame(); + if (!frame) + return false; + + return true; +#else return controls() ? HTMLElement::rendererIsNeeded(style) : false; +#endif } RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + return new (arena) RenderPartObject(this); +#else return new (arena) RenderMedia(this); +#endif } void HTMLMediaElement::insertedIntoDocument() @@ -137,10 +173,8 @@ void HTMLMediaElement::insertedIntoDocument() void HTMLMediaElement::removedFromDocument() { - // FIXME: pause() may invoke load() which seem like a strange thing to do as a side effect - // of removing an element. This might need to be fixed in the spec. - ExceptionCode ec; - pause(ec); + if (m_networkState > NETWORK_EMPTY) + pause(); HTMLElement::removedFromDocument(); } @@ -148,6 +182,10 @@ void HTMLMediaElement::attach() { ASSERT(!attached()); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + m_needWidgetUpdate = true; +#endif + HTMLElement::attach(); if (renderer()) @@ -167,36 +205,53 @@ void HTMLMediaElement::scheduleLoad() m_loadTimer.startOneShot(0); } -void HTMLMediaElement::initAndDispatchProgressEvent(const AtomicString& eventName) +void HTMLMediaElement::scheduleProgressEvent(const AtomicString& eventName) { + if (!m_sendProgressEvents) + return; + + // FIXME: don't schedule timeupdate or progress events unless there are registered listeners + bool totalKnown = m_player && m_player->totalBytesKnown(); unsigned loaded = m_player ? m_player->bytesLoaded() : 0; unsigned total = m_player ? m_player->totalBytes() : 0; - dispatchProgressEvent(eventName, totalKnown, loaded, total); + + RefPtr<ProgressEvent> evt = ProgressEvent::create(eventName, totalKnown, loaded, total); + enqueueEvent(evt); + if (renderer()) renderer()->updateFromElement(); } -void HTMLMediaElement::dispatchEventAsync(const AtomicString& eventName) +void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) +{ + enqueueEvent(Event::create(eventName, false, true)); +} + +void HTMLMediaElement::enqueueEvent(RefPtr<Event> event) { - m_asyncEventsToDispatch.append(eventName); + m_pendingEvents.append(event); if (!m_asyncEventTimer.isActive()) m_asyncEventTimer.startOneShot(0); } -void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) +void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) { - ExceptionCode ec; - load(ec); + Vector<RefPtr<Event> > pendingEvents; + ExceptionCode ec = 0; + + m_pendingEvents.swap(pendingEvents); + unsigned count = pendingEvents.size(); + for (unsigned ndx = 0; ndx < count; ++ndx) + dispatchEvent(pendingEvents[ndx].release(), ec); } -void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) +void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) { - Vector<AtomicString> asyncEventsToDispatch; - m_asyncEventsToDispatch.swap(asyncEventsToDispatch); - unsigned count = asyncEventsToDispatch.size(); - for (unsigned n = 0; n < count; ++n) - dispatchEventForType(asyncEventsToDispatch[n], false, true); + if (m_loadState == LoadingFromSourceElement) + loadNextSourceChild(); + else + loadInternal(); } static String serializeTimeOffset(float time) @@ -260,252 +315,420 @@ HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const return m_networkState; } -float HTMLMediaElement::bufferingRate() +String HTMLMediaElement::canPlayType(const String& mimeType) const { - if (!m_player) - return 0; - return m_bufferingRate; - //return m_player->dataRate(); + MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType)); + String canPlay; + + // 4.8.10.3 + switch (support) + { + case MediaPlayer::IsNotSupported: + canPlay = "no"; + break; + case MediaPlayer::MayBeSupported: + canPlay = "maybe"; + break; + case MediaPlayer::IsSupported: + canPlay = "probably"; + break; + } + + return canPlay; } void HTMLMediaElement::load(ExceptionCode& ec) { - String mediaSrc; - - // 3.14.9.4. Loading the media resource - // 1 - // if an event generated during load() ends up re-entering load(), terminate previous instances - m_loadNestingLevel++; - m_terminateLoadBelowNestingLevel = m_loadNestingLevel; - - m_progressEventTimer.stop(); - m_sentStalledEvent = false; - m_bufferingRate = 0; + if (m_restrictions & RequireUserGestureForLoadRestriction && !processingUserGesture()) + ec = INVALID_STATE_ERR; + else + loadInternal(); +} + +void HTMLMediaElement::loadInternal() +{ + // 1 - If the load() method for this element is already being invoked, then abort these steps. + if (m_processingLoad) + return; + m_processingLoad = true; + stopPeriodicTimers(); m_loadTimer.stop(); + m_sentStalledEvent = false; + m_haveFiredLoadedData = false; + + // 2 - Abort any already-running instance of the resource selection algorithm for this element. + m_currentSourceNode = 0; + + // 3 - If there are any tasks from the media element's media element event task source in + // one of the task queues, then remove those tasks. + m_pendingEvents.clear(); - // 2 - if (m_begun) { - m_begun = false; + // 4 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, set the + // error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED, + // and fire a progress event called abort at the media element. + if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) { m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); - initAndDispatchProgressEvent(eventNames().abortEvent); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - goto end; + + // fire synchronous 'abort' + bool totalKnown = m_player && m_player->totalBytesKnown(); + unsigned loaded = m_player ? m_player->bytesLoaded() : 0; + unsigned total = m_player ? m_player->totalBytes() : 0; + dispatchProgressEvent(eventNames().abortEvent, totalKnown, loaded, total); } - // 3 + // 5 m_error = 0; - m_loadedFirstFrame = false; m_autoplaying = true; + + // 6 + setPlaybackRate(defaultPlaybackRate()); - // 4 - setPlaybackRate(defaultPlaybackRate(), ec); - - // 5 - if (networkState() != EMPTY) { - m_networkState = EMPTY; - m_readyState = DATA_UNAVAILABLE; + // 7 + if (m_networkState != NETWORK_EMPTY) { + m_networkState = NETWORK_EMPTY; + m_readyState = HAVE_NOTHING; m_paused = true; m_seeking = false; if (m_player) { m_player->pause(); m_player->seek(0); } - m_currentLoop = 0; dispatchEventForType(eventNames().emptiedEvent, false, true); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - goto end; } - // 6 - mediaSrc = pickMedia(); + selectMediaResource(); + m_processingLoad = false; +} + +void HTMLMediaElement::selectMediaResource() +{ + // 1 - If the media element has neither a src attribute nor any source element children, run these substeps + String mediaSrc = getAttribute(srcAttr); + if (!mediaSrc && !havePotentialSourceChild()) { + m_loadState = WaitingForSource; + + // 1 - Set the networkState to NETWORK_NO_SOURCE + m_networkState = NETWORK_NO_SOURCE; + + // 2 - While the media element has neither a src attribute nor any source element children, + // wait. (This steps might wait forever.) + + m_delayingTheLoadEvent = false; + return; + } + + // 2 + m_delayingTheLoadEvent = true; + + // 3 + m_networkState = NETWORK_LOADING; + + // 4 + scheduleProgressEvent(eventNames().loadstartEvent); + + // 5 - If the media element has a src attribute, then run these substeps + ContentType contentType(""); + if (!mediaSrc.isEmpty()) { + mediaSrc = document()->completeURL(mediaSrc).string(); + m_loadState = LoadingFromSrcAttr; + loadResource(mediaSrc, contentType); + return; + } + + // Otherwise, the source elements will be used + m_currentSourceNode = 0; + loadNextSourceChild(); +} + +void HTMLMediaElement::loadNextSourceChild() +{ + ContentType contentType(""); + String mediaSrc; + + mediaSrc = nextSourceChild(&contentType); if (mediaSrc.isEmpty()) { - ec = INVALID_STATE_ERR; - goto end; + noneSupported(); + return; } - - // 7 - m_networkState = LOADING; - - // 8 - m_currentSrc = mediaSrc; - - // 9 - m_begun = true; - dispatchProgressEvent(eventNames().loadstartEvent, false, 0, 0); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - goto end; - - // 10, 11, 12, 13 + + m_loadState = LoadingFromSourceElement; + loadResource(mediaSrc, contentType); +} + +void HTMLMediaElement::loadResource(String url, ContentType& contentType) +{ + // The resource fetch algorithm + + m_networkState = NETWORK_LOADING; + + m_currentSrc = url; + + if (m_sendProgressEvents) + startProgressEventTimer(); + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) m_player.clear(); m_player.set(new MediaPlayer(this)); +#else + if (!m_player) + m_player.set(new MediaPlayer(this)); +#endif + updateVolume(); - m_player->load(m_currentSrc); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - goto end; + + m_player->load(m_currentSrc, contentType); if (renderer()) renderer()->updateFromElement(); - - // 14 +} + +void HTMLMediaElement::startProgressEventTimer() +{ + if (m_progressEventTimer.isActive()) + return; + m_previousProgressTime = WTF::currentTime(); m_previousProgress = 0; - if (m_begun) - // 350ms is not magic, it is in the spec! - m_progressEventTimer.startRepeating(0.350); -end: - ASSERT(m_loadNestingLevel); - m_loadNestingLevel--; + // 350ms is not magic, it is in the spec! + m_progressEventTimer.startRepeating(0.350); +} + +void HTMLMediaElement::noneSupported() +{ + stopPeriodicTimers(); + m_loadState = WaitingForSource; + m_currentSourceNode = 0; + + // 3 - Reaching this step indicates that either the URL failed to resolve, or the media + // resource failed to load. Set the error attribute to a new MediaError object whose + // code attribute is set to MEDIA_ERR_NONE_SUPPORTED. + m_error = MediaError::create(MediaError::MEDIA_ERR_NONE_SUPPORTED); + + // 4- Set the element's networkState attribute to the NETWORK_NO_SOURCE value. + m_networkState = NETWORK_NO_SOURCE; + + // 5 - Queue a task to fire a progress event called error at the media element. + scheduleProgressEvent(eventNames().errorEvent); + + // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + m_delayingTheLoadEvent = false; + + // Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource. + + if (isVideo()) + static_cast<HTMLVideoElement*>(this)->updatePosterImage(); + if (renderer()) + renderer()->updateFromElement(); +} + +void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) +{ + // 1 - The user agent should cancel the fetching process. + stopPeriodicTimers(); + m_loadState = WaitingForSource; + + // 2 - Set the error attribute to a new MediaError object whose code attribute is + // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. + m_error = err; + + // 3 - Queue a task to fire a progress event called error at the media element. + scheduleProgressEvent(eventNames().errorEvent); + + // 3 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a + // task to fire a simple event called emptied at the element. + m_networkState = NETWORK_EMPTY; + scheduleEvent(eventNames().emptiedEvent); + + // 4 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + m_delayingTheLoadEvent = false; + + // 5 - Abort the overall resource selection algorithm. + m_currentSourceNode = 0; + } void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) { - if (!m_begun || m_networkState == EMPTY) + beginProcessingMediaPlayerCallback(); + setNetworkState(m_player->networkState()); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) +{ + if (state == MediaPlayer::Empty) { + // just update the cached state and leave, we can't do anything + m_networkState = NETWORK_EMPTY; return; - - m_terminateLoadBelowNestingLevel = m_loadNestingLevel; + } - MediaPlayer::NetworkState state = m_player->networkState(); - - // 3.14.9.4. Loading the media resource - // 14 - if (state == MediaPlayer::LoadFailed) { - //delete m_player; - //m_player = 0; - // FIXME better error handling - m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK); - m_begun = false; - m_progressEventTimer.stop(); - m_bufferingRate = 0; - - initAndDispatchProgressEvent(eventNames().errorEvent); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) + if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { + stopPeriodicTimers(); + + // If we failed while trying to load a <source> element, the movie was never parsed, and there are more + // <source> children, schedule the next one without reporting an error + if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement && havePotentialSourceChild()) { + scheduleLoad(); return; - - m_networkState = EMPTY; - + } + + if (state == MediaPlayer::NetworkError) + mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); + else if (state == MediaPlayer::DecodeError) + mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); + else if (state == MediaPlayer::FormatError) + noneSupported(); + if (isVideo()) static_cast<HTMLVideoElement*>(this)->updatePosterImage(); - dispatchEventForType(eventNames().emptiedEvent, false, true); return; } - - if (state >= MediaPlayer::Loading && m_networkState < LOADING) - m_networkState = LOADING; - - if (state >= MediaPlayer::LoadedMetaData && m_networkState < LOADED_METADATA) { - m_player->seek(effectiveStart()); - m_networkState = LOADED_METADATA; - - dispatchEventForType(eventNames().durationchangeEvent, false, true); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - return; - - dispatchEventForType(eventNames().loadedmetadataEvent, false, true); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - return; + + if (state == MediaPlayer::Idle && m_networkState > NETWORK_IDLE) { + ASSERT(static_cast<ReadyState>(m_player->readyState()) < HAVE_ENOUGH_DATA); + m_networkState = NETWORK_IDLE; + stopPeriodicTimers(); + scheduleProgressEvent(eventNames().suspendEvent); } - - if (state >= MediaPlayer::LoadedFirstFrame && m_networkState < LOADED_FIRST_FRAME) { - m_networkState = LOADED_FIRST_FRAME; - - setReadyState(CAN_SHOW_CURRENT_FRAME); - - if (isVideo()) - static_cast<HTMLVideoElement*>(this)->updatePosterImage(); - - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - return; - - m_loadedFirstFrame = true; - if (renderer()) { - ASSERT(!renderer()->isImage()); - static_cast<RenderVideo*>(renderer())->videoSizeChanged(); - } - - dispatchEventForType(eventNames().loadedfirstframeEvent, false, true); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - return; - - dispatchEventForType(eventNames().canshowcurrentframeEvent, false, true); - if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel) - return; + + if (state == MediaPlayer::Loading && (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)) { + ASSERT(static_cast<ReadyState>(m_player->readyState()) < HAVE_ENOUGH_DATA); + m_networkState = NETWORK_LOADING; + startProgressEventTimer(); } - - // 15 - if (state == MediaPlayer::Loaded && m_networkState < LOADED) { - m_begun = false; - m_networkState = LOADED; + + if (state == MediaPlayer::Loaded && (m_networkState < NETWORK_LOADED || m_networkState == NETWORK_NO_SOURCE)) { + m_networkState = NETWORK_LOADED; m_progressEventTimer.stop(); - m_bufferingRate = 0; - initAndDispatchProgressEvent(eventNames().loadEvent); + + // Check to see if readyState changes need to be dealt with before sending the + // 'load' event so we report 'canplaythrough' first. This is necessary because a + // media engine reports readyState and networkState changes separately + MediaPlayer::ReadyState currentState = m_player->readyState(); + if (static_cast<ReadyState>(currentState) != m_readyState) + setReadyState(currentState); + + scheduleProgressEvent(eventNames().loadEvent); } } void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) { - MediaPlayer::ReadyState state = m_player->readyState(); - setReadyState((ReadyState)state); + beginProcessingMediaPlayerCallback(); + + setReadyState(m_player->readyState()); + + endProcessingMediaPlayerCallback(); } -void HTMLMediaElement::setReadyState(ReadyState state) +void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) { - // 3.14.9.6. The ready states - if (m_readyState == state) + // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it + bool wasPotentiallyPlaying = potentiallyPlaying(); + + ReadyState oldState = m_readyState; + m_readyState = static_cast<ReadyState>(state); + + if (m_readyState == oldState) return; - bool wasActivelyPlaying = activelyPlaying(); - m_readyState = state; - - if (state >= CAN_PLAY) + if (m_readyState >= HAVE_CURRENT_DATA) m_seeking = false; - if (networkState() == EMPTY) + if (m_networkState == NETWORK_EMPTY) return; - - if (state == DATA_UNAVAILABLE) { - dispatchEventForType(eventNames().dataunavailableEvent, false, true); - if (wasActivelyPlaying) { - dispatchEventForType(eventNames().timeupdateEvent, false, true); - dispatchEventForType(eventNames().waitingEvent, false, true); - } - } else if (state == CAN_SHOW_CURRENT_FRAME) { - if (m_loadedFirstFrame) - dispatchEventForType(eventNames().canshowcurrentframeEvent, false, true); - if (wasActivelyPlaying) { - dispatchEventForType(eventNames().timeupdateEvent, false, true); - dispatchEventForType(eventNames().waitingEvent, false, true); + + if (m_seeking && m_readyState < HAVE_CURRENT_DATA) { + // 4.8.10.10, step 9 + scheduleEvent(eventNames().seekingEvent); + m_seeking = false; + } + + if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { + // 4.8.10.9 + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().waitingEvent); + } + + if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) { + scheduleEvent(eventNames().durationchangeEvent); + scheduleEvent(eventNames().loadedmetadataEvent); + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (renderer() && !renderer()->isImage()) { + static_cast<RenderVideo*>(renderer())->videoSizeChanged(); } - } else if (state == CAN_PLAY) { - dispatchEventForType(eventNames().canplayEvent, false, true); - } else if (state == CAN_PLAY_THROUGH) { - dispatchEventForType(eventNames().canplaythroughEvent, false, true); +#endif + m_delayingTheLoadEvent = false; + m_player->seek(0); + } + + // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the + // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA", + // but the event table at the end of the spec says it is sent when: "readyState newly + // increased to HAVE_CURRENT_DATA or greater for the first time" + // We go with the later because it seems useful to count on getting this event + if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) { + m_haveFiredLoadedData = true; + scheduleEvent(eventNames().loadeddataEvent); + } + + bool isPotentiallyPlaying = potentiallyPlaying(); + if (m_readyState <= HAVE_CURRENT_DATA && oldState >= HAVE_FUTURE_DATA) { + if (isPotentiallyPlaying) + scheduleEvent(eventNames().waitingEvent); + } + + if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) { + scheduleEvent(eventNames().canplayEvent); + if (isPotentiallyPlaying) + scheduleEvent(eventNames().playingEvent); + + if (isVideo()) + static_cast<HTMLVideoElement*>(this)->updatePosterImage(); + } + + if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) { + if (oldState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().canplayEvent); + + scheduleEvent(eventNames().canplaythroughEvent); + + if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().playingEvent); + if (m_autoplaying && m_paused && autoplay()) { m_paused = false; - dispatchEventForType(eventNames().playEvent, false, true); + scheduleEvent(eventNames().playEvent); + scheduleEvent(eventNames().playingEvent); } + + if (isVideo()) + static_cast<HTMLVideoElement*>(this)->updatePosterImage(); } + updatePlayState(); } void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) { ASSERT(m_player); + if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) + return; + unsigned progress = m_player->bytesLoaded(); double time = WTF::currentTime(); double timedelta = time - m_previousProgressTime; - if (timedelta) - m_bufferingRate = (float)(0.8 * m_bufferingRate + 0.2 * ((float)(progress - m_previousProgress)) / timedelta); - + if (progress == m_previousProgress) { if (timedelta > 3.0 && !m_sentStalledEvent) { - m_bufferingRate = 0; - initAndDispatchProgressEvent(eventNames().stalledEvent); + scheduleProgressEvent(eventNames().stalledEvent); m_sentStalledEvent = true; } } else { - initAndDispatchProgressEvent(eventNames().progressEvent); + scheduleProgressEvent(eventNames().progressEvent); m_previousProgress = progress; m_previousProgressTime = time; m_sentStalledEvent = false; @@ -514,52 +737,45 @@ void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) void HTMLMediaElement::seek(float time, ExceptionCode& ec) { - // 3.14.9.8. Seeking + // 4.8.10.10. Seeking // 1 - if (networkState() < LOADED_METADATA) { + if (m_readyState == HAVE_NOTHING || !m_player) { ec = INVALID_STATE_ERR; return; } - + // 2 - float minTime; - if (currentLoop() == 0) - minTime = effectiveStart(); - else - minTime = effectiveLoopStart(); - + time = min(time, duration()); + // 3 - float maxTime = currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd(); - + time = max(time, 0.0f); + // 4 - time = min(time, maxTime); - - // 5 - time = max(time, minTime); - - // 6 RefPtr<TimeRanges> seekableRanges = seekable(); if (!seekableRanges->contain(time)) { ec = INDEX_SIZE_ERR; return; } - // 7 + // avoid generating events when the time won't actually change + float now = currentTime(); + if (time == now) + return; + + // 5 m_currentTimeDuringSeek = time; - // 8 + // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed m_seeking = true; - - // 9 - dispatchEventForType(eventNames().timeupdateEvent, false, true); - + + // 7 + scheduleTimeupdateEvent(false); + + // 8 - this is covered, if necessary, when the engine signals a readystate change + // 10 - // As soon as the user agent has established whether or not the media data for the new playback position is available, - // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false. - if (m_player) { - m_player->setEndTime(maxTime); - m_player->seek(time); - } + m_player->seek(time); + m_sentEndEvent = false; } HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const @@ -589,7 +805,10 @@ void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) float HTMLMediaElement::duration() const { - return m_player ? m_player->duration() : 0; + if (m_readyState >= HAVE_METADATA) + return m_player->duration(); + + return numeric_limits<float>::quiet_NaN(); } bool HTMLMediaElement::paused() const @@ -602,15 +821,11 @@ float HTMLMediaElement::defaultPlaybackRate() const return m_defaultPlaybackRate; } -void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode& ec) +void HTMLMediaElement::setDefaultPlaybackRate(float rate) { - if (rate == 0.0f) { - ec = NOT_SUPPORTED_ERR; - return; - } if (m_defaultPlaybackRate != rate) { m_defaultPlaybackRate = rate; - dispatchEventAsync(eventNames().ratechangeEvent); + scheduleEvent(eventNames().ratechangeEvent); } } @@ -619,16 +834,14 @@ float HTMLMediaElement::playbackRate() const return m_player ? m_player->rate() : 0; } -void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode& ec) +void HTMLMediaElement::setPlaybackRate(float rate) { - if (rate == 0.0f) { - ec = NOT_SUPPORTED_ERR; - return; + if (m_playbackRate != rate) { + m_playbackRate = rate; + scheduleEvent(eventNames().ratechangeEvent); } - if (m_player && m_player->rate() != rate) { + if (m_player && potentiallyPlaying() && m_player->rate() != rate) m_player->setRate(rate); - dispatchEventAsync(eventNames().ratechangeEvent); - } } bool HTMLMediaElement::ended() const @@ -646,126 +859,85 @@ void HTMLMediaElement::setAutoplay(bool b) setBooleanAttribute(autoplayAttr, b); } -void HTMLMediaElement::play(ExceptionCode& ec) +void HTMLMediaElement::play() { - // 3.14.9.7. Playing the media resource - if (!m_player || networkState() == EMPTY) { - ec = 0; - load(ec); - if (ec) - return; - } - ExceptionCode unused; - if (endedPlayback()) { - m_currentLoop = 0; - seek(effectiveStart(), unused); - } - setPlaybackRate(defaultPlaybackRate(), unused); - - if (m_paused) { - m_paused = false; - dispatchEventAsync(eventNames().playEvent); - } + if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture()) + return; - m_autoplaying = false; - - updatePlayState(); + playInternal(); } -void HTMLMediaElement::pause(ExceptionCode& ec) +void HTMLMediaElement::playInternal() { - // 3.14.9.7. Playing the media resource - if (!m_player || networkState() == EMPTY) { - ec = 0; - load(ec); - if (ec) - return; - } + // 4.8.10.9. Playing the media resource + if (!m_player || m_networkState == NETWORK_EMPTY) + scheduleLoad(); - if (!m_paused) { - m_paused = true; - dispatchEventAsync(eventNames().timeupdateEvent); - dispatchEventAsync(eventNames().pauseEvent); + if (endedPlayback()) { + ExceptionCode unused; + seek(0, unused); } + + setPlaybackRate(defaultPlaybackRate()); + + if (m_paused) { + m_paused = false; + scheduleEvent(eventNames().playEvent); + if (m_readyState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().waitingEvent); + else if (m_readyState >= HAVE_FUTURE_DATA) + scheduleEvent(eventNames().playingEvent); + } m_autoplaying = false; - - updatePlayState(); -} -unsigned HTMLMediaElement::playCount() const -{ - bool ok; - unsigned count = getAttribute(playcountAttr).string().toUInt(&ok); - return (count > 0 && ok) ? count : 1; + updatePlayState(); } -void HTMLMediaElement::setPlayCount(unsigned count, ExceptionCode& ec) +void HTMLMediaElement::pause() { - if (!count) { - ec = INDEX_SIZE_ERR; + if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture()) return; - } - setAttribute(playcountAttr, String::number(count)); - checkIfSeekNeeded(); -} -float HTMLMediaElement::start() const -{ - return getTimeOffsetAttribute(startAttr, 0); + pauseInternal(); } -void HTMLMediaElement::setStart(float time) -{ - setTimeOffsetAttribute(startAttr, time); - checkIfSeekNeeded(); -} -float HTMLMediaElement::end() const -{ - return getTimeOffsetAttribute(endAttr, std::numeric_limits<float>::infinity()); -} - -void HTMLMediaElement::setEnd(float time) -{ - setTimeOffsetAttribute(endAttr, time); - checkIfSeekNeeded(); -} - -float HTMLMediaElement::loopStart() const -{ - return getTimeOffsetAttribute(loopstartAttr, start()); -} - -void HTMLMediaElement::setLoopStart(float time) +void HTMLMediaElement::pauseInternal() { - setTimeOffsetAttribute(loopstartAttr, time); - checkIfSeekNeeded(); -} + // 4.8.10.9. Playing the media resource + if (!m_player || m_networkState == NETWORK_EMPTY) + scheduleLoad(); -float HTMLMediaElement::loopEnd() const -{ - return getTimeOffsetAttribute(loopendAttr, end()); -} + m_autoplaying = false; + + if (!m_paused) { + m_paused = true; + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().pauseEvent); + } -void HTMLMediaElement::setLoopEnd(float time) -{ - setTimeOffsetAttribute(loopendAttr, time); - checkIfSeekNeeded(); + updatePlayState(); } -unsigned HTMLMediaElement::currentLoop() const +bool HTMLMediaElement::loop() const { - return m_currentLoop; + return hasAttribute(loopAttr); } -void HTMLMediaElement::setCurrentLoop(unsigned currentLoop) +void HTMLMediaElement::setLoop(bool b) { - m_currentLoop = currentLoop; + setBooleanAttribute(loopAttr, b); } bool HTMLMediaElement::controls() const { + Frame* frame = document()->frame(); + + // always show controls when scripting is disabled + if (frame && !frame->script()->isEnabled()) + return true; + return hasAttribute(controlsAttr); } @@ -789,7 +961,7 @@ void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) if (m_volume != vol) { m_volume = vol; updateVolume(); - dispatchEventAsync(eventNames().volumechangeEvent); + scheduleEvent(eventNames().volumechangeEvent); } } @@ -803,110 +975,224 @@ void HTMLMediaElement::setMuted(bool muted) if (m_muted != muted) { m_muted = muted; updateVolume(); - dispatchEventAsync(eventNames().volumechangeEvent); + scheduleEvent(eventNames().volumechangeEvent); } } -bool HTMLMediaElement::canPlay() const +void HTMLMediaElement::togglePlayState() { - return paused() || ended() || networkState() < LOADED_METADATA; + // We can safely call the internal play/pause methods, which don't check restrictions, because + // this method is only called from the built-in media controller + if (canPlay()) + playInternal(); + else + pauseInternal(); } -String HTMLMediaElement::pickMedia() +void HTMLMediaElement::beginScrubbing() { - // 3.14.9.2. Location of the media resource - String mediaSrc = getAttribute(srcAttr); - if (mediaSrc.isEmpty()) { - for (Node* n = firstChild(); n; n = n->nextSibling()) { - if (n->hasTagName(sourceTag)) { - HTMLSourceElement* source = static_cast<HTMLSourceElement*>(n); - if (!source->hasAttribute(srcAttr)) - continue; - if (source->hasAttribute(mediaAttr)) { - MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); - RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); - if (!screenEval.eval(media.get())) - continue; - } - if (source->hasAttribute(typeAttr)) { - String type = source->type().stripWhiteSpace(); - - // "type" can have parameters after a semi-colon, strip them before checking with the type registry - int semi = type.find(';'); - if (semi != -1) - type = type.left(semi).stripWhiteSpace(); - - if (!MIMETypeRegistry::isSupportedMediaMIMEType(type)) - continue; - } - mediaSrc = source->src().string(); - break; - } + if (!paused()) { + if (ended()) { + // Because a media element stays in non-paused state when it reaches end, playback resumes + // when the slider is dragged from the end to another position unless we pause first. Do + // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. + pause(); + } else { + // Not at the end but we still want to pause playback so the media engine doesn't try to + // continue playing during scrubbing. Pause without generating an event as we will + // unpause after scrubbing finishes. + setPausedInternal(true); } } - if (!mediaSrc.isEmpty()) - mediaSrc = document()->completeURL(mediaSrc).string(); - return mediaSrc; } -void HTMLMediaElement::checkIfSeekNeeded() +void HTMLMediaElement::endScrubbing() { - // 3.14.9.5. Offsets into the media resource - // 1 - if (playCount() <= m_currentLoop) - m_currentLoop = playCount() - 1; - - // 2 - if (networkState() <= LOADING) + if (m_pausedInternal) + setPausedInternal(false); +} + +// The spec says to fire periodic timeupdate events (those sent while playing) every +// "15 to 250ms", we choose the slowest frequency +static const double maxTimeupdateEventFrequency = 0.25; + +void HTMLMediaElement::startPlaybackProgressTimer() +{ + if (m_playbackProgressTimer.isActive()) + return; + + m_previousProgressTime = WTF::currentTime(); + m_previousProgress = 0; + m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); +} + +void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) +{ + ASSERT(m_player); + if (!m_playbackRate) return; + + scheduleTimeupdateEvent(true); - // 3 - ExceptionCode ec; - float time = currentTime(); - if (!m_currentLoop && time < effectiveStart()) - seek(effectiveStart(), ec); + // FIXME: deal with cue ranges here +} - // 4 - if (m_currentLoop && time < effectiveLoopStart()) - seek(effectiveLoopStart(), ec); +void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) +{ + double now = WTF::currentTime(); + double timedelta = now - m_lastTimeUpdateEventWallTime; + + // throttle the periodic events + if (periodicEvent && timedelta < maxTimeupdateEventFrequency) + return; + + // Some media engines make multiple "time changed" callbacks at the same time, but we only want one + // event at a given time so filter here + float movieTime = m_player ? m_player->currentTime() : 0; + if (movieTime != m_lastTimeUpdateEventMovieTime) { + scheduleEvent(eventNames().timeupdateEvent); + m_lastTimeUpdateEventWallTime = now; + m_lastTimeUpdateEventMovieTime = movieTime; + } +} + +bool HTMLMediaElement::canPlay() const +{ + return paused() || ended() || m_readyState < HAVE_METADATA; +} + +bool HTMLMediaElement::havePotentialSourceChild() +{ + // Stash the current <source> node so we can restore it after checking + // to see there is another potential + Node* currentSourceNode = m_currentSourceNode; + String nextUrl = nextSourceChild(); + m_currentSourceNode = currentSourceNode; + + return !nextUrl.isEmpty(); +} + +String HTMLMediaElement::nextSourceChild(ContentType *contentType) +{ + String mediaSrc; + bool lookingForPreviousNode = m_currentSourceNode; + + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (!node->hasTagName(sourceTag)) + continue; + + if (lookingForPreviousNode) { + if (m_currentSourceNode == node) + lookingForPreviousNode = false; + continue; + } - // 5 - if (m_currentLoop < playCount() - 1 && time > effectiveLoopEnd()) { - seek(effectiveLoopStart(), ec); - m_currentLoop++; + HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node); + if (!source->hasAttribute(srcAttr)) + continue; + if (source->hasAttribute(mediaAttr)) { + MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); + RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); + if (!screenEval.eval(media.get())) + continue; + } + if (source->hasAttribute(typeAttr)) { + ContentType type(source->type()); + if (!MediaPlayer::supportsType(type)) + continue; + + // return type with all parameters in place so the media engine can use them + if (contentType) + *contentType = type; + } + mediaSrc = source->src().string(); + m_currentSourceNode = node; + break; } - - // 6 - if (m_currentLoop == playCount() - 1 && time > effectiveEnd()) - seek(effectiveEnd(), ec); - updatePlayState(); + if (!mediaSrc.isEmpty()) + mediaSrc = document()->completeURL(mediaSrc).string(); + + return mediaSrc; } void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) { - if (readyState() >= CAN_PLAY) + beginProcessingMediaPlayerCallback(); + + if (m_readyState >= HAVE_CURRENT_DATA && m_seeking) { + scheduleEvent(eventNames().seekedEvent); m_seeking = false; - - if (m_currentLoop < playCount() - 1 && currentTime() >= effectiveLoopEnd()) { - ExceptionCode ec; - seek(effectiveLoopStart(), ec); - m_currentLoop++; - dispatchEventForType(eventNames().timeupdateEvent, false, true); } - if (m_currentLoop == playCount() - 1 && currentTime() >= effectiveEnd()) { - dispatchEventForType(eventNames().timeupdateEvent, false, true); - dispatchEventForType(eventNames().endedEvent, false, true); + float now = currentTime(); + float dur = duration(); + if (now >= dur) { + if (loop()) { + ExceptionCode ignoredException; + m_sentEndEvent = false; + seek(0, ignoredException); + } else { + if (!m_sentEndEvent) { + m_sentEndEvent = true; + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().endedEvent); + } + } } + else + m_sentEndEvent = false; updatePlayState(); + endProcessingMediaPlayerCallback(); } void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) { + beginProcessingMediaPlayerCallback(); if (renderer()) renderer()->repaint(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + updateVolume(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + scheduleEvent(eventNames().durationchangeEvent); +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (renderer()) { + renderer()->updateFromElement(); + if (!renderer()->isImage()) + static_cast<RenderVideo*>(renderer())->videoSizeChanged(); + } +#endif + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + // Stash the rate in case the one we tried to set isn't what the engine is + // using (eg. it can't handle the rate we set) + m_playbackRate = m_player->rate(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (renderer() && !renderer()->isImage()) + static_cast<RenderVideo*>(renderer())->videoSizeChanged(); +#endif + endProcessingMediaPlayerCallback(); } PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const @@ -931,53 +1217,45 @@ PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const return TimeRanges::create(0, m_player->maxTimeSeekable()); } -float HTMLMediaElement::effectiveStart() const +bool HTMLMediaElement::potentiallyPlaying() const { - if (!m_player) - return 0; - return min(start(), m_player->duration()); + return !paused() && m_readyState >= HAVE_FUTURE_DATA && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); } -float HTMLMediaElement::effectiveEnd() const -{ - if (!m_player) - return 0; - return min(max(end(), max(start(), loopStart())), m_player->duration()); -} - -float HTMLMediaElement::effectiveLoopStart() const +bool HTMLMediaElement::endedPlayback() const { - if (!m_player) - return 0; - return min(loopStart(), m_player->duration()); + return m_player && m_readyState >= HAVE_METADATA && currentTime() >= duration() && !loop(); } -float HTMLMediaElement::effectiveLoopEnd() const +bool HTMLMediaElement::stoppedDueToErrors() const { - if (!m_player) - return 0; - return min(max(start(), max(loopStart(), loopEnd())), m_player->duration()); + if (m_readyState >= HAVE_METADATA && m_error) { + RefPtr<TimeRanges> seekableRanges = seekable(); + if (!seekableRanges->contain(currentTime())) + return true; + } + + return false; } -bool HTMLMediaElement::activelyPlaying() const +bool HTMLMediaElement::pausedForUserInteraction() const { - return !paused() && readyState() >= CAN_PLAY && !endedPlayback(); // && !stoppedDueToErrors() && !pausedForUserInteraction(); +// return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] + return false; } -bool HTMLMediaElement::endedPlayback() const -{ - return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1; -} - void HTMLMediaElement::updateVolume() { if (!m_player) return; - Page* page = document()->page(); - float volumeMultiplier = page ? page->mediaVolume() : 1; - - m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier); + // Avoid recursion when the player reports volume changes. + if (!processingMediaPlayerCallback()) { + Page* page = document()->page(); + float volumeMultiplier = page ? page->mediaVolume() : 1; + + m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier); + } if (renderer()) renderer()->updateFromElement(); @@ -987,20 +1265,26 @@ void HTMLMediaElement::updatePlayState() { if (!m_player) return; - + if (m_pausedInternal) { if (!m_player->paused()) m_player->pause(); + m_playbackProgressTimer.stop(); return; } - m_player->setEndTime(currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd()); - - bool shouldBePlaying = activelyPlaying() && currentTime() < effectiveEnd(); - if (shouldBePlaying && m_player->paused()) + bool shouldBePlaying = potentiallyPlaying(); + bool playerPaused = m_player->paused(); + if (shouldBePlaying && playerPaused) { + // Set rate before calling play in case the rate was set before the media engine wasn't setup. + // The media engine should just stash the rate since it isn't already playing. + m_player->setRate(m_playbackRate); m_player->play(); - else if (!shouldBePlaying && !m_player->paused()) + startPlaybackProgressTimer(); + } else if (!shouldBePlaying && !playerPaused) { m_player->pause(); + m_playbackProgressTimer.stop(); + } if (renderer()) renderer()->updateFromElement(); @@ -1012,25 +1296,50 @@ void HTMLMediaElement::setPausedInternal(bool b) updatePlayState(); } -void HTMLMediaElement::documentWillBecomeInactive() +void HTMLMediaElement::stopPeriodicTimers() +{ + m_progressEventTimer.stop(); + m_playbackProgressTimer.stop(); +} + +void HTMLMediaElement::userCancelledLoad() { - // 3.14.9.4. Loading the media resource - // 14 - if (m_begun) { - // For simplicity cancel the incomplete load by deleting the player + if (m_networkState != NETWORK_EMPTY) { + + // If the media data fetching process is aborted by the user: + + // 1 - The user agent should cancel the fetching process. +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) m_player.clear(); - m_progressEventTimer.stop(); +#endif + stopPeriodicTimers(); + // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORT. m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); - m_begun = false; - initAndDispatchProgressEvent(eventNames().abortEvent); - if (m_networkState >= LOADING) { - m_networkState = EMPTY; - m_readyState = DATA_UNAVAILABLE; - dispatchEventForType(eventNames().emptiedEvent, false, true); + + // 3 - Queue a task to fire a progress event called abort at the media element. + scheduleProgressEvent(eventNames().abortEvent); + + // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the + // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a + // simple event called emptied at the element. Otherwise, set set the element's networkState + // attribute to the NETWORK_IDLE value. + if (m_networkState >= NETWORK_LOADING) { + m_networkState = NETWORK_EMPTY; + m_readyState = HAVE_NOTHING; + scheduleEvent(eventNames().emptiedEvent); } + + // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + m_delayingTheLoadEvent = false; } +} + +void HTMLMediaElement::documentWillBecomeInactive() +{ m_inActiveDocument = false; + userCancelledLoad(); + // Stop the playback without generating events setPausedInternal(true); @@ -1045,6 +1354,8 @@ void HTMLMediaElement::documentDidBecomeActive() if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { // Restart the load if it was aborted in the middle by moving the document to the page cache. + // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to + // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). // This behavior is not specified but it seems like a sensible thing to do. ExceptionCode ec; load(ec); @@ -1061,13 +1372,78 @@ void HTMLMediaElement::mediaVolumeDidChange() void HTMLMediaElement::defaultEventHandler(Event* event) { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + RenderObject* r = renderer(); + if (!r || !r->isWidget()) + return; + + Widget* widget = static_cast<RenderWidget*>(r)->widget(); + if (widget) + widget->handleEvent(event); +#else if (renderer() && renderer()->isMedia()) static_cast<RenderMedia*>(renderer())->forwardEvent(event); if (event->defaultHandled()) return; HTMLElement::defaultEventHandler(event); +#endif } +bool HTMLMediaElement::processingUserGesture() const +{ + Frame* frame = document()->frame(); + FrameLoader* loader = frame ? frame->loader() : 0; + + // return 'true' for safety if we don't know the answer + return loader ? loader->userGestureHint() : true; +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) +{ + if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { + ExceptionCode ec; + togglePlayState(ec); + return; + } + + if (m_player) + m_player->deliverNotification(notification); +} + +void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) +{ + if (m_player) + m_player->setMediaPlayerProxy(proxy); +} + +String HTMLMediaElement::initialURL() +{ + String initialSrc = mediaSrc = getAttribute(srcAttr); + + if (initialSrc.isEmpty()) + initialSrc = nextSourceChild(); + + if (!initialSrc.isEmpty()) + initialSrc = document()->completeURL(initialSrc).string(); + + m_currentSrc = initialSrc; + + return initialSrc; +} + +void HTMLMediaElement::finishParsingChildren() +{ + HTMLElement::finishParsingChildren(); + if (!m_player) + m_player.set(new MediaPlayer(this)); + + document()->updateRendering(); + if (m_needWidgetUpdate && renderer()) + static_cast<RenderPartObject*>(renderer())->updateWidget(true); +} +#endif + } #endif diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h index 6dd562c..ab0ab11 100644 --- a/WebCore/html/HTMLMediaElement.h +++ b/WebCore/html/HTMLMediaElement.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,13 +31,17 @@ #include "HTMLElement.h" #include "MediaPlayer.h" #include "Timer.h" -#include "VoidCallback.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "MediaPlayerProxy.h" +#endif namespace WebCore { +class Event; class MediaError; -class TimeRanges; class KURL; +class TimeRanges; class HTMLMediaElement : public HTMLElement, public MediaPlayerClient { public: @@ -58,6 +62,7 @@ public: MediaPlayer* player() const { return m_player.get(); } virtual bool isVideo() const { return false; } + virtual bool hasVideo() const { return false; } void scheduleLoad(); @@ -76,15 +81,16 @@ public: KURL src() const; void setSrc(const String&); String currentSrc() const; - - enum NetworkState { EMPTY, LOADING, LOADED_METADATA, LOADED_FIRST_FRAME, LOADED }; + + enum NetworkState { NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_LOADED, NETWORK_NO_SOURCE }; NetworkState networkState() const; - float bufferingRate(); PassRefPtr<TimeRanges> buffered() const; void load(ExceptionCode&); + + String canPlayType(const String& mimeType) const; // ready state - enum ReadyState { DATA_UNAVAILABLE, CAN_SHOW_CURRENT_FRAME, CAN_PLAY, CAN_PLAY_THROUGH }; + enum ReadyState { HAVE_NOTHING, HAVE_METADATA, HAVE_CURRENT_DATA, HAVE_FUTURE_DATA, HAVE_ENOUGH_DATA }; ReadyState readyState() const; bool seeking() const; @@ -94,31 +100,19 @@ public: float duration() const; bool paused() const; float defaultPlaybackRate() const; - void setDefaultPlaybackRate(float, ExceptionCode&); + void setDefaultPlaybackRate(float); float playbackRate() const; - void setPlaybackRate(float, ExceptionCode&); + void setPlaybackRate(float); PassRefPtr<TimeRanges> played() const; PassRefPtr<TimeRanges> seekable() const; bool ended() const; bool autoplay() const; void setAutoplay(bool b); - void play(ExceptionCode&); - void pause(ExceptionCode&); - -// looping - float start() const; - void setStart(float time); - float end() const; - void setEnd(float time); - float loopStart() const; - void setLoopStart(float time); - float loopEnd() const; - void setLoopEnd(float time); - unsigned playCount() const; - void setPlayCount(unsigned, ExceptionCode&); - unsigned currentLoop() const; - void setCurrentLoop(unsigned); - + bool loop() const; + void setLoop(bool b); + void play(); + void pause(); + // controls bool controls() const; void setControls(bool); @@ -126,9 +120,20 @@ public: void setVolume(float, ExceptionCode&); bool muted() const; void setMuted(bool); + void togglePlayState(); + void beginScrubbing(); + void endScrubbing(); bool canPlay() const; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + void setNeedWidgetUpdate(bool needWidgetUpdate) { m_needWidgetUpdate = needWidgetUpdate; } + void deliverNotification(MediaPlayerProxyNotificationType notification); + void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); + String initialURL(); + virtual void finishParsingChildren(); +#endif + protected: float getTimeOffsetAttribute(const QualifiedName&, float valueOnError) const; void setTimeOffsetAttribute(const QualifiedName&, float value); @@ -137,73 +142,136 @@ protected: virtual void documentDidBecomeActive(); virtual void mediaVolumeDidChange(); - void initAndDispatchProgressEvent(const AtomicString& eventName); - void dispatchEventAsync(const AtomicString& eventName); - - void setReadyState(ReadyState); + void setReadyState(MediaPlayer::ReadyState); + void setNetworkState(MediaPlayer::NetworkState); private: // MediaPlayerObserver virtual void mediaPlayerNetworkStateChanged(MediaPlayer*); virtual void mediaPlayerReadyStateChanged(MediaPlayer*); virtual void mediaPlayerTimeChanged(MediaPlayer*); virtual void mediaPlayerRepaint(MediaPlayer*); + virtual void mediaPlayerVolumeChanged(MediaPlayer*); + virtual void mediaPlayerDurationChanged(MediaPlayer*); + virtual void mediaPlayerRateChanged(MediaPlayer*); + virtual void mediaPlayerSizeChanged(MediaPlayer*); private: void loadTimerFired(Timer<HTMLMediaElement>*); void asyncEventTimerFired(Timer<HTMLMediaElement>*); void progressEventTimerFired(Timer<HTMLMediaElement>*); - void seek(float time, ExceptionCode& ec); + void playbackProgressTimerFired(Timer<HTMLMediaElement>*); + void startPlaybackProgressTimer(); + void startProgressEventTimer(); + void stopPeriodicTimers(); + + void seek(float time, ExceptionCode&); void checkIfSeekNeeded(); - String pickMedia(); + void scheduleTimeupdateEvent(bool periodicEvent); + void scheduleProgressEvent(const AtomicString& eventName); + void scheduleEvent(const AtomicString& eventName); + void enqueueEvent(RefPtr<Event> event); + + // loading + void selectMediaResource(); + void loadResource(String url, ContentType& contentType); + void loadNextSourceChild(); + void userCancelledLoad(); + String nextSourceChild(ContentType* contentType = 0); + bool havePotentialSourceChild(); + void noneSupported(); + void mediaEngineError(PassRefPtr<MediaError> err); + + // These "internal" functions do not check user gesture restrictions. + void loadInternal(); + void playInternal(); + void pauseInternal(); + + bool processingUserGesture() const; + bool processingMediaPlayerCallback() const { return m_processingMediaPlayerCallback > 0; } + void beginProcessingMediaPlayerCallback() { ++m_processingMediaPlayerCallback; } + void endProcessingMediaPlayerCallback() { ASSERT(m_processingMediaPlayerCallback); --m_processingMediaPlayerCallback; } + void updateVolume(); void updatePlayState(); - float effectiveStart() const; - float effectiveEnd() const; - float effectiveLoopStart() const; - float effectiveLoopEnd() const; - bool activelyPlaying() const; + bool potentiallyPlaying() const; bool endedPlayback() const; - + bool stoppedDueToErrors() const; + bool pausedForUserInteraction() const; + + // Restrictions to change default behaviors. This is a effectively a compile time choice at the moment + // because there are no accessor methods. + enum BehaviorRestrictions + { + NoRestrictions = 0, + RequireUserGestureForLoadRestriction = 1 << 0, + RequireUserGestureForRateChangeRestriction = 1 << 1, + }; + protected: Timer<HTMLMediaElement> m_loadTimer; Timer<HTMLMediaElement> m_asyncEventTimer; Timer<HTMLMediaElement> m_progressEventTimer; - Vector<AtomicString> m_asyncEventsToDispatch; + Timer<HTMLMediaElement> m_playbackProgressTimer; + Vector<RefPtr<Event> > m_pendingEvents; + float m_playbackRate; float m_defaultPlaybackRate; NetworkState m_networkState; ReadyState m_readyState; String m_currentSrc; RefPtr<MediaError> m_error; - - bool m_begun; - bool m_loadedFirstFrame; - bool m_autoplaying; - - unsigned m_currentLoop; + float m_volume; - bool m_muted; - - bool m_paused; - bool m_seeking; - float m_currentTimeDuringSeek; unsigned m_previousProgress; double m_previousProgressTime; - bool m_sentStalledEvent; - - float m_bufferingRate; + + // the last time a timeupdate event was sent (wall clock) + double m_lastTimeUpdateEventWallTime; + + // the last time a timeupdate event was sent in movie time + float m_lastTimeUpdateEventMovieTime; - unsigned m_loadNestingLevel; - unsigned m_terminateLoadBelowNestingLevel; + // loading state + enum LoadState { WaitingForSource, LoadingFromSrcAttr, LoadingFromSourceElement }; + LoadState m_loadState; + Node *m_currentSourceNode; - bool m_pausedInternal; - bool m_inActiveDocument; - OwnPtr<MediaPlayer> m_player; + + BehaviorRestrictions m_restrictions; + + // counter incremented while processing a callback from the media player, so we can avoid + // calling the media engine recursively + int m_processingMediaPlayerCallback; + + bool m_processingLoad : 1; + bool m_delayingTheLoadEvent : 1; + bool m_haveFiredLoadedData : 1; + bool m_inActiveDocument : 1; + bool m_autoplaying : 1; + bool m_muted : 1; + bool m_paused : 1; + bool m_seeking : 1; + + // data has not been loaded since sending a "stalled" event + bool m_sentStalledEvent : 1; + + // time has not changed since sending an "ended" event + bool m_sentEndEvent : 1; + + bool m_pausedInternal : 1; + + // Not all media engines provide enough information about a file to be able to + // support progress events so setting m_sendProgressEvents disables them + bool m_sendProgressEvents : 1; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + bool m_needWidgetUpdate : 1; +#endif }; } //namespace diff --git a/WebCore/html/HTMLMediaElement.idl b/WebCore/html/HTMLMediaElement.idl index 1e3eb90..931980b 100644 --- a/WebCore/html/HTMLMediaElement.idl +++ b/WebCore/html/HTMLMediaElement.idl @@ -33,22 +33,24 @@ interface [GenerateConstructor, Conditional=VIDEO] HTMLMediaElement : HTMLElemen attribute DOMString src; readonly attribute DOMString currentSrc; - const unsigned short EMPTY = 0; - const unsigned short LOADING = 1; - const unsigned short LOADED_METADATA = 2; - const unsigned short LOADED_FIRST_FRAME = 3; - const unsigned short LOADED = 4; + const unsigned short NETWORK_EMPTY = 0; + const unsigned short NETWORK_IDLE = 1; + const unsigned short NETWORK_LOADING = 2; + const unsigned short NETWORK_LOADED = 3; + const unsigned short NETWORK_NO_SOURCE = 4; readonly attribute unsigned short networkState; - readonly attribute float bufferingRate; + readonly attribute TimeRanges buffered; void load() raises (DOMException); + DOMString canPlayType(in DOMString type); // ready state - const unsigned short DATA_UNAVAILABLE = 0; - const unsigned short CAN_SHOW_CURRENT_FRAME = 1; - const unsigned short CAN_PLAY = 2; - const unsigned short CAN_PLAY_THROUGH = 3; + const unsigned short HAVE_NOTHING = 0; + const unsigned short HAVE_METADATA = 1; + const unsigned short HAVE_CURRENT_DATA = 2; + const unsigned short HAVE_FUTURE_DATA = 3; + const unsigned short HAVE_ENOUGH_DATA = 4; readonly attribute unsigned short readyState; readonly attribute boolean seeking; @@ -57,27 +59,15 @@ interface [GenerateConstructor, Conditional=VIDEO] HTMLMediaElement : HTMLElemen setter raises (DOMException); readonly attribute float duration; readonly attribute boolean paused; - attribute float defaultPlaybackRate - setter raises (DOMException); - attribute float playbackRate - setter raises (DOMException); + attribute float defaultPlaybackRate; + attribute float playbackRate; readonly attribute TimeRanges played; readonly attribute TimeRanges seekable; readonly attribute boolean ended; attribute boolean autoplay; - void play() - raises (DOMException); - void pause() - raises (DOMException); - - // looping - attribute float start; - attribute float end; - attribute float loopStart; - attribute float loopEnd; - attribute unsigned long playCount - setter raises (DOMException); - attribute unsigned long currentLoop; + attribute boolean loop; + void play(); + void pause(); // controls attribute boolean controls; diff --git a/WebCore/html/HTMLObjectElement.cpp b/WebCore/html/HTMLObjectElement.cpp index 6f85d18..a41e037 100644 --- a/WebCore/html/HTMLObjectElement.cpp +++ b/WebCore/html/HTMLObjectElement.cpp @@ -119,14 +119,15 @@ void HTMLObjectElement::parseMappedAttribute(MappedAttribute *attr) bool HTMLObjectElement::rendererIsNeeded(RenderStyle* style) { - if (m_useFallbackContent || isImageType()) - return HTMLPlugInElement::rendererIsNeeded(style); - Frame* frame = document()->frame(); if (!frame) return false; - return true; + // Temporary Workaround for Gears plugin - see bug 24215 for details and bug 24346 to track removal. + // Gears expects the plugin to be instantiated even if display:none is set + // for the object element. + bool isGearsPlugin = equalIgnoringCase(getAttribute(typeAttr), "application/x-googlegears"); + return isGearsPlugin || HTMLPlugInElement::rendererIsNeeded(style); } RenderObject *HTMLObjectElement::createRenderer(RenderArena* arena, RenderStyle* style) @@ -156,7 +157,7 @@ void HTMLObjectElement::attach() return; if (renderer()) - static_cast<RenderImage*>(renderer())->setCachedImage(m_imageLoader->image()); + toRenderImage(renderer())->setCachedImage(m_imageLoader->image()); } } diff --git a/WebCore/html/HTMLOptGroupElement.cpp b/WebCore/html/HTMLOptGroupElement.cpp index 294472b..5c79e74 100644 --- a/WebCore/html/HTMLOptGroupElement.cpp +++ b/WebCore/html/HTMLOptGroupElement.cpp @@ -58,40 +58,30 @@ const AtomicString& HTMLOptGroupElement::type() const bool HTMLOptGroupElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) { bool result = HTMLFormControlElement::insertBefore(newChild, refChild, ec, shouldLazyAttach); - if (result) - recalcSelectOptions(); return result; } bool HTMLOptGroupElement::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) { bool result = HTMLFormControlElement::replaceChild(newChild, oldChild, ec, shouldLazyAttach); - if (result) - recalcSelectOptions(); return result; } bool HTMLOptGroupElement::removeChild(Node* oldChild, ExceptionCode& ec) { bool result = HTMLFormControlElement::removeChild(oldChild, ec); - if (result) - recalcSelectOptions(); return result; } bool HTMLOptGroupElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) { bool result = HTMLFormControlElement::appendChild(newChild, ec, shouldLazyAttach); - if (result) - recalcSelectOptions(); return result; } bool HTMLOptGroupElement::removeChildren() { bool result = HTMLFormControlElement::removeChildren(); - if (result) - recalcSelectOptions(); return result; } diff --git a/WebCore/html/HTMLOptionElement.cpp b/WebCore/html/HTMLOptionElement.cpp index 085019f..982f578 100644 --- a/WebCore/html/HTMLOptionElement.cpp +++ b/WebCore/html/HTMLOptionElement.cpp @@ -225,13 +225,16 @@ bool HTMLOptionElement::disabled() const return HTMLFormControlElement::disabled() || (parentNode() && static_cast<HTMLFormControlElement*>(parentNode())->disabled()); } -void HTMLOptionElement::insertedIntoDocument() +void HTMLOptionElement::insertedIntoTree(bool deep) { - HTMLSelectElement* select; - if (selected() && (select = ownerSelectElement())) + if (HTMLSelectElement* select = ownerSelectElement()) { + select->setRecalcListItems(); + if (selected()) + select->setSelectedIndex(index(), false); select->scrollToSelection(); + } - HTMLFormControlElement::insertedIntoDocument(); + HTMLFormControlElement::insertedIntoTree(deep); } } // namespace diff --git a/WebCore/html/HTMLOptionElement.h b/WebCore/html/HTMLOptionElement.h index 39b857c..8c0f260 100644 --- a/WebCore/html/HTMLOptionElement.h +++ b/WebCore/html/HTMLOptionElement.h @@ -77,7 +77,7 @@ public: virtual bool disabled() const; - virtual void insertedIntoDocument(); + virtual void insertedIntoTree(bool); virtual void accessKeyAction(bool); private: diff --git a/WebCore/html/HTMLParser.cpp b/WebCore/html/HTMLParser.cpp index 0403dad..a4d4671 100644 --- a/WebCore/html/HTMLParser.cpp +++ b/WebCore/html/HTMLParser.cpp @@ -61,6 +61,13 @@ using namespace HTMLNames; static const unsigned cMaxRedundantTagDepth = 20; static const unsigned cResidualStyleMaxDepth = 200; +static const int minBlockLevelTagPriority = 3; + +// A cap on the number of tags with priority minBlockLevelTagPriority or higher +// allowed in m_blockStack. The cap is enforced by adding such new elements as +// siblings instead of children once it is reached. +static const size_t cMaxBlockDepth = 4096; + struct HTMLStackElem : Noncopyable { HTMLStackElem(const AtomicString& t, int lvl, Node* n, bool r, HTMLStackElem* nx) : tagName(t) @@ -113,36 +120,38 @@ struct HTMLStackElem : Noncopyable { */ HTMLParser::HTMLParser(HTMLDocument* doc, bool reportErrors) - : document(doc) - , current(doc) - , didRefCurrent(false) - , blockStack(0) + : m_document(doc) + , m_current(doc) + , m_didRefCurrent(false) + , m_blockStack(0) + , m_blocksInStack(0) , m_hasPElementInScope(NotInScope) - , head(0) - , inBody(false) - , haveContent(false) - , haveFrameSet(false) + , m_head(0) + , m_inBody(false) + , m_haveContent(false) + , m_haveFrameSet(false) , m_isParsingFragment(false) , m_reportErrors(reportErrors) , m_handlingResidualStyleAcrossBlocks(false) - , inStrayTableContent(0) + , m_inStrayTableContent(0) { } HTMLParser::HTMLParser(DocumentFragment* frag) - : document(frag->document()) - , current(frag) - , didRefCurrent(true) - , blockStack(0) + : m_document(frag->document()) + , m_current(frag) + , m_didRefCurrent(true) + , m_blockStack(0) + , m_blocksInStack(0) , m_hasPElementInScope(NotInScope) - , head(0) - , inBody(true) - , haveContent(false) - , haveFrameSet(false) + , m_head(0) + , m_inBody(true) + , m_haveContent(false) + , m_haveFrameSet(false) , m_isParsingFragment(true) , m_reportErrors(false) , m_handlingResidualStyleAcrossBlocks(false) - , inStrayTableContent(0) + , m_inStrayTableContent(0) { if (frag) frag->ref(); @@ -151,26 +160,26 @@ HTMLParser::HTMLParser(DocumentFragment* frag) HTMLParser::~HTMLParser() { freeBlock(); - if (didRefCurrent) - current->deref(); + if (m_didRefCurrent) + m_current->deref(); } void HTMLParser::reset() { ASSERT(!m_isParsingFragment); - setCurrent(document); + setCurrent(m_document); freeBlock(); - inBody = false; - haveFrameSet = false; - haveContent = false; - inStrayTableContent = 0; + m_inBody = false; + m_haveFrameSet = false; + m_haveContent = false; + m_inStrayTableContent = 0; m_currentFormElement = 0; m_currentMapElement = 0; - head = 0; + m_head = 0; m_isindexElement = 0; m_skipModeTag = nullAtom; @@ -178,13 +187,13 @@ void HTMLParser::reset() void HTMLParser::setCurrent(Node* newCurrent) { - bool didRefNewCurrent = newCurrent && newCurrent != document; + bool didRefNewCurrent = newCurrent && newCurrent != m_document; if (didRefNewCurrent) newCurrent->ref(); - if (didRefCurrent) - current->deref(); - current = newCurrent; - didRefCurrent = didRefNewCurrent; + if (m_didRefCurrent) + m_current->deref(); + m_current = newCurrent; + m_didRefCurrent = didRefNewCurrent; } PassRefPtr<Node> HTMLParser::parseToken(Token* t) @@ -193,7 +202,7 @@ PassRefPtr<Node> HTMLParser::parseToken(Token* t) if (!t->beginTag && t->tagName == m_skipModeTag) // Found the end tag for the current skip mode, so we're done skipping. m_skipModeTag = nullAtom; - else if (current->localName() == t->tagName) + else if (m_current->localName() == t->tagName) // Do not skip </iframe>. // FIXME: What does that comment mean? How can it be right to parse a token without clearing m_skipModeTag? ; @@ -202,7 +211,7 @@ PassRefPtr<Node> HTMLParser::parseToken(Token* t) } // Apparently some sites use </br> instead of <br>. Be compatible with IE and Firefox and treat this like <br>. - if (t->isCloseTag(brTag) && document->inCompatMode()) { + if (t->isCloseTag(brTag) && m_document->inCompatMode()) { reportError(MalformedBRError); t->beginTag = true; } @@ -214,17 +223,17 @@ PassRefPtr<Node> HTMLParser::parseToken(Token* t) // Ignore spaces, if we're not inside a paragraph or other inline code. // Do not alter the text if it is part of a scriptTag. - if (t->tagName == textAtom && t->text && current->localName() != scriptTag) { - if (inBody && !skipMode() && current->localName() != styleTag && - current->localName() != titleTag && !t->text->containsOnlyWhitespace()) - haveContent = true; + if (t->tagName == textAtom && t->text && m_current->localName() != scriptTag) { + if (m_inBody && !skipMode() && m_current->localName() != styleTag && + m_current->localName() != titleTag && !t->text->containsOnlyWhitespace()) + m_haveContent = true; RefPtr<Node> n; String text = t->text.get(); unsigned charsLeft = text.length(); while (charsLeft) { // split large blocks of text to nodes of manageable size - n = Text::createWithLengthLimit(document, text, charsLeft); + n = Text::createWithLengthLimit(m_document, text, charsLeft); if (!insertNode(n.get(), t->selfClosingTag)) return 0; } @@ -269,8 +278,8 @@ PassRefPtr<Node> HTMLParser::parseToken(Token* t) if (m_currentFormElement == n) m_currentFormElement = 0; - if (head == n) - head = 0; + if (m_head == n) + m_head = 0; return 0; } @@ -280,25 +289,25 @@ PassRefPtr<Node> HTMLParser::parseToken(Token* t) void HTMLParser::parseDoctypeToken(DoctypeToken* t) { // Ignore any doctype after the first. Ignore doctypes in fragments. - if (document->doctype() || m_isParsingFragment || current != document) + if (m_document->doctype() || m_isParsingFragment || m_current != m_document) return; // Make a new doctype node and set it as our doctype. - document->addChild(DocumentType::create(document, String::adopt(t->m_name), String::adopt(t->m_publicID), String::adopt(t->m_systemID))); + m_document->addChild(DocumentType::create(m_document, String::adopt(t->m_name), String::adopt(t->m_publicID), String::adopt(t->m_systemID))); } -static bool isTableSection(Node* n) +static bool isTableSection(const Node* n) { return n->hasTagName(tbodyTag) || n->hasTagName(tfootTag) || n->hasTagName(theadTag); } -static bool isTablePart(Node* n) +static bool isTablePart(const Node* n) { return n->hasTagName(trTag) || n->hasTagName(tdTag) || n->hasTagName(thTag) || isTableSection(n); } -static bool isTableRelated(Node* n) +static bool isTableRelated(const Node* n) { return n->hasTagName(tableTag) || isTablePart(n); } @@ -317,33 +326,38 @@ bool HTMLParser::insertNode(Node* n, bool flat) // <table> is never allowed inside stray table content. Always pop out of the stray table content // and close up the first table, and then start the second table as a sibling. - if (inStrayTableContent && localName == tableTag) + if (m_inStrayTableContent && localName == tableTag) popBlock(tableTag); - + + if (tagPriority >= minBlockLevelTagPriority) { + while (m_blocksInStack >= cMaxBlockDepth) + popBlock(m_blockStack->tagName); + } + // let's be stupid and just try to insert it. // this should work if the document is well-formed - Node* newNode = current->addChild(n); + Node* newNode = m_current->addChild(n); if (!newNode) return handleError(n, flat, localName, tagPriority); // Try to handle the error. // don't push elements without end tags (e.g., <img>) on the stack - bool parentAttached = current->attached(); + bool parentAttached = m_current->attached(); if (tagPriority > 0 && !flat) { - if (newNode == current) { + if (newNode == m_current) { // This case should only be hit when a demoted <form> is placed inside a table. ASSERT(localName == formTag); - reportError(FormInsideTablePartError, ¤t->localName()); + reportError(FormInsideTablePartError, &m_current->localName()); } else { // The pushBlock function transfers ownership of current to the block stack - // so we're guaranteed that didRefCurrent is false. The code below is an + // so we're guaranteed that m_didRefCurrent is false. The code below is an // optimized version of setCurrent that takes advantage of that fact and also // assumes that newNode is neither 0 nor a pointer to the document. pushBlock(localName, tagPriority); newNode->beginParsingChildren(); - ASSERT(!didRefCurrent); + ASSERT(!m_didRefCurrent); newNode->ref(); - current = newNode; - didRefCurrent = true; + m_current = newNode; + m_didRefCurrent = true; } if (parentAttached && !n->attached() && !m_isParsingFragment) n->attach(); @@ -353,6 +367,9 @@ bool HTMLParser::insertNode(Node* n, bool flat) n->finishParsingChildren(); } + if (localName == htmlTag && m_document->frame()) + m_document->frame()->loader()->dispatchDocumentElementAvailable(); + return true; } @@ -366,28 +383,28 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, if (n->isHTMLElement()) { HTMLElement* h = static_cast<HTMLElement*>(n); if (h->hasLocalName(trTag) || h->hasLocalName(thTag) || h->hasLocalName(tdTag)) { - if (inStrayTableContent && !isTableRelated(current)) { - reportError(MisplacedTablePartError, &localName, ¤t->localName()); + if (m_inStrayTableContent && !isTableRelated(m_current)) { + reportError(MisplacedTablePartError, &localName, &m_current->localName()); // pop out to the nearest enclosing table-related tag. - while (blockStack && !isTableRelated(current)) + while (m_blockStack && !isTableRelated(m_current)) popOneBlock(); return insertNode(n); } } else if (h->hasLocalName(headTag)) { - if (!current->isDocumentNode() && !current->hasTagName(htmlTag)) { + if (!m_current->isDocumentNode() && !m_current->hasTagName(htmlTag)) { reportError(MisplacedHeadError); return false; } } else if (h->hasLocalName(metaTag) || h->hasLocalName(linkTag) || h->hasLocalName(baseTag)) { bool createdHead = false; - if (!head) { + if (!m_head) { createHead(); createdHead = true; } - if (head) { + if (m_head) { if (!createdHead) - reportError(MisplacedHeadContentError, &localName, ¤t->localName()); - if (head->addChild(n)) { + reportError(MisplacedHeadContentError, &localName, &m_current->localName()); + if (m_head->addChild(n)) { if (!n->attached() && !m_isParsingFragment) n->attach(); return true; @@ -395,13 +412,13 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, return false; } } else if (h->hasLocalName(htmlTag)) { - if (!current->isDocumentNode() ) { - if (document->documentElement() && document->documentElement()->hasTagName(htmlTag)) { + if (!m_current->isDocumentNode() ) { + if (m_document->documentElement() && m_document->documentElement()->hasTagName(htmlTag)) { reportError(RedundantHTMLBodyError, &localName); // we have another <HTML> element.... apply attributes to existing one // make sure we don't overwrite already existing attributes NamedAttrMap* map = static_cast<Element*>(n)->attributes(true); - Element* existingHTML = static_cast<Element*>(document->documentElement()); + Element* existingHTML = static_cast<Element*>(m_document->documentElement()); NamedAttrMap* bmap = existingHTML->attributes(false); for (unsigned l = 0; map && l < map->length(); ++l) { Attribute* it = map->attributeItem(l); @@ -413,19 +430,19 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, } } else if (h->hasLocalName(titleTag) || h->hasLocalName(styleTag)) { bool createdHead = false; - if (!head) { + if (!m_head) { createHead(); createdHead = true; } - if (head) { - Node* newNode = head->addChild(n); + if (m_head) { + Node* newNode = m_head->addChild(n); if (!newNode) { setSkipMode(h->tagQName()); return false; } if (!createdHead) - reportError(MisplacedHeadContentError, &localName, ¤t->localName()); + reportError(MisplacedHeadContentError, &localName, &m_current->localName()); pushBlock(localName, tagPriority); newNode->beginParsingChildren(); @@ -434,18 +451,18 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, n->attach(); return true; } - if (inBody) { + if (m_inBody) { setSkipMode(h->tagQName()); return false; } } else if (h->hasLocalName(bodyTag)) { - if (inBody && document->body()) { + if (m_inBody && m_document->body()) { // we have another <BODY> element.... apply attributes to existing one // make sure we don't overwrite already existing attributes // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor> reportError(RedundantHTMLBodyError, &localName); NamedAttrMap* map = static_cast<Element*>(n)->attributes(true); - Element* existingBody = document->body(); + Element* existingBody = m_document->body(); NamedAttrMap* bmap = existingBody->attributes(false); for (unsigned l = 0; map && l < map->length(); ++l) { Attribute* it = map->attributeItem(l); @@ -454,11 +471,11 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, } return false; } - else if (!current->isDocumentNode()) + else if (!m_current->isDocumentNode()) return false; } else if (h->hasLocalName(areaTag)) { if (m_currentMapElement) { - reportError(MisplacedAreaError, ¤t->localName()); + reportError(MisplacedAreaError, &m_current->localName()); m_currentMapElement->addChild(n); if (!n->attached() && !m_isParsingFragment) n->attach(); @@ -467,18 +484,18 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, } return false; } else if (h->hasLocalName(colgroupTag) || h->hasLocalName(captionTag)) { - if (isTableRelated(current)) { - while (blockStack && isTablePart(current)) + if (isTableRelated(m_current)) { + while (m_blockStack && isTablePart(m_current)) popOneBlock(); return insertNode(n); } } - } else if (n->isCommentNode() && !head) + } else if (n->isCommentNode() && !m_head) return false; // 2. Next we examine our currently active element to do some further error handling. - if (current->isHTMLElement()) { - HTMLElement* h = static_cast<HTMLElement*>(current); + if (m_current->isHTMLElement()) { + HTMLElement* h = static_cast<HTMLElement*>(m_current); const AtomicString& currentTagName = h->localName(); if (h->hasLocalName(htmlTag)) { HTMLElement* elt = n->isHTMLElement() ? static_cast<HTMLElement*>(n) : 0; @@ -487,9 +504,9 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, elt->hasLocalName(objectTag) || elt->hasLocalName(embedTag) || elt->hasLocalName(titleTag) || elt->hasLocalName(isindexTag) || elt->hasLocalName(baseTag))) { - if (!head) { - head = new HTMLHeadElement(headTag, document); - e = head; + if (!m_head) { + m_head = new HTMLHeadElement(headTag, m_document); + e = m_head; insertNode(e); handled = true; } @@ -499,8 +516,8 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, if (t->containsOnlyWhitespace()) return false; } - if (!haveFrameSet) { - e = new HTMLBodyElement(bodyTag, document); + if (!m_haveFrameSet) { + e = new HTMLBodyElement(bodyTag, m_document); startBody(); insertNode(e); handled = true; @@ -512,9 +529,9 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, return false; else { // This means the body starts here... - if (!haveFrameSet) { + if (!m_haveFrameSet) { popBlock(currentTagName); - e = new HTMLBodyElement(bodyTag, document); + e = new HTMLBodyElement(bodyTag, m_document); startBody(); insertNode(e); handled = true; @@ -542,7 +559,7 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, handled = true; // ...and start a new one } else { ExceptionCode ec = 0; - Node* node = current; + Node* node = m_current; Node* parent = node->parentNode(); // A script may have removed the current node's parent from the DOM // http://bugs.webkit.org/show_bug.cgi?id=7137 @@ -574,24 +591,24 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, pushBlock(localName, tagPriority); n->beginParsingChildren(); setCurrent(n); - inStrayTableContent++; - blockStack->strayTableContent = true; + m_inStrayTableContent++; + m_blockStack->strayTableContent = true; } return true; } } if (!ec) { - if (current->hasTagName(trTag)) { + if (m_current->hasTagName(trTag)) { reportError(TablePartRequiredError, &localName, &tdTag.localName()); - e = new HTMLTableCellElement(tdTag, document); - } else if (current->hasTagName(tableTag)) { + e = new HTMLTableCellElement(tdTag, m_document); + } else if (m_current->hasTagName(tableTag)) { // Don't report an error in this case, since making a <tbody> happens all the time when you have <table><tr>, // and it isn't really a parse error per se. - e = new HTMLTableSectionElement(tbodyTag, document); + e = new HTMLTableSectionElement(tbodyTag, m_document); } else { reportError(TablePartRequiredError, &localName, &trTag.localName()); - e = new HTMLTableRowElement(trTag, document); + e = new HTMLTableRowElement(trTag, m_document); } insertNode(e); @@ -625,20 +642,20 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, popBlock(currentTagName); handled = true; } else if (!h->hasLocalName(bodyTag)) { - if (isInline(current)) { + if (isInline(m_current)) { popInlineBlocks(); handled = true; } } - } else if (current->isDocumentNode()) { + } else if (m_current->isDocumentNode()) { if (n->isTextNode()) { Text* t = static_cast<Text*>(n); if (t->containsOnlyWhitespace()) return false; } - if (!document->documentElement()) { - e = new HTMLHtmlElement(htmlTag, document); + if (!m_document->documentElement()) { + e = new HTMLHtmlElement(htmlTag, m_document); insertNode(e); handled = true; } @@ -646,7 +663,7 @@ bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, // 3. If we couldn't handle the error, just return false and attempt to error-correct again. if (!handled) { - reportError(IgnoredContentError, &localName, ¤t->localName()); + reportError(IgnoredContentError, &localName, &m_current->localName()); return false; } return insertNode(n); @@ -657,21 +674,21 @@ typedef HashMap<AtomicStringImpl*, CreateErrorCheckFunc> FunctionMap; bool HTMLParser::textCreateErrorCheck(Token* t, RefPtr<Node>& result) { - result = new Text(document, t->text.get()); + result = new Text(m_document, t->text.get()); return false; } bool HTMLParser::commentCreateErrorCheck(Token* t, RefPtr<Node>& result) { - result = new Comment(document, t->text.get()); + result = new Comment(m_document, t->text.get()); return false; } bool HTMLParser::headCreateErrorCheck(Token*, RefPtr<Node>& result) { - if (!head || current->localName() == htmlTag) { - head = new HTMLHeadElement(headTag, document); - result = head; + if (!m_head || m_current->localName() == htmlTag) { + m_head = new HTMLHeadElement(headTag, m_document); + result = m_head; } else reportError(MisplacedHeadError); return false; @@ -680,7 +697,7 @@ bool HTMLParser::headCreateErrorCheck(Token*, RefPtr<Node>& result) bool HTMLParser::bodyCreateErrorCheck(Token*, RefPtr<Node>&) { // body no longer allowed if we have a frameset - if (haveFrameSet) + if (m_haveFrameSet) return false; popBlock(headTag); startBody(); @@ -690,19 +707,19 @@ bool HTMLParser::bodyCreateErrorCheck(Token*, RefPtr<Node>&) bool HTMLParser::framesetCreateErrorCheck(Token*, RefPtr<Node>&) { popBlock(headTag); - if (inBody && !haveFrameSet && !haveContent) { + if (m_inBody && !m_haveFrameSet && !m_haveContent) { popBlock(bodyTag); // ### actually for IE document.body returns the now hidden "body" element // we can't implement that behaviour now because it could cause too many // regressions and the headaches are not worth the work as long as there is // no site actually relying on that detail (Dirk) - if (document->body()) - document->body()->setAttribute(styleAttr, "display:none"); - inBody = false; + if (m_document->body()) + m_document->body()->setAttribute(styleAttr, "display:none"); + m_inBody = false; } - if ((haveContent || haveFrameSet) && current->localName() == htmlTag) + if ((m_haveContent || m_haveFrameSet) && m_current->localName() == htmlTag) return false; - haveFrameSet = true; + m_haveFrameSet = true; startBody(); return true; } @@ -712,7 +729,7 @@ bool HTMLParser::formCreateErrorCheck(Token* t, RefPtr<Node>& result) // Only create a new form if we're not already inside one. // This is consistent with other browsers' behavior. if (!m_currentFormElement) { - m_currentFormElement = new HTMLFormElement(formTag, document); + m_currentFormElement = new HTMLFormElement(formTag, m_document); result = m_currentFormElement; pCloserCreateErrorCheck(t, result); } @@ -722,7 +739,7 @@ bool HTMLParser::formCreateErrorCheck(Token* t, RefPtr<Node>& result) bool HTMLParser::isindexCreateErrorCheck(Token* t, RefPtr<Node>& result) { RefPtr<Node> n = handleIsindex(t); - if (!inBody) + if (!m_inBody) m_isindexElement = n.release(); else { t->selfClosingTag = true; @@ -800,7 +817,7 @@ bool HTMLParser::noframesCreateErrorCheck(Token*, RefPtr<Node>&) bool HTMLParser::noscriptCreateErrorCheck(Token*, RefPtr<Node>&) { if (!m_isParsingFragment) { - Settings* settings = document->settings(); + Settings* settings = m_document->settings(); if (settings && settings->isJavaScriptEnabled()) setSkipMode(noscriptTag); } @@ -816,7 +833,7 @@ bool HTMLParser::pCloserCreateErrorCheck(Token*, RefPtr<Node>&) bool HTMLParser::pCloserStrictCreateErrorCheck(Token*, RefPtr<Node>&) { - if (document->inCompatMode()) + if (m_document->inCompatMode()) return true; if (hasPElementInScope()) popBlock(pTag); @@ -825,7 +842,7 @@ bool HTMLParser::pCloserStrictCreateErrorCheck(Token*, RefPtr<Node>&) bool HTMLParser::mapCreateErrorCheck(Token*, RefPtr<Node>& result) { - m_currentMapElement = new HTMLMapElement(mapTag, document); + m_currentMapElement = new HTMLMapElement(mapTag, m_document); result = m_currentMapElement; return false; } @@ -896,7 +913,7 @@ PassRefPtr<Node> HTMLParser::getNode(Token* t) if (CreateErrorCheckFunc errorCheckFunc = gFunctionMap.get(t->tagName.impl())) proceed = (this->*errorCheckFunc)(t, result); if (proceed) - result = HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, t->tagName, xhtmlNamespaceURI), document, m_currentFormElement.get()); + result = HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, t->tagName, xhtmlNamespaceURI), m_document, m_currentFormElement.get()); return result.release(); } @@ -906,7 +923,7 @@ bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName) // about 1500 tags, all from a bunch of <b>s. We will only allow at most 20 // nested tags of the same type before just ignoring them all together. unsigned i = 0; - for (HTMLStackElem* curr = blockStack; + for (HTMLStackElem* curr = m_blockStack; i < cMaxRedundantTagDepth && curr && curr->tagName == tagName; curr = curr->next, i++) { } return i != cMaxRedundantTagDepth; @@ -929,9 +946,9 @@ void HTMLParser::processCloseTag(Token* t) else if (t->tagName == pTag) checkForCloseTagErrors = false; - HTMLStackElem* oldElem = blockStack; + HTMLStackElem* oldElem = m_blockStack; popBlock(t->tagName, checkForCloseTagErrors); - if (oldElem == blockStack && t->tagName == pTag) { + if (oldElem == m_blockStack && t->tagName == pTag) { // We encountered a stray </p>. Amazingly Gecko, WinIE, and MacIE all treat // this as a valid break, i.e., <p></p>. So go ahead and make the empty // paragraph. @@ -976,7 +993,7 @@ bool HTMLParser::isInline(Node* node) const e->hasLocalName(noembedTag)) return true; if (e->hasLocalName(noscriptTag) && !m_isParsingFragment) { - Settings* settings = document->settings(); + Settings* settings = m_document->settings(); if (settings && settings->isJavaScriptEnabled()) return true; } @@ -1047,7 +1064,7 @@ void HTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) // Find the outermost element that crosses over to a higher level. If there exists another higher-level // element, we will do another pass, until we have corrected the innermost one. ExceptionCode ec = 0; - HTMLStackElem* curr = blockStack; + HTMLStackElem* curr = m_blockStack; HTMLStackElem* prev = 0; HTMLStackElem* prevMaxElem = 0; maxElem = 0; @@ -1071,7 +1088,7 @@ void HTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) return; Node* residualElem = prev->node; - Node* blockElem = prevMaxElem ? prevMaxElem->node : current; + Node* blockElem = prevMaxElem ? prevMaxElem->node : m_current; Node* parentElem = elem->node; // Check to see if the reparenting that is going to occur is allowed according to the DOM. @@ -1220,13 +1237,13 @@ void HTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) // <table><b><i><form></b></form></i></table> // Then this check will be too simplistic. Right now the <i><form> chain will end up inside the <tbody>, which is pretty crazy. if (strayTableContent) - inStrayTableContent--; + m_inStrayTableContent--; // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>. // In the above example, Goo should stay italic. // We cap the number of tags we're willing to reopen based off cResidualStyleMaxDepth. - HTMLStackElem* curr = blockStack; + HTMLStackElem* curr = m_blockStack; HTMLStackElem* residualStyleStack = 0; unsigned stackDepth = 1; unsigned redundantStyleCount = 0; @@ -1254,7 +1271,7 @@ void HTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem) } else popOneBlock(); - curr = blockStack; + curr = m_blockStack; } reopenResidualStyleTags(residualStyleStack, 0); // Stray table content can't be an issue here, since some element above will always become the root of new stray table content. @@ -1276,7 +1293,7 @@ void HTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, Node* malformedTab if (malformedTableParent) malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), ec); else - current->appendChild(newNode, ec); + m_current->appendChild(newNode, ec); // FIXME: Is it really OK to ignore the exceptions here? // Now push a new stack element for this node we just created. @@ -1285,9 +1302,9 @@ void HTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, Node* malformedTab // Set our strayTableContent boolean if needed, so that the reopened tag also knows // that it is inside a malformed table. - blockStack->strayTableContent = malformedTableParent != 0; - if (blockStack->strayTableContent) - inStrayTableContent++; + m_blockStack->strayTableContent = malformedTableParent != 0; + if (m_blockStack->strayTableContent) + m_inStrayTableContent++; // Clear our malformed table parent variable. malformedTableParent = 0; @@ -1305,8 +1322,10 @@ void HTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, Node* malformedTab void HTMLParser::pushBlock(const AtomicString& tagName, int level) { - blockStack = new HTMLStackElem(tagName, level, current, didRefCurrent, blockStack); - didRefCurrent = false; + m_blockStack = new HTMLStackElem(tagName, level, m_current, m_didRefCurrent, m_blockStack); + if (level >= minBlockLevelTagPriority) + m_blocksInStack++; + m_didRefCurrent = false; if (tagName == pTag) m_hasPElementInScope = InScope; else if (isScopingTag(tagName)) @@ -1315,7 +1334,7 @@ void HTMLParser::pushBlock(const AtomicString& tagName, int level) void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors) { - HTMLStackElem* elem = blockStack; + HTMLStackElem* elem = m_blockStack; int maxLevel = 0; @@ -1343,12 +1362,12 @@ void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors) HTMLStackElem* residualStyleStack = 0; Node* malformedTableParent = 0; - elem = blockStack; + elem = m_blockStack; unsigned stackDepth = 1; unsigned redundantStyleCount = 0; while (elem) { if (elem->tagName == tagName) { - int strayTable = inStrayTableContent; + int strayTable = m_inStrayTableContent; popOneBlock(); elem = 0; @@ -1356,8 +1375,8 @@ void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors) // explicit <tbody> or <tr>. // If we end up needing to reopen residual style tags, the root of the reopened chain // must also know that it is the root of malformed content inside a <tbody>/<tr>. - if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) { - Node* curr = current; + if (strayTable && (m_inStrayTableContent < strayTable) && residualStyleStack) { + Node* curr = m_current; while (curr && !curr->hasTagName(tableTag)) curr = curr->parentNode(); malformedTableParent = curr ? curr->parentNode() : 0; @@ -1392,7 +1411,7 @@ void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors) popOneBlock(); } else popOneBlock(); - elem = blockStack; + elem = m_blockStack; } } @@ -1401,19 +1420,23 @@ void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors) inline HTMLStackElem* HTMLParser::popOneBlockCommon() { - HTMLStackElem* elem = blockStack; + HTMLStackElem* elem = m_blockStack; // Form elements restore their state during the parsing process. // Also, a few elements (<applet>, <object>) need to know when all child elements (<param>s) are available. - if (current && elem->node != current) - current->finishParsingChildren(); + if (m_current && elem->node != m_current) + m_current->finishParsingChildren(); - blockStack = elem->next; - current = elem->node; - didRefCurrent = elem->didRefNode; + if (m_blockStack->level >= minBlockLevelTagPriority) { + ASSERT(m_blocksInStack > 0); + m_blocksInStack--; + } + m_blockStack = elem->next; + m_current = elem->node; + m_didRefCurrent = elem->didRefNode; if (elem->strayTableContent) - inStrayTableContent--; + m_inStrayTableContent--; if (elem->tagName == pTag) m_hasPElementInScope = NotInScope; @@ -1426,8 +1449,8 @@ inline HTMLStackElem* HTMLParser::popOneBlockCommon() void HTMLParser::popOneBlock() { // Store the current node before popOneBlockCommon overwrites it. - Node* lastCurrent = current; - bool didRefLastCurrent = didRefCurrent; + Node* lastCurrent = m_current; + bool didRefLastCurrent = m_didRefCurrent; delete popOneBlockCommon(); @@ -1441,8 +1464,8 @@ void HTMLParser::moveOneBlockToStack(HTMLStackElem*& head) // See the two callers for details. // Store the current node before popOneBlockCommon overwrites it. - Node* lastCurrent = current; - bool didRefLastCurrent = didRefCurrent; + Node* lastCurrent = m_current; + bool didRefLastCurrent = m_didRefCurrent; // Pop the block, but don't deref the current node as popOneBlock does because // we'll be using the pointer in the new stack element. @@ -1450,7 +1473,7 @@ void HTMLParser::moveOneBlockToStack(HTMLStackElem*& head) // Transfer the current node into the stack element. // No need to deref the old elem->node because popOneBlockCommon transferred - // it into the current/didRefCurrent fields. + // it into the m_current/m_didRefCurrent fields. elem->node = lastCurrent; elem->didRefNode = didRefLastCurrent; elem->next = head; @@ -1460,7 +1483,7 @@ void HTMLParser::moveOneBlockToStack(HTMLStackElem*& head) void HTMLParser::checkIfHasPElementInScope() { m_hasPElementInScope = NotInScope; - HTMLStackElem* elem = blockStack; + HTMLStackElem* elem = m_blockStack; while (elem) { const AtomicString& tagName = elem->tagName; if (tagName == pTag) { @@ -1474,42 +1497,43 @@ void HTMLParser::checkIfHasPElementInScope() void HTMLParser::popInlineBlocks() { - while (blockStack && isInline(current)) + while (m_blockStack && isInline(m_current)) popOneBlock(); } void HTMLParser::freeBlock() { - while (blockStack) + while (m_blockStack) popOneBlock(); + ASSERT(!m_blocksInStack); } void HTMLParser::createHead() { - if (head || !document->documentElement()) + if (m_head || !m_document->documentElement()) return; - head = new HTMLHeadElement(headTag, document); - HTMLElement* body = document->body(); + m_head = new HTMLHeadElement(headTag, m_document); + HTMLElement* body = m_document->body(); ExceptionCode ec = 0; - document->documentElement()->insertBefore(head, body, ec); + m_document->documentElement()->insertBefore(m_head, body, ec); if (ec) - head = 0; + m_head = 0; // If the body does not exist yet, then the <head> should be pushed as the current block. - if (head && !body) { - pushBlock(head->localName(), head->tagPriority()); - setCurrent(head); + if (m_head && !body) { + pushBlock(m_head->localName(), m_head->tagPriority()); + setCurrent(m_head); } } PassRefPtr<Node> HTMLParser::handleIsindex(Token* t) { - RefPtr<Node> n = new HTMLDivElement(divTag, document); + RefPtr<Node> n = new HTMLDivElement(divTag, m_document); NamedMappedAttrMap* attrs = t->attrs.get(); - RefPtr<HTMLIsIndexElement> isIndex = new HTMLIsIndexElement(isindexTag, document, m_currentFormElement.get()); + RefPtr<HTMLIsIndexElement> isIndex = new HTMLIsIndexElement(isindexTag, m_document, m_currentFormElement.get()); isIndex->setAttributeMap(attrs); isIndex->setAttribute(typeAttr, "khtml_isindex"); @@ -1520,20 +1544,20 @@ PassRefPtr<Node> HTMLParser::handleIsindex(Token* t) t->attrs = 0; } - n->addChild(new HTMLHRElement(hrTag, document)); - n->addChild(new Text(document, text)); + n->addChild(new HTMLHRElement(hrTag, m_document)); + n->addChild(new Text(m_document, text)); n->addChild(isIndex.release()); - n->addChild(new HTMLHRElement(hrTag, document)); + n->addChild(new HTMLHRElement(hrTag, m_document)); return n.release(); } void HTMLParser::startBody() { - if (inBody) + if (m_inBody) return; - inBody = true; + m_inBody = true; if (m_isindexElement) { insertNode(m_isindexElement.get(), true /* don't descend into this node */); @@ -1544,8 +1568,8 @@ void HTMLParser::startBody() void HTMLParser::finished() { // In the case of a completely empty document, here's the place to create the HTML element. - if (current && current->isDocumentNode() && !document->documentElement()) - insertNode(new HTMLHtmlElement(htmlTag, document)); + if (m_current && m_current->isDocumentNode() && !m_document->documentElement()) + insertNode(new HTMLHtmlElement(htmlTag, m_document)); // This ensures that "current" is not left pointing to a node when the document is destroyed. freeBlock(); @@ -1553,16 +1577,16 @@ void HTMLParser::finished() // Warning, this may delete the tokenizer and parser, so don't try to do anything else after this. if (!m_isParsingFragment) - document->finishedParsing(); + m_document->finishedParsing(); } void HTMLParser::reportErrorToConsole(HTMLParserErrorCode errorCode, const AtomicString* tagName1, const AtomicString* tagName2, bool closeTags) { - Frame* frame = document->frame(); + Frame* frame = m_document->frame(); if (!frame) return; - HTMLTokenizer* htmlTokenizer = static_cast<HTMLTokenizer*>(document->tokenizer()); + HTMLTokenizer* htmlTokenizer = static_cast<HTMLTokenizer*>(m_document->tokenizer()); int lineNumber = htmlTokenizer->lineNumber() + 1; AtomicString tag1; @@ -1597,7 +1621,7 @@ void HTMLParser::reportErrorToConsole(HTMLParserErrorCode errorCode, const Atomi frame->domWindow()->console()->addMessage(HTMLMessageSource, isWarning(errorCode) ? WarningMessageLevel : ErrorMessageLevel, - message, lineNumber, document->url().string()); + message, lineNumber, m_document->url().string()); } } diff --git a/WebCore/html/HTMLParser.h b/WebCore/html/HTMLParser.h index 866835f..23fb980 100644 --- a/WebCore/html/HTMLParser.h +++ b/WebCore/html/HTMLParser.h @@ -150,33 +150,38 @@ private: void reportErrorToConsole(HTMLParserErrorCode, const AtomicString* tagName1, const AtomicString* tagName2, bool closeTags); - Document* document; + Document* m_document; // The currently active element (the one new elements will be added to). Can be a document fragment, a document or an element. - Node* current; + Node* m_current; // We can't ref a document, but we don't want to constantly check if a node is a document just to decide whether to deref. - bool didRefCurrent; + bool m_didRefCurrent; - HTMLStackElem* blockStack; + HTMLStackElem* m_blockStack; + + // The number of tags with priority minBlockLevelTagPriority or higher + // currently in m_blockStack. The parser enforces a cap on this value by + // adding such new elements as siblings instead of children once it is reached. + size_t m_blocksInStack; enum ElementInScopeState { NotInScope, InScope, Unknown }; ElementInScopeState m_hasPElementInScope; RefPtr<HTMLFormElement> m_currentFormElement; // currently active form RefPtr<HTMLMapElement> m_currentMapElement; // current map - HTMLHeadElement* head; // head element; needed for HTML which defines <base> after </head> + HTMLHeadElement* m_head; // head element; needed for HTML which defines <base> after </head> RefPtr<Node> m_isindexElement; // a possible <isindex> element in the head - bool inBody; - bool haveContent; - bool haveFrameSet; + bool m_inBody; + bool m_haveContent; + bool m_haveFrameSet; AtomicString m_skipModeTag; // tells the parser to discard all tags until it reaches the one specified bool m_isParsingFragment; bool m_reportErrors; bool m_handlingResidualStyleAcrossBlocks; - int inStrayTableContent; + int m_inStrayTableContent; }; } diff --git a/WebCore/html/HTMLQuoteElement.cpp b/WebCore/html/HTMLQuoteElement.cpp index 19164e2..e548215 100644 --- a/WebCore/html/HTMLQuoteElement.cpp +++ b/WebCore/html/HTMLQuoteElement.cpp @@ -22,6 +22,7 @@ #include "config.h" #include "HTMLQuoteElement.h" +#include "Document.h" #include "HTMLNames.h" namespace WebCore { @@ -34,6 +35,13 @@ HTMLQuoteElement::HTMLQuoteElement(const QualifiedName& tagName, Document* doc) ASSERT(hasTagName(qTag)); } +void HTMLQuoteElement::insertedIntoDocument() +{ + document()->setUsesBeforeAfterRules(true); + + HTMLElement::insertedIntoDocument(); +} + String HTMLQuoteElement::cite() const { return getAttribute(citeAttr); diff --git a/WebCore/html/HTMLQuoteElement.h b/WebCore/html/HTMLQuoteElement.h index 1e35431..623c28f 100644 --- a/WebCore/html/HTMLQuoteElement.h +++ b/WebCore/html/HTMLQuoteElement.h @@ -36,6 +36,8 @@ public: virtual HTMLTagStatus endTagRequirement() const { return TagStatusRequired; } virtual int tagPriority() const { return 1; } + virtual void insertedIntoDocument(); + String cite() const; void setCite(const String&); }; diff --git a/WebCore/html/HTMLSelectElement.cpp b/WebCore/html/HTMLSelectElement.cpp index 7a0ee29..107fbd0 100644 --- a/WebCore/html/HTMLSelectElement.cpp +++ b/WebCore/html/HTMLSelectElement.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved. * (C) 2006 Alexey Proskuryakov (ap@nypop.com) * * This library is free software; you can redistribute it and/or @@ -67,6 +67,9 @@ using namespace HTMLNames; static const DOMTimeStamp typeAheadTimeout = 1000; +// Upper limit agreed upon with representatives of Opera and Mozilla. +static const unsigned maxSelectItems = 10000; + HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) : HTMLFormControlElementWithState(tagName, doc, f) , m_minwidth(0) @@ -218,10 +221,7 @@ void HTMLSelectElement::add(HTMLElement *element, HTMLElement *before, Exception if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag))) return; - ec = 0; insertBefore(element, before, ec); - if (!ec) - setRecalcListItems(); } void HTMLSelectElement::remove(int index) @@ -236,8 +236,6 @@ void HTMLSelectElement::remove(int index) Element *item = items[listIndex]; ASSERT(item->parentNode()); item->parentNode()->removeChild(item, ec); - if (!ec) - setRecalcListItems(); } String HTMLSelectElement::value() @@ -296,46 +294,6 @@ void HTMLSelectElement::restoreState(const String& state) setChanged(); } -bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) -{ - bool result = HTMLFormControlElementWithState::insertBefore(newChild, refChild, ec, shouldLazyAttach); - if (result) - setRecalcListItems(); - return result; -} - -bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec, bool shouldLazyAttach) -{ - bool result = HTMLFormControlElementWithState::replaceChild(newChild, oldChild, ec, shouldLazyAttach); - if (result) - setRecalcListItems(); - return result; -} - -bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec) -{ - bool result = HTMLFormControlElementWithState::removeChild(oldChild, ec); - if (result) - setRecalcListItems(); - return result; -} - -bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) -{ - bool result = HTMLFormControlElementWithState::appendChild(newChild, ec, shouldLazyAttach); - if (result) - setRecalcListItems(); - return result; -} - -bool HTMLSelectElement::removeChildren() -{ - bool result = HTMLFormControlElementWithState::removeChildren(); - if (result) - setRecalcListItems(); - return result; -} - void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr) { bool oldUsesMenuList = usesMenuList(); @@ -349,7 +307,7 @@ void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr) attr->setValue(attrSize); m_size = max(size, 1); - if ((oldUsesMenuList != usesMenuList() || !oldUsesMenuList && m_size != oldSize) && attached()) { + if ((oldUsesMenuList != usesMenuList() || (!oldUsesMenuList && m_size != oldSize)) && attached()) { detach(); attach(); setRecalcListItems(); @@ -422,6 +380,9 @@ RenderObject* HTMLSelectElement::createRenderer(RenderArena* arena, RenderStyle* bool HTMLSelectElement::appendFormData(FormDataList& list, bool) { + if (name().isEmpty()) + return false; + bool successful = false; const Vector<HTMLElement*>& items = listItems(); @@ -536,6 +497,7 @@ void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange void HTMLSelectElement::setRecalcListItems() { m_recalcListItems = true; + m_activeSelectionAnchorIndex = -1; // Manual selection anchor is reset when manipulating the select programmatically. if (renderer()) { if (usesMenuList()) static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true); @@ -617,8 +579,6 @@ void HTMLSelectElement::defaultEventHandler(Event* evt) void HTMLSelectElement::menuListDefaultEventHandler(Event* evt) { - RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer()); - if (evt->type() == eventNames().keydownEvent) { if (!renderer() || !evt->isKeyboardEvent()) return; @@ -630,7 +590,8 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt) // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); - menuList->showPopup(); + if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) + menuList->showPopup(); handled = true; } #elif defined ANDROID_KEYBOARD_NAVIGATION @@ -677,7 +638,8 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt) // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); - menuList->showPopup(); + if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) + menuList->showPopup(); handled = true; } if (keyCode == '\r') { @@ -700,13 +662,15 @@ void HTMLSelectElement::menuListDefaultEventHandler(Event* evt) if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { focus(); - if (menuList->popupIsVisible()) - menuList->hidePopup(); - else { - // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, - // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. - saveLastSelection(); - menuList->showPopup(); + if (RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer())) { + if (menuList->popupIsVisible()) + menuList->hidePopup(); + else { + // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(); + menuList->showPopup(); + } } evt->setDefaultHandled(); } @@ -716,9 +680,11 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt) { if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { focus(); - - MouseEvent* mEvt = static_cast<MouseEvent*>(evt); - int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(mEvt->offsetX(), mEvt->offsetY()); + + // Convert to coords relative to the list box if needed. + MouseEvent* mouseEvent = static_cast<MouseEvent*>(evt); + IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(localOffset.x(), localOffset.y()); if (listIndex >= 0) { // Save the selection so it can be compared to the new selection when we call onChange during mouseup, or after autoscroll finishes. saveLastSelection(); @@ -727,13 +693,13 @@ void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt) bool multiSelectKeyPressed = false; #if PLATFORM(MAC) - multiSelectKeyPressed = mEvt->metaKey(); + multiSelectKeyPressed = mouseEvent->metaKey(); #else - multiSelectKeyPressed = mEvt->ctrlKey(); + multiSelectKeyPressed = mouseEvent->ctrlKey(); #endif - bool shiftSelect = multiple() && mEvt->shiftKey(); - bool multiSelect = multiple() && multiSelectKeyPressed && !mEvt->shiftKey(); + bool shiftSelect = multiple() && mouseEvent->shiftKey(); + bool multiSelect = multiple() && multiSelectKeyPressed && !mouseEvent->shiftKey(); HTMLElement* clickedElement = listItems()[listIndex]; HTMLOptionElement* option = 0; @@ -1068,8 +1034,8 @@ Node* HTMLSelectElement::item(unsigned index) void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec) { ec = 0; - if (index > INT_MAX) - index = INT_MAX; + if (index > maxSelectItems - 1) + index = maxSelectItems - 1; int diff = index - length(); HTMLElement* before = 0; // out of array bounds ? first insert empty dummies @@ -1091,15 +1057,14 @@ void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, Exc void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec) { ec = 0; - if (newLen > INT_MAX) - newLen = INT_MAX; + if (newLen > maxSelectItems) + newLen = maxSelectItems; int diff = length() - newLen; if (diff < 0) { // add dummy elements do { - RefPtr<Element> option = document()->createElement("option", ec); - if (!option) - break; + RefPtr<Element> option = document()->createElement(optionTag, false); + ASSERT(option); add(static_cast<HTMLElement*>(option.get()), 0, ec); if (ec) break; diff --git a/WebCore/html/HTMLSelectElement.h b/WebCore/html/HTMLSelectElement.h index ec49a82..59e4a4b 100644 --- a/WebCore/html/HTMLSelectElement.h +++ b/WebCore/html/HTMLSelectElement.h @@ -81,11 +81,6 @@ public: virtual bool saveState(String& value) const; virtual void restoreState(const String&); - virtual bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&, bool shouldLazyAttach = false); - virtual bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&, bool shouldLazyAttach = false); - virtual bool removeChild(Node* child, ExceptionCode&); - virtual bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&, bool shouldLazyAttach = false); - virtual bool removeChildren(); virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); virtual void parseMappedAttribute(MappedAttribute*); diff --git a/WebCore/html/HTMLSourceElement.cpp b/WebCore/html/HTMLSourceElement.cpp index c8f814f..609bcbf 100644 --- a/WebCore/html/HTMLSourceElement.cpp +++ b/WebCore/html/HTMLSourceElement.cpp @@ -53,7 +53,7 @@ void HTMLSourceElement::insertedIntoDocument() HTMLElement::insertedIntoDocument(); if (parentNode() && (parentNode()->hasTagName(audioTag) || parentNode()->hasTagName(videoTag))) { HTMLMediaElement* media = static_cast<HTMLMediaElement*>(parentNode()); - if (media->networkState() == HTMLMediaElement::EMPTY) + if (media->networkState() == HTMLMediaElement::NETWORK_EMPTY) media->scheduleLoad(); } } diff --git a/WebCore/html/HTMLTagNames.in b/WebCore/html/HTMLTagNames.in index 1697ebe..8b1fa2b 100644 --- a/WebCore/html/HTMLTagNames.in +++ b/WebCore/html/HTMLTagNames.in @@ -52,7 +52,7 @@ hr interfaceName=HTMLHRElement html i interfaceName=HTMLElement iframe interfaceName=HTMLIFrameElement, constructorNeedsCreatedByParser -image +image mapToTagName=img img interfaceName=HTMLImageElement, constructorNeedsFormElement input constructorNeedsFormElement ins interfaceName=HTMLModElement diff --git a/WebCore/html/HTMLTextAreaElement.cpp b/WebCore/html/HTMLTextAreaElement.cpp index 091718b..4eec088 100644 --- a/WebCore/html/HTMLTextAreaElement.cpp +++ b/WebCore/html/HTMLTextAreaElement.cpp @@ -37,7 +37,7 @@ #include "Page.h" #include "RenderStyle.h" #include "RenderTextControlMultiLine.h" -#include "Selection.h" +#include "VisibleSelection.h" #include "Text.h" #include <wtf/StdLibExtras.h> @@ -96,7 +96,7 @@ int HTMLTextAreaElement::selectionStart() return 0; if (document()->focusedNode() != this && m_cachedSelectionStart >= 0) return m_cachedSelectionStart; - return static_cast<RenderTextControl*>(renderer())->selectionStart(); + return toRenderTextControl(renderer())->selectionStart(); } int HTMLTextAreaElement::selectionEnd() @@ -105,35 +105,35 @@ int HTMLTextAreaElement::selectionEnd() return 0; if (document()->focusedNode() != this && m_cachedSelectionEnd >= 0) return m_cachedSelectionEnd; - return static_cast<RenderTextControl*>(renderer())->selectionEnd(); + return toRenderTextControl(renderer())->selectionEnd(); } void HTMLTextAreaElement::setSelectionStart(int start) { if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->setSelectionStart(start); + toRenderTextControl(renderer())->setSelectionStart(start); } void HTMLTextAreaElement::setSelectionEnd(int end) { if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end); + toRenderTextControl(renderer())->setSelectionEnd(end); } void HTMLTextAreaElement::select() { if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->select(); + toRenderTextControl(renderer())->select(); } void HTMLTextAreaElement::setSelectionRange(int start, int end) { if (!renderer()) return; - static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end); + toRenderTextControl(renderer())->setSelectionRange(start, end); } void HTMLTextAreaElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) @@ -206,7 +206,7 @@ bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool) // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer. // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. - RenderTextControl* control = static_cast<RenderTextControl*>(renderer()); + RenderTextControl* control = toRenderTextControl(renderer()); const String& text = (m_wrap == HardWrap && control) ? control->textWithHardLineBreaks() : value(); encoding.appendData(name(), text); return true; @@ -233,10 +233,17 @@ void HTMLTextAreaElement::updateFocusAppearance(bool restorePreviousSelection) ASSERT(renderer()); if (!restorePreviousSelection || m_cachedSelectionStart < 0) { +#if ENABLE(ON_FIRST_TEXTAREA_FOCUS_SELECT_ALL) + // Devices with trackballs or d-pads may focus on a textarea in route + // to another focusable node. By selecting all text, the next movement + // can more readily be interpreted as moving to the next node. + select(); +#else // If this is the first focus, set a caret at the beginning of the text. // This matches some browsers' behavior; see bug 11746 Comment #15. // http://bugs.webkit.org/show_bug.cgi?id=11746#c15 setSelectionRange(0, 0); +#endif } else { // Restore the cached selection. This matches other browsers' behavior. setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd); @@ -265,7 +272,7 @@ void HTMLTextAreaElement::updateValue() const return; ASSERT(renderer()); - m_value = static_cast<RenderTextControl*>(renderer())->text(); + m_value = toRenderTextControl(renderer())->text(); const_cast<HTMLTextAreaElement*>(this)->setValueMatchesRenderer(); notifyFormStateChanged(this); } @@ -382,11 +389,11 @@ void HTMLTextAreaElement::setRows(int rows) setAttribute(rowsAttr, String::number(rows)); } -Selection HTMLTextAreaElement::selection() const +VisibleSelection HTMLTextAreaElement::selection() const { if (!renderer() || m_cachedSelectionStart < 0 || m_cachedSelectionEnd < 0) - return Selection(); - return static_cast<RenderTextControl*>(renderer())->selection(m_cachedSelectionStart, m_cachedSelectionEnd); + return VisibleSelection(); + return toRenderTextControl(renderer())->selection(m_cachedSelectionStart, m_cachedSelectionEnd); } bool HTMLTextAreaElement::shouldUseInputMethod() const diff --git a/WebCore/html/HTMLTextAreaElement.h b/WebCore/html/HTMLTextAreaElement.h index f02ad65..f78386c 100644 --- a/WebCore/html/HTMLTextAreaElement.h +++ b/WebCore/html/HTMLTextAreaElement.h @@ -28,7 +28,7 @@ namespace WebCore { -class Selection; +class VisibleSelection; class HTMLTextAreaElement : public HTMLFormControlElementWithState { public: @@ -87,7 +87,7 @@ public: void setRows(int); void cacheSelection(int s, int e) { m_cachedSelectionStart = s; m_cachedSelectionEnd = e; }; - Selection selection() const; + VisibleSelection selection() const; virtual bool shouldUseInputMethod() const; diff --git a/WebCore/html/HTMLTokenizer.cpp b/WebCore/html/HTMLTokenizer.cpp index b01d4e4..e4952f7 100644 --- a/WebCore/html/HTMLTokenizer.cpp +++ b/WebCore/html/HTMLTokenizer.cpp @@ -143,7 +143,7 @@ inline void Token::addAttribute(AtomicString& attrName, const AtomicString& attr RefPtr<MappedAttribute> a = MappedAttribute::create(attrName, attributeValue); if (!attrs) { attrs = NamedMappedAttrMap::create(); - attrs->reserveCapacity(10); + attrs->reserveInitialCapacity(10); } attrs->insertAttribute(a.release(), viewSourceMode); } @@ -448,15 +448,6 @@ HTMLTokenizer::State HTMLTokenizer::scriptHandler(State state) m_scriptTagSrcAttrValue = String(); } else { // Parse m_scriptCode containing <script> info -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_doc->inLowBandwidthDisplay()) { - // ideal solution is only skipping internal JavaScript if there is external JavaScript. - // but internal JavaScript can use document.write() to create an external JavaScript, - // so we have to skip internal JavaScript all the time. - m_doc->frame()->loader()->needToSwitchOutLowBandwidthDisplay(); - doScriptExec = false; - } else -#endif doScriptExec = m_scriptNode->shouldExecuteAsJavaScript(); m_scriptNode = 0; } @@ -1600,13 +1591,13 @@ inline bool HTMLTokenizer::continueProcessing(int& processedCount, double startT return true; } -bool HTMLTokenizer::write(const SegmentedString& str, bool appendData) +void HTMLTokenizer::write(const SegmentedString& str, bool appendData) { if (!m_buffer) - return false; + return; if (m_parserStopped) - return false; + return; SegmentedString source(str); if (m_executingScript) @@ -1623,7 +1614,7 @@ bool HTMLTokenizer::write(const SegmentedString& str, bool appendData) m_preloadScanner->write(source); #endif } - return false; + return; } @@ -1639,7 +1630,7 @@ bool HTMLTokenizer::write(const SegmentedString& str, bool appendData) // Once a timer is set, it has control of when the tokenizer continues. if (m_timer.isActive()) - return false; + return; bool wasInWrite = m_inWrite; m_inWrite = true; @@ -1784,11 +1775,8 @@ bool HTMLTokenizer::write(const SegmentedString& str, bool appendData) android::TimeCounter::record(android::TimeCounter::ParsingTimeCounter, __FUNCTION__); #endif - if (m_noMoreData && !m_inWrite && !state.loadingExtScript() && !m_executingScript && !m_timer.isActive()) { + if (m_noMoreData && !m_inWrite && !state.loadingExtScript() && !m_executingScript && !m_timer.isActive()) end(); // this actually causes us to be deleted - return true; - } - return false; } void HTMLTokenizer::stopParsing() @@ -2004,11 +1992,11 @@ void HTMLTokenizer::notifyFinished(CachedResource*) #endif if (errorOccurred) - EventTargetNodeCast(n.get())->dispatchEventForType(eventNames().errorEvent, true, false); + n->dispatchEventForType(eventNames().errorEvent, true, false); else { if (static_cast<HTMLScriptElement*>(n.get())->shouldExecuteAsJavaScript()) m_state = scriptExecution(sourceCode, m_state); - EventTargetNodeCast(n.get())->dispatchEventForType(eventNames().loadEvent, false, false); + n->dispatchEventForType(eventNames().loadEvent, false, false); } // The state of m_pendingScripts.isEmpty() can change inside the scriptExecution() diff --git a/WebCore/html/HTMLTokenizer.h b/WebCore/html/HTMLTokenizer.h index b4453af..2896974 100644 --- a/WebCore/html/HTMLTokenizer.h +++ b/WebCore/html/HTMLTokenizer.h @@ -138,7 +138,7 @@ public: HTMLTokenizer(DocumentFragment*); virtual ~HTMLTokenizer(); - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void finish(); virtual void setForceSynchronous(bool force); virtual bool isWaitingForScripts() const; diff --git a/WebCore/html/HTMLVideoElement.cpp b/WebCore/html/HTMLVideoElement.cpp index a67db71..b0aac3c 100644 --- a/WebCore/html/HTMLVideoElement.cpp +++ b/WebCore/html/HTMLVideoElement.cpp @@ -52,27 +52,30 @@ bool HTMLVideoElement::rendererIsNeeded(RenderStyle* style) return HTMLElement::rendererIsNeeded(style); } +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) RenderObject* HTMLVideoElement::createRenderer(RenderArena* arena, RenderStyle*) { if (m_shouldShowPosterImage) return new (arena) RenderImage(this); return new (arena) RenderVideo(this); } +#endif void HTMLVideoElement::attach() { HTMLMediaElement::attach(); - + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) if (m_shouldShowPosterImage) { if (!m_imageLoader) m_imageLoader.set(new HTMLImageLoader(this)); m_imageLoader->updateFromElement(); if (renderer() && renderer()->isImage()) { - RenderImage* imageRenderer = static_cast<RenderImage*>(renderer()); + RenderImage* imageRenderer = toRenderImage(renderer()); imageRenderer->setCachedImage(m_imageLoader->image()); } } - +#endif } void HTMLVideoElement::detach() @@ -91,9 +94,14 @@ void HTMLVideoElement::parseMappedAttribute(MappedAttribute* attr) if (attrName == posterAttr) { updatePosterImage(); if (m_shouldShowPosterImage) { +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) if (!m_imageLoader) m_imageLoader.set(new HTMLImageLoader(this)); m_imageLoader->updateFromElementIgnoringPreviousError(); +#else + if (m_player) + m_player->setPoster(poster()); +#endif } } else if (attrName == widthAttr) addCSSLength(attr, CSSPropertyWidth, attr->value()); @@ -163,12 +171,18 @@ const QualifiedName& HTMLVideoElement::imageSourceAttributeName() const void HTMLVideoElement::updatePosterImage() { +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) bool oldShouldShowPosterImage = m_shouldShowPosterImage; - m_shouldShowPosterImage = !poster().isEmpty() && m_networkState < LOADED_FIRST_FRAME; +#endif + + m_shouldShowPosterImage = !poster().isEmpty() && readyState() < HAVE_CURRENT_DATA; + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) if (attached() && oldShouldShowPosterImage != m_shouldShowPosterImage) { detach(); attach(); } +#endif } } diff --git a/WebCore/html/HTMLVideoElement.h b/WebCore/html/HTMLVideoElement.h index 8779c85..d35f3f8 100644 --- a/WebCore/html/HTMLVideoElement.h +++ b/WebCore/html/HTMLVideoElement.h @@ -42,11 +42,14 @@ public: virtual int tagPriority() const { return 5; } virtual bool rendererIsNeeded(RenderStyle*); +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); +#endif virtual void attach(); virtual void detach(); virtual void parseMappedAttribute(MappedAttribute* attr); virtual bool isVideo() const { return true; } + virtual bool hasVideo() const { return player() && player()->hasVideo(); } virtual bool isURLAttribute(Attribute*) const; virtual const QualifiedName& imageSourceAttributeName() const; diff --git a/WebCore/html/HTMLViewSourceDocument.cpp b/WebCore/html/HTMLViewSourceDocument.cpp index 163ee72..596f16e 100644 --- a/WebCore/html/HTMLViewSourceDocument.cpp +++ b/WebCore/html/HTMLViewSourceDocument.cpp @@ -73,7 +73,7 @@ void HTMLViewSourceDocument::createContainingTable() // document. RefPtr<Element> div = new HTMLDivElement(divTag, this); RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); - attrs->insertAttribute(MappedAttribute::create(classAttr, "webkit-line-gutter-backdrop"), true); + attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-gutter-backdrop")); div->setAttributeMap(attrs.release()); body->addChild(div); div->attach(); @@ -195,7 +195,7 @@ Element* HTMLViewSourceDocument::addSpanWithClassName(const String& className) Element* span = new HTMLElement(spanTag, this); RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); - attrs->insertAttribute(MappedAttribute::create(classAttr, className), true); + attrs->addAttribute(MappedAttribute::create(classAttr, className)); span->setAttributeMap(attrs.release()); m_current->addChild(span); span->attach(); @@ -212,7 +212,7 @@ void HTMLViewSourceDocument::addLine(const String& className) // Create a cell that will hold the line number (it is generated in the stylesheet using counters). Element* td = new HTMLTableCellElement(tdTag, this); RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create(); - attrs->insertAttribute(MappedAttribute::create(classAttr, "webkit-line-number"), true); + attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-number")); td->setAttributeMap(attrs.release()); trow->addChild(td); td->attach(); @@ -220,7 +220,7 @@ void HTMLViewSourceDocument::addLine(const String& className) // Create a second cell for the line contents td = new HTMLTableCellElement(tdTag, this); attrs = NamedMappedAttrMap::create(); - attrs->insertAttribute(MappedAttribute::create(classAttr, "webkit-line-content"), true); + attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-content")); td->setAttributeMap(attrs.release()); trow->addChild(td); td->attach(); @@ -283,9 +283,9 @@ Element* HTMLViewSourceDocument::addLink(const String& url, bool isAnchor) classValue = "webkit-html-attribute-value webkit-html-external-link"; else classValue = "webkit-html-attribute-value webkit-html-resource-link"; - attrs->insertAttribute(MappedAttribute::create(classAttr, classValue), true); - attrs->insertAttribute(MappedAttribute::create(targetAttr, "_blank"), true); - attrs->insertAttribute(MappedAttribute::create(hrefAttr, url), true); + attrs->addAttribute(MappedAttribute::create(classAttr, classValue)); + attrs->addAttribute(MappedAttribute::create(targetAttr, "_blank")); + attrs->addAttribute(MappedAttribute::create(hrefAttr, url)); anchor->setAttributeMap(attrs.release()); m_current->addChild(anchor); anchor->attach(); diff --git a/WebCore/html/MediaError.h b/WebCore/html/MediaError.h index fbf375f..7dcf72a 100644 --- a/WebCore/html/MediaError.h +++ b/WebCore/html/MediaError.h @@ -34,7 +34,7 @@ namespace WebCore { class MediaError : public RefCounted<MediaError> { public: - enum Code { MEDIA_ERR_ABORTED = 1, MEDIA_ERR_NETWORK, MEDIA_ERR_DECODE }; + enum Code { MEDIA_ERR_ABORTED = 1, MEDIA_ERR_NETWORK, MEDIA_ERR_DECODE, MEDIA_ERR_NONE_SUPPORTED }; static PassRefPtr<MediaError> create(Code code) { return adoptRef(new MediaError(code)); } diff --git a/WebCore/html/MediaError.idl b/WebCore/html/MediaError.idl index 5b4f0a2..162170f 100644 --- a/WebCore/html/MediaError.idl +++ b/WebCore/html/MediaError.idl @@ -28,6 +28,7 @@ module html { const unsigned short MEDIA_ERR_ABORTED = 1; const unsigned short MEDIA_ERR_NETWORK = 2; const unsigned short MEDIA_ERR_DECODE = 3; + const unsigned short MEDIA_ERR_NONE_SUPPORTED = 4; readonly attribute unsigned short code; }; } diff --git a/WebCore/html/PreloadScanner.cpp b/WebCore/html/PreloadScanner.cpp index 7e95c65..782e9bd 100644 --- a/WebCore/html/PreloadScanner.cpp +++ b/WebCore/html/PreloadScanner.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -43,7 +44,7 @@ #include <wtf/CurrentTime.h> #include <wtf/unicode/Unicode.h> -#ifdef __GNUC__ +#if COMPILER(GCC) // The main tokenizer includes this too so we are getting two copies of the data. However, this way the code gets inlined. #include "HTMLEntityNames.c" #else @@ -128,9 +129,13 @@ bool PreloadScanner::scanningBody() const void PreloadScanner::write(const SegmentedString& source) { +#if PRELOAD_DEBUG double startTime = currentTime(); +#endif tokenize(source); +#if PRELOAD_DEBUG m_timeUsed += currentTime() - startTime; +#endif } static inline bool isWhitespace(UChar c) |