diff options
Diffstat (limited to 'dom/html/HTMLMenuElement.cpp')
-rw-r--r-- | dom/html/HTMLMenuElement.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/dom/html/HTMLMenuElement.cpp b/dom/html/HTMLMenuElement.cpp new file mode 100644 index 000000000..098590f1b --- /dev/null +++ b/dom/html/HTMLMenuElement.cpp @@ -0,0 +1,268 @@ +/* -*- 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 "mozilla/dom/HTMLMenuElement.h" + +#include "mozilla/BasicEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/HTMLMenuElementBinding.h" +#include "mozilla/dom/HTMLMenuItemElement.h" +#include "nsIMenuBuilder.h" +#include "nsAttrValueInlines.h" +#include "nsContentUtils.h" +#include "nsIURI.h" + +#define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1" + +NS_IMPL_NS_NEW_HTML_ELEMENT(Menu) + +namespace mozilla { +namespace dom { + +enum MenuType : uint8_t +{ + MENU_TYPE_CONTEXT = 1, + MENU_TYPE_TOOLBAR, + MENU_TYPE_LIST +}; + +static const nsAttrValue::EnumTable kMenuTypeTable[] = { + { "context", MENU_TYPE_CONTEXT }, + { "toolbar", MENU_TYPE_TOOLBAR }, + { "list", MENU_TYPE_LIST }, + { nullptr, 0 } +}; + +static const nsAttrValue::EnumTable* kMenuDefaultType = + &kMenuTypeTable[2]; + +enum SeparatorType +{ + ST_TRUE_INIT = -1, + ST_FALSE = 0, + ST_TRUE = 1 +}; + + + +HTMLMenuElement::HTMLMenuElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) + : nsGenericHTMLElement(aNodeInfo), mType(MENU_TYPE_LIST) +{ +} + +HTMLMenuElement::~HTMLMenuElement() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED(HTMLMenuElement, nsGenericHTMLElement, + nsIDOMHTMLMenuElement, nsIHTMLMenu) + +NS_IMPL_ELEMENT_CLONE(HTMLMenuElement) + +NS_IMPL_BOOL_ATTR(HTMLMenuElement, Compact, compact) +NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMenuElement, Type, type, + kMenuDefaultType->tag) +NS_IMPL_STRING_ATTR(HTMLMenuElement, Label, label) + + +NS_IMETHODIMP +HTMLMenuElement::SendShowEvent() +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); + + nsCOMPtr<nsIDocument> document = GetComposedDoc(); + if (!document) { + return NS_ERROR_FAILURE; + } + + WidgetEvent event(true, eShow); + event.mFlags.mBubbles = false; + event.mFlags.mCancelable = false; + + nsCOMPtr<nsIPresShell> shell = document->GetShell(); + if (!shell) { + return NS_ERROR_FAILURE; + } + + RefPtr<nsPresContext> presContext = shell->GetPresContext(); + nsEventStatus status = nsEventStatus_eIgnore; + EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext, + &event, nullptr, &status); + + return NS_OK; +} + +NS_IMETHODIMP +HTMLMenuElement::CreateBuilder(nsIMenuBuilder** _retval) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); + + nsCOMPtr<nsIMenuBuilder> builder = CreateBuilder(); + builder.swap(*_retval); + return NS_OK; +} + +already_AddRefed<nsIMenuBuilder> +HTMLMenuElement::CreateBuilder() +{ + if (mType != MENU_TYPE_CONTEXT) { + return nullptr; + } + + nsCOMPtr<nsIMenuBuilder> builder = do_CreateInstance(HTMLMENUBUILDER_CONTRACTID); + NS_WARNING_ASSERTION(builder, "No builder available"); + return builder.forget(); +} + +NS_IMETHODIMP +HTMLMenuElement::Build(nsIMenuBuilder* aBuilder) +{ + NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); + + if (!aBuilder) { + return NS_OK; + } + + BuildSubmenu(EmptyString(), this, aBuilder); + + return NS_OK; +} + + +bool +HTMLMenuElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) { + bool success = aResult.ParseEnumValue(aValue, kMenuTypeTable, + false); + if (success) { + mType = aResult.GetEnumValue(); + } else { + mType = kMenuDefaultType->value; + } + + return success; + } + + return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult); +} + +void +HTMLMenuElement::BuildSubmenu(const nsAString& aLabel, + nsIContent* aContent, + nsIMenuBuilder* aBuilder) +{ + aBuilder->OpenContainer(aLabel); + + int8_t separator = ST_TRUE_INIT; + TraverseContent(aContent, aBuilder, separator); + + if (separator == ST_TRUE) { + aBuilder->UndoAddSeparator(); + } + + aBuilder->CloseContainer(); +} + +// static +bool +HTMLMenuElement::CanLoadIcon(nsIContent* aContent, const nsAString& aIcon) +{ + if (aIcon.IsEmpty()) { + return false; + } + + nsIDocument* doc = aContent->OwnerDoc(); + + nsCOMPtr<nsIURI> baseURI = aContent->GetBaseURI(); + nsCOMPtr<nsIURI> uri; + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aIcon, doc, + baseURI); + + if (!uri) { + return false; + } + + return nsContentUtils::CanLoadImage(uri, aContent, doc, + aContent->NodePrincipal()); +} + +void +HTMLMenuElement::TraverseContent(nsIContent* aContent, + nsIMenuBuilder* aBuilder, + int8_t& aSeparator) +{ + nsCOMPtr<nsIContent> child; + for (child = aContent->GetFirstChild(); child; + child = child->GetNextSibling()) { + nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(child); + if (!element) { + continue; + } + + if (child->IsHTMLElement(nsGkAtoms::menuitem)) { + HTMLMenuItemElement* menuitem = HTMLMenuItemElement::FromContent(child); + + if (menuitem->IsHidden()) { + continue; + } + + nsAutoString label; + menuitem->GetLabel(label); + if (label.IsEmpty()) { + continue; + } + + nsAutoString icon; + menuitem->GetIcon(icon); + + aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon)); + + aSeparator = ST_FALSE; + } else if (child->IsHTMLElement(nsGkAtoms::hr)) { + aBuilder->AddSeparator(); + } else if (child->IsHTMLElement(nsGkAtoms::menu) && !element->IsHidden()) { + if (child->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) { + nsAutoString label; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label); + + BuildSubmenu(label, child, aBuilder); + + aSeparator = ST_FALSE; + } else { + AddSeparator(aBuilder, aSeparator); + + TraverseContent(child, aBuilder, aSeparator); + + AddSeparator(aBuilder, aSeparator); + } + } + } +} + +inline void +HTMLMenuElement::AddSeparator(nsIMenuBuilder* aBuilder, int8_t& aSeparator) +{ + if (aSeparator) { + return; + } + + aBuilder->AddSeparator(); + aSeparator = ST_TRUE; +} + +JSObject* +HTMLMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return HTMLMenuElementBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla |