summaryrefslogtreecommitdiffstats
path: root/dom/html/HTMLBodyElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/html/HTMLBodyElement.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/dom/html/HTMLBodyElement.cpp b/dom/html/HTMLBodyElement.cpp
new file mode 100644
index 0000000000..9fcf3e0eb6
--- /dev/null
+++ b/dom/html/HTMLBodyElement.cpp
@@ -0,0 +1,329 @@
+/* -*- 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/HTMLBodyElementBinding.h"
+
+#include "mozilla/AttributeStyles.h"
+#include "mozilla/EditorBase.h"
+#include "mozilla/HTMLEditor.h"
+#include "mozilla/MappedDeclarationsBuilder.h"
+#include "mozilla/TextEditor.h"
+#include "mozilla/dom/BindContext.h"
+#include "mozilla/dom/Document.h"
+#include "nsAttrValueInlines.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsPresContext.h"
+#include "DocumentInlines.h"
+#include "nsDocShell.h"
+#include "nsIDocShell.h"
+#include "nsGlobalWindowInner.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(
+ MappedDeclarationsBuilder& aBuilder) {
+ // 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 = aBuilder.GetAttr(nsGkAtoms::marginwidth);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyMarginWidth = value->GetIntegerValue();
+ if (bodyMarginWidth < 0) {
+ bodyMarginWidth = 0;
+ }
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)bodyMarginWidth);
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_right,
+ (float)bodyMarginWidth);
+ }
+
+ value = aBuilder.GetAttr(nsGkAtoms::marginheight);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyMarginHeight = value->GetIntegerValue();
+ if (bodyMarginHeight < 0) {
+ bodyMarginHeight = 0;
+ }
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)bodyMarginHeight);
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)bodyMarginHeight);
+ }
+
+ // topmargin (IE-attribute)
+ if (bodyMarginHeight == -1) {
+ value = aBuilder.GetAttr(nsGkAtoms::topmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyTopMargin = value->GetIntegerValue();
+ if (bodyTopMargin < 0) {
+ bodyTopMargin = 0;
+ }
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)bodyTopMargin);
+ }
+ }
+ // bottommargin (IE-attribute)
+
+ if (bodyMarginHeight == -1) {
+ value = aBuilder.GetAttr(nsGkAtoms::bottommargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyBottomMargin = value->GetIntegerValue();
+ if (bodyBottomMargin < 0) {
+ bodyBottomMargin = 0;
+ }
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)bodyBottomMargin);
+ }
+ }
+
+ // leftmargin (IE-attribute)
+ if (bodyMarginWidth == -1) {
+ value = aBuilder.GetAttr(nsGkAtoms::leftmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyLeftMargin = value->GetIntegerValue();
+ if (bodyLeftMargin < 0) {
+ bodyLeftMargin = 0;
+ }
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)bodyLeftMargin);
+ }
+ }
+ // rightmargin (IE-attribute)
+ if (bodyMarginWidth == -1) {
+ value = aBuilder.GetAttr(nsGkAtoms::rightmargin);
+ if (value && value->Type() == nsAttrValue::eInteger) {
+ bodyRightMargin = value->GetIntegerValue();
+ if (bodyRightMargin < 0) {
+ bodyRightMargin = 0;
+ }
+ aBuilder.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(aBuilder.Document().GetDocShell())) {
+ CSSIntSize margins = ds->GetFrameMargins();
+ int32_t frameMarginWidth = margins.width;
+ int32_t frameMarginHeight = margins.height;
+
+ if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
+ if (bodyLeftMargin == -1) {
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_left,
+ (float)frameMarginWidth);
+ }
+ if (bodyRightMargin == -1) {
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_right,
+ (float)frameMarginWidth);
+ }
+ }
+
+ if (bodyMarginHeight == -1 && frameMarginHeight >= 0) {
+ if (bodyTopMargin == -1) {
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_top,
+ (float)frameMarginHeight);
+ }
+ if (bodyBottomMargin == -1) {
+ aBuilder.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
+ (float)frameMarginHeight);
+ }
+ }
+ }
+ }
+
+ // When display if first asked for, go ahead and get our colors set up.
+ if (AttributeStyles* attrStyles = aBuilder.Document().GetAttributeStyles()) {
+ nscolor color;
+ value = aBuilder.GetAttr(nsGkAtoms::link);
+ if (value && value->GetColorValue(color)) {
+ attrStyles->SetLinkColor(color);
+ }
+
+ value = aBuilder.GetAttr(nsGkAtoms::alink);
+ if (value && value->GetColorValue(color)) {
+ attrStyles->SetActiveLinkColor(color);
+ }
+
+ value = aBuilder.GetAttr(nsGkAtoms::vlink);
+ if (value && value->GetColorValue(color)) {
+ attrStyles->SetVisitedLinkColor(color);
+ }
+ }
+
+ if (!aBuilder.PropertyIsSet(eCSSProperty_color)) {
+ // color: color
+ nscolor color;
+ value = aBuilder.GetAttr(nsGkAtoms::text);
+ if (value && value->GetColorValue(color)) {
+ aBuilder.SetColorValue(eCSSProperty_color, color);
+ }
+ }
+
+ nsGenericHTMLElement::MapBackgroundAttributesInto(aBuilder);
+ nsGenericHTMLElement::MapCommonAttributesInto(aBuilder);
+}
+
+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) {
+ mAttrs.MarkAsPendingPresAttributeEvaluation();
+ return nsGenericHTMLElement::BindToTree(aContext, aParent);
+}
+
+void HTMLBodyElement::FrameMarginsChanged() {
+ MOZ_ASSERT(IsInComposedDoc());
+ if (IsPendingMappedAttributeEvaluation()) {
+ return;
+ }
+ if (mAttrs.MarkAsPendingPresAttributeEvaluation()) {
+ OwnerDoc()->ScheduleForPresAttrEvaluation(this);
+ }
+}
+
+#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