Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLObjectElement.cpp
Line
Count
Source (jump to first uncovered line)
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/HTMLFormSubmission.h"
9
#include "mozilla/dom/HTMLObjectElement.h"
10
#include "mozilla/dom/HTMLObjectElementBinding.h"
11
#include "mozilla/dom/ElementInlines.h"
12
#include "nsAttrValueInlines.h"
13
#include "nsGkAtoms.h"
14
#include "nsError.h"
15
#include "nsIDocument.h"
16
#include "nsIPluginDocument.h"
17
#include "nsIObjectFrame.h"
18
#include "nsNPAPIPluginInstance.h"
19
#include "nsIWidget.h"
20
#include "nsContentUtils.h"
21
#ifdef XP_MACOSX
22
#include "mozilla/EventDispatcher.h"
23
#include "mozilla/dom/Event.h"
24
#include "nsFocusManager.h"
25
#endif
26
27
namespace mozilla {
28
namespace dom {
29
30
HTMLObjectElement::HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
31
                                     FromParser aFromParser)
32
  : nsGenericHTMLFormElement(std::move(aNodeInfo), NS_FORM_OBJECT),
33
    mIsDoneAddingChildren(!aFromParser)
34
0
{
35
0
  RegisterActivityObserver();
36
0
  SetIsNetworkCreated(aFromParser == FROM_PARSER_NETWORK);
37
0
38
0
  // <object> is always barred from constraint validation.
39
0
  SetBarredFromConstraintValidation(true);
40
0
41
0
  // By default we're in the loading state
42
0
  AddStatesSilently(NS_EVENT_STATE_LOADING);
43
0
}
44
45
HTMLObjectElement::~HTMLObjectElement()
46
0
{
47
#ifdef XP_MACOSX
48
  OnFocusBlurPlugin(this, false);
49
#endif
50
  UnregisterActivityObserver();
51
0
  DestroyImageLoadingContent();
52
0
}
53
54
bool
55
HTMLObjectElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
56
0
{
57
0
  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
58
0
         nsGenericHTMLFormElement::IsInteractiveHTMLContent(aIgnoreTabindex);
59
0
}
60
61
void
62
HTMLObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
63
0
{
64
0
  nsImageLoadingContent::AsyncEventRunning(aEvent);
65
0
}
66
67
bool
68
HTMLObjectElement::IsDoneAddingChildren()
69
0
{
70
0
  return mIsDoneAddingChildren;
71
0
}
72
73
void
74
HTMLObjectElement::DoneAddingChildren(bool aHaveNotified)
75
0
{
76
0
  mIsDoneAddingChildren = true;
77
0
78
0
  // If we're already in a document, we need to trigger the load
79
0
  // Otherwise, BindToTree takes care of that.
80
0
  if (IsInComposedDoc()) {
81
0
    StartObjectLoad(aHaveNotified, false);
82
0
  }
83
0
}
84
85
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLObjectElement)
86
87
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLObjectElement,
88
0
                                                  nsGenericHTMLFormElement)
89
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
90
0
  nsObjectLoadingContent::Traverse(tmp, cb);
91
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
92
93
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLObjectElement,
94
0
                                                nsGenericHTMLFormElement)
95
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
96
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
97
98
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLObjectElement,
99
                                             nsGenericHTMLFormElement,
100
                                             imgINotificationObserver,
101
                                             nsIRequestObserver,
102
                                             nsIStreamListener,
103
                                             nsIFrameLoaderOwner,
104
                                             nsIObjectLoadingContent,
105
                                             nsIImageLoadingContent,
106
                                             nsIChannelEventSink,
107
                                             nsIConstraintValidation)
