summaryrefslogtreecommitdiffstats
path: root/dom/html/HTMLBodyElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/HTMLBodyElement.cpp')
-rw-r--r--dom/html/HTMLBodyElement.cpp336
1 files changed, 336 insertions, 0 deletions
diff --git a/dom/html/HTMLBodyElement.cpp b/dom/html/HTMLBodyElement.cpp
new file mode 100644
index 0000000000..6748b59e77
--- /dev/null
+++ b/dom/html/HTMLBodyElement.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HTMLBodyElement.h"
+#include "mozilla/dom/BindContext.h"
+#include "mozilla/dom/HTMLBodyElementBinding.h"
+#include "mozilla/EditorBase.h"
+#include "mozilla/MappedDeclarations.h"
+#include "mozilla/HTMLEditor.h"
+#include "mozilla/TextEditor.h"
+#include "nsAttrValueInlines.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsPresContext.h"
+#include "mozilla/dom/Document.h"
+#include "DocumentInlines.h"
+#include "nsDocShell.h"
+#include "nsHTMLStyleSheet.h"
+#include "nsMappedAttributes.h"
+#include "nsIDocShell.h"
+#include "nsGlobalWindow.h"
+
+NS_IMPL_NS_NEW_HTML_ELEMENT(Body)
+
+namespace mozilla::dom {
+
+//----------------------------------------------------------------------
+
+HTMLBodyElement::~HTMLBodyElement() = default;
+
+JSObject* HTMLBodyElement::WrapNode(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return HTMLBodyElement_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+NS_IMPL_ELEMENT_CLONE(HTMLBodyElement)
+
+bool HTMLBodyElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
+ const nsAString& aValue,
+ nsIPrincipal* aMaybeScriptedPrincipal,
+ nsAttrValue& aResult) {
+ if (aNamespaceID == kNameSpaceID_None) {
+ if (aAttribute == nsGkAtoms::bgcolor || aAttribute == nsGkAtoms::text ||
+ aAttribute == nsGkAtoms::link || aAttribute == nsGkAtoms::alink ||
+ aAttribute == nsGkAtoms::vlink) {
+ return aResult.ParseColor(aValue);
+ }
+ if (aAttribute == nsGkAtoms::marginwidth ||
+ aAttribute == nsGkAtoms::marginheight ||
+ aAttribute == nsGkAtoms::topmargin ||
+ aAttribute == nsGkAtoms::bottommargin ||
+ aAttribute == nsGkAtoms::leftmargin ||
+ aAttribute == nsGkAtoms::rightmargin) {
+ return aResult.ParseNonNegativeIntValue(aValue);
+ }
+ }
+
+ return nsGenericHTMLElement::ParseBackgroundAttribute(
+ aNamespaceID, aAttribute, aValue, aResult) ||
+ nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+ aMaybeScriptedPrincipal, aResult);
+}
+
+void HTMLBodyElement::MapAttributesIntoRule(
+ const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
+ // This is the one place where we try to set the same property
+ // multiple times in presentation attributes. Servo does not support
+ // querying if a property is set (because that is O(n) behavior
+ // in ServoSpecifiedValues). Instead, we use the below values to keep
+ // track of whether we have already set a property, and if so, what value
+ // we set it to (which is used when handling margin
+ // attributes from the containing frame element)
+
+ int32_t bodyMarginWidth = -1;
+ int32_t bodyMarginHeight = -1;
+ int32_t bodyTopMargin = -1;
+ int32_t bodyBottomMargin = -1;
+ int32_t bodyLeftMargin = -1;
+ int32_t bodyRightMargin = -1;
+
+ const nsAttrValue* value;
+ // if marginwidth/marginheight are set, reflect them as 'margin'
+ value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyMarginWidth = value->GetIntegerValue();
+ if (bodyMarginWidth < 0) {
+ bodyMarginWidth = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)bodyMarginWidth);
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
+ (float)bodyMarginWidth);
+ }
+
+ value = aAttributes->GetAttr(nsGkAtoms::marginheight);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyMarginHeight = value->GetIntegerValue();
+ if (bodyMarginHeight < 0) {
+ bodyMarginHeight = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)bodyMarginHeight);
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)bodyMarginHeight);
+ }
+
+ // topmargin (IE-attribute)
+ if (bodyMarginHeight == -1) {
+ value = aAttributes->GetAttr(nsGkAtoms::topmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyTopMargin = value->GetIntegerValue();
+ if (bodyTopMargin < 0) {
+ bodyTopMargin = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)bodyTopMargin);
+ }
+ }
+ // bottommargin (IE-attribute)
+
+ if (bodyMarginHeight == -1) {
+ value = aAttributes->GetAttr(nsGkAtoms::bottommargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyBottomMargin = value->GetIntegerValue();
+ if (bodyBottomMargin < 0) {
+ bodyBottomMargin = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)bodyBottomMargin);
+ }
+ }
+
+ // leftmargin (IE-attribute)
+ if (bodyMarginWidth == -1) {
+ value = aAttributes->GetAttr(nsGkAtoms::leftmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyLeftMargin = value->GetIntegerValue();
+ if (bodyLeftMargin < 0) {
+ bodyLeftMargin = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)bodyLeftMargin);
+ }
+ }
+ // rightmargin (IE-attribute)
+ if (bodyMarginWidth == -1) {
+ value = aAttributes->GetAttr(nsGkAtoms::rightmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyRightMargin = value->GetIntegerValue();
+ if (bodyRightMargin < 0) {
+ bodyRightMargin = 0;
+ }
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
+ (float)bodyRightMargin);
+ }
+ }
+
+ // if marginwidth or marginheight is set in the <frame> and not set in the
+ // <body> reflect them as margin in the <body>
+ if (bodyMarginWidth == -1 || bodyMarginHeight == -1) {
+ if (nsDocShell* ds = nsDocShell::Cast(aDecls.Document()->GetDocShell())) {
+ CSSIntSize margins = ds->GetFrameMargins();
+ int32_t frameMarginWidth = margins.width;
+ int32_t frameMarginHeight = margins.height;
+
+ if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
+ if (bodyLeftMargin == -1) {
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)frameMarginWidth);
+ }
+ if (bodyRightMargin == -1) {
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
+ (float)frameMarginWidth);
+ }
+ }
+
+ if (bodyMarginHeight == -1 && frameMarginHeight >= 0) {
+ if (bodyTopMargin == -1) {
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)frameMarginHeight);
+ }
+ if (bodyBottomMargin == -1) {
+ aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)frameMarginHeight);
+ }
+ }
+ }
+ }
+
+ // When display if first asked for, go ahead and get our colors set up.
+ if (nsHTMLStyleSheet* styleSheet =
+ aDecls.Document()->GetAttributeStyleSheet()) {
+ nscolor color;
+ value = aAttributes->GetAttr(nsGkAtoms::link);
+ if (value && value->GetColorValue(color)) {
+ styleSheet->SetLinkColor(color);
+ }
+
+ value = aAttributes->GetAttr(nsGkAtoms::alink);
+ if (value && value->GetColorValue(color)) {
+ styleSheet->SetActiveLinkColor(color);
+ }
+
+ value = aAttributes->GetAttr(nsGkAtoms::vlink);
+ if (value && value->GetColorValue(color)) {
+ styleSheet->SetVisitedLinkColor(color);
+ }
+ }
+
+ if (!aDecls.PropertyIsSet(eCSSProperty_color)) {
+ // color: color
+ nscolor color;
+ value = aAttributes->GetAttr(nsGkAtoms::text);
+ if (value && value->GetColorValue(color)) {
+ aDecls.SetColorValue(eCSSProperty_color, color);
+ }
+ }
+
+ nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aDecls);
+ nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
+}
+
+nsMapRuleToAttributesFunc HTMLBodyElement::GetAttributeMappingFunction() const {
+ return &MapAttributesIntoRule;
+}
+
+NS_IMETHODIMP_(bool)
+HTMLBodyElement::IsAttributeMapped(const nsAtom* aAttribute) const {
+ static const MappedAttributeEntry attributes[] = {
+ {nsGkAtoms::link},
+ {nsGkAtoms::vlink},
+ {nsGkAtoms::alink},
+ {nsGkAtoms::text},
+ {nsGkAtoms::marginwidth},
+ {nsGkAtoms::marginheight},
+ {nsGkAtoms::topmargin},
+ {nsGkAtoms::rightmargin},
+ {nsGkAtoms::bottommargin},
+ {nsGkAtoms::leftmargin},
+ {nullptr},
+ };
+
+ static const MappedAttributeEntry* const map[] = {
+ attributes,
+ sCommonAttributeMap,
+ sBackgroundAttributeMap,
+ };
+
+ return FindAttributeDependence(aAttribute, map);
+}
+
+already_AddRefed<EditorBase> HTMLBodyElement::GetAssociatedEditor() {
+ MOZ_ASSERT(!GetTextEditorInternal());
+
+ // Make sure this is the actual body of the document
+ if (this != OwnerDoc()->GetBodyElement()) {
+ return nullptr;
+ }
+
+ // For designmode, try to get document's editor
+ nsPresContext* presContext = GetPresContext(eForComposedDoc);
+ if (!presContext) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
+ if (!docShell) {
+ return nullptr;
+ }
+
+ RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor();
+ return htmlEditor.forget();
+}
+
+bool HTMLBodyElement::IsEventAttributeNameInternal(nsAtom* aName) {
+ return nsContentUtils::IsEventAttributeName(
+ aName, EventNameType_HTML | EventNameType_HTMLBodyOrFramesetOnly);
+}
+
+nsresult HTMLBodyElement::BindToTree(BindContext& aContext, nsINode& aParent) {
+ nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mAttrs.ForceMapped(this, &aContext.OwnerDoc());
+}
+
+void HTMLBodyElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
+ const nsAttrValue* aValue,
+ const nsAttrValue* aOldValue,
+ nsIPrincipal* aSubjectPrincipal,
+ bool aNotify) {
+ nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, aOldValue,
+ aSubjectPrincipal, aNotify);
+ // If the last mapped attribute was removed, don't clear the
+ // nsMappedAttributes, our style can still depend on the containing frame
+ // element.
+ if (!aValue && IsAttributeMapped(aName)) {
+ Unused << mAttrs.ForceMapped(this, OwnerDoc());
+ }
+}
+
+#define EVENT(name_, id_, type_, \
+ struct_) /* nothing; handled by the superclass */
+// nsGenericHTMLElement::GetOnError returns
+// already_AddRefed<EventHandlerNonNull> while other getters return
+// EventHandlerNonNull*, so allow passing in the type to use here.
+#define WINDOW_EVENT_HELPER(name_, type_) \
+ type_* HTMLBodyElement::GetOn##name_() { \
+ if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) { \
+ nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
+ return globalWin->GetOn##name_(); \
+ } \
+ return nullptr; \
+ } \
+ void HTMLBodyElement::SetOn##name_(type_* handler) { \
+ nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow(); \
+ if (!win) { \
+ return; \
+ } \
+ \
+ nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win); \
+ return globalWin->SetOn##name_(handler); \
+ }
+#define WINDOW_EVENT(name_, id_, type_, struct_) \
+ WINDOW_EVENT_HELPER(name_, EventHandlerNonNull)
+#define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
+ WINDOW_EVENT_HELPER(name_, OnBeforeUnloadEventHandlerNonNull)
+#include "mozilla/EventNameList.h" // IWYU pragma: keep
+#undef BEFOREUNLOAD_EVENT
+#undef WINDOW_EVENT
+#undef WINDOW_EVENT_HELPER
+#undef EVENT
+
+} // namespace mozilla::dom