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