108
109
NS_IMPL_ELEMENT_CLONE(HTMLObjectElement)
110
111
#ifdef XP_MACOSX
112
113
static nsIWidget* GetWidget(Element* aElement)
114
{
115
  return nsContentUtils::WidgetForDocument(aElement->OwnerDoc());
116
}
117
118
Element* HTMLObjectElement::sLastFocused = nullptr; // Weak
119
120
class PluginFocusSetter : public Runnable
121
{
122
public:
123
  PluginFocusSetter(nsIWidget* aWidget, Element* aElement)
124
    : Runnable("PluginFocusSetter")
125
    , mWidget(aWidget)
126
    , mElement(aElement)
127
  {
128
  }
129
130
  NS_IMETHOD Run() override
131
  {
132
    if (mElement) {
133
      HTMLObjectElement::sLastFocused = mElement;
134
      bool value = true;
135
      mWidget->SetPluginFocused(value);
136
    } else if (!HTMLObjectElement::sLastFocused) {
137
      bool value = false;
138
      mWidget->SetPluginFocused(value);
139
    }
140
141
    return NS_OK;
142
  }
143
144
private:
145
  nsCOMPtr<nsIWidget> mWidget;
146
  nsCOMPtr<Element> mElement;
147
};
148
149
void
150
HTMLObjectElement::OnFocusBlurPlugin(Element* aElement, bool aFocus)
151
{
152
  // In general we don't want to call nsIWidget::SetPluginFocused() for any
153
  // Element that doesn't have a plugin running.  But if SetPluginFocused(true)
154
  // was just called for aElement while it had a plugin running, we want to
155
  // make sure nsIWidget::SetPluginFocused(false) gets called for it now, even
156
  // if aFocus is true.
157
  if (aFocus) {
158
    nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aElement);
159
    bool hasRunningPlugin = false;
160
    if (olc) {
161
      hasRunningPlugin =
162
        static_cast<nsObjectLoadingContent*>(olc.get())->HasRunningPlugin();
163
    }
164
    if (!hasRunningPlugin) {
165
      aFocus = false;
166
    }
167
  }
168
169
  if (aFocus || aElement == sLastFocused) {
170
    if (!aFocus) {
171
      sLastFocused = nullptr;
172
    }
173
    nsIWidget* widget = GetWidget(aElement);
174
    if (widget) {
175
      nsContentUtils::AddScriptRunner(
176
        new PluginFocusSetter(widget, aFocus ? aElement : nullptr));
177
    }
178
  }
179
}
180
181
void
182
HTMLObjectElement::HandlePluginCrashed(Element* aElement)
183
{
184
  OnFocusBlurPlugin(aElement, false);
185
}
186
187
void
188
HTMLObjectElement::HandlePluginInstantiated(Element* aElement)
189
{
190
  // If aElement is already focused when a plugin is instantiated, we need
191
  // to initiate a call to nsIWidget::SetPluginFocused(true).  Otherwise
192
  // keyboard input won't work in a click-to-play plugin until aElement
193
  // loses focus and regains it.
194
  nsFocusManager *fm = nsFocusManager::GetFocusManager();
195
  if (fm && fm->GetFocusedElement() == aElement) {
196
    OnFocusBlurPlugin(aElement, true);
197
  }
198
}
199
200
void
201
HTMLObjectElement::HandleFocusBlurPlugin(Element* aElement,
202
                                         WidgetEvent* aEvent)
203
{
204
  if (!aEvent->IsTrusted()) {
205
    return;
206
  }
207
  switch (aEvent->mMessage) {
208
    case eFocus: {
209
      OnFocusBlurPlugin(aElement, true);
210
      break;
211
    }
212
    case eBlur: {
213
      OnFocusBlurPlugin(aElement, false);
214
      break;
215
    }
216
    default:
217
      break;
218
  }
219
}
220
221
NS_IMETHODIMP
222
HTMLObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
223
{
224
  HandleFocusBlurPlugin(this, aVisitor.mEvent);
225
  return NS_OK;
226
}
227
228
#endif // #ifdef XP_MACOSX
229
230
nsresult
231
HTMLObjectElement::BindToTree(nsIDocument *aDocument,
232
                              nsIContent *aParent,
233
                              nsIContent *aBindingParent)
234
0
{
235
0
  nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
236
0
                                                     aBindingParent);
237
0
  NS_ENSURE_SUCCESS(rv, rv);
