Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsObjectLoadingContent.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
 * A base class implementing nsIObjectLoadingContent for use by
8
 * various content nodes that want to provide plugin/document/image
9
 * loading functionality (eg <embed>, <object>, etc).
10
 */
11
12
// Interface headers
13
#include "imgLoader.h"
14
#include "nsIClassOfService.h"
15
#include "nsIConsoleService.h"
16
#include "nsIContent.h"
17
#include "nsIContentInlines.h"
18
#include "nsIDocShell.h"
19
#include "nsIDocument.h"
20
#include "nsIExternalProtocolHandler.h"
21
#include "nsIInterfaceRequestorUtils.h"
22
#include "nsIObjectFrame.h"
23
#include "nsIOService.h"
24
#include "nsIPermissionManager.h"
25
#include "nsPluginHost.h"
26
#include "nsPluginInstanceOwner.h"
27
#include "nsJSNPRuntime.h"
28
#include "nsINestedURI.h"
29
#include "nsIPresShell.h"
30
#include "nsScriptSecurityManager.h"
31
#include "nsIScriptSecurityManager.h"
32
#include "nsIStreamConverterService.h"
33
#include "nsIURILoader.h"
34
#include "nsIURL.h"
35
#include "nsIWebNavigation.h"
36
#include "nsIWebNavigationInfo.h"
37
#include "nsIScriptChannel.h"
38
#include "nsIBlocklistService.h"
39
#include "nsIAsyncVerifyRedirectCallback.h"
40
#include "nsIAppShell.h"
41
#include "nsIXULRuntime.h"
42
#include "nsIScriptError.h"
43
44
#include "nsError.h"
45
46
// Util headers
47
#include "prenv.h"
48
#include "mozilla/Logging.h"
49
50
#include "nsCURILoader.h"
51
#include "nsContentPolicyUtils.h"
52
#include "nsContentUtils.h"
53
#include "nsDocShellCID.h"
54
#include "nsGkAtoms.h"
55
#include "nsThreadUtils.h"
56
#include "nsNetUtil.h"
57
#include "nsMimeTypes.h"
58
#include "nsStyleUtil.h"
59
#include "nsUnicharUtils.h"
60
#include "mozilla/Preferences.h"
61
#include "nsSandboxFlags.h"
62
63
// Concrete classes
64
#include "nsFrameLoader.h"
65
66
#include "nsObjectLoadingContent.h"
67
#include "mozAutoDocUpdate.h"
68
#include "nsIContentSecurityPolicy.h"
69
#include "GeckoProfiler.h"
70
#include "nsPluginFrame.h"
71
#include "nsWrapperCacheInlines.h"
72
#include "nsDOMJSUtils.h"
73
74
#include "nsWidgetsCID.h"
75
#include "nsContentCID.h"
76
#include "mozilla/BasicEvents.h"
77
#include "mozilla/dom/BindingUtils.h"
78
#include "mozilla/dom/Element.h"
79
#include "mozilla/dom/Event.h"
80
#include "mozilla/dom/ScriptSettings.h"
81
#include "mozilla/dom/PluginCrashedEvent.h"
82
#include "mozilla/AsyncEventDispatcher.h"
83
#include "mozilla/EventDispatcher.h"
84
#include "mozilla/EventStateManager.h"
85
#include "mozilla/EventStates.h"
86
#include "mozilla/IMEStateManager.h"
87
#include "mozilla/widget/IMEData.h"
88
#include "mozilla/IntegerPrintfMacros.h"
89
#include "mozilla/dom/HTMLObjectElementBinding.h"
90
#include "mozilla/dom/HTMLEmbedElement.h"
91
#include "mozilla/dom/HTMLObjectElement.h"
92
#include "mozilla/LoadInfo.h"
93
#include "nsChannelClassifier.h"
94
#include "nsFocusManager.h"
95
96
#ifdef XP_WIN
97
// Thanks so much, Microsoft! :(
98
#ifdef CreateEvent
99
#undef CreateEvent
100
#endif
101
#endif // XP_WIN
102
103
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
104
105
static const char kPrefYoutubeRewrite[] = "plugins.rewrite_youtube_embeds";
106
static const char kPrefBlockURIs[] = "browser.safebrowsing.blockedURIs.enabled";
107
static const char kPrefFavorFallbackMode[] = "plugins.favorfallback.mode";
108
static const char kPrefFavorFallbackRules[] = "plugins.favorfallback.rules";
109
110
using namespace mozilla;
111
using namespace mozilla::dom;
112
using namespace mozilla::net;
113
114
static LogModule*
115
GetObjectLog()
116
0
{
117
0
  static LazyLogModule sLog("objlc");
118
0
  return sLog;
119
0
}
120
121
0
#define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
122
#define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
123
124
static bool
125
IsFlashMIME(const nsACString & aMIMEType)
126
0
{
127
0
  return
128
0
    nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Flash;
129
0
}
130
131
static bool
132
InActiveDocument(nsIContent *aContent)
133
0
{
134
0
  if (!aContent->IsInComposedDoc()) {
135
0
    return false;
136
0
  }
137
0
  nsIDocument *doc = aContent->OwnerDoc();
138
0
  return (doc && doc->IsActive());
139
0
}
140
141
static bool
142
IsPluginType(nsObjectLoadingContent::ObjectType type)
143
0
{
144
0
  return type == nsObjectLoadingContent::eType_Plugin ||
145
0
         type == nsObjectLoadingContent::eType_FakePlugin;
146
0
}
147
148
///
149
/// Runnables and helper classes
150
///
151
152
class nsAsyncInstantiateEvent : public Runnable {
153
public:
154
  explicit nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
155
0
    : Runnable("nsAsyncInstantiateEvent"), mContent(aContent) {}
156
157
0
  ~nsAsyncInstantiateEvent() override = default;
158
159
  NS_IMETHOD Run() override;
160
161
private:
162
  nsCOMPtr<nsIObjectLoadingContent> mContent;
163
};
164
165
NS_IMETHODIMP
166
nsAsyncInstantiateEvent::Run()
167
0
{
168
0
  nsObjectLoadingContent *objLC =
169
0
    static_cast<nsObjectLoadingContent *>(mContent.get());
170
0
171
0
  // If objLC is no longer tracking this event, we've been canceled or
172
0
  // superseded
173
0
  if (objLC->mPendingInstantiateEvent != this) {
174
0
    return NS_OK;
175
0
  }
176
0
  objLC->mPendingInstantiateEvent = nullptr;
177
0
178
0
  return objLC->SyncStartPluginInstance();
179
0
}
180
181
// Checks to see if the content for a plugin instance should be unloaded
182
// (outside an active document) or stopped (in a document but unrendered). This
183
// is used to allow scripts to move a plugin around the document hierarchy
184
// without re-instantiating it.
185
class CheckPluginStopEvent : public Runnable {
186
public:
187
  explicit CheckPluginStopEvent(nsObjectLoadingContent* aContent)
188
0
  : Runnable("CheckPluginStopEvent"), mContent(aContent) {}
189
190
0
  ~CheckPluginStopEvent() override = default;
191
192
  NS_IMETHOD Run() override;
193
194
private:
195
  nsCOMPtr<nsIObjectLoadingContent> mContent;
196
};
197
198
NS_IMETHODIMP
199
CheckPluginStopEvent::Run()
200
0
{
201
0
  nsObjectLoadingContent *objLC =
202
0
    static_cast<nsObjectLoadingContent *>(mContent.get());
203
0
204
0
  // If objLC is no longer tracking this event, we've been canceled or
205
0
  // superseded. We clear this before we finish - either by calling
206
0
  // UnloadObject/StopPluginInstance, or directly if we took no action.
207
0
  if (objLC->mPendingCheckPluginStopEvent != this) {
208
0
    return NS_OK;
209
0
  }
210
0
211
0
  // CheckPluginStopEvent is queued when we either lose our frame, are removed
212
0
  // from the document, or the document goes inactive. To avoid stopping the
213
0
  // plugin when script is reparenting us or layout is rebuilding, we wait until
214
0
  // this event to decide to stop.
215
0
216
0
  nsCOMPtr<nsIContent> content =
217
0
    do_QueryInterface(static_cast<nsIImageLoadingContent *>(objLC));
218
0
  if (!InActiveDocument(content)) {
219
0
    LOG(("OBJLC [%p]: Unloading plugin outside of document", this));
220
0
    objLC->StopPluginInstance();
221
0
    return NS_OK;
222
0
  }
223
0
224
0
  if (content->GetPrimaryFrame()) {
225
0
    LOG(("OBJLC [%p]: CheckPluginStopEvent - in active document with frame"
226
0
         ", no action", this));
227
0
    objLC->mPendingCheckPluginStopEvent = nullptr;
228
0
    return NS_OK;
229
0
  }
230
0
231
0
  // In an active document, but still no frame. Flush layout to see if we can
232
0
  // regain a frame now.
233
0
  LOG(("OBJLC [%p]: CheckPluginStopEvent - No frame, flushing layout", this));
234
0
  nsIDocument* composedDoc = content->GetComposedDoc();
235
0
  if (composedDoc) {
236
0
    composedDoc->FlushPendingNotifications(FlushType::Layout);
237
0
    if (objLC->mPendingCheckPluginStopEvent != this) {
238
0
      LOG(("OBJLC [%p]: CheckPluginStopEvent - superseded in layout flush",
239
0
           this));
240
0
      return NS_OK;
241
0
    }
242
0
    if (content->GetPrimaryFrame()) {
243
0
      LOG(("OBJLC [%p]: CheckPluginStopEvent - frame gained in layout flush",
244
0
           this));
245
0
      objLC->mPendingCheckPluginStopEvent = nullptr;
246
0
      return NS_OK;
247
0
    }
248
0
  }
249
0
250
0
  // Still no frame, suspend plugin. HasNewFrame will restart us when we
251
0
  // become rendered again
252
0
  LOG(("OBJLC [%p]: Stopping plugin that lost frame", this));
253
0
  objLC->StopPluginInstance();
254
0
255
0
  return NS_OK;
256
0
}
257
258
/**
259
 * Helper task for firing simple events
260
 */
261
class nsSimplePluginEvent : public Runnable {
262
public:
263
  nsSimplePluginEvent(nsIContent* aTarget, const nsAString &aEvent)
264
    : Runnable("nsSimplePluginEvent")
265
    , mTarget(aTarget)
266
    , mDocument(aTarget->GetComposedDoc())
267
    , mEvent(aEvent)
268
0
  {
269
0
    MOZ_ASSERT(aTarget && mDocument);
270
0
  }
271
272
  nsSimplePluginEvent(nsIDocument* aTarget, const nsAString& aEvent)
273
    : mozilla::Runnable("nsSimplePluginEvent")
274
    , mTarget(aTarget)
275
    , mDocument(aTarget)
276
    , mEvent(aEvent)
277
0
  {
278
0
    MOZ_ASSERT(aTarget);
279
0
  }
280
281
  nsSimplePluginEvent(nsIContent* aTarget,
282
                      nsIDocument* aDocument,
283
                      const nsAString& aEvent)
284
    : mozilla::Runnable("nsSimplePluginEvent")
285
    , mTarget(aTarget)
286
    , mDocument(aDocument)
287
    , mEvent(aEvent)
288
0
  {
289
0
    MOZ_ASSERT(aTarget && aDocument);
290
0
  }
291
292
0
  ~nsSimplePluginEvent() override = default;
293
294
  NS_IMETHOD Run() override;
295
296
private:
297
  nsCOMPtr<nsISupports> mTarget;
298
  nsCOMPtr<nsIDocument> mDocument;
299
  nsString mEvent;
300
};
301
302
NS_IMETHODIMP
303
nsSimplePluginEvent::Run()
304
0
{
305
0
  if (mDocument && mDocument->IsActive()) {
306
0
    LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mTarget.get(),
307
0
         NS_ConvertUTF16toUTF8(mEvent).get()));
308
0
    nsContentUtils::DispatchTrustedEvent(mDocument, mTarget,
309
0
                                         mEvent, CanBubble::eYes,
310
0
                                         Cancelable::eYes);
311
0
  }
312
0
  return NS_OK;
313
0
}
314
315
/**
316
 * A task for firing PluginCrashed DOM Events.
317
 */
318
class nsPluginCrashedEvent : public Runnable {
319
public:
320
  nsCOMPtr<nsIContent> mContent;
321
  nsString mPluginDumpID;
322
  nsString mBrowserDumpID;
323
  nsString mPluginName;
324
  nsString mPluginFilename;
325
  bool mSubmittedCrashReport;
326
327
  nsPluginCrashedEvent(nsIContent* aContent,
328
                       const nsAString& aPluginDumpID,
329
                       const nsAString& aBrowserDumpID,
330
                       const nsAString& aPluginName,
331
                       const nsAString& aPluginFilename,
332
                       bool submittedCrashReport)
333
    : Runnable("nsPluginCrashedEvent"),
334
      mContent(aContent),
335
      mPluginDumpID(aPluginDumpID),
336
      mBrowserDumpID(aBrowserDumpID),
337
      mPluginName(aPluginName),
338
      mPluginFilename(aPluginFilename),
339
      mSubmittedCrashReport(submittedCrashReport)
340
0
  {}
341
342
0
  ~nsPluginCrashedEvent() override = default;
343
344
  NS_IMETHOD Run() override;
345
};
346
347
NS_IMETHODIMP
348
nsPluginCrashedEvent::Run()
349
0
{
350
0
  LOG(("OBJLC [%p]: Firing plugin crashed event\n",
351
0
       mContent.get()));
352
0
353
0
  nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
354
0
  if (!doc) {
355
0
    NS_WARNING("Couldn't get document for PluginCrashed event!");
356
0
    return NS_OK;
357
0
  }
358
0
359
0
  PluginCrashedEventInit init;
360
0
  init.mPluginDumpID = mPluginDumpID;
361
0
  init.mBrowserDumpID = mBrowserDumpID;
362
0
  init.mPluginName = mPluginName;
363
0
  init.mPluginFilename = mPluginFilename;
364
0
  init.mSubmittedCrashReport = mSubmittedCrashReport;
365
0
  init.mBubbles = true;
366
0
  init.mCancelable = true;
367
0
368
0
  RefPtr<PluginCrashedEvent> event =
369
0
    PluginCrashedEvent::Constructor(doc, NS_LITERAL_STRING("PluginCrashed"), init);
370
0
371
0
  event->SetTrusted(true);
372
0
  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
373
0
374
0
  EventDispatcher::DispatchDOMEvent(mContent, nullptr, event, nullptr, nullptr);
375
0
  return NS_OK;
376
0
}
377
378
// You can't take the address of bitfield members, so we have two separate
379
// classes for these :-/
380
381
// Sets a object's mInstantiating bit to false when destroyed
382
class AutoSetInstantiatingToFalse {
383
public:
384
  explicit AutoSetInstantiatingToFalse(nsObjectLoadingContent* aContent)
385
0
    : mContent(aContent) {}
386
0
  ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
387
private:
388
  nsObjectLoadingContent* mContent;
389
};
390
391
// Sets a object's mInstantiating bit to false when destroyed
392
class AutoSetLoadingToFalse {
393
public:
394
  explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
395
0
    : mContent(aContent) {}
396
0
  ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
397
private:
398
  nsObjectLoadingContent* mContent;
399
};
400
401
///
402
/// Helper functions
403
///
404
405
static bool
406
IsSuccessfulRequest(nsIRequest* aRequest, nsresult* aStatus)
407
0
{
408
0
  nsresult rv = aRequest->GetStatus(aStatus);
409
0
  if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
410
0
    return false;
411
0
  }
412
0
413
0
  // This may still be an error page or somesuch
414
0
  nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
415
0
  if (httpChan) {
416
0
    bool success;
417
0
    rv = httpChan->GetRequestSucceeded(&success);
418
0
    if (NS_FAILED(rv) || !success) {
419
0
      return false;
420
0
    }
421
0
  }
422
0
423
0
  // Otherwise, the request is successful
424
0
  return true;
425
0
}
426
427
static bool
428
CanHandleURI(nsIURI* aURI)
429
0
{
430
0
  nsAutoCString scheme;
431
0
  if (NS_FAILED(aURI->GetScheme(scheme))) {
432
0
    return false;
433
0
  }
434
0
435
0
  nsIIOService* ios = nsContentUtils::GetIOService();
436
0
  if (!ios)
437
0
    return false;
438
0
439
0
  nsCOMPtr<nsIProtocolHandler> handler;
440
0
  ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
441
0
  if (!handler) {
442
0
    return false;
443
0
  }
444
0
445
0
  nsCOMPtr<nsIExternalProtocolHandler> extHandler =
446
0
    do_QueryInterface(handler);
447
0
  // We can handle this URI if its protocol handler is not the external one
448
0
  return extHandler == nullptr;
449
0
}
450
451
// Helper for tedious URI equality syntax when one or both arguments may be
452
// null and URIEquals(null, null) should be true
453
static bool inline
454
URIEquals(nsIURI *a, nsIURI *b)
455
0
{
456
0
  bool equal;
457
0
  return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
458
0
}
459
460
static void
461
GetExtensionFromURI(nsIURI* uri, nsCString& ext)
462
0
{
463
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
464
0
  if (url) {
465
0
    url->GetFileExtension(ext);
466
0
  } else {
467
0
    nsCString spec;
468
0
    nsresult rv = uri->GetSpec(spec);
469
0
    if (NS_FAILED(rv)) {
470
0
      // This means we could incorrectly think a plugin is not enabled for
471
0
      // the URI when it is, but that's not so bad.
472
0
      ext.Truncate();
473
0
      return;
474
0
    }
475
0
476
0
    int32_t offset = spec.RFindChar('.');
477
0
    if (offset != kNotFound) {
478
0
      ext = Substring(spec, offset + 1, spec.Length());
479
0
    }
480
0
  }
481
0
}
482
483
/**
484
 * Checks whether a plugin exists and is enabled for the extension
485
 * in the given URI. The MIME type is returned in the mimeType out parameter.
486
 */
487
bool
488
IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
489
0
{
490
0
  nsAutoCString ext;
491
0
  GetExtensionFromURI(uri, ext);
492
0
493
0
  if (ext.IsEmpty()) {
494
0
    return false;
495
0
  }
496
0
497
0
  // Disables any native PDF plugins, when internal PDF viewer is enabled.
498
0
  if (ext.EqualsIgnoreCase("pdf") && nsContentUtils::IsPDFJSEnabled()) {
499
0
    return false;
500
0
  }
501
0
502
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
503
0
504
0
  if (!pluginHost) {
505
0
    MOZ_ASSERT_UNREACHABLE("No pluginhost");
506
0
    return false;
507
0
  }
508
0
509
0
  return pluginHost->HavePluginForExtension(ext, mimeType);
510
0
}
511
512
///
513
/// Member Functions
514
///
515
516
// Helper to queue a CheckPluginStopEvent for a OBJLC object
517
void
518
nsObjectLoadingContent::QueueCheckPluginStopEvent()
519
0
{
520
0
  nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
521
0
  mPendingCheckPluginStopEvent = event;
522
0
523
0
  NS_DispatchToCurrentThread(event);
524
0
}
525
526
// Tedious syntax to create a plugin stream listener with checks and put it in
527
// mFinalListener
528
bool
529
nsObjectLoadingContent::MakePluginListener()
530
0
{
531
0
  if (!mInstanceOwner) {
532
0
    MOZ_ASSERT_UNREACHABLE("expecting a spawned plugin");
533
0
    return false;
534
0
  }
535
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
536
0
  if (!pluginHost) {
537
0
    MOZ_ASSERT_UNREACHABLE("No pluginHost");
538
0
    return false;
539
0
  }
540
0
  NS_ASSERTION(!mFinalListener, "overwriting a final listener");
541
0
  nsresult rv;
542
0
  RefPtr<nsNPAPIPluginInstance> inst;
543
0
  nsCOMPtr<nsIStreamListener> finalListener;
544
0
  rv = mInstanceOwner->GetInstance(getter_AddRefs(inst));
545
0
  NS_ENSURE_SUCCESS(rv, false);
546
0
  rv = pluginHost->NewPluginStreamListener(mURI, inst,
547
0
                                           getter_AddRefs(finalListener));
548
0
  NS_ENSURE_SUCCESS(rv, false);
549
0
  mFinalListener = finalListener;
550
0
  return true;
551
0
}
552
553
// Helper to spawn the frameloader.
554
void
555
nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId)
556
0
{
557
0
  nsCOMPtr<nsIContent> thisContent =
558
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
559
0
  NS_ASSERTION(thisContent, "must be a content");
560
0
561
0
  mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
562
0
                                       /* aOpener = */ nullptr,
563
0
                                       mNetworkCreated, aJSPluginId);
