/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mats Palmgren <matspal@gmail.com>
* Ms2ger <ms2ger@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/Util.h"
#include "nscore.h"
#include "nsGenericHTMLElement.h"
#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsIContentViewer.h"
#include "mozilla/css/StyleRule.h"
#include "nsIDocument.h"
#include "nsIDocumentEncoder.h"
#include "nsIDOMHTMLBodyElement.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMAttr.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMHTMLMenuElement.h"
#include "nsIDOMElementCSSInlineStyle.h"
#include "nsIDOMWindow.h"
#include "nsIDOMDocument.h"
#include "nsEventListenerManager.h"
#include "nsMappedAttributes.h"
#include "nsHTMLStyleSheet.h"
#include "nsIHTMLDocument.h"
#include "nsILink.h"
#include "nsPIDOMWindow.h"
#include "nsIStyleRule.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsEscape.h"
#include "nsIFrame.h"
#include "nsIScrollableFrame.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsIWidget.h"
#include "nsRange.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsINameSpaceManager.h"
#include "nsDOMError.h"
#include "nsScriptLoader.h"
#include "nsRuleData.h"
#include "nsPresState.h"
#include "nsILayoutHistoryState.h"
#include "nsHTMLParts.h"
#include "nsContentUtils.h"
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsGkAtoms.h"
#include "nsEventStateManager.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsDOMCSSDeclaration.h"
#include "nsITextControlFrame.h"
#include "nsIForm.h"
#include "nsIFormControl.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsHTMLFormElement.h"
#include "nsFocusManager.h"
#include "nsMutationEvent.h"
#include "nsContentCID.h"
#include "nsDOMStringMap.h"
#include "nsIEditor.h"
#include "nsIEditorIMESupport.h"
#include "nsEventDispatcher.h"
#include "nsLayoutUtils.h"
#include "nsContentCreatorFunctions.h"
#include "mozAutoDocUpdate.h"
#include "nsHtml5Module.h"
#include "nsITextControlElement.h"
#include "mozilla/dom/Element.h"
#include "nsHTMLFieldSetElement.h"
#include "nsHTMLMenuElement.h"
#include "nsAsyncDOMEvent.h"
#include "nsIScriptError.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FromParser.h"
using namespace mozilla;
using namespace mozilla::dom;
#include "nsThreadUtils.h"
class nsINodeInfo;
class nsIDOMNodeList;
class nsRuleWalker;
// XXX todo: add in missing out-of-memory checks
//----------------------------------------------------------------------
#ifdef GATHER_ELEMENT_USEAGE_STATISTICS
// static objects that have constructors are kinda bad, but we don't
// care here, this is only debugging code!
static nsHashtable sGEUS_ElementCounts;
void GEUS_ElementCreated(nsINodeInfo *aNodeInfo)
{
nsAutoString name;
aNodeInfo->GetName(name);
nsStringKey key(name);
PRInt32 count = (PRInt32)sGEUS_ElementCounts.Get(&key);
count++;
sGEUS_ElementCounts.Put(&key, (void *)count);
}
bool GEUS_enum_func(nsHashKey *aKey, void *aData, void *aClosure)
{
const PRUnichar *name_chars = ((nsStringKey *)aKey)->GetString();
NS_ConvertUTF16toUTF8 name(name_chars);
printf ("%s %d\n", name.get(), aData);
return true;
}
void GEUS_DumpElementCounts()
{
printf ("Element count statistics:\n");
sGEUS_ElementCounts.Enumerate(GEUS_enum_func, nsnull);
printf ("End of element count statistics:\n");
}
nsresult
nsGenericHTMLElement::Init(nsINodeInfo *aNodeInfo)
{
GEUS_ElementCreated(aNodeInfo);
return nsGenericHTMLElementBase::Init(aNodeInfo);
}
#endif
/**
* nsAutoFocusEvent is used to dispatch a focus event when a
* nsGenericHTMLFormElement is binded to the tree with the autofocus attribute
* enabled.
*/
class nsAutoFocusEvent : public nsRunnable
{
public:
nsAutoFocusEvent(nsGenericHTMLFormElement* aElement) : mElement(aElement) {}
NS_IMETHOD Run() {
nsFocusManager* fm = nsFocusManager::GetFocusManager();
if (!fm) {
return NS_ERROR_NULL_POINTER;
}
nsIDocument* document = mElement->OwnerDoc();
nsPIDOMWindow* window = document->GetWindow();
if (!window) {
return NS_OK;
}
// Trying to found the top window (equivalent to window.top).
nsCOMPtr<nsIDOMWindow> top;
window->GetTop(getter_AddRefs(top));
if (top) {
window = static_cast<nsPIDOMWindow*>(top.get());
}
if (window->GetFocusedNode()) {
return NS_OK;
}
nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(window->GetExtantDocument());
if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) {
return NS_OK;
}
// If something is focused in the same document, ignore autofocus.
if (!fm->GetFocusedContent() ||
fm->GetFocusedContent()->OwnerDoc() != document) {
return mElement->Focus();
}
return NS_OK;
}
private:
// NOTE: nsGenericHTMLFormElement is saved as a nsGenericHTMLElement
// because AddRef/Release are ambiguous with nsGenericHTMLFormElement
// and Focus() is declared (and defined) in nsGenericHTMLElement class.
nsRefPtr<nsGenericHTMLElement> mElement;
};
class nsGenericHTMLElementTearoff : public nsIDOMElementCSSInlineStyle
{
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
nsGenericHTMLElementTearoff(nsGenericHTMLElement *aElement)
: mElement(aElement)
{
}
virtual ~nsGenericHTMLElementTearoff()
{
}
NS_IMETHOD GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
{
nsresult rv;
*aStyle = mElement->GetStyle(&rv);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*aStyle);
return NS_OK;
}
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGenericHTMLElementTearoff,
nsIDOMElementCSSInlineStyle)
private:
nsRefPtr<nsGenericHTMLElement> mElement;
};
NS_IMPL_CYCLE_COLLECTION_1(nsGenericHTMLElementTearoff, mElement)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGenericHTMLElementTearoff)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGenericHTMLElementTearoff)
NS_INTERFACE_TABLE_HEAD(nsGenericHTMLElementTearoff)
NS_INTERFACE_TABLE_INHERITED1(nsGenericHTMLElementTearoff,
nsIDOMElementCSSInlineStyle)
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsGenericHTMLElementTearoff)
NS_INTERFACE_MAP_END_AGGREGATED(mElement)
NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsGenericHTMLElement, TabIndex, tabindex, -1)
NS_IMPL_BOOL_ATTR(nsGenericHTMLElement, Hidden, hidden)
nsresult
nsGenericHTMLElement::DOMQueryInterface(nsIDOMHTMLElement *aElement,
REFNSIID aIID, void **aInstancePtr)
{
NS_PRECONDITION(aInstancePtr, "null out param");
nsresult rv = NS_ERROR_FAILURE;
NS_INTERFACE_TABLE_BEGIN
NS_INTERFACE_TABLE_ENTRY(nsIDOMHTMLElement, nsIDOMNode)
NS_INTERFACE_TABLE_ENTRY(nsIDOMHTMLElement, nsIDOMElement)
NS_INTERFACE_TABLE_ENTRY(nsIDOMHTMLElement, nsIDOMHTMLElement)
NS_INTERFACE_TABLE_END_WITH_PTR(aElement)
NS_INTERFACE_TABLE_TO_MAP_SEGUE
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMElementCSSInlineStyle,
new nsGenericHTMLElementTearoff(this))
NS_INTERFACE_MAP_END
// No closing bracket, because NS_INTERFACE_MAP_END does that for us.
nsresult
nsGenericHTMLElement::CopyInnerTo(nsGenericElement* aDst) const
{
nsresult rv;
PRInt32 i, count = GetAttrCount();
for (i = 0; i < count; ++i) {
const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i);
const nsAttrValue *value = mAttrsAndChildren.AttrAt(i);
if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) &&
value->Type() == nsAttrValue::eCSSStyleRule) {
// We can't just set this as a string, because that will fail
// to reparse the string into style data until the node is
// inserted into the document. Clone the Rule instead.
nsRefPtr<mozilla::css::Rule> ruleClone = value->GetCSSStyleRuleValue()->Clone();
nsRefPtr<mozilla::css::StyleRule> styleRule = do_QueryObject(ruleClone);
NS_ENSURE_TRUE(styleRule, NS_ERROR_UNEXPECTED);
rv = aDst->SetInlineStyleRule(styleRule, false);
NS_ENSURE_SUCCESS(rv, rv);
continue;
}
nsAutoString valStr;
value->ToString(valStr);
rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
name->GetPrefix(), valStr, false);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLElement::SetAttribute(const nsAString& aName,
const nsAString& aValue)
{
const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
if (!name) {
nsresult rv = nsContentUtils::CheckQName(aName, false);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAtom> nameAtom;
if (IsInHTMLDocument()) {
nsAutoString lower;
nsContentUtils::ASCIIToLower(aName, lower);
nameAtom = do_GetAtom(lower);
}
else {
nameAtom = do_GetAtom(aName);
}
NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
return SetAttr(kNameSpaceID_None, nameAtom, aValue, true);
}
return SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
aValue, true);
}
nsresult
nsGenericHTMLElement::GetDataset(nsIDOMDOMStringMap** aDataset)
{
nsDOMSlots *slots = DOMSlots();
if (!slots->mDataset) {
// mDataset is a weak reference so assignment will not AddRef.
// AddRef is called before assigning to out parameter.
slots->mDataset = new nsDOMStringMap(this);
}
NS_ADDREF(*aDataset = slots->mDataset);
return NS_OK;
}
nsresult
nsGenericHTMLElement::ClearDataset()
{
nsDOMSlots *slots = GetExistingDOMSlots();
NS_ASSERTION(slots && slots->mDataset,
"Slots should exist and dataset should not be null.");
slots->mDataset = nsnull;
return NS_OK;
}
// Implementation for nsIDOMHTMLElement
nsresult
nsGenericHTMLElement::GetId(nsAString& aId)
{
GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId);
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetId(const nsAString& aId)
{
SetAttr(kNameSpaceID_None, nsGkAtoms::id, aId, true);
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetTitle(nsAString& aTitle)
{
GetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle);
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetTitle(const nsAString& aTitle)
{
SetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle, true);
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetLang(nsAString& aLang)
{
GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLang);
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetLang(const nsAString& aLang)
{
SetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLang, true);
return NS_OK;
}
static const nsAttrValue::EnumTable kDirTable[] = {
{ "ltr", NS_STYLE_DIRECTION_LTR },
{ "rtl", NS_STYLE_DIRECTION_RTL },
{ 0 }
};
NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsGenericHTMLElement, Dir, dir, NULL)
nsresult
nsGenericHTMLElement::GetClassName(nsAString& aClassName)
{
GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aClassName);
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetClassName(const nsAString& aClassName)
{
SetAttr(kNameSpaceID_None, nsGkAtoms::_class, aClassName, true);
return NS_OK;
}
NS_IMPL_STRING_ATTR(nsGenericHTMLElement, AccessKey, accesskey)
NS_IMETHODIMP
nsGenericHTMLElement::GetAccessKeyLabel(nsAString& aLabel)
{
nsPresContext *presContext = GetPresContext();
if (presContext &&
presContext->EventStateManager()->GetAccessKeyLabelPrefix(aLabel)) {
nsAutoString suffix;
GetAccessKey(suffix);
aLabel.Append(suffix);
}
return NS_OK;
}
static bool IS_TABLE_CELL(nsIAtom* frameType) {
return nsGkAtoms::tableCellFrame == frameType ||
nsGkAtoms::bcTableCellFrame == frameType;
}
static bool
IsOffsetParent(nsIFrame* aFrame)
{
nsIAtom* frameType = aFrame->GetType();
return (IS_TABLE_CELL(frameType) ||
frameType == nsGkAtoms::tableFrame);
}
void
nsGenericHTMLElement::GetOffsetRect(nsRect& aRect, nsIContent** aOffsetParent)
{
*aOffsetParent = nsnull;
aRect = nsRect();
nsIFrame* frame = GetStyledFrame();
if (!frame) {
return;
}
nsIFrame* parent = frame->GetParent();
nsPoint origin(0, 0);
if (parent && parent->GetType() == nsGkAtoms::tableOuterFrame) {
origin = parent->GetPositionIgnoringScrolling();
parent = parent->GetParent();
}
Element* docElement = GetCurrentDoc()->GetRootElement();
nsIContent* content = frame->GetContent();
if (content && (content->IsHTML(nsGkAtoms::body) || content == docElement)) {
parent = frame;
}
else {
const bool isPositioned = frame->GetStyleDisplay()->IsPositioned();
const bool isAbsolutelyPositioned =
frame->GetStyleDisplay()->IsAbsolutelyPositioned();
origin += frame->GetPositionIgnoringScrolling();
for ( ; parent ; parent = parent->GetParent()) {
content = parent->GetContent();
// Stop at the first ancestor that is positioned.
if (parent->GetStyleDisplay()->IsPositioned()) {
*aOffsetParent = content;
NS_IF_ADDREF(*aOffsetParent);
break;
}
// Add the parent's origin to our own to get to the
// right coordinate system.
const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
if (!isAbsolutelyPositioned && !isOffsetParent) {
origin += parent->GetPositionIgnoringScrolling();
}
if (content) {
// If we've hit the document element, break here.
if (content == docElement) {
break;
}
// Break if the ancestor frame type makes it suitable as offset parent
// and this element is *not* positioned or if we found the body element.
if (isOffsetParent || content->IsHTML(nsGkAtoms::body)) {
*aOffsetParent = content;
NS_ADDREF(*aOffsetParent);
break;
}
}
}
if (isAbsolutelyPositioned && !*aOffsetParent) {
// If this element is absolutely positioned, but we don't have
// an offset parent it means this element is an absolutely
// positioned child that's not nested inside another positioned
// element, in this case the element's frame's parent is the
// frame for the HTML element so we fail to find the body in the
// parent chain. We want the offset parent in this case to be
// the body, so we just get the body element from the document.
nsCOMPtr<nsIDOMHTMLDocument> html_doc(do_QueryInterface(GetCurrentDoc()));
if (html_doc) {
nsCOMPtr<nsIDOMHTMLElement> html_element;
html_doc->GetBody(getter_AddRefs(html_element));
if (html_element) {
CallQueryInterface(html_element, aOffsetParent);
}
}
}
}
// Subtract the parent border unless it uses border-box sizing.
if (parent &&
parent->GetStylePosition()->mBoxSizing != NS_STYLE_BOX_SIZING_BORDER) {
const nsStyleBorder* border = parent->GetStyleBorder();
origin.x -= border->GetActualBorderWidth(NS_SIDE_LEFT);
origin.y -= border->GetActualBorderWidth(NS_SIDE_TOP);
}
// XXX We should really consider subtracting out padding for
// content-box sizing, but we should see what IE does....
// Convert to pixels.
aRect.x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
aRect.y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
// Get the union of all rectangles in this and continuation frames.
// It doesn't really matter what we use as aRelativeTo here, since
// we only care about the size. We just have to use something non-null.
nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame);
aRect.width = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.width);
aRect.height = nsPresContext::AppUnitsToIntCSSPixels(rcFrame.height);
}
nsresult
nsGenericHTMLElement::GetOffsetTop(PRInt32* aOffsetTop)
{
nsRect rcFrame;
nsCOMPtr<nsIContent> parent;
GetOffsetRect(rcFrame, getter_AddRefs(parent));
*aOffsetTop = rcFrame.y;
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetOffsetLeft(PRInt32* aOffsetLeft)
{
nsRect rcFrame;
nsCOMPtr<nsIContent> parent;
GetOffsetRect(rcFrame, getter_AddRefs(parent));
*aOffsetLeft = rcFrame.x;
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetOffsetWidth(PRInt32* aOffsetWidth)
{
nsRect rcFrame;
nsCOMPtr<nsIContent> parent;
GetOffsetRect(rcFrame, getter_AddRefs(parent));
*aOffsetWidth = rcFrame.width;
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetOffsetHeight(PRInt32* aOffsetHeight)
{
nsRect rcFrame;
nsCOMPtr<nsIContent> parent;
GetOffsetRect(rcFrame, getter_AddRefs(parent));
*aOffsetHeight = rcFrame.height;
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetOffsetParent(nsIDOMElement** aOffsetParent)
{
nsRect rcFrame;
nsCOMPtr<nsIContent> parent;
GetOffsetRect(rcFrame, getter_AddRefs(parent));
if (parent) {
CallQueryInterface(parent, aOffsetParent);
} else {
*aOffsetParent = nsnull;
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
{
aMarkup.Truncate();
nsIDocument* doc = OwnerDoc();
nsAutoString contentType;
if (IsInHTMLDocument()) {
contentType.AssignLiteral("text/html");
} else {
doc->GetContentType(contentType);
}
nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
if (!docEncoder) {
docEncoder =
do_CreateInstance(PromiseFlatCString(
nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
NS_ConvertUTF16toUTF8(contentType)
).get());
}
if (!(docEncoder || doc->IsHTML())) {
// This could be some type for which we create a synthetic document. Try
// again as XML
contentType.AssignLiteral("application/xml");
docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
}
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
nsresult rv = docEncoder->NativeInit(doc, contentType,
nsIDocumentEncoder::OutputEncodeBasicEntities |
// Output DOM-standard newlines
nsIDocumentEncoder::OutputLFLineBreak |
// Don't do linebreaking that's not present in
// the source
nsIDocumentEncoder::OutputRaw);
NS_ENSURE_SUCCESS(rv, rv);
if (aIncludeSelf) {
docEncoder->SetNativeNode(this);
} else {
docEncoder->SetNativeContainerNode(this);
}
rv = docEncoder->EncodeToString(aMarkup);
if (!aIncludeSelf) {
doc->SetCachedEncoder(docEncoder.forget());
}
return rv;
}
nsresult
nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML) {
return GetMarkup(false, aInnerHTML);
}
NS_IMETHODIMP
nsGenericHTMLElement::GetOuterHTML(nsAString& aOuterHTML) {
return GetMarkup(true, aOuterHTML);
}
void
nsGenericHTMLElement::FireMutationEventsForDirectParsing(nsIDocument* aDoc,
nsIContent* aDest,
PRInt32 aOldChildCount)
{
// Fire mutation events. Optimize for the case when there are no listeners
PRInt32 newChildCount = aDest->GetChildCount();
if (newChildCount && nsContentUtils::
HasMutationListeners(aDoc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
nsAutoTArray<nsCOMPtr<nsIContent>, 50> childNodes;
NS_ASSERTION(newChildCount - aOldChildCount >= 0,
"What, some unexpected dom mutation has happened?");
childNodes.SetCapacity(newChildCount - aOldChildCount);
for (nsIContent* child = aDest->GetFirstChild();
child;
child = child->GetNextSibling()) {
childNodes.AppendElement(child);
}
nsGenericElement::FireNodeInserted(aDoc, aDest, childNodes);
}
}
NS_IMETHODIMP
nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
{
nsIDocument* doc = OwnerDoc();
// Batch possible DOMSubtreeModified events.
mozAutoSubtreeModified subtree(doc, nsnull);
FireNodeRemovedForChildren();
// Needed when innerHTML is used in combination with contenteditable
mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
// Remove childnodes.
PRUint32 childCount = GetChildCount();
for (PRUint32 i = 0; i < childCount; ++i) {
RemoveChildAt(0, true);
}
nsAutoScriptLoaderDisabler sld(doc);
nsresult rv = NS_OK;
if (doc->IsHTML()) {
PRInt32 oldChildCount = GetChildCount();
rv = nsContentUtils::ParseFragmentHTML(aInnerHTML,
this,
Tag(),
GetNameSpaceID(),
doc->GetCompatibilityMode() ==
eCompatibility_NavQuirks,
true);
// HTML5 parser has notified, but not fired mutation events.
FireMutationEventsForDirectParsing(doc, this, oldChildCount);
} else {
nsCOMPtr<nsIDOMDocumentFragment> df;
rv = nsContentUtils::CreateContextualFragment(this, aInnerHTML,
true,
getter_AddRefs(df));
nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
if (NS_SUCCEEDED(rv)) {
// Suppress assertion about node removal mutation events that can't have
// listeners anyway, because no one has had the chance to register mutation
// listeners on the fragment that comes from the parser.
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
static_cast<nsINode*>(this)->AppendChild(fragment, &rv);
}
}
return rv;
}
NS_IMETHODIMP
nsGenericHTMLElement::SetOuterHTML(const nsAString& aOuterHTML)
{
nsINode* parent = GetNodeParent();
if (!parent) {
return NS_OK;
}
if (parent->NodeType() == nsIDOMNode::DOCUMENT_NODE) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
if (OwnerDoc()->IsHTML()) {
nsIAtom* localName;
PRInt32 namespaceID;
if (parent->IsElement()) {
localName = static_cast<nsIContent*>(parent)->Tag();
namespaceID = static_cast<nsIContent*>(parent)->GetNameSpaceID();
} else {
NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
"How come the parent isn't a document, a fragment or an element?");
localName = nsGkAtoms::body;
namespaceID = kNameSpaceID_XHTML;
}
nsCOMPtr<nsIDOMDocumentFragment> df;
nsresult rv = NS_NewDocumentFragment(getter_AddRefs(df),
OwnerDoc()->NodeInfoManager());
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIContent> fragment = do_QueryInterface(df);
nsContentUtils::ParseFragmentHTML(aOuterHTML,
fragment,
localName,
namespaceID,
OwnerDoc()->GetCompatibilityMode() ==
eCompatibility_NavQuirks,
PR_TRUE);
parent->ReplaceChild(fragment, this, &rv);
return rv;
}
nsCOMPtr<nsINode> context;
if (parent->IsElement()) {
context = parent;
} else {
NS_ASSERTION(parent->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
"How come the parent isn't a document, a fragment or an element?");
nsCOMPtr<nsINodeInfo> info =
OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::body,
nsnull,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
}
nsCOMPtr<nsIDOMDocumentFragment> df;
nsresult rv = nsContentUtils::CreateContextualFragment(context,
aOuterHTML,
PR_TRUE,
getter_AddRefs(df));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
parent->ReplaceChild(fragment, this, &rv);
return rv;
}
enum nsAdjacentPosition {
eBeforeBegin,
eAfterBegin,
eBeforeEnd,
eAfterEnd
};
NS_IMETHODIMP
nsGenericHTMLElement::InsertAdjacentHTML(const nsAString& aPosition,
const nsAString& aText)
{
nsAdjacentPosition position;
if (aPosition.LowerCaseEqualsLiteral("beforebegin")) {
position = eBeforeBegin;
} else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) {
position = eAfterBegin;
} else if (aPosition.LowerCaseEqualsLiteral("beforeend")) {
position = eBeforeEnd;
} else if (aPosition.LowerCaseEqualsLiteral("afterend")) {
position = eAfterEnd;
} else {
return NS_ERROR_DOM_SYNTAX_ERR;
}
nsCOMPtr<nsIContent> destination;
if (position == eBeforeBegin || position == eAfterEnd) {
destination = GetParent();
if (!destination) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
} else {
destination = this;
}
nsIDocument* doc = OwnerDoc();
// Needed when insertAdjacentHTML is used in combination with contenteditable
mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
nsAutoScriptLoaderDisabler sld(doc);
// Batch possible DOMSubtreeModified events.
mozAutoSubtreeModified subtree(doc, nsnull);
nsresult rv;
// Parse directly into destination if possible
if (doc->IsHTML() &&
(position == eBeforeEnd ||
(position == eAfterEnd && !GetNextSibling()) ||
(position == eAfterBegin && !GetFirstChild()))) {
PRInt32 oldChildCount = destination->GetChildCount();
PRInt32 contextNs = destination->GetNameSpaceID();
nsIAtom* contextLocal = destination->Tag();
if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
// For compat with IE6 through IE9. Willful violation of HTML5 as of
// 2011-04-06. CreateContextualFragment does the same already.
// Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
contextLocal = nsGkAtoms::body;
}
rv = nsContentUtils::ParseFragmentHTML(aText,
destination,
contextLocal,
contextNs,
doc->GetCompatibilityMode() ==
eCompatibility_NavQuirks,
true);
// HTML5 parser has notified, but not fired mutation events.
FireMutationEventsForDirectParsing(doc, destination, oldChildCount);
return rv;
}
// couldn't parse directly
nsCOMPtr<nsIDOMDocumentFragment> df;
rv = nsContentUtils::CreateContextualFragment(destination,
aText,
true,
getter_AddRefs(df));
nsCOMPtr<nsINode> fragment = do_QueryInterface(df);
NS_ENSURE_SUCCESS(rv, rv);
// Suppress assertion about node removal mutation events that can't have
// listeners anyway, because no one has had the chance to register mutation
// listeners on the fragment that comes from the parser.
nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
switch (position) {
case eBeforeBegin:
destination->InsertBefore(fragment, this, &rv);
break;
case eAfterBegin:
static_cast<nsINode*>(this)->InsertBefore(fragment, GetFirstChild(), &rv);
break;
case eBeforeEnd:
static_cast<nsINode*>(this)->AppendChild(fragment, &rv);
break;
case eAfterEnd:
destination->InsertBefore(fragment, GetNextSibling(), &rv);
break;
default:
NS_NOTREACHED("Bad position.");
break;
}
return rv;
}
nsresult
nsGenericHTMLElement::ScrollIntoView(bool aTop, PRUint8 optional_argc)
{
nsIDocument *document = GetCurrentDoc();
if (!document) {
return NS_OK;
}
// Get the presentation shell
nsCOMPtr<nsIPresShell> presShell = document->GetShell();
if (!presShell) {
return NS_OK;
}
if (!optional_argc) {
aTop = true;
}
PRIntn vpercent = aTop ? NS_PRESSHELL_SCROLL_TOP :
NS_PRESSHELL_SCROLL_BOTTOM;
presShell->ScrollContentIntoView(this, vpercent,
NS_PRESSHELL_SCROLL_ANYWHERE,
nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLElement::GetSpellcheck(bool* aSpellcheck)
{
NS_ENSURE_ARG_POINTER(aSpellcheck);
*aSpellcheck = false; // Default answer is to not spellcheck
// Has the state has been explicitly set?
nsIContent* node;
for (node = this; node; node = node->GetParent()) {
if (node->IsHTML()) {
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::_true, &nsGkAtoms::_false, nsnull};
switch (node->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::spellcheck,
strings, eCaseMatters)) {
case 0: // spellcheck = "true"
*aSpellcheck = true;
// Fall through
case 1: // spellcheck = "false"
return NS_OK;
}
}
}
// Is this a chrome element?
if (nsContentUtils::IsChromeDoc(OwnerDoc())) {
return NS_OK; // Not spellchecked by default
}
if (IsCurrentBodyElement()) {
nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(GetCurrentDoc());
if (doc) {
*aSpellcheck = doc->IsEditingOn();
}
return NS_OK;
}
// Is this element editable?
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(this);
if (!formControl) {
return NS_OK; // Not spellchecked by default
}
// Is this a multiline plaintext input?
PRInt32 controlType = formControl->GetType();
if (controlType == NS_FORM_TEXTAREA) {
*aSpellcheck = true; // Spellchecked by default
return NS_OK;
}
// Is this anything other than an input text?
// Other inputs are not spellchecked.
if (controlType != NS_FORM_INPUT_TEXT) {
return NS_OK; // Not spellchecked by default
}
// Does the user want input text spellchecked by default?
// NOTE: Do not reflect a pref value of 0 back to the DOM getter.
// The web page should not know if the user has disabled spellchecking.
// We'll catch this in the editor itself.
PRInt32 spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
if (spellcheckLevel == 2) { // "Spellcheck multi- and single-line"
*aSpellcheck = true; // Spellchecked by default
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLElement::SetSpellcheck(bool aSpellcheck)
{
if (aSpellcheck) {
return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("true"));
}
return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("false"));
}
NS_IMETHODIMP
nsGenericHTMLElement::GetDraggable(bool* aDraggable)
{
*aDraggable = AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
nsGkAtoms::_true, eIgnoreCase);
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLElement::SetDraggable(bool aDraggable)
{
return SetAttrHelper(nsGkAtoms::draggable,
aDraggable ? NS_LITERAL_STRING("true")
: NS_LITERAL_STRING("false"));
}
bool
nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
{
return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
}
void
nsGenericHTMLElement::UpdateEditableState(bool aNotify)
{
// XXX Should we do this only when in a document?
ContentEditableTristate value = GetContentEditableValue();
if (value != eInherit) {
DoSetEditableFlag(!!value, aNotify);
return;
}
nsStyledElement::UpdateEditableState(aNotify);
}
nsresult
nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument) {
RegAccessKey();
if (HasName()) {
aDocument->
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
}
if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, +1);
}
}
}
return rv;
}
void
nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
if (IsInDoc()) {
UnregAccessKey();
}
RemoveFromNameTable();
if (GetContentEditableValue() == eTrue) {
nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetCurrentDoc());
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, -1);
}
}
nsStyledElement::UnbindFromTree(aDeep, aNullParent);
}
nsHTMLFormElement*
nsGenericHTMLElement::FindAncestorForm(nsHTMLFormElement* aCurrentForm)
{
NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::form),
"FindAncestorForm should not be called if @form is set!");
// Make sure we don't end up finding a form that's anonymous from
// our point of view.
nsIContent* bindingParent = GetBindingParent();
nsIContent* content = this;
while (content != bindingParent && content) {
// If the current ancestor is a form, return it as our form
if (content->IsHTML(nsGkAtoms::form)) {
#ifdef DEBUG
if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
// It's possible that we started unbinding at |content| or
// some ancestor of it, and |content| and |this| used to all be
// anonymous. Check for this the hard way.
for (nsIContent* child = this; child != content;
child = child->GetParent()) {
NS_ASSERTION(child->GetParent()->IndexOf(child) != -1,
"Walked too far?");
}
}
#endif
return static_cast<nsHTMLFormElement*>(content);
}
nsIContent *prevContent = content;
content = prevContent->GetParent();
if (!content && aCurrentForm) {
// We got to the root of the subtree we're in, and we're being removed
// from the DOM (the only time we get into this method with a non-null
// aCurrentForm). Check whether aCurrentForm is in the same subtree. If
// it is, we want to return aCurrentForm, since this case means that
// we're one of those inputs-in-a-table that have a hacked mForm pointer
// and a subtree containing both us and the form got removed from the
// DOM.
if (nsContentUtils::ContentIsDescendantOf(aCurrentForm, prevContent)) {
return aCurrentForm;
}
}
}
return nsnull;
}
static bool
IsArea(nsIContent *aContent)
{
return (aContent->Tag() == nsGkAtoms::area &&
aContent->IsHTML());
}
bool
nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(nsEventChainVisitor& aVisitor)
{
NS_PRECONDITION(nsCOMPtr<nsILink>(do_QueryInterface(this)),
"should be called only when |this| implements |nsILink|");
if (!aVisitor.mPresContext) {
// We need a pres context to do link stuff. Some events (e.g. mutation
// events) don't have one.
// XXX: ideally, shouldn't we be able to do what we need without one?
return false;
}
//Need to check if we hit an imagemap area and if so see if we're handling
//the event on that map or on a link farther up the tree. If we're on a
//link farther up, do nothing.
nsCOMPtr<nsIContent> target = aVisitor.mPresContext->EventStateManager()->
GetEventTargetContent(aVisitor.mEvent);
return !target || !IsArea(target) || IsArea(this);
}
nsresult
nsGenericHTMLElement::PreHandleEventForAnchors(nsEventChainPreVisitor& aVisitor)
{
nsresult rv = nsGenericHTMLElementBase::PreHandleEvent(aVisitor);
NS_ENSURE_SUCCESS(rv, rv);
if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
return NS_OK;
}
return PreHandleEventForLinks(aVisitor);
}
nsresult
nsGenericHTMLElement::PostHandleEventForAnchors(nsEventChainPostVisitor& aVisitor)
{
if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
return NS_OK;
}
return PostHandleEventForLinks(aVisitor);
}
bool
nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const
{
NS_PRECONDITION(aURI, "Must provide aURI out param");
*aURI = GetHrefURIForAnchors().get();
// We promise out param is non-null if we return true, so base rv on it
return *aURI != nsnull;
}
already_AddRefed<nsIURI>
nsGenericHTMLElement::GetHrefURIForAnchors() const
{
// This is used by the three nsILink implementations and
// nsHTMLStyleElement.
// Get href= attribute (relative URI).
// We use the nsAttrValue's copy of the URI string to avoid copying.
nsCOMPtr<nsIURI> uri;
GetURIAttr(nsGkAtoms::href, nsnull, getter_AddRefs(uri));
return uri.forget();
}
nsresult
nsGenericHTMLElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAString* aValue, bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML) && aValue) {
nsresult rv = AddScriptEventListener(aName, *aValue);
NS_ENSURE_SUCCESS(rv, rv);
}
else if (aNotify && aName == nsGkAtoms::spellcheck) {
SyncEditorsOnSubtree(this);
}
}
return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
aValue, aNotify);
}
nsEventListenerManager*
nsGenericHTMLElement::GetEventListenerManagerForAttr(nsIAtom* aAttrName,
bool* aDefer)
{
// Attributes on the body and frameset tags get set on the global object
if ((mNodeInfo->Equals(nsGkAtoms::body) ||
mNodeInfo->Equals(nsGkAtoms::frameset)) &&
// We only forward some event attributes from body/frameset to window
(0
#define EVENT(name_, id_, type_, struct_) /* nothing */
#define FORWARDED_EVENT(name_, id_, type_, struct_) \
|| nsGkAtoms::on##name_ == aAttrName
#define WINDOW_EVENT FORWARDED_EVENT
#include "nsEventNameList.h"
#undef WINDOW_EVENT
#undef FORWARDED_EVENT
#undef EVENT
)
) {
nsPIDOMWindow *win;
// If we have a document, and it has a window, add the event
// listener on the window (the inner window). If not, proceed as
// normal.
// XXXbz sXBL/XBL2 issue: should we instead use GetCurrentDoc() here,
// override BindToTree for those classes and munge event listeners there?
nsIDocument *document = OwnerDoc();
// FIXME (https://bugzilla.mozilla.org/show_bug.cgi?id=431767)
// nsDocument::GetInnerWindow can return an outer window in some cases,
// we don't want to stick an event listener on an outer window, so
// bail if it does. See similar code in nsHTMLBodyElement and
// nsHTMLFramesetElement
*aDefer = false;
if ((win = document->GetInnerWindow()) && win->IsInnerWindow()) {
nsCOMPtr<nsIDOMEventTarget> piTarget(do_QueryInterface(win));
return piTarget->GetListenerManager(true);
}
return nsnull;
}
return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName,
aDefer);
}
nsresult
nsGenericHTMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
bool contentEditable = aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::contenteditable;
bool accessKey = aName == nsGkAtoms::accesskey &&
aNameSpaceID == kNameSpaceID_None;
PRInt32 change = 0;
if (contentEditable) {
change = GetContentEditableValue() == eTrue ? -1 : 0;
SetMayHaveContentEditableAttr();
}
if (accessKey) {
UnregAccessKey();
}
nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (contentEditable) {
if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) {
change += 1;
}
ChangeEditableState(change);
}
if (accessKey && !aValue.IsEmpty()) {
SetFlags(NODE_HAS_ACCESSKEY);
RegAccessKey();
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
bool contentEditable = false;
PRInt32 contentEditableChange = 0;
// Check for event handlers
if (aNameSpaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::name) {
// Have to do this before clearing flag. See RemoveFromNameTable
RemoveFromNameTable();
ClearHasName();
}
else if (aAttribute == nsGkAtoms::contenteditable) {
contentEditable = true;
contentEditableChange = GetContentEditableValue() == eTrue ? -1 : 0;
}
else if (aAttribute == nsGkAtoms::accesskey) {
// Have to unregister before clearing flag. See UnregAccessKey
UnregAccessKey();
UnsetFlags(NODE_HAS_ACCESSKEY);
}
else if (nsContentUtils::IsEventAttributeName(aAttribute,
EventNameType_HTML)) {
nsEventListenerManager* manager = GetListenerManager(false);
if (manager) {
manager->RemoveScriptEventListener(aAttribute);
}
}
// Remove dataset property if necessary.
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots && slots->mDataset) {
slots->mDataset->RemoveProp(aAttribute);
}
}
nsresult rv = nsGenericHTMLElementBase::UnsetAttr(aNameSpaceID, aAttribute,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (contentEditable) {
ChangeEditableState(contentEditableChange);
}
return NS_OK;
}
void
nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const
{
OwnerDoc()->GetBaseTarget(aBaseTarget);
}
//----------------------------------------------------------------------
static bool
CanHaveName(nsIAtom* aTag)
{
return aTag == nsGkAtoms::img ||
aTag == nsGkAtoms::form ||
aTag == nsGkAtoms::applet ||
aTag == nsGkAtoms::embed ||
aTag == nsGkAtoms::object;
}
bool
nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::dir) {
return aResult.ParseEnumValue(aValue, kDirTable, false);
}
if (aAttribute == nsGkAtoms::tabindex) {
return aResult.ParseIntWithBounds(aValue, -32768, 32767);
}
if (aAttribute == nsGkAtoms::name) {
// Store name as an atom. name="" means that the element has no name,
// not that it has an emptystring as the name.
RemoveFromNameTable();
if (aValue.IsEmpty()) {
ClearHasName();
return false;
}
aResult.ParseAtom(aValue);
if (CanHaveName(Tag())) {
SetHasName();
AddToNameTable(aResult.GetAtomValue());
}
return true;
}
if (aAttribute == nsGkAtoms::contenteditable) {
aResult.ParseAtom(aValue);
return true;
}
}
return nsGenericHTMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
aValue, aResult);
}
bool
nsGenericHTMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
{
static const MappedAttributeEntry* const map[] = {
sCommonAttributeMap
};
return FindAttributeDependence(aAttribute, map);
}
nsMapRuleToAttributesFunc
nsGenericHTMLElement::GetAttributeMappingFunction() const
{
return &MapCommonAttributesInto;
}
nsIFormControlFrame*
nsGenericHTMLElement::GetFormControlFrame(bool aFlushFrames)
{
if (aFlushFrames && IsInDoc()) {
// Cause a flush of the frames, so we get up-to-date frame information
GetCurrentDoc()->FlushPendingNotifications(Flush_Frames);
}
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
nsIFormControlFrame* form_frame = do_QueryFrame(frame);
if (form_frame) {
return form_frame;
}
// If we have generated content, the primary frame will be a
// wrapper frame.. out real frame will be in its child list.
for (frame = frame->GetFirstPrincipalChild();
frame;
frame = frame->GetNextSibling()) {
form_frame = do_QueryFrame(frame);
if (form_frame) {
return form_frame;
}
}
}
return nsnull;
}
/* static */ nsresult
nsGenericHTMLElement::GetPrimaryPresState(nsGenericHTMLElement* aContent,
nsPresState** aPresState)
{
NS_ENSURE_ARG_POINTER(aPresState);
*aPresState = nsnull;
nsresult result = NS_OK;
nsCOMPtr<nsILayoutHistoryState> history;
nsCAutoString key;
GetLayoutHistoryAndKey(aContent, false, getter_AddRefs(history), key);
if (history) {
// Get the pres state for this key, if it doesn't exist, create one
result = history->GetState(key, aPresState);
if (!*aPresState) {
*aPresState = new nsPresState();
result = history->AddState(key, *aPresState);
}
}
return result;
}
nsresult
nsGenericHTMLElement::GetLayoutHistoryAndKey(nsGenericHTMLElement* aContent,
bool aRead,
nsILayoutHistoryState** aHistory,
nsACString& aKey)
{
//
// Get the pres shell
//
nsCOMPtr<nsIDocument> doc = aContent->GetDocument();
if (!doc) {
return NS_OK;
}
//
// Get the history (don't bother with the key if the history is not there)
//
*aHistory = doc->GetLayoutHistoryState().get();
if (!*aHistory) {
return NS_OK;
}
if (aRead && !(*aHistory)->HasStates()) {
NS_RELEASE(*aHistory);
return NS_OK;
}
//
// Get the state key
//
nsresult rv = nsContentUtils::GenerateStateKey(aContent, doc,
nsIStatefulFrame::eNoID,
aKey);
if (NS_FAILED(rv)) {
NS_RELEASE(*aHistory);
return rv;
}
// If the state key is blank, this is anonymous content or for
// whatever reason we are not supposed to save/restore state.
if (aKey.IsEmpty()) {
NS_RELEASE(*aHistory);
return NS_OK;
}
// Add something unique to content so layout doesn't muck us up
aKey += "-C";
return rv;
}
bool
nsGenericHTMLElement::RestoreFormControlState(nsGenericHTMLElement* aContent,
nsIFormControl* aControl)
{
nsCOMPtr<nsILayoutHistoryState> history;
nsCAutoString key;
GetLayoutHistoryAndKey(aContent, true,
getter_AddRefs(history), key);
if (!history) {
return false;
}
nsPresState *state;
// Get the pres state for this key
nsresult rv = history->GetState(key, &state);
if (NS_SUCCEEDED(rv) && state) {
bool result = aControl->RestoreState(state);
history->RemoveState(key);
return result;
}
return false;
}
// XXX This creates a dependency between content and frames
nsPresContext*
nsGenericHTMLElement::GetPresContext()
{
// Get the document
nsIDocument* doc = GetDocument();
if (doc) {
// Get presentation shell.
nsIPresShell *presShell = doc->GetShell();
if (presShell) {
return presShell->GetPresContext();
}
}
return nsnull;
}
static const nsAttrValue::EnumTable kAlignTable[] = {
{ "left", NS_STYLE_TEXT_ALIGN_LEFT },
{ "right", NS_STYLE_TEXT_ALIGN_RIGHT },
{ "top", NS_STYLE_VERTICAL_ALIGN_TOP },
{ "middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE },
{ "bottom", NS_STYLE_VERTICAL_ALIGN_BASELINE },
{ "center", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE },
{ "baseline", NS_STYLE_VERTICAL_ALIGN_BASELINE },
{ "texttop", NS_STYLE_VERTICAL_ALIGN_TEXT_TOP },
{ "absmiddle", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
{ "abscenter", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
{ "absbottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM },
{ 0 }
};
static const nsAttrValue::EnumTable kDivAlignTable[] = {
{ "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
{ "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
{ "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
{ "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
{ "justify", NS_STYLE_TEXT_ALIGN_JUSTIFY },
{ 0 }
};
static const nsAttrValue::EnumTable kFrameborderTable[] = {
{ "yes", NS_STYLE_FRAME_YES },
{ "no", NS_STYLE_FRAME_NO },
{ "1", NS_STYLE_FRAME_1 },
{ "0", NS_STYLE_FRAME_0 },
{ 0 }
};
static const nsAttrValue::EnumTable kScrollingTable[] = {
{ "yes", NS_STYLE_FRAME_YES },
{ "no", NS_STYLE_FRAME_NO },
{ "on", NS_STYLE_FRAME_ON },
{ "off", NS_STYLE_FRAME_OFF },
{ "scroll", NS_STYLE_FRAME_SCROLL },
{ "noscroll", NS_STYLE_FRAME_NOSCROLL },
{ "auto", NS_STYLE_FRAME_AUTO },
{ 0 }
};
static const nsAttrValue::EnumTable kTableVAlignTable[] = {
{ "top", NS_STYLE_VERTICAL_ALIGN_TOP },
{ "middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
{ "bottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM },
{ "baseline",NS_STYLE_VERTICAL_ALIGN_BASELINE },
{ 0 }
};
bool
nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kAlignTable, false);
}
//----------------------------------------
static const nsAttrValue::EnumTable kTableHAlignTable[] = {
{ "left", NS_STYLE_TEXT_ALIGN_LEFT },
{ "right", NS_STYLE_TEXT_ALIGN_RIGHT },
{ "center", NS_STYLE_TEXT_ALIGN_CENTER },
{ "char", NS_STYLE_TEXT_ALIGN_CHAR },
{ "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
{ 0 }
};
bool
nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kTableHAlignTable, false);
}
//----------------------------------------
// This table is used for td, th, tr, col, thead, tbody and tfoot.
static const nsAttrValue::EnumTable kTableCellHAlignTable[] = {
{ "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
{ "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
{ "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
{ "char", NS_STYLE_TEXT_ALIGN_CHAR },
{ "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
{ "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
{ "absmiddle", NS_STYLE_TEXT_ALIGN_CENTER },
{ 0 }
};
bool
nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false);
}
//----------------------------------------
bool
nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kTableVAlignTable, false);
}
bool
nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kDivAlignTable, false);
}
bool
nsGenericHTMLElement::ParseImageAttribute(nsIAtom* aAttribute,
const nsAString& aString,
nsAttrValue& aResult)
{
if ((aAttribute == nsGkAtoms::width) ||
(aAttribute == nsGkAtoms::height)) {
return aResult.ParseSpecialIntValue(aString);
}
if ((aAttribute == nsGkAtoms::hspace) ||
(aAttribute == nsGkAtoms::vspace) ||
(aAttribute == nsGkAtoms::border)) {
return aResult.ParseIntWithBounds(aString, 0);
}
return false;
}
bool
nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kFrameborderTable, false);
}
bool
nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
nsAttrValue& aResult)
{
return aResult.ParseEnumValue(aString, kScrollingTable, false);
}
/**
* Handle attributes common to all html elements
*/
void
nsGenericHTMLElement::MapCommonAttributesExceptHiddenInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(UserInterface)) {
nsCSSValue* userModify = aData->ValueForUserModify();
if (userModify->GetUnit() == eCSSUnit_Null) {
const nsAttrValue* value =
aAttributes->GetAttr(nsGkAtoms::contenteditable);
if (value) {
if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
userModify->SetIntValue(NS_STYLE_USER_MODIFY_READ_WRITE,
eCSSUnit_Enumerated);
}
else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) {
userModify->SetIntValue(NS_STYLE_USER_MODIFY_READ_ONLY,
eCSSUnit_Enumerated);
}
}
}
}
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Visibility)) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::lang);
if (value && value->Type() == nsAttrValue::eString) {
aData->ValueForLang()->SetStringValue(value->GetStringValue(),
eCSSUnit_Ident);
}
}
}
void
nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
nsGenericHTMLElement::MapCommonAttributesExceptHiddenInto(aAttributes, aData);
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
nsCSSValue* display = aData->ValueForDisplay();
if (display->GetUnit() == eCSSUnit_Null) {
if (aAttributes->IndexOfAttr(nsGkAtoms::hidden, kNameSpaceID_None) >= 0) {
display->SetIntValue(NS_STYLE_DISPLAY_NONE, eCSSUnit_Enumerated);
}
}
}
}
void
nsGenericHTMLFormElement::UpdateEditableFormControlState(bool aNotify)
{
// nsCSSFrameConstructor::MaybeConstructLazily is based on the logic of this
// function, so should be kept in sync with that.
ContentEditableTristate value = GetContentEditableValue();
if (value != eInherit) {
DoSetEditableFlag(!!value, aNotify);
return;
}
nsIContent *parent = GetParent();
if (parent && parent->HasFlag(NODE_IS_EDITABLE)) {
DoSetEditableFlag(true, aNotify);
return;
}
if (!IsTextControl(false)) {
DoSetEditableFlag(false, aNotify);
return;
}
// If not contentEditable we still need to check the readonly attribute.
bool roState;
GetBoolAttr(nsGkAtoms::readonly, &roState);
DoSetEditableFlag(!roState, aNotify);
}
/* static */ const nsGenericHTMLElement::MappedAttributeEntry
nsGenericHTMLElement::sCommonAttributeMap[] = {
{ &nsGkAtoms::contenteditable },
{ &nsGkAtoms::lang },
{ &nsGkAtoms::hidden },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {
{ &nsGkAtoms::width },
{ &nsGkAtoms::height },
{ &nsGkAtoms::hspace },
{ &nsGkAtoms::vspace },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sImageAlignAttributeMap[] = {
{ &nsGkAtoms::align },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sDivAlignAttributeMap[] = {
{ &nsGkAtoms::align },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sImageBorderAttributeMap[] = {
{ &nsGkAtoms::border },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sBackgroundAttributeMap[] = {
{ &nsGkAtoms::background },
{ &nsGkAtoms::bgcolor },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sBackgroundColorAttributeMap[] = {
{ &nsGkAtoms::bgcolor },
{ nsnull }
};
/* static */ const nsGenericElement::MappedAttributeEntry
nsGenericHTMLElement::sScrollingAttributeMap[] = {
{ &nsGkAtoms::scrolling },
{ nsnull }
};
void
nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes,
nsRuleData* aRuleData)
{
if (aRuleData->mSIDs & (NS_STYLE_INHERIT_BIT(Display) |
NS_STYLE_INHERIT_BIT(TextReset))) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum) {
PRInt32 align = value->GetEnumValue();
if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
nsCSSValue* cssFloat = aRuleData->ValueForCssFloat();
if (cssFloat->GetUnit() == eCSSUnit_Null) {
if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
cssFloat->SetIntValue(NS_STYLE_FLOAT_LEFT, eCSSUnit_Enumerated);
} else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
cssFloat->SetIntValue(NS_STYLE_FLOAT_RIGHT, eCSSUnit_Enumerated);
}
}
}
if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
nsCSSValue* verticalAlign = aRuleData->ValueForVerticalAlign();
if (verticalAlign->GetUnit() == eCSSUnit_Null) {
switch (align) {
case NS_STYLE_TEXT_ALIGN_LEFT:
case NS_STYLE_TEXT_ALIGN_RIGHT:
break;
default:
verticalAlign->SetIntValue(align, eCSSUnit_Enumerated);
break;
}
}
}
}
}
}
void
nsGenericHTMLElement::MapDivAlignAttributeInto(const nsMappedAttributes* aAttributes,
nsRuleData* aRuleData)
{
if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
nsCSSValue* textAlign = aRuleData->ValueForTextAlign();
if (textAlign->GetUnit() == eCSSUnit_Null) {
// align: enum
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
if (value && value->Type() == nsAttrValue::eEnum)
textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
}
}
}
void
nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Margin)))
return;
const nsAttrValue* value;
// hspace: value
value = aAttributes->GetAttr(nsGkAtoms::hspace);
if (value) {
nsCSSValue hval;
if (value->Type() == nsAttrValue::eInteger)
hval.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
else if (value->Type() == nsAttrValue::ePercent)
hval.SetPercentValue(value->GetPercentValue());
if (hval.GetUnit() != eCSSUnit_Null) {
nsCSSValue* left = aData->ValueForMarginLeftValue();
if (left->GetUnit() == eCSSUnit_Null)
*left = hval;
nsCSSValue* right = aData->ValueForMarginRightValue();
if (right->GetUnit() == eCSSUnit_Null)
*right = hval;
}
}
// vspace: value
value = aAttributes->GetAttr(nsGkAtoms::vspace);
if (value) {
nsCSSValue vval;
if (value->Type() == nsAttrValue::eInteger)
vval.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
else if (value->Type() == nsAttrValue::ePercent)
vval.SetPercentValue(value->GetPercentValue());
if (vval.GetUnit() != eCSSUnit_Null) {
nsCSSValue* top = aData->ValueForMarginTop();
if (top->GetUnit() == eCSSUnit_Null)
*top = vval;
nsCSSValue* bottom = aData->ValueForMarginBottom();
if (bottom->GetUnit() == eCSSUnit_Null)
*bottom = vval;
}
}
}
void
nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)))
return;
// width: value
nsCSSValue* width = aData->ValueForWidth();
if (width->GetUnit() == eCSSUnit_Null) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
if (value && value->Type() == nsAttrValue::eInteger)
width->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
else if (value && value->Type() == nsAttrValue::ePercent)
width->SetPercentValue(value->GetPercentValue());
}
// height: value
nsCSSValue* height = aData->ValueForHeight();
if (height->GetUnit() == eCSSUnit_Null) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
if (value && value->Type() == nsAttrValue::eInteger)
height->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
else if (value && value->Type() == nsAttrValue::ePercent)
height->SetPercentValue(value->GetPercentValue());
}
}
void
nsGenericHTMLElement::MapImageBorderAttributeInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Border)))
return;
// border: pixels
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::border);
if (!value)
return;
nscoord val = 0;
if (value->Type() == nsAttrValue::eInteger)
val = value->GetIntegerValue();
nsCSSValue* borderLeftWidth = aData->ValueForBorderLeftWidthValue();
if (borderLeftWidth->GetUnit() == eCSSUnit_Null)
borderLeftWidth->SetFloatValue((float)val, eCSSUnit_Pixel);
nsCSSValue* borderTopWidth = aData->ValueForBorderTopWidth();
if (borderTopWidth->GetUnit() == eCSSUnit_Null)
borderTopWidth->SetFloatValue((float)val, eCSSUnit_Pixel);
nsCSSValue* borderRightWidth = aData->ValueForBorderRightWidthValue();
if (borderRightWidth->GetUnit() == eCSSUnit_Null)
borderRightWidth->SetFloatValue((float)val, eCSSUnit_Pixel);
nsCSSValue* borderBottomWidth = aData->ValueForBorderBottomWidth();
if (borderBottomWidth->GetUnit() == eCSSUnit_Null)
borderBottomWidth->SetFloatValue((float)val, eCSSUnit_Pixel);
nsCSSValue* borderLeftStyle = aData->ValueForBorderLeftStyleValue();
if (borderLeftStyle->GetUnit() == eCSSUnit_Null)
borderLeftStyle->SetIntValue(NS_STYLE_BORDER_STYLE_SOLID, eCSSUnit_Enumerated);
nsCSSValue* borderTopStyle = aData->ValueForBorderTopStyle();
if (borderTopStyle->GetUnit() == eCSSUnit_Null)
borderTopStyle->SetIntValue(NS_STYLE_BORDER_STYLE_SOLID, eCSSUnit_Enumerated);
nsCSSValue* borderRightStyle = aData->ValueForBorderRightStyleValue();
if (borderRightStyle->GetUnit() == eCSSUnit_Null)
borderRightStyle->SetIntValue(NS_STYLE_BORDER_STYLE_SOLID, eCSSUnit_Enumerated);
nsCSSValue* borderBottomStyle = aData->ValueForBorderBottomStyle();
if (borderBottomStyle->GetUnit() == eCSSUnit_Null)
borderBottomStyle->SetIntValue(NS_STYLE_BORDER_STYLE_SOLID, eCSSUnit_Enumerated);
nsCSSValue* borderLeftColor = aData->ValueForBorderLeftColorValue();
if (borderLeftColor->GetUnit() == eCSSUnit_Null)
borderLeftColor->SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
nsCSSValue* borderTopColor = aData->ValueForBorderTopColor();
if (borderTopColor->GetUnit() == eCSSUnit_Null)
borderTopColor->SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
nsCSSValue* borderRightColor = aData->ValueForBorderRightColorValue();
if (borderRightColor->GetUnit() == eCSSUnit_Null)
borderRightColor->SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
nsCSSValue* borderBottomColor = aData->ValueForBorderBottomColor();
if (borderBottomColor->GetUnit() == eCSSUnit_Null)
borderBottomColor->SetIntValue(NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, eCSSUnit_Enumerated);
}
void
nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)))
return;
nsPresContext* presContext = aData->mPresContext;
nsCSSValue* backImage = aData->ValueForBackgroundImage();
if (backImage->GetUnit() == eCSSUnit_Null &&
presContext->UseDocumentColors()) {
// background
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::background);
if (value && value->Type() == nsAttrValue::eString) {
const nsString& spec = value->GetStringValue();
if (!spec.IsEmpty()) {
// Resolve url to an absolute url
// XXX this breaks if the HTML element has an xml:base
// attribute (the xml:base will not be taken into account)
// as well as elements with _baseHref set. We need to be able
// to get to the element somehow, or store the base URI in the
// attributes.
nsIDocument* doc = presContext->Document();
nsCOMPtr<nsIURI> uri;
nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
getter_AddRefs(uri), spec, doc, doc->GetDocBaseURI());
if (NS_SUCCEEDED(rv)) {
// Note that this should generally succeed here, due to the way
// |spec| is created. Maybe we should just add an nsStringBuffer
// accessor on nsAttrValue?
nsRefPtr<nsStringBuffer> buffer = nsCSSValue::BufferFromString(spec);
if (NS_LIKELY(buffer)) {
// XXXbz it would be nice to assert that doc->NodePrincipal() is
// the same as the principal of the node (which we'd need to store
// in the mapped attrs or something?)
nsCSSValue::Image *img =
new nsCSSValue::Image(uri, buffer, doc->GetDocumentURI(),
doc->NodePrincipal(), doc);
if (NS_LIKELY(img)) {
nsCSSValueList* list = backImage->SetListValue();
list->mValue.SetImageValue(img);
}
}
}
}
else if (presContext->CompatibilityMode() == eCompatibility_NavQuirks) {
// in NavQuirks mode, allow the empty string to set the
// background to empty
nsCSSValueList* list = backImage->SetListValue();
list->mValue.SetNoneValue();
}
}
}
}
void
nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)))
return;
nsCSSValue* backColor = aData->ValueForBackgroundColor();
if (backColor->GetUnit() == eCSSUnit_Null &&
aData->mPresContext->UseDocumentColors()) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bgcolor);
nscolor color;
if (value && value->GetColorValue(color)) {
backColor->SetColorValue(color);
}
}
}
void
nsGenericHTMLElement::MapBackgroundAttributesInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
MapBackgroundInto(aAttributes, aData);
MapBGColorInto(aAttributes, aData);
}
void
nsGenericHTMLElement::MapScrollingAttributeInto(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)))
return;
// scrolling
nsCSSValue* overflowValues[2] = {
aData->ValueForOverflowX(),
aData->ValueForOverflowY(),
};
for (PRUint32 i = 0; i < ArrayLength(overflowValues); ++i) {
if (overflowValues[i]->GetUnit() == eCSSUnit_Null) {
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::scrolling);
if (value && value->Type() == nsAttrValue::eEnum) {
PRInt32 mappedValue;
switch (value->GetEnumValue()) {
case NS_STYLE_FRAME_ON:
case NS_STYLE_FRAME_SCROLL:
case NS_STYLE_FRAME_YES:
mappedValue = NS_STYLE_OVERFLOW_SCROLL;
break;
case NS_STYLE_FRAME_OFF:
case NS_STYLE_FRAME_NOSCROLL:
case NS_STYLE_FRAME_NO:
mappedValue = NS_STYLE_OVERFLOW_HIDDEN;
break;
case NS_STYLE_FRAME_AUTO:
mappedValue = NS_STYLE_OVERFLOW_AUTO;
break;
default:
NS_NOTREACHED("unexpected value");
mappedValue = NS_STYLE_OVERFLOW_AUTO;
break;
}
overflowValues[i]->SetIntValue(mappedValue, eCSSUnit_Enumerated);
}
}
}
}
//----------------------------------------------------------------------
nsresult
nsGenericHTMLElement::GetAttrHelper(nsIAtom* aAttr, nsAString& aValue)
{
GetAttr(kNameSpaceID_None, aAttr, aValue);
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetAttrHelper(nsIAtom* aAttr, const nsAString& aValue)
{
return SetAttr(kNameSpaceID_None, aAttr, aValue, true);
}
nsresult
nsGenericHTMLElement::SetBoolAttr(nsIAtom* aAttr, bool aValue)
{
if (aValue) {
return SetAttr(kNameSpaceID_None, aAttr, EmptyString(), true);
}
return UnsetAttr(kNameSpaceID_None, aAttr, true);
}
nsresult
nsGenericHTMLElement::GetBoolAttr(nsIAtom* aAttr, bool* aValue) const
{
*aValue = HasAttr(kNameSpaceID_None, aAttr);
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetIntAttr(nsIAtom* aAttr, PRInt32 aDefault, PRInt32* aResult)
{
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
*aResult = attrVal->GetIntegerValue();
}
else {
*aResult = aDefault;
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetIntAttr(nsIAtom* aAttr, PRInt32 aValue)
{
nsAutoString value;
value.AppendInt(aValue);
return SetAttr(kNameSpaceID_None, aAttr, value, true);
}
nsresult
nsGenericHTMLElement::GetUnsignedIntAttr(nsIAtom* aAttr, PRUint32 aDefault,
PRUint32* aResult)
{
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
*aResult = attrVal->GetIntegerValue();
}
else {
*aResult = aDefault;
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetUnsignedIntAttr(nsIAtom* aAttr, PRUint32 aValue)
{
nsAutoString value;
value.AppendInt(aValue);
return SetAttr(kNameSpaceID_None, aAttr, value, true);
}
nsresult
nsGenericHTMLElement::GetDoubleAttr(nsIAtom* aAttr, double aDefault, double* aResult)
{
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
if (attrVal && attrVal->Type() == nsAttrValue::eDoubleValue) {
*aResult = attrVal->GetDoubleValue();
}
else {
*aResult = aDefault;
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetDoubleAttr(nsIAtom* aAttr, double aValue)
{
nsAutoString value;
value.AppendFloat(aValue);
return SetAttr(kNameSpaceID_None, aAttr, value, true);
}
nsresult
nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsAString& aResult)
{
nsCOMPtr<nsIURI> uri;
bool hadAttr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
if (!hadAttr) {
aResult.Truncate();
return NS_OK;
}
if (!uri) {
// Just return the attr value
GetAttr(kNameSpaceID_None, aAttr, aResult);
return NS_OK;
}
nsCAutoString spec;
uri->GetSpec(spec);
CopyUTF8toUTF16(spec, aResult);
return NS_OK;
}
bool
nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsIURI** aURI) const
{
*aURI = nsnull;
const nsAttrValue* attr = mAttrsAndChildren.GetAttr(aAttr);
if (!attr) {
return false;
}
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
if (aBaseAttr) {
nsAutoString baseAttrValue;
if (GetAttr(kNameSpaceID_None, aBaseAttr, baseAttrValue)) {
nsCOMPtr<nsIURI> baseAttrURI;
nsresult rv =
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(baseAttrURI),
baseAttrValue, OwnerDoc(),
baseURI);
if (NS_FAILED(rv)) {
return true;
}
baseURI.swap(baseAttrURI);
}
}
// Don't care about return value. If it fails, we still want to
// return true, and *aURI will be null.
nsContentUtils::NewURIWithDocumentCharset(aURI,
attr->GetStringValue(),
OwnerDoc(), baseURI);
return true;
}
nsresult
nsGenericHTMLElement::GetURIListAttr(nsIAtom* aAttr, nsAString& aResult)
{
aResult.Truncate();
nsAutoString value;
if (!GetAttr(kNameSpaceID_None, aAttr, value))
return NS_OK;
nsIDocument* doc = OwnerDoc();
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
// Value contains relative URIs split on spaces (U+0020)
const PRUnichar *start = value.BeginReading();
const PRUnichar *end = value.EndReading();
const PRUnichar *iter = start;
for (;;) {
if (iter < end && *iter != ' ') {
++iter;
} else { // iter is pointing at either end or a space
while (*start == ' ' && start < iter)
++start;
if (iter != start) {
if (!aResult.IsEmpty())
aResult.Append(PRUnichar(' '));
const nsSubstring& uriPart = Substring(start, iter);
nsCOMPtr<nsIURI> attrURI;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(attrURI),
uriPart, doc, baseURI);
if (attrURI) {
nsCAutoString spec;
attrURI->GetSpec(spec);
AppendUTF8toUTF16(spec, aResult);
} else {
aResult.Append(uriPart);
}
}
start = iter = iter + 1;
if (iter >= end)
break;
}
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetEnumAttr(nsIAtom* aAttr,
const char* aDefault,
nsAString& aResult)
{
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(aAttr);
aResult.Truncate();
if (attrVal && attrVal->Type() == nsAttrValue::eEnum) {
attrVal->GetEnumString(aResult, true);
} else if (aDefault) {
AppendASCIItoUTF16(nsDependentCString(aDefault), aResult);
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetContentEditable(nsAString& aContentEditable)
{
ContentEditableTristate value = GetContentEditableValue();
if (value == eTrue) {
aContentEditable.AssignLiteral("true");
}
else if (value == eFalse) {
aContentEditable.AssignLiteral("false");
}
else {
aContentEditable.AssignLiteral("inherit");
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::SetContentEditable(const nsAString& aContentEditable)
{
nsString contentEditable;
ToLowerCase(aContentEditable, contentEditable);
if (contentEditable.EqualsLiteral("inherit")) {
UnsetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, true);
return NS_OK;
}
if (!contentEditable.EqualsLiteral("true") &&
!contentEditable.EqualsLiteral("false")) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
SetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, contentEditable,
true);
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetIsContentEditable(bool* aContentEditable)
{
NS_ENSURE_ARG_POINTER(aContentEditable);
for (nsIContent* node = this; node; node = node->GetParent()) {
nsGenericHTMLElement* element = FromContent(node);
if (element) {
ContentEditableTristate value = element->GetContentEditableValue();
if (value != eInherit) {
*aContentEditable = value == eTrue;
return NS_OK;
}
}
}
*aContentEditable = false;
return NS_OK;
}
nsresult
nsGenericHTMLElement::GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu)
{
*aContextMenu = nsnull;
nsAutoString value;
GetAttr(kNameSpaceID_None, nsGkAtoms::contextmenu, value);
if (value.IsEmpty()) {
return NS_OK;
}
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsRefPtr<nsHTMLMenuElement> element =
nsHTMLMenuElement::FromContent(doc->GetElementById(value));
element.forget(aContextMenu);
}
return NS_OK;
}
//----------------------------------------------------------------------
NS_IMPL_INT_ATTR(nsGenericHTMLFrameElement, TabIndex, tabindex)
NS_IMPL_BOOL_ATTR(nsGenericHTMLFrameElement, MozBrowser, mozbrowser)
nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
, mForm(nsnull)
, mFieldSet(nsnull)
{
// We should add the NS_EVENT_STATE_ENABLED bit here as needed, but
// that depends on our type, which is not initialized yet. So we
// have to do this in subclasses.
}
nsGenericHTMLFormElement::~nsGenericHTMLFormElement()
{
if (mFieldSet) {
mFieldSet->RemoveElement(this);
}
// Check that this element doesn't know anything about its form at this point.
NS_ASSERTION(!mForm, "mForm should be null at this point!");
}
NS_IMPL_QUERY_INTERFACE_INHERITED1(nsGenericHTMLFormElement,
nsGenericHTMLElement,
nsIFormControl)
bool
nsGenericHTMLFormElement::IsNodeOfType(PRUint32 aFlags) const
{
return !(aFlags & ~(eCONTENT | eHTML_FORM_CONTROL));
}
void
nsGenericHTMLFormElement::SaveSubtreeState()
{
SaveState();
nsGenericHTMLElement::SaveSubtreeState();
}
void
nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm)
{
NS_PRECONDITION(aForm, "Don't pass null here");
NS_ASSERTION(!mForm,
"We don't support switching from one non-null form to another.");
// keep a *weak* ref to the form here
mForm = static_cast<nsHTMLFormElement*>(aForm);
}
void
nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm)
{
NS_ASSERTION((mForm != nsnull) == HasFlag(ADDED_TO_FORM),
"Form control should have had flag set correctly");
if (!mForm) {
return;
}
if (aRemoveFromForm) {
nsAutoString nameVal, idVal;
GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
mForm->RemoveElement(this, true);
if (!nameVal.IsEmpty()) {
mForm->RemoveElementFromTable(this, nameVal);
}
if (!idVal.IsEmpty()) {
mForm->RemoveElementFromTable(this, idVal);
}
}
UnsetFlags(ADDED_TO_FORM);
mForm = nsnull;
}
Element*
nsGenericHTMLFormElement::GetFormElement()
{
return mForm;
}
nsresult
nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
{
NS_ENSURE_ARG_POINTER(aForm);
NS_IF_ADDREF(*aForm = mForm);
return NS_OK;
}
nsIContent::IMEState
nsGenericHTMLFormElement::GetDesiredIMEState()
{
nsCOMPtr<nsIEditor> editor = nsnull;
nsresult rv = GetEditorInternal(getter_AddRefs(editor));
if (NS_FAILED(rv) || !editor)
return nsGenericHTMLElement::GetDesiredIMEState();
nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
if (!imeEditor)
return nsGenericHTMLElement::GetDesiredIMEState();
IMEState state;
rv = imeEditor->GetPreferredIMEState(&state);
if (NS_FAILED(rv))
return nsGenericHTMLElement::GetDesiredIMEState();
return state;
}
bool
nsGenericHTMLFrameElement::IsHTMLFocusable(bool aWithMouse,
bool *aIsFocusable,
PRInt32 *aTabIndex)
{
if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
return true;
}
*aIsFocusable = nsContentUtils::IsSubDocumentTabbable(this);
if (!*aIsFocusable && aTabIndex) {
*aTabIndex = -1;
}
return false;
}
nsresult
nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// An autofocus event has to be launched if the autofocus attribute is
// specified and the element accept the autofocus attribute. In addition,
// the document should not be already loaded and the "browser.autofocus"
// preference should be 'true'.
if (IsAutofocusable() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) &&
Preferences::GetBool("browser.autofocus", true)) {
nsCOMPtr<nsIRunnable> event = new nsAutoFocusEvent(this);
rv = NS_DispatchToCurrentThread(event);
NS_ENSURE_SUCCESS(rv, rv);
}
// If @form is set, the element *has* to be in a document, otherwise it
// wouldn't be possible to find an element with the corresponding id.
// If @form isn't set, the element *has* to have a parent, otherwise it
// wouldn't be possible to find a form ancestor.
// We should not call UpdateFormOwner if none of these conditions are
// fulfilled.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? !!GetCurrentDoc()
: !!aParent) {
UpdateFormOwner(true, nsnull);
}
// Set parent fieldset which should be used for the disabled state.
UpdateFieldSet(false);
return NS_OK;
}
void
nsGenericHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
// Save state before doing anything
SaveState();
if (mForm) {
// Might need to unset mForm
if (aNullParent) {
// No more parent means no more form
ClearForm(true);
} else {
// Recheck whether we should still have an mForm.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
!FindAncestorForm(mForm)) {
ClearForm(true);
} else {
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
}
}
if (!mForm) {
// Our novalidate state might have changed
UpdateState(false);
}
}
// We have to remove the form id observer if there was one.
// We will re-add one later if needed (during bind to tree).
if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
nsGkAtoms::form)) {
RemoveFormIdObserver();
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
// The element might not have a fieldset anymore.
UpdateFieldSet(false);
}
nsresult
nsGenericHTMLFormElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None) {
nsAutoString tmp;
// remove the control from the hashtable as needed
if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
GetAttr(kNameSpaceID_None, aName, tmp);
if (!tmp.IsEmpty()) {
mForm->RemoveElementFromTable(this, tmp);
}
}
if (mForm && aName == nsGkAtoms::type) {
GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
if (!tmp.IsEmpty()) {
mForm->RemoveElementFromTable(this, tmp);
}
GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
if (!tmp.IsEmpty()) {
mForm->RemoveElementFromTable(this, tmp);
}
mForm->RemoveElement(this, false);
// Removing the element from the form can make it not be the default
// control anymore. Go ahead and notify on that change, though we might
// end up readding and becoming the default control again in
// AfterSetAttr.
// FIXME: Bug 656197
UpdateState(aNotify);
}
if (aName == nsGkAtoms::form) {
// If @form isn't set or set to the empty string, there were no observer
// so we don't have to remove it.
if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
nsGkAtoms::form)) {
// The current form id observer is no longer needed.
// A new one may be added in AfterSetAttr.
RemoveFormIdObserver();
}
}
}
return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
aValue, aNotify);
}
nsresult
nsGenericHTMLFormElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None) {
// add the control to the hashtable as needed
if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
aValue) {
if (!aValue->IsEmpty()) {
mForm->AddElementToTable(this, *aValue);
}
}
if (mForm && aName == nsGkAtoms::type) {
nsAutoString tmp;
GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
if (!tmp.IsEmpty()) {
mForm->AddElementToTable(this, tmp);
}
GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
if (!tmp.IsEmpty()) {
mForm->AddElementToTable(this, tmp);
}
mForm->AddElement(this, false, aNotify);
// Adding the element to the form can make it be the default control .
// Go ahead and notify on that change.
// Note: no need to notify on CanBeDisabled(), since type attr
// changes can't affect that.
UpdateState(aNotify);
}
if (aName == nsGkAtoms::form) {
// We need a new form id observer.
nsIDocument* doc = GetCurrentDoc();
if (doc) {
Element* formIdElement = nsnull;
if (aValue && !aValue->IsEmpty()) {
formIdElement = AddFormIdObserver();
}
// Because we have a new @form value (or no more @form), we have to
// update our form owner.
UpdateFormOwner(false, formIdElement);
}
}
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
aValue, aNotify);
}
nsresult
nsGenericHTMLFormElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
if (NS_IS_TRUSTED_EVENT(aVisitor.mEvent)) {
switch (aVisitor.mEvent->message) {
case NS_FOCUS_CONTENT:
{
// Check to see if focus has bubbled up from a form control's
// child textfield or button. If that's the case, don't focus
// this parent file control -- leave focus on the child.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
if (formControlFrame &&
aVisitor.mEvent->originalTarget == static_cast<nsINode*>(this))
formControlFrame->SetFocus(true, true);
break;
}
case NS_BLUR_CONTENT:
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
if (formControlFrame)
formControlFrame->SetFocus(false, false);
break;
}
}
}
return nsGenericHTMLElement::PreHandleEvent(aVisitor);
}
/* virtual */
bool
nsGenericHTMLFormElement::IsDisabled() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) ||
(mFieldSet && mFieldSet->IsDisabled());
}
void
nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset)
{
if (mFieldSet == aFieldset) {
mFieldSet = nsnull;
}
}
bool
nsGenericHTMLFormElement::CanBeDisabled() const
{
PRInt32 type = GetType();
// It's easier to test the types that _cannot_ be disabled
return
type != NS_FORM_LABEL &&
type != NS_FORM_OBJECT &&
type != NS_FORM_OUTPUT &&
type != NS_FORM_PROGRESS;
}
bool
nsGenericHTMLFormElement::IsHTMLFocusable(bool aWithMouse,
bool* aIsFocusable,
PRInt32* aTabIndex)
{
if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
return true;
}
#ifdef XP_MACOSX
*aIsFocusable =
(!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && *aIsFocusable;
#endif
return false;
}
nsEventStates
nsGenericHTMLFormElement::IntrinsicState() const
{
// If you add attribute-dependent states here, you need to add them them to
// AfterSetAttr too. And add them to AfterSetAttr for all subclasses that
// implement IntrinsicState() and are affected by that attribute.
nsEventStates state = nsGenericHTMLElement::IntrinsicState();
if (CanBeDisabled()) {
// :enabled/:disabled
if (IsDisabled()) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
}
}
if (mForm && mForm->IsDefaultSubmitElement(this)) {
NS_ASSERTION(IsSubmitControl(),
"Default submit element that isn't a submit control.");
// We are the default submit element (:default)
state |= NS_EVENT_STATE_DEFAULT;
}
return state;
}
nsGenericHTMLFormElement::FocusTristate
nsGenericHTMLFormElement::FocusState()
{
// We can't be focused if we aren't in a document
nsIDocument* doc = GetCurrentDoc();
if (!doc)
return eUnfocusable;
// first see if we are disabled or not. If disabled then do nothing.
if (IsDisabled()) {
return eUnfocusable;
}
// If the window is not active, do not allow the focus to bring the
// window to the front. We update the focus controller, but do
// nothing else.
nsPIDOMWindow* win = doc->GetWindow();
if (win) {
nsCOMPtr<nsIDOMWindow> rootWindow = do_QueryInterface(win->GetPrivateRoot());
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
if (fm && rootWindow) {
nsCOMPtr<nsIDOMWindow> activeWindow;
fm->GetActiveWindow(getter_AddRefs(activeWindow));
if (activeWindow == rootWindow) {
return eActiveWindow;
}
}
}
return eInactiveWindow;
}
Element*
nsGenericHTMLFormElement::AddFormIdObserver()
{
NS_ASSERTION(GetCurrentDoc(), "When adding a form id observer, "
"we should be in a document!");
nsAutoString formId;
nsIDocument* doc = OwnerDoc();
GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
NS_ASSERTION(!formId.IsEmpty(),
"@form value should not be the empty string!");
nsCOMPtr<nsIAtom> atom = do_GetAtom(formId);
return doc->AddIDTargetObserver(atom, FormIdUpdated, this, false);
}
void
nsGenericHTMLFormElement::RemoveFormIdObserver()
{
/**
* We are using OwnerDoc() because we don't really care about having the
* element actually being in the tree. If it is not and @form value changes,
* this method will be called for nothing but removing an observer which does
* not exist doesn't cost so much (no entry in the hash table) so having a
* boolean for GetCurrentDoc()/GetOwnerDoc() would make everything look more
* complex for nothing.
*/
nsIDocument* doc = OwnerDoc();
// At this point, we may not have a document anymore. In that case, we can't
// remove the observer. The document did that for us.
if (!doc) {
return;
}
nsAutoString formId;
GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
NS_ASSERTION(!formId.IsEmpty(),
"@form value should not be the empty string!");
nsCOMPtr<nsIAtom> atom = do_GetAtom(formId);
doc->RemoveIDTargetObserver(atom, FormIdUpdated, this, false);
}
/* static */
bool
nsGenericHTMLFormElement::FormIdUpdated(Element* aOldElement,
Element* aNewElement,
void* aData)
{
nsGenericHTMLFormElement* element =
static_cast<nsGenericHTMLFormElement*>(aData);
NS_ASSERTION(element->IsHTML(), "aData should be an HTML element");
element->UpdateFormOwner(false, aNewElement);
return true;
}
bool
nsGenericHTMLFormElement::IsElementDisabledForEvents(PRUint32 aMessage,
nsIFrame* aFrame)
{
bool disabled = IsDisabled();
if (!disabled && aFrame) {
const nsStyleUserInterface* uiStyle = aFrame->GetStyleUserInterface();
disabled = uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE ||
uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED;
}
return disabled && aMessage != NS_MOUSE_MOVE;
}
void
nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
Element* aFormIdElement)
{
NS_PRECONDITION(!aBindToTree || !aFormIdElement,
"aFormIdElement shouldn't be set if aBindToTree is true!");
bool needStateUpdate = false;
if (!aBindToTree) {
needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this);
ClearForm(true);
}
nsHTMLFormElement *oldForm = mForm;
if (!mForm) {
// If @form is set, we have to use that to find the form.
nsAutoString formId;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId)) {
if (!formId.IsEmpty()) {
Element* element = nsnull;
if (aBindToTree) {
element = AddFormIdObserver();
} else {
element = aFormIdElement;
}
NS_ASSERTION(GetCurrentDoc(), "The element should be in a document "
"when UpdateFormOwner is called!");
NS_ASSERTION(!GetCurrentDoc() ||
element == GetCurrentDoc()->GetElementById(formId),
"element should be equals to the current element "
"associated with the id in @form!");
if (element && element->IsHTML(nsGkAtoms::form)) {
mForm = static_cast<nsHTMLFormElement*>(element);
}
}
} else {
// We now have a parent, so we may have picked up an ancestor form. Search
// for it. Note that if mForm is already set we don't want to do this,
// because that means someone (probably the content sink) has already set
// it to the right value. Also note that even if being bound here didn't
// change our parent, we still need to search, since our parent chain
// probably changed _somewhere_.
mForm = FindAncestorForm();
}
}
if (mForm && !HasFlag(ADDED_TO_FORM)) {
// Now we need to add ourselves to the form
nsAutoString nameVal, idVal;
GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
SetFlags(ADDED_TO_FORM);
// Notify only if we just found this mForm.
mForm->AddElement(this, true, oldForm == nsnull);
if (!nameVal.IsEmpty()) {
mForm->AddElementToTable(this, nameVal);
}
if (!idVal.IsEmpty()) {
mForm->AddElementToTable(this, idVal);
}
}
if (mForm != oldForm || needStateUpdate) {
UpdateState(true);
}
}
void
nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify)
{
nsIContent* parent = nsnull;
nsIContent* prev = nsnull;
for (parent = GetParent(); parent;
prev = parent, parent = parent->GetParent()) {
nsHTMLFieldSetElement* fieldset =
nsHTMLFieldSetElement::FromContent(parent);
if (fieldset &&
(!prev || fieldset->GetFirstLegend() != prev)) {
if (mFieldSet == fieldset) {
// We already have the right fieldset;
return;
}
if (mFieldSet) {
mFieldSet->RemoveElement(this);
}
mFieldSet = fieldset;
fieldset->AddElement(this);
// The disabled state may have changed
FieldSetDisabledChanged(aNotify);
return;
}
}
// No fieldset found.
if (mFieldSet) {
mFieldSet->RemoveElement(this);
mFieldSet = nsnull;
// The disabled state may have changed
FieldSetDisabledChanged(aNotify);
}
}
void
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
{
UpdateState(aNotify);
}
//----------------------------------------------------------------------
nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
{
if (mFrameLoader) {
mFrameLoader->Destroy();
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mFrameLoader, nsIFrameLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_TABLE_HEAD(nsGenericHTMLFrameElement)
NS_INTERFACE_TABLE_INHERITED2(nsGenericHTMLFrameElement,
nsIFrameLoaderOwner,
nsIDOMMozBrowserFrameElement)
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsGenericHTMLFrameElement)
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
nsresult
nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
{
NS_PRECONDITION(aContentDocument, "Null out param");
*aContentDocument = nsnull;
nsCOMPtr<nsIDOMWindow> win;
GetContentWindow(getter_AddRefs(win));
if (!win) {
return NS_OK;
}
return win->GetDocument(aContentDocument);
}
nsresult
nsGenericHTMLFrameElement::GetContentWindow(nsIDOMWindow** aContentWindow)
{
NS_PRECONDITION(aContentWindow, "Null out param");
*aContentWindow = nsnull;
nsresult rv = EnsureFrameLoader();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFrameLoader) {
return NS_OK;
}
bool depthTooGreat = false;
mFrameLoader->GetDepthTooGreat(&depthTooGreat);
if (depthTooGreat) {
// Claim to have no contentWindow
return NS_OK;
}
nsCOMPtr<nsIDocShell> doc_shell;
mFrameLoader->GetDocShell(getter_AddRefs(doc_shell));
nsCOMPtr<nsPIDOMWindow> win(do_GetInterface(doc_shell));
if (!win) {
return NS_OK;
}
NS_ASSERTION(win->IsOuterWindow(),
"Uh, this window should always be an outer window!");
return CallQueryInterface(win, aContentWindow);
}
nsresult
nsGenericHTMLFrameElement::EnsureFrameLoader()
{
if (!GetParent() || !IsInDoc() || mFrameLoader) {
// If frame loader is there, we just keep it around, cached
return NS_OK;
}
mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
{
NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
return NS_OK;
}
NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
nsGenericHTMLFrameElement::GetFrameLoader()
{
nsRefPtr<nsFrameLoader> loader = mFrameLoader;
return loader.forget();
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
{
// We don't support this yet
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsGenericHTMLFrameElement::LoadSrc()
{
nsresult rv = EnsureFrameLoader();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFrameLoader) {
return NS_OK;
}
rv = mFrameLoader->LoadFrame();
#ifdef DEBUG
if (NS_FAILED(rv)) {
NS_WARNING("failed to load URL");
}
#endif
return rv;
}
nsresult
nsGenericHTMLFrameElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"Missing a script blocker!");
// We're in a document now. Kick off the frame load.
LoadSrc();
}
// We're now in document and scripts may move us, so clear
// the mNetworkCreated flag.
mNetworkCreated = false;
return rv;
}
void
nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
if (mFrameLoader) {
// This iframe is being taken out of the document, destroy the
// iframe's frame loader (doing that will tear down the window in
// this iframe).
// XXXbz we really want to only partially destroy the frame
// loader... we don't want to tear down the docshell. Food for
// later bug.
mFrameLoader->Destroy();
mFrameLoader = nsnull;
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
nsresult
nsGenericHTMLFrameElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
// Don't propagate error here. The attribute was successfully set, that's
// what we should reflect.
LoadSrc();
}
return NS_OK;
}
void
nsGenericHTMLFrameElement::DestroyContent()
{
if (mFrameLoader) {
mFrameLoader->Destroy();
mFrameLoader = nsnull;
}
nsGenericHTMLElement::DestroyContent();
}
nsresult
nsGenericHTMLFrameElement::CopyInnerTo(nsGenericElement* aDest) const
{
nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
NS_ENSURE_SUCCESS(rv, rv);
nsIDocument* doc = aDest->OwnerDoc();
if (doc->IsStaticDocument() && mFrameLoader) {
nsGenericHTMLFrameElement* dest =
static_cast<nsGenericHTMLFrameElement*>(aDest);
nsFrameLoader* fl = nsFrameLoader::Create(dest, false);
NS_ENSURE_STATE(fl);
dest->mFrameLoader = fl;
static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
}
return rv;
}
PRInt64
nsGenericHTMLFrameElement::SizeOf() const
{
PRInt64 size = MemoryReporter::GetBasicSize<nsGenericHTMLFrameElement,
nsGenericHTMLElement>(this);
// TODO: need to implement SizeOf() in nsFrameLoader, bug 672539.
size += mFrameLoader ? sizeof(*mFrameLoader.get()) : 0;
return size;
}
namespace {
// GetContentStateCallbackRunnable is used by MozGetContentState to fire its callback
// asynchronously.
class GetContentStateCallbackRunnable : public nsRunnable
{
public:
GetContentStateCallbackRunnable(nsIDOMMozGetContentStateCallback *aCallback,
nsIDOMEventTarget *aEventTarget,
const nsAString &aResult)
: mCallback(aCallback)
, mEventTarget(aEventTarget)
, mResult(aResult)
{
}
NS_IMETHOD Run()
{
FireCallback();
// Break cycles.
mCallback = NULL;
mEventTarget = NULL;
return NS_OK;
}
private:
void FireCallback()
{
nsCxPusher pusher;
if (!pusher.Push(mEventTarget)) {
return;
}
mCallback->Callback(mResult);
}
nsCOMPtr<nsIDOMMozGetContentStateCallback> mCallback;
nsCOMPtr<nsIDOMEventTarget> mEventTarget;
const nsString mResult;
};
} // anonymous namespace
nsresult
nsGenericHTMLFrameElement::BrowserFrameSecurityCheck()
{
if (!Preferences::GetBool("dom.mozBrowserFramesEnabled")) {
return NS_ERROR_FAILURE;
}
bool browser;
GetMozBrowser(&browser);
if (!browser) {
return NS_ERROR_FAILURE;
}
nsIPrincipal *principal = NodePrincipal();
nsCOMPtr<nsIURI> principalURI;
principal->GetURI(getter_AddRefs(principalURI));
if (!nsContentUtils::URIIsChromeOrInPref(principalURI,
"dom.mozBrowserFramesWhitelist")) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericHTMLFrameElement::MozGetContentState(const nsAString &aProperty,
nsIDOMMozGetContentStateCallback *aCallback)
{
nsresult rv = BrowserFrameSecurityCheck();
NS_ENSURE_SUCCESS(rv, rv);
if (!aProperty.EqualsLiteral("location")) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMWindow> contentWindow;
GetContentWindow(getter_AddRefs(contentWindow));
NS_ENSURE_TRUE(contentWindow, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMLocation> location;
rv = contentWindow->GetLocation(getter_AddRefs(location));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString href;
rv = location->ToString(href);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMEventTarget> eventTarget =
do_QueryInterface(nsContentUtils::GetWindowFromCaller());
NS_ENSURE_TRUE(eventTarget, NS_ERROR_FAILURE);
// Asynchronously fire the callback.
nsRefPtr<GetContentStateCallbackRunnable> runnable =
new GetContentStateCallbackRunnable(aCallback, eventTarget, href);
NS_DispatchToMainThread(runnable);
return NS_OK;
}
//----------------------------------------------------------------------
nsresult
nsGenericHTMLElement::Blur()
{
if (!ShouldBlur(this)) {
return NS_OK;
}
nsIDocument* doc = GetCurrentDoc();
if (!doc) {
return NS_OK;
}
nsIDOMWindow* win = doc->GetWindow();
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
return (win && fm) ? fm->ClearFocus(win) : NS_OK;
}
nsresult
nsGenericHTMLElement::Focus()
{
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(this);
return fm ? fm->SetFocus(elem, 0) : NS_OK;
}
nsresult nsGenericHTMLElement::MozRequestFullScreen()
{
// Only grant full-screen requests if this is called from inside a trusted
// event handler (i.e. inside an event handler for a user initiated event).
// This stops the full-screen from being abused similar to the popups of old,
// and it also makes it harder for bad guys' script to go full-screen and
// spoof the browser chrome/window and phish logins etc.
if (!nsContentUtils::IsRequestFullScreenAllowed()) {
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
"DOM", OwnerDoc(),
nsContentUtils::eDOM_PROPERTIES,
"FullScreenDeniedNotInputDriven");
nsRefPtr<nsAsyncDOMEvent> e =
new nsAsyncDOMEvent(OwnerDoc(),
NS_LITERAL_STRING("mozfullscreenerror"),
true,
false);
e->PostDOMEvent();
return NS_OK;
}
OwnerDoc()->AsyncRequestFullScreen(this);
return NS_OK;
}
nsresult nsGenericHTMLElement::Click()
{
if (HasFlag(NODE_HANDLING_CLICK))
return NS_OK;
// Strong in case the event kills it
nsCOMPtr<nsIDocument> doc = GetCurrentDoc();
nsCOMPtr<nsIPresShell> shell;
nsRefPtr<nsPresContext> context;
if (doc) {
shell = doc->GetShell();
if (shell) {
context = shell->GetPresContext();
}
}
SetFlags(NODE_HANDLING_CLICK);
// Click() is never called from native code, but it may be
// called from chrome JS. Mark this event trusted if Click()
// is called from chrome code.
nsMouseEvent event(nsContentUtils::IsCallerChrome(),
NS_MOUSE_CLICK, nsnull, nsMouseEvent::eReal);
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
nsEventDispatcher::Dispatch(this, context, &event);
UnsetFlags(NODE_HANDLING_CLICK);
return NS_OK;
}
bool
nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
bool *aIsFocusable,
PRInt32 *aTabIndex)
{
nsIDocument *doc = GetCurrentDoc();
if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
// In designMode documents we only allow focusing the document.
if (aTabIndex) {
*aTabIndex = -1;
}
*aIsFocusable = false;
return true;
}
PRInt32 tabIndex = 0; // Default value for non HTML elements with -moz-user-focus
GetTabIndex(&tabIndex);
bool override, disabled = false;
if (IsEditableRoot()) {
// Editable roots should always be focusable.
override = true;
// Ignore the disabled attribute in editable contentEditable/designMode
// roots.
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
// The default value for tabindex should be 0 for editable
// contentEditable roots.
tabIndex = 0;
}
}
else {
override = false;
// Just check for disabled attribute on form controls
disabled = IsDisabled();
if (disabled) {
tabIndex = -1;
}
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
// If a tabindex is specified at all, or the default tabindex is 0, we're focusable
*aIsFocusable =
(tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)));
return override;
}
void
nsGenericHTMLElement::RegUnRegAccessKey(bool aDoReg)
{
// first check to see if we have an access key
nsAutoString accessKey;
GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
if (accessKey.IsEmpty()) {
return;
}
// We have an access key, so get the ESM from the pres context.
nsPresContext *presContext = GetPresContext();
if (presContext) {
nsEventStateManager *esm = presContext->EventStateManager();
// Register or unregister as appropriate.
if (aDoReg) {
esm->RegisterAccessKey(this, (PRUint32)accessKey.First());
} else {
esm->UnregisterAccessKey(this, (PRUint32)accessKey.First());
}
}
}
void
nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation,
bool aIsTrustedEvent)
{
nsPresContext *presContext = GetPresContext();
if (!presContext)
return;
// It's hard to say what HTML4 wants us to do in all cases.
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm) {
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(this);
fm->SetFocus(elem, nsIFocusManager::FLAG_BYKEY);
}
if (aKeyCausesActivation) {
// Click on it if the users prefs indicate to do so.
nsMouseEvent event(aIsTrustedEvent, NS_MOUSE_CLICK,
nsnull, nsMouseEvent::eReal);
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ?
openAllowed : openAbused);
nsEventDispatcher::Dispatch(this, presContext, &event);
}
}
const nsAttrName*
nsGenericHTMLElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
{
if (IsInHTMLDocument()) {
nsAutoString lower;
nsContentUtils::ASCIIToLower(aStr, lower);
return mAttrsAndChildren.GetExistingAttrNameFromQName(lower);
}
return mAttrsAndChildren.GetExistingAttrNameFromQName(aStr);
}
nsresult
nsGenericHTMLElement::GetEditor(nsIEditor** aEditor)
{
*aEditor = nsnull;
if (!nsContentUtils::IsCallerTrustedForWrite()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
return GetEditorInternal(aEditor);
}
nsresult
nsGenericHTMLElement::GetEditorInternal(nsIEditor** aEditor)
{
*aEditor = nsnull;
nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
if (textCtrl) {
*aEditor = textCtrl->GetTextEditor();
NS_IF_ADDREF(*aEditor);
}
return NS_OK;
}
already_AddRefed<nsIEditor>
nsGenericHTMLElement::GetAssociatedEditor()
{
// If contenteditable is ever implemented, it might need to do something different here?
nsIEditor* editor = nsnull;
GetEditorInternal(&editor);
return editor;
}
bool
nsGenericHTMLElement::IsCurrentBodyElement()
{
// TODO Bug 698498: Should this handle the case where GetBody returns a
// frameset?
nsCOMPtr<nsIDOMHTMLBodyElement> bodyElement = do_QueryInterface(this);
if (!bodyElement) {
return false;
}
nsCOMPtr<nsIDOMHTMLDocument> htmlDocument =
do_QueryInterface(GetCurrentDoc());
if (!htmlDocument) {
return false;
}
nsCOMPtr<nsIDOMHTMLElement> htmlElement;
htmlDocument->GetBody(getter_AddRefs(htmlElement));
return htmlElement == bodyElement;
}
// static
void
nsGenericHTMLElement::SyncEditorsOnSubtree(nsIContent* content)
{
/* Sync this node */
nsGenericHTMLElement* element = FromContent(content);
if (element) {
nsCOMPtr<nsIEditor> editor = element->GetAssociatedEditor();
if (editor) {
editor->SyncRealTimeSpell();
}
}
/* Sync all children */
for (nsIContent* child = content->GetFirstChild();
child;
child = child->GetNextSibling()) {
SyncEditorsOnSubtree(child);
}
}
void
nsGenericHTMLElement::RecompileScriptEventListeners()
{
PRInt32 i, count = mAttrsAndChildren.AttrCount();
for (i = 0; i < count; ++i) {
const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i);
// Eventlistenener-attributes are always in the null namespace
if (!name->IsAtom()) {
continue;
}
nsIAtom *attr = name->Atom();
if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_HTML)) {
continue;
}
nsAutoString value;
GetAttr(kNameSpaceID_None, attr, value);
AddScriptEventListener(attr, value, true);
}
}
bool
nsGenericHTMLElement::IsEditableRoot() const
{
nsIDocument *document = GetCurrentDoc();
if (!document) {
return false;
}
if (document->HasFlag(NODE_IS_EDITABLE)) {
return false;
}
if (GetContentEditableValue() != eTrue) {
return false;
}
nsIContent *parent = GetParent();
return !parent || !parent->HasFlag(NODE_IS_EDITABLE);
}
static void
MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
{
// If aContent is not an element, we just need to update its
// internal editable state and don't need to notify anyone about
// that. For elements, we need to send a ContentStateChanged
// notification.
if (!aContent->IsElement()) {
aContent->UpdateEditableState(false);
return;
}
Element *element = aContent->AsElement();
element->UpdateEditableState(true);
for (nsIContent *child = aContent->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (!child->HasAttr(kNameSpaceID_None, nsGkAtoms::contenteditable)) {
MakeContentDescendantsEditable(child, aDocument);
}
}
}
void
nsGenericHTMLElement::ChangeEditableState(PRInt32 aChange)
{
nsIDocument* document = GetCurrentDoc();
if (!document) {
return;
}
if (aChange != 0) {
nsCOMPtr<nsIHTMLDocument> htmlDocument =
do_QueryInterface(document);
if (htmlDocument) {
htmlDocument->ChangeContentEditableCount(this, aChange);
}
}
if (document->HasFlag(NODE_IS_EDITABLE)) {
document = nsnull;
}
// MakeContentDescendantsEditable is going to call ContentStateChanged for
// this element and all descendants if editable state has changed.
// We might as well wrap it all in one script blocker.
nsAutoScriptBlocker scriptBlocker;
MakeContentDescendantsEditable(this, document);
}