238
0
239
0
  rv = nsObjectLoadingContent::BindToTree(aDocument, aParent,
240
0
                                          aBindingParent);
241
0
  NS_ENSURE_SUCCESS(rv, rv);
242
0
243
0
  // Don't kick off load from being bound to a plugin document - the plugin
244
0
  // document will call nsObjectLoadingContent::InitializeFromChannel() for the
245
0
  // initial load.
246
0
  nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
247
0
248
0
  // If we already have all the children, start the load.
249
0
  if (mIsDoneAddingChildren && !pluginDoc) {
250
0
    void (HTMLObjectElement::*start)() = &HTMLObjectElement::StartObjectLoad;
251
0
    nsContentUtils::AddScriptRunner(
252
0
      NewRunnableMethod("dom::HTMLObjectElement::BindToTree", this, start));
253
0
  }
254
0
255
0
  return NS_OK;
256
0
}
257
258
void
259
HTMLObjectElement::UnbindFromTree(bool aDeep,
260
                                  bool aNullParent)
261
0
{
262
#ifdef XP_MACOSX
263
  // When a page is reloaded (when an nsIDocument's content is removed), the
264
  // focused element isn't necessarily sent an eBlur event. See
265
  // nsFocusManager::ContentRemoved(). This means that a widget may think it
266
  // still contains a focused plugin when it doesn't -- which in turn can
267
  // disable text input in the browser window. See bug 1137229.
268
  OnFocusBlurPlugin(this, false);
269
#endif
270
  nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
271
0
  nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
272
0
}
273
274
nsresult
275
HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
276
                                const nsAttrValue* aValue,
277
                                const nsAttrValue* aOldValue,
278
                                nsIPrincipal* aSubjectPrincipal,
279
                                bool aNotify)
280
0
{
281
0
  nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
282
0
  NS_ENSURE_SUCCESS(rv, rv);
283
0
284
0
  return nsGenericHTMLFormElement::AfterSetAttr(aNamespaceID, aName, aValue,
285
0
                                                aOldValue, aSubjectPrincipal, aNotify);
286
0
}
287
288
nsresult
289
HTMLObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
290
                                          const nsAttrValueOrString& aValue,
291
                                          bool aNotify)
292
0
{
293
0
  nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
294
0
  NS_ENSURE_SUCCESS(rv, rv);
295
0
296
0
  return nsGenericHTMLFormElement::OnAttrSetButNotChanged(aNamespaceID, aName,
297
0
                                                          aValue, aNotify);
298
0
}
299
300
nsresult
301
HTMLObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName,
302
                                        bool aNotify)
303
0
{
304
0
  if (aNamespaceID == kNameSpaceID_None) {
305
0
    // if aNotify is false, we are coming from the parser or some such place;
306
0
    // we'll get bound after all the attributes have been set, so we'll do the
307
0
    // object load from BindToTree/DoneAddingChildren.
308
0
    // Skip the LoadObject call in that case.
309
0
    // We also don't want to start loading the object when we're not yet in
310
0
    // a document, just in case that the caller wants to set additional
311
0
    // attributes before inserting the node into the document.
312
0
    if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
313
0
        aName == nsGkAtoms::data && !BlockEmbedOrObjectContentLoading()) {
314
0
      return LoadObject(aNotify, true);
315
0
    }
316
0
  }
317
0
318
0
  return NS_OK;
319
0
}
320
321
bool
322
HTMLObjectElement::IsFocusableForTabIndex()
323
0
{
324
0
  nsIDocument* doc = GetComposedDoc();
325
0
  if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
326
0
    return false;
327
0
  }
328
0
329
0
  return IsEditableRoot() ||
330
0
         ((Type() == eType_Document || Type() == eType_FakePlugin) &&
331
0
          nsContentUtils::IsSubDocumentTabbable(this));
332
0
}
333
334
bool
335
HTMLObjectElement::IsHTMLFocusable(bool aWithMouse,
336
                                   bool *aIsFocusable, int32_t *aTabIndex)