564
0
  MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed");
565
0
}
566
567
// Helper to spawn the frameloader and return a pointer to its docshell.
568
already_AddRefed<nsIDocShell>
569
nsObjectLoadingContent::SetupDocShell(nsIURI* aRecursionCheckURI)
570
0
{
571
0
  SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
572
0
  if (!mFrameLoader) {
573
0
    return nullptr;
574
0
  }
575
0
576
0
  nsCOMPtr<nsIDocShell> docShell;
577
0
578
0
  if (aRecursionCheckURI) {
579
0
    nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
580
0
    if (NS_SUCCEEDED(rv)) {
581
0
      IgnoredErrorResult result;
582
0
      docShell = mFrameLoader->GetDocShell(result);
583
0
      if (result.Failed()) {
584
0
        MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
585
0
      }
586
0
    } else {
587
0
      LOG(("OBJLC [%p]: Aborting recursive load", this));
588
0
    }
589
0
  }
590
0
591
0
  if (!docShell) {
592
0
    mFrameLoader->Destroy();
593
0
    mFrameLoader = nullptr;
594
0
    return nullptr;
595
0
  }
596
0
597
0
  return docShell.forget();
598
0
}
599
600
nsresult
601
nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
602
                                   nsIContent* aParent,
603
                                   nsIContent* aBindingParent)
604
0
{
605
0
  nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent);
606
0
607
0
  if (aDocument) {
608
0
    aDocument->AddPlugin(this);
609
0
  }
610
0
  return NS_OK;
611
0
}
612
613
void
614
nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
615
0
{
616
0
  nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
617
0
618
0
  nsCOMPtr<Element> thisElement =
619
0
    do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
620
0
  MOZ_ASSERT(thisElement);
621
0
  nsIDocument* ownerDoc = thisElement->OwnerDoc();
622
0
  ownerDoc->RemovePlugin(this);
623
0
624
0
  /// XXX(johns): Do we want to somehow propogate the reparenting behavior to
625
0
  ///             FakePlugin types as well?
626
0
  if (mType == eType_Plugin && (mInstanceOwner || mInstantiating)) {
627
0
    // we'll let the plugin continue to run at least until we get back to
628
0
    // the event loop. If we get back to the event loop and the node
629
0
    // has still not been added back to the document then we tear down the
630
0
    // plugin
631
0
    QueueCheckPluginStopEvent();
632
0
  } else if (mType != eType_Image) {
633
0
    // nsImageLoadingContent handles the image case.
634
0
    // Reset state and clear pending events
635
0
    /// XXX(johns): The implementation for GenericFrame notes that ideally we
636
0
    ///             would keep the docshell around, but trash the frameloader
637
0
    UnloadObject();
638
0
  }
639
0
  if (mType == eType_Plugin) {
640
0
    nsIDocument* doc = thisElement->GetComposedDoc();
641
0
    if (doc && doc->IsActive()) {
642
0
      nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc,
643
0
                                                         NS_LITERAL_STRING("PluginRemoved"));
644
0
      NS_DispatchToCurrentThread(ev);
645
0
    }
646
0
  }
647
0
}
648
649
nsObjectLoadingContent::nsObjectLoadingContent()
650
  : mType(eType_Loading)
651
  , mFallbackType(eFallbackAlternate)
652
  , mRunID(0)
653
  , mHasRunID(false)
654
  , mChannelLoaded(false)
655
  , mInstantiating(false)
656
  , mNetworkCreated(true)
657
  , mActivated(false)
658
  , mContentBlockingEnabled(false)
659
  , mSkipFakePlugins(false)
660
  , mIsStopping(false)
661
  , mIsLoading(false)
662
  , mScriptRequested(false)
663
  , mRewrittenYoutubeEmbed(false)
664
  , mPreferFallback(false)
665
0
  , mPreferFallbackKnown(false) {}
666
667
nsObjectLoadingContent::~nsObjectLoadingContent()
668
0
{
669
0
  // Should have been unbound from the tree at this point, and
670
0
  // CheckPluginStopEvent keeps us alive
671
0
  if (mFrameLoader) {
672
0
    MOZ_ASSERT_UNREACHABLE("Should not be tearing down frame loaders at this point");
673
0
    mFrameLoader->Destroy();
674
0
  }
675
0
  if (mInstanceOwner || mInstantiating) {
676
0
    // This is especially bad as delayed stop will try to hold on to this
677
0
    // object...
678
0
    MOZ_ASSERT_UNREACHABLE("Should not be tearing down a plugin at this point!");
679
0
    StopPluginInstance();
680
0
  }
681
0
  DestroyImageLoadingContent();
682
0
}
683
684
nsresult
685
nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
686
0
{
687
0
  if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
688
0
      mInstantiating) {
689
0
    // If we hit this assertion it's probably because LoadObject re-entered :(
690
0
    //
691
0
    // XXX(johns): This hackiness will go away in bug 767635
692
0
    NS_ASSERTION(mIsLoading || !aIsLoading,
693
0
                 "aIsLoading should only be true inside LoadObject");
694
0
    return NS_OK;
695
0
  }
696
0
697
0
  mInstantiating = true;
698
0
  AutoSetInstantiatingToFalse autoInstantiating(this);
699
0
700
0
  nsCOMPtr<nsIContent> thisContent =
701
0
    do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
702
0
703
0
  nsCOMPtr<nsIDocument> doc = thisContent->GetComposedDoc();
704
0
  if (!doc || !InActiveDocument(thisContent)) {
705
0
    NS_ERROR("Shouldn't be calling "
706
0
             "InstantiatePluginInstance without an active document");
707
0
    return NS_ERROR_FAILURE;
708
0
  }
709
0
710
0
  // Instantiating an instance can result in script execution, which
711
0
  // can destroy this DOM object. Don't allow that for the scope
712
0
  // of this method.
713
0
  nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
714
0
715
0
  // Flush layout so that the frame is created if possible and the plugin is
716
0
  // initialized with the latest information.
717
0
  doc->FlushPendingNotifications(FlushType::Layout);
718
0
  // Flushing layout may have re-entered and loaded something underneath us
719
0
  NS_ENSURE_TRUE(mInstantiating, NS_OK);
720
0
721
0
  if (!thisContent->GetPrimaryFrame()) {
722
0
    LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this));
723
0
    return NS_OK;
724
0
  }
725
0
726
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
727
0
728
0
  if (!pluginHost) {
729
0
    MOZ_ASSERT_UNREACHABLE("No pluginhost");
730
0
    return NS_ERROR_FAILURE;
731
0
  }
732
0
733
0
  // If you add early return(s), be sure to balance this call to
734
0
  // appShell->SuspendNative() with additional call(s) to
735
0
  // appShell->ReturnNative().
736
0
  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
737
0
  if (appShell) {
738
0
    appShell->SuspendNative();
739
0
  }
740
0
741
0
  RefPtr<nsPluginInstanceOwner> newOwner;
742
0
  nsresult rv = pluginHost->InstantiatePluginInstance(mContentType,
743
0
                                                      mURI.get(), this,
744
0
                                                      getter_AddRefs(newOwner));
745
0
746
0
  // XXX(johns): We don't suspend native inside stopping plugins...
747
0
  if (appShell) {
748
0
    appShell->ResumeNative();
749
0
  }
750
0
751
0
  if (!mInstantiating || NS_FAILED(rv)) {
752
0
    LOG(("OBJLC [%p]: Plugin instantiation failed or re-entered, "
753
0
         "killing old instance", this));
754
0
    // XXX(johns): This needs to be de-duplicated with DoStopPlugin, but we
755
0
    //             don't want to touch the protochain or delayed stop.
756
0
    //             (Bug 767635)
757
0
    if (newOwner) {
758
0
      RefPtr<nsNPAPIPluginInstance> inst;
759
0
      newOwner->GetInstance(getter_AddRefs(inst));
760
0
      newOwner->SetFrame(nullptr);
761
0
      if (inst) {
762
0
        pluginHost->StopPluginInstance(inst);
763
0
      }
764
0
      newOwner->Destroy();
765
0
    }
766
0
    return NS_OK;
767
0
  }
768
0
769
0
  mInstanceOwner = newOwner;
770
0
771
0
  if (mInstanceOwner) {
772
0
    RefPtr<nsNPAPIPluginInstance> inst;
773
0
    rv = mInstanceOwner->GetInstance(getter_AddRefs(inst));
774
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
775
0
      return rv;
776
0
    }
777
0
778
0
    rv = inst->GetRunID(&mRunID);
779
0
    mHasRunID = NS_SUCCEEDED(rv);
780
0
  }
781
0
782
0
  // Ensure the frame did not change during instantiation re-entry (common).
783
0
  // HasNewFrame would not have mInstanceOwner yet, so the new frame would be
784
0
  // dangling. (Bug 854082)
785
0
  nsIFrame* frame = thisContent->GetPrimaryFrame();
786
0
  if (frame && mInstanceOwner) {
787
0
    mInstanceOwner->SetFrame(static_cast<nsPluginFrame*>(frame));
788
0
789
0
    // Bug 870216 - Adobe Reader renders with incorrect dimensions until it gets
790
0
    // a second SetWindow call. This is otherwise redundant.
791
0
    mInstanceOwner->CallSetWindow();
792
0
  }
793
0
794
0
  // Set up scripting interfaces.
795
0
  NotifyContentObjectWrapper();
796
0
797
0
  RefPtr<nsNPAPIPluginInstance> pluginInstance;
798
0
  GetPluginInstance(getter_AddRefs(pluginInstance));
799
0
  if (pluginInstance) {
800
0
    nsCOMPtr<nsIPluginTag> pluginTag;
801
0
    pluginHost->GetPluginTagForInstance(pluginInstance,
802
0
                                        getter_AddRefs(pluginTag));
803
0
804
0
805
0
    uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
806
0
    pluginTag->GetBlocklistState(&blockState);
807
0
    if (blockState == nsIBlocklistService::STATE_OUTDATED) {
808
0
      // Fire plugin outdated event if necessary
809
0
      LOG(("OBJLC [%p]: Dispatching plugin outdated event for content\n",
810
0
           this));
811
0
      nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
812
0
                                                   NS_LITERAL_STRING("PluginOutdated"));
813
0
      nsresult rv = NS_DispatchToCurrentThread(ev);
814
0
      if (NS_FAILED(rv)) {
815
0
        NS_WARNING("failed to dispatch nsSimplePluginEvent");
816
0
      }
817
0
    }
818
0
819
0
    // If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
820
0
    // or we did load with a channel but are re-instantiating, re-open the
821
0
    // channel. OpenChannel() performs security checks, and this plugin has
822
0
    // already passed content policy in LoadObject.
823
0
    if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
824
0
      NS_ASSERTION(!mChannel, "should not have an existing channel here");
825
0
      // We intentionally ignore errors here, leaving it up to the plugin to
826
0
      // deal with not having an initial stream.
827
0
      OpenChannel();
828
0
    }
829
0
  }
830
0
831
0
  nsCOMPtr<nsIRunnable> ev = \
832
0
    new nsSimplePluginEvent(thisContent,
833
0
                            doc,
834
0
                            NS_LITERAL_STRING("PluginInstantiated"));
835
0
  NS_DispatchToCurrentThread(ev);
836
0
837
#ifdef XP_MACOSX
838
  HTMLObjectElement::HandlePluginInstantiated(thisContent->AsElement());
839
#endif
840
841
0
  return NS_OK;
842
0
}
843
844
void
845
nsObjectLoadingContent::GetPluginAttributes(nsTArray<MozPluginParameter>& aAttributes)
846
0
{
847
0
  aAttributes = mCachedAttributes;
848
0
}
849
850
void
851
nsObjectLoadingContent::GetPluginParameters(nsTArray<MozPluginParameter>& aParameters)
852
0
{
853
0
  aParameters = mCachedParameters;
854
0
}
855
856
void
857
nsObjectLoadingContent::GetNestedParams(nsTArray<MozPluginParameter>& aParams)
858
0
{
859
0
  nsCOMPtr<Element> ourElement =
860
0
    do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
861
0
862
0
  nsCOMPtr<nsIHTMLCollection> allParams;
863
0
  NS_NAMED_LITERAL_STRING(xhtml_ns, "http://www.w3.org/1999/xhtml");
864
0
  ErrorResult rv;
865
0
  allParams = ourElement->GetElementsByTagNameNS(xhtml_ns,
866
0
                                                 NS_LITERAL_STRING("param"),
867
0
                                                 rv);
868
0
  if (rv.Failed()) {
869
0
    return;
870
0
  }
871
0
  MOZ_ASSERT(allParams);
872
0
873
0
  uint32_t numAllParams = allParams->Length();
874
0
  for (uint32_t i = 0; i < numAllParams; i++) {
875
0
    RefPtr<Element> element = allParams->Item(i);
876
0
877
0
    nsAutoString name;
878
0
    element->GetAttribute(NS_LITERAL_STRING("name"), name);
879
0
880
0
    if (name.IsEmpty())
881
0
      continue;
882
0
883
0
    nsCOMPtr<nsIContent> parent = element->GetParent();
884
0
    RefPtr<HTMLObjectElement> objectElement;
885
0
    while (!objectElement && parent) {
886
0
      objectElement = HTMLObjectElement::FromNode(parent);
887
0
      parent = parent->GetParent();
888
0
    }
889
0
890
0
    if (objectElement) {
891
0
      parent = objectElement;
892
0
    } else {
893
0
      continue;
894
0
    }
895
0
896
0
    if (parent == ourElement) {
897
0
      MozPluginParameter param;
898
0
      element->GetAttribute(NS_LITERAL_STRING("name"), param.mName);
899
0
      element->GetAttribute(NS_LITERAL_STRING("value"), param.mValue);
900
0
901
0
      param.mName.Trim(" \n\r\t\b", true, true, false);
902
0
      param.mValue.Trim(" \n\r\t\b", true, true, false);
903
0
904
0
      aParams.AppendElement(param);
905
0
    }
906
0
  }
907
0
}
908
909
nsresult
910
nsObjectLoadingContent::BuildParametersArray()
911
0
{
912
0
  if (mCachedAttributes.Length() || mCachedParameters.Length()) {
913
0
    MOZ_ASSERT(false, "Parameters array should be empty.");
914
0
    return NS_OK;
915
0
  }
916
0
917
0
  nsCOMPtr<Element> element =
918
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
919
0
920
0
  for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) {
921
0
    MozPluginParameter param;
922
0
    const nsAttrName* attrName = element->GetAttrNameAt(i);
923
0
    nsAtom* atom = attrName->LocalName();
924
0
    element->GetAttr(attrName->NamespaceID(), atom, param.mValue);
925
0
    atom->ToString(param.mName);
926
0
    mCachedAttributes.AppendElement(param);
927
0
  }
928
0
929
0
  nsAutoCString wmodeOverride;
930
0
  Preferences::GetCString("plugins.force.wmode", wmodeOverride);
931
0
932
0
  for (uint32_t i = 0; i < mCachedAttributes.Length(); i++) {
933
0
    if (!wmodeOverride.IsEmpty() && mCachedAttributes[i].mName.EqualsIgnoreCase("wmode")) {
934
0
      CopyASCIItoUTF16(wmodeOverride, mCachedAttributes[i].mValue);
935
0
      wmodeOverride.Truncate();
936
0
    }
937
0
  }
938
0
939
0
  if (!wmodeOverride.IsEmpty()) {
940
0
    MozPluginParameter param;
941
0
    param.mName = NS_LITERAL_STRING("wmode");
942
0
    CopyASCIItoUTF16(wmodeOverride, param.mValue);
943
0
    mCachedAttributes.AppendElement(param);
944
0
  }
945
0
946
0
  // Some plugins were never written to understand the "data" attribute of the OBJECT tag.
947
0
  // Real and WMP will not play unless they find a "src" attribute, see bug 152334.
948
0
  // Nav 4.x would simply replace the "data" with "src". Because some plugins correctly
949
0
  // look for "data", lets instead copy the "data" attribute and add another entry
950
0
  // to the bottom of the array if there isn't already a "src" specified.
951
0
  if (element->IsHTMLElement(nsGkAtoms::object) &&
952
0
      !element->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
953
0
    MozPluginParameter param;
954
0
    element->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue);
955
0
    if (!param.mValue.IsEmpty()) {
956
0
      param.mName = NS_LITERAL_STRING("SRC");
957
0
      mCachedAttributes.AppendElement(param);
958
0
    }
959
0
  }
960
0
961
0
  GetNestedParams(mCachedParameters);
962
0
963
0
  return NS_OK;
964
0
}
965
966
void
967
nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
968
0
{
969
0
  // XXX(johns): We cannot touch plugins or run arbitrary script from this call,
970
0
  //             as nsDocument is in a non-reentrant state.
971
0
972
0
  // If we have a plugin we want to queue an event to stop it unless we are
973
0
  // moved into an active document before returning to the event loop.
974
0
  if (mInstanceOwner || mInstantiating) {
975
0
    QueueCheckPluginStopEvent();
976
0
  }
977
0
}
978
979
// nsIRequestObserver
980
NS_IMETHODIMP
981
nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
982
                                       nsISupports *aContext)
983
0
{
984
0
  AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
985
0
986
0
  LOG(("OBJLC [%p]: Channel OnStartRequest", this));
987
0
988
0
  if (aRequest != mChannel || !aRequest) {
989
0
    // happens when a new load starts before the previous one got here
990
0
    return NS_BINDING_ABORTED;
991
0
  }
992
0
993
0
  // If we already switched to type plugin, this channel can just be passed to
994
0
  // the final listener.
995
0
  if (mType == eType_Plugin) {
996
0
    if (!mInstanceOwner) {
997
0
      // We drop mChannel when stopping plugins, so something is wrong
998
0
      MOZ_ASSERT_UNREACHABLE("Opened a channel in plugin mode, but don't have "
999
0
                             "a plugin");
1000
0
      return NS_BINDING_ABORTED;
1001
0
    }
1002
0
    if (MakePluginListener()) {
1003
0
      return mFinalListener->OnStartRequest(aRequest, nullptr);
1004
0
    }
1005
0
    MOZ_ASSERT_UNREACHABLE("Failed to create PluginStreamListener, aborting "
1006
0
                           "channel");
1007
0
    return NS_BINDING_ABORTED;
1008
0
  }
1009
0
1010
0
  // Otherwise we should be state loading, and call LoadObject with the channel
1011
0
  if (mType != eType_Loading) {
1012
0
    MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
1013
0
    return NS_BINDING_ABORTED;
1014
0
  }
1015
0
  NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
1016
0
  NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
1017
0
1018
0
  mChannelLoaded = true;
1019
0
1020
0
  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1021
0
  NS_ASSERTION(chan, "Why is our request not a channel?");
1022
0
1023
0
  nsresult status = NS_OK;
1024
0
  bool success = IsSuccessfulRequest(aRequest, &status);
1025
0
1026
0
  if (status == NS_ERROR_BLOCKED_URI) {
1027
0
    nsCOMPtr<nsIConsoleService> console(
1028
0
      do_GetService("@mozilla.org/consoleservice;1"));
1029
0
    if (console) {
1030
0
      nsCOMPtr<nsIURI> uri;
1031
0
      chan->GetURI(getter_AddRefs(uri));
1032
0
      nsString message = NS_LITERAL_STRING("Blocking ") +
1033
0
        NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
1034
0
        NS_LITERAL_STRING(" since it was found on an internal Firefox blocklist.");
1035
0
      console->LogStringMessage(message.get());
1036
0
    }
1037
0
    mContentBlockingEnabled = true;
1038
0
    return NS_ERROR_FAILURE;
1039
0
  }
1040
0
1041
0
  if (status == NS_ERROR_TRACKING_URI) {
1042
0
    mContentBlockingEnabled = true;
1043
0
    return NS_ERROR_FAILURE;
1044
0
  }
1045
0
1046
0
  if (!success) {
1047
0
    LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
1048
0
    // If the request fails, we still call LoadObject() to handle fallback
1049
0
    // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
1050
0
    // the bad state.
1051
0
    mChannel = nullptr;
1052
0
    LoadObject(true, false);
1053
0
    return NS_ERROR_FAILURE;
1054
0
  }
1055
0
1056
0
  return LoadObject(true, false, aRequest);
1057
0
}
1058
1059
NS_IMETHODIMP
1060
nsObjectLoadingContent::OnStopRequest(nsIRequest *aRequest,
1061
                                      nsISupports *aContext,
1062
                                      nsresult aStatusCode)
