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