1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:set et cin sw=2 sts=2:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla <object> loading code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Christian Biesinger <cbiesinger@web.de>.
20 : * Portions created by the Initial Developer are Copyright (C) 2005
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Justin Dolske <dolske@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * A base class implementing nsIObjectLoadingContent for use by
42 : * various content nodes that want to provide plugin/document/image
43 : * loading functionality (eg <embed>, <object>, <applet>, etc).
44 : */
45 :
46 : // Interface headers
47 : #include "imgILoader.h"
48 : #include "nsEventDispatcher.h"
49 : #include "nsIContent.h"
50 : #include "nsIDocShell.h"
51 : #include "nsIDocument.h"
52 : #include "nsIDOMDataContainerEvent.h"
53 : #include "nsIDOMDocument.h"
54 : #include "nsIDOMEventTarget.h"
55 : #include "nsIExternalProtocolHandler.h"
56 : #include "nsEventStates.h"
57 : #include "nsIObjectFrame.h"
58 : #include "nsIPluginDocument.h"
59 : #include "nsPluginHost.h"
60 : #include "nsIPresShell.h"
61 : #include "nsIPrivateDOMEvent.h"
62 : #include "nsIScriptGlobalObject.h"
63 : #include "nsIScriptSecurityManager.h"
64 : #include "nsIStreamConverterService.h"
65 : #include "nsIURILoader.h"
66 : #include "nsIURL.h"
67 : #include "nsIWebNavigation.h"
68 : #include "nsIWebNavigationInfo.h"
69 : #include "nsIScriptChannel.h"
70 : #include "nsIBlocklistService.h"
71 : #include "nsIAsyncVerifyRedirectCallback.h"
72 : #include "nsIAppShell.h"
73 :
74 : #include "nsPluginError.h"
75 :
76 : // Util headers
77 : #include "prlog.h"
78 :
79 : #include "nsAutoPtr.h"
80 : #include "nsCURILoader.h"
81 : #include "nsContentPolicyUtils.h"
82 : #include "nsContentUtils.h"
83 : #include "nsDocShellCID.h"
84 : #include "nsGkAtoms.h"
85 : #include "nsThreadUtils.h"
86 : #include "nsNetUtil.h"
87 : #include "nsMimeTypes.h"
88 : #include "nsStyleUtil.h"
89 : #include "nsGUIEvent.h"
90 : #include "nsUnicharUtils.h"
91 :
92 : // Concrete classes
93 : #include "nsFrameLoader.h"
94 :
95 : #include "nsObjectLoadingContent.h"
96 : #include "mozAutoDocUpdate.h"
97 : #include "nsIContentSecurityPolicy.h"
98 : #include "nsIChannelPolicy.h"
99 : #include "nsChannelPolicy.h"
100 : #include "mozilla/dom/Element.h"
101 : #include "sampler.h"
102 : #include "nsObjectFrame.h"
103 : #include "nsDOMClassInfo.h"
104 :
105 : #include "nsWidgetsCID.h"
106 : #include "nsContentCID.h"
107 : static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
108 :
109 : #ifdef PR_LOGGING
110 1464 : static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
111 : #endif
112 :
113 : #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
114 : #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
115 :
116 : #include "mozilla/Preferences.h"
117 :
118 : class nsAsyncInstantiateEvent : public nsRunnable {
119 : public:
120 : nsObjectLoadingContent *mContent;
121 0 : nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
122 0 : : mContent(aContent)
123 : {
124 0 : static_cast<nsIObjectLoadingContent *>(mContent)->AddRef();
125 0 : }
126 :
127 0 : ~nsAsyncInstantiateEvent()
128 0 : {
129 0 : static_cast<nsIObjectLoadingContent *>(mContent)->Release();
130 0 : }
131 :
132 : NS_IMETHOD Run();
133 : };
134 :
135 : NS_IMETHODIMP
136 0 : nsAsyncInstantiateEvent::Run()
137 : {
138 : // do nothing if we've been revoked
139 0 : if (mContent->mPendingInstantiateEvent != this) {
140 0 : return NS_OK;
141 : }
142 0 : mContent->mPendingInstantiateEvent = nsnull;
143 :
144 0 : return mContent->SyncStartPluginInstance();
145 : }
146 :
147 : // Checks to see if the content for a plugin instance has a parent.
148 : // The plugin instance is stopped if there is no parent.
149 : class InDocCheckEvent : public nsRunnable {
150 : public:
151 : nsCOMPtr<nsIContent> mContent;
152 :
153 0 : InDocCheckEvent(nsIContent* aContent)
154 0 : : mContent(aContent)
155 : {
156 0 : }
157 :
158 0 : ~InDocCheckEvent()
159 0 : {
160 0 : }
161 :
162 : NS_IMETHOD Run();
163 : };
164 :
165 : NS_IMETHODIMP
166 0 : InDocCheckEvent::Run()
167 : {
168 0 : if (!mContent->IsInDoc()) {
169 0 : nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(mContent);
170 0 : if (olc) {
171 0 : olc->StopPluginInstance();
172 : }
173 : }
174 0 : return NS_OK;
175 : }
176 :
177 : /**
178 : * A task for firing PluginNotFound and PluginBlocklisted DOM Events.
179 : */
180 : class nsPluginErrorEvent : public nsRunnable {
181 : public:
182 : nsCOMPtr<nsIContent> mContent;
183 : PluginSupportState mState;
184 :
185 0 : nsPluginErrorEvent(nsIContent* aContent, PluginSupportState aState)
186 : : mContent(aContent),
187 0 : mState(aState)
188 0 : {}
189 :
190 0 : ~nsPluginErrorEvent() {}
191 :
192 : NS_IMETHOD Run();
193 : };
194 :
195 : NS_IMETHODIMP
196 0 : nsPluginErrorEvent::Run()
197 : {
198 0 : LOG(("OBJLC []: Firing plugin not found event for content %p\n",
199 : mContent.get()));
200 0 : nsString type;
201 0 : switch (mState) {
202 : case ePluginClickToPlay:
203 0 : type = NS_LITERAL_STRING("PluginClickToPlay");
204 0 : break;
205 : case ePluginUnsupported:
206 0 : type = NS_LITERAL_STRING("PluginNotFound");
207 0 : break;
208 : case ePluginDisabled:
209 0 : type = NS_LITERAL_STRING("PluginDisabled");
210 0 : break;
211 : case ePluginBlocklisted:
212 0 : type = NS_LITERAL_STRING("PluginBlocklisted");
213 0 : break;
214 : case ePluginOutdated:
215 0 : type = NS_LITERAL_STRING("PluginOutdated");
216 0 : break;
217 : default:
218 0 : return NS_OK;
219 : }
220 : nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
221 0 : type, true, true);
222 :
223 0 : return NS_OK;
224 : }
225 :
226 : /**
227 : * A task for firing PluginCrashed DOM Events.
228 : */
229 : class nsPluginCrashedEvent : public nsRunnable {
230 : public:
231 : nsCOMPtr<nsIContent> mContent;
232 : nsString mPluginDumpID;
233 : nsString mBrowserDumpID;
234 : nsString mPluginName;
235 : nsString mPluginFilename;
236 : bool mSubmittedCrashReport;
237 :
238 0 : nsPluginCrashedEvent(nsIContent* aContent,
239 : const nsAString& aPluginDumpID,
240 : const nsAString& aBrowserDumpID,
241 : const nsAString& aPluginName,
242 : const nsAString& aPluginFilename,
243 : bool submittedCrashReport)
244 : : mContent(aContent),
245 : mPluginDumpID(aPluginDumpID),
246 : mBrowserDumpID(aBrowserDumpID),
247 : mPluginName(aPluginName),
248 : mPluginFilename(aPluginFilename),
249 0 : mSubmittedCrashReport(submittedCrashReport)
250 0 : {}
251 :
252 0 : ~nsPluginCrashedEvent() {}
253 :
254 : NS_IMETHOD Run();
255 : };
256 :
257 : NS_IMETHODIMP
258 0 : nsPluginCrashedEvent::Run()
259 : {
260 0 : LOG(("OBJLC []: Firing plugin crashed event for content %p\n",
261 : mContent.get()));
262 :
263 : nsCOMPtr<nsIDOMDocument> domDoc =
264 0 : do_QueryInterface(mContent->GetDocument());
265 0 : if (!domDoc) {
266 0 : NS_WARNING("Couldn't get document for PluginCrashed event!");
267 0 : return NS_OK;
268 : }
269 :
270 0 : nsCOMPtr<nsIDOMEvent> event;
271 0 : domDoc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"),
272 0 : getter_AddRefs(event));
273 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
274 0 : nsCOMPtr<nsIDOMDataContainerEvent> containerEvent(do_QueryInterface(event));
275 0 : if (!privateEvent || !containerEvent) {
276 0 : NS_WARNING("Couldn't QI event for PluginCrashed event!");
277 0 : return NS_OK;
278 : }
279 :
280 0 : event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), true, true);
281 0 : privateEvent->SetTrusted(true);
282 0 : privateEvent->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
283 :
284 0 : nsCOMPtr<nsIWritableVariant> variant;
285 :
286 : // add a "pluginDumpID" property to this event
287 0 : variant = do_CreateInstance("@mozilla.org/variant;1");
288 0 : if (!variant) {
289 0 : NS_WARNING("Couldn't create pluginDumpID variant for PluginCrashed event!");
290 0 : return NS_OK;
291 : }
292 0 : variant->SetAsAString(mPluginDumpID);
293 0 : containerEvent->SetData(NS_LITERAL_STRING("pluginDumpID"), variant);
294 :
295 : // add a "browserDumpID" property to this event
296 0 : variant = do_CreateInstance("@mozilla.org/variant;1");
297 0 : if (!variant) {
298 0 : NS_WARNING("Couldn't create browserDumpID variant for PluginCrashed event!");
299 0 : return NS_OK;
300 : }
301 0 : variant->SetAsAString(mBrowserDumpID);
302 0 : containerEvent->SetData(NS_LITERAL_STRING("browserDumpID"), variant);
303 :
304 : // add a "pluginName" property to this event
305 0 : variant = do_CreateInstance("@mozilla.org/variant;1");
306 0 : if (!variant) {
307 0 : NS_WARNING("Couldn't create pluginName variant for PluginCrashed event!");
308 0 : return NS_OK;
309 : }
310 0 : variant->SetAsAString(mPluginName);
311 0 : containerEvent->SetData(NS_LITERAL_STRING("pluginName"), variant);
312 :
313 : // add a "pluginFilename" property to this event
314 0 : variant = do_CreateInstance("@mozilla.org/variant;1");
315 0 : if (!variant) {
316 0 : NS_WARNING("Couldn't create pluginFilename variant for PluginCrashed event!");
317 0 : return NS_OK;
318 : }
319 0 : variant->SetAsAString(mPluginFilename);
320 0 : containerEvent->SetData(NS_LITERAL_STRING("pluginFilename"), variant);
321 :
322 : // add a "submittedCrashReport" property to this event
323 0 : variant = do_CreateInstance("@mozilla.org/variant;1");
324 0 : if (!variant) {
325 0 : NS_WARNING("Couldn't create crashSubmit variant for PluginCrashed event!");
326 0 : return NS_OK;
327 : }
328 0 : variant->SetAsBool(mSubmittedCrashReport);
329 0 : containerEvent->SetData(NS_LITERAL_STRING("submittedCrashReport"), variant);
330 :
331 0 : nsEventDispatcher::DispatchDOMEvent(mContent, nsnull, event, nsnull, nsnull);
332 0 : return NS_OK;
333 : }
334 :
335 : class nsStopPluginRunnable : public nsRunnable, public nsITimerCallback
336 0 : {
337 : public:
338 : NS_DECL_ISUPPORTS_INHERITED
339 :
340 0 : nsStopPluginRunnable(nsPluginInstanceOwner* aInstanceOwner,
341 : nsObjectLoadingContent* aContent)
342 : : mInstanceOwner(aInstanceOwner)
343 0 : , mContent(aContent)
344 : {
345 0 : NS_ASSERTION(aInstanceOwner, "need an owner");
346 0 : NS_ASSERTION(aContent, "need a nsObjectLoadingContent");
347 0 : }
348 :
349 : // nsRunnable
350 : NS_IMETHOD Run();
351 :
352 : // nsITimerCallback
353 : NS_IMETHOD Notify(nsITimer *timer);
354 :
355 : private:
356 : nsCOMPtr<nsITimer> mTimer;
357 : nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
358 : nsCOMPtr<nsIObjectLoadingContent> mContent;
359 : };
360 :
361 0 : NS_IMPL_ISUPPORTS_INHERITED1(nsStopPluginRunnable, nsRunnable, nsITimerCallback)
362 :
363 : NS_IMETHODIMP
364 0 : nsStopPluginRunnable::Notify(nsITimer *aTimer)
365 : {
366 0 : return Run();
367 : }
368 :
369 : NS_IMETHODIMP
370 0 : nsStopPluginRunnable::Run()
371 : {
372 : // InitWithCallback calls Release before AddRef so we need to hold a
373 : // strong ref on 'this' since we fall through to this scope if it fails.
374 0 : nsCOMPtr<nsITimerCallback> kungFuDeathGrip = this;
375 0 : nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
376 0 : if (appShell) {
377 0 : PRUint32 currentLevel = 0;
378 0 : appShell->GetEventloopNestingLevel(¤tLevel);
379 0 : if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) {
380 0 : if (!mTimer)
381 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1");
382 0 : if (mTimer) {
383 : // Fire 100ms timer to try to tear down this plugin as quickly as
384 : // possible once the nesting level comes back down.
385 0 : nsresult rv = mTimer->InitWithCallback(this, 100, nsITimer::TYPE_ONE_SHOT);
386 0 : if (NS_SUCCEEDED(rv)) {
387 0 : return rv;
388 : }
389 : }
390 : NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe "
391 0 : "time). Stopping the plugin now, this might crash.");
392 : }
393 : }
394 :
395 0 : mTimer = nsnull;
396 :
397 0 : static_cast<nsObjectLoadingContent*>(mContent.get())->
398 0 : DoStopPlugin(mInstanceOwner, false, true);
399 :
400 0 : return NS_OK;
401 : }
402 :
403 : class AutoNotifier {
404 : public:
405 0 : AutoNotifier(nsObjectLoadingContent* aContent, bool aNotify) :
406 0 : mContent(aContent), mNotify(aNotify) {
407 0 : mOldType = aContent->Type();
408 0 : mOldState = aContent->ObjectState();
409 0 : }
410 0 : ~AutoNotifier() {
411 0 : mContent->NotifyStateChanged(mOldType, mOldState, false, mNotify);
412 0 : }
413 :
414 : /**
415 : * Send notifications now, ignoring the value of mNotify. The new type and
416 : * state is saved, and the destructor will notify again if mNotify is true
417 : * and the values changed.
418 : */
419 0 : void Notify() {
420 0 : NS_ASSERTION(mNotify, "Should not notify when notify=false");
421 :
422 0 : mContent->NotifyStateChanged(mOldType, mOldState, true, true);
423 0 : mOldType = mContent->Type();
424 0 : mOldState = mContent->ObjectState();
425 0 : }
426 :
427 : private:
428 : nsObjectLoadingContent* mContent;
429 : bool mNotify;
430 : nsObjectLoadingContent::ObjectType mOldType;
431 : nsEventStates mOldState;
432 : };
433 :
434 : /**
435 : * A class that will automatically fall back if a |rv| variable has a failure
436 : * code when this class is destroyed. It does not notify.
437 : */
438 : class AutoFallback {
439 : public:
440 0 : AutoFallback(nsObjectLoadingContent* aContent, const nsresult* rv)
441 0 : : mContent(aContent), mResult(rv), mPluginState(ePluginOtherState) {}
442 0 : ~AutoFallback() {
443 0 : if (NS_FAILED(*mResult)) {
444 0 : LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult));
445 0 : mContent->Fallback(false);
446 0 : if (mPluginState != ePluginOtherState) {
447 0 : mContent->mFallbackReason = mPluginState;
448 : }
449 : }
450 0 : }
451 :
452 : /**
453 : * This should be set to something other than ePluginOtherState to indicate
454 : * a specific failure that should be passed on.
455 : */
456 0 : void SetPluginState(PluginSupportState aState) {
457 0 : NS_ASSERTION(aState != ePluginOtherState, "Should not be setting ePluginOtherState");
458 0 : mPluginState = aState;
459 0 : }
460 : private:
461 : nsObjectLoadingContent* mContent;
462 : const nsresult* mResult;
463 : PluginSupportState mPluginState;
464 : };
465 :
466 : /**
467 : * A class that automatically sets mInstantiating to false when it goes
468 : * out of scope.
469 : */
470 : class AutoSetInstantiatingToFalse {
471 : public:
472 0 : AutoSetInstantiatingToFalse(nsObjectLoadingContent* objlc) : mContent(objlc) {}
473 0 : ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
474 : private:
475 : nsObjectLoadingContent* mContent;
476 : };
477 :
478 : // helper functions
479 : static bool
480 0 : IsSupportedImage(const nsCString& aMimeType)
481 : {
482 0 : imgILoader* loader = nsContentUtils::GetImgLoader();
483 0 : if (!loader) {
484 0 : return false;
485 : }
486 :
487 : bool supported;
488 0 : nsresult rv = loader->SupportImageWithMimeType(aMimeType.get(), &supported);
489 0 : return NS_SUCCEEDED(rv) && supported;
490 : }
491 :
492 0 : nsresult nsObjectLoadingContent::IsPluginEnabledForType(const nsCString& aMIMEType)
493 : {
494 0 : if (!mShouldPlay) {
495 0 : return NS_ERROR_PLUGIN_CLICKTOPLAY;
496 : }
497 :
498 0 : nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
499 0 : nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
500 0 : if (!pluginHost) {
501 0 : return false;
502 : }
503 0 : return pluginHost->IsPluginEnabledForType(aMIMEType.get());
504 : }
505 :
506 : static void
507 0 : GetExtensionFromURI(nsIURI* uri, nsCString& ext)
508 : {
509 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
510 0 : if (url) {
511 0 : url->GetFileExtension(ext);
512 : } else {
513 0 : nsCString spec;
514 0 : uri->GetSpec(spec);
515 :
516 0 : PRInt32 offset = spec.RFindChar('.');
517 0 : if (offset != kNotFound) {
518 0 : ext = Substring(spec, offset + 1, spec.Length());
519 : }
520 : }
521 0 : }
522 :
523 : /**
524 : * Checks whether a plugin exists and is enabled for the extension
525 : * in the given URI. The MIME type is returned in the mimeType out parameter.
526 : */
527 0 : bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
528 : {
529 0 : if (!mShouldPlay) {
530 0 : return false;
531 : }
532 :
533 0 : nsCAutoString ext;
534 0 : GetExtensionFromURI(uri, ext);
535 :
536 0 : if (ext.IsEmpty()) {
537 0 : return false;
538 : }
539 :
540 0 : nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
541 0 : nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
542 0 : if (!pluginHost) {
543 0 : return false;
544 : }
545 :
546 : const char* typeFromExt;
547 0 : if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) {
548 0 : mimeType = typeFromExt;
549 0 : return true;
550 : }
551 0 : return false;
552 : }
553 :
554 0 : nsObjectLoadingContent::nsObjectLoadingContent()
555 : : mPendingInstantiateEvent(nsnull)
556 : , mChannel(nsnull)
557 : , mType(eType_Loading)
558 : , mInstantiating(false)
559 : , mUserDisabled(false)
560 : , mSuppressed(false)
561 : , mNetworkCreated(true)
562 : // If plugins.click_to_play is false, plugins should always play
563 0 : , mShouldPlay(!mozilla::Preferences::GetBool("plugins.click_to_play", false))
564 : , mIsStopping(false)
565 : , mSrcStreamLoading(false)
566 0 : , mFallbackReason(ePluginOtherState)
567 : {
568 0 : }
569 :
570 0 : nsObjectLoadingContent::~nsObjectLoadingContent()
571 : {
572 0 : DestroyImageLoadingContent();
573 0 : if (mFrameLoader) {
574 0 : mFrameLoader->Destroy();
575 : }
576 0 : }
577 :
578 : nsresult
579 0 : nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI)
580 : {
581 0 : if (!mShouldPlay) {
582 0 : return NS_ERROR_PLUGIN_CLICKTOPLAY;
583 : }
584 :
585 : // Don't do anything if we already have an active instance.
586 0 : if (mInstanceOwner) {
587 0 : return NS_OK;
588 : }
589 :
590 : // Don't allow re-entry into initialization code.
591 0 : if (mInstantiating) {
592 0 : return NS_OK;
593 : }
594 0 : mInstantiating = true;
595 0 : AutoSetInstantiatingToFalse autoInstantiating(this);
596 :
597 : // Instantiating an instance can result in script execution, which
598 : // can destroy this DOM object. Don't allow that for the scope
599 : // of this method.
600 0 : nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
601 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
602 :
603 0 : nsCOMPtr<nsIURI> baseURI;
604 0 : if (!aURI) {
605 : // We need some URI. If we have nothing else, use the base URI.
606 : // XXX(biesi): The code used to do this. Not sure why this is correct...
607 0 : GetObjectBaseURI(thisContent, getter_AddRefs(baseURI));
608 0 : aURI = baseURI;
609 : }
610 :
611 : // Flush layout so that the plugin is initialized with the latest information.
612 0 : nsIDocument* doc = thisContent->GetCurrentDoc();
613 0 : if (!doc) {
614 0 : return NS_ERROR_FAILURE;
615 : }
616 0 : if (!doc->IsActive()) {
617 0 : NS_ERROR("Shouldn't be calling InstantiatePluginInstance in an inactive document");
618 0 : return NS_ERROR_FAILURE;
619 : }
620 0 : doc->FlushPendingNotifications(Flush_Layout);
621 :
622 0 : nsresult rv = NS_ERROR_FAILURE;
623 0 : nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv));
624 0 : nsPluginHost* pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
625 0 : if (NS_FAILED(rv)) {
626 0 : return rv;
627 : }
628 :
629 : // If you add early return(s), be sure to balance this call to
630 : // appShell->SuspendNative() with additional call(s) to
631 : // appShell->ReturnNative().
632 0 : nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
633 0 : if (appShell) {
634 0 : appShell->SuspendNative();
635 : }
636 :
637 0 : nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(doc));
638 0 : bool fullPageMode = false;
639 0 : if (pDoc) {
640 0 : pDoc->GetWillHandleInstantiation(&fullPageMode);
641 : }
642 :
643 0 : if (fullPageMode) {
644 0 : nsCOMPtr<nsIStreamListener> stream;
645 0 : rv = pluginHost->InstantiateFullPagePlugin(aMimeType, aURI, this, getter_AddRefs(mInstanceOwner), getter_AddRefs(stream));
646 0 : if (NS_SUCCEEDED(rv)) {
647 0 : pDoc->SetStreamListener(stream);
648 : }
649 : } else {
650 0 : rv = pluginHost->InstantiateEmbeddedPlugin(aMimeType, aURI, this, getter_AddRefs(mInstanceOwner));
651 : }
652 :
653 0 : if (appShell) {
654 0 : appShell->ResumeNative();
655 : }
656 :
657 0 : if (NS_FAILED(rv)) {
658 0 : return rv;
659 : }
660 :
661 : // Set up scripting interfaces.
662 0 : NotifyContentObjectWrapper();
663 :
664 0 : nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
665 0 : GetPluginInstance(getter_AddRefs(pluginInstance));
666 0 : if (pluginInstance) {
667 0 : nsCOMPtr<nsIPluginTag> pluginTag;
668 0 : pluginHost->GetPluginTagForInstance(pluginInstance, getter_AddRefs(pluginTag));
669 :
670 : nsCOMPtr<nsIBlocklistService> blocklist =
671 0 : do_GetService("@mozilla.org/extensions/blocklist;1");
672 0 : if (blocklist) {
673 0 : PRUint32 blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
674 0 : blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
675 0 : EmptyString(), &blockState);
676 0 : if (blockState == nsIBlocklistService::STATE_OUTDATED)
677 0 : FirePluginError(thisContent, ePluginOutdated);
678 : }
679 : }
680 :
681 0 : return NS_OK;
682 : }
683 :
684 : void
685 0 : nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
686 : {
687 0 : if (!mInstanceOwner) {
688 0 : return;
689 : }
690 :
691 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
692 0 : nsIDocument* ownerDoc = thisContent->OwnerDoc();
693 0 : if (!ownerDoc->IsActive()) {
694 0 : StopPluginInstance();
695 : }
696 : }
697 :
698 : // nsIRequestObserver
699 : NS_IMETHODIMP
700 0 : nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
701 : nsISupports *aContext)
702 : {
703 0 : SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest");
704 :
705 0 : if (aRequest != mChannel || !aRequest) {
706 : // This is a bit of an edge case - happens when a new load starts before the
707 : // previous one got here
708 0 : return NS_BINDING_ABORTED;
709 : }
710 :
711 0 : AutoNotifier notifier(this, true);
712 :
713 0 : if (!IsSuccessfulRequest(aRequest)) {
714 0 : LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
715 0 : Fallback(false);
716 0 : return NS_BINDING_ABORTED;
717 : }
718 :
719 0 : nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
720 0 : NS_ASSERTION(chan, "Why is our request not a channel?");
721 :
722 0 : nsresult rv = NS_ERROR_UNEXPECTED;
723 : // This fallback variable MUST be declared after the notifier variable. Do NOT
724 : // change the order of the declarations!
725 0 : AutoFallback fallback(this, &rv);
726 :
727 0 : nsCString channelType;
728 0 : rv = chan->GetContentType(channelType);
729 0 : NS_ENSURE_SUCCESS(rv, rv);
730 :
731 0 : if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
732 0 : channelType = APPLICATION_OCTET_STREAM;
733 0 : chan->SetContentType(channelType);
734 : }
735 :
736 : // We want to use the channel type unless one of the following is true:
737 : //
738 : // 1) The channel type is application/octet-stream and we have a
739 : // type hint and the type hint is not a document type.
740 : // 2) Our type hint is a type that we support with a plugin.
741 0 : if ((channelType.EqualsASCII(APPLICATION_OCTET_STREAM) &&
742 0 : !mContentType.IsEmpty() &&
743 0 : GetTypeOfContent(mContentType) != eType_Document) ||
744 : // Need to check IsPluginEnabledForType() in addition to GetTypeOfContent()
745 : // because otherwise the default plug-in's catch-all behavior would
746 : // confuse things.
747 0 : (NS_SUCCEEDED(IsPluginEnabledForType(mContentType)) &&
748 0 : GetTypeOfContent(mContentType) == eType_Plugin)) {
749 : // Set the type we'll use for dispatch on the channel. Otherwise we could
750 : // end up trying to dispatch to a nsFrameLoader, which will complain that
751 : // it couldn't find a way to handle application/octet-stream
752 0 : nsCAutoString typeHint, dummy;
753 0 : NS_ParseContentType(mContentType, typeHint, dummy);
754 0 : if (!typeHint.IsEmpty()) {
755 0 : chan->SetContentType(typeHint);
756 : }
757 : } else {
758 0 : mContentType = channelType;
759 : }
760 :
761 0 : nsCOMPtr<nsIURI> uri;
762 0 : chan->GetURI(getter_AddRefs(uri));
763 :
764 0 : if (mContentType.EqualsASCII(APPLICATION_OCTET_STREAM)) {
765 0 : nsCAutoString extType;
766 0 : if (IsPluginEnabledByExtension(uri, extType)) {
767 0 : mContentType = extType;
768 0 : chan->SetContentType(extType);
769 : }
770 : }
771 :
772 : // Now find out what type the content is
773 : // UnloadContent will set our type to null; need to be sure to only set it to
774 : // the real value on success
775 0 : ObjectType newType = GetTypeOfContent(mContentType);
776 0 : LOG(("OBJLC [%p]: OnStartRequest: Content Type=<%s> Old type=%u New Type=%u\n",
777 : this, mContentType.get(), mType, newType));
778 :
779 : // Now do a content policy check
780 : // XXXbz this duplicates some code in nsContentBlocker::ShouldLoad
781 : PRInt32 contentPolicyType;
782 0 : switch (newType) {
783 : case eType_Image:
784 0 : contentPolicyType = nsIContentPolicy::TYPE_IMAGE;
785 0 : break;
786 : case eType_Document:
787 0 : contentPolicyType = nsIContentPolicy::TYPE_SUBDOCUMENT;
788 0 : break;
789 : default:
790 0 : contentPolicyType = nsIContentPolicy::TYPE_OBJECT;
791 0 : break;
792 : }
793 : nsCOMPtr<nsIContent> thisContent =
794 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
795 0 : NS_ASSERTION(thisContent, "must be a content");
796 :
797 0 : nsIDocument* doc = thisContent->OwnerDoc();
798 :
799 0 : PRInt16 shouldProcess = nsIContentPolicy::ACCEPT;
800 : rv =
801 : NS_CheckContentProcessPolicy(contentPolicyType,
802 : uri,
803 : doc->NodePrincipal(),
804 : static_cast<nsIImageLoadingContent*>(this),
805 : mContentType,
806 : nsnull, //extra
807 : &shouldProcess,
808 : nsContentUtils::GetContentPolicy(),
809 0 : nsContentUtils::GetSecurityManager());
810 0 : if (NS_FAILED(rv) || NS_CP_REJECTED(shouldProcess)) {
811 0 : HandleBeingBlockedByContentPolicy(rv, shouldProcess);
812 0 : rv = NS_OK; // otherwise, the AutoFallback will make us fall back
813 0 : return NS_BINDING_ABORTED;
814 : }
815 :
816 0 : if (mType != newType) {
817 0 : UnloadContent();
818 : }
819 :
820 0 : switch (newType) {
821 : case eType_Image:
822 0 : rv = LoadImageWithChannel(chan, getter_AddRefs(mFinalListener));
823 0 : NS_ENSURE_SUCCESS(rv, rv);
824 :
825 : // If we have a success result but no final listener, then the image is
826 : // cached. In that case, we can just return: No need to try to call the
827 : // final listener.
828 0 : if (!mFinalListener) {
829 0 : mType = newType;
830 0 : return NS_BINDING_ABORTED;
831 : }
832 0 : break;
833 : case eType_Document: {
834 0 : if (!mFrameLoader) {
835 0 : mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
836 0 : mNetworkCreated);
837 0 : if (!mFrameLoader) {
838 0 : Fallback(false);
839 0 : return NS_ERROR_UNEXPECTED;
840 : }
841 : }
842 :
843 0 : rv = mFrameLoader->CheckForRecursiveLoad(uri);
844 0 : if (NS_FAILED(rv)) {
845 0 : Fallback(false);
846 0 : return rv;
847 : }
848 :
849 0 : if (mType != newType) {
850 : // XXX We must call this before getting the docshell to work around
851 : // bug 300540; when that's fixed, this if statement can be removed.
852 0 : mType = newType;
853 0 : notifier.Notify();
854 :
855 0 : if (!mFrameLoader) {
856 : // mFrameLoader got nulled out when we notified, which most
857 : // likely means the node was removed from the
858 : // document. Abort the load that just started.
859 0 : return NS_BINDING_ABORTED;
860 : }
861 : }
862 :
863 : // We're loading a document, so we have to set LOAD_DOCUMENT_URI
864 : // (especially important for firing onload)
865 0 : nsLoadFlags flags = 0;
866 0 : chan->GetLoadFlags(&flags);
867 0 : flags |= nsIChannel::LOAD_DOCUMENT_URI;
868 0 : chan->SetLoadFlags(flags);
869 :
870 0 : nsCOMPtr<nsIDocShell> docShell;
871 0 : rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
872 0 : NS_ENSURE_SUCCESS(rv, rv);
873 :
874 0 : nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
875 0 : NS_ASSERTION(req, "Docshell must be an ifreq");
876 :
877 : nsCOMPtr<nsIURILoader>
878 0 : uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
879 0 : NS_ENSURE_SUCCESS(rv, rv);
880 0 : rv = uriLoader->OpenChannel(chan, nsIURILoader::DONT_RETARGET, req,
881 0 : getter_AddRefs(mFinalListener));
882 0 : break;
883 : }
884 : case eType_Plugin: {
885 0 : if (mType != newType) {
886 0 : mType = newType;
887 0 : notifier.Notify();
888 : }
889 0 : nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
890 0 : nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
891 0 : if (!pluginHost) {
892 0 : return NS_ERROR_NOT_AVAILABLE;
893 : }
894 0 : pluginHost->CreateListenerForChannel(chan, this, getter_AddRefs(mFinalListener));
895 0 : break;
896 : }
897 : case eType_Loading:
898 0 : NS_NOTREACHED("Should not have a loading type here!");
899 : case eType_Null:
900 : // Need to fallback here (instead of using the case below), so that we can
901 : // set mFallbackReason without it being overwritten. This is also why we
902 : // return early.
903 0 : Fallback(false);
904 :
905 : PluginSupportState pluginState = GetPluginSupportState(thisContent,
906 0 : mContentType);
907 : // Do nothing, but fire the plugin not found event if needed
908 0 : if (pluginState != ePluginOtherState) {
909 0 : mFallbackReason = pluginState;
910 0 : FirePluginError(thisContent, pluginState);
911 : }
912 0 : return NS_BINDING_ABORTED;
913 : }
914 :
915 0 : if (mFinalListener) {
916 0 : mType = newType;
917 :
918 0 : mSrcStreamLoading = true;
919 0 : rv = mFinalListener->OnStartRequest(aRequest, aContext);
920 0 : mSrcStreamLoading = false;
921 :
922 0 : if (NS_SUCCEEDED(rv)) {
923 : // Plugins need to set up for NPRuntime.
924 0 : if (mType == eType_Plugin) {
925 0 : NotifyContentObjectWrapper();
926 : }
927 : } else {
928 : // Plugins don't fall back if there is an error here.
929 0 : if (mType == eType_Plugin) {
930 0 : rv = NS_OK; // this is necessary to avoid auto-fallback
931 0 : return NS_BINDING_ABORTED;
932 : }
933 0 : Fallback(false);
934 : }
935 :
936 0 : return rv;
937 : }
938 :
939 0 : Fallback(false);
940 0 : return NS_BINDING_ABORTED;
941 : }
942 :
943 : NS_IMETHODIMP
944 0 : nsObjectLoadingContent::OnStopRequest(nsIRequest *aRequest,
945 : nsISupports *aContext,
946 : nsresult aStatusCode)
947 : {
948 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
949 :
950 0 : if (aRequest != mChannel) {
951 0 : return NS_BINDING_ABORTED;
952 : }
953 :
954 0 : mChannel = nsnull;
955 :
956 0 : if (mFinalListener) {
957 0 : mFinalListener->OnStopRequest(aRequest, aContext, aStatusCode);
958 0 : mFinalListener = nsnull;
959 : }
960 :
961 : // Return value doesn't matter
962 0 : return NS_OK;
963 : }
964 :
965 :
966 : // nsIStreamListener
967 : NS_IMETHODIMP
968 0 : nsObjectLoadingContent::OnDataAvailable(nsIRequest *aRequest,
969 : nsISupports *aContext,
970 : nsIInputStream *aInputStream,
971 : PRUint32 aOffset, PRUint32 aCount)
972 : {
973 0 : NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
974 :
975 0 : if (aRequest != mChannel) {
976 0 : return NS_BINDING_ABORTED;
977 : }
978 :
979 0 : if (mFinalListener) {
980 0 : return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
981 : }
982 :
983 : // Abort this load if we have no listener here
984 0 : return NS_ERROR_UNEXPECTED;
985 : }
986 :
987 : // nsIFrameLoaderOwner
988 : NS_IMETHODIMP
989 0 : nsObjectLoadingContent::GetFrameLoader(nsIFrameLoader** aFrameLoader)
990 : {
991 0 : *aFrameLoader = mFrameLoader;
992 0 : NS_IF_ADDREF(*aFrameLoader);
993 0 : return NS_OK;
994 : }
995 :
996 : NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
997 0 : nsObjectLoadingContent::GetFrameLoader()
998 : {
999 0 : nsFrameLoader* loader = mFrameLoader;
1000 0 : NS_IF_ADDREF(loader);
1001 0 : return loader;
1002 : }
1003 :
1004 : NS_IMETHODIMP
1005 0 : nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
1006 : {
1007 0 : return NS_ERROR_NOT_IMPLEMENTED;
1008 : }
1009 :
1010 : // nsIObjectLoadingContent
1011 : NS_IMETHODIMP
1012 0 : nsObjectLoadingContent::GetActualType(nsACString& aType)
1013 : {
1014 0 : aType = mContentType;
1015 0 : return NS_OK;
1016 : }
1017 :
1018 : NS_IMETHODIMP
1019 0 : nsObjectLoadingContent::GetDisplayedType(PRUint32* aType)
1020 : {
1021 0 : *aType = mType;
1022 0 : return NS_OK;
1023 : }
1024 :
1025 : NS_IMETHODIMP
1026 0 : nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
1027 : {
1028 : // Not having an instance yet is OK, but try to start one now that
1029 : // we have a frame.
1030 0 : if (!mInstanceOwner) {
1031 0 : AsyncStartPluginInstance();
1032 0 : return NS_OK;
1033 : }
1034 :
1035 : // Disconnect any existing frame
1036 0 : DisconnectFrame();
1037 :
1038 : // Set up relationship between instance owner and frame.
1039 0 : nsObjectFrame *objFrame = static_cast<nsObjectFrame*>(aFrame);
1040 0 : mInstanceOwner->SetFrame(objFrame);
1041 :
1042 : // Set up new frame to draw.
1043 0 : objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size());
1044 0 : objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf());
1045 :
1046 0 : return NS_OK;
1047 : }
1048 :
1049 : NS_IMETHODIMP
1050 0 : nsObjectLoadingContent::DisconnectFrame()
1051 : {
1052 0 : if (mInstanceOwner) {
1053 0 : mInstanceOwner->SetFrame(nsnull);
1054 : }
1055 0 : return NS_OK;
1056 : }
1057 :
1058 : NS_IMETHODIMP
1059 0 : nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance)
1060 : {
1061 0 : *aInstance = nsnull;
1062 :
1063 0 : if (!mInstanceOwner) {
1064 0 : return NS_OK;
1065 : }
1066 :
1067 0 : return mInstanceOwner->GetInstance(aInstance);
1068 : }
1069 :
1070 : NS_IMETHODIMP
1071 0 : nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
1072 : PRUint32* aType)
1073 : {
1074 0 : *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
1075 0 : return NS_OK;
1076 : }
1077 :
1078 : // nsIInterfaceRequestor
1079 : NS_IMETHODIMP
1080 0 : nsObjectLoadingContent::GetInterface(const nsIID & aIID, void **aResult)
1081 : {
1082 0 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1083 0 : nsIChannelEventSink* sink = this;
1084 0 : *aResult = sink;
1085 0 : NS_ADDREF(sink);
1086 0 : return NS_OK;
1087 : }
1088 0 : return NS_NOINTERFACE;
1089 : }
1090 :
1091 : // nsIChannelEventSink
1092 : NS_IMETHODIMP
1093 0 : nsObjectLoadingContent::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
1094 : nsIChannel *aNewChannel,
1095 : PRUint32 aFlags,
1096 : nsIAsyncVerifyRedirectCallback *cb)
1097 : {
1098 : // If we're already busy with a new load, or have no load at all,
1099 : // cancel the redirect.
1100 0 : if (!mChannel || aOldChannel != mChannel) {
1101 0 : return NS_BINDING_ABORTED;
1102 : }
1103 :
1104 0 : mChannel = aNewChannel;
1105 0 : cb->OnRedirectVerifyCallback(NS_OK);
1106 0 : return NS_OK;
1107 : }
1108 :
1109 : // <public>
1110 : nsEventStates
1111 0 : nsObjectLoadingContent::ObjectState() const
1112 : {
1113 0 : switch (mType) {
1114 : case eType_Loading:
1115 0 : return NS_EVENT_STATE_LOADING;
1116 : case eType_Image:
1117 0 : return ImageState();
1118 : case eType_Plugin:
1119 : case eType_Document:
1120 : // These are OK. If documents start to load successfully, they display
1121 : // something, and are thus not broken in this sense. The same goes for
1122 : // plugins.
1123 0 : return nsEventStates();
1124 : case eType_Null:
1125 0 : if (mSuppressed)
1126 0 : return NS_EVENT_STATE_SUPPRESSED;
1127 0 : if (mUserDisabled)
1128 0 : return NS_EVENT_STATE_USERDISABLED;
1129 :
1130 : // Otherwise, broken
1131 0 : nsEventStates state = NS_EVENT_STATE_BROKEN;
1132 0 : switch (mFallbackReason) {
1133 : case ePluginClickToPlay:
1134 0 : return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
1135 : case ePluginDisabled:
1136 0 : state |= NS_EVENT_STATE_HANDLER_DISABLED;
1137 0 : break;
1138 : case ePluginBlocklisted:
1139 0 : state |= NS_EVENT_STATE_HANDLER_BLOCKED;
1140 0 : break;
1141 : case ePluginCrashed:
1142 0 : state |= NS_EVENT_STATE_HANDLER_CRASHED;
1143 0 : break;
1144 : case ePluginUnsupported:
1145 0 : state |= NS_EVENT_STATE_TYPE_UNSUPPORTED;
1146 0 : break;
1147 : case ePluginOutdated:
1148 : case ePluginOtherState:
1149 : // Do nothing, but avoid a compile warning
1150 0 : break;
1151 : }
1152 0 : return state;
1153 : };
1154 0 : NS_NOTREACHED("unknown type?");
1155 : // this return statement only exists to avoid a compile warning
1156 0 : return nsEventStates();
1157 : }
1158 :
1159 : // <protected>
1160 : nsresult
1161 0 : nsObjectLoadingContent::LoadObject(const nsAString& aURI,
1162 : bool aNotify,
1163 : const nsCString& aTypeHint,
1164 : bool aForceLoad)
1165 : {
1166 0 : LOG(("OBJLC [%p]: Loading object: URI string=<%s> notify=%i type=<%s> forceload=%i\n",
1167 : this, NS_ConvertUTF16toUTF8(aURI).get(), aNotify, aTypeHint.get(), aForceLoad));
1168 :
1169 : // Avoid StringToURI in order to use the codebase attribute as base URI
1170 : nsCOMPtr<nsIContent> thisContent =
1171 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1172 0 : NS_ASSERTION(thisContent, "must be a content");
1173 :
1174 0 : nsIDocument* doc = thisContent->OwnerDoc();
1175 0 : nsCOMPtr<nsIURI> baseURI;
1176 0 : GetObjectBaseURI(thisContent, getter_AddRefs(baseURI));
1177 :
1178 0 : nsCOMPtr<nsIURI> uri;
1179 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
1180 : aURI, doc,
1181 0 : baseURI);
1182 : // If URI creation failed, fallback immediately - this only happens for
1183 : // malformed URIs
1184 0 : if (!uri) {
1185 0 : Fallback(aNotify);
1186 0 : return NS_OK;
1187 : }
1188 :
1189 0 : NS_TryToSetImmutable(uri);
1190 :
1191 0 : return LoadObject(uri, aNotify, aTypeHint, aForceLoad);
1192 : }
1193 :
1194 : void
1195 0 : nsObjectLoadingContent::UpdateFallbackState(nsIContent* aContent,
1196 : AutoFallback& fallback,
1197 : const nsCString& aTypeHint)
1198 : {
1199 : // Notify the UI and update the fallback state
1200 0 : PluginSupportState state = GetPluginSupportState(aContent, aTypeHint);
1201 0 : if (state != ePluginOtherState) {
1202 0 : fallback.SetPluginState(state);
1203 0 : FirePluginError(aContent, state);
1204 : }
1205 0 : }
1206 :
1207 : nsresult
1208 0 : nsObjectLoadingContent::LoadObject(nsIURI* aURI,
1209 : bool aNotify,
1210 : const nsCString& aTypeHint,
1211 : bool aForceLoad)
1212 : {
1213 : // Only do a URI equality check for things that aren't stopped plugins.
1214 : // This is because we still need to load again if the plugin has been stopped.
1215 0 : if (mType == eType_Document || mType == eType_Image || mInstanceOwner) {
1216 0 : if (mURI && aURI) {
1217 : bool equal;
1218 0 : nsresult rv = mURI->Equals(aURI, &equal);
1219 0 : if (NS_SUCCEEDED(rv) && equal && !aForceLoad) {
1220 : // URI didn't change, do nothing
1221 0 : return NS_OK;
1222 : }
1223 0 : StopPluginInstance();
1224 : }
1225 : }
1226 :
1227 : // Need to revoke any potentially pending instantiate events
1228 0 : if (mType == eType_Plugin && mPendingInstantiateEvent) {
1229 0 : mPendingInstantiateEvent = nsnull;
1230 : }
1231 :
1232 0 : AutoNotifier notifier(this, aNotify);
1233 :
1234 0 : mUserDisabled = mSuppressed = false;
1235 :
1236 0 : mURI = aURI;
1237 0 : mContentType = aTypeHint;
1238 :
1239 : nsCOMPtr<nsIContent> thisContent =
1240 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1241 0 : NS_ASSERTION(thisContent, "must be a content");
1242 :
1243 0 : nsIDocument* doc = thisContent->OwnerDoc();
1244 0 : if (doc->IsBeingUsedAsImage()) {
1245 0 : return NS_OK;
1246 : }
1247 :
1248 : // From here on, we will always change the content. This means that a
1249 : // possibly-loading channel should be aborted.
1250 0 : if (mChannel) {
1251 0 : LOG(("OBJLC [%p]: Cancelling existing load\n", this));
1252 :
1253 : // These three statements are carefully ordered:
1254 : // - onStopRequest should get a channel whose status is the same as the
1255 : // status argument
1256 : // - onStopRequest must get a non-null channel
1257 0 : mChannel->Cancel(NS_BINDING_ABORTED);
1258 0 : if (mFinalListener) {
1259 : // NOTE: Since mFinalListener is only set in onStartRequest, which takes
1260 : // care of calling mFinalListener->OnStartRequest, mFinalListener is only
1261 : // non-null here if onStartRequest was already called.
1262 0 : mFinalListener->OnStopRequest(mChannel, nsnull, NS_BINDING_ABORTED);
1263 0 : mFinalListener = nsnull;
1264 : }
1265 0 : mChannel = nsnull;
1266 : }
1267 :
1268 : // Security checks
1269 0 : if (doc->IsLoadedAsData()) {
1270 0 : if (!doc->IsStaticDocument()) {
1271 0 : Fallback(false);
1272 : }
1273 0 : return NS_OK;
1274 : }
1275 :
1276 : // Can't do security checks without a URI - hopefully the plugin will take
1277 : // care of that
1278 : // Null URIs happen when the URL to load is specified via other means than the
1279 : // data/src attribute, for example via custom <param> elements.
1280 0 : if (aURI) {
1281 0 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
1282 0 : NS_ASSERTION(secMan, "No security manager!?");
1283 : nsresult rv =
1284 0 : secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), aURI, 0);
1285 0 : if (NS_FAILED(rv)) {
1286 0 : Fallback(false);
1287 0 : return NS_OK;
1288 : }
1289 :
1290 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit
1291 : rv =
1292 : NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
1293 : aURI,
1294 : doc->NodePrincipal(),
1295 : static_cast<nsIImageLoadingContent*>(this),
1296 : aTypeHint,
1297 : nsnull, //extra
1298 : &shouldLoad,
1299 : nsContentUtils::GetContentPolicy(),
1300 0 : secMan);
1301 0 : if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
1302 0 : HandleBeingBlockedByContentPolicy(rv, shouldLoad);
1303 0 : return NS_OK;
1304 : }
1305 : }
1306 :
1307 0 : nsresult rv = NS_ERROR_UNEXPECTED;
1308 : // This fallback variable MUST be declared after the notifier variable. Do NOT
1309 : // change the order of the declarations!
1310 0 : AutoFallback fallback(this, &rv);
1311 :
1312 0 : PRUint32 caps = GetCapabilities();
1313 0 : LOG(("OBJLC [%p]: Capabilities: %04x\n", this, caps));
1314 :
1315 0 : nsCAutoString overrideType;
1316 0 : if ((caps & eOverrideServerType) &&
1317 0 : ((!aTypeHint.IsEmpty() && NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) ||
1318 0 : (aURI && IsPluginEnabledByExtension(aURI, overrideType)))) {
1319 : ObjectType newType;
1320 0 : if (overrideType.IsEmpty()) {
1321 0 : newType = GetTypeOfContent(aTypeHint);
1322 : } else {
1323 0 : mContentType = overrideType;
1324 0 : newType = eType_Plugin;
1325 : }
1326 :
1327 0 : if (newType != mType) {
1328 0 : LOG(("OBJLC [%p]: (eOverrideServerType) Changing type from %u to %u\n", this, mType, newType));
1329 :
1330 0 : UnloadContent();
1331 :
1332 : // Must have a frameloader before creating a frame, or the frame will
1333 : // create its own.
1334 0 : if (!mFrameLoader && newType == eType_Document) {
1335 0 : mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
1336 0 : mNetworkCreated);
1337 0 : if (!mFrameLoader) {
1338 0 : mURI = nsnull;
1339 0 : return NS_OK;
1340 : }
1341 : }
1342 :
1343 : // Must notify here for plugins
1344 : // If aNotify is false, we'll just wait until we get a frame and use the
1345 : // async instantiate path.
1346 : // XXX is this still needed? (for documents?)
1347 0 : mType = newType;
1348 0 : if (aNotify)
1349 0 : notifier.Notify();
1350 : }
1351 0 : switch (newType) {
1352 : case eType_Image:
1353 : // Don't notify, because we will take care of that ourselves.
1354 0 : if (aURI) {
1355 0 : rv = LoadImage(aURI, aForceLoad, false);
1356 : } else {
1357 0 : rv = NS_ERROR_NOT_AVAILABLE;
1358 : }
1359 0 : break;
1360 : case eType_Plugin:
1361 0 : rv = AsyncStartPluginInstance();
1362 0 : break;
1363 : case eType_Document:
1364 0 : if (aURI) {
1365 0 : rv = mFrameLoader->LoadURI(aURI);
1366 : } else {
1367 0 : rv = NS_ERROR_NOT_AVAILABLE;
1368 : }
1369 0 : break;
1370 : case eType_Loading:
1371 0 : NS_NOTREACHED("Should not have a loading type here!");
1372 : case eType_Null:
1373 : // No need to load anything, notify of the failure.
1374 0 : UpdateFallbackState(thisContent, fallback, aTypeHint);
1375 0 : break;
1376 : };
1377 0 : return NS_OK;
1378 : }
1379 :
1380 : // If the class ID specifies a supported plugin, or if we have no explicit URI
1381 : // but a type, immediately instantiate the plugin.
1382 0 : bool isSupportedClassID = false;
1383 0 : nsCAutoString typeForID; // Will be set iff isSupportedClassID == true
1384 0 : bool hasID = false;
1385 0 : if (caps & eSupportClassID) {
1386 0 : nsAutoString classid;
1387 0 : thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classid);
1388 0 : if (!classid.IsEmpty()) {
1389 0 : hasID = true;
1390 0 : isSupportedClassID = NS_SUCCEEDED(TypeForClassID(classid, typeForID));
1391 : }
1392 : }
1393 :
1394 0 : if (hasID && !isSupportedClassID) {
1395 : // We have a class ID and it's unsupported. Fallback in that case.
1396 0 : rv = NS_ERROR_NOT_AVAILABLE;
1397 0 : return NS_OK;
1398 : }
1399 :
1400 0 : if (isSupportedClassID ||
1401 0 : (!aURI && !aTypeHint.IsEmpty() &&
1402 0 : GetTypeOfContent(aTypeHint) == eType_Plugin)) {
1403 : // No URI, but we have a type. The plugin will handle the load.
1404 : // Or: supported class id, plugin will handle the load.
1405 0 : mType = eType_Plugin;
1406 :
1407 : // At this point, the stored content type
1408 : // must be equal to our type hint. Similar,
1409 : // our URI must be the requested URI.
1410 : // (->Equals would suffice, but == is cheaper
1411 : // and handles NULL)
1412 0 : NS_ASSERTION(mContentType.Equals(aTypeHint), "mContentType wrong!");
1413 0 : NS_ASSERTION(mURI == aURI, "mURI wrong!");
1414 :
1415 0 : if (isSupportedClassID) {
1416 : // Use the classid's type
1417 0 : NS_ASSERTION(!typeForID.IsEmpty(), "Must have a real type!");
1418 0 : mContentType = typeForID;
1419 : // XXX(biesi). The plugin instantiation code used to pass the base URI
1420 : // here instead of the plugin URI for instantiation via class ID, so I
1421 : // continue to do so. Why that is, no idea...
1422 0 : GetObjectBaseURI(thisContent, getter_AddRefs(mURI));
1423 0 : if (!mURI) {
1424 0 : mURI = aURI;
1425 : }
1426 : }
1427 :
1428 : // rv is references by a stack-based object, need to assign here
1429 0 : rv = AsyncStartPluginInstance();
1430 :
1431 0 : return rv;
1432 : }
1433 :
1434 0 : if (!aURI) {
1435 : // No URI and if we have got this far no enabled plugin supports the type
1436 0 : rv = NS_ERROR_NOT_AVAILABLE;
1437 :
1438 : // We should only notify the UI if there is at least a type to go on for
1439 : // finding a plugin to use, unless it's a supported image or document type.
1440 0 : if (!aTypeHint.IsEmpty() && GetTypeOfContent(aTypeHint) == eType_Null) {
1441 0 : UpdateFallbackState(thisContent, fallback, aTypeHint);
1442 : }
1443 :
1444 0 : return NS_OK;
1445 : }
1446 :
1447 : // E.g. mms://
1448 0 : if (!CanHandleURI(aURI)) {
1449 0 : if (aTypeHint.IsEmpty()) {
1450 0 : rv = NS_ERROR_NOT_AVAILABLE;
1451 0 : return NS_OK;
1452 : }
1453 :
1454 0 : if (NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) {
1455 0 : mType = eType_Plugin;
1456 : } else {
1457 0 : rv = NS_ERROR_NOT_AVAILABLE;
1458 : // No plugin to load, notify of the failure.
1459 0 : UpdateFallbackState(thisContent, fallback, aTypeHint);
1460 : }
1461 :
1462 0 : return NS_OK;
1463 : }
1464 :
1465 0 : nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
1466 0 : nsCOMPtr<nsIChannel> chan;
1467 0 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
1468 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
1469 0 : rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
1470 0 : NS_ENSURE_SUCCESS(rv, rv);
1471 0 : if (csp) {
1472 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
1473 0 : channelPolicy->SetContentSecurityPolicy(csp);
1474 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT);
1475 : }
1476 0 : rv = NS_NewChannel(getter_AddRefs(chan), aURI, nsnull, group, this,
1477 : nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
1478 : nsIChannel::LOAD_CLASSIFY_URI,
1479 0 : channelPolicy);
1480 0 : NS_ENSURE_SUCCESS(rv, rv);
1481 :
1482 : // Referrer
1483 0 : nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
1484 0 : if (httpChan) {
1485 0 : httpChan->SetReferrer(doc->GetDocumentURI());
1486 : }
1487 :
1488 : // MIME Type hint
1489 0 : if (!aTypeHint.IsEmpty()) {
1490 0 : nsCAutoString typeHint, dummy;
1491 0 : NS_ParseContentType(aTypeHint, typeHint, dummy);
1492 0 : if (!typeHint.IsEmpty()) {
1493 0 : chan->SetContentType(typeHint);
1494 : }
1495 : }
1496 :
1497 : // Set up the channel's principal and such, like nsDocShell::DoURILoad does
1498 0 : nsContentUtils::SetUpChannelOwner(thisContent->NodePrincipal(),
1499 0 : chan, aURI, true);
1500 :
1501 0 : nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
1502 0 : if (scriptChannel) {
1503 : // Allow execution against our context if the principals match
1504 0 : scriptChannel->
1505 0 : SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
1506 : }
1507 :
1508 : // AsyncOpen can fail if a file does not exist.
1509 : // Show fallback content in that case.
1510 0 : rv = chan->AsyncOpen(this, nsnull);
1511 0 : if (NS_SUCCEEDED(rv)) {
1512 0 : LOG(("OBJLC [%p]: Channel opened.\n", this));
1513 :
1514 0 : mChannel = chan;
1515 0 : mType = eType_Loading;
1516 : }
1517 0 : return NS_OK;
1518 : }
1519 :
1520 : PRUint32
1521 0 : nsObjectLoadingContent::GetCapabilities() const
1522 : {
1523 : return eSupportImages |
1524 : eSupportPlugins |
1525 : eSupportDocuments |
1526 0 : eSupportSVG;
1527 : }
1528 :
1529 : void
1530 0 : nsObjectLoadingContent::Fallback(bool aNotify)
1531 : {
1532 0 : AutoNotifier notifier(this, aNotify);
1533 :
1534 0 : UnloadContent();
1535 0 : }
1536 :
1537 : void
1538 0 : nsObjectLoadingContent::RemovedFromDocument()
1539 : {
1540 0 : if (mFrameLoader) {
1541 : // XXX This is very temporary and must go away
1542 0 : mFrameLoader->Destroy();
1543 0 : mFrameLoader = nsnull;
1544 :
1545 : // Clear the current URI, so that LoadObject doesn't think that we
1546 : // have already loaded the content.
1547 0 : mURI = nsnull;
1548 : }
1549 :
1550 : // When a plugin instance node is removed from the document we'll
1551 : // let the plugin continue to run at least until we get back to
1552 : // the event loop. If we get back to the event loop and the node
1553 : // has still not been added back to the document then we stop
1554 : // the plugin.
1555 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1556 0 : nsCOMPtr<nsIRunnable> event = new InDocCheckEvent(thisContent);
1557 :
1558 0 : nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1559 0 : if (appShell) {
1560 0 : appShell->RunInStableState(event);
1561 : }
1562 0 : }
1563 :
1564 : /* static */
1565 : void
1566 0 : nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp,
1567 : nsCycleCollectionTraversalCallback &cb)
1568 : {
1569 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameLoader");
1570 0 : cb.NoteXPCOMChild(static_cast<nsIFrameLoader*>(tmp->mFrameLoader));
1571 0 : }
1572 :
1573 : // <private>
1574 : /* static */ bool
1575 0 : nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest)
1576 : {
1577 : nsresult status;
1578 0 : nsresult rv = aRequest->GetStatus(&status);
1579 0 : if (NS_FAILED(rv) || NS_FAILED(status)) {
1580 0 : return false;
1581 : }
1582 :
1583 : // This may still be an error page or somesuch
1584 0 : nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
1585 0 : if (httpChan) {
1586 : bool success;
1587 0 : rv = httpChan->GetRequestSucceeded(&success);
1588 0 : if (NS_FAILED(rv) || !success) {
1589 0 : return false;
1590 : }
1591 : }
1592 :
1593 : // Otherwise, the request is successful
1594 0 : return true;
1595 : }
1596 :
1597 : /* static */ bool
1598 0 : nsObjectLoadingContent::CanHandleURI(nsIURI* aURI)
1599 : {
1600 0 : nsCAutoString scheme;
1601 0 : if (NS_FAILED(aURI->GetScheme(scheme))) {
1602 0 : return false;
1603 : }
1604 :
1605 0 : nsIIOService* ios = nsContentUtils::GetIOService();
1606 0 : if (!ios)
1607 0 : return false;
1608 :
1609 0 : nsCOMPtr<nsIProtocolHandler> handler;
1610 0 : ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
1611 0 : if (!handler) {
1612 0 : return false;
1613 : }
1614 :
1615 : nsCOMPtr<nsIExternalProtocolHandler> extHandler =
1616 0 : do_QueryInterface(handler);
1617 : // We can handle this URI if its protocol handler is not the external one
1618 0 : return extHandler == nsnull;
1619 : }
1620 :
1621 : bool
1622 0 : nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
1623 : {
1624 : nsCOMPtr<nsIContent> thisContent =
1625 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1626 0 : NS_ASSERTION(thisContent, "must be a content");
1627 :
1628 : nsresult rv;
1629 : nsCOMPtr<nsIWebNavigationInfo> info(
1630 0 : do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv));
1631 : PRUint32 supported;
1632 0 : if (info) {
1633 0 : nsCOMPtr<nsIWebNavigation> webNav;
1634 0 : nsIDocument* currentDoc = thisContent->GetCurrentDoc();
1635 0 : if (currentDoc) {
1636 0 : webNav = do_GetInterface(currentDoc->GetScriptGlobalObject());
1637 : }
1638 0 : rv = info->IsTypeSupported(aMimeType, webNav, &supported);
1639 : }
1640 :
1641 0 : if (NS_SUCCEEDED(rv)) {
1642 0 : if (supported == nsIWebNavigationInfo::UNSUPPORTED) {
1643 : // Try a stream converter
1644 : // NOTE: We treat any type we can convert from as a supported type. If a
1645 : // type is not actually supported, the URI loader will detect that and
1646 : // return an error, and we'll fallback.
1647 : nsCOMPtr<nsIStreamConverterService> convServ =
1648 0 : do_GetService("@mozilla.org/streamConverters;1");
1649 0 : bool canConvert = false;
1650 0 : if (convServ) {
1651 0 : rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
1652 : }
1653 :
1654 0 : return NS_SUCCEEDED(rv) && canConvert;
1655 : }
1656 :
1657 : // Don't want to support plugins as documents
1658 0 : return supported != nsIWebNavigationInfo::PLUGIN;
1659 : }
1660 :
1661 0 : return false;
1662 : }
1663 :
1664 : void
1665 0 : nsObjectLoadingContent::UnloadContent()
1666 : {
1667 : // Don't notify in CancelImageRequests. We do it ourselves.
1668 0 : CancelImageRequests(false);
1669 0 : if (mFrameLoader) {
1670 0 : mFrameLoader->Destroy();
1671 0 : mFrameLoader = nsnull;
1672 : }
1673 0 : mType = eType_Null;
1674 0 : mUserDisabled = mSuppressed = false;
1675 0 : mFallbackReason = ePluginOtherState;
1676 0 : }
1677 :
1678 : void
1679 0 : nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
1680 : nsEventStates aOldState,
1681 : bool aSync,
1682 : bool aNotify)
1683 : {
1684 0 : LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx) (sync=%i)\n",
1685 : this, aOldType, aOldState.GetInternalValue(), mType,
1686 : ObjectState().GetInternalValue(), aSync));
1687 :
1688 : nsCOMPtr<nsIContent> thisContent =
1689 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1690 0 : NS_ASSERTION(thisContent, "must be a content");
1691 :
1692 0 : NS_ASSERTION(thisContent->IsElement(), "Not an element?");
1693 :
1694 : // Unfortunately, we do some state changes without notifying
1695 : // (e.g. in Fallback when canceling image requests), so we have to
1696 : // manually notify object state changes.
1697 0 : thisContent->AsElement()->UpdateState(false);
1698 :
1699 0 : if (!aNotify) {
1700 : // We're done here
1701 : return;
1702 : }
1703 :
1704 0 : nsIDocument* doc = thisContent->GetCurrentDoc();
1705 0 : if (!doc) {
1706 : return; // Nothing to do
1707 : }
1708 :
1709 0 : nsEventStates newState = ObjectState();
1710 :
1711 0 : if (newState != aOldState) {
1712 : // This will trigger frame construction
1713 0 : NS_ASSERTION(thisContent->IsInDoc(), "Something is confused");
1714 0 : nsEventStates changedBits = aOldState ^ newState;
1715 :
1716 : {
1717 0 : nsAutoScriptBlocker scriptBlocker;
1718 0 : doc->ContentStateChanged(thisContent, changedBits);
1719 : }
1720 0 : if (aSync) {
1721 : // Make sure that frames are actually constructed immediately.
1722 0 : doc->FlushPendingNotifications(Flush_Frames);
1723 : }
1724 0 : } else if (aOldType != mType) {
1725 : // If our state changed, then we already recreated frames
1726 : // Otherwise, need to do that here
1727 0 : nsCOMPtr<nsIPresShell> shell = doc->GetShell();
1728 0 : if (shell) {
1729 0 : shell->RecreateFramesFor(thisContent);
1730 : }
1731 : }
1732 : }
1733 :
1734 : /* static */ void
1735 0 : nsObjectLoadingContent::FirePluginError(nsIContent* thisContent,
1736 : PluginSupportState state)
1737 : {
1738 0 : LOG(("OBJLC []: Dispatching nsPluginErrorEvent for content %p\n",
1739 : thisContent));
1740 :
1741 0 : nsCOMPtr<nsIRunnable> ev = new nsPluginErrorEvent(thisContent, state);
1742 0 : nsresult rv = NS_DispatchToCurrentThread(ev);
1743 0 : if (NS_FAILED(rv)) {
1744 0 : NS_WARNING("failed to dispatch nsPluginErrorEvent");
1745 : }
1746 0 : }
1747 :
1748 : nsObjectLoadingContent::ObjectType
1749 0 : nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
1750 : {
1751 0 : PRUint32 caps = GetCapabilities();
1752 :
1753 0 : if ((caps & eSupportImages) && IsSupportedImage(aMIMEType)) {
1754 0 : return eType_Image;
1755 : }
1756 :
1757 0 : bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml");
1758 0 : bool supportedSVG = isSVG && (caps & eSupportSVG);
1759 0 : if (((caps & eSupportDocuments) || supportedSVG) &&
1760 0 : IsSupportedDocument(aMIMEType)) {
1761 0 : return eType_Document;
1762 : }
1763 :
1764 0 : if ((caps & eSupportPlugins) && NS_SUCCEEDED(IsPluginEnabledForType(aMIMEType))) {
1765 0 : return eType_Plugin;
1766 : }
1767 :
1768 0 : return eType_Null;
1769 : }
1770 :
1771 : nsresult
1772 0 : nsObjectLoadingContent::TypeForClassID(const nsAString& aClassID,
1773 : nsACString& aType)
1774 : {
1775 0 : if (StringBeginsWith(aClassID, NS_LITERAL_STRING("java:"))) {
1776 : // Supported if we have a java plugin
1777 0 : aType.AssignLiteral("application/x-java-vm");
1778 0 : nsresult rv = IsPluginEnabledForType(NS_LITERAL_CSTRING("application/x-java-vm"));
1779 0 : return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE;
1780 : }
1781 :
1782 : // If it starts with "clsid:", this is ActiveX content
1783 0 : if (StringBeginsWith(aClassID, NS_LITERAL_STRING("clsid:"), nsCaseInsensitiveStringComparator())) {
1784 : // Check if we have a plugin for that
1785 :
1786 0 : if (NS_SUCCEEDED(IsPluginEnabledForType(NS_LITERAL_CSTRING("application/x-oleobject")))) {
1787 0 : aType.AssignLiteral("application/x-oleobject");
1788 0 : return NS_OK;
1789 : }
1790 0 : if (NS_SUCCEEDED(IsPluginEnabledForType(NS_LITERAL_CSTRING("application/oleobject")))) {
1791 0 : aType.AssignLiteral("application/oleobject");
1792 0 : return NS_OK;
1793 : }
1794 : }
1795 :
1796 0 : return NS_ERROR_NOT_AVAILABLE;
1797 : }
1798 :
1799 : void
1800 0 : nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI)
1801 : {
1802 : // We want to use swap(); since this is just called from this file,
1803 : // we can assert this (callers use comptrs)
1804 0 : NS_PRECONDITION(*aURI == nsnull, "URI must be inited to zero");
1805 :
1806 : // For plugins, the codebase attribute is the base URI
1807 0 : nsCOMPtr<nsIURI> baseURI = thisContent->GetBaseURI();
1808 0 : nsAutoString codebase;
1809 : thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase,
1810 0 : codebase);
1811 0 : if (!codebase.IsEmpty()) {
1812 : nsContentUtils::NewURIWithDocumentCharset(aURI, codebase,
1813 : thisContent->OwnerDoc(),
1814 0 : baseURI);
1815 : } else {
1816 0 : baseURI.swap(*aURI);
1817 : }
1818 0 : }
1819 :
1820 : nsObjectFrame*
1821 0 : nsObjectLoadingContent::GetExistingFrame()
1822 : {
1823 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1824 0 : nsIFrame* frame = thisContent->GetPrimaryFrame();
1825 0 : nsIObjectFrame* objFrame = do_QueryFrame(frame);
1826 0 : return static_cast<nsObjectFrame*>(objFrame);
1827 : }
1828 :
1829 : void
1830 0 : nsObjectLoadingContent::HandleBeingBlockedByContentPolicy(nsresult aStatus,
1831 : PRInt16 aRetval)
1832 : {
1833 : // Must call UnloadContent first, as it overwrites
1834 : // mSuppressed/mUserDisabled. It also takes care of setting the type to
1835 : // eType_Null.
1836 0 : UnloadContent();
1837 0 : if (NS_SUCCEEDED(aStatus)) {
1838 0 : if (aRetval == nsIContentPolicy::REJECT_TYPE) {
1839 0 : mUserDisabled = true;
1840 0 : } else if (aRetval == nsIContentPolicy::REJECT_SERVER) {
1841 0 : mSuppressed = true;
1842 : }
1843 : }
1844 0 : }
1845 :
1846 : PluginSupportState
1847 0 : nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
1848 : const nsCString& aContentType)
1849 : {
1850 0 : if (!aContent->IsHTML()) {
1851 0 : return ePluginOtherState;
1852 : }
1853 :
1854 0 : if (aContent->Tag() == nsGkAtoms::embed ||
1855 0 : aContent->Tag() == nsGkAtoms::applet) {
1856 0 : return GetPluginDisabledState(aContentType);
1857 : }
1858 :
1859 0 : bool hasAlternateContent = false;
1860 :
1861 : // Search for a child <param> with a pluginurl name
1862 0 : for (nsIContent* child = aContent->GetFirstChild();
1863 : child;
1864 0 : child = child->GetNextSibling()) {
1865 0 : if (child->IsHTML(nsGkAtoms::param)) {
1866 0 : if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
1867 0 : NS_LITERAL_STRING("pluginurl"), eIgnoreCase)) {
1868 0 : return GetPluginDisabledState(aContentType);
1869 : }
1870 0 : } else if (!hasAlternateContent) {
1871 : hasAlternateContent =
1872 0 : nsStyleUtil::IsSignificantChild(child, true, false);
1873 : }
1874 : }
1875 :
1876 : return hasAlternateContent ? ePluginOtherState :
1877 0 : GetPluginDisabledState(aContentType);
1878 : }
1879 :
1880 : PluginSupportState
1881 0 : nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
1882 : {
1883 0 : nsresult rv = IsPluginEnabledForType(aContentType);
1884 0 : if (rv == NS_ERROR_PLUGIN_DISABLED)
1885 0 : return ePluginDisabled;
1886 0 : if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY)
1887 0 : return ePluginClickToPlay;
1888 0 : if (rv == NS_ERROR_PLUGIN_BLOCKLISTED)
1889 0 : return ePluginBlocklisted;
1890 0 : return ePluginUnsupported;
1891 : }
1892 :
1893 : void
1894 0 : nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
1895 : {
1896 0 : nsImageLoadingContent::CreateStaticImageClone(aDest);
1897 :
1898 0 : aDest->mType = mType;
1899 0 : nsObjectLoadingContent* thisObj = const_cast<nsObjectLoadingContent*>(this);
1900 0 : if (thisObj->mPrintFrame.IsAlive()) {
1901 0 : aDest->mPrintFrame = thisObj->mPrintFrame;
1902 : } else {
1903 0 : aDest->mPrintFrame = const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame();
1904 : }
1905 :
1906 0 : if (mFrameLoader) {
1907 : nsCOMPtr<nsIContent> content =
1908 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
1909 0 : nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), false);
1910 0 : if (fl) {
1911 0 : aDest->mFrameLoader = fl;
1912 0 : mFrameLoader->CreateStaticClone(fl);
1913 : }
1914 : }
1915 0 : }
1916 :
1917 : NS_IMETHODIMP
1918 0 : nsObjectLoadingContent::GetPrintFrame(nsIFrame** aFrame)
1919 : {
1920 0 : *aFrame = mPrintFrame.GetFrame();
1921 0 : return NS_OK;
1922 : }
1923 :
1924 : NS_IMETHODIMP
1925 0 : nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
1926 : const nsAString& pluginDumpID,
1927 : const nsAString& browserDumpID,
1928 : bool submittedCrashReport)
1929 : {
1930 0 : AutoNotifier notifier(this, true);
1931 0 : UnloadContent();
1932 0 : mFallbackReason = ePluginCrashed;
1933 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1934 :
1935 : // Note that aPluginTag in invalidated after we're called, so copy
1936 : // out any data we need now.
1937 0 : nsCAutoString pluginName;
1938 0 : aPluginTag->GetName(pluginName);
1939 0 : nsCAutoString pluginFilename;
1940 0 : aPluginTag->GetFilename(pluginFilename);
1941 :
1942 : nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent,
1943 : pluginDumpID,
1944 : browserDumpID,
1945 0 : NS_ConvertUTF8toUTF16(pluginName),
1946 0 : NS_ConvertUTF8toUTF16(pluginFilename),
1947 0 : submittedCrashReport);
1948 0 : nsresult rv = NS_DispatchToCurrentThread(ev);
1949 0 : if (NS_FAILED(rv)) {
1950 0 : NS_WARNING("failed to dispatch nsPluginCrashedEvent");
1951 : }
1952 0 : return NS_OK;
1953 : }
1954 :
1955 : NS_IMETHODIMP
1956 0 : nsObjectLoadingContent::SyncStartPluginInstance()
1957 : {
1958 0 : NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
1959 : "Must be able to run script in order to instantiate a plugin instance!");
1960 :
1961 : // Don't even attempt to start an instance unless the content is in
1962 : // the document.
1963 : nsCOMPtr<nsIContent> thisContent =
1964 0 : do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1965 0 : if (!thisContent->IsInDoc()) {
1966 0 : return NS_ERROR_FAILURE;
1967 : }
1968 :
1969 0 : nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
1970 0 : nsCString contentType(mContentType);
1971 0 : return InstantiatePluginInstance(contentType.get(), mURI.get());
1972 : }
1973 :
1974 : NS_IMETHODIMP
1975 0 : nsObjectLoadingContent::AsyncStartPluginInstance()
1976 : {
1977 : // OK to have an instance already.
1978 0 : if (mInstanceOwner) {
1979 0 : return NS_OK;
1980 : }
1981 :
1982 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1983 0 : nsIDocument* doc = thisContent->OwnerDoc();
1984 0 : if (doc->IsStaticDocument() || doc->IsBeingUsedAsImage()) {
1985 0 : return NS_OK;
1986 : }
1987 :
1988 : // We always start plugins on a runnable.
1989 : // We don't want a script blocker on the stack during instantiation.
1990 0 : nsCOMPtr<nsIRunnable> event = new nsAsyncInstantiateEvent(this);
1991 0 : if (!event) {
1992 0 : return NS_ERROR_OUT_OF_MEMORY;
1993 : }
1994 0 : nsresult rv = NS_DispatchToCurrentThread(event);
1995 0 : if (NS_SUCCEEDED(rv)) {
1996 : // Remember this event. This is a weak reference that will be cleared
1997 : // when the event runs.
1998 0 : mPendingInstantiateEvent = event;
1999 : }
2000 :
2001 0 : return rv;
2002 : }
2003 :
2004 : NS_IMETHODIMP
2005 0 : nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
2006 : {
2007 0 : NS_IF_ADDREF(*aURI = mURI);
2008 0 : return NS_OK;
2009 : }
2010 :
2011 : static bool
2012 0 : DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner,
2013 : nsObjectLoadingContent* aContent,
2014 : bool aDelayedStop)
2015 : {
2016 : #if (MOZ_PLATFORM_MAEMO==5)
2017 : // Don't delay stop on Maemo/Hildon (bug 530739).
2018 : if (aDelayedStop && aInstanceOwner->MatchPluginName("Shockwave Flash"))
2019 : return false;
2020 : #endif
2021 :
2022 : // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524),
2023 : // XStandard (bug 430219), CMISS Zinc (bug 429604).
2024 0 : if (aDelayedStop
2025 : #if !(defined XP_WIN || defined MOZ_X11)
2026 : && !aInstanceOwner->MatchPluginName("QuickTime")
2027 : && !aInstanceOwner->MatchPluginName("Flip4Mac")
2028 : && !aInstanceOwner->MatchPluginName("XStandard plugin")
2029 : && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin")
2030 : #endif
2031 : ) {
2032 : nsCOMPtr<nsIRunnable> evt =
2033 0 : new nsStopPluginRunnable(aInstanceOwner, aContent);
2034 0 : NS_DispatchToCurrentThread(evt);
2035 0 : return true;
2036 : }
2037 0 : return false;
2038 : }
2039 :
2040 : void
2041 0 : nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
2042 : bool aDelayedStop,
2043 : bool aForcedReentry)
2044 : {
2045 : // DoStopPlugin can process events and there may be pending InDocCheckEvent
2046 : // events which can drop in underneath us and destroy the instance we are
2047 : // about to destroy unless we prevent that with the mIsStopping flag.
2048 : // (aForcedReentry is only true from the callback of an earlier delayed stop)
2049 0 : if (mIsStopping && !aForcedReentry) {
2050 0 : return;
2051 : }
2052 0 : mIsStopping = true;
2053 :
2054 0 : nsRefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
2055 0 : nsRefPtr<nsNPAPIPluginInstance> inst;
2056 0 : aInstanceOwner->GetInstance(getter_AddRefs(inst));
2057 0 : if (inst) {
2058 0 : if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) {
2059 : return;
2060 : }
2061 :
2062 : #if defined(XP_MACOSX)
2063 : aInstanceOwner->HidePluginWindow();
2064 : #endif
2065 :
2066 0 : nsCOMPtr<nsIPluginHost> pluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
2067 0 : NS_ASSERTION(pluginHost, "Without a pluginHost, how can we have an instance to destroy?");
2068 0 : static_cast<nsPluginHost*>(pluginHost.get())->StopPluginInstance(inst);
2069 : }
2070 :
2071 0 : aInstanceOwner->Destroy();
2072 0 : mIsStopping = false;
2073 : }
2074 :
2075 : NS_IMETHODIMP
2076 0 : nsObjectLoadingContent::StopPluginInstance()
2077 : {
2078 0 : if (!mInstanceOwner) {
2079 0 : return NS_OK;
2080 : }
2081 :
2082 0 : DisconnectFrame();
2083 :
2084 0 : bool delayedStop = false;
2085 : #ifdef XP_WIN
2086 : // Force delayed stop for Real plugin only; see bug 420886, 426852.
2087 : nsRefPtr<nsNPAPIPluginInstance> inst;
2088 : mInstanceOwner->GetInstance(getter_AddRefs(inst));
2089 : if (inst) {
2090 : const char* mime = nsnull;
2091 : if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
2092 : if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) {
2093 : delayedStop = true;
2094 : }
2095 : }
2096 : }
2097 : #endif
2098 :
2099 0 : DoStopPlugin(mInstanceOwner, delayedStop);
2100 :
2101 0 : mInstanceOwner = nsnull;
2102 :
2103 0 : return NS_OK;
2104 : }
2105 :
2106 : void
2107 0 : nsObjectLoadingContent::NotifyContentObjectWrapper()
2108 : {
2109 0 : nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
2110 :
2111 0 : nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
2112 0 : if (!doc)
2113 : return;
2114 :
2115 0 : nsIScriptGlobalObject *sgo = doc->GetScopeObject();
2116 0 : if (!sgo)
2117 : return;
2118 :
2119 0 : nsIScriptContext *scx = sgo->GetContext();
2120 0 : if (!scx)
2121 : return;
2122 :
2123 0 : JSContext *cx = scx->GetNativeContext();
2124 :
2125 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
2126 0 : nsContentUtils::XPConnect()->
2127 0 : GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), thisContent,
2128 : NS_GET_IID(nsISupports),
2129 0 : getter_AddRefs(wrapper));
2130 :
2131 0 : if (!wrapper) {
2132 : // Nothing to do here if there's no wrapper for mContent. The proto
2133 : // chain will be fixed appropriately when the wrapper is created.
2134 : return;
2135 : }
2136 :
2137 0 : JSObject *obj = nsnull;
2138 0 : nsresult rv = wrapper->GetJSObject(&obj);
2139 0 : if (NS_FAILED(rv))
2140 : return;
2141 :
2142 0 : nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj);
2143 : }
2144 :
2145 : NS_IMETHODIMP
2146 0 : nsObjectLoadingContent::PlayPlugin()
2147 : {
2148 0 : if (!nsContentUtils::IsCallerChrome())
2149 0 : return NS_OK;
2150 :
2151 0 : mShouldPlay = true;
2152 0 : return LoadObject(mURI, true, mContentType, true);
2153 4392 : }
|