1063
0
{
1064
0
  AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
1065
0
1066
0
  // Handle object not loading error because source was a tracking URL.
1067
0
  // We make a note of this object node by including it in a dedicated
1068
0
  // array of blocked tracking nodes under its parent document.
1069
0
  if (aStatusCode == NS_ERROR_TRACKING_URI) {
1070
0
    nsCOMPtr<nsIContent> thisNode =
1071
0
      do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
1072
0
    if (thisNode && thisNode->IsInComposedDoc()) {
1073
0
      thisNode->GetComposedDoc()->AddBlockedTrackingNode(thisNode);
1074
0
    }
1075
0
  }
1076
0
1077
0
  if (aRequest != mChannel) {
1078
0
    return NS_BINDING_ABORTED;
1079
0
  }
1080
0
1081
0
  mChannel = nullptr;
1082
0
1083
0
  if (mFinalListener) {
1084
0
    // This may re-enter in the case of plugin listeners
1085
0
    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1086
0
    mFinalListener = nullptr;
1087
0
    listenerGrip->OnStopRequest(aRequest, aContext, aStatusCode);
1088
0
  }
1089
0
1090
0
  // Return value doesn't matter
1091
0
  return NS_OK;
1092
0
}
1093
1094
1095
// nsIStreamListener
1096
NS_IMETHODIMP
1097
nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
1098
                                        nsISupports *aContext,
1099
                                        nsIInputStream *aInputStream,
1100
                                        uint64_t aOffset, uint32_t aCount)
1101
0
{
1102
0
  if (aRequest != mChannel) {
1103
0
    return NS_BINDING_ABORTED;
1104
0
  }
1105
0
1106
0
  if (mFinalListener) {
1107
0
    // This may re-enter in the case of plugin listeners
1108
0
    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1109
0
    return listenerGrip->OnDataAvailable(aRequest, aContext, aInputStream,
1110
0
                                         aOffset, aCount);
1111
0
  }
1112
0
1113
0
  // We shouldn't have a connected channel with no final listener
1114
0
  MOZ_ASSERT_UNREACHABLE("Got data for channel with no connected final "
1115
0
                         "listener");
1116
0
  mChannel = nullptr;
1117
0
1118
0
  return NS_ERROR_UNEXPECTED;
1119
0
}
1120
1121
// nsIFrameLoaderOwner
1122
NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
1123
nsObjectLoadingContent::GetFrameLoader()
1124
0
{
1125
0
  RefPtr<nsFrameLoader> loader = mFrameLoader;
1126
0
  return loader.forget();
1127
0
}
1128
1129
void
1130
nsObjectLoadingContent::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, mozilla::ErrorResult& aRv)
1131
0
{
1132
0
  aRv.Throw(NS_ERROR_FAILURE);
1133
0
}
1134
1135
void
1136
nsObjectLoadingContent::InternalSetFrameLoader(nsFrameLoader* aNewFrameLoader)
1137
0
{
1138
0
  MOZ_CRASH("You shouldn't be calling this function, it doesn't make any sense on this type.");
1139
0
}
1140
1141
NS_IMETHODIMP
1142
nsObjectLoadingContent::GetActualType(nsACString& aType)
1143
0
{
1144
0
  aType = mContentType;
1145
0
  return NS_OK;
1146
0
}
1147
1148
NS_IMETHODIMP
1149
nsObjectLoadingContent::GetDisplayedType(uint32_t* aType)
1150
0
{
1151
0
  *aType = DisplayedType();
1152
0
  return NS_OK;
1153
0
}
1154
1155
NS_IMETHODIMP
1156
nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
1157
0
{
1158
0
  if (mType != eType_Plugin) {
1159
0
    return NS_OK;
1160
0
  }
1161
0
1162
0
  if (!aFrame) {
1163
0
    // Lost our frame. If we aren't going to be getting a new frame, e.g. we've
1164
0
    // become display:none, we'll want to stop the plugin. Queue a
1165
0
    // CheckPluginStopEvent
1166
0
    if (mInstanceOwner || mInstantiating) {
1167
0
      if (mInstanceOwner) {
1168
0
        mInstanceOwner->SetFrame(nullptr);
1169
0
      }
1170
0
      QueueCheckPluginStopEvent();
1171
0
    }
1172
0
    return NS_OK;
1173
0
  }
1174
0
1175
0
  // Have a new frame
1176
0
1177
0
  if (!mInstanceOwner) {
1178
0
    // We are successfully setup as type plugin, but have not spawned an
1179
0
    // instance due to a lack of a frame.
1180
0
    AsyncStartPluginInstance();
1181
0
    return NS_OK;
1182
0
  }
1183
0
1184
0
  // Otherwise, we're just changing frames
1185
0
  // Set up relationship between instance owner and frame.
1186
0
  nsPluginFrame *objFrame = static_cast<nsPluginFrame*>(aFrame);
1187
0
  mInstanceOwner->SetFrame(objFrame);
1188
0
1189
0
  return NS_OK;
1190
0
}
1191
1192
NS_IMETHODIMP
1193
nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance)
1194
0
{
1195
0
  *aInstance = nullptr;
1196
0
1197
0
  if (!mInstanceOwner) {
1198
0
    return NS_OK;
1199
0
  }
1200
0
1201
0
  return mInstanceOwner->GetInstance(aInstance);
1202
0
}
1203
1204
NS_IMETHODIMP
1205
nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
1206
                                                  uint32_t* aType)
1207
0
{
1208
0
  *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType), false);
1209
0
  return NS_OK;
1210
0
}
1211
1212
NS_IMETHODIMP
1213
nsObjectLoadingContent::GetBaseURI(nsIURI **aResult)
1214
0
{
1215
0
  NS_IF_ADDREF(*aResult = mBaseURI);
1216
0
  return NS_OK;
1217
0
}
1218
1219
// nsIInterfaceRequestor
1220
// We use a shim class to implement this so that JS consumers still
1221
// see an interface requestor even though WebIDL bindings don't expose
1222
// that stuff.
1223
class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
1224
                                           public nsIChannelEventSink,
1225
                                           public nsIStreamListener
1226
{
1227
public:
1228
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1229
  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
1230
                                           nsIInterfaceRequestor)
1231
  NS_DECL_NSIINTERFACEREQUESTOR
1232
  // RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
1233
  // hence the ugly static cast :(
1234
  NS_FORWARD_NSICHANNELEVENTSINK(static_cast<nsObjectLoadingContent *>
1235
                                 (mContent.get())->)
1236
  NS_FORWARD_NSISTREAMLISTENER  (static_cast<nsObjectLoadingContent *>
1237
                                 (mContent.get())->)
1238
  NS_FORWARD_NSIREQUESTOBSERVER (static_cast<nsObjectLoadingContent *>
1239
                                 (mContent.get())->)
1240
1241
  explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
1242
    : mContent(aContent)
1243
0
  {}
1244
1245
protected:
1246
0
  ~ObjectInterfaceRequestorShim() = default;
1247
  nsCOMPtr<nsIObjectLoadingContent> mContent;
1248
};
1249
1250
NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
1251
1252
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
1253
0
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1254
0
  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
1255
0
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
1256
0
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
1257
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
1258
0
NS_INTERFACE_MAP_END
1259
1260
NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
1261
NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
1262
1263
NS_IMETHODIMP
1264
ObjectInterfaceRequestorShim::GetInterface(const nsIID & aIID, void **aResult)
1265
0
{
1266
0
  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1267
0
    nsIChannelEventSink* sink = this;
1268
0
    *aResult = sink;
1269
0
    NS_ADDREF(sink);
1270
0
    return NS_OK;
1271
0
  }
1272
0
  return NS_NOINTERFACE;
1273
0
}
1274
1275
// nsIChannelEventSink
1276
NS_IMETHODIMP
1277
nsObjectLoadingContent::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1278
                                               nsIChannel *aNewChannel,
1279
                                               uint32_t aFlags,
1280
                                               nsIAsyncVerifyRedirectCallback *cb)
1281
0
{
1282
0
  // If we're already busy with a new load, or have no load at all,
1283
0
  // cancel the redirect.
1284
0
  if (!mChannel || aOldChannel != mChannel) {
1285
0
    return NS_BINDING_ABORTED;
1286
0
  }
1287
0
1288
0
  mChannel = aNewChannel;
1289
0
  cb->OnRedirectVerifyCallback(NS_OK);
1290
0
  return NS_OK;
1291
0
}
1292
1293
// <public>
1294
EventStates
1295
nsObjectLoadingContent::ObjectState() const
1296
0
{
1297
0
  switch (mType) {
1298
0
    case eType_Loading:
1299
0
      return NS_EVENT_STATE_LOADING;
1300
0
    case eType_Image:
1301
0
      return ImageState();
1302
0
    case eType_Plugin:
1303
0
    case eType_FakePlugin:
1304
0
    case eType_Document:
1305
0
      // These are OK. If documents start to load successfully, they display
1306
0
      // something, and are thus not broken in this sense. The same goes for
1307
0
      // plugins.
1308
0
      return EventStates();
1309
0
    case eType_Null:
1310
0
      switch (mFallbackType) {
1311
0
        case eFallbackSuppressed:
1312
0
          return NS_EVENT_STATE_SUPPRESSED;
1313
0
        case eFallbackUserDisabled:
1314
0
          return NS_EVENT_STATE_USERDISABLED;
1315
0
        case eFallbackClickToPlay:
1316
0
        case eFallbackClickToPlayQuiet:
1317
0
          return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
1318
0
        case eFallbackDisabled:
1319
0
          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED;
1320
0
        case eFallbackBlocklisted:
1321
0
          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED;
1322
0
        case eFallbackCrashed:
1323
0
          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED;
1324
0
        case eFallbackUnsupported:
1325
0
        case eFallbackOutdated:
1326
0
        case eFallbackAlternate:
1327
0
          return NS_EVENT_STATE_BROKEN;
1328
0
        case eFallbackVulnerableUpdatable:
1329
0
          return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
1330
0
        case eFallbackVulnerableNoUpdate:
1331
0
          return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
1332
0
      }
1333
0
  }
1334
0
  MOZ_ASSERT_UNREACHABLE("unknown type?");
1335
0
  return NS_EVENT_STATE_LOADING;
1336
0
}
1337
1338
void
1339
nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI, nsIURI* aBaseURI, nsIURI** aOutURI)
1340
0
{
1341
0
  nsCOMPtr<nsIContent> thisContent =
1342
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1343
0
  NS_ASSERTION(thisContent, "Must be an instance of content");
1344
0
1345
0
  // We're only interested in switching out embed and object tags
1346
0
  if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed) &&
1347
0
      !thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
1348
0
    return;
1349
0
  }
1350
0
1351
0
  nsCOMPtr<nsIEffectiveTLDService> tldService =
1352
0
    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
1353
0
  // If we can't analyze the URL, just pass on through.
1354
0
  if(!tldService) {
1355
0
    NS_WARNING("Could not get TLD service!");
1356
0
    return;
1357
0
  }
1358
0
1359
0
  nsAutoCString currentBaseDomain;
1360
0
  bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
1361
0
  if (!ok) {
1362
0
    // Data URIs (commonly used for things like svg embeds) won't parse
1363
0
    // correctly, so just fail silently here.
1364
0
    return;
1365
0
  }
1366
0
1367
0
  // See if URL is referencing youtube
1368
0
  if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
1369
0
      !currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
1370
0
    return;
1371
0
  }
1372
0
1373
0
  // We should only rewrite URLs with paths starting with "/v/", as we shouldn't
1374
0
  // touch object nodes with "/embed/" urls that already do that right thing.
1375
0
  nsAutoCString path;
1376
0
  aURI->GetPathQueryRef(path);
1377
0
  if (!StringBeginsWith(path, NS_LITERAL_CSTRING("/v/"))) {
1378
0
    return;
1379
0
  }
1380
0
1381
0
  // See if requester is planning on using the JS API.
1382
0
  nsAutoCString uri;
1383
0
  nsresult rv = aURI->GetSpec(uri);
1384
0
  if (NS_FAILED(rv)) {
1385
0
    return;
1386
0
  }
1387
0
1388
0
  // Some YouTube urls have parameters in path components, e.g.
1389
0
  // http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
1390
0
  // but break iframe/object embedding. If this situation occurs with rewritten
1391
0
  // URLs, convert the parameters to query in order to make the video load
1392
0
  // correctly as an iframe. In either case, warn about it in the
1393
0
  // developer console.
1394
0
  int32_t ampIndex = uri.FindChar('&', 0);
1395
0
  bool replaceQuery = false;
1396
0
  if (ampIndex != -1) {
1397
0
    int32_t qmIndex = uri.FindChar('?', 0);
1398
0
    if (qmIndex == -1 ||
1399
0
        qmIndex > ampIndex) {
1400
0
      replaceQuery = true;
1401
0
    }
1402
0
  }
1403
0
1404
0
  // If we're pref'd off, return after telemetry has been logged.
1405
0
  if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
1406
0
    return;
1407
0
  }
1408
0
1409
0
  nsAutoString utf16OldURI = NS_ConvertUTF8toUTF16(uri);
1410
0
  // If we need to convert the URL, it means an ampersand comes first.
1411
0
  // Use the index we found earlier.
1412
0
  if (replaceQuery) {
1413
0
    // Replace question marks with ampersands.
1414
0
    uri.ReplaceChar('?', '&');
1415
0
    // Replace the first ampersand with a question mark.
1416
0
    uri.SetCharAt('?', ampIndex);
1417
0
  }
1418
0
  // Switch out video access url formats, which should possibly allow HTML5
1419
0
  // video loading.
1420
0
  uri.ReplaceSubstring(NS_LITERAL_CSTRING("/v/"),
1421
0
                       NS_LITERAL_CSTRING("/embed/"));
1422
0
  nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
1423
0
  rv = nsContentUtils::NewURIWithDocumentCharset(aOutURI,
1424
0
                                                 utf16URI,
1425
0
                                                 thisContent->OwnerDoc(),
1426
0
                                                 aBaseURI);
1427
0
  if (NS_FAILED(rv)) {
1428
0
    return;
1429
0
  }
1430
0
  const char16_t* params[] = { utf16OldURI.get(), utf16URI.get() };
1431
0
  const char* msgName;
1432
0
  // If there's no query to rewrite, just notify in the developer console
1433
0
  // that we're changing the embed.
1434
0
  if (!replaceQuery) {
1435
0
    msgName = "RewriteYouTubeEmbed";
1436
0
  } else {
1437
0
    msgName = "RewriteYouTubeEmbedPathParams";
1438
0
  }
1439
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1440
0
                                  NS_LITERAL_CSTRING("Plugins"),
1441
0
                                  thisContent->OwnerDoc(),
1442
0
                                  nsContentUtils::eDOM_PROPERTIES,
1443
0
                                  msgName,
1444
0
                                  params, ArrayLength(params));
1445
0
}
1446
1447
bool
1448
nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy)
1449
0
{
1450
0
  if (!aContentPolicy || !mURI) {
1451
0
    MOZ_ASSERT_UNREACHABLE("Doing it wrong");
1452
0
    return false;
1453
0
  }
1454
0
1455
0
  nsCOMPtr<nsIContent> thisContent =
1456
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1457
0
  NS_ASSERTION(thisContent, "Must be an instance of content");
1458
0
1459
0
  nsIDocument* doc = thisContent->OwnerDoc();
1460
0
1461
0
  nsContentPolicyType contentPolicyType = GetContentPolicyType();
1462
0
1463
0
  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
1464
0
    new LoadInfo(doc->NodePrincipal(), // loading principal
1465
0
                 doc->NodePrincipal(), // triggering principal
1466
0
                 thisContent,
1467
0
                 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1468
0
                 contentPolicyType);
1469
0
1470
0
  *aContentPolicy = nsIContentPolicy::ACCEPT;
1471
0
  nsresult rv = NS_CheckContentLoadPolicy(mURI,
1472
0
                                          secCheckLoadInfo,
1473
0
                                          mContentType,
1474
0
                                          aContentPolicy,
1475
0
                                          nsContentUtils::GetContentPolicy());
1476
0
  NS_ENSURE_SUCCESS(rv, false);
1477
0
  if (NS_CP_REJECTED(*aContentPolicy)) {
1478
0
    LOG(("OBJLC [%p]: Content policy denied load of %s",
1479
0
         this, mURI->GetSpecOrDefault().get()));
1480
0
    return false;
1481
0
  }
1482
0
1483
0
  return true;
1484
0
}
1485
1486
bool
1487
nsObjectLoadingContent::CheckProcessPolicy(int16_t *aContentPolicy)
1488
0
{
1489
0
  if (!aContentPolicy) {
1490
0
    MOZ_ASSERT_UNREACHABLE("Null out variable");
1491
0
    return false;
1492
0
  }
1493
0
1494
0
  nsCOMPtr<nsIContent> thisContent =
1495
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1496
0
  NS_ASSERTION(thisContent, "Must be an instance of content");
1497
0
1498
0
  nsIDocument* doc = thisContent->OwnerDoc();
1499
0
1500
0
  int32_t objectType;
1501
0
  switch (mType) {
1502
0
    case eType_Image:
1503
0
      objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
1504
0
      break;
1505
0
    case eType_Document:
1506
0
      objectType = nsIContentPolicy::TYPE_DOCUMENT;
1507
0
      break;
1508
0
    // FIXME Fake plugins look just like real plugins to CSP, should they use
1509
0
    // the fake plugin's handler URI and look like documents instead?
1510
0
    case eType_FakePlugin:
1511
0
    case eType_Plugin:
1512
0
      objectType = GetContentPolicyType();
1513
0
      break;
1514
0
    default:
1515
0
      MOZ_ASSERT_UNREACHABLE("Calling checkProcessPolicy with an unloadable "
1516
0
                             "type");
1517
0
      return false;
1518
0
  }
1519
0
1520
0
  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
1521
0
    new LoadInfo(doc->NodePrincipal(), // loading principal
1522
0
                 doc->NodePrincipal(), // triggering principal
1523
0
                 thisContent,
1524
0
                 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1525
0
                 objectType);
1526
0
1527
0
  *aContentPolicy = nsIContentPolicy::ACCEPT;
1528
0
  nsresult rv =
1529
0
    NS_CheckContentProcessPolicy(mURI ? mURI : mBaseURI,
1530
0
                                 secCheckLoadInfo,
1531
0
                                 mContentType,
1532
0
                                 aContentPolicy,
1533
0
                                 nsContentUtils::GetContentPolicy());
1534
0
  NS_ENSURE_SUCCESS(rv, false);
1535
0
1536
0
  if (NS_CP_REJECTED(*aContentPolicy)) {
1537
0
    LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
1538
0
    return false;
1539
0
  }
1540
0
1541
0
  return true;
1542
0
}
1543
1544
nsObjectLoadingContent::ParameterUpdateFlags
1545
nsObjectLoadingContent::UpdateObjectParameters()
1546
0
{
1547
0
  nsCOMPtr<Element> thisElement =
1548
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1549
0
  MOZ_ASSERT(thisElement, "Must be an Element");
1550
0
1551
0
  uint32_t caps = GetCapabilities();
1552
0
  LOG(("OBJLC [%p]: Updating object parameters", this));
1553
0
1554
0
  nsresult rv;
1555
0
  nsAutoCString newMime;
1556
0
  nsAutoString typeAttr;
1557
0
  nsCOMPtr<nsIURI> newURI;
1558
0
  nsCOMPtr<nsIURI> newBaseURI;
1559
0
  ObjectType newType;
1560
0
  // Set if this state can't be used to load anything, forces eType_Null
1561
0
  bool stateInvalid = false;
1562
0
  // Indicates what parameters changed.
1563
0
  // eParamChannelChanged - means parameters that affect channel opening
1564
0
  //                        decisions changed
1565
0
  // eParamStateChanged -   means anything that affects what content we load
1566
0
  //                        changed, even if the channel we'd open remains the
1567
0
  //                        same.
1568
0
  //
1569
0
  // State changes outside of the channel parameters only matter if we've
1570
0
  // already opened a channel or tried to instantiate content, whereas channel
1571
0
  // parameter changes require re-opening the channel even if we haven't gotten
1572
0
  // that far.
1573
0
  nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
1574
0
1575
0
  ///
1576
0
  /// Initial MIME Type
1577
0
  ///
1578
0
1579
0
1580
0
  if (caps & eFallbackIfClassIDPresent) {
1581
0
    nsAutoString classIDAttr;
1582
0
    thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
1583
0
    // We don't support class ID plugin references, so we should always treat
1584
0
    // having class Ids as attributes as invalid, and fallback accordingly.
1585
0
    if (!classIDAttr.IsEmpty()) {
1586
0
      newMime.Truncate();
1587
0
      stateInvalid = true;
1588
0
    }
1589
0
  }
1590
0
1591
0
  ///
1592
0
  /// Codebase
1593
0
  ///
1594
0
1595
0
  nsAutoString codebaseStr;
1596
0
  nsCOMPtr<nsIURI> docBaseURI = thisElement->GetBaseURI();
1597
0
  thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr);
