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/. */
7 #include "mozilla/EventStates.h"
8 #include "mozilla/dom/BindContext.h"
9 #include "mozilla/dom/HTMLFormSubmission.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"
17 #include "mozilla/dom/Document.h"
18 #include "nsIPluginDocument.h"
19 #include "nsIObjectFrame.h"
20 #include "nsNPAPIPluginInstance.h"
21 #include "nsIWidget.h"
22 #include "nsContentUtils.h"
24 # include "mozilla/EventDispatcher.h"
25 # include "mozilla/dom/Event.h"
26 # include "nsFocusManager.h"
32 HTMLObjectElement::HTMLObjectElement(
33 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
34 FromParser aFromParser
)
35 : nsGenericHTMLFormElement(std::move(aNodeInfo
), NS_FORM_OBJECT
),
36 mIsDoneAddingChildren(!aFromParser
) {
37 RegisterActivityObserver();
38 SetIsNetworkCreated(aFromParser
== FROM_PARSER_NETWORK
);
40 // <object> is always barred from constraint validation.
41 SetBarredFromConstraintValidation(true);
43 // By default we're in the loading state
44 AddStatesSilently(NS_EVENT_STATE_LOADING
);
47 HTMLObjectElement::~HTMLObjectElement() {
49 OnFocusBlurPlugin(this, false);
51 UnregisterActivityObserver();
52 nsImageLoadingContent::Destroy();
55 bool HTMLObjectElement::IsInteractiveHTMLContent() const {
56 return HasAttr(kNameSpaceID_None
, nsGkAtoms::usemap
) ||
57 nsGenericHTMLFormElement::IsInteractiveHTMLContent();
60 void HTMLObjectElement::AsyncEventRunning(AsyncEventDispatcher
* aEvent
) {
61 nsImageLoadingContent::AsyncEventRunning(aEvent
);
64 bool HTMLObjectElement::IsDoneAddingChildren() { return mIsDoneAddingChildren
; }
66 void HTMLObjectElement::DoneAddingChildren(bool aHaveNotified
) {
67 mIsDoneAddingChildren
= true;
69 // If we're already in a document, we need to trigger the load
70 // Otherwise, BindToTree takes care of that.
71 if (IsInComposedDoc()) {
72 StartObjectLoad(aHaveNotified
, false);
76 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLObjectElement
)
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLObjectElement
,
79 nsGenericHTMLFormElement
)
80 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity
)
81 nsObjectLoadingContent::Traverse(tmp
, cb
);
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
84 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLObjectElement
,
85 nsGenericHTMLFormElement
)
86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity
)
87 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
89 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(
90 HTMLObjectElement
, nsGenericHTMLFormElement
, imgINotificationObserver
,
91 nsIRequestObserver
, nsIStreamListener
, nsFrameLoaderOwner
,
92 nsIObjectLoadingContent
, nsIImageLoadingContent
, nsIChannelEventSink
,
93 nsIConstraintValidation
)
95 NS_IMPL_ELEMENT_CLONE(HTMLObjectElement
)
99 static nsIWidget
* GetWidget(Element
* aElement
) {
100 return nsContentUtils::WidgetForDocument(aElement
->OwnerDoc());
103 Element
* HTMLObjectElement::sLastFocused
= nullptr; // Weak
105 class PluginFocusSetter
: public Runnable
{
107 PluginFocusSetter(nsIWidget
* aWidget
, Element
* aElement
)
108 : Runnable("PluginFocusSetter"), mWidget(aWidget
), mElement(aElement
) {}
110 NS_IMETHOD
Run() override
{
112 HTMLObjectElement::sLastFocused
= mElement
;
114 mWidget
->SetPluginFocused(value
);
115 } else if (!HTMLObjectElement::sLastFocused
) {
117 mWidget
->SetPluginFocused(value
);
124 nsCOMPtr
<nsIWidget
> mWidget
;
125 nsCOMPtr
<Element
> mElement
;
128 void HTMLObjectElement::OnFocusBlurPlugin(Element
* aElement
, bool aFocus
) {
129 // In general we don't want to call nsIWidget::SetPluginFocused() for any
130 // Element that doesn't have a plugin running. But if SetPluginFocused(true)
131 // was just called for aElement while it had a plugin running, we want to
132 // make sure nsIWidget::SetPluginFocused(false) gets called for it now, even
133 // if aFocus is true.
135 nsCOMPtr
<nsIObjectLoadingContent
> olc
= do_QueryInterface(aElement
);
136 bool hasRunningPlugin
= false;
139 static_cast<nsObjectLoadingContent
*>(olc
.get())->HasRunningPlugin();
141 if (!hasRunningPlugin
) {
146 if (aFocus
|| aElement
== sLastFocused
) {
148 sLastFocused
= nullptr;
150 nsIWidget
* widget
= GetWidget(aElement
);
152 nsContentUtils::AddScriptRunner(
153 new PluginFocusSetter(widget
, aFocus
? aElement
: nullptr));
158 void HTMLObjectElement::HandlePluginCrashed(Element
* aElement
) {
159 OnFocusBlurPlugin(aElement
, false);
162 void HTMLObjectElement::HandlePluginInstantiated(Element
* aElement
) {
163 // If aElement is already focused when a plugin is instantiated, we need
164 // to initiate a call to nsIWidget::SetPluginFocused(true). Otherwise
165 // keyboard input won't work in a click-to-play plugin until aElement
166 // loses focus and regains it.
167 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
168 if (fm
&& fm
->GetFocusedElement() == aElement
) {
169 OnFocusBlurPlugin(aElement
, true);
173 void HTMLObjectElement::HandleFocusBlurPlugin(Element
* aElement
,
174 WidgetEvent
* aEvent
) {
175 if (!aEvent
->IsTrusted()) {
178 switch (aEvent
->mMessage
) {
180 OnFocusBlurPlugin(aElement
, true);
184 OnFocusBlurPlugin(aElement
, false);
193 HTMLObjectElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
194 HandleFocusBlurPlugin(this, aVisitor
.mEvent
);
198 #endif // #ifdef XP_MACOSX
200 nsresult
HTMLObjectElement::BindToTree(BindContext
& aContext
,
202 nsresult rv
= nsGenericHTMLFormElement::BindToTree(aContext
, aParent
);
203 NS_ENSURE_SUCCESS(rv
, rv
);
205 rv
= nsObjectLoadingContent::BindToTree(aContext
, aParent
);
206 NS_ENSURE_SUCCESS(rv
, rv
);
208 // Don't kick off load from being bound to a plugin document - the plugin
209 // document will call nsObjectLoadingContent::InitializeFromChannel() for the
211 if (IsInComposedDoc()) {
212 nsCOMPtr
<nsIPluginDocument
> pluginDoc
=
213 do_QueryInterface(&aContext
.OwnerDoc());
214 // If we already have all the children, start the load.
215 if (mIsDoneAddingChildren
&& !pluginDoc
) {
216 void (HTMLObjectElement::*start
)() = &HTMLObjectElement::StartObjectLoad
;
217 nsContentUtils::AddScriptRunner(
218 NewRunnableMethod("dom::HTMLObjectElement::BindToTree", this, start
));
225 void HTMLObjectElement::UnbindFromTree(bool aNullParent
) {
227 // When a page is reloaded (when an Document's content is removed), the
228 // focused element isn't necessarily sent an eBlur event. See
229 // nsFocusManager::ContentRemoved(). This means that a widget may think it
230 // still contains a focused plugin when it doesn't -- which in turn can
231 // disable text input in the browser window. See bug 1137229.
232 OnFocusBlurPlugin(this, false);
234 nsObjectLoadingContent::UnbindFromTree(aNullParent
);
235 nsGenericHTMLFormElement::UnbindFromTree(aNullParent
);
238 nsresult
HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
239 const nsAttrValue
* aValue
,
240 const nsAttrValue
* aOldValue
,
241 nsIPrincipal
* aSubjectPrincipal
,
243 nsresult rv
= AfterMaybeChangeAttr(aNamespaceID
, aName
, aNotify
);
244 NS_ENSURE_SUCCESS(rv
, rv
);
246 return nsGenericHTMLFormElement::AfterSetAttr(
247 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
250 nsresult
HTMLObjectElement::OnAttrSetButNotChanged(
251 int32_t aNamespaceID
, nsAtom
* aName
, const nsAttrValueOrString
& aValue
,
253 nsresult rv
= AfterMaybeChangeAttr(aNamespaceID
, aName
, aNotify
);
254 NS_ENSURE_SUCCESS(rv
, rv
);
256 return nsGenericHTMLFormElement::OnAttrSetButNotChanged(aNamespaceID
, aName
,
260 nsresult
HTMLObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID
,
261 nsAtom
* aName
, bool aNotify
) {
262 if (aNamespaceID
== kNameSpaceID_None
) {
263 // if aNotify is false, we are coming from the parser or some such place;
264 // we'll get bound after all the attributes have been set, so we'll do the
265 // object load from BindToTree/DoneAddingChildren.
266 // Skip the LoadObject call in that case.
267 // We also don't want to start loading the object when we're not yet in
268 // a document, just in case that the caller wants to set additional
269 // attributes before inserting the node into the document.
270 if (aNotify
&& IsInComposedDoc() && mIsDoneAddingChildren
&&
271 aName
== nsGkAtoms::data
&& !BlockEmbedOrObjectContentLoading()) {
272 return LoadObject(aNotify
, true);
279 bool HTMLObjectElement::IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
280 int32_t* aTabIndex
) {
281 // TODO: this should probably be managed directly by IsHTMLFocusable.
283 Document
* doc
= GetComposedDoc();
284 if (!doc
|| doc
->HasFlag(NODE_IS_EDITABLE
)) {
289 *aIsFocusable
= false;
293 const nsAttrValue
* attrVal
= mAttrs
.GetAttr(nsGkAtoms::tabindex
);
294 bool isFocusable
= attrVal
&& attrVal
->Type() == nsAttrValue::eInteger
;
296 // Has plugin content: let the plugin decide what to do in terms of
297 // internal focus from mouse clicks
298 if (Type() == eType_Plugin
) {
300 *aTabIndex
= isFocusable
? attrVal
->GetIntegerValue() : -1;
303 *aIsFocusable
= true;
307 // This method doesn't call nsGenericHTMLFormElement intentionally.
308 // TODO: It should probably be changed when bug 597242 will be fixed.
309 if (IsEditableRoot() ||
310 ((Type() == eType_Document
|| Type() == eType_FakePlugin
) &&
311 nsContentUtils::IsSubDocumentTabbable(this))) {
313 *aTabIndex
= isFocusable
? attrVal
->GetIntegerValue() : 0;
316 *aIsFocusable
= true;
320 // TODO: this should probably be managed directly by IsHTMLFocusable.
322 if (aTabIndex
&& isFocusable
) {
323 *aTabIndex
= attrVal
->GetIntegerValue();
324 *aIsFocusable
= true;
330 nsIContent::IMEState
HTMLObjectElement::GetDesiredIMEState() {
331 if (Type() == eType_Plugin
) {
332 return IMEState(IMEState::PLUGIN
);
335 return nsGenericHTMLFormElement::GetDesiredIMEState();
339 HTMLObjectElement::Reset() { return NS_OK
; }
342 HTMLObjectElement::SubmitNamesValues(HTMLFormSubmission
* aFormSubmission
) {
344 if (!GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, name
)) {
345 // No name, don't submit.
350 nsIFrame
* frame
= GetPrimaryFrame();
352 nsIObjectFrame
* objFrame
= do_QueryFrame(frame
);
354 // No frame, nothing to submit.
359 RefPtr
<nsNPAPIPluginInstance
> pi
= objFrame
->GetPluginInstance();
365 nsresult rv
= pi
->GetFormValue(value
);
366 NS_ENSURE_SUCCESS(rv
, rv
);
368 return aFormSubmission
->AddNameValuePair(name
, value
);
371 int32_t HTMLObjectElement::TabIndexDefault() { return 0; }
373 Nullable
<WindowProxyHolder
> HTMLObjectElement::GetContentWindow(
374 nsIPrincipal
& aSubjectPrincipal
) {
375 Document
* doc
= GetContentDocument(aSubjectPrincipal
);
377 nsPIDOMWindowOuter
* win
= doc
->GetWindow();
379 return WindowProxyHolder(win
->GetBrowsingContext());
386 bool HTMLObjectElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
387 const nsAString
& aValue
,
388 nsIPrincipal
* aMaybeScriptedPrincipal
,
389 nsAttrValue
& aResult
) {
390 if (aNamespaceID
== kNameSpaceID_None
) {
391 if (aAttribute
== nsGkAtoms::align
) {
392 return ParseAlignValue(aValue
, aResult
);
394 if (ParseImageAttribute(aAttribute
, aValue
, aResult
)) {
399 return nsGenericHTMLFormElement::ParseAttribute(
400 aNamespaceID
, aAttribute
, aValue
, aMaybeScriptedPrincipal
, aResult
);
403 void HTMLObjectElement::MapAttributesIntoRule(
404 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
405 nsGenericHTMLFormElement::MapImageAlignAttributeInto(aAttributes
, aDecls
);
406 nsGenericHTMLFormElement::MapImageBorderAttributeInto(aAttributes
, aDecls
);
407 nsGenericHTMLFormElement::MapImageMarginAttributeInto(aAttributes
, aDecls
);
408 nsGenericHTMLFormElement::MapImageSizeAttributesInto(aAttributes
, aDecls
);
409 nsGenericHTMLFormElement::MapCommonAttributesInto(aAttributes
, aDecls
);
413 HTMLObjectElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
414 static const MappedAttributeEntry
* const map
[] = {
416 sImageMarginSizeAttributeMap
,
417 sImageBorderAttributeMap
,
418 sImageAlignAttributeMap
,
421 return FindAttributeDependence(aAttribute
, map
);
424 nsMapRuleToAttributesFunc
HTMLObjectElement::GetAttributeMappingFunction()
426 return &MapAttributesIntoRule
;
429 void HTMLObjectElement::StartObjectLoad(bool aNotify
, bool aForce
) {
430 // BindToTree can call us asynchronously, and we may be removed from the tree
432 if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
433 BlockEmbedOrObjectContentLoading()) {
437 LoadObject(aNotify
, aForce
);
438 SetIsNetworkCreated(false);
441 EventStates
HTMLObjectElement::IntrinsicState() const {
442 return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
445 uint32_t HTMLObjectElement::GetCapabilities() const {
446 return nsObjectLoadingContent::GetCapabilities() | eFallbackIfClassIDPresent
;
449 void HTMLObjectElement::DestroyContent() {
450 nsObjectLoadingContent::Destroy();
451 nsGenericHTMLFormElement::DestroyContent();
454 nsresult
HTMLObjectElement::CopyInnerTo(Element
* aDest
) {
455 nsresult rv
= nsGenericHTMLFormElement::CopyInnerTo(aDest
);
456 NS_ENSURE_SUCCESS(rv
, rv
);
458 if (aDest
->OwnerDoc()->IsStaticDocument()) {
459 CreateStaticClone(static_cast<HTMLObjectElement
*>(aDest
));
465 JSObject
* HTMLObjectElement::WrapNode(JSContext
* aCx
,
466 JS::Handle
<JSObject
*> aGivenProto
) {
467 JS::Rooted
<JSObject
*> obj(
468 aCx
, HTMLObjectElement_Binding::Wrap(aCx
, this, aGivenProto
));
472 SetupProtoChain(aCx
, obj
);
477 } // namespace mozilla
479 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Object
)