337
0
{
338
0
  // TODO: this should probably be managed directly by IsHTMLFocusable.
339
0
  // See bug 597242.
340
0
  nsIDocument *doc = GetComposedDoc();
341
0
  if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
342
0
    if (aTabIndex) {
343
0
      *aTabIndex = TabIndex();
344
0
    }
345
0
346
0
    *aIsFocusable = false;
347
0
348
0
    return false;
349
0
  }
350
0
351
0
  // This method doesn't call nsGenericHTMLFormElement intentionally.
352
0
  // TODO: It should probably be changed when bug 597242 will be fixed.
353
0
  if (Type() == eType_Plugin || IsEditableRoot() ||
354
0
      ((Type() == eType_Document || Type() == eType_FakePlugin) &&
355
0
       nsContentUtils::IsSubDocumentTabbable(this))) {
356
0
    // Has plugin content: let the plugin decide what to do in terms of
357
0
    // internal focus from mouse clicks
358
0
    if (aTabIndex) {
359
0
      *aTabIndex = TabIndex();
360
0
    }
361
0
362
0
    *aIsFocusable = true;
363
0
364
0
    return false;
365
0
  }
366
0
367
0
  // TODO: this should probably be managed directly by IsHTMLFocusable.
368
0
  // See bug 597242.
369
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::tabindex);
370
0
371
0
  *aIsFocusable = attrVal && attrVal->Type() == nsAttrValue::eInteger;
372
0
373
0
  if (aTabIndex && *aIsFocusable) {
374
0
    *aTabIndex = attrVal->GetIntegerValue();
375
0
  }
376
0
377
0
  return false;
378
0
}
379
380
nsIContent::IMEState
381
HTMLObjectElement::GetDesiredIMEState()
382
0
{
383
0
  if (Type() == eType_Plugin) {
384
0
    return IMEState(IMEState::PLUGIN);
385
0
  }
386
0
387
0
  return nsGenericHTMLFormElement::GetDesiredIMEState();
388
0
}
389
390
NS_IMETHODIMP
391
HTMLObjectElement::Reset()
392
0
{
393
0
  return NS_OK;
394
0
}
395
396
NS_IMETHODIMP
397
HTMLObjectElement::SubmitNamesValues(HTMLFormSubmission *aFormSubmission)
398
0
{
399
0
  nsAutoString name;
400
0
  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
401
0
    // No name, don't submit.
402
0
403
0
    return NS_OK;
404
0
  }
405
0
406
0
  nsIFrame* frame = GetPrimaryFrame();
407
0
408
0
  nsIObjectFrame *objFrame = do_QueryFrame(frame);
409
0
  if (!objFrame) {
410
0
    // No frame, nothing to submit.
411
0
412
0
    return NS_OK;
413
0
  }
414
0
415
0
  RefPtr<nsNPAPIPluginInstance> pi;
416
0
  objFrame->GetPluginInstance(getter_AddRefs(pi));
417
0
  if (!pi)
418
0
    return NS_OK;
419
0
420
0
  nsAutoString value;
421
0
  nsresult rv = pi->GetFormValue(value);
422
0
  NS_ENSURE_SUCCESS(rv, rv);
423
0
424
0
  return aFormSubmission->AddNameValuePair(name, value);
425
0
}
426
427
int32_t
428
HTMLObjectElement::TabIndexDefault()
429
0
{
430
0
  return IsFocusableForTabIndex() ? 0 : -1;
431
0
}
432
433
nsPIDOMWindowOuter*
434
HTMLObjectElement::GetContentWindow(nsIPrincipal& aSubjectPrincipal)
435
0
{
436
0
  nsIDocument* doc = GetContentDocument(aSubjectPrincipal);
437
0
  if (doc) {
438
0
    return doc->GetWindow();
439
0
  }
440
0
441
0
  return nullptr;
442
0
}
443
444
bool
445
HTMLObjectElement::ParseAttribute(int32_t aNamespaceID,
446
                                  nsAtom *aAttribute,
447
                                  const nsAString &aValue,
448
                                  nsIPrincipal* aMaybeScriptedPrincipal,
449
                                  nsAttrValue &aResult)
