dom/plugins/base/nsPluginHost.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* nsPluginHost.cpp - top-level plugin management code */
michael@0 7
michael@0 8 #include "nscore.h"
michael@0 9 #include "nsPluginHost.h"
michael@0 10
michael@0 11 #include <cstdlib>
michael@0 12 #include <stdio.h>
michael@0 13 #include "prio.h"
michael@0 14 #include "prmem.h"
michael@0 15 #include "nsNPAPIPlugin.h"
michael@0 16 #include "nsNPAPIPluginStreamListener.h"
michael@0 17 #include "nsNPAPIPluginInstance.h"
michael@0 18 #include "nsPluginInstanceOwner.h"
michael@0 19 #include "nsObjectLoadingContent.h"
michael@0 20 #include "nsIHTTPHeaderListener.h"
michael@0 21 #include "nsIHttpHeaderVisitor.h"
michael@0 22 #include "nsIObserverService.h"
michael@0 23 #include "nsIHttpProtocolHandler.h"
michael@0 24 #include "nsIHttpChannel.h"
michael@0 25 #include "nsIUploadChannel.h"
michael@0 26 #include "nsIByteRangeRequest.h"
michael@0 27 #include "nsIStreamListener.h"
michael@0 28 #include "nsIInputStream.h"
michael@0 29 #include "nsIOutputStream.h"
michael@0 30 #include "nsIURL.h"
michael@0 31 #include "nsTArray.h"
michael@0 32 #include "nsReadableUtils.h"
michael@0 33 #include "nsProtocolProxyService.h"
michael@0 34 #include "nsIStreamConverterService.h"
michael@0 35 #include "nsIFile.h"
michael@0 36 #if defined(XP_MACOSX)
michael@0 37 #include "nsILocalFileMac.h"
michael@0 38 #endif
michael@0 39 #include "nsISeekableStream.h"
michael@0 40 #include "nsNetUtil.h"
michael@0 41 #include "nsIProgressEventSink.h"
michael@0 42 #include "nsIDocument.h"
michael@0 43 #include "nsPluginLogging.h"
michael@0 44 #include "nsIScriptChannel.h"
michael@0 45 #include "nsIBlocklistService.h"
michael@0 46 #include "nsVersionComparator.h"
michael@0 47 #include "nsIObjectLoadingContent.h"
michael@0 48 #include "nsIWritablePropertyBag2.h"
michael@0 49 #include "nsICategoryManager.h"
michael@0 50 #include "nsPluginStreamListenerPeer.h"
michael@0 51 #include "mozilla/Preferences.h"
michael@0 52
michael@0 53 #include "nsEnumeratorUtils.h"
michael@0 54 #include "nsXPCOM.h"
michael@0 55 #include "nsXPCOMCID.h"
michael@0 56 #include "nsISupportsPrimitives.h"
michael@0 57
michael@0 58 #include "nsXULAppAPI.h"
michael@0 59 #include "nsIXULRuntime.h"
michael@0 60
michael@0 61 // for the dialog
michael@0 62 #include "nsIWindowWatcher.h"
michael@0 63 #include "nsIDOMElement.h"
michael@0 64 #include "nsIDOMWindow.h"
michael@0 65
michael@0 66 #include "nsNetCID.h"
michael@0 67 #include "prprf.h"
michael@0 68 #include "nsThreadUtils.h"
michael@0 69 #include "nsIInputStreamTee.h"
michael@0 70
michael@0 71 #include "nsDirectoryServiceDefs.h"
michael@0 72 #include "nsAppDirectoryServiceDefs.h"
michael@0 73 #include "nsPluginDirServiceProvider.h"
michael@0 74
michael@0 75 #include "nsUnicharUtils.h"
michael@0 76 #include "nsPluginManifestLineReader.h"
michael@0 77
michael@0 78 #include "nsIWeakReferenceUtils.h"
michael@0 79 #include "nsIPresShell.h"
michael@0 80 #include "nsPluginNativeWindow.h"
michael@0 81 #include "nsIScriptSecurityManager.h"
michael@0 82 #include "nsIContentPolicy.h"
michael@0 83 #include "nsContentPolicyUtils.h"
michael@0 84 #include "mozilla/TimeStamp.h"
michael@0 85 #include "mozilla/Telemetry.h"
michael@0 86 #include "nsIImageLoadingContent.h"
michael@0 87 #include "mozilla/Preferences.h"
michael@0 88 #include "nsVersionComparator.h"
michael@0 89
michael@0 90 #if defined(XP_WIN)
michael@0 91 #include "nsIWindowMediator.h"
michael@0 92 #include "nsIBaseWindow.h"
michael@0 93 #include "windows.h"
michael@0 94 #include "winbase.h"
michael@0 95 #endif
michael@0 96
michael@0 97 #ifdef ANDROID
michael@0 98 #include <android/log.h>
michael@0 99 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
michael@0 100 #endif
michael@0 101
michael@0 102 #if MOZ_CRASHREPORTER
michael@0 103 #include "nsExceptionHandler.h"
michael@0 104 #endif
michael@0 105
michael@0 106 using namespace mozilla;
michael@0 107 using mozilla::TimeStamp;
michael@0 108
michael@0 109 // Null out a strong ref to a linked list iteratively to avoid
michael@0 110 // exhausting the stack (bug 486349).
michael@0 111 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
michael@0 112 { \
michael@0 113 while (list_) { \
michael@0 114 type_ temp = list_->mNext_; \
michael@0 115 list_->mNext_ = nullptr; \
michael@0 116 list_ = temp; \
michael@0 117 } \
michael@0 118 }
michael@0 119
michael@0 120 // this is the name of the directory which will be created
michael@0 121 // to cache temporary files.
michael@0 122 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
michael@0 123
michael@0 124 static const char *kPrefWhitelist = "plugin.allowed_types";
michael@0 125 static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
michael@0 126 static const char *kPrefJavaMIME = "plugin.java.mime";
michael@0 127
michael@0 128 // Version of cached plugin info
michael@0 129 // 0.01 first implementation
michael@0 130 // 0.02 added caching of CanUnload to fix bug 105935
michael@0 131 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
michael@0 132 // 0.04 added new mime entry point on Mac, bug 113464
michael@0 133 // 0.05 added new entry point check for the default plugin, bug 132430
michael@0 134 // 0.06 strip off suffixes in mime description strings, bug 53895
michael@0 135 // 0.07 changed nsIRegistry to flat file support for caching plugins info
michael@0 136 // 0.08 mime entry point on MachO, bug 137535
michael@0 137 // 0.09 the file encoding is changed to UTF-8, bug 420285
michael@0 138 // 0.10 added plugin versions on appropriate platforms, bug 427743
michael@0 139 // 0.11 file name and full path fields now store expected values on all platforms, bug 488181
michael@0 140 // 0.12 force refresh due to quicktime pdf claim fix, bug 611197
michael@0 141 // 0.13 add architecture and list of invalid plugins, bug 616271
michael@0 142 // 0.14 force refresh due to locale comparison fix, bug 611296
michael@0 143 // 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
michael@0 144 // 0.16 version bump to avoid importing the plugin flags in newer versions
michael@0 145 // 0.17 added flag on whether plugin is loaded from an XPI
michael@0 146 // The current plugin registry version (and the maximum version we know how to read)
michael@0 147 static const char *kPluginRegistryVersion = "0.17";
michael@0 148 // The minimum registry version we know how to read
michael@0 149 static const char *kMinimumRegistryVersion = "0.9";
michael@0 150
michael@0 151 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
michael@0 152
michael@0 153 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
michael@0 154
michael@0 155 #ifdef PLUGIN_LOGGING
michael@0 156 PRLogModuleInfo* nsPluginLogging::gNPNLog = nullptr;
michael@0 157 PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr;
michael@0 158 PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr;
michael@0 159 #endif
michael@0 160
michael@0 161 // #defines for plugin cache and prefs
michael@0 162 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
michael@0 163 // Raise this from '10' to '50' to work around a bug in Apple's current Java
michael@0 164 // plugins on OS X Lion and SnowLeopard. See bug 705931.
michael@0 165 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
michael@0 166
michael@0 167 nsIFile *nsPluginHost::sPluginTempDir;
michael@0 168 nsPluginHost *nsPluginHost::sInst;
michael@0 169
michael@0 170 NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
michael@0 171
michael@0 172 nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
michael@0 173 : mFullPath(aFullPath),
michael@0 174 mLastModifiedTime(aLastModifiedTime),
michael@0 175 mSeen(false)
michael@0 176 {}
michael@0 177
michael@0 178 nsInvalidPluginTag::~nsInvalidPluginTag()
michael@0 179 {}
michael@0 180
michael@0 181 // Helper to check for a MIME in a comma-delimited preference
michael@0 182 static bool
michael@0 183 IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
michael@0 184 {
michael@0 185 nsAutoCString searchStr;
michael@0 186 searchStr.Assign(',');
michael@0 187 searchStr.Append(aTypeList);
michael@0 188 searchStr.Append(',');
michael@0 189
michael@0 190 nsACString::const_iterator start, end;
michael@0 191
michael@0 192 searchStr.BeginReading(start);
michael@0 193 searchStr.EndReading(end);
michael@0 194
michael@0 195 nsAutoCString commaSeparated;
michael@0 196 commaSeparated.Assign(',');
michael@0 197 commaSeparated += aMimeType;
michael@0 198 commaSeparated.Append(',');
michael@0 199
michael@0 200 return FindInReadable(commaSeparated, start, end);
michael@0 201 }
michael@0 202
michael@0 203 // flat file reg funcs
michael@0 204 static
michael@0 205 bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
michael@0 206 {
michael@0 207 do {
michael@0 208 if (*reader.LinePtr() == '[') {
michael@0 209 char* p = reader.LinePtr() + (reader.LineLength() - 1);
michael@0 210 if (*p != ']')
michael@0 211 break;
michael@0 212 *p = 0;
michael@0 213
michael@0 214 char* values[1];
michael@0 215 if (1 != reader.ParseLine(values, 1))
michael@0 216 break;
michael@0 217 // ignore the leading '['
michael@0 218 if (PL_strcmp(values[0]+1, token)) {
michael@0 219 break; // it's wrong token
michael@0 220 }
michael@0 221 return true;
michael@0 222 }
michael@0 223 } while (reader.NextLine());
michael@0 224 return false;
michael@0 225 }
michael@0 226
michael@0 227 static bool UnloadPluginsASAP()
michael@0 228 {
michael@0 229 return Preferences::GetBool("dom.ipc.plugins.unloadASAP", false);
michael@0 230 }
michael@0 231
michael@0 232 nsPluginHost::nsPluginHost()
michael@0 233 // No need to initialize members to nullptr, false etc because this class
michael@0 234 // has a zeroing operator new.
michael@0 235 {
michael@0 236 // check to see if pref is set at startup to let plugins take over in
michael@0 237 // full page mode for certain image mime types that we handle internally
michael@0 238 mOverrideInternalTypes =
michael@0 239 Preferences::GetBool("plugin.override_internal_types", false);
michael@0 240
michael@0 241 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
michael@0 242 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
michael@0 243
michael@0 244 Preferences::AddStrongObserver(this, "plugin.disable");
michael@0 245 Preferences::AddStrongObserver(this, "plugins.click_to_play");
michael@0 246
michael@0 247 nsCOMPtr<nsIObserverService> obsService =
michael@0 248 mozilla::services::GetObserverService();
michael@0 249 if (obsService) {
michael@0 250 obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
michael@0 251 obsService->AddObserver(this, "blocklist-updated", false);
michael@0 252 #ifdef MOZ_WIDGET_ANDROID
michael@0 253 obsService->AddObserver(this, "application-foreground", false);
michael@0 254 obsService->AddObserver(this, "application-background", false);
michael@0 255 #endif
michael@0 256 }
michael@0 257
michael@0 258 #ifdef PLUGIN_LOGGING
michael@0 259 nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
michael@0 260 nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
michael@0 261 nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
michael@0 262
michael@0 263 PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
michael@0 264 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
michael@0 265 PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
michael@0 266
michael@0 267 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
michael@0 268 PR_LogFlush();
michael@0 269 #endif
michael@0 270 }
michael@0 271
michael@0 272 nsPluginHost::~nsPluginHost()
michael@0 273 {
michael@0 274 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
michael@0 275
michael@0 276 UnloadPlugins();
michael@0 277 sInst = nullptr;
michael@0 278 }
michael@0 279
michael@0 280 NS_IMPL_ISUPPORTS(nsPluginHost,
michael@0 281 nsIPluginHost,
michael@0 282 nsIObserver,
michael@0 283 nsITimerCallback,
michael@0 284 nsISupportsWeakReference)
michael@0 285
michael@0 286 already_AddRefed<nsPluginHost>
michael@0 287 nsPluginHost::GetInst()
michael@0 288 {
michael@0 289 if (!sInst) {
michael@0 290 sInst = new nsPluginHost();
michael@0 291 if (!sInst)
michael@0 292 return nullptr;
michael@0 293 NS_ADDREF(sInst);
michael@0 294 }
michael@0 295
michael@0 296 nsRefPtr<nsPluginHost> inst = sInst;
michael@0 297 return inst.forget();
michael@0 298 }
michael@0 299
michael@0 300 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
michael@0 301 {
michael@0 302 if (!aPluginTag || !aPluginTag->mPlugin) {
michael@0 303 return false;
michael@0 304 }
michael@0 305
michael@0 306 for (uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 307 nsNPAPIPluginInstance *instance = mInstances[i].get();
michael@0 308 if (instance &&
michael@0 309 instance->GetPlugin() == aPluginTag->mPlugin &&
michael@0 310 instance->IsRunning()) {
michael@0 311 return true;
michael@0 312 }
michael@0 313 }
michael@0 314
michael@0 315 return false;
michael@0 316 }
michael@0 317
michael@0 318 nsresult nsPluginHost::ReloadPlugins()
michael@0 319 {
michael@0 320 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 321 ("nsPluginHost::ReloadPlugins Begin\n"));
michael@0 322
michael@0 323 nsresult rv = NS_OK;
michael@0 324
michael@0 325 // this will create the initial plugin list out of cache
michael@0 326 // if it was not created yet
michael@0 327 if (!mPluginsLoaded)
michael@0 328 return LoadPlugins();
michael@0 329
michael@0 330 // we are re-scanning plugins. New plugins may have been added, also some
michael@0 331 // plugins may have been removed, so we should probably shut everything down
michael@0 332 // but don't touch running (active and not stopped) plugins
michael@0 333
michael@0 334 // check if plugins changed, no need to do anything else
michael@0 335 // if no changes to plugins have been made
michael@0 336 // false instructs not to touch the plugin list, just to
michael@0 337 // look for possible changes
michael@0 338 bool pluginschanged = true;
michael@0 339 FindPlugins(false, &pluginschanged);
michael@0 340
michael@0 341 // if no changed detected, return an appropriate error code
michael@0 342 if (!pluginschanged)
michael@0 343 return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
michael@0 344
michael@0 345 // shutdown plugins and kill the list if there are no running plugins
michael@0 346 nsRefPtr<nsPluginTag> prev;
michael@0 347 nsRefPtr<nsPluginTag> next;
michael@0 348
michael@0 349 for (nsRefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
michael@0 350 next = p->mNext;
michael@0 351
michael@0 352 // only remove our plugin from the list if it's not running.
michael@0 353 if (!IsRunningPlugin(p)) {
michael@0 354 if (p == mPlugins)
michael@0 355 mPlugins = next;
michael@0 356 else
michael@0 357 prev->mNext = next;
michael@0 358
michael@0 359 p->mNext = nullptr;
michael@0 360
michael@0 361 // attempt to unload plugins whenever they are removed from the list
michael@0 362 p->TryUnloadPlugin(false);
michael@0 363
michael@0 364 p = next;
michael@0 365 continue;
michael@0 366 }
michael@0 367
michael@0 368 prev = p;
michael@0 369 p = next;
michael@0 370 }
michael@0 371
michael@0 372 // set flags
michael@0 373 mPluginsLoaded = false;
michael@0 374
michael@0 375 // load them again
michael@0 376 rv = LoadPlugins();
michael@0 377
michael@0 378 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 379 ("nsPluginHost::ReloadPlugins End\n"));
michael@0 380
michael@0 381 return rv;
michael@0 382 }
michael@0 383
michael@0 384 #define NS_RETURN_UASTRING_SIZE 128
michael@0 385
michael@0 386 nsresult nsPluginHost::UserAgent(const char **retstring)
michael@0 387 {
michael@0 388 static char resultString[NS_RETURN_UASTRING_SIZE];
michael@0 389 nsresult res;
michael@0 390
michael@0 391 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
michael@0 392 if (NS_FAILED(res))
michael@0 393 return res;
michael@0 394
michael@0 395 nsAutoCString uaString;
michael@0 396 res = http->GetUserAgent(uaString);
michael@0 397
michael@0 398 if (NS_SUCCEEDED(res)) {
michael@0 399 if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
michael@0 400 PL_strcpy(resultString, uaString.get());
michael@0 401 } else {
michael@0 402 // Copy as much of UA string as we can (terminate at right-most space).
michael@0 403 PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
michael@0 404 for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
michael@0 405 if (i == 0) {
michael@0 406 resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
michael@0 407 }
michael@0 408 else if (resultString[i] == ' ') {
michael@0 409 resultString[i] = '\0';
michael@0 410 break;
michael@0 411 }
michael@0 412 }
michael@0 413 }
michael@0 414 *retstring = resultString;
michael@0 415 }
michael@0 416 else {
michael@0 417 *retstring = nullptr;
michael@0 418 }
michael@0 419
michael@0 420 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
michael@0 421
michael@0 422 return res;
michael@0 423 }
michael@0 424
michael@0 425 nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
michael@0 426 const char* url,
michael@0 427 const char* target,
michael@0 428 nsNPAPIPluginStreamListener* streamListener,
michael@0 429 const char* altHost,
michael@0 430 const char* referrer,
michael@0 431 bool forceJSEnabled)
michael@0 432 {
michael@0 433 return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
michael@0 434 url, target, streamListener, altHost, referrer,
michael@0 435 forceJSEnabled, 0, nullptr);
michael@0 436 }
michael@0 437
michael@0 438 nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
michael@0 439 const char* url,
michael@0 440 const char* target,
michael@0 441 nsNPAPIPluginStreamListener* streamListener,
michael@0 442 const char* altHost,
michael@0 443 const char* referrer,
michael@0 444 bool forceJSEnabled,
michael@0 445 uint32_t getHeadersLength,
michael@0 446 const char* getHeaders)
michael@0 447 {
michael@0 448 // we can only send a stream back to the plugin (as specified by a
michael@0 449 // null target) if we also have a nsNPAPIPluginStreamListener to talk to
michael@0 450 if (!target && !streamListener)
michael@0 451 return NS_ERROR_ILLEGAL_VALUE;
michael@0 452
michael@0 453 nsresult rv = DoURLLoadSecurityCheck(pluginInst, url);
michael@0 454 if (NS_FAILED(rv))
michael@0 455 return rv;
michael@0 456
michael@0 457 if (target) {
michael@0 458 nsRefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
michael@0 459 if (owner) {
michael@0 460 if ((0 == PL_strcmp(target, "newwindow")) ||
michael@0 461 (0 == PL_strcmp(target, "_new")))
michael@0 462 target = "_blank";
michael@0 463 else if (0 == PL_strcmp(target, "_current"))
michael@0 464 target = "_self";
michael@0 465
michael@0 466 rv = owner->GetURL(url, target, nullptr, nullptr, 0);
michael@0 467 }
michael@0 468 }
michael@0 469
michael@0 470 if (streamListener)
michael@0 471 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
michael@0 472 streamListener, nullptr,
michael@0 473 getHeaders, getHeadersLength);
michael@0 474
michael@0 475 return rv;
michael@0 476 }
michael@0 477
michael@0 478 nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
michael@0 479 const char* url,
michael@0 480 uint32_t postDataLen,
michael@0 481 const char* postData,
michael@0 482 bool isFile,
michael@0 483 const char* target,
michael@0 484 nsNPAPIPluginStreamListener* streamListener,
michael@0 485 const char* altHost,
michael@0 486 const char* referrer,
michael@0 487 bool forceJSEnabled,
michael@0 488 uint32_t postHeadersLength,
michael@0 489 const char* postHeaders)
michael@0 490 {
michael@0 491 nsresult rv;
michael@0 492
michael@0 493 // we can only send a stream back to the plugin (as specified
michael@0 494 // by a null target) if we also have a nsNPAPIPluginStreamListener
michael@0 495 // to talk to also
michael@0 496 if (!target && !streamListener)
michael@0 497 return NS_ERROR_ILLEGAL_VALUE;
michael@0 498
michael@0 499 nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
michael@0 500
michael@0 501 rv = DoURLLoadSecurityCheck(instance, url);
michael@0 502 if (NS_FAILED(rv))
michael@0 503 return rv;
michael@0 504
michael@0 505 nsCOMPtr<nsIInputStream> postStream;
michael@0 506 if (isFile) {
michael@0 507 nsCOMPtr<nsIFile> file;
michael@0 508 rv = CreateTempFileToPost(postData, getter_AddRefs(file));
michael@0 509 if (NS_FAILED(rv))
michael@0 510 return rv;
michael@0 511
michael@0 512 nsCOMPtr<nsIInputStream> fileStream;
michael@0 513 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream),
michael@0 514 file,
michael@0 515 PR_RDONLY,
michael@0 516 0600,
michael@0 517 nsIFileInputStream::DELETE_ON_CLOSE |
michael@0 518 nsIFileInputStream::CLOSE_ON_EOF);
michael@0 519 if (NS_FAILED(rv))
michael@0 520 return rv;
michael@0 521
michael@0 522 rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192);
michael@0 523 if (NS_FAILED(rv))
michael@0 524 return rv;
michael@0 525 } else {
michael@0 526 char *dataToPost;
michael@0 527 uint32_t newDataToPostLen;
michael@0 528 ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
michael@0 529 if (!dataToPost)
michael@0 530 return NS_ERROR_UNEXPECTED;
michael@0 531
michael@0 532 nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
michael@0 533 if (!sis) {
michael@0 534 NS_Free(dataToPost);
michael@0 535 return rv;
michael@0 536 }
michael@0 537
michael@0 538 // data allocated by ParsePostBufferToFixHeaders() is managed and
michael@0 539 // freed by the string stream.
michael@0 540 postDataLen = newDataToPostLen;
michael@0 541 sis->AdoptData(dataToPost, postDataLen);
michael@0 542 postStream = sis;
michael@0 543 }
michael@0 544
michael@0 545 if (target) {
michael@0 546 nsRefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
michael@0 547 if (owner) {
michael@0 548 if ((0 == PL_strcmp(target, "newwindow")) ||
michael@0 549 (0 == PL_strcmp(target, "_new"))) {
michael@0 550 target = "_blank";
michael@0 551 } else if (0 == PL_strcmp(target, "_current")) {
michael@0 552 target = "_self";
michael@0 553 }
michael@0 554 rv = owner->GetURL(url, target, postStream,
michael@0 555 (void*)postHeaders, postHeadersLength);
michael@0 556 }
michael@0 557 }
michael@0 558
michael@0 559 // if we don't have a target, just create a stream. This does
michael@0 560 // NS_OpenURI()!
michael@0 561 if (streamListener)
michael@0 562 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
michael@0 563 streamListener,
michael@0 564 postStream, postHeaders, postHeadersLength);
michael@0 565
michael@0 566 return rv;
michael@0 567 }
michael@0 568
michael@0 569 /* This method queries the prefs for proxy information.
michael@0 570 * It has been tested and is known to work in the following three cases
michael@0 571 * when no proxy host or port is specified
michael@0 572 * when only the proxy host is specified
michael@0 573 * when only the proxy port is specified
michael@0 574 * This method conforms to the return code specified in
michael@0 575 * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
michael@0 576 * with the exception that multiple values are not implemented.
michael@0 577 */
michael@0 578
michael@0 579 nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
michael@0 580 {
michael@0 581 if (!url || !result) {
michael@0 582 return NS_ERROR_INVALID_ARG;
michael@0 583 }
michael@0 584 nsresult res;
michael@0 585
michael@0 586 nsCOMPtr<nsIProtocolProxyService> proxyService =
michael@0 587 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
michael@0 588 if (NS_FAILED(res) || !proxyService)
michael@0 589 return res;
michael@0 590
michael@0 591 nsRefPtr<nsProtocolProxyService> rawProxyService = do_QueryObject(proxyService);
michael@0 592 if (!rawProxyService)
michael@0 593 return NS_ERROR_FAILURE;
michael@0 594
michael@0 595 nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
michael@0 596 if (NS_FAILED(res) || !ioService)
michael@0 597 return res;
michael@0 598
michael@0 599 // make a temporary channel from the argument url
michael@0 600 nsCOMPtr<nsIChannel> tempChannel;
michael@0 601 res = ioService->NewChannel(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(tempChannel));
michael@0 602 if (NS_FAILED(res))
michael@0 603 return res;
michael@0 604
michael@0 605 nsCOMPtr<nsIProxyInfo> pi;
michael@0 606
michael@0 607 // Remove this deprecated call in the future (see Bug 778201):
michael@0 608 res = rawProxyService->DeprecatedBlockingResolve(tempChannel, 0, getter_AddRefs(pi));
michael@0 609 if (NS_FAILED(res))
michael@0 610 return res;
michael@0 611
michael@0 612 nsAutoCString host, type;
michael@0 613 int32_t port = -1;
michael@0 614
michael@0 615 // These won't fail, and even if they do... we'll be ok.
michael@0 616 if (pi) {
michael@0 617 pi->GetType(type);
michael@0 618 pi->GetHost(host);
michael@0 619 pi->GetPort(&port);
michael@0 620 }
michael@0 621
michael@0 622 if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
michael@0 623 *result = PL_strdup("DIRECT");
michael@0 624 } else if (type.EqualsLiteral("http")) {
michael@0 625 *result = PR_smprintf("PROXY %s:%d", host.get(), port);
michael@0 626 } else if (type.EqualsLiteral("socks4")) {
michael@0 627 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
michael@0 628 } else if (type.EqualsLiteral("socks")) {
michael@0 629 // XXX - this is socks5, but there is no API for us to tell the
michael@0 630 // plugin that fact. SOCKS for now, in case the proxy server
michael@0 631 // speaks SOCKS4 as well. See bug 78176
michael@0 632 // For a long time this was returning an http proxy type, so
michael@0 633 // very little is probably broken by this
michael@0 634 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
michael@0 635 } else {
michael@0 636 NS_ASSERTION(false, "Unknown proxy type!");
michael@0 637 *result = PL_strdup("DIRECT");
michael@0 638 }
michael@0 639
michael@0 640 if (nullptr == *result)
michael@0 641 res = NS_ERROR_OUT_OF_MEMORY;
michael@0 642
michael@0 643 return res;
michael@0 644 }
michael@0 645
michael@0 646 nsresult nsPluginHost::Init()
michael@0 647 {
michael@0 648 return NS_OK;
michael@0 649 }
michael@0 650
michael@0 651 nsresult nsPluginHost::UnloadPlugins()
michael@0 652 {
michael@0 653 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
michael@0 654
michael@0 655 if (!mPluginsLoaded)
michael@0 656 return NS_OK;
michael@0 657
michael@0 658 // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
michael@0 659 // for those plugins who want it
michael@0 660 DestroyRunningInstances(nullptr);
michael@0 661
michael@0 662 nsPluginTag *pluginTag;
michael@0 663 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
michael@0 664 pluginTag->TryUnloadPlugin(true);
michael@0 665 }
michael@0 666
michael@0 667 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
michael@0 668 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 669 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 670
michael@0 671 // Lets remove any of the temporary files that we created.
michael@0 672 if (sPluginTempDir) {
michael@0 673 sPluginTempDir->Remove(true);
michael@0 674 NS_RELEASE(sPluginTempDir);
michael@0 675 }
michael@0 676
michael@0 677 #ifdef XP_WIN
michael@0 678 if (mPrivateDirServiceProvider) {
michael@0 679 nsCOMPtr<nsIDirectoryService> dirService =
michael@0 680 do_GetService(kDirectoryServiceContractID);
michael@0 681 if (dirService)
michael@0 682 dirService->UnregisterProvider(mPrivateDirServiceProvider);
michael@0 683 mPrivateDirServiceProvider = nullptr;
michael@0 684 }
michael@0 685 #endif /* XP_WIN */
michael@0 686
michael@0 687 mPluginsLoaded = false;
michael@0 688
michael@0 689 return NS_OK;
michael@0 690 }
michael@0 691
michael@0 692 void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
michael@0 693 {
michael@0 694 bool hasInstance = false;
michael@0 695 for (uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 696 if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
michael@0 697 hasInstance = true;
michael@0 698 break;
michael@0 699 }
michael@0 700 }
michael@0 701
michael@0 702 // We have some options for unloading plugins if they have no instances.
michael@0 703 //
michael@0 704 // Unloading plugins immediately can be bad - some plugins retain state
michael@0 705 // between instances even when there are none. This is largely limited to
michael@0 706 // going from one page to another, so state is retained without an instance
michael@0 707 // for only a very short period of time. In order to allow this to work
michael@0 708 // we don't unload plugins immediately by default. This is supported
michael@0 709 // via a hidden user pref though.
michael@0 710 //
michael@0 711 // Another reason not to unload immediately is that loading is expensive,
michael@0 712 // and it is better to leave popular plugins loaded.
michael@0 713 //
michael@0 714 // Our default behavior is to try to unload a plugin three minutes after
michael@0 715 // its last instance is destroyed. This seems like a reasonable compromise
michael@0 716 // that allows us to reclaim memory while allowing short state retention
michael@0 717 // and avoid perf hits for loading popular plugins.
michael@0 718 if (!hasInstance) {
michael@0 719 if (UnloadPluginsASAP()) {
michael@0 720 aPluginTag->TryUnloadPlugin(false);
michael@0 721 } else {
michael@0 722 if (aPluginTag->mUnloadTimer) {
michael@0 723 aPluginTag->mUnloadTimer->Cancel();
michael@0 724 } else {
michael@0 725 aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 726 }
michael@0 727 aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT);
michael@0 728 }
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 nsresult
michael@0 733 nsPluginHost::GetPluginTempDir(nsIFile **aDir)
michael@0 734 {
michael@0 735 if (!sPluginTempDir) {
michael@0 736 nsCOMPtr<nsIFile> tmpDir;
michael@0 737 nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
michael@0 738 getter_AddRefs(tmpDir));
michael@0 739 NS_ENSURE_SUCCESS(rv, rv);
michael@0 740
michael@0 741 rv = tmpDir->AppendNative(kPluginTmpDirName);
michael@0 742
michael@0 743 // make it unique, and mode == 0700, not world-readable
michael@0 744 rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
michael@0 745 NS_ENSURE_SUCCESS(rv, rv);
michael@0 746
michael@0 747 tmpDir.swap(sPluginTempDir);
michael@0 748 }
michael@0 749
michael@0 750 return sPluginTempDir->Clone(aDir);
michael@0 751 }
michael@0 752
michael@0 753 nsresult
michael@0 754 nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
michael@0 755 nsObjectLoadingContent *aContent,
michael@0 756 nsPluginInstanceOwner** aOwner)
michael@0 757 {
michael@0 758 NS_ENSURE_ARG_POINTER(aOwner);
michael@0 759
michael@0 760 #ifdef PLUGIN_LOGGING
michael@0 761 nsAutoCString urlSpec;
michael@0 762 if (aURL)
michael@0 763 aURL->GetAsciiSpec(urlSpec);
michael@0 764
michael@0 765 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
michael@0 766 ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
michael@0 767 aMimeType, urlSpec.get()));
michael@0 768
michael@0 769 PR_LogFlush();
michael@0 770 #endif
michael@0 771
michael@0 772 if (!aMimeType) {
michael@0 773 NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
michael@0 774 return NS_ERROR_FAILURE;
michael@0 775 }
michael@0 776
michael@0 777 nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
michael@0 778 if (!instanceOwner) {
michael@0 779 return NS_ERROR_OUT_OF_MEMORY;
michael@0 780 }
michael@0 781
michael@0 782 nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
michael@0 783 nsresult rv = instanceOwner->Init(ourContent);
michael@0 784 if (NS_FAILED(rv)) {
michael@0 785 return rv;
michael@0 786 }
michael@0 787
michael@0 788 nsPluginTagType tagType;
michael@0 789 rv = instanceOwner->GetTagType(&tagType);
michael@0 790 if (NS_FAILED(rv)) {
michael@0 791 return rv;
michael@0 792 }
michael@0 793
michael@0 794 if (tagType != nsPluginTagType_Embed &&
michael@0 795 tagType != nsPluginTagType_Applet &&
michael@0 796 tagType != nsPluginTagType_Object) {
michael@0 797 return NS_ERROR_FAILURE;
michael@0 798 }
michael@0 799
michael@0 800 rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
michael@0 801 if (NS_FAILED(rv)) {
michael@0 802 return NS_ERROR_FAILURE;
michael@0 803 }
michael@0 804
michael@0 805 nsRefPtr<nsNPAPIPluginInstance> instance;
michael@0 806 rv = instanceOwner->GetInstance(getter_AddRefs(instance));
michael@0 807 if (NS_FAILED(rv)) {
michael@0 808 return rv;
michael@0 809 }
michael@0 810
michael@0 811 if (instance) {
michael@0 812 instanceOwner->CreateWidget();
michael@0 813
michael@0 814 // If we've got a native window, the let the plugin know about it.
michael@0 815 instanceOwner->CallSetWindow();
michael@0 816 }
michael@0 817
michael@0 818 // At this point we consider instantiation to be successful. Do not return an error.
michael@0 819 instanceOwner.forget(aOwner);
michael@0 820
michael@0 821 #ifdef PLUGIN_LOGGING
michael@0 822 nsAutoCString urlSpec2;
michael@0 823 if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
michael@0 824
michael@0 825 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
michael@0 826 ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
michael@0 827 aMimeType, rv, urlSpec2.get()));
michael@0 828
michael@0 829 PR_LogFlush();
michael@0 830 #endif
michael@0 831
michael@0 832 return NS_OK;
michael@0 833 }
michael@0 834
michael@0 835 nsPluginTag*
michael@0 836 nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
michael@0 837 {
michael@0 838 nsPluginTag* pluginTag;
michael@0 839 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
michael@0 840 if (pluginTag->mLibrary == aLibrary) {
michael@0 841 return pluginTag;
michael@0 842 }
michael@0 843 }
michael@0 844 return nullptr;
michael@0 845 }
michael@0 846
michael@0 847 nsPluginTag*
michael@0 848 nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
michael@0 849 {
michael@0 850 nsPluginTag* pluginTag;
michael@0 851 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
michael@0 852 if (pluginTag->mPlugin == aPlugin) {
michael@0 853 return pluginTag;
michael@0 854 }
michael@0 855 }
michael@0 856 // a plugin should never exist without a corresponding tag
michael@0 857 NS_ERROR("TagForPlugin has failed");
michael@0 858 return nullptr;
michael@0 859 }
michael@0 860
michael@0 861 nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
michael@0 862 nsIURI *aURL,
michael@0 863 nsPluginInstanceOwner *aOwner)
michael@0 864 {
michael@0 865 NS_ENSURE_ARG_POINTER(aOwner);
michael@0 866
michael@0 867 nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
michael@0 868 if (NS_SUCCEEDED(rv)) {
michael@0 869 return rv;
michael@0 870 }
michael@0 871
michael@0 872 // If we failed to load a plugin instance we'll try again after
michael@0 873 // reloading our plugin list. Only do that once per document to
michael@0 874 // avoid redundant high resource usage on pages with multiple
michael@0 875 // unkown instance types. We'll do that by caching the document.
michael@0 876 nsCOMPtr<nsIDocument> document;
michael@0 877 aOwner->GetDocument(getter_AddRefs(document));
michael@0 878
michael@0 879 nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
michael@0 880 if (document == currentdocument) {
michael@0 881 return rv;
michael@0 882 }
michael@0 883
michael@0 884 mCurrentDocument = do_GetWeakReference(document);
michael@0 885
michael@0 886 // Don't try to set up an instance again if nothing changed.
michael@0 887 if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
michael@0 888 return rv;
michael@0 889 }
michael@0 890
michael@0 891 return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
michael@0 892 }
michael@0 893
michael@0 894 nsresult
michael@0 895 nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
michael@0 896 nsIURI *aURL,
michael@0 897 nsPluginInstanceOwner *aOwner)
michael@0 898 {
michael@0 899 #ifdef PLUGIN_LOGGING
michael@0 900 nsAutoCString urlSpec;
michael@0 901 if (aURL != nullptr) aURL->GetSpec(urlSpec);
michael@0 902
michael@0 903 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
michael@0 904 ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
michael@0 905 aMimeType, aOwner, urlSpec.get()));
michael@0 906
michael@0 907 PR_LogFlush();
michael@0 908 #endif
michael@0 909
michael@0 910 nsRefPtr<nsNPAPIPlugin> plugin;
michael@0 911 GetPlugin(aMimeType, getter_AddRefs(plugin));
michael@0 912 if (!plugin) {
michael@0 913 return NS_ERROR_FAILURE;
michael@0 914 }
michael@0 915
michael@0 916 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
michael@0 917
michael@0 918 NS_ASSERTION(pluginTag, "Must have plugin tag here!");
michael@0 919
michael@0 920 #if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
michael@0 921 if (pluginTag->mIsFlashPlugin) {
michael@0 922 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->mVersion);
michael@0 923 }
michael@0 924 #endif
michael@0 925
michael@0 926 nsRefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
michael@0 927
michael@0 928 // This will create the owning reference. The connection must be made between the
michael@0 929 // instance and the instance owner before initialization. Plugins can call into
michael@0 930 // the browser during initialization.
michael@0 931 aOwner->SetInstance(instance.get());
michael@0 932
michael@0 933 // Add the instance to the instances list before we call NPP_New so that
michael@0 934 // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
michael@0 935 mInstances.AppendElement(instance.get());
michael@0 936
michael@0 937 // this should not addref the instance or owner
michael@0 938 // except in some cases not Java, see bug 140931
michael@0 939 // our COM pointer will free the peer
michael@0 940 nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
michael@0 941 if (NS_FAILED(rv)) {
michael@0 942 mInstances.RemoveElement(instance.get());
michael@0 943 aOwner->SetInstance(nullptr);
michael@0 944 return rv;
michael@0 945 }
michael@0 946
michael@0 947 // Cancel the plugin unload timer since we are creating
michael@0 948 // an instance for it.
michael@0 949 if (pluginTag->mUnloadTimer) {
michael@0 950 pluginTag->mUnloadTimer->Cancel();
michael@0 951 }
michael@0 952
michael@0 953 #ifdef PLUGIN_LOGGING
michael@0 954 nsAutoCString urlSpec2;
michael@0 955 if (aURL)
michael@0 956 aURL->GetSpec(urlSpec2);
michael@0 957
michael@0 958 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
michael@0 959 ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
michael@0 960 aMimeType, rv, aOwner, urlSpec2.get()));
michael@0 961
michael@0 962 PR_LogFlush();
michael@0 963 #endif
michael@0 964
michael@0 965 return rv;
michael@0 966 }
michael@0 967
michael@0 968 bool
michael@0 969 nsPluginHost::PluginExistsForType(const char* aMimeType)
michael@0 970 {
michael@0 971 nsPluginTag *plugin = FindPluginForType(aMimeType, false);
michael@0 972 return nullptr != plugin;
michael@0 973 }
michael@0 974
michael@0 975 NS_IMETHODIMP
michael@0 976 nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
michael@0 977 nsIPluginTag** aResult)
michael@0 978 {
michael@0 979 nsPluginTag* plugin = FindPluginForType(aMimeType.Data(), true);
michael@0 980 if (!plugin) {
michael@0 981 plugin = FindPluginForType(aMimeType.Data(), false);
michael@0 982 }
michael@0 983 if (!plugin) {
michael@0 984 return NS_ERROR_NOT_AVAILABLE;
michael@0 985 }
michael@0 986 NS_ADDREF(*aResult = plugin);
michael@0 987 return NS_OK;
michael@0 988 }
michael@0 989
michael@0 990 NS_IMETHODIMP
michael@0 991 nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
michael@0 992 {
michael@0 993 nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
michael@0 994 if (!plugin) {
michael@0 995 plugin = FindPluginForType(aMimeType.Data(), false);
michael@0 996 }
michael@0 997 if (!plugin) {
michael@0 998 return NS_ERROR_UNEXPECTED;
michael@0 999 }
michael@0 1000
michael@0 1001 return plugin->GetEnabledState(aResult);
michael@0 1002 }
michael@0 1003
michael@0 1004 NS_IMETHODIMP
michael@0 1005 nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
michael@0 1006 {
michael@0 1007 nsPluginTag *plugin = FindPluginForType(aMimeType, true);
michael@0 1008 if (!plugin) {
michael@0 1009 plugin = FindPluginForType(aMimeType, false);
michael@0 1010 }
michael@0 1011 if (!plugin) {
michael@0 1012 return NS_ERROR_FAILURE;
michael@0 1013 }
michael@0 1014
michael@0 1015 *aState = plugin->GetBlocklistState();
michael@0 1016 return NS_OK;
michael@0 1017 }
michael@0 1018
michael@0 1019 NS_IMETHODIMP
michael@0 1020 nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
michael@0 1021 {
michael@0 1022 aPermissionString.Truncate();
michael@0 1023 uint32_t blocklistState;
michael@0 1024 nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
michael@0 1025 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1026 nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
michael@0 1027 if (!tag) {
michael@0 1028 tag = FindPluginForType(aMimeType.Data(), false);
michael@0 1029 }
michael@0 1030 if (!tag) {
michael@0 1031 return NS_ERROR_FAILURE;
michael@0 1032 }
michael@0 1033
michael@0 1034 if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
michael@0 1035 blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
michael@0 1036 aPermissionString.AssignLiteral("plugin-vulnerable:");
michael@0 1037 }
michael@0 1038 else {
michael@0 1039 aPermissionString.AssignLiteral("plugin:");
michael@0 1040 }
michael@0 1041
michael@0 1042 aPermissionString.Append(tag->GetNiceFileName());
michael@0 1043
michael@0 1044 return NS_OK;
michael@0 1045 }
michael@0 1046
michael@0 1047 // check comma delimitered extensions
michael@0 1048 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
michael@0 1049 {
michael@0 1050 if (!aExtensionList || !aExtension)
michael@0 1051 return -1;
michael@0 1052
michael@0 1053 const char *pExt = aExtensionList;
michael@0 1054 const char *pComma = strchr(pExt, ',');
michael@0 1055 if (!pComma)
michael@0 1056 return PL_strcasecmp(pExt, aExtension);
michael@0 1057
michael@0 1058 int extlen = strlen(aExtension);
michael@0 1059 while (pComma) {
michael@0 1060 int length = pComma - pExt;
michael@0 1061 if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
michael@0 1062 return 0;
michael@0 1063 pComma++;
michael@0 1064 pExt = pComma;
michael@0 1065 pComma = strchr(pExt, ',');
michael@0 1066 }
michael@0 1067
michael@0 1068 // the last one
michael@0 1069 return PL_strcasecmp(pExt, aExtension);
michael@0 1070 }
michael@0 1071
michael@0 1072 nsresult
michael@0 1073 nsPluginHost::IsPluginEnabledForExtension(const char* aExtension,
michael@0 1074 const char* &aMimeType)
michael@0 1075 {
michael@0 1076 nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
michael@0 1077 if (plugin)
michael@0 1078 return NS_OK;
michael@0 1079
michael@0 1080 return NS_ERROR_FAILURE;
michael@0 1081 }
michael@0 1082
michael@0 1083 void
michael@0 1084 nsPluginHost::GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray)
michael@0 1085 {
michael@0 1086 aPluginArray.Clear();
michael@0 1087
michael@0 1088 LoadPlugins();
michael@0 1089
michael@0 1090 nsPluginTag* plugin = mPlugins;
michael@0 1091 while (plugin != nullptr) {
michael@0 1092 if (plugin->IsEnabled()) {
michael@0 1093 aPluginArray.AppendElement(plugin);
michael@0 1094 }
michael@0 1095 plugin = plugin->mNext;
michael@0 1096 }
michael@0 1097 }
michael@0 1098
michael@0 1099 NS_IMETHODIMP
michael@0 1100 nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
michael@0 1101 {
michael@0 1102 LoadPlugins();
michael@0 1103
michael@0 1104 uint32_t count = 0;
michael@0 1105 nsRefPtr<nsPluginTag> plugin = mPlugins;
michael@0 1106 while (plugin != nullptr) {
michael@0 1107 count++;
michael@0 1108 plugin = plugin->mNext;
michael@0 1109 }
michael@0 1110
michael@0 1111 *aResults = static_cast<nsIPluginTag**>
michael@0 1112 (nsMemory::Alloc(count * sizeof(**aResults)));
michael@0 1113 if (!*aResults)
michael@0 1114 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1115
michael@0 1116 *aPluginCount = count;
michael@0 1117
michael@0 1118 plugin = mPlugins;
michael@0 1119 for (uint32_t i = 0; i < count; i++) {
michael@0 1120 (*aResults)[i] = plugin;
michael@0 1121 NS_ADDREF((*aResults)[i]);
michael@0 1122 plugin = plugin->mNext;
michael@0 1123 }
michael@0 1124
michael@0 1125 return NS_OK;
michael@0 1126 }
michael@0 1127
michael@0 1128 nsPluginTag*
michael@0 1129 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
michael@0 1130 {
michael@0 1131 // We prefer the plugin with the highest version number.
michael@0 1132 /// XXX(johns): This seems to assume the only time multiple plugins will have
michael@0 1133 /// the same MIME type is if they're multiple versions of the same
michael@0 1134 /// plugin -- but since plugin filenames and pretty names can both
michael@0 1135 /// update, it's probably less arbitrary than just going at it
michael@0 1136 /// alphabetically.
michael@0 1137
michael@0 1138 if (matches.IsEmpty()) {
michael@0 1139 return nullptr;
michael@0 1140 }
michael@0 1141
michael@0 1142 nsPluginTag *preferredPlugin = matches[0];
michael@0 1143 for (unsigned int i = 1; i < matches.Length(); i++) {
michael@0 1144 if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) {
michael@0 1145 preferredPlugin = matches[i];
michael@0 1146 }
michael@0 1147 }
michael@0 1148
michael@0 1149 return preferredPlugin;
michael@0 1150 }
michael@0 1151
michael@0 1152 nsPluginTag*
michael@0 1153 nsPluginHost::FindPluginForType(const char* aMimeType,
michael@0 1154 bool aCheckEnabled)
michael@0 1155 {
michael@0 1156 if (!aMimeType) {
michael@0 1157 return nullptr;
michael@0 1158 }
michael@0 1159
michael@0 1160 LoadPlugins();
michael@0 1161
michael@0 1162 InfallibleTArray<nsPluginTag*> matchingPlugins;
michael@0 1163
michael@0 1164 nsPluginTag *plugin = mPlugins;
michael@0 1165 while (plugin) {
michael@0 1166 if (!aCheckEnabled || plugin->IsActive()) {
michael@0 1167 int32_t mimeCount = plugin->mMimeTypes.Length();
michael@0 1168 for (int32_t i = 0; i < mimeCount; i++) {
michael@0 1169 if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) {
michael@0 1170 matchingPlugins.AppendElement(plugin);
michael@0 1171 break;
michael@0 1172 }
michael@0 1173 }
michael@0 1174 }
michael@0 1175 plugin = plugin->mNext;
michael@0 1176 }
michael@0 1177
michael@0 1178 return FindPreferredPlugin(matchingPlugins);
michael@0 1179 }
michael@0 1180
michael@0 1181 nsPluginTag*
michael@0 1182 nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
michael@0 1183 const char*& aMimeType)
michael@0 1184 {
michael@0 1185 if (!aExtension) {
michael@0 1186 return nullptr;
michael@0 1187 }
michael@0 1188
michael@0 1189 LoadPlugins();
michael@0 1190
michael@0 1191 InfallibleTArray<nsPluginTag*> matchingPlugins;
michael@0 1192
michael@0 1193 nsPluginTag *plugin = mPlugins;
michael@0 1194 while (plugin) {
michael@0 1195 if (plugin->IsActive()) {
michael@0 1196 int32_t variants = plugin->mExtensions.Length();
michael@0 1197 for (int32_t i = 0; i < variants; i++) {
michael@0 1198 // mExtensionsArray[cnt] is a list of extensions separated by commas
michael@0 1199 if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) {
michael@0 1200 matchingPlugins.AppendElement(plugin);
michael@0 1201 break;
michael@0 1202 }
michael@0 1203 }
michael@0 1204 }
michael@0 1205 plugin = plugin->mNext;
michael@0 1206 }
michael@0 1207
michael@0 1208 nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
michael@0 1209 if (!preferredPlugin) {
michael@0 1210 return nullptr;
michael@0 1211 }
michael@0 1212
michael@0 1213 int32_t variants = preferredPlugin->mExtensions.Length();
michael@0 1214 for (int32_t i = 0; i < variants; i++) {
michael@0 1215 // mExtensionsArray[cnt] is a list of extensions separated by commas
michael@0 1216 if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) {
michael@0 1217 aMimeType = preferredPlugin->mMimeTypes[i].get();
michael@0 1218 break;
michael@0 1219 }
michael@0 1220 }
michael@0 1221
michael@0 1222 return preferredPlugin;
michael@0 1223 }
michael@0 1224
michael@0 1225 static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
michael@0 1226 nsNPAPIPlugin **aOutNPAPIPlugin)
michael@0 1227 {
michael@0 1228 // If this is an in-process plugin we'll need to load it here if we haven't already.
michael@0 1229 if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
michael@0 1230 if (aPluginTag->mFullPath.IsEmpty())
michael@0 1231 return NS_ERROR_FAILURE;
michael@0 1232 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
michael@0 1233 file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
michael@0 1234 nsPluginFile pluginFile(file);
michael@0 1235 PRLibrary* pluginLibrary = nullptr;
michael@0 1236
michael@0 1237 if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
michael@0 1238 return NS_ERROR_FAILURE;
michael@0 1239
michael@0 1240 aPluginTag->mLibrary = pluginLibrary;
michael@0 1241 }
michael@0 1242
michael@0 1243 nsresult rv;
michael@0 1244 rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
michael@0 1245
michael@0 1246 return rv;
michael@0 1247 }
michael@0 1248
michael@0 1249 nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
michael@0 1250 {
michael@0 1251 nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
michael@0 1252 if (!plugin) {
michael@0 1253 nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
michael@0 1254 if (NS_FAILED(rv)) {
michael@0 1255 return rv;
michael@0 1256 }
michael@0 1257 aPluginTag->mPlugin = plugin;
michael@0 1258 }
michael@0 1259 return NS_OK;
michael@0 1260 }
michael@0 1261
michael@0 1262 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
michael@0 1263 {
michael@0 1264 nsresult rv = NS_ERROR_FAILURE;
michael@0 1265 *aPlugin = nullptr;
michael@0 1266
michael@0 1267 if (!aMimeType)
michael@0 1268 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1269
michael@0 1270 // If plugins haven't been scanned yet, do so now
michael@0 1271 LoadPlugins();
michael@0 1272
michael@0 1273 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
michael@0 1274 if (pluginTag) {
michael@0 1275 rv = NS_OK;
michael@0 1276 PLUGIN_LOG(PLUGIN_LOG_BASIC,
michael@0 1277 ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
michael@0 1278 aMimeType, pluginTag->mFileName.get()));
michael@0 1279
michael@0 1280 #ifdef DEBUG
michael@0 1281 if (aMimeType && !pluginTag->mFileName.IsEmpty())
michael@0 1282 printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
michael@0 1283 #endif
michael@0 1284
michael@0 1285 rv = EnsurePluginLoaded(pluginTag);
michael@0 1286 if (NS_FAILED(rv)) {
michael@0 1287 return rv;
michael@0 1288 }
michael@0 1289
michael@0 1290 NS_ADDREF(*aPlugin = pluginTag->mPlugin);
michael@0 1291 return NS_OK;
michael@0 1292 }
michael@0 1293
michael@0 1294 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 1295 ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
michael@0 1296 aMimeType, rv, *aPlugin,
michael@0 1297 (pluginTag ? pluginTag->mFileName.get() : "(not found)")));
michael@0 1298
michael@0 1299 return rv;
michael@0 1300 }
michael@0 1301
michael@0 1302 // Normalize 'host' to ACE.
michael@0 1303 nsresult
michael@0 1304 nsPluginHost::NormalizeHostname(nsCString& host)
michael@0 1305 {
michael@0 1306 if (IsASCII(host)) {
michael@0 1307 ToLowerCase(host);
michael@0 1308 return NS_OK;
michael@0 1309 }
michael@0 1310
michael@0 1311 if (!mIDNService) {
michael@0 1312 nsresult rv;
michael@0 1313 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
michael@0 1314 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1315 }
michael@0 1316
michael@0 1317 return mIDNService->ConvertUTF8toACE(host, host);
michael@0 1318 }
michael@0 1319
michael@0 1320 // Enumerate a 'sites' array returned by GetSitesWithData and determine if
michael@0 1321 // any of them have a base domain in common with 'domain'; if so, append them
michael@0 1322 // to the 'result' array. If 'firstMatchOnly' is true, return after finding the
michael@0 1323 // first match.
michael@0 1324 nsresult
michael@0 1325 nsPluginHost::EnumerateSiteData(const nsACString& domain,
michael@0 1326 const InfallibleTArray<nsCString>& sites,
michael@0 1327 InfallibleTArray<nsCString>& result,
michael@0 1328 bool firstMatchOnly)
michael@0 1329 {
michael@0 1330 NS_ASSERTION(!domain.IsVoid(), "null domain string");
michael@0 1331
michael@0 1332 nsresult rv;
michael@0 1333 if (!mTLDService) {
michael@0 1334 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
michael@0 1335 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1336 }
michael@0 1337
michael@0 1338 // Get the base domain from the domain.
michael@0 1339 nsCString baseDomain;
michael@0 1340 rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
michael@0 1341 bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
michael@0 1342 if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
michael@0 1343 // The base domain is the site itself. However, we must be careful to
michael@0 1344 // normalize.
michael@0 1345 baseDomain = domain;
michael@0 1346 rv = NormalizeHostname(baseDomain);
michael@0 1347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1348 } else if (NS_FAILED(rv)) {
michael@0 1349 return rv;
michael@0 1350 }
michael@0 1351
michael@0 1352 // Enumerate the array of sites with data.
michael@0 1353 for (uint32_t i = 0; i < sites.Length(); ++i) {
michael@0 1354 const nsCString& site = sites[i];
michael@0 1355
michael@0 1356 // Check if the site is an IP address.
michael@0 1357 bool siteIsIP =
michael@0 1358 site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
michael@0 1359 if (siteIsIP != isIP)
michael@0 1360 continue;
michael@0 1361
michael@0 1362 nsCString siteBaseDomain;
michael@0 1363 if (siteIsIP) {
michael@0 1364 // Strip the '[]'.
michael@0 1365 siteBaseDomain = Substring(site, 1, site.Length() - 2);
michael@0 1366 } else {
michael@0 1367 // Determine the base domain of the site.
michael@0 1368 rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
michael@0 1369 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
michael@0 1370 // The base domain is the site itself. However, we must be careful to
michael@0 1371 // normalize.
michael@0 1372 siteBaseDomain = site;
michael@0 1373 rv = NormalizeHostname(siteBaseDomain);
michael@0 1374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1375 } else if (NS_FAILED(rv)) {
michael@0 1376 return rv;
michael@0 1377 }
michael@0 1378 }
michael@0 1379
michael@0 1380 // At this point, we can do an exact comparison of the two domains.
michael@0 1381 if (baseDomain != siteBaseDomain) {
michael@0 1382 continue;
michael@0 1383 }
michael@0 1384
michael@0 1385 // Append the site to the result array.
michael@0 1386 result.AppendElement(site);
michael@0 1387
michael@0 1388 // If we're supposed to return early, do so.
michael@0 1389 if (firstMatchOnly) {
michael@0 1390 break;
michael@0 1391 }
michael@0 1392 }
michael@0 1393
michael@0 1394 return NS_OK;
michael@0 1395 }
michael@0 1396
michael@0 1397 NS_IMETHODIMP
michael@0 1398 nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType,
michael@0 1399 bool ignoreCTP,
michael@0 1400 const nsACString& redirectURL)
michael@0 1401 {
michael@0 1402 nsAutoCString mt(mimeType);
michael@0 1403 nsAutoCString url(redirectURL);
michael@0 1404 if (url.Length() == 0) {
michael@0 1405 // using default play preview iframe URL, if redirectURL is not specified
michael@0 1406 url.Assign("data:application/x-moz-playpreview;,");
michael@0 1407 url.Append(mimeType);
michael@0 1408 }
michael@0 1409
michael@0 1410 nsRefPtr<nsPluginPlayPreviewInfo> playPreview =
michael@0 1411 new nsPluginPlayPreviewInfo(mt.get(), ignoreCTP, url.get());
michael@0 1412 mPlayPreviewMimeTypes.AppendElement(playPreview);
michael@0 1413 return NS_OK;
michael@0 1414 }
michael@0 1415
michael@0 1416 NS_IMETHODIMP
michael@0 1417 nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType)
michael@0 1418 {
michael@0 1419 nsAutoCString mimeTypeToRemove(mimeType);
michael@0 1420 for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0; i--) {
michael@0 1421 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i - 1];
michael@0 1422 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToRemove.get()) == 0) {
michael@0 1423 mPlayPreviewMimeTypes.RemoveElementAt(i - 1);
michael@0 1424 break;
michael@0 1425 }
michael@0 1426 }
michael@0 1427 return NS_OK;
michael@0 1428 }
michael@0 1429
michael@0 1430 NS_IMETHODIMP
michael@0 1431 nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType,
michael@0 1432 nsIPluginPlayPreviewInfo** aResult)
michael@0 1433 {
michael@0 1434 nsAutoCString mimeTypeToFind(mimeType);
michael@0 1435 for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) {
michael@0 1436 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i];
michael@0 1437 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToFind.get()) == 0) {
michael@0 1438 *aResult = new nsPluginPlayPreviewInfo(pp.get());
michael@0 1439 NS_ADDREF(*aResult);
michael@0 1440 return NS_OK;
michael@0 1441 }
michael@0 1442 }
michael@0 1443 *aResult = nullptr;
michael@0 1444 return NS_ERROR_NOT_AVAILABLE;
michael@0 1445 }
michael@0 1446
michael@0 1447 NS_IMETHODIMP
michael@0 1448 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
michael@0 1449 uint64_t flags, int64_t maxAge)
michael@0 1450 {
michael@0 1451 // maxAge must be either a nonnegative integer or -1.
michael@0 1452 NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
michael@0 1453
michael@0 1454 // Caller may give us a tag object that is no longer live.
michael@0 1455 if (!IsLiveTag(plugin)) {
michael@0 1456 return NS_ERROR_NOT_AVAILABLE;
michael@0 1457 }
michael@0 1458
michael@0 1459 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
michael@0 1460
michael@0 1461 // We only ensure support for clearing Flash site data for now.
michael@0 1462 // We will also attempt to clear data for any plugin that happens
michael@0 1463 // to be loaded already.
michael@0 1464 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
michael@0 1465 return NS_ERROR_FAILURE;
michael@0 1466 }
michael@0 1467
michael@0 1468 // Make sure the plugin is loaded.
michael@0 1469 nsresult rv = EnsurePluginLoaded(tag);
michael@0 1470 if (NS_FAILED(rv)) {
michael@0 1471 return rv;
michael@0 1472 }
michael@0 1473
michael@0 1474 PluginLibrary* library = tag->mPlugin->GetLibrary();
michael@0 1475
michael@0 1476 // If 'domain' is the null string, clear everything.
michael@0 1477 if (domain.IsVoid()) {
michael@0 1478 return library->NPP_ClearSiteData(nullptr, flags, maxAge);
michael@0 1479 }
michael@0 1480
michael@0 1481 // Get the list of sites from the plugin.
michael@0 1482 InfallibleTArray<nsCString> sites;
michael@0 1483 rv = library->NPP_GetSitesWithData(sites);
michael@0 1484 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1485
michael@0 1486 // Enumerate the sites and build a list of matches.
michael@0 1487 InfallibleTArray<nsCString> matches;
michael@0 1488 rv = EnumerateSiteData(domain, sites, matches, false);
michael@0 1489 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1490
michael@0 1491 // Clear the matches.
michael@0 1492 for (uint32_t i = 0; i < matches.Length(); ++i) {
michael@0 1493 const nsCString& match = matches[i];
michael@0 1494 rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
michael@0 1495 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1496 }
michael@0 1497
michael@0 1498 return NS_OK;
michael@0 1499 }
michael@0 1500
michael@0 1501 NS_IMETHODIMP
michael@0 1502 nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
michael@0 1503 bool* result)
michael@0 1504 {
michael@0 1505 // Caller may give us a tag object that is no longer live.
michael@0 1506 if (!IsLiveTag(plugin)) {
michael@0 1507 return NS_ERROR_NOT_AVAILABLE;
michael@0 1508 }
michael@0 1509
michael@0 1510 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
michael@0 1511
michael@0 1512 // We only ensure support for clearing Flash site data for now.
michael@0 1513 // We will also attempt to clear data for any plugin that happens
michael@0 1514 // to be loaded already.
michael@0 1515 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
michael@0 1516 return NS_ERROR_FAILURE;
michael@0 1517 }
michael@0 1518
michael@0 1519 // Make sure the plugin is loaded.
michael@0 1520 nsresult rv = EnsurePluginLoaded(tag);
michael@0 1521 if (NS_FAILED(rv)) {
michael@0 1522 return rv;
michael@0 1523 }
michael@0 1524
michael@0 1525 PluginLibrary* library = tag->mPlugin->GetLibrary();
michael@0 1526
michael@0 1527 // Get the list of sites from the plugin.
michael@0 1528 InfallibleTArray<nsCString> sites;
michael@0 1529 rv = library->NPP_GetSitesWithData(sites);
michael@0 1530 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1531
michael@0 1532 // If there's no data, we're done.
michael@0 1533 if (sites.IsEmpty()) {
michael@0 1534 *result = false;
michael@0 1535 return NS_OK;
michael@0 1536 }
michael@0 1537
michael@0 1538 // If 'domain' is the null string, and there's data for at least one site,
michael@0 1539 // we're done.
michael@0 1540 if (domain.IsVoid()) {
michael@0 1541 *result = true;
michael@0 1542 return NS_OK;
michael@0 1543 }
michael@0 1544
michael@0 1545 // Enumerate the sites and determine if there's a match.
michael@0 1546 InfallibleTArray<nsCString> matches;
michael@0 1547 rv = EnumerateSiteData(domain, sites, matches, true);
michael@0 1548 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1549
michael@0 1550 *result = !matches.IsEmpty();
michael@0 1551 return NS_OK;
michael@0 1552 }
michael@0 1553
michael@0 1554 bool nsPluginHost::IsJavaMIMEType(const char* aType)
michael@0 1555 {
michael@0 1556 // The java mime pref may well not be one of these,
michael@0 1557 // e.g. application/x-java-test used in the test suite
michael@0 1558 nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
michael@0 1559 return aType &&
michael@0 1560 (javaMIME.EqualsIgnoreCase(aType) ||
michael@0 1561 (0 == PL_strncasecmp(aType, "application/x-java-vm",
michael@0 1562 sizeof("application/x-java-vm") - 1)) ||
michael@0 1563 (0 == PL_strncasecmp(aType, "application/x-java-applet",
michael@0 1564 sizeof("application/x-java-applet") - 1)) ||
michael@0 1565 (0 == PL_strncasecmp(aType, "application/x-java-bean",
michael@0 1566 sizeof("application/x-java-bean") - 1)));
michael@0 1567 }
michael@0 1568
michael@0 1569 // Check whether or not a tag is a live, valid tag, and that it's loaded.
michael@0 1570 bool
michael@0 1571 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
michael@0 1572 {
michael@0 1573 nsPluginTag* tag;
michael@0 1574 for (tag = mPlugins; tag; tag = tag->mNext) {
michael@0 1575 if (tag == aPluginTag) {
michael@0 1576 return true;
michael@0 1577 }
michael@0 1578 }
michael@0 1579 return false;
michael@0 1580 }
michael@0 1581
michael@0 1582 nsPluginTag*
michael@0 1583 nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
michael@0 1584 {
michael@0 1585 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
michael@0 1586 if (tag->HasSameNameAndMimes(aPluginTag)) {
michael@0 1587 return tag;
michael@0 1588 }
michael@0 1589 }
michael@0 1590 return nullptr;
michael@0 1591 }
michael@0 1592
michael@0 1593 nsPluginTag*
michael@0 1594 nsPluginHost::FirstPluginWithPath(const nsCString& path)
michael@0 1595 {
michael@0 1596 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
michael@0 1597 if (tag->mFullPath.Equals(path)) {
michael@0 1598 return tag;
michael@0 1599 }
michael@0 1600 }
michael@0 1601 return nullptr;
michael@0 1602 }
michael@0 1603
michael@0 1604 namespace {
michael@0 1605
michael@0 1606 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
michael@0 1607 {
michael@0 1608 PRTime fileModTime = 0;
michael@0 1609
michael@0 1610 #if defined(XP_MACOSX)
michael@0 1611 // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
michael@0 1612 // is a much better guide to when it was last modified than the date of
michael@0 1613 // its package directory. See bug 313700.
michael@0 1614 nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
michael@0 1615 if (localFileMac) {
michael@0 1616 localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
michael@0 1617 } else {
michael@0 1618 localfile->GetLastModifiedTime(&fileModTime);
michael@0 1619 }
michael@0 1620 #else
michael@0 1621 localfile->GetLastModifiedTime(&fileModTime);
michael@0 1622 #endif
michael@0 1623
michael@0 1624 return fileModTime;
michael@0 1625 }
michael@0 1626
michael@0 1627 bool
michael@0 1628 GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
michael@0 1629 const nsCOMArray<nsIFile>& extensionDirs)
michael@0 1630 {
michael@0 1631 for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
michael@0 1632 bool contains;
michael@0 1633 if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, true, &contains)) || !contains) {
michael@0 1634 continue;
michael@0 1635 }
michael@0 1636
michael@0 1637 return true;
michael@0 1638 }
michael@0 1639
michael@0 1640 return false;
michael@0 1641 }
michael@0 1642
michael@0 1643 void
michael@0 1644 GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
michael@0 1645 {
michael@0 1646 nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
michael@0 1647 if (!dirService) {
michael@0 1648 return;
michael@0 1649 }
michael@0 1650
michael@0 1651 nsCOMPtr<nsISimpleEnumerator> list;
michael@0 1652 nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
michael@0 1653 NS_GET_IID(nsISimpleEnumerator),
michael@0 1654 getter_AddRefs(list));
michael@0 1655 if (NS_FAILED(rv)) {
michael@0 1656 return;
michael@0 1657 }
michael@0 1658
michael@0 1659 bool more;
michael@0 1660 while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
michael@0 1661 nsCOMPtr<nsISupports> next;
michael@0 1662 if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
michael@0 1663 break;
michael@0 1664 }
michael@0 1665 nsCOMPtr<nsIFile> file = do_QueryInterface(next);
michael@0 1666 if (file) {
michael@0 1667 file->Normalize();
michael@0 1668 dirs.AppendElement(file);
michael@0 1669 }
michael@0 1670 }
michael@0 1671 }
michael@0 1672
michael@0 1673 struct CompareFilesByTime
michael@0 1674 {
michael@0 1675 bool
michael@0 1676 LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
michael@0 1677 {
michael@0 1678 return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
michael@0 1679 }
michael@0 1680
michael@0 1681 bool
michael@0 1682 Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
michael@0 1683 {
michael@0 1684 return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
michael@0 1685 }
michael@0 1686 };
michael@0 1687
michael@0 1688 } // anonymous namespace
michael@0 1689
michael@0 1690 PRBool nsPluginHost::GhettoBlacklist(nsIFile *pluginFile)
michael@0 1691 {
michael@0 1692 nsCString leaf;
michael@0 1693 const char *leafStr;
michael@0 1694 nsresult rv;
michael@0 1695
michael@0 1696 rv = pluginFile->GetNativeLeafName(leaf);
michael@0 1697 if (NS_FAILED(rv)) {
michael@0 1698 return PR_TRUE; // fuck 'em. blacklist.
michael@0 1699 }
michael@0 1700
michael@0 1701 leafStr = leaf.get();
michael@0 1702
michael@0 1703 if (!leafStr) {
michael@0 1704 return PR_TRUE; // fuck 'em. blacklist.
michael@0 1705 }
michael@0 1706
michael@0 1707 // libgnashplugin.so, libflashplayer.so, Flash Player-10.4-10.5.plugin,
michael@0 1708 // NPSWF32.dll, NPSWF64.dll
michael@0 1709 if (strstr(leafStr, "libgnashplugin") == leafStr ||
michael@0 1710 strstr(leafStr, "libflashplayer") == leafStr ||
michael@0 1711 strstr(leafStr, "Flash Player") == leafStr ||
michael@0 1712 strstr(leafStr, "NPSWF") == leafStr) {
michael@0 1713 return PR_FALSE;
michael@0 1714 }
michael@0 1715
michael@0 1716 return PR_TRUE; // fuck 'em. blacklist.
michael@0 1717 }
michael@0 1718
michael@0 1719 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
michael@0 1720
michael@0 1721 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
michael@0 1722 bool aCreatePluginList,
michael@0 1723 bool *aPluginsChanged)
michael@0 1724 {
michael@0 1725 NS_ENSURE_ARG_POINTER(aPluginsChanged);
michael@0 1726 nsresult rv;
michael@0 1727
michael@0 1728 *aPluginsChanged = false;
michael@0 1729
michael@0 1730 #ifdef PLUGIN_LOGGING
michael@0 1731 nsAutoCString dirPath;
michael@0 1732 pluginsDir->GetNativePath(dirPath);
michael@0 1733 PLUGIN_LOG(PLUGIN_LOG_BASIC,
michael@0 1734 ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
michael@0 1735 #endif
michael@0 1736
michael@0 1737 nsCOMPtr<nsISimpleEnumerator> iter;
michael@0 1738 rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
michael@0 1739 if (NS_FAILED(rv))
michael@0 1740 return rv;
michael@0 1741
michael@0 1742 nsAutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
michael@0 1743
michael@0 1744 bool hasMore;
michael@0 1745 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
michael@0 1746 nsCOMPtr<nsISupports> supports;
michael@0 1747 rv = iter->GetNext(getter_AddRefs(supports));
michael@0 1748 if (NS_FAILED(rv))
michael@0 1749 continue;
michael@0 1750 nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
michael@0 1751 if (NS_FAILED(rv))
michael@0 1752 continue;
michael@0 1753
michael@0 1754 // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
michael@0 1755 // See bug 197855.
michael@0 1756 dirEntry->Normalize();
michael@0 1757
michael@0 1758 if (nsPluginsDir::IsPluginFile(dirEntry)) {
michael@0 1759 pluginFiles.AppendElement(dirEntry);
michael@0 1760 }
michael@0 1761 }
michael@0 1762
michael@0 1763 pluginFiles.Sort(CompareFilesByTime());
michael@0 1764
michael@0 1765 nsCOMArray<nsIFile> extensionDirs;
michael@0 1766 GetExtensionDirectories(extensionDirs);
michael@0 1767
michael@0 1768 bool warnOutdated = false;
michael@0 1769
michael@0 1770 for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
michael@0 1771 nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
michael@0 1772
michael@0 1773 nsString utf16FilePath;
michael@0 1774 rv = localfile->GetPath(utf16FilePath);
michael@0 1775 if (NS_FAILED(rv))
michael@0 1776 continue;
michael@0 1777
michael@0 1778 const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
michael@0 1779 const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
michael@0 1780
michael@0 1781 // Look for it in our cache
michael@0 1782 NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
michael@0 1783 nsRefPtr<nsPluginTag> pluginTag;
michael@0 1784 RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
michael@0 1785
michael@0 1786 bool seenBefore = false;
michael@0 1787
michael@0 1788 if (pluginTag) {
michael@0 1789 seenBefore = true;
michael@0 1790 // If plugin changed, delete cachedPluginTag and don't use cache
michael@0 1791 if (fileModTime != pluginTag->mLastModifiedTime) {
michael@0 1792 // Plugins has changed. Don't use cached plugin info.
michael@0 1793 pluginTag = nullptr;
michael@0 1794
michael@0 1795 // plugin file changed, flag this fact
michael@0 1796 *aPluginsChanged = true;
michael@0 1797 }
michael@0 1798
michael@0 1799 // If we're not creating a list and we already know something changed then
michael@0 1800 // we're done.
michael@0 1801 if (!aCreatePluginList) {
michael@0 1802 if (*aPluginsChanged) {
michael@0 1803 return NS_OK;
michael@0 1804 }
michael@0 1805 continue;
michael@0 1806 }
michael@0 1807 }
michael@0 1808
michael@0 1809 bool isKnownInvalidPlugin = false;
michael@0 1810 for (nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
michael@0 1811 invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
michael@0 1812 // If already marked as invalid, ignore it
michael@0 1813 if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
michael@0 1814 invalidPlugins->mLastModifiedTime == fileModTime) {
michael@0 1815 if (aCreatePluginList) {
michael@0 1816 invalidPlugins->mSeen = true;
michael@0 1817 }
michael@0 1818 isKnownInvalidPlugin = true;
michael@0 1819 break;
michael@0 1820 }
michael@0 1821 }
michael@0 1822 if (isKnownInvalidPlugin) {
michael@0 1823 continue;
michael@0 1824 }
michael@0 1825
michael@0 1826 if (GhettoBlacklist(localfile)) {
michael@0 1827 continue;
michael@0 1828 }
michael@0 1829
michael@0 1830 // if it is not found in cache info list or has been changed, create a new one
michael@0 1831 if (!pluginTag) {
michael@0 1832 nsPluginFile pluginFile(localfile);
michael@0 1833
michael@0 1834 // create a tag describing this plugin.
michael@0 1835 PRLibrary *library = nullptr;
michael@0 1836 nsPluginInfo info;
michael@0 1837 memset(&info, 0, sizeof(info));
michael@0 1838 nsresult res;
michael@0 1839 // Opening a block for the telemetry AutoTimer
michael@0 1840 {
michael@0 1841 Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
michael@0 1842 res = pluginFile.GetPluginInfo(info, &library);
michael@0 1843 }
michael@0 1844 // if we don't have mime type don't proceed, this is not a plugin
michael@0 1845 if (NS_FAILED(res) || !info.fMimeTypeArray) {
michael@0 1846 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
michael@0 1847 fileModTime);
michael@0 1848 pluginFile.FreePluginInfo(info);
michael@0 1849
michael@0 1850 if (aCreatePluginList) {
michael@0 1851 invalidTag->mSeen = true;
michael@0 1852 }
michael@0 1853 invalidTag->mNext = mInvalidPlugins;
michael@0 1854 if (mInvalidPlugins) {
michael@0 1855 mInvalidPlugins->mPrev = invalidTag;
michael@0 1856 }
michael@0 1857 mInvalidPlugins = invalidTag;
michael@0 1858
michael@0 1859 // Mark aPluginsChanged so pluginreg is rewritten
michael@0 1860 *aPluginsChanged = true;
michael@0 1861 continue;
michael@0 1862 }
michael@0 1863
michael@0 1864 pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
michael@0 1865 pluginFile.FreePluginInfo(info);
michael@0 1866 if (!pluginTag)
michael@0 1867 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1868
michael@0 1869 pluginTag->mLibrary = library;
michael@0 1870 uint32_t state = pluginTag->GetBlocklistState();
michael@0 1871
michael@0 1872 // If the blocklist says it is risky and we have never seen this
michael@0 1873 // plugin before, then disable it.
michael@0 1874 // If the blocklist says this is an outdated plugin, warn about
michael@0 1875 // outdated plugins.
michael@0 1876 if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
michael@0 1877 pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
michael@0 1878 }
michael@0 1879 if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
michael@0 1880 warnOutdated = true;
michael@0 1881 }
michael@0 1882
michael@0 1883 // Plugin unloading is tag-based. If we created a new tag and loaded
michael@0 1884 // the library in the process then we want to attempt to unload it here.
michael@0 1885 // Only do this if the pref is set for aggressive unloading.
michael@0 1886 if (UnloadPluginsASAP()) {
michael@0 1887 pluginTag->TryUnloadPlugin(false);
michael@0 1888 }
michael@0 1889 }
michael@0 1890
michael@0 1891 // do it if we still want it
michael@0 1892 if (!seenBefore) {
michael@0 1893 // We have a valid new plugin so report that plugins have changed.
michael@0 1894 *aPluginsChanged = true;
michael@0 1895 }
michael@0 1896
michael@0 1897 // Avoid adding different versions of the same plugin if they are running
michael@0 1898 // in-process, otherwise we risk undefined behaviour.
michael@0 1899 if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
michael@0 1900 if (HaveSamePlugin(pluginTag)) {
michael@0 1901 continue;
michael@0 1902 }
michael@0 1903 }
michael@0 1904
michael@0 1905 // Don't add the same plugin again if it hasn't changed
michael@0 1906 if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
michael@0 1907 if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
michael@0 1908 continue;
michael@0 1909 }
michael@0 1910 }
michael@0 1911
michael@0 1912 // If we're not creating a plugin list, simply looking for changes,
michael@0 1913 // then we're done.
michael@0 1914 if (!aCreatePluginList) {
michael@0 1915 return NS_OK;
michael@0 1916 }
michael@0 1917
michael@0 1918 // Add plugin tags such that the list is ordered by modification date,
michael@0 1919 // newest to oldest. This is ugly, it'd be easier with just about anything
michael@0 1920 // other than a single-directional linked list.
michael@0 1921 if (mPlugins) {
michael@0 1922 nsPluginTag *prev = nullptr;
michael@0 1923 nsPluginTag *next = mPlugins;
michael@0 1924 while (next) {
michael@0 1925 if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
michael@0 1926 pluginTag->mNext = next;
michael@0 1927 if (prev) {
michael@0 1928 prev->mNext = pluginTag;
michael@0 1929 } else {
michael@0 1930 mPlugins = pluginTag;
michael@0 1931 }
michael@0 1932 break;
michael@0 1933 }
michael@0 1934 prev = next;
michael@0 1935 next = prev->mNext;
michael@0 1936 if (!next) {
michael@0 1937 prev->mNext = pluginTag;
michael@0 1938 }
michael@0 1939 }
michael@0 1940 } else {
michael@0 1941 mPlugins = pluginTag;
michael@0 1942 }
michael@0 1943
michael@0 1944 if (pluginTag->IsActive()) {
michael@0 1945 nsAdoptingCString disableFullPage =
michael@0 1946 Preferences::GetCString(kPrefDisableFullPage);
michael@0 1947 for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
michael@0 1948 if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
michael@0 1949 RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
michael@0 1950 ePluginRegister);
michael@0 1951 }
michael@0 1952 }
michael@0 1953 }
michael@0 1954 }
michael@0 1955
michael@0 1956 if (warnOutdated) {
michael@0 1957 Preferences::SetBool("plugins.update.notifyUser", true);
michael@0 1958 }
michael@0 1959
michael@0 1960 return NS_OK;
michael@0 1961 }
michael@0 1962
michael@0 1963 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
michael@0 1964 bool aCreatePluginList,
michael@0 1965 bool *aPluginsChanged)
michael@0 1966 {
michael@0 1967 bool hasMore;
michael@0 1968 while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
michael@0 1969 nsCOMPtr<nsISupports> supports;
michael@0 1970 nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
michael@0 1971 if (NS_FAILED(rv))
michael@0 1972 continue;
michael@0 1973 nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
michael@0 1974 if (NS_FAILED(rv))
michael@0 1975 continue;
michael@0 1976
michael@0 1977 // don't pass aPluginsChanged directly to prevent it from been reset
michael@0 1978 bool pluginschanged = false;
michael@0 1979 ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
michael@0 1980
michael@0 1981 if (pluginschanged)
michael@0 1982 *aPluginsChanged = true;
michael@0 1983
michael@0 1984 // if changes are detected and we are not creating the list, do not proceed
michael@0 1985 if (!aCreatePluginList && *aPluginsChanged)
michael@0 1986 break;
michael@0 1987 }
michael@0 1988 return NS_OK;
michael@0 1989 }
michael@0 1990
michael@0 1991 nsresult nsPluginHost::LoadPlugins()
michael@0 1992 {
michael@0 1993 #ifdef ANDROID
michael@0 1994 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 1995 return NS_OK;
michael@0 1996 }
michael@0 1997 #endif
michael@0 1998 // do not do anything if it is already done
michael@0 1999 // use ReloadPlugins() to enforce loading
michael@0 2000 if (mPluginsLoaded)
michael@0 2001 return NS_OK;
michael@0 2002
michael@0 2003 if (mPluginsDisabled)
michael@0 2004 return NS_OK;
michael@0 2005
michael@0 2006 bool pluginschanged;
michael@0 2007 nsresult rv = FindPlugins(true, &pluginschanged);
michael@0 2008 if (NS_FAILED(rv))
michael@0 2009 return rv;
michael@0 2010
michael@0 2011 // only if plugins have changed will we notify plugin-change observers
michael@0 2012 if (pluginschanged) {
michael@0 2013 nsCOMPtr<nsIObserverService> obsService =
michael@0 2014 mozilla::services::GetObserverService();
michael@0 2015 if (obsService)
michael@0 2016 obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
michael@0 2017 }
michael@0 2018
michael@0 2019 return NS_OK;
michael@0 2020 }
michael@0 2021
michael@0 2022 // if aCreatePluginList is false we will just scan for plugins
michael@0 2023 // and see if any changes have been made to the plugins.
michael@0 2024 // This is needed in ReloadPlugins to prevent possible recursive reloads
michael@0 2025 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
michael@0 2026 {
michael@0 2027 Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
michael@0 2028
michael@0 2029 NS_ENSURE_ARG_POINTER(aPluginsChanged);
michael@0 2030
michael@0 2031 *aPluginsChanged = false;
michael@0 2032 nsresult rv;
michael@0 2033
michael@0 2034 // Read cached plugins info. If the profile isn't yet available then don't
michael@0 2035 // scan for plugins
michael@0 2036 if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
michael@0 2037 return NS_OK;
michael@0 2038
michael@0 2039 #ifdef XP_WIN
michael@0 2040 // Failure here is not a show-stopper so just warn.
michael@0 2041 rv = EnsurePrivateDirServiceProvider();
michael@0 2042 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
michael@0 2043 #endif /* XP_WIN */
michael@0 2044
michael@0 2045 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
michael@0 2046 if (NS_FAILED(rv))
michael@0 2047 return rv;
michael@0 2048
michael@0 2049 nsCOMPtr<nsISimpleEnumerator> dirList;
michael@0 2050
michael@0 2051 // Scan plugins directories;
michael@0 2052 // don't pass aPluginsChanged directly, to prevent its
michael@0 2053 // possible reset in subsequent ScanPluginsDirectory calls
michael@0 2054 bool pluginschanged = false;
michael@0 2055
michael@0 2056 // Scan the app-defined list of plugin dirs.
michael@0 2057 rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
michael@0 2058 if (NS_SUCCEEDED(rv)) {
michael@0 2059 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
michael@0 2060
michael@0 2061 if (pluginschanged)
michael@0 2062 *aPluginsChanged = true;
michael@0 2063
michael@0 2064 // if we are just looking for possible changes,
michael@0 2065 // no need to proceed if changes are detected
michael@0 2066 if (!aCreatePluginList && *aPluginsChanged) {
michael@0 2067 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2068 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2069 return NS_OK;
michael@0 2070 }
michael@0 2071 } else {
michael@0 2072 #ifdef ANDROID
michael@0 2073 LOG("getting plugins dir failed");
michael@0 2074 #endif
michael@0 2075 }
michael@0 2076
michael@0 2077 mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
michael@0 2078 // the rest is optional
michael@0 2079
michael@0 2080 #ifdef XP_WIN
michael@0 2081 bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
michael@0 2082
michael@0 2083 // Now lets scan any PLID directories
michael@0 2084 if (bScanPLIDs && mPrivateDirServiceProvider) {
michael@0 2085 rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
michael@0 2086 if (NS_SUCCEEDED(rv)) {
michael@0 2087 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
michael@0 2088
michael@0 2089 if (pluginschanged)
michael@0 2090 *aPluginsChanged = true;
michael@0 2091
michael@0 2092 // if we are just looking for possible changes,
michael@0 2093 // no need to proceed if changes are detected
michael@0 2094 if (!aCreatePluginList && *aPluginsChanged) {
michael@0 2095 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2096 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2097 return NS_OK;
michael@0 2098 }
michael@0 2099 }
michael@0 2100 }
michael@0 2101
michael@0 2102
michael@0 2103 // Scan the installation paths of our popular plugins if the prefs are enabled
michael@0 2104
michael@0 2105 // This table controls the order of scanning
michael@0 2106 const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY,
michael@0 2107 NS_WIN_QUICKTIME_SCAN_KEY,
michael@0 2108 NS_WIN_WMP_SCAN_KEY};
michael@0 2109
michael@0 2110 uint32_t size = sizeof(prefs) / sizeof(prefs[0]);
michael@0 2111
michael@0 2112 for (uint32_t i = 0; i < size; i+=1) {
michael@0 2113 nsCOMPtr<nsIFile> dirToScan;
michael@0 2114 bool bExists;
michael@0 2115 if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
michael@0 2116 dirToScan &&
michael@0 2117 NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
michael@0 2118 bExists) {
michael@0 2119
michael@0 2120 ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
michael@0 2121
michael@0 2122 if (pluginschanged)
michael@0 2123 *aPluginsChanged = true;
michael@0 2124
michael@0 2125 // if we are just looking for possible changes,
michael@0 2126 // no need to proceed if changes are detected
michael@0 2127 if (!aCreatePluginList && *aPluginsChanged) {
michael@0 2128 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2129 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2130 return NS_OK;
michael@0 2131 }
michael@0 2132 }
michael@0 2133 }
michael@0 2134 #endif
michael@0 2135
michael@0 2136 // We should also consider plugins to have changed if any plugins have been removed.
michael@0 2137 // We'll know if any were removed if they weren't taken out of the cached plugins list
michael@0 2138 // during our scan, thus we can assume something was removed if the cached plugins list
michael@0 2139 // contains anything.
michael@0 2140 if (!*aPluginsChanged && mCachedPlugins) {
michael@0 2141 *aPluginsChanged = true;
michael@0 2142 }
michael@0 2143
michael@0 2144 // Remove unseen invalid plugins
michael@0 2145 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
michael@0 2146 while (invalidPlugins) {
michael@0 2147 if (!invalidPlugins->mSeen) {
michael@0 2148 nsRefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
michael@0 2149
michael@0 2150 if (invalidPlugin->mPrev) {
michael@0 2151 invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
michael@0 2152 }
michael@0 2153 else {
michael@0 2154 mInvalidPlugins = invalidPlugin->mNext;
michael@0 2155 }
michael@0 2156 if (invalidPlugin->mNext) {
michael@0 2157 invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
michael@0 2158 }
michael@0 2159
michael@0 2160 invalidPlugins = invalidPlugin->mNext;
michael@0 2161
michael@0 2162 invalidPlugin->mPrev = nullptr;
michael@0 2163 invalidPlugin->mNext = nullptr;
michael@0 2164 }
michael@0 2165 else {
michael@0 2166 invalidPlugins->mSeen = false;
michael@0 2167 invalidPlugins = invalidPlugins->mNext;
michael@0 2168 }
michael@0 2169 }
michael@0 2170
michael@0 2171 // if we are not creating the list, there is no need to proceed
michael@0 2172 if (!aCreatePluginList) {
michael@0 2173 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2174 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2175 return NS_OK;
michael@0 2176 }
michael@0 2177
michael@0 2178 // if we are creating the list, it is already done;
michael@0 2179 // update the plugins info cache if changes are detected
michael@0 2180 if (*aPluginsChanged)
michael@0 2181 WritePluginInfo();
michael@0 2182
michael@0 2183 // No more need for cached plugins. Clear it up.
michael@0 2184 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2185 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2186
michael@0 2187 return NS_OK;
michael@0 2188 }
michael@0 2189
michael@0 2190 nsresult
michael@0 2191 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
michael@0 2192 {
michael@0 2193 ReadPluginInfo();
michael@0 2194 WritePluginInfo();
michael@0 2195 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
michael@0 2196 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
michael@0 2197
michael@0 2198 if (!aPluginTag) {
michael@0 2199 return NS_OK;
michael@0 2200 }
michael@0 2201
michael@0 2202 // Update types with category manager
michael@0 2203 nsAdoptingCString disableFullPage =
michael@0 2204 Preferences::GetCString(kPrefDisableFullPage);
michael@0 2205 for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
michael@0 2206 nsRegisterType shouldRegister;
michael@0 2207
michael@0 2208 if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
michael@0 2209 shouldRegister = ePluginUnregister;
michael@0 2210 } else {
michael@0 2211 nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
michael@0 2212 true);
michael@0 2213 shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
michael@0 2214 }
michael@0 2215
michael@0 2216 RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister);
michael@0 2217 }
michael@0 2218
michael@0 2219 nsCOMPtr<nsIObserverService> obsService =
michael@0 2220 mozilla::services::GetObserverService();
michael@0 2221 if (obsService)
michael@0 2222 obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
michael@0 2223
michael@0 2224 // Reload instances if needed
michael@0 2225 if (aPluginTag->IsActive()) {
michael@0 2226 return NS_OK;
michael@0 2227 }
michael@0 2228
michael@0 2229 return NS_OK;
michael@0 2230 }
michael@0 2231
michael@0 2232 /* static */ bool
michael@0 2233 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
michael@0 2234 {
michael@0 2235 nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
michael@0 2236 if (!whitelist.Length()) {
michael@0 2237 return true;
michael@0 2238 }
michael@0 2239 nsDependentCString wrap(aMimeType);
michael@0 2240 return IsTypeInList(wrap, whitelist);
michael@0 2241 }
michael@0 2242
michael@0 2243 void
michael@0 2244 nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
michael@0 2245 nsRegisterType aType)
michael@0 2246 {
michael@0 2247 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 2248 ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
michael@0 2249 aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
michael@0 2250
michael@0 2251 nsCOMPtr<nsICategoryManager> catMan =
michael@0 2252 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
michael@0 2253 if (!catMan) {
michael@0 2254 return;
michael@0 2255 }
michael@0 2256
michael@0 2257 const char *contractId =
michael@0 2258 "@mozilla.org/content/plugin/document-loader-factory;1";
michael@0 2259
michael@0 2260 if (aType == ePluginRegister) {
michael@0 2261 catMan->AddCategoryEntry("Gecko-Content-Viewers",
michael@0 2262 aMimeType.get(),
michael@0 2263 contractId,
michael@0 2264 false, /* persist: broken by bug 193031 */
michael@0 2265 mOverrideInternalTypes,
michael@0 2266 nullptr);
michael@0 2267 } else {
michael@0 2268 // Only delete the entry if a plugin registered for it
michael@0 2269 nsXPIDLCString value;
michael@0 2270 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
michael@0 2271 aMimeType.get(),
michael@0 2272 getter_Copies(value));
michael@0 2273 if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
michael@0 2274 catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
michael@0 2275 aMimeType.get(),
michael@0 2276 true);
michael@0 2277 }
michael@0 2278 }
michael@0 2279 }
michael@0 2280
michael@0 2281 nsresult
michael@0 2282 nsPluginHost::WritePluginInfo()
michael@0 2283 {
michael@0 2284
michael@0 2285 nsresult rv = NS_OK;
michael@0 2286 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
michael@0 2287 if (NS_FAILED(rv))
michael@0 2288 return rv;
michael@0 2289
michael@0 2290 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
michael@0 2291 getter_AddRefs(mPluginRegFile));
michael@0 2292
michael@0 2293 if (!mPluginRegFile)
michael@0 2294 return NS_ERROR_FAILURE;
michael@0 2295
michael@0 2296 PRFileDesc* fd = nullptr;
michael@0 2297
michael@0 2298 nsCOMPtr<nsIFile> pluginReg;
michael@0 2299
michael@0 2300 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
michael@0 2301 if (NS_FAILED(rv))
michael@0 2302 return rv;
michael@0 2303
michael@0 2304 nsAutoCString filename(kPluginRegistryFilename);
michael@0 2305 filename.Append(".tmp");
michael@0 2306 rv = pluginReg->AppendNative(filename);
michael@0 2307 if (NS_FAILED(rv))
michael@0 2308 return rv;
michael@0 2309
michael@0 2310 rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
michael@0 2311 if (NS_FAILED(rv))
michael@0 2312 return rv;
michael@0 2313
michael@0 2314 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
michael@0 2315 if (!runtime) {
michael@0 2316 return NS_ERROR_FAILURE;
michael@0 2317 }
michael@0 2318
michael@0 2319 nsAutoCString arch;
michael@0 2320 rv = runtime->GetXPCOMABI(arch);
michael@0 2321 if (NS_FAILED(rv)) {
michael@0 2322 return rv;
michael@0 2323 }
michael@0 2324
michael@0 2325 PR_fprintf(fd, "Generated File. Do not edit.\n");
michael@0 2326
michael@0 2327 PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n",
michael@0 2328 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2329 kPluginRegistryVersion,
michael@0 2330 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2331 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
michael@0 2332 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2333 arch.get(),
michael@0 2334 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2335 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2336
michael@0 2337 // Store all plugins in the mPlugins list - all plugins currently in use.
michael@0 2338 PR_fprintf(fd, "\n[PLUGINS]\n");
michael@0 2339
michael@0 2340 for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
michael@0 2341 // store each plugin info into the registry
michael@0 2342 // filename & fullpath are on separate line
michael@0 2343 // because they can contain field delimiter char
michael@0 2344 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
michael@0 2345 (tag->mFileName.get()),
michael@0 2346 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2347 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
michael@0 2348 (tag->mFullPath.get()),
michael@0 2349 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2350 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
michael@0 2351 (tag->mVersion.get()),
michael@0 2352 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2353 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2354
michael@0 2355 // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
michael@0 2356 PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
michael@0 2357 tag->mLastModifiedTime,
michael@0 2358 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2359 false, // did store whether or not to unload in-process plugins
michael@0 2360 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2361 0, // legacy field for flags
michael@0 2362 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2363 tag->IsFromExtension(),
michael@0 2364 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2365 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2366
michael@0 2367 //description, name & mtypecount are on separate line
michael@0 2368 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
michael@0 2369 (tag->mDescription.get()),
michael@0 2370 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2371 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
michael@0 2372 (tag->mName.get()),
michael@0 2373 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2374 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
michael@0 2375 tag->mMimeTypes.Length());
michael@0 2376
michael@0 2377 // Add in each mimetype this plugin supports
michael@0 2378 for (uint32_t i = 0; i < tag->mMimeTypes.Length(); i++) {
michael@0 2379 PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
michael@0 2380 i,PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2381 (tag->mMimeTypes[i].get()),
michael@0 2382 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2383 (tag->mMimeDescriptions[i].get()),
michael@0 2384 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2385 (tag->mExtensions[i].get()),
michael@0 2386 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2387 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2388 }
michael@0 2389 }
michael@0 2390
michael@0 2391 PR_fprintf(fd, "\n[INVALID]\n");
michael@0 2392
michael@0 2393 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
michael@0 2394 while (invalidPlugins) {
michael@0 2395 // fullPath
michael@0 2396 PR_fprintf(fd, "%s%c%c\n",
michael@0 2397 (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
michael@0 2398 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2399 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2400
michael@0 2401 // lastModifiedTimeStamp
michael@0 2402 PR_fprintf(fd, "%lld%c%c\n",
michael@0 2403 invalidPlugins->mLastModifiedTime,
michael@0 2404 PLUGIN_REGISTRY_FIELD_DELIMITER,
michael@0 2405 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
michael@0 2406
michael@0 2407 invalidPlugins = invalidPlugins->mNext;
michael@0 2408 }
michael@0 2409
michael@0 2410 PRStatus prrc;
michael@0 2411 prrc = PR_Close(fd);
michael@0 2412 if (prrc != PR_SUCCESS) {
michael@0 2413 // we should obtain a refined value based on prrc;
michael@0 2414 rv = NS_ERROR_FAILURE;
michael@0 2415 MOZ_ASSERT(false, "PR_Close() failed.");
michael@0 2416 return rv;
michael@0 2417 }
michael@0 2418 nsCOMPtr<nsIFile> parent;
michael@0 2419 rv = pluginReg->GetParent(getter_AddRefs(parent));
michael@0 2420 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2421 rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
michael@0 2422 return rv;
michael@0 2423 }
michael@0 2424
michael@0 2425 nsresult
michael@0 2426 nsPluginHost::ReadPluginInfo()
michael@0 2427 {
michael@0 2428 const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
michael@0 2429 const long PLUGIN_REG_MAX_MIMETYPES = 1000;
michael@0 2430
michael@0 2431 // we need to import the legacy flags from the plugin registry once
michael@0 2432 const bool pluginStateImported =
michael@0 2433 Preferences::GetDefaultBool("plugin.importedState", false);
michael@0 2434
michael@0 2435 nsresult rv;
michael@0 2436
michael@0 2437 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
michael@0 2438 if (NS_FAILED(rv))
michael@0 2439 return rv;
michael@0 2440
michael@0 2441 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
michael@0 2442 getter_AddRefs(mPluginRegFile));
michael@0 2443
michael@0 2444 if (!mPluginRegFile) {
michael@0 2445 // There is no profile yet, this will tell us if there is going to be one
michael@0 2446 // in the future.
michael@0 2447 directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
michael@0 2448 getter_AddRefs(mPluginRegFile));
michael@0 2449 if (!mPluginRegFile)
michael@0 2450 return NS_ERROR_FAILURE;
michael@0 2451 else
michael@0 2452 return NS_ERROR_NOT_AVAILABLE;
michael@0 2453 }
michael@0 2454
michael@0 2455 PRFileDesc* fd = nullptr;
michael@0 2456
michael@0 2457 nsCOMPtr<nsIFile> pluginReg;
michael@0 2458
michael@0 2459 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
michael@0 2460 if (NS_FAILED(rv))
michael@0 2461 return rv;
michael@0 2462
michael@0 2463 rv = pluginReg->AppendNative(kPluginRegistryFilename);
michael@0 2464 if (NS_FAILED(rv))
michael@0 2465 return rv;
michael@0 2466
michael@0 2467 int64_t fileSize;
michael@0 2468 rv = pluginReg->GetFileSize(&fileSize);
michael@0 2469 if (NS_FAILED(rv))
michael@0 2470 return rv;
michael@0 2471
michael@0 2472 if (fileSize > INT32_MAX) {
michael@0 2473 return NS_ERROR_FAILURE;
michael@0 2474 }
michael@0 2475 int32_t flen = int32_t(fileSize);
michael@0 2476 if (flen == 0) {
michael@0 2477 NS_WARNING("Plugins Registry Empty!");
michael@0 2478 return NS_OK; // ERROR CONDITION
michael@0 2479 }
michael@0 2480
michael@0 2481 nsPluginManifestLineReader reader;
michael@0 2482 char* registry = reader.Init(flen);
michael@0 2483 if (!registry)
michael@0 2484 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2485
michael@0 2486 rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
michael@0 2487 if (NS_FAILED(rv))
michael@0 2488 return rv;
michael@0 2489
michael@0 2490 // set rv to return an error on goto out
michael@0 2491 rv = NS_ERROR_FAILURE;
michael@0 2492
michael@0 2493 int32_t bread = PR_Read(fd, registry, flen);
michael@0 2494
michael@0 2495 PRStatus prrc;
michael@0 2496 prrc = PR_Close(fd);
michael@0 2497 if (prrc != PR_SUCCESS) {
michael@0 2498 // Strange error: this is one of those "Should not happen" error.
michael@0 2499 // we may want to report something more refined than NS_ERROR_FAILURE.
michael@0 2500 MOZ_ASSERT(false, "PR_Close() failed.");
michael@0 2501 return rv;
michael@0 2502 }
michael@0 2503
michael@0 2504 if (flen > bread)
michael@0 2505 return rv;
michael@0 2506
michael@0 2507 if (!ReadSectionHeader(reader, "HEADER"))
michael@0 2508 return rv;;
michael@0 2509
michael@0 2510 if (!reader.NextLine())
michael@0 2511 return rv;
michael@0 2512
michael@0 2513 char* values[6];
michael@0 2514
michael@0 2515 // VersionLiteral, kPluginRegistryVersion
michael@0 2516 if (2 != reader.ParseLine(values, 2))
michael@0 2517 return rv;
michael@0 2518
michael@0 2519 // VersionLiteral
michael@0 2520 if (PL_strcmp(values[0], "Version"))
michael@0 2521 return rv;
michael@0 2522
michael@0 2523 // kPluginRegistryVersion
michael@0 2524 int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
michael@0 2525 mozilla::Version version(values[1]);
michael@0 2526 // If this is a registry from some future version then don't attempt to read it
michael@0 2527 if (vdiff > 0)
michael@0 2528 return rv;
michael@0 2529 // If this is a registry from before the minimum then don't attempt to read it
michael@0 2530 if (version < kMinimumRegistryVersion)
michael@0 2531 return rv;
michael@0 2532
michael@0 2533 // Registry v0.10 and upwards includes the plugin version field
michael@0 2534 bool regHasVersion = (version >= "0.10");
michael@0 2535
michael@0 2536 // Registry v0.13 and upwards includes the architecture
michael@0 2537 if (version >= "0.13") {
michael@0 2538 char* archValues[6];
michael@0 2539
michael@0 2540 if (!reader.NextLine()) {
michael@0 2541 return rv;
michael@0 2542 }
michael@0 2543
michael@0 2544 // ArchLiteral, Architecture
michael@0 2545 if (2 != reader.ParseLine(archValues, 2)) {
michael@0 2546 return rv;
michael@0 2547 }
michael@0 2548
michael@0 2549 // ArchLiteral
michael@0 2550 if (PL_strcmp(archValues[0], "Arch")) {
michael@0 2551 return rv;
michael@0 2552 }
michael@0 2553
michael@0 2554 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
michael@0 2555 if (!runtime) {
michael@0 2556 return rv;
michael@0 2557 }
michael@0 2558
michael@0 2559 nsAutoCString arch;
michael@0 2560 if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
michael@0 2561 return rv;
michael@0 2562 }
michael@0 2563
michael@0 2564 // If this is a registry from a different architecture then don't attempt to read it
michael@0 2565 if (PL_strcmp(archValues[1], arch.get())) {
michael@0 2566 return rv;
michael@0 2567 }
michael@0 2568 }
michael@0 2569
michael@0 2570 // Registry v0.13 and upwards includes the list of invalid plugins
michael@0 2571 const bool hasInvalidPlugins = (version >= "0.13");
michael@0 2572
michael@0 2573 // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
michael@0 2574 const bool hasValidFlags = (version < "0.16");
michael@0 2575
michael@0 2576 // Registry v0.17 and upwards store whether the plugin comes from an XPI.
michael@0 2577 const bool hasFromExtension = (version >= "0.17");
michael@0 2578
michael@0 2579 #if defined(XP_MACOSX)
michael@0 2580 const bool hasFullPathInFileNameField = false;
michael@0 2581 #else
michael@0 2582 const bool hasFullPathInFileNameField = (version < "0.11");
michael@0 2583 #endif
michael@0 2584
michael@0 2585 if (!ReadSectionHeader(reader, "PLUGINS"))
michael@0 2586 return rv;
michael@0 2587
michael@0 2588 while (reader.NextLine()) {
michael@0 2589 const char *filename;
michael@0 2590 const char *fullpath;
michael@0 2591 nsAutoCString derivedFileName;
michael@0 2592
michael@0 2593 if (hasInvalidPlugins && *reader.LinePtr() == '[') {
michael@0 2594 break;
michael@0 2595 }
michael@0 2596
michael@0 2597 if (hasFullPathInFileNameField) {
michael@0 2598 fullpath = reader.LinePtr();
michael@0 2599 if (!reader.NextLine())
michael@0 2600 return rv;
michael@0 2601 // try to derive a file name from the full path
michael@0 2602 if (fullpath) {
michael@0 2603 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
michael@0 2604 file->InitWithNativePath(nsDependentCString(fullpath));
michael@0 2605 file->GetNativeLeafName(derivedFileName);
michael@0 2606 filename = derivedFileName.get();
michael@0 2607 } else {
michael@0 2608 filename = nullptr;
michael@0 2609 }
michael@0 2610
michael@0 2611 // skip the next line, useless in this version
michael@0 2612 if (!reader.NextLine())
michael@0 2613 return rv;
michael@0 2614 } else {
michael@0 2615 filename = reader.LinePtr();
michael@0 2616 if (!reader.NextLine())
michael@0 2617 return rv;
michael@0 2618
michael@0 2619 fullpath = reader.LinePtr();
michael@0 2620 if (!reader.NextLine())
michael@0 2621 return rv;
michael@0 2622 }
michael@0 2623
michael@0 2624 const char *version;
michael@0 2625 if (regHasVersion) {
michael@0 2626 version = reader.LinePtr();
michael@0 2627 if (!reader.NextLine())
michael@0 2628 return rv;
michael@0 2629 } else {
michael@0 2630 version = "0";
michael@0 2631 }
michael@0 2632
michael@0 2633 // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
michael@0 2634 const int count = hasFromExtension ? 4 : 3;
michael@0 2635 if (reader.ParseLine(values, count) != count)
michael@0 2636 return rv;
michael@0 2637
michael@0 2638 // If this is an old plugin registry mark this plugin tag to be refreshed
michael@0 2639 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
michael@0 2640 uint32_t tagflag = atoi(values[2]);
michael@0 2641 bool fromExtension = false;
michael@0 2642 if (hasFromExtension) {
michael@0 2643 fromExtension = atoi(values[3]);
michael@0 2644 }
michael@0 2645 if (!reader.NextLine())
michael@0 2646 return rv;
michael@0 2647
michael@0 2648 char *description = reader.LinePtr();
michael@0 2649 if (!reader.NextLine())
michael@0 2650 return rv;
michael@0 2651
michael@0 2652 #if MOZ_WIDGET_ANDROID
michael@0 2653 // Flash on Android does not populate the version field, but it is tacked on to the description.
michael@0 2654 // For example, "Shockwave Flash 11.1 r115"
michael@0 2655 if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) {
michael@0 2656 version = &description[16];
michael@0 2657 }
michael@0 2658 #endif
michael@0 2659
michael@0 2660 const char *name = reader.LinePtr();
michael@0 2661 if (!reader.NextLine())
michael@0 2662 return rv;
michael@0 2663
michael@0 2664 long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
michael@0 2665 if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
michael@0 2666 mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
michael@0 2667 return NS_ERROR_FAILURE;
michael@0 2668 }
michael@0 2669
michael@0 2670 char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
michael@0 2671 char **mimetypes;
michael@0 2672 char **mimedescriptions;
michael@0 2673 char **extensions;
michael@0 2674 char **heapalloced = 0;
michael@0 2675 if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
michael@0 2676 heapalloced = new char *[mimetypecount * 3];
michael@0 2677 mimetypes = heapalloced;
michael@0 2678 } else {
michael@0 2679 mimetypes = stackalloced;
michael@0 2680 }
michael@0 2681 mimedescriptions = mimetypes + mimetypecount;
michael@0 2682 extensions = mimedescriptions + mimetypecount;
michael@0 2683
michael@0 2684 int mtr = 0; //mimetype read
michael@0 2685 for (; mtr < mimetypecount; mtr++) {
michael@0 2686 if (!reader.NextLine())
michael@0 2687 break;
michael@0 2688
michael@0 2689 //line number|mimetype|description|extension
michael@0 2690 if (4 != reader.ParseLine(values, 4))
michael@0 2691 break;
michael@0 2692 int line = atoi(values[0]);
michael@0 2693 if (line != mtr)
michael@0 2694 break;
michael@0 2695 mimetypes[mtr] = values[1];
michael@0 2696 mimedescriptions[mtr] = values[2];
michael@0 2697 extensions[mtr] = values[3];
michael@0 2698 }
michael@0 2699
michael@0 2700 if (mtr != mimetypecount) {
michael@0 2701 if (heapalloced) {
michael@0 2702 delete [] heapalloced;
michael@0 2703 }
michael@0 2704 return rv;
michael@0 2705 }
michael@0 2706
michael@0 2707 nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
michael@0 2708 description,
michael@0 2709 filename,
michael@0 2710 fullpath,
michael@0 2711 version,
michael@0 2712 (const char* const*)mimetypes,
michael@0 2713 (const char* const*)mimedescriptions,
michael@0 2714 (const char* const*)extensions,
michael@0 2715 mimetypecount, lastmod, fromExtension, true);
michael@0 2716 if (heapalloced)
michael@0 2717 delete [] heapalloced;
michael@0 2718
michael@0 2719 // Import flags from registry into prefs for old registry versions
michael@0 2720 if (hasValidFlags && !pluginStateImported) {
michael@0 2721 tag->ImportFlagsToPrefs(tagflag);
michael@0 2722 }
michael@0 2723
michael@0 2724 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
michael@0 2725 ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
michael@0 2726 tag->mNext = mCachedPlugins;
michael@0 2727 mCachedPlugins = tag;
michael@0 2728 }
michael@0 2729
michael@0 2730 // On Android we always want to try to load a plugin again (Flash). Bug 935676.
michael@0 2731 #ifndef MOZ_WIDGET_ANDROID
michael@0 2732 if (hasInvalidPlugins) {
michael@0 2733 if (!ReadSectionHeader(reader, "INVALID")) {
michael@0 2734 return rv;
michael@0 2735 }
michael@0 2736
michael@0 2737 while (reader.NextLine()) {
michael@0 2738 const char *fullpath = reader.LinePtr();
michael@0 2739 if (!reader.NextLine()) {
michael@0 2740 return rv;
michael@0 2741 }
michael@0 2742
michael@0 2743 const char *lastModifiedTimeStamp = reader.LinePtr();
michael@0 2744 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
michael@0 2745
michael@0 2746 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
michael@0 2747
michael@0 2748 invalidTag->mNext = mInvalidPlugins;
michael@0 2749 if (mInvalidPlugins) {
michael@0 2750 mInvalidPlugins->mPrev = invalidTag;
michael@0 2751 }
michael@0 2752 mInvalidPlugins = invalidTag;
michael@0 2753 }
michael@0 2754 }
michael@0 2755 #endif
michael@0 2756
michael@0 2757 // flip the pref so we don't import the legacy flags again
michael@0 2758 Preferences::SetBool("plugin.importedState", true);
michael@0 2759
michael@0 2760 return NS_OK;
michael@0 2761 }
michael@0 2762
michael@0 2763 void
michael@0 2764 nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
michael@0 2765 {
michael@0 2766 nsRefPtr<nsPluginTag> prev;
michael@0 2767 nsRefPtr<nsPluginTag> tag = mCachedPlugins;
michael@0 2768 while (tag)
michael@0 2769 {
michael@0 2770 if (tag->mFullPath.Equals(filePath)) {
michael@0 2771 // Found it. Remove it from our list
michael@0 2772 if (prev)
michael@0 2773 prev->mNext = tag->mNext;
michael@0 2774 else
michael@0 2775 mCachedPlugins = tag->mNext;
michael@0 2776 tag->mNext = nullptr;
michael@0 2777 *result = tag;
michael@0 2778 NS_ADDREF(*result);
michael@0 2779 break;
michael@0 2780 }
michael@0 2781 prev = tag;
michael@0 2782 tag = tag->mNext;
michael@0 2783 }
michael@0 2784 }
michael@0 2785
michael@0 2786 #ifdef XP_WIN
michael@0 2787 nsresult
michael@0 2788 nsPluginHost::EnsurePrivateDirServiceProvider()
michael@0 2789 {
michael@0 2790 if (!mPrivateDirServiceProvider) {
michael@0 2791 nsresult rv;
michael@0 2792 mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
michael@0 2793 if (!mPrivateDirServiceProvider)
michael@0 2794 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2795 nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
michael@0 2796 if (NS_FAILED(rv))
michael@0 2797 return rv;
michael@0 2798 rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
michael@0 2799 if (NS_FAILED(rv))
michael@0 2800 return rv;
michael@0 2801 }
michael@0 2802 return NS_OK;
michael@0 2803 }
michael@0 2804 #endif /* XP_WIN */
michael@0 2805
michael@0 2806 nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
michael@0 2807 nsNPAPIPluginInstance *aInstance,
michael@0 2808 nsNPAPIPluginStreamListener* aListener,
michael@0 2809 nsIInputStream *aPostStream,
michael@0 2810 const char *aHeadersData,
michael@0 2811 uint32_t aHeadersDataLen)
michael@0 2812 {
michael@0 2813 nsCOMPtr<nsIURI> url;
michael@0 2814 nsAutoString absUrl;
michael@0 2815 nsresult rv;
michael@0 2816
michael@0 2817 if (aURL.Length() <= 0)
michael@0 2818 return NS_OK;
michael@0 2819
michael@0 2820 // get the base URI for the plugin to create an absolute url
michael@0 2821 // in case aURL is relative
michael@0 2822 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
michael@0 2823 if (owner) {
michael@0 2824 rv = NS_MakeAbsoluteURI(absUrl, aURL, nsCOMPtr<nsIURI>(owner->GetBaseURI()));
michael@0 2825 }
michael@0 2826
michael@0 2827 if (absUrl.IsEmpty())
michael@0 2828 absUrl.Assign(aURL);
michael@0 2829
michael@0 2830 rv = NS_NewURI(getter_AddRefs(url), absUrl);
michael@0 2831 if (NS_FAILED(rv))
michael@0 2832 return rv;
michael@0 2833
michael@0 2834 nsCOMPtr<nsIDOMElement> element;
michael@0 2835 nsCOMPtr<nsIDocument> doc;
michael@0 2836 if (owner) {
michael@0 2837 owner->GetDOMElement(getter_AddRefs(element));
michael@0 2838 owner->GetDocument(getter_AddRefs(doc));
michael@0 2839 }
michael@0 2840
michael@0 2841 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
michael@0 2842 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
michael@0 2843 url,
michael@0 2844 (doc ? doc->NodePrincipal() : nullptr),
michael@0 2845 element,
michael@0 2846 EmptyCString(), //mime guess
michael@0 2847 nullptr, //extra
michael@0 2848 &shouldLoad);
michael@0 2849 if (NS_FAILED(rv))
michael@0 2850 return rv;
michael@0 2851 if (NS_CP_REJECTED(shouldLoad)) {
michael@0 2852 // Disallowed by content policy
michael@0 2853 return NS_ERROR_CONTENT_BLOCKED;
michael@0 2854 }
michael@0 2855
michael@0 2856 nsRefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
michael@0 2857 if (!listenerPeer)
michael@0 2858 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2859
michael@0 2860 rv = listenerPeer->Initialize(url, aInstance, aListener);
michael@0 2861 if (NS_FAILED(rv))
michael@0 2862 return rv;
michael@0 2863
michael@0 2864 nsCOMPtr<nsIChannel> channel;
michael@0 2865 rv = NS_NewChannel(getter_AddRefs(channel), url, nullptr,
michael@0 2866 nullptr, /* do not add this internal plugin's channel
michael@0 2867 on the load group otherwise this channel could be canceled
michael@0 2868 form |nsDocShell::OnLinkClickSync| bug 166613 */
michael@0 2869 listenerPeer);
michael@0 2870 if (NS_FAILED(rv))
michael@0 2871 return rv;
michael@0 2872
michael@0 2873 if (doc) {
michael@0 2874 // Set the owner of channel to the document principal...
michael@0 2875 channel->SetOwner(doc->NodePrincipal());
michael@0 2876
michael@0 2877 // And if it's a script allow it to execute against the
michael@0 2878 // document's script context.
michael@0 2879 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
michael@0 2880 if (scriptChannel) {
michael@0 2881 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
michael@0 2882 // Plug-ins seem to depend on javascript: URIs running synchronously
michael@0 2883 scriptChannel->SetExecuteAsync(false);
michael@0 2884 }
michael@0 2885 }
michael@0 2886
michael@0 2887 // deal with headers and post data
michael@0 2888 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
michael@0 2889 if (httpChannel) {
michael@0 2890 if (!aPostStream) {
michael@0 2891 // Only set the Referer header for GET requests because IIS throws
michael@0 2892 // errors about malformed requests if we include it in POSTs. See
michael@0 2893 // bug 724465.
michael@0 2894 nsCOMPtr<nsIURI> referer;
michael@0 2895
michael@0 2896 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
michael@0 2897 if (olc)
michael@0 2898 olc->GetSrcURI(getter_AddRefs(referer));
michael@0 2899
michael@0 2900
michael@0 2901 if (!referer) {
michael@0 2902 if (!doc) {
michael@0 2903 return NS_ERROR_FAILURE;
michael@0 2904 }
michael@0 2905 referer = doc->GetDocumentURI();
michael@0 2906 }
michael@0 2907
michael@0 2908 rv = httpChannel->SetReferrer(referer);
michael@0 2909 NS_ENSURE_SUCCESS(rv,rv);
michael@0 2910 }
michael@0 2911
michael@0 2912 if (aPostStream) {
michael@0 2913 // XXX it's a bit of a hack to rewind the postdata stream
michael@0 2914 // here but it has to be done in case the post data is
michael@0 2915 // being reused multiple times.
michael@0 2916 nsCOMPtr<nsISeekableStream>
michael@0 2917 postDataSeekable(do_QueryInterface(aPostStream));
michael@0 2918 if (postDataSeekable)
michael@0 2919 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 2920
michael@0 2921 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
michael@0 2922 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
michael@0 2923
michael@0 2924 uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
michael@0 2925 }
michael@0 2926
michael@0 2927 if (aHeadersData) {
michael@0 2928 rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
michael@0 2929 NS_ENSURE_SUCCESS(rv,rv);
michael@0 2930 }
michael@0 2931 }
michael@0 2932 rv = channel->AsyncOpen(listenerPeer, nullptr);
michael@0 2933 if (NS_SUCCEEDED(rv))
michael@0 2934 listenerPeer->TrackRequest(channel);
michael@0 2935 return rv;
michael@0 2936 }
michael@0 2937
michael@0 2938 // Called by GetURL and PostURL
michael@0 2939 nsresult
michael@0 2940 nsPluginHost::DoURLLoadSecurityCheck(nsNPAPIPluginInstance *aInstance,
michael@0 2941 const char* aURL)
michael@0 2942 {
michael@0 2943 if (!aURL || *aURL == '\0')
michael@0 2944 return NS_OK;
michael@0 2945
michael@0 2946 // get the base URI for the plugin element
michael@0 2947 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
michael@0 2948 if (!owner)
michael@0 2949 return NS_ERROR_FAILURE;
michael@0 2950
michael@0 2951 nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
michael@0 2952 if (!baseURI)
michael@0 2953 return NS_ERROR_FAILURE;
michael@0 2954
michael@0 2955 // Create an absolute URL for the target in case the target is relative
michael@0 2956 nsCOMPtr<nsIURI> targetURL;
michael@0 2957 NS_NewURI(getter_AddRefs(targetURL), aURL, baseURI);
michael@0 2958 if (!targetURL)
michael@0 2959 return NS_ERROR_FAILURE;
michael@0 2960
michael@0 2961 nsCOMPtr<nsIDocument> doc;
michael@0 2962 owner->GetDocument(getter_AddRefs(doc));
michael@0 2963 if (!doc)
michael@0 2964 return NS_ERROR_FAILURE;
michael@0 2965
michael@0 2966 nsresult rv;
michael@0 2967 nsCOMPtr<nsIScriptSecurityManager> secMan(
michael@0 2968 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
michael@0 2969 if (NS_FAILED(rv))
michael@0 2970 return rv;
michael@0 2971
michael@0 2972 return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL,
michael@0 2973 nsIScriptSecurityManager::STANDARD);
michael@0 2974
michael@0 2975 }
michael@0 2976
michael@0 2977 nsresult
michael@0 2978 nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
michael@0 2979 uint32_t aHeadersDataLen,
michael@0 2980 nsIChannel *aGenericChannel)
michael@0 2981 {
michael@0 2982 nsresult rv = NS_OK;
michael@0 2983
michael@0 2984 nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
michael@0 2985 if (!aChannel) {
michael@0 2986 return NS_ERROR_NULL_POINTER;
michael@0 2987 }
michael@0 2988
michael@0 2989 // used during the manipulation of the String from the aHeadersData
michael@0 2990 nsAutoCString headersString;
michael@0 2991 nsAutoCString oneHeader;
michael@0 2992 nsAutoCString headerName;
michael@0 2993 nsAutoCString headerValue;
michael@0 2994 int32_t crlf = 0;
michael@0 2995 int32_t colon = 0;
michael@0 2996
michael@0 2997 // Turn the char * buffer into an nsString.
michael@0 2998 headersString = aHeadersData;
michael@0 2999
michael@0 3000 // Iterate over the nsString: for each "\r\n" delimited chunk,
michael@0 3001 // add the value as a header to the nsIHTTPChannel
michael@0 3002 while (true) {
michael@0 3003 crlf = headersString.Find("\r\n", true);
michael@0 3004 if (-1 == crlf) {
michael@0 3005 rv = NS_OK;
michael@0 3006 return rv;
michael@0 3007 }
michael@0 3008 headersString.Mid(oneHeader, 0, crlf);
michael@0 3009 headersString.Cut(0, crlf + 2);
michael@0 3010 oneHeader.StripWhitespace();
michael@0 3011 colon = oneHeader.Find(":");
michael@0 3012 if (-1 == colon) {
michael@0 3013 rv = NS_ERROR_NULL_POINTER;
michael@0 3014 return rv;
michael@0 3015 }
michael@0 3016 oneHeader.Left(headerName, colon);
michael@0 3017 colon++;
michael@0 3018 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
michael@0 3019
michael@0 3020 // FINALLY: we can set the header!
michael@0 3021
michael@0 3022 rv = aChannel->SetRequestHeader(headerName, headerValue, true);
michael@0 3023 if (NS_FAILED(rv)) {
michael@0 3024 rv = NS_ERROR_NULL_POINTER;
michael@0 3025 return rv;
michael@0 3026 }
michael@0 3027 }
michael@0 3028 return rv;
michael@0 3029 }
michael@0 3030
michael@0 3031 nsresult
michael@0 3032 nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
michael@0 3033 {
michael@0 3034 if (PluginDestructionGuard::DelayDestroy(aInstance)) {
michael@0 3035 return NS_OK;
michael@0 3036 }
michael@0 3037
michael@0 3038 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 3039 ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
michael@0 3040
michael@0 3041 if (aInstance->HasStartedDestroying()) {
michael@0 3042 return NS_OK;
michael@0 3043 }
michael@0 3044
michael@0 3045 Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
michael@0 3046 aInstance->Stop();
michael@0 3047
michael@0 3048 // if the instance does not want to be 'cached' just remove it
michael@0 3049 bool doCache = aInstance->ShouldCache();
michael@0 3050 if (doCache) {
michael@0 3051 // try to get the max cached instances from a pref or use default
michael@0 3052 uint32_t cachedInstanceLimit =
michael@0 3053 Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
michael@0 3054 DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
michael@0 3055 if (StoppedInstanceCount() >= cachedInstanceLimit) {
michael@0 3056 nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
michael@0 3057 if (oldestInstance) {
michael@0 3058 nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
michael@0 3059 oldestInstance->Destroy();
michael@0 3060 mInstances.RemoveElement(oldestInstance);
michael@0 3061 // TODO: Remove this check once bug 752422 was investigated
michael@0 3062 if (pluginTag) {
michael@0 3063 OnPluginInstanceDestroyed(pluginTag);
michael@0 3064 }
michael@0 3065 }
michael@0 3066 }
michael@0 3067 } else {
michael@0 3068 nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
michael@0 3069 aInstance->Destroy();
michael@0 3070 mInstances.RemoveElement(aInstance);
michael@0 3071 // TODO: Remove this check once bug 752422 was investigated
michael@0 3072 if (pluginTag) {
michael@0 3073 OnPluginInstanceDestroyed(pluginTag);
michael@0 3074 }
michael@0 3075 }
michael@0 3076
michael@0 3077 return NS_OK;
michael@0 3078 }
michael@0 3079
michael@0 3080 nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
michael@0 3081 nsNPAPIPluginInstance* aInstance,
michael@0 3082 nsIStreamListener **aStreamListener)
michael@0 3083 {
michael@0 3084 NS_ENSURE_ARG_POINTER(aURI);
michael@0 3085 NS_ENSURE_ARG_POINTER(aStreamListener);
michael@0 3086
michael@0 3087 nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
michael@0 3088 nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
michael@0 3089 if (NS_FAILED(rv)) {
michael@0 3090 return rv;
michael@0 3091 }
michael@0 3092
michael@0 3093 listener.forget(aStreamListener);
michael@0 3094
michael@0 3095 return NS_OK;
michael@0 3096 }
michael@0 3097
michael@0 3098 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
michael@0 3099 const char *aTopic,
michael@0 3100 const char16_t *someData)
michael@0 3101 {
michael@0 3102 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
michael@0 3103 OnShutdown();
michael@0 3104 UnloadPlugins();
michael@0 3105 sInst->Release();
michael@0 3106 }
michael@0 3107 if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
michael@0 3108 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
michael@0 3109 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
michael@0 3110 // Unload or load plugins as needed
michael@0 3111 if (mPluginsDisabled) {
michael@0 3112 UnloadPlugins();
michael@0 3113 } else {
michael@0 3114 LoadPlugins();
michael@0 3115 }
michael@0 3116 }
michael@0 3117 if (!strcmp("blocklist-updated", aTopic)) {
michael@0 3118 nsPluginTag* plugin = mPlugins;
michael@0 3119 while (plugin) {
michael@0 3120 plugin->InvalidateBlocklistState();
michael@0 3121 plugin = plugin->mNext;
michael@0 3122 }
michael@0 3123 }
michael@0 3124 #ifdef MOZ_WIDGET_ANDROID
michael@0 3125 if (!strcmp("application-background", aTopic)) {
michael@0 3126 for(uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3127 mInstances[i]->NotifyForeground(false);
michael@0 3128 }
michael@0 3129 }
michael@0 3130 if (!strcmp("application-foreground", aTopic)) {
michael@0 3131 for(uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3132 if (mInstances[i]->IsOnScreen())
michael@0 3133 mInstances[i]->NotifyForeground(true);
michael@0 3134 }
michael@0 3135 }
michael@0 3136 if (!strcmp("memory-pressure", aTopic)) {
michael@0 3137 for(uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3138 mInstances[i]->MemoryPressure();
michael@0 3139 }
michael@0 3140 }
michael@0 3141 #endif
michael@0 3142 return NS_OK;
michael@0 3143 }
michael@0 3144
michael@0 3145 nsresult
michael@0 3146 nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
michael@0 3147 char **outPostData, uint32_t *outPostDataLen)
michael@0 3148 {
michael@0 3149 if (!inPostData || !outPostData || !outPostDataLen)
michael@0 3150 return NS_ERROR_NULL_POINTER;
michael@0 3151
michael@0 3152 *outPostData = 0;
michael@0 3153 *outPostDataLen = 0;
michael@0 3154
michael@0 3155 const char CR = '\r';
michael@0 3156 const char LF = '\n';
michael@0 3157 const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
michael@0 3158 const char ContentLenHeader[] = "Content-length";
michael@0 3159
michael@0 3160 nsAutoTArray<const char*, 8> singleLF;
michael@0 3161 const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
michael@0 3162 const char *pSod = 0; // pointer to start of data in inPostData
michael@0 3163 const char *pEoh = 0; // pointer to end of headers in inPostData
michael@0 3164 const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
michael@0 3165 if (*inPostData == LF) {
michael@0 3166 // If no custom headers are required, simply add a blank
michael@0 3167 // line ('\n') to the beginning of the file or buffer.
michael@0 3168 // so *inPostData == '\n' is valid
michael@0 3169 pSod = inPostData + 1;
michael@0 3170 } else {
michael@0 3171 const char *s = inPostData; //tmp pointer to sourse inPostData
michael@0 3172 while (s < pEod) {
michael@0 3173 if (!pSCntlh &&
michael@0 3174 (*s == 'C' || *s == 'c') &&
michael@0 3175 (s + sizeof(ContentLenHeader) - 1 < pEod) &&
michael@0 3176 (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
michael@0 3177 {
michael@0 3178 // lets assume this is ContentLenHeader for now
michael@0 3179 const char *p = pSCntlh = s;
michael@0 3180 p += sizeof(ContentLenHeader) - 1;
michael@0 3181 // search for first CR or LF == end of ContentLenHeader
michael@0 3182 for (; p < pEod; p++) {
michael@0 3183 if (*p == CR || *p == LF) {
michael@0 3184 // got delimiter,
michael@0 3185 // one more check; if previous char is a digit
michael@0 3186 // most likely pSCntlh points to the start of ContentLenHeader
michael@0 3187 if (*(p-1) >= '0' && *(p-1) <= '9') {
michael@0 3188 s = p;
michael@0 3189 }
michael@0 3190 break; //for loop
michael@0 3191 }
michael@0 3192 }
michael@0 3193 if (pSCntlh == s) { // curret ptr is the same
michael@0 3194 pSCntlh = 0; // that was not ContentLenHeader
michael@0 3195 break; // there is nothing to parse, break *WHILE LOOP* here
michael@0 3196 }
michael@0 3197 }
michael@0 3198
michael@0 3199 if (*s == CR) {
michael@0 3200 if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
michael@0 3201 ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
michael@0 3202 !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
michael@0 3203 {
michael@0 3204 s += sizeof(CRLFCRLF)-1;
michael@0 3205 pEoh = pSod = s; // data stars here
michael@0 3206 break;
michael@0 3207 }
michael@0 3208 } else if (*s == LF) {
michael@0 3209 if (*(s-1) != CR) {
michael@0 3210 singleLF.AppendElement(s);
michael@0 3211 }
michael@0 3212 if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
michael@0 3213 s++;
michael@0 3214 singleLF.AppendElement(s);
michael@0 3215 s++;
michael@0 3216 pEoh = pSod = s; // data stars here
michael@0 3217 break;
michael@0 3218 }
michael@0 3219 }
michael@0 3220 s++;
michael@0 3221 }
michael@0 3222 }
michael@0 3223
michael@0 3224 // deal with output buffer
michael@0 3225 if (!pSod) { // lets assume whole buffer is a data
michael@0 3226 pSod = inPostData;
michael@0 3227 }
michael@0 3228
michael@0 3229 uint32_t newBufferLen = 0;
michael@0 3230 uint32_t dataLen = pEod - pSod;
michael@0 3231 uint32_t headersLen = pEoh ? pSod - inPostData : 0;
michael@0 3232
michael@0 3233 char *p; // tmp ptr into new output buf
michael@0 3234 if (headersLen) { // we got a headers
michael@0 3235 // this function does not make any assumption on correctness
michael@0 3236 // of ContentLenHeader value in this case.
michael@0 3237
michael@0 3238 newBufferLen = dataLen + headersLen;
michael@0 3239 // in case there were single LFs in headers
michael@0 3240 // reserve an extra space for CR will be added before each single LF
michael@0 3241 int cntSingleLF = singleLF.Length();
michael@0 3242 newBufferLen += cntSingleLF;
michael@0 3243
michael@0 3244 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
michael@0 3245 return NS_ERROR_OUT_OF_MEMORY;
michael@0 3246
michael@0 3247 // deal with single LF
michael@0 3248 const char *s = inPostData;
michael@0 3249 if (cntSingleLF) {
michael@0 3250 for (int i=0; i<cntSingleLF; i++) {
michael@0 3251 const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
michael@0 3252 int n = plf - s; // bytes to copy
michael@0 3253 if (n) { // for '\n\n' there is nothing to memcpy
michael@0 3254 memcpy(p, s, n);
michael@0 3255 p += n;
michael@0 3256 }
michael@0 3257 *p++ = CR;
michael@0 3258 s = plf;
michael@0 3259 *p++ = *s++;
michael@0 3260 }
michael@0 3261 }
michael@0 3262 // are we done with headers?
michael@0 3263 headersLen = pEoh - s;
michael@0 3264 if (headersLen) { // not yet
michael@0 3265 memcpy(p, s, headersLen); // copy the rest
michael@0 3266 p += headersLen;
michael@0 3267 }
michael@0 3268 } else if (dataLen) { // no ContentLenHeader is found but there is a data
michael@0 3269 // make new output buffer big enough
michael@0 3270 // to keep ContentLenHeader+value followed by data
michael@0 3271 uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
michael@0 3272 newBufferLen = dataLen + l;
michael@0 3273 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
michael@0 3274 return NS_ERROR_OUT_OF_MEMORY;
michael@0 3275 headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
michael@0 3276 if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
michael@0 3277 nsMemory::Free(p);
michael@0 3278 *outPostData = 0;
michael@0 3279 return NS_ERROR_FAILURE;
michael@0 3280 }
michael@0 3281 p += headersLen;
michael@0 3282 newBufferLen = headersLen + dataLen;
michael@0 3283 }
michael@0 3284 // at this point we've done with headers.
michael@0 3285 // there is a possibility that input buffer has only headers info in it
michael@0 3286 // which already parsed and copied into output buffer.
michael@0 3287 // copy the data
michael@0 3288 if (dataLen) {
michael@0 3289 memcpy(p, pSod, dataLen);
michael@0 3290 }
michael@0 3291
michael@0 3292 *outPostDataLen = newBufferLen;
michael@0 3293
michael@0 3294 return NS_OK;
michael@0 3295 }
michael@0 3296
michael@0 3297 nsresult
michael@0 3298 nsPluginHost::CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile)
michael@0 3299 {
michael@0 3300 nsresult rv;
michael@0 3301 int64_t fileSize;
michael@0 3302 nsAutoCString filename;
michael@0 3303
michael@0 3304 // stat file == get size & convert file:///c:/ to c: if needed
michael@0 3305 nsCOMPtr<nsIFile> inFile;
michael@0 3306 rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL),
michael@0 3307 getter_AddRefs(inFile));
michael@0 3308 if (NS_FAILED(rv)) {
michael@0 3309 nsCOMPtr<nsIFile> localFile;
michael@0 3310 rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false,
michael@0 3311 getter_AddRefs(localFile));
michael@0 3312 if (NS_FAILED(rv)) return rv;
michael@0 3313 inFile = localFile;
michael@0 3314 }
michael@0 3315 rv = inFile->GetFileSize(&fileSize);
michael@0 3316 if (NS_FAILED(rv)) return rv;
michael@0 3317 rv = inFile->GetNativePath(filename);
michael@0 3318 if (NS_FAILED(rv)) return rv;
michael@0 3319
michael@0 3320 if (fileSize != 0) {
michael@0 3321 nsCOMPtr<nsIInputStream> inStream;
michael@0 3322 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
michael@0 3323 if (NS_FAILED(rv)) return rv;
michael@0 3324
michael@0 3325 // Create a temporary file to write the http Content-length:
michael@0 3326 // %ld\r\n\" header and "\r\n" == end of headers for post data to
michael@0 3327
michael@0 3328 nsCOMPtr<nsIFile> tempFile;
michael@0 3329 rv = GetPluginTempDir(getter_AddRefs(tempFile));
michael@0 3330 if (NS_FAILED(rv))
michael@0 3331 return rv;
michael@0 3332
michael@0 3333 nsAutoCString inFileName;
michael@0 3334 inFile->GetNativeLeafName(inFileName);
michael@0 3335 // XXX hack around bug 70083
michael@0 3336 inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
michael@0 3337 rv = tempFile->AppendNative(inFileName);
michael@0 3338
michael@0 3339 if (NS_FAILED(rv))
michael@0 3340 return rv;
michael@0 3341
michael@0 3342 // make it unique, and mode == 0600, not world-readable
michael@0 3343 rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
michael@0 3344 if (NS_FAILED(rv))
michael@0 3345 return rv;
michael@0 3346
michael@0 3347 nsCOMPtr<nsIOutputStream> outStream;
michael@0 3348 if (NS_SUCCEEDED(rv)) {
michael@0 3349 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
michael@0 3350 tempFile,
michael@0 3351 (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
michael@0 3352 0600); // 600 so others can't read our form data
michael@0 3353 }
michael@0 3354 NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
michael@0 3355 if (NS_FAILED(rv))
michael@0 3356 return rv;
michael@0 3357
michael@0 3358 char buf[1024];
michael@0 3359 uint32_t br, bw;
michael@0 3360 bool firstRead = true;
michael@0 3361 while (1) {
michael@0 3362 // Read() mallocs if buffer is null
michael@0 3363 rv = inStream->Read(buf, 1024, &br);
michael@0 3364 if (NS_FAILED(rv) || (int32_t)br <= 0)
michael@0 3365 break;
michael@0 3366 if (firstRead) {
michael@0 3367 //"For protocols in which the headers must be distinguished from the body,
michael@0 3368 // such as HTTP, the buffer or file should contain the headers, followed by
michael@0 3369 // a blank line, then the body. If no custom headers are required, simply
michael@0 3370 // add a blank line ('\n') to the beginning of the file or buffer.
michael@0 3371
michael@0 3372 char *parsedBuf;
michael@0 3373 // assuming first 1K (or what we got) has all headers in,
michael@0 3374 // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders()
michael@0 3375 ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
michael@0 3376 rv = outStream->Write(parsedBuf, bw, &br);
michael@0 3377 nsMemory::Free(parsedBuf);
michael@0 3378 if (NS_FAILED(rv) || (bw != br))
michael@0 3379 break;
michael@0 3380
michael@0 3381 firstRead = false;
michael@0 3382 continue;
michael@0 3383 }
michael@0 3384 bw = br;
michael@0 3385 rv = outStream->Write(buf, bw, &br);
michael@0 3386 if (NS_FAILED(rv) || (bw != br))
michael@0 3387 break;
michael@0 3388 }
michael@0 3389
michael@0 3390 inStream->Close();
michael@0 3391 outStream->Close();
michael@0 3392 if (NS_SUCCEEDED(rv))
michael@0 3393 tempFile.forget(aTmpFile);
michael@0 3394 }
michael@0 3395 return rv;
michael@0 3396 }
michael@0 3397
michael@0 3398 nsresult
michael@0 3399 nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
michael@0 3400 {
michael@0 3401 return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
michael@0 3402 }
michael@0 3403
michael@0 3404 nsresult
michael@0 3405 nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
michael@0 3406 const char** aPluginName)
michael@0 3407 {
michael@0 3408 nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
michael@0 3409 if (!instance)
michael@0 3410 return NS_ERROR_FAILURE;
michael@0 3411
michael@0 3412 nsNPAPIPlugin* plugin = instance->GetPlugin();
michael@0 3413 if (!plugin)
michael@0 3414 return NS_ERROR_FAILURE;
michael@0 3415
michael@0 3416 *aPluginName = TagForPlugin(plugin)->mName.get();
michael@0 3417
michael@0 3418 return NS_OK;
michael@0 3419 }
michael@0 3420
michael@0 3421 nsresult
michael@0 3422 nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
michael@0 3423 nsIPluginTag **aPluginTag)
michael@0 3424 {
michael@0 3425 NS_ENSURE_ARG_POINTER(aPluginInstance);
michael@0 3426 NS_ENSURE_ARG_POINTER(aPluginTag);
michael@0 3427
michael@0 3428 nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
michael@0 3429 if (!plugin)
michael@0 3430 return NS_ERROR_FAILURE;
michael@0 3431
michael@0 3432 *aPluginTag = TagForPlugin(plugin);
michael@0 3433
michael@0 3434 NS_ADDREF(*aPluginTag);
michael@0 3435 return NS_OK;
michael@0 3436 }
michael@0 3437
michael@0 3438 NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
michael@0 3439 {
michael@0 3440 nsRefPtr<nsPluginTag> pluginTag = mPlugins;
michael@0 3441 while (pluginTag) {
michael@0 3442 if (pluginTag->mUnloadTimer == timer) {
michael@0 3443 if (!IsRunningPlugin(pluginTag)) {
michael@0 3444 pluginTag->TryUnloadPlugin(false);
michael@0 3445 }
michael@0 3446 return NS_OK;
michael@0 3447 }
michael@0 3448 pluginTag = pluginTag->mNext;
michael@0 3449 }
michael@0 3450
michael@0 3451 return NS_ERROR_FAILURE;
michael@0 3452 }
michael@0 3453
michael@0 3454 #ifdef XP_WIN
michael@0 3455 // Re-enable any top level browser windows that were disabled by modal dialogs
michael@0 3456 // displayed by the crashed plugin.
michael@0 3457 static void
michael@0 3458 CheckForDisabledWindows()
michael@0 3459 {
michael@0 3460 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
michael@0 3461 if (!wm)
michael@0 3462 return;
michael@0 3463
michael@0 3464 nsCOMPtr<nsISimpleEnumerator> windowList;
michael@0 3465 wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
michael@0 3466 if (!windowList)
michael@0 3467 return;
michael@0 3468
michael@0 3469 bool haveWindows;
michael@0 3470 do {
michael@0 3471 windowList->HasMoreElements(&haveWindows);
michael@0 3472 if (!haveWindows)
michael@0 3473 return;
michael@0 3474
michael@0 3475 nsCOMPtr<nsISupports> supportsWindow;
michael@0 3476 windowList->GetNext(getter_AddRefs(supportsWindow));
michael@0 3477 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
michael@0 3478 if (baseWin) {
michael@0 3479 nsCOMPtr<nsIWidget> widget;
michael@0 3480 baseWin->GetMainWidget(getter_AddRefs(widget));
michael@0 3481 if (widget && !widget->GetParent() &&
michael@0 3482 widget->IsVisible() &&
michael@0 3483 !widget->IsEnabled()) {
michael@0 3484 nsIWidget* child = widget->GetFirstChild();
michael@0 3485 bool enable = true;
michael@0 3486 while (child) {
michael@0 3487 if (child->WindowType() == eWindowType_dialog) {
michael@0 3488 enable = false;
michael@0 3489 break;
michael@0 3490 }
michael@0 3491 child = child->GetNextSibling();
michael@0 3492 }
michael@0 3493 if (enable) {
michael@0 3494 widget->Enable(true);
michael@0 3495 }
michael@0 3496 }
michael@0 3497 }
michael@0 3498 } while (haveWindows);
michael@0 3499 }
michael@0 3500 #endif
michael@0 3501
michael@0 3502 void
michael@0 3503 nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
michael@0 3504 const nsAString& pluginDumpID,
michael@0 3505 const nsAString& browserDumpID)
michael@0 3506 {
michael@0 3507 nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
michael@0 3508
michael@0 3509 // Notify the app's observer that a plugin crashed so it can submit
michael@0 3510 // a crashreport.
michael@0 3511 bool submittedCrashReport = false;
michael@0 3512 nsCOMPtr<nsIObserverService> obsService =
michael@0 3513 mozilla::services::GetObserverService();
michael@0 3514 nsCOMPtr<nsIWritablePropertyBag2> propbag =
michael@0 3515 do_CreateInstance("@mozilla.org/hash-property-bag;1");
michael@0 3516 if (obsService && propbag) {
michael@0 3517 propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
michael@0 3518 pluginDumpID);
michael@0 3519 propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
michael@0 3520 browserDumpID);
michael@0 3521 propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
michael@0 3522 submittedCrashReport);
michael@0 3523 obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
michael@0 3524 // see if an observer submitted a crash report.
michael@0 3525 propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
michael@0 3526 &submittedCrashReport);
michael@0 3527 }
michael@0 3528
michael@0 3529 // Invalidate each nsPluginInstanceTag for the crashed plugin
michael@0 3530
michael@0 3531 for (uint32_t i = mInstances.Length(); i > 0; i--) {
michael@0 3532 nsNPAPIPluginInstance* instance = mInstances[i - 1];
michael@0 3533 if (instance->GetPlugin() == aPlugin) {
michael@0 3534 // notify the content node (nsIObjectLoadingContent) that the
michael@0 3535 // plugin has crashed
michael@0 3536 nsCOMPtr<nsIDOMElement> domElement;
michael@0 3537 instance->GetDOMElement(getter_AddRefs(domElement));
michael@0 3538 nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
michael@0 3539 if (objectContent) {
michael@0 3540 objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
michael@0 3541 submittedCrashReport);
michael@0 3542 }
michael@0 3543
michael@0 3544 instance->Destroy();
michael@0 3545 mInstances.RemoveElement(instance);
michael@0 3546 OnPluginInstanceDestroyed(crashedPluginTag);
michael@0 3547 }
michael@0 3548 }
michael@0 3549
michael@0 3550 // Only after all instances have been invalidated is it safe to null
michael@0 3551 // out nsPluginTag.mPlugin. The next time we try to create an
michael@0 3552 // instance of this plugin we reload it (launch a new plugin process).
michael@0 3553
michael@0 3554 crashedPluginTag->mPlugin = nullptr;
michael@0 3555
michael@0 3556 #ifdef XP_WIN
michael@0 3557 CheckForDisabledWindows();
michael@0 3558 #endif
michael@0 3559 }
michael@0 3560
michael@0 3561 nsNPAPIPluginInstance*
michael@0 3562 nsPluginHost::FindInstance(const char *mimetype)
michael@0 3563 {
michael@0 3564 for (uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3565 nsNPAPIPluginInstance* instance = mInstances[i];
michael@0 3566
michael@0 3567 const char* mt;
michael@0 3568 nsresult rv = instance->GetMIMEType(&mt);
michael@0 3569 if (NS_FAILED(rv))
michael@0 3570 continue;
michael@0 3571
michael@0 3572 if (PL_strcasecmp(mt, mimetype) == 0)
michael@0 3573 return instance;
michael@0 3574 }
michael@0 3575
michael@0 3576 return nullptr;
michael@0 3577 }
michael@0 3578
michael@0 3579 nsNPAPIPluginInstance*
michael@0 3580 nsPluginHost::FindOldestStoppedInstance()
michael@0 3581 {
michael@0 3582 nsNPAPIPluginInstance *oldestInstance = nullptr;
michael@0 3583 TimeStamp oldestTime = TimeStamp::Now();
michael@0 3584 for (uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3585 nsNPAPIPluginInstance *instance = mInstances[i];
michael@0 3586 if (instance->IsRunning())
michael@0 3587 continue;
michael@0 3588
michael@0 3589 TimeStamp time = instance->StopTime();
michael@0 3590 if (time < oldestTime) {
michael@0 3591 oldestTime = time;
michael@0 3592 oldestInstance = instance;
michael@0 3593 }
michael@0 3594 }
michael@0 3595
michael@0 3596 return oldestInstance;
michael@0 3597 }
michael@0 3598
michael@0 3599 uint32_t
michael@0 3600 nsPluginHost::StoppedInstanceCount()
michael@0 3601 {
michael@0 3602 uint32_t stoppedCount = 0;
michael@0 3603 for (uint32_t i = 0; i < mInstances.Length(); i++) {
michael@0 3604 nsNPAPIPluginInstance *instance = mInstances[i];
michael@0 3605 if (!instance->IsRunning())
michael@0 3606 stoppedCount++;
michael@0 3607 }
michael@0 3608 return stoppedCount;
michael@0 3609 }
michael@0 3610
michael@0 3611 nsTArray< nsRefPtr<nsNPAPIPluginInstance> >*
michael@0 3612 nsPluginHost::InstanceArray()
michael@0 3613 {
michael@0 3614 return &mInstances;
michael@0 3615 }
michael@0 3616
michael@0 3617 void
michael@0 3618 nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
michael@0 3619 {
michael@0 3620 for (int32_t i = mInstances.Length(); i > 0; i--) {
michael@0 3621 nsNPAPIPluginInstance *instance = mInstances[i - 1];
michael@0 3622 if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
michael@0 3623 instance->SetWindow(nullptr);
michael@0 3624 instance->Stop();
michael@0 3625
michael@0 3626 // Get rid of all the instances without the possibility of caching.
michael@0 3627 nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
michael@0 3628 instance->SetWindow(nullptr);
michael@0 3629
michael@0 3630 nsCOMPtr<nsIDOMElement> domElement;
michael@0 3631 instance->GetDOMElement(getter_AddRefs(domElement));
michael@0 3632 nsCOMPtr<nsIObjectLoadingContent> objectContent =
michael@0 3633 do_QueryInterface(domElement);
michael@0 3634
michael@0 3635 instance->Destroy();
michael@0 3636
michael@0 3637 mInstances.RemoveElement(instance);
michael@0 3638 OnPluginInstanceDestroyed(pluginTag);
michael@0 3639
michael@0 3640 // Notify owning content that we destroyed its plugin out from under it
michael@0 3641 if (objectContent) {
michael@0 3642 objectContent->PluginDestroyed();
michael@0 3643 }
michael@0 3644 }
michael@0 3645 }
michael@0 3646 }
michael@0 3647
michael@0 3648 // Runnable that does an async destroy of a plugin.
michael@0 3649
michael@0 3650 class nsPluginDestroyRunnable : public nsRunnable,
michael@0 3651 public PRCList
michael@0 3652 {
michael@0 3653 public:
michael@0 3654 nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
michael@0 3655 : mInstance(aInstance)
michael@0 3656 {
michael@0 3657 PR_INIT_CLIST(this);
michael@0 3658 PR_APPEND_LINK(this, &sRunnableListHead);
michael@0 3659 }
michael@0 3660
michael@0 3661 virtual ~nsPluginDestroyRunnable()
michael@0 3662 {
michael@0 3663 PR_REMOVE_LINK(this);
michael@0 3664 }
michael@0 3665
michael@0 3666 NS_IMETHOD Run()
michael@0 3667 {
michael@0 3668 nsRefPtr<nsNPAPIPluginInstance> instance;
michael@0 3669
michael@0 3670 // Null out mInstance to make sure this code in another runnable
michael@0 3671 // will do the right thing even if someone was holding on to this
michael@0 3672 // runnable longer than we expect.
michael@0 3673 instance.swap(mInstance);
michael@0 3674
michael@0 3675 if (PluginDestructionGuard::DelayDestroy(instance)) {
michael@0 3676 // It's still not safe to destroy the plugin, it's now up to the
michael@0 3677 // outermost guard on the stack to take care of the destruction.
michael@0 3678 return NS_OK;
michael@0 3679 }
michael@0 3680
michael@0 3681 nsPluginDestroyRunnable *r =
michael@0 3682 static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
michael@0 3683
michael@0 3684 while (r != &sRunnableListHead) {
michael@0 3685 if (r != this && r->mInstance == instance) {
michael@0 3686 // There's another runnable scheduled to tear down
michael@0 3687 // instance. Let it do the job.
michael@0 3688 return NS_OK;
michael@0 3689 }
michael@0 3690 r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
michael@0 3691 }
michael@0 3692
michael@0 3693 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 3694 ("Doing delayed destroy of instance %p\n", instance.get()));
michael@0 3695
michael@0 3696 nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
michael@0 3697 if (host)
michael@0 3698 host->StopPluginInstance(instance);
michael@0 3699
michael@0 3700 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
michael@0 3701 ("Done with delayed destroy of instance %p\n", instance.get()));
michael@0 3702
michael@0 3703 return NS_OK;
michael@0 3704 }
michael@0 3705
michael@0 3706 protected:
michael@0 3707 nsRefPtr<nsNPAPIPluginInstance> mInstance;
michael@0 3708
michael@0 3709 static PRCList sRunnableListHead;
michael@0 3710 };
michael@0 3711
michael@0 3712 PRCList nsPluginDestroyRunnable::sRunnableListHead =
michael@0 3713 PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
michael@0 3714
michael@0 3715 PRCList PluginDestructionGuard::sListHead =
michael@0 3716 PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
michael@0 3717
michael@0 3718 PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
michael@0 3719 : mInstance(aInstance)
michael@0 3720 {
michael@0 3721 Init();
michael@0 3722 }
michael@0 3723
michael@0 3724 PluginDestructionGuard::PluginDestructionGuard(NPP npp)
michael@0 3725 : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
michael@0 3726 {
michael@0 3727 Init();
michael@0 3728 }
michael@0 3729
michael@0 3730 PluginDestructionGuard::~PluginDestructionGuard()
michael@0 3731 {
michael@0 3732 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
michael@0 3733
michael@0 3734 PR_REMOVE_LINK(this);
michael@0 3735
michael@0 3736 if (mDelayedDestroy) {
michael@0 3737 // We've attempted to destroy the plugin instance we're holding on
michael@0 3738 // to while we were guarding it. Do the actual destroy now, off of
michael@0 3739 // a runnable.
michael@0 3740 nsRefPtr<nsPluginDestroyRunnable> evt =
michael@0 3741 new nsPluginDestroyRunnable(mInstance);
michael@0 3742
michael@0 3743 NS_DispatchToMainThread(evt);
michael@0 3744 }
michael@0 3745 }
michael@0 3746
michael@0 3747 // static
michael@0 3748 bool
michael@0 3749 PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
michael@0 3750 {
michael@0 3751 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
michael@0 3752 NS_ASSERTION(aInstance, "Uh, I need an instance!");
michael@0 3753
michael@0 3754 // Find the first guard on the stack and make it do a delayed
michael@0 3755 // destroy upon destruction.
michael@0 3756
michael@0 3757 PluginDestructionGuard *g =
michael@0 3758 static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
michael@0 3759
michael@0 3760 while (g != &sListHead) {
michael@0 3761 if (g->mInstance == aInstance) {
michael@0 3762 g->mDelayedDestroy = true;
michael@0 3763
michael@0 3764 return true;
michael@0 3765 }
michael@0 3766 g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
michael@0 3767 }
michael@0 3768
michael@0 3769 return false;
michael@0 3770 }

mercurial