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/EventStates.h"
8 #include "mozilla/dom/BindContext.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/HTMLObjectElement.h"
11 #include "mozilla/dom/HTMLObjectElementBinding.h"
12 #include "mozilla/dom/ElementInlines.h"
13 #include "mozilla/dom/WindowProxyHolder.h"
14 #include "nsAttrValueInlines.h"
15 #include "nsGkAtoms.h"
16 #include "nsError.h"
17 #include "nsIContentInlines.h"
18 #include "nsIWidget.h"
19 #include "nsContentUtils.h"
20 #ifdef XP_MACOSX
21 #  include "mozilla/EventDispatcher.h"
22 #  include "mozilla/dom/Event.h"
23 #  include "nsFocusManager.h"
24 #endif
25 
26 namespace mozilla::dom {
27 
HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo,FromParser aFromParser)28 HTMLObjectElement::HTMLObjectElement(
29     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
30     FromParser aFromParser)
31     : nsGenericHTMLFormControlElement(std::move(aNodeInfo),
32                                       FormControlType::Object),
33       mIsDoneAddingChildren(!aFromParser) {
34   RegisterActivityObserver();
35   SetIsNetworkCreated(aFromParser == FROM_PARSER_NETWORK);
36 
37   // <object> is always barred from constraint validation.
38   SetBarredFromConstraintValidation(true);
39 
40   // By default we're in the loading state
41   AddStatesSilently(NS_EVENT_STATE_LOADING);
42 }
43 
~HTMLObjectElement()44 HTMLObjectElement::~HTMLObjectElement() {
45   UnregisterActivityObserver();
46   nsImageLoadingContent::Destroy();
47 }
48 
IsInteractiveHTMLContent() const49 bool HTMLObjectElement::IsInteractiveHTMLContent() const {
50   return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
51          nsGenericHTMLFormControlElement::IsInteractiveHTMLContent();
52 }
53 
AsyncEventRunning(AsyncEventDispatcher * aEvent)54 void HTMLObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
55   nsImageLoadingContent::AsyncEventRunning(aEvent);
56 }
57 
IsDoneAddingChildren()58 bool HTMLObjectElement::IsDoneAddingChildren() { return mIsDoneAddingChildren; }
59 
DoneAddingChildren(bool aHaveNotified)60 void HTMLObjectElement::DoneAddingChildren(bool aHaveNotified) {
61   mIsDoneAddingChildren = true;
62 
63   // If we're already in a document, we need to trigger the load
64   // Otherwise, BindToTree takes care of that.
65   if (IsInComposedDoc()) {
66     StartObjectLoad(aHaveNotified, false);
67   }
68 }
69 
70 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLObjectElement)
71 
72 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(
73     HTMLObjectElement, nsGenericHTMLFormControlElement)
74   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
75   nsObjectLoadingContent::Traverse(tmp, cb);
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
77 
78 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLObjectElement,
79                                                 nsGenericHTMLFormControlElement)
80   NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
81   nsObjectLoadingContent::Unlink(tmp);
82 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
83 
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLObjectElement,nsGenericHTMLFormControlElement,imgINotificationObserver,nsIRequestObserver,nsIStreamListener,nsFrameLoaderOwner,nsIObjectLoadingContent,nsIImageLoadingContent,nsIChannelEventSink,nsIConstraintValidation)84 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(
85     HTMLObjectElement, nsGenericHTMLFormControlElement,
86     imgINotificationObserver, nsIRequestObserver, nsIStreamListener,
87     nsFrameLoaderOwner, nsIObjectLoadingContent, nsIImageLoadingContent,
88     nsIChannelEventSink, nsIConstraintValidation)
89 
90 NS_IMPL_ELEMENT_CLONE(HTMLObjectElement)
91 
92 nsresult HTMLObjectElement::BindToTree(BindContext& aContext,
93                                        nsINode& aParent) {
94   nsresult rv = nsGenericHTMLFormControlElement::BindToTree(aContext, aParent);
95   NS_ENSURE_SUCCESS(rv, rv);
96 
97   rv = nsObjectLoadingContent::BindToTree(aContext, aParent);
98   NS_ENSURE_SUCCESS(rv, rv);
99 
100   // If we already have all the children, start the load.
101   if (IsInComposedDoc() && mIsDoneAddingChildren) {
102     void (HTMLObjectElement::*start)() = &HTMLObjectElement::StartObjectLoad;
103     nsContentUtils::AddScriptRunner(
104         NewRunnableMethod("dom::HTMLObjectElement::BindToTree", this, start));
105   }
106 
107   return NS_OK;
108 }
109 
UnbindFromTree(bool aNullParent)110 void HTMLObjectElement::UnbindFromTree(bool aNullParent) {
111   nsObjectLoadingContent::UnbindFromTree(aNullParent);
112   nsGenericHTMLFormControlElement::UnbindFromTree(aNullParent);
113 }
114 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)115 nsresult HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
116                                          const nsAttrValue* aValue,
117                                          const nsAttrValue* aOldValue,
118                                          nsIPrincipal* aSubjectPrincipal,
119                                          bool aNotify) {
120   nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
121   NS_ENSURE_SUCCESS(rv, rv);
122 
123   return nsGenericHTMLFormControlElement::AfterSetAttr(
124       aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
125 }
126 
OnAttrSetButNotChanged(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString & aValue,bool aNotify)127 nsresult HTMLObjectElement::OnAttrSetButNotChanged(
128     int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
129     bool aNotify) {
130   nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
131   NS_ENSURE_SUCCESS(rv, rv);
132 
133   return nsGenericHTMLFormControlElement::OnAttrSetButNotChanged(
134       aNamespaceID, aName, aValue, aNotify);
135 }
136 
AfterMaybeChangeAttr(int32_t aNamespaceID,nsAtom * aName,bool aNotify)137 nsresult HTMLObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
138                                                  nsAtom* aName, bool aNotify) {
139   if (aNamespaceID == kNameSpaceID_None) {
140     // if aNotify is false, we are coming from the parser or some such place;
141     // we'll get bound after all the attributes have been set, so we'll do the
142     // object load from BindToTree/DoneAddingChildren.
143     // Skip the LoadObject call in that case.
144     // We also don't want to start loading the object when we're not yet in
145     // a document, just in case that the caller wants to set additional
146     // attributes before inserting the node into the document.
147     if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
148         aName == nsGkAtoms::data && !BlockEmbedOrObjectContentLoading()) {
149       nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
150           "HTMLObjectElement::LoadObject",
151           [self = RefPtr<HTMLObjectElement>(this), aNotify]() {
152             if (self->IsInComposedDoc()) {
153               self->LoadObject(aNotify, true);
154             }
155           }));
156       return NS_OK;
157     }
158   }
159 
160   return NS_OK;
161 }
162 
IsHTMLFocusable(bool aWithMouse,bool * aIsFocusable,int32_t * aTabIndex)163 bool HTMLObjectElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
164                                         int32_t* aTabIndex) {
165   // TODO: this should probably be managed directly by IsHTMLFocusable.
166   // See bug 597242.
167   Document* doc = GetComposedDoc();
168   if (!doc || IsInDesignMode()) {
169     if (aTabIndex) {
170       *aTabIndex = -1;
171     }
172 
173     *aIsFocusable = false;
174     return false;
175   }
176 
177   // Plugins that show the empty fallback should not accept focus.
178   if (Type() == eType_Fallback) {
179     if (aTabIndex) {
180       *aTabIndex = -1;
181     }
182 
183     *aIsFocusable = false;
184     return false;
185   }
186 
187   const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex);
188   bool isFocusable = attrVal && attrVal->Type() == nsAttrValue::eInteger;
189 
190   // This method doesn't call nsGenericHTMLFormControlElement intentionally.
191   // TODO: It should probably be changed when bug 597242 will be fixed.
192   if (IsEditableRoot() ||
193       ((Type() == eType_Document || Type() == eType_FakePlugin) &&
194        nsContentUtils::IsSubDocumentTabbable(this))) {
195     if (aTabIndex) {
196       *aTabIndex = isFocusable ? attrVal->GetIntegerValue() : 0;
197     }
198 
199     *aIsFocusable = true;
200     return false;
201   }
202 
203   // TODO: this should probably be managed directly by IsHTMLFocusable.
204   // See bug 597242.
205   if (aTabIndex && isFocusable) {
206     *aTabIndex = attrVal->GetIntegerValue();
207     *aIsFocusable = true;
208   }
209 
210   return false;
211 }
212 
TabIndexDefault()213 int32_t HTMLObjectElement::TabIndexDefault() { return 0; }
214 
GetContentWindow(nsIPrincipal & aSubjectPrincipal)215 Nullable<WindowProxyHolder> HTMLObjectElement::GetContentWindow(
216     nsIPrincipal& aSubjectPrincipal) {
217   Document* doc = GetContentDocument(aSubjectPrincipal);
218   if (doc) {
219     nsPIDOMWindowOuter* win = doc->GetWindow();
220     if (win) {
221       return WindowProxyHolder(win->GetBrowsingContext());
222     }
223   }
224 
225   return nullptr;
226 }
227 
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)228 bool HTMLObjectElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
229                                        const nsAString& aValue,
230                                        nsIPrincipal* aMaybeScriptedPrincipal,
231                                        nsAttrValue& aResult) {
232   if (aNamespaceID == kNameSpaceID_None) {
233     if (aAttribute == nsGkAtoms::align) {
234       return ParseAlignValue(aValue, aResult);
235     }
236     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
237       return true;
238     }
239   }
240 
241   return nsGenericHTMLFormControlElement::ParseAttribute(
242       aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
243 }
244 
MapAttributesIntoRule(const nsMappedAttributes * aAttributes,MappedDeclarations & aDecls)245 void HTMLObjectElement::MapAttributesIntoRule(
246     const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
247   nsGenericHTMLFormControlElement::MapImageAlignAttributeInto(aAttributes,
248                                                               aDecls);
249   nsGenericHTMLFormControlElement::MapImageBorderAttributeInto(aAttributes,
250                                                                aDecls);
251   nsGenericHTMLFormControlElement::MapImageMarginAttributeInto(aAttributes,
252                                                                aDecls);
253   nsGenericHTMLFormControlElement::MapImageSizeAttributesInto(aAttributes,
254                                                               aDecls);
255   nsGenericHTMLFormControlElement::MapCommonAttributesInto(aAttributes, aDecls);
256 }
257 
NS_IMETHODIMP_(bool)258 NS_IMETHODIMP_(bool)
259 HTMLObjectElement::IsAttributeMapped(const nsAtom* aAttribute) const {
260   static const MappedAttributeEntry* const map[] = {
261       sCommonAttributeMap,
262       sImageMarginSizeAttributeMap,
263       sImageBorderAttributeMap,
264       sImageAlignAttributeMap,
265   };
266 
267   return FindAttributeDependence(aAttribute, map);
268 }
269 
GetAttributeMappingFunction() const270 nsMapRuleToAttributesFunc HTMLObjectElement::GetAttributeMappingFunction()
271     const {
272   return &MapAttributesIntoRule;
273 }
274 
StartObjectLoad(bool aNotify,bool aForce)275 void HTMLObjectElement::StartObjectLoad(bool aNotify, bool aForce) {
276   // BindToTree can call us asynchronously, and we may be removed from the tree
277   // in the interim
278   if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
279       BlockEmbedOrObjectContentLoading()) {
280     return;
281   }
282 
283   LoadObject(aNotify, aForce);
284   SetIsNetworkCreated(false);
285 }
286 
IntrinsicState() const287 EventStates HTMLObjectElement::IntrinsicState() const {
288   return nsGenericHTMLFormControlElement::IntrinsicState() | ObjectState();
289 }
290 
GetCapabilities() const291 uint32_t HTMLObjectElement::GetCapabilities() const {
292   return nsObjectLoadingContent::GetCapabilities() | eFallbackIfClassIDPresent;
293 }
294 
DestroyContent()295 void HTMLObjectElement::DestroyContent() {
296   nsObjectLoadingContent::Destroy();
297   nsGenericHTMLFormControlElement::DestroyContent();
298 }
299 
CopyInnerTo(Element * aDest)300 nsresult HTMLObjectElement::CopyInnerTo(Element* aDest) {
301   nsresult rv = nsGenericHTMLFormControlElement::CopyInnerTo(aDest);
302   NS_ENSURE_SUCCESS(rv, rv);
303 
304   if (aDest->OwnerDoc()->IsStaticDocument()) {
305     CreateStaticClone(static_cast<HTMLObjectElement*>(aDest));
306   }
307 
308   return rv;
309 }
310 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)311 JSObject* HTMLObjectElement::WrapNode(JSContext* aCx,
312                                       JS::Handle<JSObject*> aGivenProto) {
313   return HTMLObjectElement_Binding::Wrap(aCx, this, aGivenProto);
314 }
315 
316 }  // namespace mozilla::dom
317 
318 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Object)
319