1598
0
1599
0
  if (!codebaseStr.IsEmpty()) {
1600
0
    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newBaseURI),
1601
0
                                                   codebaseStr,
1602
0
                                                   thisElement->OwnerDoc(),
1603
0
                                                   docBaseURI);
1604
0
    if (NS_FAILED(rv)) {
1605
0
      // Malformed URI
1606
0
      LOG(("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
1607
0
           "will use document baseURI instead", this));
1608
0
    }
1609
0
  }
1610
0
1611
0
  nsAutoString rawTypeAttr;
1612
0
  thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, rawTypeAttr);
1613
0
  if (!rawTypeAttr.IsEmpty()) {
1614
0
    typeAttr = rawTypeAttr;
1615
0
    CopyUTF16toUTF8(rawTypeAttr, newMime);
1616
0
  }
1617
0
1618
0
  // If we failed to build a valid URI, use the document's base URI
1619
0
  if (!newBaseURI) {
1620
0
    newBaseURI = docBaseURI;
1621
0
  }
1622
0
1623
0
  ///
1624
0
  /// URI
1625
0
  ///
1626
0
1627
0
  nsAutoString uriStr;
1628
0
  // Different elements keep this in various locations
1629
0
  if (thisElement->NodeInfo()->Equals(nsGkAtoms::object)) {
1630
0
    thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr);
1631
0
  } else if (thisElement->NodeInfo()->Equals(nsGkAtoms::embed)) {
1632
0
    thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr);
1633
0
  } else {
1634
0
    MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
1635
0
  }
1636
0
1637
0
  mRewrittenYoutubeEmbed = false;
1638
0
  // Note that the baseURI changing could affect the newURI, even if uriStr did
1639
0
  // not change.
1640
0
  if (!uriStr.IsEmpty()) {
1641
0
    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newURI),
1642
0
                                                   uriStr,
1643
0
                                                   thisElement->OwnerDoc(),
1644
0
                                                   newBaseURI);
1645
0
    nsCOMPtr<nsIURI> rewrittenURI;
1646
0
    MaybeRewriteYoutubeEmbed(newURI,
1647
0
                             newBaseURI,
1648
0
                             getter_AddRefs(rewrittenURI));
1649
0
    if (rewrittenURI) {
1650
0
      newURI = rewrittenURI;
1651
0
      mRewrittenYoutubeEmbed = true;
1652
0
      newMime = NS_LITERAL_CSTRING("text/html");
1653
0
    }
1654
0
1655
0
    if (NS_FAILED(rv)) {
1656
0
      stateInvalid = true;
1657
0
    }
1658
0
  }
1659
0
1660
0
  // For eAllowPluginSkipChannel tags, if we have a non-plugin type, but can get
1661
0
  // a plugin type from the extension, prefer that to falling back to a channel.
1662
0
  if (!IsPluginType(GetTypeOfContent(newMime, mSkipFakePlugins)) && newURI &&
1663
0
      (caps & eAllowPluginSkipChannel) &&
1664
0
      IsPluginEnabledByExtension(newURI, newMime)) {
1665
0
    LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get()));
1666
0
  }
1667
0
1668
0
  ///
1669
0
  /// Check if the original (pre-channel) content-type or URI changed, and
1670
0
  /// record mOriginal{ContentType,URI}
1671
0
  ///
1672
0
1673
0
  if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
1674
0
    // These parameters changing requires re-opening the channel, so don't
1675
0
    // consider the currently-open channel below
1676
0
    // XXX(johns): Changing the mime type might change our decision on whether
1677
0
    //             or not we load a channel, so we count changes to it as a
1678
0
    //             channel parameter change for the sake of simplicity.
1679
0
    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1680
0
    LOG(("OBJLC [%p]: Channel parameters changed", this));
1681
0
  }
1682
0
  mOriginalContentType = newMime;
1683
0
  mOriginalURI = newURI;
1684
0
1685
0
  ///
1686
0
  /// If we have a channel, see if its MIME type should take precendence and
1687
0
  /// check the final (redirected) URL
1688
0
  ///
1689
0
1690
0
  // If we have a loaded channel and channel parameters did not change, use it
1691
0
  // to determine what we would load.
1692
0
  bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
1693
0
  // If we have a channel and are type loading, as opposed to having an existing
1694
0
  // channel for a previous load.
1695
0
  bool newChannel = useChannel && mType == eType_Loading;
1696
0
1697
0
  if (newChannel && mChannel) {
1698
0
    nsCString channelType;
1699
0
    rv = mChannel->GetContentType(channelType);
1700
0
    if (NS_FAILED(rv)) {
1701
0
      MOZ_ASSERT_UNREACHABLE("GetContentType failed");
1702
0
      stateInvalid = true;
1703
0
      channelType.Truncate();
1704
0
    }
1705
0
1706
0
    LOG(("OBJLC [%p]: Channel has a content type of %s", this, channelType.get()));
1707
0
1708
0
    bool binaryChannelType = false;
1709
0
    if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
1710
0
      channelType = APPLICATION_OCTET_STREAM;
1711
0
      mChannel->SetContentType(channelType);
1712
0
      binaryChannelType = true;
1713
0
    } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM)
1714
0
               || channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
1715
0
      binaryChannelType = true;
1716
0
    }
1717
0
1718
0
    // Channel can change our URI through redirection
1719
0
    rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
1720
0
    if (NS_FAILED(rv)) {
1721
0
      MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
1722
0
      stateInvalid = true;
1723
0
    }
1724
0
1725
0
    ObjectType typeHint = newMime.IsEmpty() ? eType_Null
1726
0
                          : GetTypeOfContent(newMime, mSkipFakePlugins);
1727
0
1728
0
    //
1729
0
    // In order of preference:
1730
0
    //
1731
0
    // 1) Perform typemustmatch check.
1732
0
    //    If check is sucessful use type without further checks.
1733
0
    //    If check is unsuccessful set stateInvalid to true
1734
0
    // 2) Use our type hint if it matches a plugin
1735
0
    // 3) If we have eAllowPluginSkipChannel, use the uri file extension if
1736
0
    //    it matches a plugin
1737
0
    // 4) If the channel returns a binary stream type:
1738
0
    //    4a) If we have a type non-null non-document type hint, use that
1739
0
    //    4b) If the uri file extension matches a plugin type, use that
1740
0
    // 5) Use the channel type
1741
0
1742
0
    bool overrideChannelType = false;
1743
0
    if (thisElement->HasAttr(kNameSpaceID_None, nsGkAtoms::typemustmatch)) {
1744
0
      if (!typeAttr.LowerCaseEqualsASCII(channelType.get())) {
1745
0
        stateInvalid = true;
1746
0
      }
1747
0
    } else if (IsPluginType(typeHint)) {
1748
0
      LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
1749
0
           this));
1750
0
      overrideChannelType = true;
1751
0
    } else if ((caps & eAllowPluginSkipChannel) &&
1752
0
               IsPluginEnabledByExtension(newURI, newMime)) {
1753
0
      LOG(("OBJLC [%p]: Using extension as type hint for "
1754
0
           "eAllowPluginSkipChannel tag (%s)", this, newMime.get()));
1755
0
      overrideChannelType = true;
1756
0
    } else if (binaryChannelType &&
1757
0
               typeHint != eType_Null && typeHint != eType_Document) {
1758
0
      LOG(("OBJLC [%p]: Using type hint in favor of binary channel type",
1759
0
           this));
1760
0
      overrideChannelType = true;
1761
0
    } else if (binaryChannelType &&
1762
0
               IsPluginEnabledByExtension(newURI, newMime)) {
1763
0
      LOG(("OBJLC [%p]: Using extension as type hint for binary channel (%s)",
1764
0
           this, newMime.get()));
1765
0
      overrideChannelType = true;
1766
0
    }
1767
0
1768
0
    if (overrideChannelType) {
1769
0
      // Set the type we'll use for dispatch on the channel.  Otherwise we could
1770
0
      // end up trying to dispatch to a nsFrameLoader, which will complain that
1771
0
      // it couldn't find a way to handle application/octet-stream
1772
0
      nsAutoCString parsedMime, dummy;
1773
0
      NS_ParseResponseContentType(newMime, parsedMime, dummy);
1774
0
      if (!parsedMime.IsEmpty()) {
1775
0
        mChannel->SetContentType(parsedMime);
1776
0
      }
1777
0
    } else {
1778
0
      newMime = channelType;
1779
0
    }
1780
0
  } else if (newChannel) {
1781
0
    LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
1782
0
    stateInvalid = true;
1783
0
  }
1784
0
1785
0
  ///
1786
0
  /// Determine final type
1787
0
  ///
1788
0
  // In order of preference:
1789
0
  //  1) If we have attempted channel load, or set stateInvalid above, the type
1790
0
  //     is always null (fallback)
1791
0
  //  2) If we have a loaded channel, we grabbed its mimeType above, use that
1792
0
  //     type.
1793
0
  //  3) If we have a plugin type and no URI, use that type.
1794
0
  //  4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
1795
0
  //  5) if we have a URI, set type to loading to indicate we'd need a channel
1796
0
  //     to proceed.
1797
0
  //  6) Otherwise, type null to indicate unloadable content (fallback)
1798
0
  //
1799
0
1800
0
  ObjectType newMime_Type = GetTypeOfContent(newMime, mSkipFakePlugins);
1801
0
1802
0
  if (stateInvalid) {
1803
0
    newType = eType_Null;
1804
0
    newMime.Truncate();
1805
0
  } else if (newChannel) {
1806
0
    // If newChannel is set above, we considered it in setting newMime
1807
0
    newType = newMime_Type;
1808
0
    LOG(("OBJLC [%p]: Using channel type", this));
1809
0
  } else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
1810
0
             IsPluginType(newMime_Type)) {
1811
0
    newType = newMime_Type;
1812
0
    LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
1813
0
  } else if (newURI && (mOriginalContentType.IsEmpty() || newMime_Type != eType_Null)) {
1814
0
    // We could potentially load this if we opened a channel on mURI, indicate
1815
0
    // this by leaving type as loading.
1816
0
    //
1817
0
    // If a MIME type was requested in the tag, but we have decided to set load
1818
0
    // type to null, ignore (otherwise we'll default to document type loading).
1819
0
    newType = eType_Loading;
1820
0
  } else {
1821
0
    // Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
1822
0
    // documents) always load with a channel.
1823
0
    newType = eType_Null;
1824
0
  }
1825
0
1826
0
  ///
1827
0
  /// Handle existing channels
1828
0
  ///
1829
0
1830
0
  if (useChannel && newType == eType_Loading) {
1831
0
    // We decided to use a channel, and also that the previous channel is still
1832
0
    // usable, so re-use the existing values.
1833
0
    newType = mType;
1834
0
    newMime = mContentType;
1835
0
    newURI = mURI;
1836
0
  } else if (useChannel && !newChannel) {
1837
0
    // We have an existing channel, but did not decide to use one.
1838
0
    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1839
0
    useChannel = false;
1840
0
  }
1841
0
1842
0
  ///
1843
0
  /// Update changed values
1844
0
  ///
1845
0
1846
0
  if (newType != mType) {
1847
0
    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1848
0
    LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
1849
0
    bool updateIMEState = (mType == eType_Loading && newType == eType_Plugin);
1850
0
    mType = newType;
1851
0
    // The IME manager needs to know if this is a plugin so it can adjust
1852
0
    // input handling to an appropriate mode for plugins.
1853
0
    nsFocusManager* fm = nsFocusManager::GetFocusManager();
1854
0
    nsCOMPtr<nsIContent> thisContent =
1855
0
      do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1856
0
    MOZ_ASSERT(thisContent, "should have content");
1857
0
    if (updateIMEState && thisContent && fm && fm->IsFocused(thisContent)) {
1858
0
      widget::IMEState state;
1859
0
      state.mEnabled = widget::IMEState::PLUGIN;
1860
0
      state.mOpen = widget::IMEState::DONT_CHANGE_OPEN_STATE;
1861
0
      IMEStateManager::UpdateIMEState(state, thisContent, nullptr);
1862
0
    }
1863
0
  }
1864
0
1865
0
  if (!URIEquals(mBaseURI, newBaseURI)) {
1866
0
    LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1867
0
    mBaseURI = newBaseURI;
1868
0
  }
1869
0
1870
0
  if (!URIEquals(newURI, mURI)) {
1871
0
    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1872
0
    LOG(("OBJLC [%p]: Object effective URI changed", this));
1873
0
    mURI = newURI;
1874
0
  }
1875
0
1876
0
  // We don't update content type when loading, as the type is not final and we
1877
0
  // don't want to superfluously change between mOriginalContentType ->
1878
0
  // mContentType when doing |obj.data = obj.data| with a channel and differing
1879
0
  // type.
1880
0
  if (mType != eType_Loading && mContentType != newMime) {
1881
0
    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1882
0
    retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1883
0
    LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)",
1884
0
         this, mContentType.get(), newMime.get()));
1885
0
    mContentType = newMime;
1886
0
  }
1887
0
1888
0
  // If we decided to keep using info from an old channel, but also that state
1889
0
  // changed, we need to invalidate it.
1890
0
  if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1891
0
    mType = eType_Loading;
1892
0
    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1893
0
  }
1894
0
1895
0
  return retval;
1896
0
}
1897
1898
// Used by PluginDocument to kick off our initial load from the already-opened
1899
// channel.
1900
NS_IMETHODIMP
1901
nsObjectLoadingContent::InitializeFromChannel(nsIRequest *aChannel)
1902
0
{
1903
0
  LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
1904
0
  if (mType != eType_Loading || mChannel) {
1905
0
    // We could technically call UnloadObject() here, if consumers have a valid
1906
0
    // reason for wanting to call this on an already-loaded tag.
1907
0
    MOZ_ASSERT_UNREACHABLE("Should not have begun loading at this point");
1908
0
    return NS_ERROR_UNEXPECTED;
1909
0
  }
1910
0
1911
0
  // Because we didn't open this channel from an initial LoadObject, we'll
1912
0
  // update our parameters now, so the OnStartRequest->LoadObject doesn't
1913
0
  // believe our src/type suddenly changed.
1914
0
  UpdateObjectParameters();
1915
0
  // But we always want to load from a channel, in this case.
1916
0
  mType = eType_Loading;
1917
0
  mChannel = do_QueryInterface(aChannel);
1918
0
  NS_ASSERTION(mChannel, "passed a request that is not a channel");
1919
0
1920
0
  // OnStartRequest will now see we have a channel in the loading state, and
1921
0
  // call into LoadObject. There's a possibility LoadObject will decide not to
1922
0
  // load anything from a channel - it will call CloseChannel() in that case.
1923
0
  return NS_OK;
1924
0
}
1925
1926
// Only OnStartRequest should be passing the channel parameter
1927
nsresult
1928
nsObjectLoadingContent::LoadObject(bool aNotify,
1929
                                   bool aForceLoad)
1930
0
{
1931
0
  return LoadObject(aNotify, aForceLoad, nullptr);
1932
0
}
1933
1934
nsresult
1935
nsObjectLoadingContent::LoadObject(bool aNotify,
1936
                                   bool aForceLoad,
1937
                                   nsIRequest *aLoadingChannel)
1938
0
{
1939
0
  nsCOMPtr<nsIContent> thisContent =
1940
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1941
0
  NS_ASSERTION(thisContent, "must be a content");
1942
0
  nsIDocument* doc = thisContent->OwnerDoc();
1943
0
  nsresult rv = NS_OK;
1944
0
1945
0
  // Per bug 1318303, if the parent document is not active, load the alternative
1946
0
  // and return.
1947
0
  if (!doc->IsCurrentActiveDocument()) {
1948
0
    // Since this can be triggered on change of attributes, make sure we've
1949
0
    // unloaded whatever is loaded first.
1950
0
    UnloadObject();
1951
0
    LoadFallback(eFallbackAlternate, false);
1952
0
    return NS_OK;
1953
0
  }
1954
0
1955
0
  // XXX(johns): In these cases, we refuse to touch our content and just
1956
0
  //   remain unloaded, as per legacy behavior. It would make more sense to
1957
0
  //   load fallback content initially and refuse to ever change state again.
1958
0
  if (doc->IsBeingUsedAsImage() || doc->IsLoadedAsData()) {
1959
0
    return NS_OK;
1960
0
  }
1961
0
1962
0
  LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
1963
0
       this, aNotify, aForceLoad, aLoadingChannel));
1964
0
1965
0
  // We can't re-use an already open channel, but aForceLoad may make us try
1966
0
  // to load a plugin without any changes in channel state.
1967
0
  if (aForceLoad && mChannelLoaded) {
1968
0
    CloseChannel();
1969
0
    mChannelLoaded = false;
1970
0
  }
1971
0
1972
0
  // Save these for NotifyStateChanged();
1973
0
  EventStates oldState = ObjectState();
1974
0
  ObjectType oldType = mType;
1975
0
1976
0
  ParameterUpdateFlags stateChange = UpdateObjectParameters();
1977
0
1978
0
  if (!stateChange && !aForceLoad) {
1979
0
    return NS_OK;
1980
0
  }
1981
0
1982
0
  ///
1983
0
  /// State has changed, unload existing content and attempt to load new type
1984
0
  ///
1985
0
  LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)",
1986
0
       this, stateChange));
1987
0
1988
0
  // Setup fallback info. We may also change type to fallback below in case of
1989
0
  // sanity/OOM/etc. errors. We default to showing alternate content
1990
0
  // NOTE LoadFallback can override this in some cases
1991
0
  FallbackType fallbackType = eFallbackAlternate;
1992
0
1993
0
  // If GetTypeOfContent(mContentType) is null we truly have no handler for the
1994
0
  // type -- otherwise, we have a handler but UpdateObjectParameters rejected
1995
0
  // the configuration for another reason (e.g. an embed tag with type
1996
0
  // "image/png" but no URI). Don't show a plugin error or unknown type error in
1997
0
  // the latter case.
1998
0
  if (mType == eType_Null &&
1999
0
      GetTypeOfContent(mContentType, mSkipFakePlugins) == eType_Null) {
2000
0
    fallbackType = eFallbackUnsupported;
2001
0
  }
2002
0
2003
0
  // Explicit user activation should reset if the object changes content types
