dom/html/HTMLMenuElement.cpp
author Masayuki Nakano <masayuki@d-toybox.com>
Fri, 19 May 2017 17:49:41 +0900
changeset 407399 b75c111837a802ceb953dba50a3c5a193d53ca22
parent 407211 74de5283b24d6626067de8d8f043f47b74a4cf6a
child 408694 e3358d758c15ad1c66404591f16dbf434d9e5837
permissions -rw-r--r--
Bug 1339543 part 4 Change nsIWidget::ExecuteNativeKeyBinding() to nsIWidget::GetEditCommands() which just retrieves edit commands for the type r=smaug Now, nsIWidget::ExecuteNativeKeyBinding() isn't used by anybody for executing edit commands. Instead, they need array of edit commands for the key combination. So, the method should be renamed to GetEditCommands() and just return edit commands as an array. MozReview-Commit-ID: 4G0B1lJ8Lbe

/* -*- 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)

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)


void
HTMLMenuElement::SendShowEvent()
{
  nsCOMPtr<nsIDocument> document = GetComposedDoc();
  if (!document) {
    return;
  }

  WidgetEvent event(true, eShow);
  event.mFlags.mBubbles = false;
  event.mFlags.mCancelable = false;

  nsCOMPtr<nsIPresShell> shell = document->GetShell();
  if (!shell) {
    return;
  }
 
  RefPtr<nsPresContext> presContext = shell->GetPresContext();
  nsEventStatus status = nsEventStatus_eIgnore;
  EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
                            &event, nullptr, &status);
}

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();
}

void
HTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
{
  if (!aBuilder) {
    return;
  }

  BuildSubmenu(EmptyString(), this, aBuilder);
}

nsresult
HTMLMenuElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                              const nsAttrValue* aValue,
                              const nsAttrValue* aOldValue, bool aNotify)
{
  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::type) {
    if (aValue) {
      mType = aValue->GetEnumValue();
    } else {
      mType = kMenuDefaultType->value;
    }
  }

  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                            aOldValue, aNotify);
}

bool
HTMLMenuElement::ParseAttribute(int32_t aNamespaceID,
                                nsIAtom* aAttribute,
                                const nsAString& aValue,
                                nsAttrValue& aResult)
{
  if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) {
    return aResult.ParseEnumValue(aValue, kMenuTypeTable, false,
                                  kMenuDefaultType);
  }

  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