Source code

Revision control

Other Tools

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 "mozilla/BasePrincipal.h"
20
#include "mozilla/dom/BindContext.h"
21
#include "mozilla/dom/Document.h"
22
#include "nsIExternalProtocolHandler.h"
23
#include "nsIInterfaceRequestorUtils.h"
24
#include "nsIObjectFrame.h"
25
#include "nsIOService.h"
26
#include "nsIPermissionManager.h"
27
#include "nsPluginHost.h"
28
#include "nsPluginInstanceOwner.h"
29
#include "nsJSNPRuntime.h"
30
#include "nsINestedURI.h"
31
#include "nsScriptSecurityManager.h"
32
#include "nsIURILoader.h"
33
#include "nsIURL.h"
34
#include "nsIScriptChannel.h"
35
#include "nsIBlocklistService.h"
36
#include "nsIAsyncVerifyRedirectCallback.h"
37
#include "nsIAppShell.h"
38
#include "nsIXULRuntime.h"
39
#include "nsIScriptError.h"
40
41
#include "nsError.h"
42
43
// Util headers
44
#include "prenv.h"
45
#include "mozilla/Logging.h"
46
47
#include "nsCURILoader.h"
48
#include "nsContentPolicyUtils.h"
49
#include "nsContentUtils.h"
50
#include "nsDocShellCID.h"
51
#include "nsGkAtoms.h"
52
#include "nsThreadUtils.h"
53
#include "nsNetUtil.h"
54
#include "nsMimeTypes.h"
55
#include "nsStyleUtil.h"
56
#include "nsUnicharUtils.h"
57
#include "mozilla/Preferences.h"
58
#include "nsSandboxFlags.h"
59
60
// Concrete classes
61
#include "nsFrameLoader.h"
62
63
#include "nsObjectLoadingContent.h"
64
#include "mozAutoDocUpdate.h"
65
#include "GeckoProfiler.h"
66
#include "nsPluginFrame.h"
67
#include "nsWrapperCacheInlines.h"
68
#include "nsDOMJSUtils.h"
69
70
#include "nsWidgetsCID.h"
71
#include "nsContentCID.h"
72
#include "mozilla/BasicEvents.h"
73
#include "mozilla/Components.h"
74
#include "mozilla/dom/BindingUtils.h"
75
#include "mozilla/dom/Element.h"
76
#include "mozilla/dom/Event.h"
77
#include "mozilla/dom/ScriptSettings.h"
78
#include "mozilla/dom/PluginCrashedEvent.h"
79
#include "mozilla/AsyncEventDispatcher.h"
80
#include "mozilla/EventDispatcher.h"
81
#include "mozilla/EventStates.h"
82
#include "mozilla/IMEStateManager.h"
83
#include "mozilla/widget/IMEData.h"
84
#include "mozilla/IntegerPrintfMacros.h"
85
#include "mozilla/dom/HTMLObjectElementBinding.h"
86
#include "mozilla/dom/HTMLEmbedElement.h"
87
#include "mozilla/dom/HTMLObjectElement.h"
88
#include "mozilla/dom/UserActivation.h"
89
#include "mozilla/net/UrlClassifierFeatureFactory.h"
90
#include "mozilla/LoadInfo.h"
91
#include "mozilla/PresShell.h"
92
#include "nsChannelClassifier.h"
93
#include "nsFocusManager.h"
94
#include "ReferrerInfo.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 kPrefFavorFallbackMode[] = "plugins.favorfallback.mode";
107
static const char kPrefFavorFallbackRules[] = "plugins.favorfallback.rules";
108
109
using namespace mozilla;
110
using namespace mozilla::dom;
111
using namespace mozilla::net;
112
113
static LogModule* GetObjectLog() {
114
static LazyLogModule sLog("objlc");
115
return sLog;
116
}
117
118
#define LOG(args) MOZ_LOG(GetObjectLog(), mozilla::LogLevel::Debug, args)
119
#define LOG_ENABLED() MOZ_LOG_TEST(GetObjectLog(), mozilla::LogLevel::Debug)
120
121
static bool IsFlashMIME(const nsACString& aMIMEType) {
122
return nsPluginHost::GetSpecialType(aMIMEType) ==
123
nsPluginHost::eSpecialType_Flash;
124
}
125
126
static bool InActiveDocument(nsIContent* aContent) {
127
if (!aContent->IsInComposedDoc()) {
128
return false;
129
}
130
Document* doc = aContent->OwnerDoc();
131
return (doc && doc->IsActive());
132
}
133
134
static bool IsPluginType(nsObjectLoadingContent::ObjectType type) {
135
return type == nsObjectLoadingContent::eType_Plugin ||
136
type == nsObjectLoadingContent::eType_FakePlugin;
137
}
138
139
///
140
/// Runnables and helper classes
141
///
142
143
class nsAsyncInstantiateEvent : public Runnable {
144
public:
145
explicit nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
146
: Runnable("nsAsyncInstantiateEvent"), mContent(aContent) {}
147
148
~nsAsyncInstantiateEvent() override = default;
149
150
NS_IMETHOD Run() override;
151
152
private:
153
nsCOMPtr<nsIObjectLoadingContent> mContent;
154
};
155
156
NS_IMETHODIMP
157
nsAsyncInstantiateEvent::Run() {
158
nsObjectLoadingContent* objLC =
159
static_cast<nsObjectLoadingContent*>(mContent.get());
160
161
// If objLC is no longer tracking this event, we've been canceled or
162
// superseded
163
if (objLC->mPendingInstantiateEvent != this) {
164
return NS_OK;
165
}
166
objLC->mPendingInstantiateEvent = nullptr;
167
168
return objLC->SyncStartPluginInstance();
169
}
170
171
// Checks to see if the content for a plugin instance should be unloaded
172
// (outside an active document) or stopped (in a document but unrendered). This
173
// is used to allow scripts to move a plugin around the document hierarchy
174
// without re-instantiating it.
175
class CheckPluginStopEvent : public Runnable {
176
public:
177
explicit CheckPluginStopEvent(nsObjectLoadingContent* aContent)
178
: Runnable("CheckPluginStopEvent"), mContent(aContent) {}
179
180
~CheckPluginStopEvent() override = default;
181
182
NS_IMETHOD Run() override;
183
184
private:
185
nsCOMPtr<nsIObjectLoadingContent> mContent;
186
};
187
188
NS_IMETHODIMP
189
CheckPluginStopEvent::Run() {
190
nsObjectLoadingContent* objLC =
191
static_cast<nsObjectLoadingContent*>(mContent.get());
192
193
// If objLC is no longer tracking this event, we've been canceled or
194
// superseded. We clear this before we finish - either by calling
195
// UnloadObject/StopPluginInstance, or directly if we took no action.
196
if (objLC->mPendingCheckPluginStopEvent != this) {
197
return NS_OK;
198
}
199
200
// CheckPluginStopEvent is queued when we either lose our frame, are removed
201
// from the document, or the document goes inactive. To avoid stopping the
202
// plugin when script is reparenting us or layout is rebuilding, we wait until
203
// this event to decide to stop.
204
205
nsCOMPtr<nsIContent> content =
206
do_QueryInterface(static_cast<nsIImageLoadingContent*>(objLC));
207
if (!InActiveDocument(content)) {
208
LOG(("OBJLC [%p]: Unloading plugin outside of document", this));
209
objLC->StopPluginInstance();
210
return NS_OK;
211
}
212
213
if (content->GetPrimaryFrame()) {
214
LOG(
215
("OBJLC [%p]: CheckPluginStopEvent - in active document with frame"
216
", no action",
217
this));
218
objLC->mPendingCheckPluginStopEvent = nullptr;
219
return NS_OK;
220
}
221
222
// In an active document, but still no frame. Flush layout to see if we can
223
// regain a frame now.
224
LOG(("OBJLC [%p]: CheckPluginStopEvent - No frame, flushing layout", this));
225
Document* composedDoc = content->GetComposedDoc();
226
if (composedDoc) {
227
composedDoc->FlushPendingNotifications(FlushType::Layout);
228
if (objLC->mPendingCheckPluginStopEvent != this) {
229
LOG(("OBJLC [%p]: CheckPluginStopEvent - superseded in layout flush",
230
this));
231
return NS_OK;
232
}
233
if (content->GetPrimaryFrame()) {
234
LOG(("OBJLC [%p]: CheckPluginStopEvent - frame gained in layout flush",
235
this));
236
objLC->mPendingCheckPluginStopEvent = nullptr;
237
return NS_OK;
238
}
239
}
240
241
// Still no frame, suspend plugin. HasNewFrame will restart us when we
242
// become rendered again
243
LOG(("OBJLC [%p]: Stopping plugin that lost frame", this));
244
objLC->StopPluginInstance();
245
246
return NS_OK;
247
}
248
249
/**
250
* Helper task for firing simple events
251
*/
252
class nsSimplePluginEvent : public Runnable {
253
public:
254
nsSimplePluginEvent(nsIContent* aTarget, const nsAString& aEvent)
255
: Runnable("nsSimplePluginEvent"),
256
mTarget(aTarget),
257
mDocument(aTarget->GetComposedDoc()),
258
mEvent(aEvent) {
259
MOZ_ASSERT(aTarget && mDocument);
260
}
261
262
nsSimplePluginEvent(Document* aTarget, const nsAString& aEvent)
263
: mozilla::Runnable("nsSimplePluginEvent"),
264
mTarget(ToSupports(aTarget)),
265
mDocument(aTarget),
266
mEvent(aEvent) {
267
MOZ_ASSERT(aTarget);
268
}
269
270
nsSimplePluginEvent(nsIContent* aTarget, Document* aDocument,
271
const nsAString& aEvent)
272
: mozilla::Runnable("nsSimplePluginEvent"),
273
mTarget(aTarget),
274
mDocument(aDocument),
275
mEvent(aEvent) {
276
MOZ_ASSERT(aTarget && aDocument);
277
}
278
279
~nsSimplePluginEvent() override = default;
280
281
NS_IMETHOD Run() override;
282
283
private:
284
nsCOMPtr<nsISupports> mTarget;
285
nsCOMPtr<Document> mDocument;
286
nsString mEvent;
287
};
288
289
NS_IMETHODIMP
290
nsSimplePluginEvent::Run() {
291
if (mDocument && mDocument->IsActive()) {
292
LOG(("OBJLC [%p]: nsSimplePluginEvent firing event \"%s\"", mTarget.get(),
293
NS_ConvertUTF16toUTF8(mEvent).get()));
294
nsContentUtils::DispatchTrustedEvent(mDocument, mTarget, mEvent,
295
CanBubble::eYes, Cancelable::eYes);
296
}
297
return NS_OK;
298
}
299
300
/**
301
* A task for firing PluginCrashed DOM Events.
302
*/
303
class nsPluginCrashedEvent : public Runnable {
304
public:
305
nsCOMPtr<nsIContent> mContent;
306
nsString mPluginDumpID;
307
nsString mPluginName;
308
nsString mPluginFilename;
309
bool mSubmittedCrashReport;
310
311
nsPluginCrashedEvent(nsIContent* aContent, const nsAString& aPluginDumpID,
312
const nsAString& aPluginName,
313
const nsAString& aPluginFilename,
314
bool submittedCrashReport)
315
: Runnable("nsPluginCrashedEvent"),
316
mContent(aContent),
317
mPluginDumpID(aPluginDumpID),
318
mPluginName(aPluginName),
319
mPluginFilename(aPluginFilename),
320
mSubmittedCrashReport(submittedCrashReport) {}
321
322
~nsPluginCrashedEvent() override = default;
323
324
NS_IMETHOD Run() override;
325
};
326
327
NS_IMETHODIMP
328
nsPluginCrashedEvent::Run() {
329
LOG(("OBJLC [%p]: Firing plugin crashed event\n", mContent.get()));
330
331
nsCOMPtr<Document> doc = mContent->GetComposedDoc();
332
if (!doc) {
333
NS_WARNING("Couldn't get document for PluginCrashed event!");
334
return NS_OK;
335
}
336
337
PluginCrashedEventInit init;
338
init.mPluginDumpID = mPluginDumpID;
339
init.mPluginName = mPluginName;
340
init.mPluginFilename = mPluginFilename;
341
init.mSubmittedCrashReport = mSubmittedCrashReport;
342
init.mBubbles = true;
343
init.mCancelable = true;
344
345
RefPtr<PluginCrashedEvent> event = PluginCrashedEvent::Constructor(
346
doc, NS_LITERAL_STRING("PluginCrashed"), init);
347
348
event->SetTrusted(true);
349
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
350
351
EventDispatcher::DispatchDOMEvent(mContent, nullptr, event, nullptr, nullptr);
352
return NS_OK;
353
}
354
355
// You can't take the address of bitfield members, so we have two separate
356
// classes for these :-/
357
358
// Sets a object's mInstantiating bit to false when destroyed
359
class AutoSetInstantiatingToFalse {
360
public:
361
explicit AutoSetInstantiatingToFalse(nsObjectLoadingContent* aContent)
362
: mContent(aContent) {}
363
~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
364
365
private:
366
nsObjectLoadingContent* mContent;
367
};
368
369
// Sets a object's mInstantiating bit to false when destroyed
370
class AutoSetLoadingToFalse {
371
public:
372
explicit AutoSetLoadingToFalse(nsObjectLoadingContent* aContent)
373
: mContent(aContent) {}
374
~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
375
376
private:
377
nsObjectLoadingContent* mContent;
378
};
379
380
///
381
/// Helper functions
382
///
383
384
static bool IsSuccessfulRequest(nsIRequest* aRequest, nsresult* aStatus) {
385
nsresult rv = aRequest->GetStatus(aStatus);
386
if (NS_FAILED(rv) || NS_FAILED(*aStatus)) {
387
return false;
388
}
389
390
// This may still be an error page or somesuch
391
nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
392
if (httpChan) {
393
bool success;
394
rv = httpChan->GetRequestSucceeded(&success);
395
if (NS_FAILED(rv) || !success) {
396
return false;
397
}
398
}
399
400
// Otherwise, the request is successful
401
return true;
402
}
403
404
static bool CanHandleURI(nsIURI* aURI) {
405
nsAutoCString scheme;
406
if (NS_FAILED(aURI->GetScheme(scheme))) {
407
return false;
408
}
409
410
nsIIOService* ios = nsContentUtils::GetIOService();
411
if (!ios) return false;
412
413
nsCOMPtr<nsIProtocolHandler> handler;
414
ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
415
if (!handler) {
416
return false;
417
}
418
419
nsCOMPtr<nsIExternalProtocolHandler> extHandler = do_QueryInterface(handler);
420
// We can handle this URI if its protocol handler is not the external one
421
return extHandler == nullptr;
422
}
423
424
// Helper for tedious URI equality syntax when one or both arguments may be
425
// null and URIEquals(null, null) should be true
426
static bool inline URIEquals(nsIURI* a, nsIURI* b) {
427
bool equal;
428
return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
429
}
430
431
static void GetExtensionFromURI(nsIURI* uri, nsCString& ext) {
432
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
433
if (url) {
434
url->GetFileExtension(ext);
435
} else {
436
nsCString spec;
437
nsresult rv = uri->GetSpec(spec);
438
if (NS_FAILED(rv)) {
439
// This means we could incorrectly think a plugin is not enabled for
440
// the URI when it is, but that's not so bad.
441
ext.Truncate();
442
return;
443
}
444
445
int32_t offset = spec.RFindChar('.');
446
if (offset != kNotFound) {
447
ext = Substring(spec, offset + 1, spec.Length());
448
}
449
}
450
}
451
452
/**
453
* Checks whether a plugin exists and is enabled for the extension
454
* in the given URI. The MIME type is returned in the mimeType out parameter.
455
*/
456
bool IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType) {
457
nsAutoCString ext;
458
GetExtensionFromURI(uri, ext);
459
460
if (ext.IsEmpty()) {
461
return false;
462
}
463
464
// Disables any native PDF plugins, when internal PDF viewer is enabled.
465
if (ext.EqualsIgnoreCase("pdf") && nsContentUtils::IsPDFJSEnabled()) {
466
return false;
467
}
468
469
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
470
471
if (!pluginHost) {
472
MOZ_ASSERT_UNREACHABLE("No pluginhost");
473
return false;
474
}
475
476
return pluginHost->HavePluginForExtension(ext, mimeType);
477
}
478
479
///
480
/// Member Functions
481
///
482
483
// Helper to queue a CheckPluginStopEvent for a OBJLC object
484
void nsObjectLoadingContent::QueueCheckPluginStopEvent() {
485
nsCOMPtr<nsIRunnable> event = new CheckPluginStopEvent(this);
486
mPendingCheckPluginStopEvent = event;
487
488
NS_DispatchToCurrentThread(event);
489
}
490
491
// Tedious syntax to create a plugin stream listener with checks and put it in
492
// mFinalListener
493
bool nsObjectLoadingContent::MakePluginListener() {
494
if (!mInstanceOwner) {
495
MOZ_ASSERT_UNREACHABLE("expecting a spawned plugin");
496
return false;
497
}
498
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
499
if (!pluginHost) {
500
MOZ_ASSERT_UNREACHABLE("No pluginHost");
501
return false;
502
}
503
NS_ASSERTION(!mFinalListener, "overwriting a final listener");
504
nsresult rv;
505
RefPtr<nsNPAPIPluginInstance> inst = mInstanceOwner->GetInstance();
506
nsCOMPtr<nsIStreamListener> finalListener;
507
rv = pluginHost->NewPluginStreamListener(mURI, inst,
508
getter_AddRefs(finalListener));
509
NS_ENSURE_SUCCESS(rv, false);
510
mFinalListener = finalListener;
511
return true;
512
}
513
514
// Helper to spawn the frameloader.
515
void nsObjectLoadingContent::SetupFrameLoader(int32_t aJSPluginId) {
516
nsCOMPtr<nsIContent> thisContent =
517
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
518
NS_ASSERTION(thisContent, "must be a content");
519
520
mFrameLoader =
521
nsFrameLoader::Create(thisContent->AsElement(),
522
/* aOpener = */ nullptr, mNetworkCreated);
523
MOZ_ASSERT(mFrameLoader, "nsFrameLoader::Create failed");
524
}
525
526
// Helper to spawn the frameloader and return a pointer to its docshell.
527
already_AddRefed<nsIDocShell> nsObjectLoadingContent::SetupDocShell(
528
nsIURI* aRecursionCheckURI) {
529
SetupFrameLoader(nsFakePluginTag::NOT_JSPLUGIN);
530
if (!mFrameLoader) {
531
return nullptr;
532
}
533
534
nsCOMPtr<nsIDocShell> docShell;
535
536
if (aRecursionCheckURI) {
537
nsresult rv = mFrameLoader->CheckForRecursiveLoad(aRecursionCheckURI);
538
if (NS_SUCCEEDED(rv)) {
539
IgnoredErrorResult result;
540
docShell = mFrameLoader->GetDocShell(result);
541
if (result.Failed()) {
542
MOZ_ASSERT_UNREACHABLE("Could not get DocShell from mFrameLoader?");
543
}
544
} else {
545
LOG(("OBJLC [%p]: Aborting recursive load", this));
546
}
547
}
548
549
if (!docShell) {
550
mFrameLoader->Destroy();
551
mFrameLoader = nullptr;
552
return nullptr;
553
}
554
555
return docShell.forget();
556
}
557
558
nsresult nsObjectLoadingContent::BindToTree(BindContext& aContext,
559
nsINode& aParent) {
560
nsImageLoadingContent::BindToTree(aContext, aParent);
561
// FIXME(emilio): Should probably use composed doc?
562
if (Document* doc = aContext.GetUncomposedDoc()) {
563
doc->AddPlugin(this);
564
}
565
return NS_OK;
566
}
567
568
void nsObjectLoadingContent::UnbindFromTree(bool aNullParent) {
569
nsImageLoadingContent::UnbindFromTree(aNullParent);
570
571
nsCOMPtr<Element> thisElement =
572
do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
573
MOZ_ASSERT(thisElement);
574
Document* ownerDoc = thisElement->OwnerDoc();
575
ownerDoc->RemovePlugin(this);
576
577
/// XXX(johns): Do we want to somehow propogate the reparenting behavior to
578
/// FakePlugin types as well?
579
if (mType == eType_Plugin && (mInstanceOwner || mInstantiating)) {
580
// we'll let the plugin continue to run at least until we get back to
581
// the event loop. If we get back to the event loop and the node
582
// has still not been added back to the document then we tear down the
583
// plugin
584
QueueCheckPluginStopEvent();
585
} else if (mType != eType_Image) {
586
// nsImageLoadingContent handles the image case.
587
// Reset state and clear pending events
588
/// XXX(johns): The implementation for GenericFrame notes that ideally we
589
/// would keep the docshell around, but trash the frameloader
590
UnloadObject();
591
}
592
593
// Unattach plugin problem UIWidget if any.
594
if (thisElement->IsInComposedDoc()) {
595
thisElement->NotifyUAWidgetTeardown();
596
}
597
598
if (mType == eType_Plugin) {
599
Document* doc = thisElement->GetComposedDoc();
600
if (doc && doc->IsActive()) {
601
nsCOMPtr<nsIRunnable> ev =
602
new nsSimplePluginEvent(doc, NS_LITERAL_STRING("PluginRemoved"));
603
NS_DispatchToCurrentThread(ev);
604
}
605
}
606
}
607
608
nsObjectLoadingContent::nsObjectLoadingContent()
609
: mType(eType_Loading),
610
mFallbackType(eFallbackAlternate),
611
mRunID(0),
612
mHasRunID(false),
613
mChannelLoaded(false),
614
mInstantiating(false),
615
mNetworkCreated(true),
616
mActivated(false),
617
mContentBlockingEnabled(false),
618
mSkipFakePlugins(false),
619
mIsStopping(false),
620
mIsLoading(false),
621
mScriptRequested(false),
622
mRewrittenYoutubeEmbed(false),
623
mPreferFallback(false),
624
mPreferFallbackKnown(false) {}
625
626
nsObjectLoadingContent::~nsObjectLoadingContent() {
627
// Should have been unbound from the tree at this point, and
628
// CheckPluginStopEvent keeps us alive
629
if (mFrameLoader) {
630
MOZ_ASSERT_UNREACHABLE(
631
"Should not be tearing down frame loaders at this point");
632
mFrameLoader->Destroy();
633
}
634
if (mInstanceOwner || mInstantiating) {
635
// This is especially bad as delayed stop will try to hold on to this
636
// object...
637
MOZ_ASSERT_UNREACHABLE(
638
"Should not be tearing down a plugin at this point!");
639
StopPluginInstance();
640
}
641
DestroyImageLoadingContent();
642
}
643
644
nsresult nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading) {
645
if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
646
mInstantiating) {
647
// If we hit this assertion it's probably because LoadObject re-entered :(
648
//
649
// XXX(johns): This hackiness will go away in bug 767635
650
NS_ASSERTION(mIsLoading || !aIsLoading,
651
"aIsLoading should only be true inside LoadObject");
652
return NS_OK;
653
}
654
655
mInstantiating = true;
656
AutoSetInstantiatingToFalse autoInstantiating(this);
657
658
nsCOMPtr<nsIContent> thisContent =
659
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
660
661
nsCOMPtr<Document> doc = thisContent->GetComposedDoc();
662
if (!doc || !InActiveDocument(thisContent)) {
663
NS_ERROR(
664
"Shouldn't be calling "
665
"InstantiatePluginInstance without an active document");
666
return NS_ERROR_FAILURE;
667
}
668
669
// Instantiating an instance can result in script execution, which
670
// can destroy this DOM object. Don't allow that for the scope
671
// of this method.
672
nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
673
674
// Flush layout so that the frame is created if possible and the plugin is
675
// initialized with the latest information.
676
doc->FlushPendingNotifications(FlushType::Layout);
677
// Flushing layout may have re-entered and loaded something underneath us
678
NS_ENSURE_TRUE(mInstantiating, NS_OK);
679
680
if (!thisContent->GetPrimaryFrame()) {
681
LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this));
682
return NS_OK;
683
}
684
685
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
686
687
if (!pluginHost) {
688
MOZ_ASSERT_UNREACHABLE("No pluginhost");
689
return NS_ERROR_FAILURE;
690
}
691
692
// If you add early return(s), be sure to balance this call to
693
// appShell->SuspendNative() with additional call(s) to
694
// appShell->ReturnNative().
695
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
696
if (appShell) {
697
appShell->SuspendNative();
698
}
699
700
RefPtr<nsPluginInstanceOwner> newOwner;
701
nsresult rv = pluginHost->InstantiatePluginInstance(
702
mContentType, mURI.get(), this, getter_AddRefs(newOwner));
703
704
// XXX(johns): We don't suspend native inside stopping plugins...
705
if (appShell) {
706
appShell->ResumeNative();
707
}
708
709
if (!mInstantiating || NS_FAILED(rv)) {
710
LOG(
711
("OBJLC [%p]: Plugin instantiation failed or re-entered, "
712
"killing old instance",
713
this));
714
// XXX(johns): This needs to be de-duplicated with DoStopPlugin, but we
715
// don't want to touch the protochain or delayed stop.
716
// (Bug 767635)
717
if (newOwner) {
718
RefPtr<nsNPAPIPluginInstance> inst = newOwner->GetInstance();
719
newOwner->SetFrame(nullptr);
720
if (inst) {
721
pluginHost->StopPluginInstance(inst);
722
}
723
newOwner->Destroy();
724
}
725
return NS_OK;
726
}
727
728
mInstanceOwner = newOwner;
729
730
if (mInstanceOwner) {
731
RefPtr<nsNPAPIPluginInstance> inst = mInstanceOwner->GetInstance();
732
733
rv = inst->GetRunID(&mRunID);
734
mHasRunID = NS_SUCCEEDED(rv);
735
}
736
737
// Ensure the frame did not change during instantiation re-entry (common).
738
// HasNewFrame would not have mInstanceOwner yet, so the new frame would be
739
// dangling. (Bug 854082)
740
nsIFrame* frame = thisContent->GetPrimaryFrame();
741
if (frame && mInstanceOwner) {
742
mInstanceOwner->SetFrame(static_cast<nsPluginFrame*>(frame));
743
744
// Bug 870216 - Adobe Reader renders with incorrect dimensions until it gets
745
// a second SetWindow call. This is otherwise redundant.
746
mInstanceOwner->CallSetWindow();
747
}
748
749
// Set up scripting interfaces.
750
NotifyContentObjectWrapper();
751
752
RefPtr<nsNPAPIPluginInstance> pluginInstance = GetPluginInstance();
753
if (pluginInstance) {
754
nsCOMPtr<nsIPluginTag> pluginTag;
755
pluginHost->GetPluginTagForInstance(pluginInstance,
756
getter_AddRefs(pluginTag));
757
758
uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
759
pluginTag->GetBlocklistState(&blockState);
760
if (blockState == nsIBlocklistService::STATE_OUTDATED) {
761
// Fire plugin outdated event if necessary
762
LOG(("OBJLC [%p]: Dispatching plugin outdated event for content\n",
763
this));
764
nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(
765
thisContent, NS_LITERAL_STRING("PluginOutdated"));
766
nsresult rv = NS_DispatchToCurrentThread(ev);
767
if (NS_FAILED(rv)) {
768
NS_WARNING("failed to dispatch nsSimplePluginEvent");
769
}
770
}
771
772
// If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
773
// or we did load with a channel but are re-instantiating, re-open the
774
// channel. OpenChannel() performs security checks, and this plugin has
775
// already passed content policy in LoadObject.
776
if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
777
NS_ASSERTION(!mChannel, "should not have an existing channel here");
778
// We intentionally ignore errors here, leaving it up to the plugin to
779
// deal with not having an initial stream.
780
OpenChannel();
781
}
782
}
783
784
nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(
785
thisContent, doc, NS_LITERAL_STRING("PluginInstantiated"));
786
NS_DispatchToCurrentThread(ev);
787
788
#ifdef XP_MACOSX
789
HTMLObjectElement::HandlePluginInstantiated(thisContent->AsElement());
790
#endif
791
792
return NS_OK;
793
}
794
795
void nsObjectLoadingContent::GetPluginAttributes(
796
nsTArray<MozPluginParameter>& aAttributes) {
797
aAttributes = mCachedAttributes;
798
}
799
800
void nsObjectLoadingContent::GetPluginParameters(
801
nsTArray<MozPluginParameter>& aParameters) {
802
aParameters = mCachedParameters;
803
}
804
805
void nsObjectLoadingContent::GetNestedParams(
806
nsTArray<MozPluginParameter>& aParams) {
807
nsCOMPtr<Element> ourElement =
808
do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
809
810
nsCOMPtr<nsIHTMLCollection> allParams;
811
NS_NAMED_LITERAL_STRING(xhtml_ns, "http://www.w3.org/1999/xhtml");
812
ErrorResult rv;
813
allParams = ourElement->GetElementsByTagNameNS(
814
xhtml_ns, NS_LITERAL_STRING("param"), rv);
815
if (rv.Failed()) {
816
return;
817
}
818
MOZ_ASSERT(allParams);
819
820
uint32_t numAllParams = allParams->Length();
821
for (uint32_t i = 0; i < numAllParams; i++) {
822
RefPtr<Element> element = allParams->Item(i);
823
824
nsAutoString name;
825
element->GetAttribute(NS_LITERAL_STRING("name"), name);
826
827
if (name.IsEmpty()) continue;
828
829
nsCOMPtr<nsIContent> parent = element->GetParent();
830
RefPtr<HTMLObjectElement> objectElement;
831
while (!objectElement && parent) {
832
objectElement = HTMLObjectElement::FromNode(parent);
833
parent = parent->GetParent();
834
}
835
836
if (objectElement) {
837
parent = objectElement;
838
} else {
839
continue;
840
}
841
842
if (parent == ourElement) {
843
MozPluginParameter param;
844
element->GetAttribute(NS_LITERAL_STRING("name"), param.mName);
845
element->GetAttribute(NS_LITERAL_STRING("value"), param.mValue);
846
847
param.mName.Trim(" \n\r\t\b", true, true, false);
848
param.mValue.Trim(" \n\r\t\b", true, true, false);
849
850
aParams.AppendElement(param);
851
}
852
}
853
}
854
855
nsresult nsObjectLoadingContent::BuildParametersArray() {
856
if (mCachedAttributes.Length() || mCachedParameters.Length()) {
857
MOZ_ASSERT(false, "Parameters array should be empty.");
858
return NS_OK;
859
}
860
861
nsCOMPtr<Element> element =
862
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
863
864
for (uint32_t i = 0; i != element->GetAttrCount(); i += 1) {
865
MozPluginParameter param;
866
const nsAttrName* attrName = element->GetAttrNameAt(i);
867
nsAtom* atom = attrName->LocalName();
868
element->GetAttr(attrName->NamespaceID(), atom, param.mValue);
869
atom->ToString(param.mName);
870
mCachedAttributes.AppendElement(param);
871
}
872
873
nsAutoCString wmodeOverride;
874
Preferences::GetCString("plugins.force.wmode", wmodeOverride);
875
876
for (uint32_t i = 0; i < mCachedAttributes.Length(); i++) {
877
if (!wmodeOverride.IsEmpty() &&
878
mCachedAttributes[i].mName.EqualsIgnoreCase("wmode")) {
879
CopyASCIItoUTF16(wmodeOverride, mCachedAttributes[i].mValue);
880
wmodeOverride.Truncate();
881
}
882
}
883
884
if (!wmodeOverride.IsEmpty()) {
885
MozPluginParameter param;
886
param.mName = NS_LITERAL_STRING("wmode");
887
CopyASCIItoUTF16(wmodeOverride, param.mValue);
888
mCachedAttributes.AppendElement(param);
889
}
890
891
// Some plugins were never written to understand the "data" attribute of the
892
// OBJECT tag. Real and WMP will not play unless they find a "src" attribute,
893
// see bug 152334. Nav 4.x would simply replace the "data" with "src". Because
894
// some plugins correctly look for "data", lets instead copy the "data"
895
// attribute and add another entry to the bottom of the array if there isn't
896
// already a "src" specified.
897
if (element->IsHTMLElement(nsGkAtoms::object) &&
898
!element->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
899
MozPluginParameter param;
900
element->GetAttr(kNameSpaceID_None, nsGkAtoms::data, param.mValue);
901
if (!param.mValue.IsEmpty()) {
902
param.mName = NS_LITERAL_STRING("SRC");
903
mCachedAttributes.AppendElement(param);
904
}
905
}
906
907
GetNestedParams(mCachedParameters);
908
909
return NS_OK;
910
}
911
912
void nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged() {
913
// XXX(johns): We cannot touch plugins or run arbitrary script from this call,
914
// as Document is in a non-reentrant state.
915
916
// If we have a plugin we want to queue an event to stop it unless we are
917
// moved into an active document before returning to the event loop.
918
if (mInstanceOwner || mInstantiating) {
919
QueueCheckPluginStopEvent();
920
}
921
nsImageLoadingContent::NotifyOwnerDocumentActivityChanged();
922
}
923
924
// nsIRequestObserver
925
NS_IMETHODIMP
926
nsObjectLoadingContent::OnStartRequest(nsIRequest* aRequest) {
927
AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStartRequest", NETWORK);
928
929
LOG(("OBJLC [%p]: Channel OnStartRequest", this));
930
931
if (aRequest != mChannel || !aRequest) {
932
// happens when a new load starts before the previous one got here
933
return NS_BINDING_ABORTED;
934
}
935
936
// If we already switched to type plugin, this channel can just be passed to
937
// the final listener.
938
if (mType == eType_Plugin) {
939
if (!mInstanceOwner) {
940
// We drop mChannel when stopping plugins, so something is wrong
941
MOZ_ASSERT_UNREACHABLE(
942
"Opened a channel in plugin mode, but don't have "
943
"a plugin");
944
return NS_BINDING_ABORTED;
945
}
946
if (MakePluginListener()) {
947
return mFinalListener->OnStartRequest(aRequest);
948
}
949
MOZ_ASSERT_UNREACHABLE(
950
"Failed to create PluginStreamListener, aborting "
951
"channel");
952
return NS_BINDING_ABORTED;
953
}
954
955
// Otherwise we should be state loading, and call LoadObject with the channel
956
if (mType != eType_Loading) {
957
MOZ_ASSERT_UNREACHABLE("Should be type loading at this point");
958
return NS_BINDING_ABORTED;
959
}
960
NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
961
NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
962
963
mChannelLoaded = true;
964
965
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
966
NS_ASSERTION(chan, "Why is our request not a channel?");
967
968
nsresult status = NS_OK;
969
bool success = IsSuccessfulRequest(aRequest, &status);
970
971
if (status == NS_ERROR_BLOCKED_URI) {
972
nsCOMPtr<nsIConsoleService> console(
973
do_GetService("@mozilla.org/consoleservice;1"));
974
if (console) {
975
nsCOMPtr<nsIURI> uri;
976
chan->GetURI(getter_AddRefs(uri));
977
nsString message =
978
NS_LITERAL_STRING("Blocking ") +
979
NS_ConvertASCIItoUTF16(uri->GetSpecOrDefault().get()) +
980
NS_LITERAL_STRING(
981
" since it was found on an internal Firefox blocklist.");
982
console->LogStringMessage(message.get());
983
}
984
mContentBlockingEnabled = true;
985
return NS_ERROR_FAILURE;
986
}
987
988
if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
989
mContentBlockingEnabled = true;
990
return NS_ERROR_FAILURE;
991
}
992
993
if (!success) {
994
LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
995
// If the request fails, we still call LoadObject() to handle fallback
996
// content and notifying of failure. (mChannelLoaded && !mChannel) indicates
997
// the bad state.
998
mChannel = nullptr;
999
LoadObject(true, false);
1000
return NS_ERROR_FAILURE;
1001
}
1002
1003
return LoadObject(true, false, aRequest);
1004
}
1005
1006
NS_IMETHODIMP
1007
nsObjectLoadingContent::OnStopRequest(nsIRequest* aRequest,
1008
nsresult aStatusCode) {
1009
AUTO_PROFILER_LABEL("nsObjectLoadingContent::OnStopRequest", NETWORK);
1010
1011
// Handle object not loading error because source was a tracking URL (or
1012
// fingerprinting, cryptomining, etc.).
1013
// We make a note of this object node by including it in a dedicated
1014
// array of blocked tracking nodes under its parent document.
1015
if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatusCode)) {
1016
nsCOMPtr<nsIContent> thisNode =
1017
do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
1018
if (thisNode && thisNode->IsInComposedDoc()) {
1019
thisNode->GetComposedDoc()->AddBlockedNodeByClassifier(thisNode);
1020
}
1021
}
1022
1023
if (aRequest != mChannel) {
1024
return NS_BINDING_ABORTED;
1025
}
1026
1027
mChannel = nullptr;
1028
1029
if (mFinalListener) {
1030
// This may re-enter in the case of plugin listeners
1031
nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1032
mFinalListener = nullptr;
1033
listenerGrip->OnStopRequest(aRequest, aStatusCode);
1034
}
1035
1036
// Return value doesn't matter
1037
return NS_OK;
1038
}
1039
1040
// nsIStreamListener
1041
NS_IMETHODIMP
1042
nsObjectLoadingContent::OnDataAvailable(nsIRequest* aRequest,
1043
nsIInputStream* aInputStream,
1044
uint64_t aOffset, uint32_t aCount) {
1045
if (aRequest != mChannel) {
1046
return NS_BINDING_ABORTED;
1047
}
1048
1049
if (mFinalListener) {
1050
// This may re-enter in the case of plugin listeners
1051
nsCOMPtr<nsIStreamListener> listenerGrip(mFinalListener);
1052
return listenerGrip->OnDataAvailable(aRequest, aInputStream, aOffset,
1053
aCount);
1054
}
1055
1056
// We shouldn't have a connected channel with no final listener
1057
MOZ_ASSERT_UNREACHABLE(
1058
"Got data for channel with no connected final "
1059
"listener");
1060
mChannel = nullptr;
1061
1062
return NS_ERROR_UNEXPECTED;
1063
}
1064
1065
void nsObjectLoadingContent::PresetOpenerWindow(
1066
const Nullable<WindowProxyHolder>& aOpenerWindow, ErrorResult& aRv) {
1067
aRv.Throw(NS_ERROR_FAILURE);
1068
}
1069
1070
NS_IMETHODIMP
1071
nsObjectLoadingContent::GetActualType(nsACString& aType) {
1072
aType = mContentType;
1073
return NS_OK;
1074
}
1075
1076
NS_IMETHODIMP
1077
nsObjectLoadingContent::GetDisplayedType(uint32_t* aType) {
1078
*aType = DisplayedType();
1079
return NS_OK;
1080
}
1081
1082
NS_IMETHODIMP
1083
nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) {
1084
if (mType != eType_Plugin) {
1085
return NS_OK;
1086
}
1087
1088
if (!aFrame) {
1089
// Lost our frame. If we aren't going to be getting a new frame, e.g. we've
1090
// become display:none, we'll want to stop the plugin. Queue a
1091
// CheckPluginStopEvent
1092
if (mInstanceOwner || mInstantiating) {
1093
if (mInstanceOwner) {
1094
mInstanceOwner->SetFrame(nullptr);
1095
}
1096
QueueCheckPluginStopEvent();
1097
}
1098
return NS_OK;
1099
}
1100
1101
// Have a new frame
1102
1103
if (!mInstanceOwner) {
1104
// We are successfully setup as type plugin, but have not spawned an
1105
// instance due to a lack of a frame.
1106
AsyncStartPluginInstance();
1107
return NS_OK;
1108
}
1109
1110
// Otherwise, we're just changing frames
1111
// Set up relationship between instance owner and frame.
1112
nsPluginFrame* objFrame = static_cast<nsPluginFrame*>(aFrame);
1113
mInstanceOwner->SetFrame(objFrame);
1114
1115
return NS_OK;
1116
}
1117
1118
nsNPAPIPluginInstance* nsObjectLoadingContent::GetPluginInstance() {
1119
if (!mInstanceOwner) {
1120
return nullptr;
1121
}
1122
1123
return mInstanceOwner->GetInstance();
1124
}
1125
1126
NS_IMETHODIMP
1127
nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
1128
uint32_t* aType) {
1129
*aType = GetTypeOfContent(PromiseFlatCString(aMIMEType), false);
1130
return NS_OK;
1131
}
1132
1133
// nsIInterfaceRequestor
1134
// We use a shim class to implement this so that JS consumers still
1135
// see an interface requestor even though WebIDL bindings don't expose
1136
// that stuff.
1137
class ObjectInterfaceRequestorShim final : public nsIInterfaceRequestor,
1138
public nsIChannelEventSink,
1139
public nsIStreamListener {
1140
public:
1141
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1142
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ObjectInterfaceRequestorShim,
1143
nsIInterfaceRequestor)
1144
NS_DECL_NSIINTERFACEREQUESTOR
1145
// RefPtr<nsObjectLoadingContent> fails due to ambiguous AddRef/Release,
1146
// hence the ugly static cast :(
1147
NS_FORWARD_NSICHANNELEVENTSINK(
1148
static_cast<nsObjectLoadingContent*>(mContent.get())->)
1149
NS_FORWARD_NSISTREAMLISTENER(
1150
static_cast<nsObjectLoadingContent*>(mContent.get())->)
1151
NS_FORWARD_NSIREQUESTOBSERVER(
1152
static_cast<nsObjectLoadingContent*>(mContent.get())->)
1153
1154
explicit ObjectInterfaceRequestorShim(nsIObjectLoadingContent* aContent)
1155
: mContent(aContent) {}
1156
1157
protected:
1158
~ObjectInterfaceRequestorShim() = default;
1159
nsCOMPtr<nsIObjectLoadingContent> mContent;
1160
};
1161
1162
NS_IMPL_CYCLE_COLLECTION(ObjectInterfaceRequestorShim, mContent)
1163
1164
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ObjectInterfaceRequestorShim)
1165
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1166
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
1167
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
1168
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
1169
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
1170
NS_INTERFACE_MAP_END
1171
1172
NS_IMPL_CYCLE_COLLECTING_ADDREF(ObjectInterfaceRequestorShim)
1173
NS_IMPL_CYCLE_COLLECTING_RELEASE(ObjectInterfaceRequestorShim)
1174
1175
NS_IMETHODIMP
1176
ObjectInterfaceRequestorShim::GetInterface(const nsIID& aIID, void** aResult) {
1177
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1178
nsIChannelEventSink* sink = this;
1179
*aResult = sink;
1180
NS_ADDREF(sink);
1181
return NS_OK;
1182
}
1183
return NS_NOINTERFACE;
1184
}
1185
1186
// nsIChannelEventSink
1187
NS_IMETHODIMP
1188
nsObjectLoadingContent::AsyncOnChannelRedirect(
1189
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
1190
nsIAsyncVerifyRedirectCallback* cb) {
1191
// If we're already busy with a new load, or have no load at all,
1192
// cancel the redirect.
1193
if (!mChannel || aOldChannel != mChannel) {
1194
return NS_BINDING_ABORTED;
1195
}
1196
1197
mChannel = aNewChannel;
1198
cb->OnRedirectVerifyCallback(NS_OK);
1199
return NS_OK;
1200
}
1201
1202
// <public>
1203
EventStates nsObjectLoadingContent::ObjectState() const {
1204
switch (mType) {
1205
case eType_Loading:
1206
return NS_EVENT_STATE_LOADING;
1207
case eType_Image:
1208
return ImageState();
1209
case eType_Plugin:
1210
case eType_FakePlugin:
1211
case eType_Document:
1212
// These are OK. If documents start to load successfully, they display
1213
// something, and are thus not broken in this sense. The same goes for
1214
// plugins.
1215
return EventStates();
1216
case eType_Null:
1217
switch (mFallbackType) {
1218
case eFallbackSuppressed:
1219
return NS_EVENT_STATE_SUPPRESSED;
1220
case eFallbackUserDisabled:
1221
return NS_EVENT_STATE_USERDISABLED;
1222
case eFallbackClickToPlay:
1223
case eFallbackClickToPlayQuiet:
1224
return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
1225
case eFallbackDisabled:
1226
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED;
1227
case eFallbackBlocklisted:
1228
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED;
1229
case eFallbackCrashed:
1230
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED;
1231
case eFallbackUnsupported:
1232
case eFallbackOutdated:
1233
case eFallbackAlternate:
1234
return NS_EVENT_STATE_BROKEN;
1235
case eFallbackVulnerableUpdatable:
1236
return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
1237
case eFallbackVulnerableNoUpdate:
1238
return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
1239
}
1240
}
1241
MOZ_ASSERT_UNREACHABLE("unknown type?");
1242
return NS_EVENT_STATE_LOADING;
1243
}
1244
1245
void nsObjectLoadingContent::MaybeRewriteYoutubeEmbed(nsIURI* aURI,
1246
nsIURI* aBaseURI,
1247
nsIURI** aOutURI) {
1248
nsCOMPtr<nsIContent> thisContent =
1249
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1250
NS_ASSERTION(thisContent, "Must be an instance of content");
1251
1252
// We're only interested in switching out embed and object tags
1253
if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed) &&
1254
!thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
1255
return;
1256
}
1257
1258
nsCOMPtr<nsIEffectiveTLDService> tldService =
1259
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
1260
// If we can't analyze the URL, just pass on through.
1261
if (!tldService) {
1262
NS_WARNING("Could not get TLD service!");
1263
return;
1264
}
1265
1266
nsAutoCString currentBaseDomain;
1267
bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(aURI, 0, currentBaseDomain));
1268
if (!ok) {
1269
// Data URIs (commonly used for things like svg embeds) won't parse
1270
// correctly, so just fail silently here.
1271
return;
1272
}
1273
1274
// See if URL is referencing youtube
1275
if (!currentBaseDomain.EqualsLiteral("youtube.com") &&
1276
!currentBaseDomain.EqualsLiteral("youtube-nocookie.com")) {
1277
return;
1278
}
1279
1280
// We should only rewrite URLs with paths starting with "/v/", as we shouldn't
1281
// touch object nodes with "/embed/" urls that already do that right thing.
1282
nsAutoCString path;
1283
aURI->GetPathQueryRef(path);
1284
if (!StringBeginsWith(path, NS_LITERAL_CSTRING("/v/"))) {
1285
return;
1286
}
1287
1288
// See if requester is planning on using the JS API.
1289
nsAutoCString uri;
1290
nsresult rv = aURI->GetSpec(uri);
1291
if (NS_FAILED(rv)) {
1292
return;
1293
}
1294
1295
// Some YouTube urls have parameters in path components, e.g.
1296
// http://youtube.com/embed/7LcUOEP7Brc&start=35. These URLs work with flash,
1297
// but break iframe/object embedding. If this situation occurs with rewritten
1298
// URLs, convert the parameters to query in order to make the video load
1299
// correctly as an iframe. In either case, warn about it in the
1300
// developer console.
1301
int32_t ampIndex = uri.FindChar('&', 0);
1302
bool replaceQuery = false;
1303
if (ampIndex != -1) {
1304
int32_t qmIndex = uri.FindChar('?', 0);
1305
if (qmIndex == -1 || qmIndex > ampIndex) {
1306
replaceQuery = true;
1307
}
1308
}
1309
1310
// If we're pref'd off, return after telemetry has been logged.
1311
if (!Preferences::GetBool(kPrefYoutubeRewrite)) {
1312
return;
1313
}
1314
1315
nsAutoString utf16OldURI = NS_ConvertUTF8toUTF16(uri);
1316
// If we need to convert the URL, it means an ampersand comes first.
1317
// Use the index we found earlier.
1318
if (replaceQuery) {
1319
// Replace question marks with ampersands.
1320
uri.ReplaceChar('?', '&');
1321
// Replace the first ampersand with a question mark.
1322
uri.SetCharAt('?', ampIndex);
1323
}
1324
// Switch out video access url formats, which should possibly allow HTML5
1325
// video loading.
1326
uri.ReplaceSubstring(NS_LITERAL_CSTRING("/v/"),
1327
NS_LITERAL_CSTRING("/embed/"));
1328
nsAutoString utf16URI = NS_ConvertUTF8toUTF16(uri);
1329
rv = nsContentUtils::NewURIWithDocumentCharset(
1330
aOutURI, utf16URI, thisContent->OwnerDoc(), aBaseURI);
1331
if (NS_FAILED(rv)) {
1332
return;
1333
}
1334
AutoTArray<nsString, 2> params = {utf16OldURI, utf16URI};
1335
const char* msgName;
1336
// If there's no query to rewrite, just notify in the developer console
1337
// that we're changing the embed.
1338
if (!replaceQuery) {
1339
msgName = "RewriteYouTubeEmbed";
1340
} else {
1341
msgName = "RewriteYouTubeEmbedPathParams";
1342
}
1343
nsContentUtils::ReportToConsole(
1344
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Plugins"),
1345
thisContent->OwnerDoc(), nsContentUtils::eDOM_PROPERTIES, msgName,
1346
params);
1347
}
1348
1349
bool nsObjectLoadingContent::CheckLoadPolicy(int16_t* aContentPolicy) {
1350
if (!aContentPolicy || !mURI) {
1351
MOZ_ASSERT_UNREACHABLE("Doing it wrong");
1352
return false;
1353
}
1354
1355
nsCOMPtr<nsIContent> thisContent =
1356
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1357
NS_ASSERTION(thisContent, "Must be an instance of content");
1358
1359
Document* doc = thisContent->OwnerDoc();
1360
1361
nsContentPolicyType contentPolicyType = GetContentPolicyType();
1362
1363
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
1364
doc->NodePrincipal(), // loading principal
1365
doc->NodePrincipal(), // triggering principal
1366
thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1367
contentPolicyType);
1368
1369
*aContentPolicy = nsIContentPolicy::ACCEPT;
1370
nsresult rv = NS_CheckContentLoadPolicy(mURI, secCheckLoadInfo, mContentType,
1371
aContentPolicy,
1372
nsContentUtils::GetContentPolicy());
1373
NS_ENSURE_SUCCESS(rv, false);
1374
if (NS_CP_REJECTED(*aContentPolicy)) {
1375
LOG(("OBJLC [%p]: Content policy denied load of %s", this,
1376
mURI->GetSpecOrDefault().get()));
1377
return false;
1378
}
1379
1380
return true;
1381
}
1382
1383
bool nsObjectLoadingContent::CheckProcessPolicy(int16_t* aContentPolicy) {
1384
if (!aContentPolicy) {
1385
MOZ_ASSERT_UNREACHABLE("Null out variable");
1386
return false;
1387
}
1388
1389
nsCOMPtr<nsIContent> thisContent =
1390
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1391
NS_ASSERTION(thisContent, "Must be an instance of content");
1392
1393
Document* doc = thisContent->OwnerDoc();
1394
1395
int32_t objectType;
1396
switch (mType) {
1397
case eType_Image:
1398
objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
1399
break;
1400
case eType_Document:
1401
objectType = nsIContentPolicy::TYPE_DOCUMENT;
1402
break;
1403
// FIXME Fake plugins look just like real plugins to CSP, should they use
1404
// the fake plugin's handler URI and look like documents instead?
1405
case eType_FakePlugin:
1406
case eType_Plugin:
1407
objectType = GetContentPolicyType();
1408
break;
1409
default:
1410
MOZ_ASSERT_UNREACHABLE(
1411
"Calling checkProcessPolicy with an unloadable "
1412
"type");
1413
return false;
1414
}
1415
1416
nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
1417
doc->NodePrincipal(), // loading principal
1418
doc->NodePrincipal(), // triggering principal
1419
thisContent, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
1420
objectType);
1421
1422
*aContentPolicy = nsIContentPolicy::ACCEPT;
1423
nsresult rv = NS_CheckContentProcessPolicy(
1424
mURI ? mURI : mBaseURI, secCheckLoadInfo, mContentType, aContentPolicy,
1425
nsContentUtils::GetContentPolicy());
1426
NS_ENSURE_SUCCESS(rv, false);
1427
1428
if (NS_CP_REJECTED(*aContentPolicy)) {
1429
LOG(("OBJLC [%p]: CheckContentProcessPolicy rejected load", this));
1430
return false;
1431
}
1432
1433
return true;
1434
}
1435
1436
nsObjectLoadingContent::ParameterUpdateFlags
1437
nsObjectLoadingContent::UpdateObjectParameters() {
1438
nsCOMPtr<Element> thisElement =
1439
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1440
MOZ_ASSERT(thisElement, "Must be an Element");
1441
1442
uint32_t caps = GetCapabilities();
1443
LOG(("OBJLC [%p]: Updating object parameters", this));
1444
1445
nsresult rv;
1446
nsAutoCString newMime;
1447
nsAutoString typeAttr;
1448
nsCOMPtr<nsIURI> newURI;
1449
nsCOMPtr<nsIURI> newBaseURI;
1450
ObjectType newType;
1451
// Set if this state can't be used to load anything, forces eType_Null
1452
bool stateInvalid = false;
1453
// Indicates what parameters changed.
1454
// eParamChannelChanged - means parameters that affect channel opening
1455
// decisions changed
1456
// eParamStateChanged - means anything that affects what content we load
1457
// changed, even if the channel we'd open remains the
1458
// same.
1459
//
1460
// State changes outside of the channel parameters only matter if we've
1461
// already opened a channel or tried to instantiate content, whereas channel
1462
// parameter changes require re-opening the channel even if we haven't gotten
1463
// that far.
1464
nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
1465
1466
///
1467
/// Initial MIME Type
1468
///
1469
1470
if (caps & eFallbackIfClassIDPresent) {
1471
nsAutoString classIDAttr;
1472
thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
1473
// We don't support class ID plugin references, so we should always treat
1474
// having class Ids as attributes as invalid, and fallback accordingly.
1475
if (!classIDAttr.IsEmpty()) {
1476
newMime.Truncate();
1477
stateInvalid = true;
1478
}
1479
}
1480
1481
///
1482
/// Codebase
1483
///
1484
1485
nsAutoString codebaseStr;
1486
nsIURI* docBaseURI = thisElement->GetBaseURI();
1487
thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr);
1488
1489
if (!codebaseStr.IsEmpty()) {
1490
rv = nsContentUtils::NewURIWithDocumentCharset(
1491
getter_AddRefs(newBaseURI), codebaseStr, thisElement->OwnerDoc(),
1492
docBaseURI);
1493
if (NS_FAILED(rv)) {
1494
// Malformed URI
1495
LOG(
1496
("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
1497
"will use document baseURI instead",
1498
this));
1499
}
1500
}
1501
1502
// If we failed to build a valid URI, use the document's base URI
1503
if (!newBaseURI) {
1504
newBaseURI = docBaseURI;
1505
}
1506
1507
nsAutoString rawTypeAttr;
1508
thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, rawTypeAttr);
1509
if (!rawTypeAttr.IsEmpty()) {
1510
typeAttr = rawTypeAttr;
1511
nsAutoString params;
1512
nsAutoString mime;
1513
nsContentUtils::SplitMimeType(rawTypeAttr, mime, params);
1514
CopyUTF16toUTF8(mime, newMime);
1515
}
1516
1517
///
1518
/// URI
1519
///
1520
1521
nsAutoString uriStr;
1522
// Different elements keep this in various locations
1523
if (thisElement->NodeInfo()->Equals(nsGkAtoms::object)) {
1524
thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr);
1525
} else if (thisElement->NodeInfo()->Equals(nsGkAtoms::embed)) {
1526
thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr);
1527
} else {
1528
MOZ_ASSERT_UNREACHABLE("Unrecognized plugin-loading tag");
1529
}
1530
1531
mRewrittenYoutubeEmbed = false;
1532
// Note that the baseURI changing could affect the newURI, even if uriStr did
1533
// not change.
1534
if (!uriStr.IsEmpty()) {
1535
rv = nsContentUtils::NewURIWithDocumentCharset(
1536
getter_AddRefs(newURI), uriStr, thisElement->OwnerDoc(), newBaseURI);
1537
nsCOMPtr<nsIURI> rewrittenURI;
1538
MaybeRewriteYoutubeEmbed(newURI, newBaseURI, getter_AddRefs(rewrittenURI));
1539
if (rewrittenURI) {
1540
newURI = rewrittenURI;
1541
mRewrittenYoutubeEmbed = true;
1542
newMime = NS_LITERAL_CSTRING("text/html");
1543
}
1544
1545
if (NS_FAILED(rv)) {
1546
stateInvalid = true;
1547
}
1548
}
1549
1550
// For eAllowPluginSkipChannel tags, if we have a non-plugin type, but can get
1551
// a plugin type from the extension, prefer that to falling back to a channel.
1552
if (!IsPluginType(GetTypeOfContent(newMime, mSkipFakePlugins)) && newURI &&
1553
(caps & eAllowPluginSkipChannel) &&
1554
IsPluginEnabledByExtension(newURI, newMime)) {
1555
LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get()));
1556
}
1557
1558
///
1559
/// Check if the original (pre-channel) content-type or URI changed, and
1560
/// record mOriginal{ContentType,URI}
1561
///
1562
1563
if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
1564
// These parameters changing requires re-opening the channel, so don't
1565
// consider the currently-open channel below
1566
// XXX(johns): Changing the mime type might change our decision on whether
1567
// or not we load a channel, so we count changes to it as a
1568
// channel parameter change for the sake of simplicity.
1569
retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1570
LOG(("OBJLC [%p]: Channel parameters changed", this));
1571
}
1572
mOriginalContentType = newMime;
1573
mOriginalURI = newURI;
1574
1575
///
1576
/// If we have a channel, see if its MIME type should take precendence and
1577
/// check the final (redirected) URL
1578
///
1579
1580
// If we have a loaded channel and channel parameters did not change, use it
1581
// to determine what we would load.
1582
bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
1583
// If we have a channel and are type loading, as opposed to having an existing
1584
// channel for a previous load.
1585
bool newChannel = useChannel && mType == eType_Loading;
1586
1587
if (newChannel && mChannel) {
1588
nsCString channelType;
1589
rv = mChannel->GetContentType(channelType);
1590
if (NS_FAILED(rv)) {
1591
MOZ_ASSERT_UNREACHABLE("GetContentType failed");
1592
stateInvalid = true;
1593
channelType.Truncate();
1594
}
1595
1596
LOG(("OBJLC [%p]: Channel has a content type of %s", this,
1597
channelType.get()));
1598
1599
bool binaryChannelType = false;
1600
if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
1601
channelType = APPLICATION_OCTET_STREAM;
1602
mChannel->SetContentType(channelType);
1603
binaryChannelType = true;
1604
} else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
1605
channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
1606
binaryChannelType = true;
1607
}
1608
1609
// Channel can change our URI through redirection
1610
rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
1611
if (NS_FAILED(rv)) {
1612
MOZ_ASSERT_UNREACHABLE("NS_GetFinalChannelURI failure");
1613
stateInvalid = true;
1614
}
1615
1616
ObjectType typeHint = newMime.IsEmpty()
1617
? eType_Null
1618
: GetTypeOfContent(newMime, mSkipFakePlugins);
1619
1620
//
1621
// In order of preference:
1622
//
1623
// 1) Use our type hint if it matches a plugin
1624
// 2) If we have eAllowPluginSkipChannel, use the uri file extension if
1625
// it matches a plugin
1626
// 3) If the channel returns a binary stream type:
1627
// 3a) If we have a type non-null non-document type hint, use that
1628
// 3b) If the uri file extension matches a plugin type, use that
1629
// 4) Use the channel type
1630
1631
bool overrideChannelType = false;
1632
if (IsPluginType(typeHint)) {
1633
LOG(("OBJLC [%p]: Using plugin type hint in favor of any channel type",
1634
this));
1635
overrideChannelType = true;
1636
} else if ((caps & eAllowPluginSkipChannel) &&
1637
IsPluginEnabledByExtension(newURI, newMime)) {
1638
LOG(
1639
("OBJLC [%p]: Using extension as type hint for "
1640
"eAllowPluginSkipChannel tag (%s)",
1641
this, newMime.get()));
1642
overrideChannelType = true;
1643
} else if (binaryChannelType && typeHint != eType_Null &&
1644
typeHint != eType_Document) {
1645
LOG(("OBJLC [%p]: Using type hint in favor of binary channel type",
1646
this));
1647
overrideChannelType = true;
1648
} else if (binaryChannelType &&
1649
IsPluginEnabledByExtension(newURI, newMime)) {
1650
LOG(("OBJLC [%p]: Using extension as type hint for binary channel (%s)",
1651
this, newMime.get()));
1652
overrideChannelType = true;
1653
}
1654
1655
if (overrideChannelType) {
1656
// Set the type we'll use for dispatch on the channel. Otherwise we could
1657
// end up trying to dispatch to a nsFrameLoader, which will complain that
1658
// it couldn't find a way to handle application/octet-stream
1659
nsAutoCString parsedMime, dummy;
1660
NS_ParseResponseContentType(newMime, parsedMime, dummy);
1661
if (!parsedMime.IsEmpty()) {
1662
mChannel->SetContentType(parsedMime);
1663
}
1664
} else {
1665
newMime = channelType;
1666
}
1667
} else if (newChannel) {
1668
LOG(("OBJLC [%p]: We failed to open a channel, marking invalid", this));
1669
stateInvalid = true;
1670
}
1671
1672
///
1673
/// Determine final type
1674
///
1675
// In order of preference:
1676
// 1) If we have attempted channel load, or set stateInvalid above, the type
1677
// is always null (fallback)
1678
// 2) If we have a loaded channel, we grabbed its mimeType above, use that
1679
// type.
1680
// 3) If we have a plugin type and no URI, use that type.
1681
// 4) If we have a plugin type and eAllowPluginSkipChannel, use that type.
1682
// 5) if we have a URI, set type to loading to indicate we'd need a channel
1683
// to proceed.
1684
// 6) Otherwise, type null to indicate unloadable content (fallback)
1685
//
1686
1687
ObjectType newMime_Type = GetTypeOfContent(newMime, mSkipFakePlugins);
1688
1689
if (stateInvalid) {
1690
newType = eType_Null;
1691
newMime.Truncate();
1692
} else if (newChannel) {
1693
// If newChannel is set above, we considered it in setting newMime
1694
newType = newMime_Type;
1695
LOG(("OBJLC [%p]: Using channel type", this));
1696
} else if (((caps & eAllowPluginSkipChannel) || !newURI) &&
1697
IsPluginType(newMime_Type)) {
1698
newType = newMime_Type;
1699
LOG(("OBJLC [%p]: Plugin type with no URI, skipping channel load", this));
1700
} else if (newURI &&
1701
(mOriginalContentType.IsEmpty() || newMime_Type != eType_Null)) {
1702
// We could potentially load this if we opened a channel on mURI, indicate
1703
// this by leaving type as loading.
1704
//
1705
// If a MIME type was requested in the tag, but we have decided to set load
1706
// type to null, ignore (otherwise we'll default to document type loading).
1707
newType = eType_Loading;
1708
} else {
1709
// Unloadable - no URI, and no plugin/MIME type. Non-plugin types (images,
1710
// documents) always load with a channel.
1711
newType = eType_Null;
1712
}
1713
1714
///
1715
/// Handle existing channels
1716
///
1717
1718
if (useChannel && newType == eType_Loading) {
1719
// We decided to use a channel, and also that the previous channel is still
1720
// usable, so re-use the existing values.
1721
newType = mType;
1722
newMime = mContentType;
1723
newURI = mURI;
1724
} else if (useChannel && !newChannel) {
1725
// We have an existing channel, but did not decide to use one.
1726
retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1727
useChannel = false;
1728
}
1729
1730
///
1731
/// Update changed values
1732
///
1733
1734
if (newType != mType) {
1735
retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1736
LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
1737
bool updateIMEState = (mType == eType_Loading && newType == eType_Plugin);
1738
mType = newType;
1739
// The IME manager needs to know if this is a plugin so it can adjust
1740
// input handling to an appropriate mode for plugins.
1741
nsFocusManager* fm = nsFocusManager::GetFocusManager();
1742
nsCOMPtr<nsIContent> thisContent =
1743
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1744
MOZ_ASSERT(thisContent, "should have content");
1745
if (updateIMEState && thisContent && fm && fm->IsFocused(thisContent)) {
1746
widget::IMEState state;
1747
state.mEnabled = widget::IMEState::PLUGIN;
1748
state.mOpen = widget::IMEState::DONT_CHANGE_OPEN_STATE;
1749
IMEStateManager::UpdateIMEState(state, thisContent, nullptr);
1750
}
1751
}
1752
1753
if (!URIEquals(mBaseURI, newBaseURI)) {
1754
LOG(("OBJLC [%p]: Object effective baseURI changed", this));
1755
mBaseURI = newBaseURI;
1756
}
1757
1758
if (!URIEquals(newURI, mURI)) {
1759
retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1760
LOG(("OBJLC [%p]: Object effective URI changed", this));
1761
mURI = newURI;
1762
}
1763
1764
// We don't update content type when loading, as the type is not final and we
1765
// don't want to superfluously change between mOriginalContentType ->
1766
// mContentType when doing |obj.data = obj.data| with a channel and differing
1767
// type.
1768
if (mType != eType_Loading && mContentType != newMime) {
1769
retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
1770
retval = (ParameterUpdateFlags)(retval | eParamContentTypeChanged);
1771
LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this,
1772
mContentType.get(), newMime.get()));
1773
mContentType = newMime;
1774
}
1775
1776
// If we decided to keep using info from an old channel, but also that state
1777
// changed, we need to invalidate it.
1778
if (useChannel && !newChannel && (retval & eParamStateChanged)) {
1779
mType = eType_Loading;
1780
retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
1781
}
1782
1783
return retval;
1784
}
1785
1786
// Used by PluginDocument to kick off our initial load from the already-opened
1787
// channel.
1788
NS_IMETHODIMP
1789
nsObjectLoadingContent::InitializeFromChannel(nsIRequest* aChannel) {
1790
LOG(("OBJLC [%p] InitializeFromChannel: %p", this, aChannel));
1791
if (mType != eType_Loading || mChannel) {
1792
// We could technically call UnloadObject() here, if consumers have a valid
1793
// reason for wanting to call this on an already-loaded tag.
1794
MOZ_ASSERT_UNREACHABLE("Should not have begun loading at this point");
1795
return NS_ERROR_UNEXPECTED;
1796
}
1797
1798
// Because we didn't open this channel from an initial LoadObject, we'll
1799
// update our parameters now, so the OnStartRequest->LoadObject doesn't
1800
// believe our src/type suddenly changed.
1801
UpdateObjectParameters();
1802
// But we always want to load from a channel, in this case.
1803
mType = eType_Loading;
1804
mChannel = do_QueryInterface(aChannel);
1805
NS_ASSERTION(mChannel, "passed a request that is not a channel");
1806
1807
// OnStartRequest will now see we have a channel in the loading state, and
1808
// call into LoadObject. There's a possibility LoadObject will decide not to
1809
// load anything from a channel - it will call CloseChannel() in that case.
1810
return NS_OK;
1811
}
1812
1813
// Only OnStartRequest should be passing the channel parameter
1814