2004
0
  if (mActivated && (stateChange & eParamContentTypeChanged)) {
2005
0
    LOG(("OBJLC [%p]: Content type changed, clearing activation state", this));
2006
0
    mActivated = false;
2007
0
  }
2008
0
2009
0
  // We synchronously start/stop plugin instances below, which may spin the
2010
0
  // event loop. Re-entering into the load is fine, but at that point the
2011
0
  // original load call needs to abort when unwinding
2012
0
  // NOTE this is located *after* the state change check, a subsequent load
2013
0
  //      with no subsequently changed state will be a no-op.
2014
0
  if (mIsLoading) {
2015
0
    LOG(("OBJLC [%p]: Re-entering into LoadObject", this));
2016
0
  }
2017
0
  mIsLoading = true;
2018
0
  AutoSetLoadingToFalse reentryCheck(this);
2019
0
2020
0
  // Unload existing content, keeping in mind stopping plugins might spin the
2021
0
  // event loop. Note that we check for still-open channels below
2022
0
  UnloadObject(false); // Don't reset state
2023
0
  if (!mIsLoading) {
2024
0
    // The event loop must've spun and re-entered into LoadObject, which
2025
0
    // finished the load
2026
0
    LOG(("OBJLC [%p]: Re-entered into LoadObject, aborting outer load", this));
2027
0
    return NS_OK;
2028
0
  }
2029
0
2030
0
  // Determine what's going on with our channel.
2031
0
  if (stateChange & eParamChannelChanged) {
2032
0
    // If the channel params changed, throw away the channel, but unset
2033
0
    // mChannelLoaded so we'll still try to open a new one for this load if
2034
0
    // necessary
2035
0
    CloseChannel();
2036
0
    mChannelLoaded = false;
2037
0
  } else if (mType == eType_Null && mChannel) {
2038
0
    // If we opened a channel but then failed to find a loadable state, throw it
2039
0
    // away. mChannelLoaded will indicate that we tried to load a channel at one
2040
0
    // point so we wont recurse
2041
0
    CloseChannel();
2042
0
  } else if (mType == eType_Loading && mChannel) {
2043
0
    // We're still waiting on a channel load, already opened one, and
2044
0
    // channel parameters didn't change
2045
0
    return NS_OK;
2046
0
  } else if (mChannelLoaded && mChannel != aLoadingChannel) {
2047
0
    // The only time we should have a loaded channel with a changed state is
2048
0
    // when the channel has just opened -- in which case this call should
2049
0
    // have originated from OnStartRequest
2050
0
    MOZ_ASSERT_UNREACHABLE("Loading with a channel, but state doesn't make sense");
2051
0
    return NS_OK;
2052
0
  }
2053
0
2054
0
  //
2055
0
  // Security checks
2056
0
  //
2057
0
2058
0
  if (mType != eType_Null) {
2059
0
    bool allowLoad = true;
2060
0
    int16_t contentPolicy = nsIContentPolicy::ACCEPT;
2061
0
    // If mChannelLoaded is set we presumably already passed load policy
2062
0
    // If mType == eType_Loading then we call OpenChannel() which internally
2063
0
    // creates a new channel and calls asyncOpen2() on that channel which
2064
0
    // then enforces content policy checks.
2065
0
    if (allowLoad && mURI && !mChannelLoaded && mType != eType_Loading) {
2066
0
      allowLoad = CheckLoadPolicy(&contentPolicy);
2067
0
    }
2068
0
    // If we're loading a type now, check ProcessPolicy. Note that we may check
2069
0
    // both now in the case of plugins whose type is determined before opening a
2070
0
    // channel.
2071
0
    if (allowLoad && mType != eType_Loading) {
2072
0
      allowLoad = CheckProcessPolicy(&contentPolicy);
2073
0
    }
2074
0
2075
0
    // Content policy implementations can mutate the DOM, check for re-entry
2076
0
    if (!mIsLoading) {
2077
0
      LOG(("OBJLC [%p]: We re-entered in content policy, leaving original load",
2078
0
           this));
2079
0
      return NS_OK;
2080
0
    }
2081
0
2082
0
    // Load denied, switch to fallback and set disabled/suppressed if applicable
2083
0
    if (!allowLoad) {
2084
0
      LOG(("OBJLC [%p]: Load denied by policy", this));
2085
0
      mType = eType_Null;
2086
0
      if (contentPolicy == nsIContentPolicy::REJECT_TYPE) {
2087
0
        // XXX(johns) This is assuming that we were rejected by
2088
0
        //            nsContentBlocker, which rejects by type if permissions
2089
0
        //            reject plugins
2090
0
        fallbackType = eFallbackUserDisabled;
2091
0
      } else {
2092
0
        fallbackType = eFallbackSuppressed;
2093
0
      }
2094
0
    }
2095
0
  }
2096
0
2097
0
  // Don't allow view-source scheme.
2098
0
  // view-source is the only scheme to which this applies at the moment due to
2099
0
  // potential timing attacks to read data from cross-origin documents. If this
2100
0
  // widens we should add a protocol flag for whether the scheme is only allowed
2101
0
  // in top and use something like nsNetUtil::NS_URIChainHasFlags.
2102
0
  if (mType != eType_Null) {
2103
0
    nsCOMPtr<nsIURI> tempURI = mURI;
2104
0
    nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
2105
0
    while (nestedURI) {
2106
0
      // view-source should always be an nsINestedURI, loop and check the
2107
0
      // scheme on this and all inner URIs that are also nested URIs.
2108
0
      bool isViewSource = false;
2109
0
      rv = tempURI->SchemeIs("view-source", &isViewSource);
2110
0
      if (NS_FAILED(rv) || isViewSource) {
2111
0
        LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme",
2112
0
             this));
2113
0
        mType = eType_Null;
2114
0
        break;
2115
0
      }
2116
0
2117
0
      nestedURI->GetInnerURI(getter_AddRefs(tempURI));
2118
0
      nestedURI = do_QueryInterface(tempURI);
2119
0
    }
2120
0
  }
2121
0
2122
0
  // Items resolved as Image/Document are no candidates for content blocking,
2123
0
  // as well as invalid plugins (they will not have the mContentType set).
2124
0
  if ((mType == eType_Null || IsPluginType(mType)) && ShouldBlockContent()) {
2125
0
    LOG(("OBJLC [%p]: Enable content blocking", this));
2126
0
    mType = eType_Loading;
2127
0
  }
2128
0
2129
0
  // If we're a plugin but shouldn't start yet, load fallback with
2130
0
  // reason click-to-play instead. Items resolved as Image/Document
2131
0
  // will not be checked for previews, as well as invalid plugins
2132
0
  // (they will not have the mContentType set).
2133
0
  FallbackType clickToPlayReason;
2134
0
  if (!mActivated && IsPluginType(mType) && !ShouldPlay(clickToPlayReason)) {
2135
0
    LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
2136
0
    mType = eType_Null;
2137
0
    fallbackType = clickToPlayReason;
2138
0
  }
2139
0
2140
0
  if (!mActivated && IsPluginType(mType)) {
2141
0
    // Object passed ShouldPlay, so it should be considered
2142
0
    // activated until it changes content type
2143
0
    LOG(("OBJLC [%p]: Object implicitly activated", this));
2144
0
    mActivated = true;
2145
0
  }
2146
0
2147
0
  // Sanity check: We shouldn't have any loaded resources, pending events, or
2148
0
  // a final listener at this point
2149
0
  if (mFrameLoader || mPendingInstantiateEvent || mInstanceOwner ||
2150
0
      mPendingCheckPluginStopEvent || mFinalListener)
2151
0
  {
2152
0
    MOZ_ASSERT_UNREACHABLE("Trying to load new plugin with existing content");
2153
0
    return NS_OK;
2154
0
  }
2155
0
2156
0
  // More sanity-checking:
2157
0
  // If mChannel is set, mChannelLoaded should be set, and vice-versa
2158
0
  if (mType != eType_Null && !!mChannel != mChannelLoaded) {
2159
0
    MOZ_ASSERT_UNREACHABLE("Trying to load with bad channel state");
2160
0
    return NS_OK;
2161
0
  }
2162
0
2163
0
  ///
2164
0
  /// Attempt to load new type
2165
0
  ///
2166
0
2167
0
2168
0
  // Cache the current attributes and parameters.
2169
0
  if (mType == eType_Plugin || mType == eType_Null) {
2170
0
    rv = BuildParametersArray();
2171
0
    NS_ENSURE_SUCCESS(rv, rv);
2172
0
  }
2173
0
2174
0
  // We don't set mFinalListener until OnStartRequest has been called, to
2175
0
  // prevent re-entry ugliness with CloseChannel()
2176
0
  nsCOMPtr<nsIStreamListener> finalListener;
2177
0
  // If we decide to synchronously spawn a plugin, we do it after firing
2178
0
  // notifications to avoid re-entry causing notifications to fire out of order.
2179
0
  bool doSpawnPlugin = false;
2180
0
  switch (mType) {
2181
0
    case eType_Image:
2182
0
      if (!mChannel) {
2183
0
        // We have a LoadImage() call, but UpdateObjectParameters requires a
2184
0
        // channel for images, so this is not a valid state.
2185
0
        MOZ_ASSERT_UNREACHABLE("Attempting to load image without a channel?");
2186
0
        rv = NS_ERROR_UNEXPECTED;
2187
0
        break;
2188
0
      }
2189
0
      rv = LoadImageWithChannel(mChannel, getter_AddRefs(finalListener));
2190
0
      // finalListener will receive OnStartRequest below
2191
0
    break;
2192
0
    case eType_Plugin:
2193
0
    {
2194
0
      if (mChannel) {
2195
0
        // Force a sync state change now, we need the frame created
2196
0
        NotifyStateChanged(oldType, oldState, true, aNotify);
2197
0
        oldType = mType;
2198
0
        oldState = ObjectState();
2199
0
2200
0
        if (!thisContent->GetPrimaryFrame()) {
2201
0
          // We're un-rendered, and can't instantiate a plugin. HasNewFrame will
2202
0
          // re-start us when we can proceed.
2203
0
          LOG(("OBJLC [%p]: Aborting load - plugin-type, but no frame", this));
2204
0
          CloseChannel();
2205
0
          break;
2206
0
        }
2207
0
2208
0
        // We'll handle this below
2209
0
        doSpawnPlugin = true;
2210
0
      } else {
2211
0
        rv = AsyncStartPluginInstance();
2212
0
      }
2213
0
    }
2214
0
    break;
2215
0
    case eType_FakePlugin:
2216
0
    {
2217
0
      if (mChannel) {
2218
0
        /// XXX(johns): Ideally we'd have some way to pass the channel to the
2219
0
        ///             fake plugin handler, but for now handlers will need to
2220
0
        ///             request element.srcURI themselves if they want it
2221
0
        LOG(("OBJLC [%p]: Closing unused channel for fake plugin type", this));
2222
0
        CloseChannel();
2223
0
      }
2224
0
2225
0
      /// XXX(johns) Bug FIXME - We need to cleanup the various plugintag
2226
0
      ///            classes to be more sane and avoid this dance
2227
0
      nsCOMPtr<nsIPluginTag> basetag =
2228
0
        nsContentUtils::PluginTagForType(mContentType, false);
2229
0
      nsCOMPtr<nsIFakePluginTag> tag = do_QueryInterface(basetag);
2230
0
2231
0
      uint32_t id;
2232
0
      if (NS_FAILED(tag->GetId(&id))) {
2233
0
        rv = NS_ERROR_FAILURE;
2234
0
        break;
2235
0
      }
2236
0
2237
0
      MOZ_ASSERT(id <= PR_INT32_MAX,
2238
0
                 "Something went wrong, nsPluginHost::RegisterFakePlugin shouldn't have "
2239
0
                 "given out this id.");
2240
0
2241
0
      SetupFrameLoader(int32_t(id));
2242
0
      if (!mFrameLoader) {
2243
0
        rv = NS_ERROR_FAILURE;
2244
0
        break;
2245
0
      }
2246
0
2247
0
2248
0
      nsString sandboxScript;
2249
0
      tag->GetSandboxScript(sandboxScript);
2250
0
      if (!sandboxScript.IsEmpty()) {
2251
0
        // Create a sandbox.
2252
0
        AutoJSAPI jsapi;
2253
0
        jsapi.Init();
2254
0
        JS::Rooted<JSObject*> sandbox(jsapi.cx());
2255
0
        rv = nsContentUtils::XPConnect()->
2256
0
          CreateSandbox(jsapi.cx(), nsContentUtils::GetSystemPrincipal(),
2257
0
                        sandbox.address());
2258
0
        if (NS_FAILED(rv)) {
2259
0
          break;
2260
0
        }
2261
0
2262
0
        AutoEntryScript aes(sandbox, "JS plugin sandbox code");
2263
0
2264
0
        JS::Rooted<JS::Value> element(aes.cx());
2265
0
        if (!ToJSValue(aes.cx(), thisContent, &element)) {
2266
0
          rv = NS_ERROR_FAILURE;
2267
0
          break;
2268
0
        }
2269
0
2270
0
        if (!JS_DefineProperty(aes.cx(), sandbox, "pluginElement", element, JSPROP_ENUMERATE)) {
2271
0
          rv = NS_ERROR_FAILURE;
2272
0
          break;
2273
0
        }
2274
0
2275
0
        JS::Rooted<JS::Value> rval(aes.cx());
2276
0
        // If the eval'ed code throws we won't load and do fallback instead.
2277
0
        rv = nsContentUtils::XPConnect()->EvalInSandboxObject(sandboxScript, nullptr, aes.cx(), sandbox, &rval);
2278
0
        if (NS_FAILED(rv)) {
2279
0
          break;
2280
0
        }
2281
0
      }
2282
0
2283
0
      nsCOMPtr<nsIURI> handlerURI;
2284
0
      if (tag) {
2285
0
        tag->GetHandlerURI(getter_AddRefs(handlerURI));
2286
0
      }
2287
0
2288
0
      if (!handlerURI) {
2289
0
        MOZ_ASSERT_UNREACHABLE("Selected type is not a proper fake plugin "
2290
0
                               "handler");
2291
0
        rv = NS_ERROR_FAILURE;
2292
0
        break;
2293
0
      }
2294
0
2295
0
      nsCString spec;
2296
0
      handlerURI->GetSpec(spec);
2297
0
      LOG(("OBJLC [%p]: Loading fake plugin handler (%s)", this, spec.get()));
2298
0
2299
0
      rv = mFrameLoader->LoadURI(handlerURI, false);
2300
0
      if (NS_FAILED(rv)) {
2301
0
        LOG(("OBJLC [%p]: LoadURI() failed for fake handler", this));
2302
0
        mFrameLoader->Destroy();
2303
0
        mFrameLoader = nullptr;
2304
0
      }
2305
0
    }
2306
0
    break;
2307
0
    case eType_Document:
2308
0
    {
2309
0
      if (!mChannel) {
2310
0
        // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
2311
0
        // requires documents have a channel, so this is not a valid state.
2312
0
        MOZ_ASSERT_UNREACHABLE("Attempting to load a document without a "
2313
0
                               "channel");
2314
0
        rv = NS_ERROR_FAILURE;
2315
0
        break;
2316
0
      }
2317
0
2318
0
      nsCOMPtr<nsIDocShell> docShell = SetupDocShell(mURI);
2319
0
      if (!docShell) {
2320
0
        rv = NS_ERROR_FAILURE;
2321
0
        break;
2322
0
      }
2323
0
2324
0
      // We're loading a document, so we have to set LOAD_DOCUMENT_URI
2325
0
      // (especially important for firing onload)
2326
0
      nsLoadFlags flags = 0;
2327
0
      mChannel->GetLoadFlags(&flags);
2328
0
      flags |= nsIChannel::LOAD_DOCUMENT_URI;
2329
0
      mChannel->SetLoadFlags(flags);
2330
0
2331
0
      nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
2332
0
      NS_ASSERTION(req, "Docshell must be an ifreq");
2333
0
2334
0
      nsCOMPtr<nsIURILoader>
2335
0
        uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
2336
0
      if (NS_FAILED(rv)) {
2337
0
        MOZ_ASSERT_UNREACHABLE("Failed to get uriLoader service");
2338
0
        mFrameLoader->Destroy();
2339
0
        mFrameLoader = nullptr;
2340
0
        break;
2341
0
      }
2342
0
2343
0
      rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
2344
0
                                  getter_AddRefs(finalListener));
2345
0
      // finalListener will receive OnStartRequest below
2346
0
    }
2347
0
    break;
2348
0
    case eType_Loading:
2349
0
      // If our type remains Loading, we need a channel to proceed
2350
0
      rv = OpenChannel();
2351
0
      if (NS_FAILED(rv)) {
2352
0
        LOG(("OBJLC [%p]: OpenChannel returned failure (%" PRIu32 ")",
2353
0
             this, static_cast<uint32_t>(rv)));
2354
0
      }
2355
0
    break;
2356
0
    case eType_Null:
2357
0
      // Handled below, silence compiler warnings
2358
0
    break;
2359
0
  }
2360
0
2361
0
  //
2362
0
  // Loaded, handle notifications and fallback
2363
0
  //
2364
0
  if (NS_FAILED(rv)) {
2365
0
    // If we failed in the loading hunk above, switch to fallback
2366
0
    LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
2367
0
    mType = eType_Null;
2368
0
  }
2369
0
2370
0
  // If we didn't load anything, handle switching to fallback state
2371
0
  if (mType == eType_Null) {
2372
0
    LOG(("OBJLC [%p]: Loading fallback, type %u", this, fallbackType));
2373
0
    NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
2374
0
                 "switched to type null but also loaded something");
2375
0
2376
0
    // Don't fire error events if we're falling back to click-to-play; instead
2377
0
    // pretend like this is a really slow-loading plug-in instead.
2378
0
    if (fallbackType != eFallbackClickToPlay && fallbackType != eFallbackClickToPlayQuiet) {
2379
0
      MaybeFireErrorEvent();
2380
0
    }
2381
0
2382
0
    if (mChannel) {
2383
0
      // If we were loading with a channel but then failed over, throw it away
2384
0
      CloseChannel();
2385
0
    }
2386
0
2387
0
    // Don't try to initialize plugins or final listener below
2388
0
    doSpawnPlugin = false;
2389
0
    finalListener = nullptr;
2390
0
2391
0
    // Don't notify, as LoadFallback doesn't know of our previous state
2392
0
    // (so really this is just setting mFallbackType)
2393
0
    LoadFallback(fallbackType, false);
2394
0
  }
2395
0
2396
0
  // Notify of our final state
2397
0
  NotifyStateChanged(oldType, oldState, false, aNotify);
2398
0
  NS_ENSURE_TRUE(mIsLoading, NS_OK);
2399
0
2400
0
2401
0
  //
2402
0
  // Spawning plugins and dispatching to the final listener may re-enter, so are
2403
0
  // delayed until after we fire a notification, to prevent missing
2404
0
  // notifications or firing them out of order.
2405
0
  //
2406
0
  // Note that we ensured that we entered into LoadObject() from
2407
0
  // ::OnStartRequest above when loading with a channel.
2408
0
  //
2409
0
2410
0
  rv = NS_OK;
2411
0
  if (doSpawnPlugin) {
2412
0
    rv = InstantiatePluginInstance(true);
2413
0
    NS_ENSURE_TRUE(mIsLoading, NS_OK);
2414
0
    // Create the final listener if we're loading with a channel. We can't do
2415
0
    // this in the loading block above as it requires an instance.
2416
0
    if (aLoadingChannel && NS_SUCCEEDED(rv)) {
2417
0
      if (NS_SUCCEEDED(rv) && MakePluginListener()) {
2418
0
        rv = mFinalListener->OnStartRequest(mChannel, nullptr);
2419
0
        if (NS_FAILED(rv)) {
2420
0
          // Plugins can reject their initial stream, but continue to run.
2421
0
          CloseChannel();
2422
0
          NS_ENSURE_TRUE(mIsLoading, NS_OK);
2423
0
          rv = NS_OK;
2424
0
        }
2425
0
      }
2426
0
    }
2427
0
  } else if (finalListener) {
2428
0
    NS_ASSERTION(mType != eType_Null && mType != eType_Loading,
2429
0
                 "We should not have a final listener with a non-loaded type");
2430
0
    mFinalListener = finalListener;
2431
0
    rv = finalListener->OnStartRequest(mChannel, nullptr);
2432
0
  }
