content/base/src/nsObjectLoadingContent.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial