LCOV - code coverage report
Current view: top level - dom/html - HTMLMenuItemElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 219 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/HTMLMenuItemElement.h"
       8             : 
       9             : #include "mozilla/BasicEvents.h"
      10             : #include "mozilla/EventDispatcher.h"
      11             : #include "mozilla/dom/HTMLMenuItemElementBinding.h"
      12             : #include "nsAttrValueInlines.h"
      13             : #include "nsContentUtils.h"
      14             : 
      15             : 
      16           0 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(MenuItem)
      17             : 
      18             : namespace mozilla {
      19             : namespace dom {
      20             : 
      21             : // First bits are needed for the menuitem type.
      22             : #define NS_CHECKED_IS_TOGGLED (1 << 2)
      23             : #define NS_ORIGINAL_CHECKED_VALUE (1 << 3)
      24             : #define NS_MENUITEM_TYPE(bits) ((bits) & ~( \
      25             :   NS_CHECKED_IS_TOGGLED | NS_ORIGINAL_CHECKED_VALUE))
      26             : 
      27             : enum CmdType : uint8_t
      28             : {
      29             :   CMD_TYPE_MENUITEM = 1,
      30             :   CMD_TYPE_CHECKBOX,
      31             :   CMD_TYPE_RADIO
      32             : };
      33             : 
      34             : static const nsAttrValue::EnumTable kMenuItemTypeTable[] = {
      35             :   { "menuitem", CMD_TYPE_MENUITEM },
      36             :   { "checkbox", CMD_TYPE_CHECKBOX },
      37             :   { "radio", CMD_TYPE_RADIO },
      38             :   { nullptr, 0 }
      39             : };
      40             : 
      41             : static const nsAttrValue::EnumTable* kMenuItemDefaultType =
      42             :   &kMenuItemTypeTable[0];
      43             : 
      44             : // A base class inherited by all radio visitors.
      45             : class Visitor
      46             : {
      47             : public:
      48           0 :   Visitor() { }
      49           0 :   virtual ~Visitor() { }
      50             : 
      51             :   /**
      52             :    * Visit a node in the tree. This is meant to be called on all radios in a
      53             :    * group, sequentially. If the method returns false then the iteration is
      54             :    * stopped.
      55             :    */
      56             :   virtual bool Visit(HTMLMenuItemElement* aMenuItem) = 0;
      57             : };
      58             : 
      59             : // Find the selected radio, see GetSelectedRadio().
      60           0 : class GetCheckedVisitor : public Visitor
      61             : {
      62             : public:
      63           0 :   explicit GetCheckedVisitor(HTMLMenuItemElement** aResult)
      64           0 :     : mResult(aResult)
      65           0 :     { }
      66           0 :   virtual bool Visit(HTMLMenuItemElement* aMenuItem)
      67             :   {
      68           0 :     if (aMenuItem->IsChecked()) {
      69           0 :       *mResult = aMenuItem;
      70           0 :       return false;
      71             :     }
      72           0 :     return true;
      73             :   }
      74             : protected:
      75             :   HTMLMenuItemElement** mResult;
      76             : };
      77             : 
      78             : // Deselect all radios except the one passed to the constructor.
      79           0 : class ClearCheckedVisitor : public Visitor
      80             : {
      81             : public:
      82           0 :   explicit ClearCheckedVisitor(HTMLMenuItemElement* aExcludeMenuItem)
      83           0 :     : mExcludeMenuItem(aExcludeMenuItem)
      84           0 :     { }
      85           0 :   virtual bool Visit(HTMLMenuItemElement* aMenuItem)
      86             :   {
      87           0 :     if (aMenuItem != mExcludeMenuItem && aMenuItem->IsChecked()) {
      88           0 :       aMenuItem->ClearChecked();
      89             :     }
      90           0 :     return true;
      91             :   }
      92             : protected:
      93             :   HTMLMenuItemElement* mExcludeMenuItem;
      94             : };
      95             : 
      96             : // Get current value of the checked dirty flag. The same value is stored on all
      97             : // radios in the group, so we need to check only the first one.
      98           0 : class GetCheckedDirtyVisitor : public Visitor
      99             : {
     100             : public:
     101           0 :   GetCheckedDirtyVisitor(bool* aCheckedDirty,
     102             :                          HTMLMenuItemElement* aExcludeMenuItem)
     103           0 :     : mCheckedDirty(aCheckedDirty),
     104           0 :       mExcludeMenuItem(aExcludeMenuItem)
     105           0 :     { }
     106           0 :   virtual bool Visit(HTMLMenuItemElement* aMenuItem)
     107             :   {
     108           0 :     if (aMenuItem == mExcludeMenuItem) {
     109           0 :       return true;
     110             :     }
     111           0 :     *mCheckedDirty = aMenuItem->IsCheckedDirty();
     112           0 :     return false;
     113             :   }
     114             : protected:
     115             :   bool* mCheckedDirty;
     116             :   HTMLMenuItemElement* mExcludeMenuItem;
     117             : };
     118             : 
     119             : // Set checked dirty to true on all radios in the group.
     120           0 : class SetCheckedDirtyVisitor : public Visitor
     121             : {
     122             : public:
     123           0 :   SetCheckedDirtyVisitor()
     124           0 :     { }
     125           0 :   virtual bool Visit(HTMLMenuItemElement* aMenuItem)
     126             :   {
     127           0 :     aMenuItem->SetCheckedDirty();
     128           0 :     return true;
     129             :   }
     130             : };
     131             : 
     132             : // A helper visitor that is used to combine two operations (visitors) to avoid
     133             : // iterating over radios twice.
     134           0 : class CombinedVisitor : public Visitor
     135             : {
     136             : public:
     137           0 :   CombinedVisitor(Visitor* aVisitor1, Visitor* aVisitor2)
     138           0 :     : mVisitor1(aVisitor1), mVisitor2(aVisitor2),
     139           0 :       mContinue1(true), mContinue2(true)
     140           0 :     { }
     141           0 :   virtual bool Visit(HTMLMenuItemElement* aMenuItem)
     142             :   {
     143           0 :     if (mContinue1) {
     144           0 :       mContinue1 = mVisitor1->Visit(aMenuItem);
     145             :     }
     146           0 :     if (mContinue2) {
     147           0 :       mContinue2 = mVisitor2->Visit(aMenuItem);
     148             :     }
     149           0 :     return mContinue1 || mContinue2;
     150             :   }
     151             : protected:
     152             :   Visitor* mVisitor1;
     153             :   Visitor* mVisitor2;
     154             :   bool mContinue1;
     155             :   bool mContinue2;
     156             : };
     157             : 
     158             : 
     159           0 : HTMLMenuItemElement::HTMLMenuItemElement(
     160           0 :   already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, FromParser aFromParser)
     161             :   : nsGenericHTMLElement(aNodeInfo),
     162           0 :     mType(kMenuItemDefaultType->value),
     163             :     mParserCreating(false),
     164             :     mShouldInitChecked(false),
     165             :     mCheckedDirty(false),
     166           0 :     mChecked(false)
     167             : {
     168           0 :   mParserCreating = aFromParser;
     169           0 : }
     170             : 
     171           0 : HTMLMenuItemElement::~HTMLMenuItemElement()
     172             : {
     173           0 : }
     174             : 
     175             : 
     176           0 : NS_IMPL_ISUPPORTS_INHERITED(HTMLMenuItemElement, nsGenericHTMLElement,
     177             :                             nsIDOMHTMLMenuItemElement)
     178             : 
     179             : //NS_IMPL_ELEMENT_CLONE(HTMLMenuItemElement)
     180             : nsresult
     181           0 : HTMLMenuItemElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
     182             :                            bool aPreallocateArrays) const
     183             : {
     184           0 :   *aResult = nullptr;
     185           0 :   already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
     186             :   RefPtr<HTMLMenuItemElement> it =
     187           0 :     new HTMLMenuItemElement(ni, NOT_FROM_PARSER);
     188           0 :   nsresult rv = const_cast<HTMLMenuItemElement*>(this)->CopyInnerTo(it, aPreallocateArrays);
     189           0 :   if (NS_SUCCEEDED(rv)) {
     190           0 :     switch (mType) {
     191             :       case CMD_TYPE_CHECKBOX:
     192             :       case CMD_TYPE_RADIO:
     193           0 :         if (mCheckedDirty) {
     194             :           // We no longer have our original checked state.  Set our
     195             :           // checked state on the clone.
     196           0 :           it->mCheckedDirty = true;
     197           0 :           it->mChecked = mChecked;
     198             :         }
     199           0 :         break;
     200             :     }
     201             : 
     202           0 :     it.forget(aResult);
     203             :   }
     204             : 
     205           0 :   return rv;
     206             : }
     207             : 
     208             : 
     209           0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMenuItemElement, Type, type,
     210             :                                 kMenuItemDefaultType->tag)
     211             : // GetText returns a whitespace compressed .textContent value.
     212           0 : NS_IMPL_STRING_ATTR_WITH_FALLBACK(HTMLMenuItemElement, Label, label, GetText)
     213           0 : NS_IMPL_URI_ATTR(HTMLMenuItemElement, Icon, icon)
     214           0 : NS_IMPL_BOOL_ATTR(HTMLMenuItemElement, Disabled, disabled)
     215           0 : NS_IMPL_BOOL_ATTR(HTMLMenuItemElement, DefaultChecked, checked)
     216             : //NS_IMPL_BOOL_ATTR(HTMLMenuItemElement, Checked, checked)
     217           0 : NS_IMPL_STRING_ATTR(HTMLMenuItemElement, Radiogroup, radiogroup)
     218             : 
     219             : NS_IMETHODIMP
     220           0 : HTMLMenuItemElement::GetChecked(bool* aChecked)
     221             : {
     222           0 :   *aChecked = mChecked;
     223           0 :   return NS_OK;
     224             : }
     225             : 
     226             : NS_IMETHODIMP
     227           0 : HTMLMenuItemElement::SetChecked(bool aChecked)
     228             : {
     229           0 :   bool checkedChanged = mChecked != aChecked;
     230             : 
     231           0 :   mChecked = aChecked;
     232             : 
     233           0 :   if (mType == CMD_TYPE_RADIO) {
     234           0 :     if (checkedChanged) {
     235           0 :       if (mCheckedDirty) {
     236           0 :         ClearCheckedVisitor visitor(this);
     237           0 :         WalkRadioGroup(&visitor);
     238             :       } else {
     239           0 :         ClearCheckedVisitor visitor1(this);
     240           0 :         SetCheckedDirtyVisitor visitor2;
     241           0 :         CombinedVisitor visitor(&visitor1, &visitor2);
     242           0 :         WalkRadioGroup(&visitor);
     243             :       }
     244           0 :     } else if (!mCheckedDirty) {
     245           0 :       SetCheckedDirtyVisitor visitor;
     246           0 :       WalkRadioGroup(&visitor);
     247             :     }
     248             :   } else {
     249           0 :     mCheckedDirty = true;
     250             :   }
     251             : 
     252           0 :   return NS_OK;
     253             : }
     254             : 
     255             : nsresult
     256           0 : HTMLMenuItemElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
     257             : {
     258           0 :   if (aVisitor.mEvent->mMessage == eMouseClick) {
     259             : 
     260           0 :     bool originalCheckedValue = false;
     261           0 :     switch (mType) {
     262             :       case CMD_TYPE_CHECKBOX:
     263           0 :         originalCheckedValue = mChecked;
     264           0 :         SetChecked(!originalCheckedValue);
     265           0 :         aVisitor.mItemFlags |= NS_CHECKED_IS_TOGGLED;
     266           0 :         break;
     267             :       case CMD_TYPE_RADIO:
     268           0 :         nsCOMPtr<nsIDOMHTMLMenuItemElement> selectedRadio = GetSelectedRadio();
     269           0 :         aVisitor.mItemData = selectedRadio;
     270             : 
     271           0 :         originalCheckedValue = mChecked;
     272           0 :         if (!originalCheckedValue) {
     273           0 :           SetChecked(true);
     274           0 :           aVisitor.mItemFlags |= NS_CHECKED_IS_TOGGLED;
     275             :         }
     276           0 :         break;
     277             :     }
     278             : 
     279           0 :     if (originalCheckedValue) {
     280           0 :       aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
     281             :     }
     282             : 
     283             :     // We must cache type because mType may change during JS event.
     284           0 :     aVisitor.mItemFlags |= mType;
     285             :   }
     286             : 
     287           0 :   return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
     288             : }
     289             : 
     290             : nsresult
     291           0 : HTMLMenuItemElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
     292             : {
     293             :   // Check to see if the event was cancelled.
     294           0 :   if (aVisitor.mEvent->mMessage == eMouseClick &&
     295           0 :       aVisitor.mItemFlags & NS_CHECKED_IS_TOGGLED &&
     296           0 :       aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
     297             :     bool originalCheckedValue =
     298           0 :       !!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
     299           0 :     uint8_t oldType = NS_MENUITEM_TYPE(aVisitor.mItemFlags);
     300             : 
     301             :     nsCOMPtr<nsIDOMHTMLMenuItemElement> selectedRadio =
     302           0 :       do_QueryInterface(aVisitor.mItemData);
     303           0 :     if (selectedRadio) {
     304           0 :       selectedRadio->SetChecked(true);
     305           0 :       if (mType != CMD_TYPE_RADIO) {
     306           0 :         SetChecked(false);
     307             :       }
     308           0 :     } else if (oldType == CMD_TYPE_CHECKBOX) {
     309           0 :       SetChecked(originalCheckedValue);
     310             :     }
     311             :   }
     312             : 
     313           0 :   return NS_OK;
     314             : }
     315             : 
     316             : nsresult
     317           0 : HTMLMenuItemElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
     318             :                                 nsIContent* aBindingParent,
     319             :                                 bool aCompileEventHandlers)
     320             : {
     321           0 :   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
     322             :                                                  aBindingParent,
     323           0 :                                                  aCompileEventHandlers);
     324             : 
     325           0 :   if (NS_SUCCEEDED(rv) && aDocument && mType == CMD_TYPE_RADIO) {
     326           0 :     AddedToRadioGroup();
     327             :   }
     328             : 
     329           0 :   return rv;
     330             : }
     331             : 
     332             : bool
     333           0 : HTMLMenuItemElement::ParseAttribute(int32_t aNamespaceID,
     334             :                                     nsIAtom* aAttribute,
     335             :                                     const nsAString& aValue,
     336             :                                     nsAttrValue& aResult)
     337             : {
     338           0 :   if (aNamespaceID == kNameSpaceID_None) {
     339           0 :     if (aAttribute == nsGkAtoms::type) {
     340           0 :       return aResult.ParseEnumValue(aValue, kMenuItemTypeTable, false,
     341           0 :                                     kMenuItemDefaultType);
     342             :     }
     343             : 
     344           0 :     if (aAttribute == nsGkAtoms::radiogroup) {
     345           0 :       aResult.ParseAtom(aValue);
     346           0 :       return true;
     347             :     }
     348             :   }
     349             : 
     350           0 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
     351           0 :                                               aResult);
     352             : }
     353             : 
     354             : void
     355           0 : HTMLMenuItemElement::DoneCreatingElement()
     356             : {
     357           0 :   mParserCreating = false;
     358             : 
     359           0 :   if (mShouldInitChecked) {
     360           0 :     InitChecked();
     361           0 :     mShouldInitChecked = false;
     362             :   }
     363           0 : }
     364             : 
     365             : void
     366           0 : HTMLMenuItemElement::GetText(nsAString& aText)
     367             : {
     368           0 :   nsAutoString text;
     369           0 :   nsContentUtils::GetNodeTextContent(this, false, text);
     370             : 
     371           0 :   text.CompressWhitespace(true, true);
     372           0 :   aText = text;
     373           0 : }
     374             : 
     375             : nsresult
     376           0 : HTMLMenuItemElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
     377             :                                   const nsAttrValue* aValue,
     378             :                                   const nsAttrValue* aOldValue, bool aNotify)
     379             : {
     380           0 :   if (aNameSpaceID == kNameSpaceID_None) {
     381             :     // Handle type changes first, since some of the later conditions in this
     382             :     // method look at mType and want to see the new value.
     383           0 :     if (aName == nsGkAtoms::type) {
     384           0 :       if (aValue) {
     385           0 :         mType = aValue->GetEnumValue();
     386             :       } else {
     387           0 :         mType = kMenuItemDefaultType->value;
     388             :       }
     389             :     }
     390             : 
     391           0 :     if ((aName == nsGkAtoms::radiogroup || aName == nsGkAtoms::type) &&
     392           0 :         mType == CMD_TYPE_RADIO &&
     393           0 :         !mParserCreating) {
     394           0 :       if (IsInUncomposedDoc() && GetParent()) {
     395           0 :         AddedToRadioGroup();
     396             :       }
     397             :     }
     398             : 
     399             :     // Checked must be set no matter what type of menuitem it is, since
     400             :     // GetChecked() must reflect the new value
     401           0 :     if (aName == nsGkAtoms::checked &&
     402           0 :         !mCheckedDirty) {
     403           0 :       if (mParserCreating) {
     404           0 :         mShouldInitChecked = true;
     405             :       } else {
     406           0 :         InitChecked();
     407             :       }
     408             :     }
     409             :   }
     410             : 
     411           0 :   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
     412           0 :                                             aOldValue, aNotify);
     413             : }
     414             : 
     415             : void
     416           0 : HTMLMenuItemElement::WalkRadioGroup(Visitor* aVisitor)
     417             : {
     418           0 :   nsIContent* parent = GetParent();
     419           0 :   if (!parent) {
     420           0 :     aVisitor->Visit(this);
     421           0 :     return;
     422             :   }
     423             : 
     424             :   BorrowedAttrInfo info1(GetAttrInfo(kNameSpaceID_None,
     425           0 :                                nsGkAtoms::radiogroup));
     426           0 :   bool info1Empty = !info1.mValue || info1.mValue->IsEmptyString();
     427             : 
     428           0 :   for (nsIContent* cur = parent->GetFirstChild();
     429           0 :        cur;
     430           0 :        cur = cur->GetNextSibling()) {
     431           0 :     HTMLMenuItemElement* menuitem = HTMLMenuItemElement::FromContent(cur);
     432             : 
     433           0 :     if (!menuitem || menuitem->GetType() != CMD_TYPE_RADIO) {
     434           0 :       continue;
     435             :     }
     436             : 
     437             :     BorrowedAttrInfo info2(menuitem->GetAttrInfo(kNameSpaceID_None,
     438           0 :                                            nsGkAtoms::radiogroup));
     439           0 :     bool info2Empty = !info2.mValue || info2.mValue->IsEmptyString();
     440             : 
     441           0 :     if (info1Empty != info2Empty ||
     442           0 :         (info1.mValue && info2.mValue && !info1.mValue->Equals(*info2.mValue))) {
     443           0 :       continue;
     444             :     }
     445             : 
     446           0 :     if (!aVisitor->Visit(menuitem)) {
     447           0 :       break;
     448             :     }
     449             :   }
     450             : }
     451             : 
     452             : HTMLMenuItemElement*
     453           0 : HTMLMenuItemElement::GetSelectedRadio()
     454             : {
     455           0 :   HTMLMenuItemElement* result = nullptr;
     456             : 
     457           0 :   GetCheckedVisitor visitor(&result);
     458           0 :   WalkRadioGroup(&visitor);
     459             : 
     460           0 :   return result;
     461             : }
     462             : 
     463             : void
     464           0 : HTMLMenuItemElement::AddedToRadioGroup()
     465             : {
     466           0 :   bool checkedDirty = mCheckedDirty;
     467           0 :   if (mChecked) {
     468           0 :     ClearCheckedVisitor visitor1(this);
     469           0 :     GetCheckedDirtyVisitor visitor2(&checkedDirty, this);
     470           0 :     CombinedVisitor visitor(&visitor1, &visitor2);
     471           0 :     WalkRadioGroup(&visitor);
     472             :   } else {
     473           0 :     GetCheckedDirtyVisitor visitor(&checkedDirty, this);
     474           0 :     WalkRadioGroup(&visitor);
     475             :   }
     476           0 :   mCheckedDirty = checkedDirty;
     477           0 : }
     478             : 
     479             : void
     480           0 : HTMLMenuItemElement::InitChecked()
     481             : {
     482             :   bool defaultChecked;
     483           0 :   GetDefaultChecked(&defaultChecked);
     484           0 :   mChecked = defaultChecked;
     485           0 :   if (mType == CMD_TYPE_RADIO) {
     486           0 :     ClearCheckedVisitor visitor(this);
     487           0 :     WalkRadioGroup(&visitor);
     488             :   }
     489           0 : }
     490             : 
     491             : JSObject*
     492           0 : HTMLMenuItemElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     493             : {
     494           0 :   return HTMLMenuItemElementBinding::Wrap(aCx, this, aGivenProto);
     495             : }
     496             : 
     497             : } // namespace dom
     498             : } // namespace mozilla
     499             : 
     500             : #undef NS_ORIGINAL_CHECKED_VALUE

Generated by: LCOV version 1.13