2433
0
2434
0
  if (NS_FAILED(rv) && mIsLoading) {
2435
0
    // Since we've already notified of our transition, we can just Unload and
2436
0
    // call LoadFallback (which will notify again)
2437
0
    mType = eType_Null;
2438
0
    UnloadObject(false);
2439
0
    NS_ENSURE_TRUE(mIsLoading, NS_OK);
2440
0
    CloseChannel();
2441
0
    LoadFallback(fallbackType, true);
2442
0
  }
2443
0
2444
0
  return NS_OK;
2445
0
}
2446
2447
// This call can re-enter when dealing with plugin listeners
2448
nsresult
2449
nsObjectLoadingContent::CloseChannel()
2450
0
{
2451
0
  if (mChannel) {
2452
0
    LOG(("OBJLC [%p]: Closing channel\n", this));
2453
0
    // Null the values before potentially-reentering, and ensure they survive
2454
0
    // the call
2455
0
    nsCOMPtr<nsIChannel> channelGrip(mChannel);
2456
0
    nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
2457
0
    mChannel = nullptr;
2458
0
    mFinalListener = nullptr;
2459
0
    channelGrip->Cancel(NS_BINDING_ABORTED);
2460
0
    if (listenerGrip) {
2461
0
      // mFinalListener is only set by LoadObject after OnStartRequest, or
2462
0
      // by OnStartRequest in the case of late-opened plugin streams
2463
0
      listenerGrip->OnStopRequest(channelGrip, nullptr, NS_BINDING_ABORTED);
2464
0
    }
2465
0
  }
2466
0
  return NS_OK;
2467
0
}
2468
2469
nsresult
2470
nsObjectLoadingContent::OpenChannel()
2471
0
{
2472
0
  nsCOMPtr<nsIContent> thisContent =
2473
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2474
0
  NS_ASSERTION(thisContent, "must be a content");
2475
0
  nsIDocument* doc = thisContent->OwnerDoc();
2476
0
  NS_ASSERTION(doc, "No owner document?");
2477
0
2478
0
  nsresult rv;
2479
0
  mChannel = nullptr;
2480
0
2481
0
  // E.g. mms://
2482
0
  if (!mURI || !CanHandleURI(mURI)) {
2483
0
    return NS_ERROR_NOT_AVAILABLE;
2484
0
  }
2485
0
2486
0
  nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
2487
0
  nsCOMPtr<nsIChannel> chan;
2488
0
  RefPtr<ObjectInterfaceRequestorShim> shim =
2489
0
    new ObjectInterfaceRequestorShim(this);
2490
0
2491
0
  bool isSandBoxed = doc->GetSandboxFlags() & SANDBOXED_ORIGIN;
2492
0
  bool inherit = nsContentUtils::ChannelShouldInheritPrincipal(thisContent->NodePrincipal(),
2493
0
                                                               mURI,
2494
0
                                                               true,   // aInheritForAboutBlank
2495
0
                                                               false); // aForceInherit
2496
0
  nsSecurityFlags securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
2497
0
2498
0
  bool isData;
2499
0
  bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() &&
2500
0
                           NS_SUCCEEDED(mURI->SchemeIs("data", &isData)) &&
2501
0
                           isData;
2502
0
2503
0
  if (inherit && !isURIUniqueOrigin) {
2504
0
    securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
2505
0
  }
2506
0
  if (isSandBoxed) {
2507
0
    securityFlags |= nsILoadInfo::SEC_SANDBOXED;
2508
0
  }
2509
0
2510
0
  nsContentPolicyType contentPolicyType = GetContentPolicyType();
2511
0
2512
0
  rv = NS_NewChannel(getter_AddRefs(chan),
2513
0
                     mURI,
2514
0
                     thisContent,
2515
0
                     securityFlags,
2516
0
                     contentPolicyType,
2517
0
                     nullptr, // aPerformanceStorage
2518
0
                     group, // aLoadGroup
2519
0
                     shim,  // aCallbacks
2520
0
                     nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
2521
0
                     nsIChannel::LOAD_CLASSIFY_URI |
2522
0
                     nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
2523
0
                     nsIRequest::LOAD_HTML_OBJECT_DATA);
2524
0
  NS_ENSURE_SUCCESS(rv, rv);
2525
0
  if (inherit) {
2526
0
    nsCOMPtr<nsILoadInfo> loadinfo = chan->GetLoadInfo();
2527
0
    NS_ENSURE_STATE(loadinfo);
2528
0
    loadinfo->SetPrincipalToInherit(thisContent->NodePrincipal());
2529
0
  }
2530
0
2531
0
  // Referrer
2532
0
  nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
2533
0
  if (httpChan) {
2534
0
    rv = httpChan->SetReferrerWithPolicy(doc->GetDocumentURI(),
2535
0
                                         doc->GetReferrerPolicy());
2536
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2537
0
2538
0
    // Set the initiator type
2539
0
    nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
2540
0
    if (timedChannel) {
2541
0
      timedChannel->SetInitiatorType(thisContent->LocalName());
2542
0
    }
2543
0
2544
0
    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(httpChan));
2545
0
    if (cos && EventStateManager::IsHandlingUserInput()) {
2546
0
      cos->AddClassFlags(nsIClassOfService::UrgentStart);
2547
0
    }
2548
0
  }
2549
0
2550
0
  nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
2551
0
  if (scriptChannel) {
2552
0
    // Allow execution against our context if the principals match
2553
0
    scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
2554
0
  }
2555
0
2556
0
  // AsyncOpen2 can fail if a file does not exist.
2557
0
  rv = chan->AsyncOpen2(shim);
2558
0
  NS_ENSURE_SUCCESS(rv, rv);
2559
0
  LOG(("OBJLC [%p]: Channel opened", this));
2560
0
  mChannel = chan;
2561
0
  return NS_OK;
2562
0
}
2563
2564
uint32_t
2565
nsObjectLoadingContent::GetCapabilities() const
2566
0
{
2567
0
  return eSupportImages |
2568
0
         eSupportPlugins |
2569
0
         eSupportDocuments;
2570
0
}
2571
2572
void
2573
nsObjectLoadingContent::DestroyContent()
2574
0
{
2575
0
  if (mFrameLoader) {
2576
0
    mFrameLoader->Destroy();
2577
0
    mFrameLoader = nullptr;
2578
0
  }
2579
0
2580
0
  if (mInstanceOwner || mInstantiating) {
2581
0
    QueueCheckPluginStopEvent();
2582
0
  }
2583
0
}
2584
2585
/* static */
2586
void
2587
nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp,
2588
                                 nsCycleCollectionTraversalCallback &cb)
2589
0
{
2590
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader);
2591
0
}
2592
2593
void
2594
nsObjectLoadingContent::UnloadObject(bool aResetState)
2595
0
{
2596
0
  // Don't notify in CancelImageRequests until we transition to a new loaded
2597
0
  // state
2598
0
  CancelImageRequests(false);
2599
0
  if (mFrameLoader) {
2600
0
    mFrameLoader->Destroy();
2601
0
    mFrameLoader = nullptr;
2602
0
  }
2603
0
2604
0
  if (aResetState) {
2605
0
    if (mType != eType_Plugin) {
2606
0
      // This can re-enter when dealing with plugins, and StopPluginInstance
2607
0
      // will handle it
2608
0
      CloseChannel();
2609
0
    }
2610
0
    mChannelLoaded = false;
2611
0
    mType = eType_Loading;
2612
0
    mURI = mOriginalURI = mBaseURI = nullptr;
2613
0
    mContentType.Truncate();
2614
0
    mOriginalContentType.Truncate();
2615
0
  }
2616
0
2617
0
  // InstantiatePluginInstance checks this after re-entrant calls and aborts if
2618
0
  // it was cleared from under it
2619
0
  mInstantiating = false;
2620
0
2621
0
  mScriptRequested = false;
2622
0
2623
0
  if (mIsStopping) {
2624
0
    // The protochain is normally thrown out after a plugin stops, but if we
2625
0
    // re-enter while stopping a plugin and try to load something new, we need
2626
0
    // to throw away the old protochain in the nested unload.
2627
0
    TeardownProtoChain();
2628
0
    mIsStopping = false;
2629
0
  }
2630
0
2631
0
  mCachedAttributes.Clear();
2632
0
  mCachedParameters.Clear();
2633
0
2634
0
  // This call should be last as it may re-enter
2635
0
  StopPluginInstance();
2636
0
}
2637
2638
void
2639
nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
2640
                                           EventStates aOldState,
2641
                                           bool aSync,
2642
                                           bool aNotify)
2643
0
{
2644
0
  LOG(("OBJLC [%p]: Notifying about state change: (%u, %" PRIx64 ") -> (%u, %" PRIx64 ")"
2645
0
       " (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(),
2646
0
       mType, ObjectState().GetInternalValue(), aSync, aNotify));
2647
0
2648
0
  nsCOMPtr<nsIContent> thisContent =
2649
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2650
0
  NS_ASSERTION(thisContent, "must be a content");
2651
0
2652
0
  NS_ASSERTION(thisContent->IsElement(), "Not an element?");
2653
0
2654
0
  // XXX(johns): A good bit of the code below replicates UpdateState(true)
2655
0
2656
0
  // Unfortunately, we do some state changes without notifying
2657
0
  // (e.g. in Fallback when canceling image requests), so we have to
2658
0
  // manually notify object state changes.
2659
0
  thisContent->AsElement()->UpdateState(false);
2660
0
2661
0
  if (!aNotify) {
2662
0
    // We're done here
2663
0
    return;
2664
0
  }
2665
0
2666
0
  nsIDocument* doc = thisContent->GetComposedDoc();
2667
0
  if (!doc) {
2668
0
    return; // Nothing to do
2669
0
  }
2670
0
2671
0
  EventStates newState = ObjectState();
2672
0
2673
0
  if (newState == aOldState && mType == aOldType) {
2674
0
    return; // Also done.
2675
0
  }
2676
0
2677
0
  if (newState != aOldState) {
2678
0
    NS_ASSERTION(thisContent->IsInComposedDoc(), "Something is confused");
2679
0
    // This will trigger frame construction
2680
0
    EventStates changedBits = aOldState ^ newState;
2681
0
    {
2682
0
      nsAutoScriptBlocker scriptBlocker;
2683
0
      doc->ContentStateChanged(thisContent, changedBits);
2684
0
    }
2685
0
  } else if (aOldType != mType) {
2686
0
    // If our state changed, then we already recreated frames
2687
0
    // Otherwise, need to do that here
2688
0
    nsCOMPtr<nsIPresShell> shell = doc->GetShell();
2689
0
    if (shell) {
2690
0
      shell->PostRecreateFramesFor(thisContent->AsElement());
2691
0
    }
2692
0
  }
2693
0
2694
0
  if (aSync) {
2695
0
    NS_ASSERTION(InActiveDocument(thisContent), "Something is confused");
2696
0
    // Make sure that frames are actually constructed immediately.
2697
0
    doc->FlushPendingNotifications(FlushType::Frames);
2698
0
  }
2699
0
}
2700
2701
nsObjectLoadingContent::ObjectType
2702
nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType,
2703
                                         bool aNoFakePlugin)
2704
0
{
2705
0
  nsCOMPtr<nsIContent> thisContent =
2706
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2707
0
  NS_ASSERTION(thisContent, "must be a content");
2708
0
2709
0
  ObjectType type = static_cast<ObjectType>(
2710
0
    nsContentUtils::HtmlObjectContentTypeForMIMEType(aMIMEType, aNoFakePlugin,
2711
0
                                                     thisContent));
2712
0
2713
0
  // Switch the result type to eType_Null ic the capability is not present.
2714
0
  uint32_t caps = GetCapabilities();
2715
0
  if (!(caps & eSupportImages) && type == eType_Image) {
2716
0
    type = eType_Null;
2717
0
  }
2718
0
  if (!(caps & eSupportDocuments) && type == eType_Document) {
2719
0
    type = eType_Null;
2720
0
  }
2721
0
  if (!(caps & eSupportPlugins) &&
2722
0
      (type == eType_Plugin || type == eType_FakePlugin)) {
2723
0
    type = eType_Null;
2724
0
  }
2725
0
2726
0
  return type;
2727
0
}
2728
2729
nsPluginFrame*
2730
nsObjectLoadingContent::GetExistingFrame()
2731
0
{
2732
0
  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2733
0
  nsIFrame* frame = thisContent->GetPrimaryFrame();
2734
0
  nsIObjectFrame* objFrame = do_QueryFrame(frame);
2735
0
  return static_cast<nsPluginFrame*>(objFrame);
2736
0
}
2737
2738
void
2739
nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
2740
0
{
2741
0
  nsImageLoadingContent::CreateStaticImageClone(aDest);
2742
0
2743
0
  aDest->mType = mType;
2744
0
  nsObjectLoadingContent* thisObj = const_cast<nsObjectLoadingContent*>(this);
2745
0
  if (thisObj->mPrintFrame.IsAlive()) {
2746
0
    aDest->mPrintFrame = thisObj->mPrintFrame;
2747
0
  } else {
2748
0
    aDest->mPrintFrame = const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame();
2749
0
  }
2750
0
2751
0
  if (mFrameLoader) {
2752
0
    nsCOMPtr<nsIContent> content =
2753
0
      do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
2754
0
    nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), nullptr, false);
2755
0
    if (fl) {
2756
0
      aDest->mFrameLoader = fl;
2757
0
      mFrameLoader->CreateStaticClone(fl);
2758
0
    }
2759
0
  }
2760
0
}
2761
2762
NS_IMETHODIMP
2763
nsObjectLoadingContent::GetPrintFrame(nsIFrame** aFrame)
2764
0
{
2765
0
  *aFrame = mPrintFrame.GetFrame();
2766
0
  return NS_OK;
2767
0
}
2768
2769
NS_IMETHODIMP
2770
nsObjectLoadingContent::PluginDestroyed()
2771
0
{
2772
0
  // Called when our plugin is destroyed from under us, usually when reloading
2773
0
  // plugins in plugin host. Invalidate instance owner / prototype but otherwise
2774
0
  // don't take any action.
2775
0
  TeardownProtoChain();
2776
0
  if (mInstanceOwner) {
2777
0
    mInstanceOwner->Destroy();
2778
0
    mInstanceOwner = nullptr;
2779
0
  }
2780
0
  return NS_OK;
2781
0
}
2782
2783
NS_IMETHODIMP
2784
nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
2785
                                      const nsAString& pluginDumpID,
2786
                                      const nsAString& browserDumpID,
2787
                                      bool submittedCrashReport)
2788
0
{
2789
0
  LOG(("OBJLC [%p]: Plugin Crashed, queuing crash event", this));
2790
0
  NS_ASSERTION(mType == eType_Plugin, "PluginCrashed at non-plugin type");
2791
0
2792
0
  nsCOMPtr<nsIContent> thisContent =
2793
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2794
0
2795
#ifdef XP_MACOSX
2796
  HTMLObjectElement::HandlePluginCrashed(thisContent->AsElement());
2797
#endif
2798
2799
0
  PluginDestroyed();
2800
0
2801
0
  // Switch to fallback/crashed state, notify
2802
0
  LoadFallback(eFallbackCrashed, true);
2803
0
2804
0
  // send nsPluginCrashedEvent
2805
0
2806
0
  // Note that aPluginTag in invalidated after we're called, so copy
2807
0
  // out any data we need now.
2808
0
  nsAutoCString pluginName;
2809
0
  aPluginTag->GetName(pluginName);
2810
0
  nsAutoCString pluginFilename;
2811
0
  aPluginTag->GetFilename(pluginFilename);
2812
0
2813
0
  nsCOMPtr<nsIRunnable> ev =
2814
0
    new nsPluginCrashedEvent(thisContent,
2815
0
                             pluginDumpID,
2816
0
                             browserDumpID,
2817
0
                             NS_ConvertUTF8toUTF16(pluginName),
2818
0
                             NS_ConvertUTF8toUTF16(pluginFilename),
2819
0
                             submittedCrashReport);
2820
0
  nsresult rv = NS_DispatchToCurrentThread(ev);
2821
0
  if (NS_FAILED(rv)) {
2822
0
    NS_WARNING("failed to dispatch nsPluginCrashedEvent");
2823
0
  }
2824
0
  return NS_OK;
2825
0
}
2826
2827
nsresult
2828
nsObjectLoadingContent::ScriptRequestPluginInstance(JSContext* aCx,
2829
                                                    nsNPAPIPluginInstance **aResult)
2830
0
{
2831
0
  // The below methods pull the cx off the stack, so make sure they match.
2832
0
  //
2833
0
  // NB: Sometimes there's a null cx on the stack, in which case |cx| is the
2834
0
  // safe JS context. But in that case, IsCallerChrome() will return true,
2835
0
  // so the ensuing expression is short-circuited.
2836
0
  // XXXbz the NB comment above doesn't really make sense.  At the moment, all
2837
0
  // the callers to this except maybe SetupProtoChain have a useful JSContext*
2838
0
  // that could be used for nsContentUtils::IsSystemCaller...  We do need to
2839
0
  // sort out what the SetupProtoChain callers look like.
2840
0
  MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContext(),
2841
0
                aCx == nsContentUtils::GetCurrentJSContext());
2842
0
  bool callerIsContentJS = (nsContentUtils::GetCurrentJSContext() &&
2843
0
                            !nsContentUtils::IsCallerChrome() &&
2844
0
                            !nsContentUtils::IsCallerContentXBL());
2845
0
2846
0
  nsCOMPtr<nsIContent> thisContent =
2847
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2848
0
2849
0
  *aResult = nullptr;
2850
0
2851
0
  // The first time content script attempts to access placeholder content, fire
2852
0
  // an event.  Fallback types >= eFallbackClickToPlay are plugin-replacement
2853
0
  // types, see header.
2854
0
  if (callerIsContentJS && !mScriptRequested &&
2855
0
      InActiveDocument(thisContent) && mType == eType_Null &&
2856
0
      mFallbackType >= eFallbackClickToPlay) {
2857
0
    nsCOMPtr<nsIRunnable> ev =
2858
0
      new nsSimplePluginEvent(thisContent,
2859
0
                              NS_LITERAL_STRING("PluginScripted"));
2860
0
    nsresult rv = NS_DispatchToCurrentThread(ev);
2861
0
    if (NS_FAILED(rv)) {
2862
0
      MOZ_ASSERT_UNREACHABLE("failed to dispatch PluginScripted event");
2863
0
    }
2864
0
    mScriptRequested = true;
2865
0
  } else if (callerIsContentJS && mType == eType_Plugin && !mInstanceOwner &&
2866
0
             nsContentUtils::IsSafeToRunScript() &&
2867
0
             InActiveDocument(thisContent)) {
2868
0
    // If we're configured as a plugin in an active document and it's safe to
2869
0
    // run scripts right now, try spawning synchronously
2870
0
    SyncStartPluginInstance();
2871
0
  }
2872
0
2873
0
  if (mInstanceOwner) {
2874
0
    return mInstanceOwner->GetInstance(aResult);
2875
0
  }
2876
0
2877
0
  // Note that returning a null plugin is expected (and happens often)
2878
0
  return NS_OK;
2879
0
}
2880
2881
NS_IMETHODIMP
2882
nsObjectLoadingContent::SyncStartPluginInstance()
2883
0
{
2884
0
  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
2885
0
               "Must be able to run script in order to instantiate a plugin instance!");
2886
0
2887
0
  // Don't even attempt to start an instance unless the content is in
2888
0
  // the document and active
2889
0
  nsCOMPtr<nsIContent> thisContent =
2890
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2891
0
  if (!InActiveDocument(thisContent)) {
2892
0
    return NS_ERROR_FAILURE;
2893
0
  }
2894
0
2895
0
  nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
2896
0
  mozilla::Unused << kungFuURIGrip; // This URI is not referred to within this function