450
0
{
451
0
  if (aNamespaceID == kNameSpaceID_None) {
452
0
    if (aAttribute == nsGkAtoms::align) {
453
0
      return ParseAlignValue(aValue, aResult);
454
0
    }
455
0
    if (ParseImageAttribute(aAttribute, aValue, aResult)) {
456
0
      return true;
457
0
    }
458
0
  }
459
0
460
0
  return nsGenericHTMLFormElement::ParseAttribute(aNamespaceID, aAttribute,
461
0
                                                  aValue, aMaybeScriptedPrincipal, aResult);
462
0
}
463
464
void
465
HTMLObjectElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
466
                                         MappedDeclarations& aDecls)
467
0
{
468
0
  nsGenericHTMLFormElement::MapImageAlignAttributeInto(aAttributes, aDecls);
469
0
  nsGenericHTMLFormElement::MapImageBorderAttributeInto(aAttributes, aDecls);
470
0
  nsGenericHTMLFormElement::MapImageMarginAttributeInto(aAttributes, aDecls);
471
0
  nsGenericHTMLFormElement::MapImageSizeAttributesInto(aAttributes, aDecls);
472
0
  nsGenericHTMLFormElement::MapCommonAttributesInto(aAttributes, aDecls);
473
0
}
474
475
NS_IMETHODIMP_(bool)
476
HTMLObjectElement::IsAttributeMapped(const nsAtom *aAttribute) const
477
0
{
478
0
  static const MappedAttributeEntry* const map[] = {
479
0
    sCommonAttributeMap,
480
0
    sImageMarginSizeAttributeMap,
481
0
    sImageBorderAttributeMap,
482
0
    sImageAlignAttributeMap,
483
0
  };
484
0
485
0
  return FindAttributeDependence(aAttribute, map);
486
0
}
487
488
489
nsMapRuleToAttributesFunc
490
HTMLObjectElement::GetAttributeMappingFunction() const
491
0
{
492
0
  return &MapAttributesIntoRule;
493
0
}
494
495
void
496
HTMLObjectElement::StartObjectLoad(bool aNotify, bool aForce)
497
0
{
498
0
  // BindToTree can call us asynchronously, and we may be removed from the tree
499
0
  // in the interim
500
0
  if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
501
0
      BlockEmbedOrObjectContentLoading()) {
502
0
    return;
503
0
  }
504
0
505
0
  LoadObject(aNotify, aForce);
506
0
  SetIsNetworkCreated(false);
507
0
}
508
509
EventStates
510
HTMLObjectElement::IntrinsicState() const
511
0
{
512
0
  return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
513
0
}
514
515
uint32_t
516
HTMLObjectElement::GetCapabilities() const
517
0
{
518
0
  return nsObjectLoadingContent::GetCapabilities() | eFallbackIfClassIDPresent;
519
0
}
520
521
void
522
HTMLObjectElement::DestroyContent()
523
0
{
524
0
  nsObjectLoadingContent::DestroyContent();
525
0
  nsGenericHTMLFormElement::DestroyContent();
526
0
}
527
528
nsresult
529
HTMLObjectElement::CopyInnerTo(Element* aDest)
530
0
{
531
0
  nsresult rv = nsGenericHTMLFormElement::CopyInnerTo(aDest);
532
0
  NS_ENSURE_SUCCESS(rv, rv);
533
0
534
0
  if (aDest->OwnerDoc()->IsStaticDocument()) {
535
0
    CreateStaticClone(static_cast<HTMLObjectElement*>(aDest));
536
0
  }
537
0
538
0
  return rv;
539
0
}
540
541
JSObject*
542
HTMLObjectElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
543
0
{
544
0
  JS::Rooted<JSObject*> obj(aCx,
545
0
    HTMLObjectElement_Binding::Wrap(aCx, this, aGivenProto));
546
0
  if (!obj) {
547
0
    return nullptr;
548
0
  }
549
0
  SetupProtoChain(aCx, obj);
550
0
  return obj;
551
0
}
552
553
} // namespace dom
554
} // namespace mozilla
555
556
NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Object)