2897
0
  nsCString contentType(mContentType);
2898
0
  return InstantiatePluginInstance();
2899
0
}
2900
2901
NS_IMETHODIMP
2902
nsObjectLoadingContent::AsyncStartPluginInstance()
2903
0
{
2904
0
  // OK to have an instance already or a pending spawn.
2905
0
  if (mInstanceOwner || mPendingInstantiateEvent) {
2906
0
    return NS_OK;
2907
0
  }
2908
0
2909
0
  nsCOMPtr<nsIContent> thisContent =
2910
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2911
0
  nsIDocument* doc = thisContent->OwnerDoc();
2912
0
  if (doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) {
2913
0
    return NS_OK;
2914
0
  }
2915
0
2916
0
  nsCOMPtr<nsIRunnable> event = new nsAsyncInstantiateEvent(this);
2917
0
  nsresult rv = NS_DispatchToCurrentThread(event);
2918
0
  if (NS_SUCCEEDED(rv)) {
2919
0
    // Track pending events
2920
0
    mPendingInstantiateEvent = event;
2921
0
  }
2922
0
2923
0
  return rv;
2924
0
}
2925
2926
NS_IMETHODIMP
2927
nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
2928
0
{
2929
0
  NS_IF_ADDREF(*aURI = GetSrcURI());
2930
0
  return NS_OK;
2931
0
}
2932
2933
void
2934
0
nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
2935
0
  EventStates oldState = ObjectState();
2936
0
  ObjectType oldType = mType;
2937
0
2938
0
  NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel,
2939
0
               "LoadFallback called with loaded content");
2940
0
2941
0
  //
2942
0
  // Fixup mFallbackType
2943
0
  //
2944
0
  nsCOMPtr<nsIContent> thisContent =
2945
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2946
0
  NS_ASSERTION(thisContent, "must be a content");
2947
0
2948
0
  if (!thisContent->IsHTMLElement() || mContentType.IsEmpty()) {
2949
0
    // Don't let custom fallback handlers run outside HTML, tags without a
2950
0
    // determined type should always just be alternate content
2951
0
    aType = eFallbackAlternate;
2952
0
  }
2953
0
2954
0
  // We'll set this to null no matter what now, doing it here means we'll load
2955
0
  // child embeds as we find them in the upcoming loop.
2956
0
  mType = eType_Null;
2957
0
2958
0
  bool thisIsObject = thisContent->IsHTMLElement(nsGkAtoms::object);
2959
0
2960
0
  // Do a depth-first traverse of node tree with the current element as root,
2961
0
  // looking for <embed> or <object> elements that might now need to load.
2962
0
  nsTArray<nsINodeList*> childNodes;
2963
0
  if (thisContent->IsHTMLElement(nsGkAtoms::object) &&
2964
0
      (aType == eFallbackUnsupported ||
2965
0
       aType == eFallbackDisabled ||
2966
0
       aType == eFallbackBlocklisted ||
2967
0
       aType == eFallbackAlternate))
2968
0
  {
2969
0
    for (nsIContent* child = thisContent->GetFirstChild(); child; ) {
2970
0
      // When we advance to our next child, we don't want to traverse subtrees
2971
0
      // under descendant <object> and <embed> elements; those will handle
2972
0
      // those subtrees themselves if they end up falling back.
2973
0
      bool skipChildDescendants = false;
2974
0
      if (aType != eFallbackAlternate &&
2975
0
          !child->IsHTMLElement(nsGkAtoms::param) &&
2976
0
          nsStyleUtil::IsSignificantChild(child, false)) {
2977
0
        aType = eFallbackAlternate;
2978
0
      }
2979
0
      if (thisIsObject) {
2980
0
        if (auto embed = HTMLEmbedElement::FromNode(child)) {
2981
0
          embed->StartObjectLoad(true, true);
2982
0
          skipChildDescendants = true;
2983
0
        } else if (auto object = HTMLObjectElement::FromNode(child)) {
2984
0
          object->StartObjectLoad(true, true);
2985
0
          skipChildDescendants = true;
2986
0
        }
2987
0
      }
2988
0
2989
0
      if (skipChildDescendants) {
2990
0
        child = child->GetNextNonChildNode(thisContent);
2991
0
      } else {
2992
0
        child = child->GetNextNode(thisContent);
2993
0
      }
2994
0
    }
2995
0
  }
2996
0
2997
0
  mFallbackType = aType;
2998
0
2999
0
  // Notify
3000
0
  if (!aNotify) {
3001
0
    return; // done
3002
0
  }
3003
0
3004
0
  NotifyStateChanged(oldType, oldState, false, true);
3005
0
}
3006
3007
void
3008
nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner)
3009
0
{
3010
0
  // DoStopPlugin can process events -- There may be pending
3011
0
  // CheckPluginStopEvent events which can drop in underneath us and destroy the
3012
0
  // instance we are about to destroy. We prevent that with the mIsStopping
3013
0
  // flag.
3014
0
  if (mIsStopping) {
3015
0
    return;
3016
0
  }
3017
0
  mIsStopping = true;
3018
0
3019
0
  RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
3020
0
  if (mType == eType_FakePlugin) {
3021
0
    if (mFrameLoader) {
3022
0
      mFrameLoader->Destroy();
3023
0
      mFrameLoader = nullptr;
3024
0
    }
3025
0
  } else {
3026
0
    RefPtr<nsNPAPIPluginInstance> inst;
3027
0
    aInstanceOwner->GetInstance(getter_AddRefs(inst));
3028
0
    if (inst) {
3029
#if defined(XP_MACOSX)
3030
      aInstanceOwner->HidePluginWindow();
3031
#endif
3032
3033
0
      RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
3034
0
      NS_ASSERTION(pluginHost, "No plugin host?");
3035
0
      pluginHost->StopPluginInstance(inst);
3036
0
    }
3037
0
  }
3038
0
3039
0
  aInstanceOwner->Destroy();
3040
0
3041
0
  // If we re-enter in plugin teardown UnloadObject will tear down the
3042
0
  // protochain -- the current protochain could be from a new, unrelated, load.
3043
0
  if (!mIsStopping) {
3044
0
    LOG(("OBJLC [%p]: Re-entered in plugin teardown", this));
3045
0
    return;
3046
0
  }
3047
0
3048
0
  TeardownProtoChain();
3049
0
  mIsStopping = false;
3050
0
}
3051
3052
NS_IMETHODIMP
3053
nsObjectLoadingContent::StopPluginInstance()
3054
0
{
3055
0
  AUTO_PROFILER_LABEL("nsObjectLoadingContent::StopPluginInstance", OTHER);
3056
0
  // Clear any pending events
3057
0
  mPendingInstantiateEvent = nullptr;
3058
0
  mPendingCheckPluginStopEvent = nullptr;
3059
0
3060
0
  // If we're currently instantiating, clearing this will cause
3061
0
  // InstantiatePluginInstance's re-entrance check to destroy the created plugin
3062
0
  mInstantiating = false;
3063
0
3064
0
  if (!mInstanceOwner) {
3065
0
    return NS_OK;
3066
0
  }
3067
0
3068
0
  if (mChannel) {
3069
0
    // The plugin has already used data from this channel, we'll need to
3070
0
    // re-open it to handle instantiating again, even if we don't invalidate
3071
0
    // our loaded state.
3072
0
    /// XXX(johns): Except currently, we don't, just leaving re-opening channels
3073
0
    ///             to plugins...
3074
0
    LOG(("OBJLC [%p]: StopPluginInstance - Closing used channel", this));
3075
0
    CloseChannel();
3076
0
  }
3077
0
3078
0
  // We detach the instance owner's frame before destruction, but don't destroy
3079
0
  // the instance owner until the plugin is stopped.
3080
0
  mInstanceOwner->SetFrame(nullptr);
3081
0
3082
0
  RefPtr<nsPluginInstanceOwner> ownerGrip(mInstanceOwner);
3083
0
  mInstanceOwner = nullptr;
3084
0
3085
0
  // This can/will re-enter
3086
0
  DoStopPlugin(ownerGrip);
3087
0
3088
0
  return NS_OK;
3089
0
}
3090
3091
void
3092
nsObjectLoadingContent::NotifyContentObjectWrapper()
3093
0
{
3094
0
  nsCOMPtr<nsIContent> thisContent =
3095
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3096
0
3097
0
  AutoJSAPI jsapi;
3098
0
  jsapi.Init();
3099
0
  JSContext* cx = jsapi.cx();
3100
0
3101
0
  JS::Rooted<JSObject*> obj(cx, thisContent->GetWrapper());
3102
0
  if (!obj) {
3103
0
    // Nothing to do here if there's no wrapper for mContent. The proto
3104
0
    // chain will be fixed appropriately when the wrapper is created.
3105
0
    return;
3106
0
  }
3107
0
3108
0
  SetupProtoChain(cx, obj);
3109
0
}
3110
3111
void
3112
nsObjectLoadingContent::PlayPlugin(SystemCallerGuarantee, ErrorResult& aRv)
3113
0
{
3114
0
  // This is a ChromeOnly method, so no need to check caller type here.
3115
0
  if (!mActivated) {
3116
0
    mActivated = true;
3117
0
    LOG(("OBJLC [%p]: Activated by user", this));
3118
0
  }
3119
0
3120
0
  // If we're in a click-to-play state, reload.
3121
0
  // Fallback types >= eFallbackClickToPlay are plugin-replacement types, see
3122
0
  // header
3123
0
  if (mType == eType_Null && mFallbackType >= eFallbackClickToPlay) {
3124
0
    aRv = LoadObject(true, true);
3125
0
  }
3126
0
}
3127
3128
NS_IMETHODIMP
3129
nsObjectLoadingContent::Reload(bool aClearActivation)
3130
0
{
3131
0
  if (aClearActivation) {
3132
0
    mActivated = false;
3133
0
    mSkipFakePlugins = false;
3134
0
  }
3135
0
3136
0
  return LoadObject(true, true);
3137
0
}
3138
3139
NS_IMETHODIMP
3140
nsObjectLoadingContent::GetActivated(bool *aActivated)
3141
0
{
3142
0
  *aActivated = Activated();
3143
0
  return NS_OK;
3144
0
}
3145
3146
uint32_t
3147
nsObjectLoadingContent::DefaultFallbackType()
3148
0
{
3149
0
  FallbackType reason;
3150
0
  if (ShouldPlay(reason)) {
3151
0
    return PLUGIN_ACTIVE;
3152
0
  }
3153
0
  return reason;
3154
0
}
3155
3156
NS_IMETHODIMP
3157
nsObjectLoadingContent::SkipFakePlugins()
3158
0
{
3159
0
  if (!nsContentUtils::IsCallerChrome())
3160
0
    return NS_ERROR_NOT_AVAILABLE;
3161
0
3162
0
  mSkipFakePlugins = true;
3163
0
3164
0
  // If we're showing a fake plugin now, reload
3165
0
  if (mType == eType_FakePlugin) {
3166
0
    return LoadObject(true, true);
3167
0
  }
3168
0
3169
0
  return NS_OK;
3170
0
}
3171
3172
uint32_t
3173
nsObjectLoadingContent::GetRunID(SystemCallerGuarantee, ErrorResult& aRv)
3174
0
{
3175
0
  if (!mHasRunID) {
3176
0
    // The plugin instance must not have a run ID, so we must
3177
0
    // be running the plugin in-process.
3178
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
3179
0
    return 0;
3180
0
  }
3181
0
  return mRunID;
3182
0
}
3183
3184
static bool sPrefsInitialized;
3185
static uint32_t sSessionTimeoutMinutes;
3186
static uint32_t sPersistentTimeoutDays;
3187
static bool sBlockURIs;
3188
3189
static void initializeObjectLoadingContentPrefs()
3190
0
{
3191
0
  if (!sPrefsInitialized) {
3192
0
    Preferences::AddUintVarCache(&sSessionTimeoutMinutes,
3193
0
                                 "plugin.sessionPermissionNow.intervalInMinutes", 60);
3194
0
    Preferences::AddUintVarCache(&sPersistentTimeoutDays,
3195
0
                                 "plugin.persistentPermissionAlways.intervalInDays", 90);
3196
0
3197
0
    Preferences::AddBoolVarCache(&sBlockURIs, kPrefBlockURIs, false);
3198
0
    sPrefsInitialized = true;
3199
0
  }
3200
0
}
3201
3202
bool
3203
nsObjectLoadingContent::ShouldBlockContent()
3204
0
{
3205
0
3206
0
  if (!sPrefsInitialized) {
3207
0
    initializeObjectLoadingContentPrefs();
3208
0
  }
3209
0
3210
0
  if (mContentBlockingEnabled && mURI && IsFlashMIME(mContentType) && sBlockURIs ) {
3211
0
    return true;
3212
0
  }
3213
0
3214
0
  return false;
3215
0
}
3216
3217
bool
3218
nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
3219
0
{
3220
0
  nsresult rv;
3221
0
3222
0
  if (!sPrefsInitialized) {
3223
0
    initializeObjectLoadingContentPrefs();
3224
0
  }
3225
0
3226
0
  if (BrowserTabsRemoteAutostart()) {
3227
0
    bool shouldLoadInParent = nsPluginHost::ShouldLoadTypeInParent(mContentType);
3228
0
    bool inParent = XRE_IsParentProcess();
3229
0
3230
0
    if (shouldLoadInParent != inParent) {
3231
0
      // Plugins need to be locked to either the parent process or the content
3232
0
      // process. If a plugin is locked to one process type, it can't be used in
3233
0
      // the other. Otherwise we'll get hangs.
3234
0
      aReason = eFallbackDisabled;
3235
0
      return false;
3236
0
    }
3237
0
  }
3238
0
3239
0
  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
3240
0
3241
0
  // Order of checks:
3242
0
  // * Assume a default of click-to-play
3243
0
  // * If globally disabled, per-site permissions cannot override.
3244
0
  // * If blocklisted, override the reason with the blocklist reason
3245
0
  // * Check if the flash blocking status for this page denies flash from loading.
3246
0
  // * Check per-site permissions and follow those if specified.
3247
0
  // * Honor per-plugin disabled permission
3248
0
  // * Blocklisted plugins are forced to CtP
3249
0
  // * Check per-plugin permission and follow that.
3250
0
3251
0
  aReason = eFallbackClickToPlay;
3252
0
3253
0
  uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
3254
0
  pluginHost->GetStateForType(mContentType, nsPluginHost::eExcludeNone,
3255
0
                              &enabledState);
3256
0
  if (nsIPluginTag::STATE_DISABLED == enabledState) {
3257
0
    aReason = eFallbackDisabled;
3258
0
    return false;
3259
0
  }
3260
0
3261
0
  // Before we check permissions, get the blocklist state of this plugin to set
3262
0
  // the fallback reason correctly. In the content process this will involve
3263
0
  // an ipc call to chrome.
3264
0
  uint32_t blocklistState = nsIBlocklistService::STATE_BLOCKED;
3265
0
  pluginHost->GetBlocklistStateForType(mContentType,
3266
0
                                       nsPluginHost::eExcludeNone,
3267
0
                                       &blocklistState);
3268
0
  if (blocklistState == nsIBlocklistService::STATE_BLOCKED) {
3269
0
    // no override possible
3270
0
    aReason = eFallbackBlocklisted;
3271
0
    return false;
3272
0
  }
3273
0
3274
0
  if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
3275
0
    aReason = eFallbackVulnerableUpdatable;
3276
0
  } else if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
3277
0
    aReason = eFallbackVulnerableNoUpdate;
3278
0
  }
3279
0
3280
0
  // Document and window lookup
3281
0
  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
3282
0
  MOZ_ASSERT(thisContent);
3283
0
  nsIDocument* ownerDoc = thisContent->OwnerDoc();
3284
0
3285
0
  nsCOMPtr<nsPIDOMWindowOuter> window = ownerDoc->GetWindow();
3286
0
  if (!window) {
3287
0
    return false;
3288
0
  }
3289
0
  nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetTop();
3290
0
  NS_ENSURE_TRUE(topWindow, false);
3291
0
  nsCOMPtr<nsIDocument> topDoc = topWindow->GetDoc();
3292
0
  NS_ENSURE_TRUE(topDoc, false);
3293
0
3294
0
  // Check the flash blocking status for this page (this applies to Flash only)
3295
0
  FlashClassification documentClassification = FlashClassification::Unknown;
3296
0
  if (IsFlashMIME(mContentType)) {
3297
0
    documentClassification = ownerDoc->DocumentFlashClassification();
3298
0
  }
3299
0
  if (documentClassification == FlashClassification::Denied) {
3300
0
    aReason = eFallbackSuppressed;
3301
0
    return false;
3302
0
  }
3303
0
3304
0
  // Check the permission manager for permission based on the principal of
3305
0
  // the toplevel content.
3306
0
  nsCOMPtr<nsIPermissionManager> permissionManager = services::GetPermissionManager();
3307
0
  NS_ENSURE_TRUE(permissionManager, false);
3308
0
3309
0
  // For now we always say that the system principal uses click-to-play since
3310
0
  // that maintains current behavior and we have tests that expect this.  What
3311
0
  // we really should do is disable plugins entirely in pages that use the
3312
0
  // system principal, i.e. in chrome pages. That way the click-to-play code
3313
0
  // here wouldn't matter at all. Bug 775301 is tracking this.
3314
0
  if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
3315
0
    nsAutoCString permissionString;
3316
0
    rv = pluginHost->GetPermissionStringForType(mContentType,
3317
0
                                                nsPluginHost::eExcludeNone,
3318
0
                                                permissionString);
3319
0
    NS_ENSURE_SUCCESS(rv, false);
3320
0
    uint32_t permission;
3321
0
    rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
3322
0
                                                        permissionString.Data(),
3323
0
                                                        &permission);
3324
0
    NS_ENSURE_SUCCESS(rv, false);
3325
0
    if (permission != nsIPermissionManager::UNKNOWN_ACTION) {
3326
0
      uint64_t nowms = PR_Now() / 1000;
3327
0
      permissionManager->UpdateExpireTime(
3328
0
        topDoc->NodePrincipal(), permissionString.Data(), false,
3329
0
        nowms + sSessionTimeoutMinutes * 60 * 1000,
3330
0
        nowms / 1000 + uint64_t(sPersistentTimeoutDays) * 24 * 60 * 60 * 1000);
3331
0
    }
3332
0
    switch (permission) {
3333
0
    case nsIPermissionManager::ALLOW_ACTION:
3334
0
      if (PreferFallback(false /* isPluginClickToPlay */)) {
3335
0
        aReason = eFallbackAlternate;
3336
0
        return false;
3337
0
      }
3338
0
3339
0
      return true;
3340
0
    case nsIPermissionManager::DENY_ACTION:
3341
0
      aReason = eFallbackDisabled;
3342
0
      return false;
3343
0
    case PLUGIN_PERMISSION_PROMPT_ACTION_QUIET:
3344
0
      if (PreferFallback(true /* isPluginClickToPlay */)) {
3345
0
        aReason = eFallbackAlternate;
3346
0
      } else {
3347
0
        aReason = eFallbackClickToPlayQuiet;
3348
0
      }
3349
0
3350
0
      return false;
3351
0
    case nsIPermissionManager::PROMPT_ACTION:
3352
0
      if (PreferFallback(true /* isPluginClickToPlay */)) {
3353
0
        // False is already returned in this case, but
3354
0
        // it's important to correctly set aReason too.
3355
0
        aReason = eFallbackAlternate;
3356
0
      }
3357
0
3358
0
      return false;
3359
0
    case nsIPermissionManager::UNKNOWN_ACTION:
3360
0
      break;
3361
0
    default:
3362
0
      MOZ_ASSERT(false);
3363
0
      return false;
3364
0
    }
3365
0
  }
3366
0
3367
0
  // No site-specific permissions. Vulnerable plugins are automatically CtP
3368
0
  if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
3369
0
      blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
3370
0
    return false;
3371
0
  }
3372
0
3373
0
  if (PreferFallback(enabledState == nsIPluginTag::STATE_CLICKTOPLAY)) {
3374
0
    aReason = eFallbackAlternate;
3375
0
    return false;
3376
0
  }
3377
0
3378
0
  // On the following switch we don't need to handle the case where
3379
0
  // documentClassification is FlashClassification::Denied because
3380
0
  // that's already handled above.
3381
0
  switch (enabledState) {
3382
0
  case nsIPluginTag::STATE_ENABLED:
3383
0
    return true;
3384
0
  case nsIPluginTag::STATE_CLICKTOPLAY:
3385
0
    if (documentClassification == FlashClassification::Allowed) {
3386
0
      return true;
3387
0
    }
3388
0
    return false;
3389
0
  }
3390
0
  MOZ_CRASH("Unexpected enabledState");
3391
0
}
3392
3393
bool
3394
0
nsObjectLoadingContent::FavorFallbackMode(bool aIsPluginClickToPlay) {
3395
0
  if (!IsFlashMIME(mContentType)) {
3396
0
    return false;
3397
0
  }
3398
0
3399
0
  nsAutoCString prefString;
3400
0
  if (NS_SUCCEEDED(Preferences::GetCString(kPrefFavorFallbackMode, prefString))) {
3401
0
    if (aIsPluginClickToPlay &&
3402
0
        prefString.EqualsLiteral("follow-ctp")) {
3403
0
      return true;
3404
0
    }
3405
0
3406
0
    if (prefString.EqualsLiteral("always")) {
3407
0
      return true;
3408
0
    }
3409
0
  }
3410
0
3411
0
  return false;
3412
0
}
3413
3414
bool
3415
0
nsObjectLoadingContent::HasGoodFallback() {
3416
0
  nsCOMPtr<nsIContent> thisContent =
3417
0
  do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3418
0
  NS_ASSERTION(thisContent, "must be a content");
3419
0
3420
0
  if (!thisContent->IsHTMLElement(nsGkAtoms::object) ||
3421
0
      mContentType.IsEmpty()) {
3422
0
    return false;
3423
0
  }
3424
0
3425
0
  nsTArray<nsCString> rulesList;
3426
0
  nsAutoCString prefString;
3427
0
  if (NS_SUCCEEDED(Preferences::GetCString(kPrefFavorFallbackRules, prefString))) {
3428
0
      ParseString(prefString, ',', rulesList);
3429
0
  }
3430
0
3431
0
  for (uint32_t i = 0; i < rulesList.Length(); ++i) {
3432
0
    // RULE "embed":
3433
0
    // Don't use fallback content if the object contains an <embed> inside its
3434
0
    // fallback content.
3435
0
    if (rulesList[i].EqualsLiteral("embed")) {
3436
0
      nsTArray<nsINodeList*> childNodes;
3437
0
      for (nsIContent* child = thisContent->GetFirstChild();
3438
0
           child;
3439
0
           child = child->GetNextNode(thisContent)) {
3440
0
        if (child->IsHTMLElement(nsGkAtoms::embed)) {
3441
0
          return false;
3442
0
        }
3443
0
      }
3444
0
    }
3445
0
3446
0
    // RULE "video":
3447
0
    // Use fallback content if the object contains a <video> inside its
3448
0
    // fallback content.
3449
0
    if (rulesList[i].EqualsLiteral("video")) {
3450
0
      nsTArray<nsINodeList*> childNodes;
3451
0
      for (nsIContent* child = thisContent->GetFirstChild();
3452
0
           child;
3453
0
           child = child->GetNextNode(thisContent)) {
3454
0
        if (child->IsHTMLElement(nsGkAtoms::video)) {
3455
0
          return true;
3456
0
        }
3457
0
      }
3458
0
    }
3459
0
3460
0
    // RULE "nosrc":
3461
0
    // Use fallback content if the object has not specified an URI.
3462
0
    if (rulesList[i].EqualsLiteral("nosrc")) {
3463
0
      if (!mOriginalURI) {
3464
0
        return true;
3465
0
      }
3466
0
    }
3467
0
3468
0
    // RULE "adobelink":
3469
0
    // Don't use fallback content when it has a link to adobe's website.
3470
0
    if (rulesList[i].EqualsLiteral("adobelink")) {
3471
0
      nsTArray<nsINodeList*> childNodes;
3472
0
      for (nsIContent* child = thisContent->GetFirstChild();
3473
0
           child;
3474
0
           child = child->GetNextNode(thisContent)) {
3475
0
        if (child->IsHTMLElement(nsGkAtoms::a)) {
3476
0
          nsCOMPtr<nsIURI> href = child->GetHrefURI();
3477
0
          if (href) {
3478
0
            nsAutoCString asciiHost;
3479
0
            nsresult rv = href->GetAsciiHost(asciiHost);
3480
0
            if (NS_SUCCEEDED(rv) &&
3481
0
                !asciiHost.IsEmpty() &&
3482
0
                (asciiHost.EqualsLiteral("adobe.com") ||
3483
0
                 StringEndsWith(asciiHost, NS_LITERAL_CSTRING(".adobe.com")))) {
3484
0
              return false;
3485
0
            }
3486
0
          }
3487
0
        }
3488
0
      }
3489
0
    }
3490
0
3491
0
    // RULE "installinstructions":
3492
0
    // Don't use fallback content when the text content on the fallback appears
3493
0
    // to contain instructions to install or download Flash.
3494
0
    if (rulesList[i].EqualsLiteral("installinstructions")) {
3495
0
      nsAutoString textContent;
3496
0
      ErrorResult rv;
3497
0
      thisContent->GetTextContent(textContent, rv);
3498
0
      bool hasText =
3499
0
        !rv.Failed() &&
3500
0
        (CaseInsensitiveFindInReadable(NS_LITERAL_STRING("Flash"), textContent) ||
3501
0
         CaseInsensitiveFindInReadable(NS_LITERAL_STRING("Install"), textContent) ||
3502
0
         CaseInsensitiveFindInReadable(NS_LITERAL_STRING("Download"), textContent));
3503
0
3504
0
      if (hasText) {
3505
0
        return false;
3506
0
      }
3507
0
    }
3508
0
3509
0
    // RULE "true":
3510
0
    // By having a rule that returns true, we can put it at the end of the rules list
3511
0
    // to change the default-to-false behavior to be default-to-true.
3512
0
    if (rulesList[i].EqualsLiteral("true")) {
3513
0
      return true;
3514
0
    }
3515
0
  }
3516
0
3517
0
  return false;
3518
0
}
3519
3520
bool
3521
0
nsObjectLoadingContent::PreferFallback(bool aIsPluginClickToPlay) {
3522
0
  if (mPreferFallbackKnown) {
3523
0
    return mPreferFallback;
3524
0
  }
3525
0
3526
0
  mPreferFallbackKnown = true;
3527
0
  mPreferFallback = FavorFallbackMode(aIsPluginClickToPlay) && HasGoodFallback();
3528
0
  return mPreferFallback;
3529
0
}
3530
3531
nsIDocument*
3532
nsObjectLoadingContent::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
3533
0
{
3534
0
  nsCOMPtr<nsIContent> thisContent =
3535
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3536
0
3537
0
  if (!thisContent->IsInComposedDoc()) {
3538
0
    return nullptr;
3539
0
  }
3540
0
3541
0
  nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
3542
0
  if (!sub_doc) {
3543
0
    return nullptr;
3544
0
  }
3545
0
3546
0
  // Return null for cross-origin contentDocument.
3547
0
  if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
3548
0
    return nullptr;
3549
0
  }
3550
0
3551
0
  return sub_doc;
3552
0
}
3553
3554
void
3555
nsObjectLoadingContent::SetupProtoChain(JSContext* aCx,
3556
                                        JS::Handle<JSObject*> aObject)
3557
0
{
3558
0
  if (mType != eType_Plugin) {
3559
0
    return;
3560
0
  }
3561
0
3562
0
  if (!nsContentUtils::IsSafeToRunScript()) {
3563
0
    RefPtr<SetupProtoChainRunner> runner = new SetupProtoChainRunner(this);
3564
0
    nsContentUtils::AddScriptRunner(runner);
3565
0
    return;
3566
0
  }
3567
0
3568
0
  // We get called on random realms here for some reason
3569
0
  // (perhaps because WrapObject can happen on a random realm?)
3570
0
  // so make sure to enter the realm of aObject.
3571
0
  MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
3572
0
3573
0
  MOZ_ASSERT(IsDOMObject(aObject));
3574
0
  JSAutoRealm ar(aCx, aObject);
3575
0
3576
0
  RefPtr<nsNPAPIPluginInstance> pi;
3577
0
  nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3578
0
  if (NS_FAILED(rv)) {
3579
0
    return;
3580
0
  }
3581
0
3582
0
  if (!pi) {
3583
0
    // No plugin around for this object.
3584
0
    return;
3585
0
  }
3586
0
3587
0
  JS::Rooted<JSObject*> pi_obj(aCx); // XPConnect-wrapped peer object, when we get it.
3588
0
  JS::Rooted<JSObject*> pi_proto(aCx); // 'pi.__proto__'
3589
0
3590
0
  rv = GetPluginJSObject(aCx, pi, &pi_obj, &pi_proto);
3591
0
  if (NS_FAILED(rv)) {
3592
0
    return;
3593
0
  }
3594
0
3595
0
  if (!pi_obj) {
3596
0
    // Didn't get a plugin instance JSObject, nothing we can do then.
3597
0
    return;
3598
0
  }
3599
0
3600
0
  // If we got an xpconnect-wrapped plugin object, set obj's
3601
0
  // prototype's prototype to the scriptable plugin.
3602
0
3603
0
  JS::Handle<JSObject*> my_proto = GetDOMClass(aObject)->mGetProto(aCx);
3604
0
  MOZ_ASSERT(my_proto);
3605
0
3606
0
  // Set 'this.__proto__' to pi
3607
0
  if (!::JS_SetPrototype(aCx, aObject, pi_obj)) {
3608
0
    return;
3609
0
  }
3610
0
3611
0
  if (pi_proto && js::GetObjectClass(pi_proto) != js::ObjectClassPtr) {
3612
0
    // The plugin wrapper has a proto that's not Object.prototype, set
3613
0
    // 'pi.__proto__.__proto__' to the original 'this.__proto__'
3614
0
    if (pi_proto != my_proto && !::JS_SetPrototype(aCx, pi_proto, my_proto)) {
3615
0
      return;
3616
0
    }
3617
0
  } else {
3618
0
    // 'pi' didn't have a prototype, or pi's proto was
3619
0
    // 'Object.prototype' (i.e. pi is an NPRuntime wrapped JS object)
3620
0
    // set 'pi.__proto__' to the original 'this.__proto__'
3621
0
    if (!::JS_SetPrototype(aCx, pi_obj, my_proto)) {
3622
0
      return;
3623
0
    }
3624
0
  }
3625
0
3626
0
  // Before this proto dance the objects involved looked like this:
3627
0
  //
3628
0
  // this.__proto__.__proto__
3629
0
  //   ^      ^         ^
3630
0
  //   |      |         |__ Object.prototype
3631
0
  //   |      |
3632
0
  //   |      |__ WebIDL prototype (shared)
3633
0
  //   |
3634
0
  //   |__ WebIDL object
3635
0
  //
3636
0
  // pi.__proto__
3637
0
  // ^      ^
3638
0
  // |      |__ Object.prototype or some other object
3639
0
  // |
3640
0
  // |__ Plugin NPRuntime JS object wrapper
3641
0
  //
3642
0
  // Now, after the above prototype setup the prototype chain should
3643
0
  // look like this if pi.__proto__ was Object.prototype:
3644
0
  //
3645
0
  // this.__proto__.__proto__.__proto__
3646
0
  //   ^      ^         ^         ^
3647
0
  //   |      |         |         |__ Object.prototype
3648
0
  //   |      |         |
3649
0
  //   |      |         |__ WebIDL prototype (shared)
3650
0
  //   |      |
3651
0
  //   |      |__ Plugin NPRuntime JS object wrapper
3652
0
  //   |
3653
0
  //   |__ WebIDL object
3654
0
  //
3655
0
  // or like this if pi.__proto__ was some other object:
3656
0
  //
3657
0
  // this.__proto__.__proto__.__proto__.__proto__
3658
0
  //   ^      ^         ^         ^         ^
3659
0
  //   |      |         |         |         |__ Object.prototype
3660
0
  //   |      |         |         |
3661
0
  //   |      |         |         |__ WebIDL prototype (shared)
3662
0
  //   |      |         |
3663
0
  //   |      |         |__ old pi.__proto__
3664
0
  //   |      |
3665
0
  //   |      |__ Plugin NPRuntime JS object wrapper
3666
0
  //   |
3667
0
  //   |__ WebIDL object
3668
0
  //
3669
0
}
3670
3671
// static
3672
nsresult
3673
nsObjectLoadingContent::GetPluginJSObject(JSContext *cx,
3674
                                          nsNPAPIPluginInstance *plugin_inst,
3675
                                          JS::MutableHandle<JSObject*> plugin_obj,
3676
                                          JS::MutableHandle<JSObject*> plugin_proto)
3677
0
{
3678
0
  if (plugin_inst) {
3679
0
    plugin_inst->GetJSObject(cx, plugin_obj.address());
3680
0
    if (plugin_obj) {
3681
0
      if (!::JS_GetPrototype(cx, plugin_obj, plugin_proto)) {
3682
0
        return NS_ERROR_UNEXPECTED;
3683
0
      }
3684
0
    }
3685
0
  }
3686
0
3687
0
  return NS_OK;
3688
0
}
3689
3690
void
3691
nsObjectLoadingContent::TeardownProtoChain()
3692
0
{
3693
0
  nsCOMPtr<nsIContent> thisContent =
3694
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3695
0
3696
0
  NS_ENSURE_TRUE_VOID(thisContent->GetWrapper());
3697
0
3698
0
  // We don't init the AutoJSAPI with our wrapper because we don't want it
3699
0
  // reporting errors to our window's onerror listeners.
3700
0
  AutoJSAPI jsapi;
3701
0
  jsapi.Init();
3702
0
  JSContext* cx = jsapi.cx();
3703
0
  JS::Rooted<JSObject*> obj(cx, thisContent->GetWrapper());
3704
0
  MOZ_ASSERT(obj);
3705
0
3706
0
  JS::Rooted<JSObject*> proto(cx);
3707
0
  JSAutoRealm ar(cx, obj);
3708
0
3709
0
  // Loop over the DOM element's JS object prototype chain and remove
3710
0
  // all JS objects of the class sNPObjectJSWrapperClass
3711
0
  DebugOnly<bool> removed = false;
3712
0
  while (obj) {
3713
0
    if (!::JS_GetPrototype(cx, obj, &proto)) {
3714
0
      return;
3715
0
    }
3716
0
    if (!proto) {
3717
0
      break;
3718
0
    }
3719
0
    // Unwrap while checking the class - if the prototype is a wrapper for
3720
0
    // an NP object, that counts too.
3721
0
    if (nsNPObjWrapper::IsWrapper(js::UncheckedUnwrap(proto))) {
3722
0
      // We found an NPObject on the proto chain, get its prototype...
3723
0
      if (!::JS_GetPrototype(cx, proto, &proto)) {
3724
0
        return;
3725
0
      }
3726
0
3727
0
      MOZ_ASSERT(!removed, "more than one NPObject in prototype chain");
3728
0
      removed = true;
3729
0
3730
0
      // ... and pull it out of the chain.
3731
0
      ::JS_SetPrototype(cx, obj, proto);
3732
0
    }
3733
0
3734
0
    obj = proto;
3735
0
  }
3736
0
}
3737
3738
bool
3739
nsObjectLoadingContent::DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
3740
                                  JS::Handle<jsid> aId,
3741
                                  JS::MutableHandle<JS::PropertyDescriptor> aDesc)
3742
0
{
3743
0
  // We don't resolve anything; we just try to make sure we're instantiated.
3744
0
  // This purposefully does not fire for chrome/xray resolves, see bug 967694
3745
0
3746
0
  RefPtr<nsNPAPIPluginInstance> pi;
3747
0
  nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3748
0
  if (NS_FAILED(rv)) {
3749
0
    return mozilla::dom::Throw(aCx, rv);
3750
0
  }
3751
0
  return true;
3752
0
}
3753
3754
/* static */
3755
bool
3756
nsObjectLoadingContent::MayResolve(jsid aId)
3757
0
{
3758
0
  // We can resolve anything, really.
3759
0
  return true;
3760
0
}
3761
3762
void
3763
nsObjectLoadingContent::GetOwnPropertyNames(JSContext* aCx,
3764
                                            JS::AutoIdVector& /* unused */,
3765
                                            bool /* unused */,
3766
                                            ErrorResult& aRv)
3767
0
{
3768
0
  // Just like DoResolve, just make sure we're instantiated.  That will do
3769
0
  // the work our Enumerate hook needs to do.  This purposefully does not fire
3770
0
  // for xray resolves, see bug 967694
3771
0
  RefPtr<nsNPAPIPluginInstance> pi;
3772
0
  aRv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
3773
0
}
3774
3775
void
3776
nsObjectLoadingContent::MaybeFireErrorEvent()
3777
0
{
3778
0
  nsCOMPtr<nsIContent> thisContent =
3779
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3780
0
  // Queue a task to fire an error event if we're an <object> element.  The
3781
0
  // queueing is important, since then we don't have to worry about reentry.
3782
0
  if (thisContent->IsHTMLElement(nsGkAtoms::object)) {
3783
0
    RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
3784
0
      new LoadBlockingAsyncEventDispatcher(thisContent,
3785
0
                                           NS_LITERAL_STRING("error"),
3786
0
                                           CanBubble::eNo,
3787
0
                                           ChromeOnlyDispatch::eNo);
3788
0
    loadBlockingAsyncDispatcher->PostDOMEvent();
3789
0
  }
3790
0
}
3791
3792
bool
3793
nsObjectLoadingContent::BlockEmbedOrObjectContentLoading()
3794
0
{
3795
0
  nsCOMPtr<nsIContent> thisContent =
3796
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
3797
0
3798
0
  // Traverse up the node tree to see if we have any ancestors that may block us
3799
0
  // from loading
3800
0
  for (nsIContent* parent = thisContent->GetParent();
3801
0
       parent;
3802
0
       parent = parent->GetParent()) {
3803
0
    if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
3804
0
      return true;
3805
0
    }
3806
0
    // If we have an ancestor that is an object with a source, it'll have an
3807
0
    // associated displayed type. If that type is not null, don't load content
3808
0
    // for the embed.
3809
0
    if (HTMLObjectElement* object = HTMLObjectElement::FromNode(parent)) {
3810
0
      uint32_t type = object->DisplayedType();
3811
0
      if (type != eType_Null) {
3812
0
        return true;
3813
0
      }
3814
0
    }
3815
0
  }
3816
0
  return false;
3817
0
}
3818
3819
// SetupProtoChainRunner implementation
3820
nsObjectLoadingContent::SetupProtoChainRunner::SetupProtoChainRunner(
3821
    nsObjectLoadingContent* aContent)
3822
  : mContent(aContent)
3823
0
{
3824
0
}
3825
3826
NS_IMETHODIMP
3827
nsObjectLoadingContent::SetupProtoChainRunner::Run()
3828
0
{
3829
0
  AutoJSAPI jsapi;
3830
0
  jsapi.Init();
3831
0
  JSContext* cx = jsapi.cx();
3832
0
3833
0
  nsCOMPtr<nsIContent> content;
3834
0
  CallQueryInterface(mContent.get(), getter_AddRefs(content));
3835
0
  JS::Rooted<JSObject*> obj(cx, content->GetWrapper());
3836
0
  if (!obj) {
3837
0
    // No need to set up our proto chain if we don't even have an object
3838
0
    return NS_OK;
3839
0
  }
3840
0
  nsObjectLoadingContent* objectLoadingContent =
3841
0
    static_cast<nsObjectLoadingContent*>(mContent.get());
3842
0
  objectLoadingContent->SetupProtoChain(cx, obj);
3843
0
  return NS_OK;
3844
0
}
3845
3846
NS_IMPL_ISUPPORTS(nsObjectLoadingContent::SetupProtoChainRunner, nsIRunnable)