Bumping manifests a=b2g-bump
[gecko.git] / dom / plugins / base / nsPluginHost.cpp
blob305f6bbc69b36a85700db8b1e5929333b3425936
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* nsPluginHost.cpp - top-level plugin management code */
8 #include "nscore.h"
9 #include "nsPluginHost.h"
11 #include <cstdlib>
12 #include <stdio.h>
13 #include "prio.h"
14 #include "prmem.h"
15 #include "nsNPAPIPlugin.h"
16 #include "nsNPAPIPluginStreamListener.h"
17 #include "nsNPAPIPluginInstance.h"
18 #include "nsPluginInstanceOwner.h"
19 #include "nsObjectLoadingContent.h"
20 #include "nsIHTTPHeaderListener.h"
21 #include "nsIHttpHeaderVisitor.h"
22 #include "nsIObserverService.h"
23 #include "nsIHttpProtocolHandler.h"
24 #include "nsIHttpChannel.h"
25 #include "nsIUploadChannel.h"
26 #include "nsIByteRangeRequest.h"
27 #include "nsIStreamListener.h"
28 #include "nsIInputStream.h"
29 #include "nsIOutputStream.h"
30 #include "nsIURL.h"
31 #include "nsTArray.h"
32 #include "nsReadableUtils.h"
33 #include "nsIProtocolProxyService2.h"
34 #include "nsIStreamConverterService.h"
35 #include "nsIFile.h"
36 #if defined(XP_MACOSX)
37 #include "nsILocalFileMac.h"
38 #endif
39 #include "nsISeekableStream.h"
40 #include "nsNetUtil.h"
41 #include "nsIProgressEventSink.h"
42 #include "nsIDocument.h"
43 #include "nsPluginLogging.h"
44 #include "nsIScriptChannel.h"
45 #include "nsIBlocklistService.h"
46 #include "nsVersionComparator.h"
47 #include "nsIObjectLoadingContent.h"
48 #include "nsIWritablePropertyBag2.h"
49 #include "nsICategoryManager.h"
50 #include "nsPluginStreamListenerPeer.h"
51 #include "mozilla/dom/ContentChild.h"
52 #include "mozilla/LoadInfo.h"
53 #include "mozilla/plugins/PluginAsyncSurrogate.h"
54 #include "mozilla/plugins/PluginBridge.h"
55 #include "mozilla/plugins/PluginTypes.h"
56 #include "mozilla/Preferences.h"
58 #include "nsEnumeratorUtils.h"
59 #include "nsXPCOM.h"
60 #include "nsXPCOMCID.h"
61 #include "nsISupportsPrimitives.h"
63 #include "nsXULAppAPI.h"
64 #include "nsIXULRuntime.h"
66 // for the dialog
67 #include "nsIWindowWatcher.h"
68 #include "nsIDOMElement.h"
69 #include "nsIDOMWindow.h"
71 #include "nsNetCID.h"
72 #include "prprf.h"
73 #include "nsThreadUtils.h"
74 #include "nsIInputStreamTee.h"
76 #include "nsDirectoryServiceDefs.h"
77 #include "nsAppDirectoryServiceDefs.h"
78 #include "nsPluginDirServiceProvider.h"
80 #include "nsUnicharUtils.h"
81 #include "nsPluginManifestLineReader.h"
83 #include "nsIWeakReferenceUtils.h"
84 #include "nsIPresShell.h"
85 #include "nsPluginNativeWindow.h"
86 #include "nsIScriptSecurityManager.h"
87 #include "nsIContentPolicy.h"
88 #include "nsContentPolicyUtils.h"
89 #include "mozilla/TimeStamp.h"
90 #include "mozilla/Telemetry.h"
91 #include "nsIImageLoadingContent.h"
92 #include "mozilla/Preferences.h"
93 #include "nsVersionComparator.h"
95 #if defined(XP_WIN)
96 #include "nsIWindowMediator.h"
97 #include "nsIBaseWindow.h"
98 #include "windows.h"
99 #include "winbase.h"
100 #endif
102 #ifdef ANDROID
103 #include <android/log.h>
104 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
105 #endif
107 #if MOZ_CRASHREPORTER
108 #include "nsExceptionHandler.h"
109 #endif
111 using namespace mozilla;
112 using mozilla::TimeStamp;
113 using mozilla::plugins::PluginTag;
114 using mozilla::plugins::PluginAsyncSurrogate;
116 // Null out a strong ref to a linked list iteratively to avoid
117 // exhausting the stack (bug 486349).
118 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
120 while (list_) { \
121 type_ temp = list_->mNext_; \
122 list_->mNext_ = nullptr; \
123 list_ = temp; \
127 // this is the name of the directory which will be created
128 // to cache temporary files.
129 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
131 static const char *kPrefWhitelist = "plugin.allowed_types";
132 static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
133 static const char *kPrefJavaMIME = "plugin.java.mime";
135 // How long we wait before unloading an idle plugin process.
136 // Defaults to 30 seconds.
137 static const char *kPrefUnloadPluginTimeoutSecs = "dom.ipc.plugins.unloadTimeoutSecs";
138 static const uint32_t kDefaultPluginUnloadingTimeout = 30;
140 // Version of cached plugin info
141 // 0.01 first implementation
142 // 0.02 added caching of CanUnload to fix bug 105935
143 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
144 // 0.04 added new mime entry point on Mac, bug 113464
145 // 0.05 added new entry point check for the default plugin, bug 132430
146 // 0.06 strip off suffixes in mime description strings, bug 53895
147 // 0.07 changed nsIRegistry to flat file support for caching plugins info
148 // 0.08 mime entry point on MachO, bug 137535
149 // 0.09 the file encoding is changed to UTF-8, bug 420285
150 // 0.10 added plugin versions on appropriate platforms, bug 427743
151 // 0.11 file name and full path fields now store expected values on all platforms, bug 488181
152 // 0.12 force refresh due to quicktime pdf claim fix, bug 611197
153 // 0.13 add architecture and list of invalid plugins, bug 616271
154 // 0.14 force refresh due to locale comparison fix, bug 611296
155 // 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
156 // 0.16 version bump to avoid importing the plugin flags in newer versions
157 // 0.17 added flag on whether plugin is loaded from an XPI
158 // The current plugin registry version (and the maximum version we know how to read)
159 static const char *kPluginRegistryVersion = "0.17";
160 // The minimum registry version we know how to read
161 static const char *kMinimumRegistryVersion = "0.9";
163 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
165 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
167 #ifdef PLUGIN_LOGGING
168 PRLogModuleInfo* nsPluginLogging::gNPNLog = nullptr;
169 PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr;
170 PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr;
171 #endif
173 // #defines for plugin cache and prefs
174 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
175 // Raise this from '10' to '50' to work around a bug in Apple's current Java
176 // plugins on OS X Lion and SnowLeopard. See bug 705931.
177 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
179 nsIFile *nsPluginHost::sPluginTempDir;
180 nsPluginHost *nsPluginHost::sInst;
182 NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
184 nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
185 : mFullPath(aFullPath),
186 mLastModifiedTime(aLastModifiedTime),
187 mSeen(false)
190 nsInvalidPluginTag::~nsInvalidPluginTag()
193 // Helper to check for a MIME in a comma-delimited preference
194 static bool
195 IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
197 nsAutoCString searchStr;
198 searchStr.Assign(',');
199 searchStr.Append(aTypeList);
200 searchStr.Append(',');
202 nsACString::const_iterator start, end;
204 searchStr.BeginReading(start);
205 searchStr.EndReading(end);
207 nsAutoCString commaSeparated;
208 commaSeparated.Assign(',');
209 commaSeparated += aMimeType;
210 commaSeparated.Append(',');
212 return FindInReadable(commaSeparated, start, end);
215 // flat file reg funcs
216 static
217 bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
219 do {
220 if (*reader.LinePtr() == '[') {
221 char* p = reader.LinePtr() + (reader.LineLength() - 1);
222 if (*p != ']')
223 break;
224 *p = 0;
226 char* values[1];
227 if (1 != reader.ParseLine(values, 1))
228 break;
229 // ignore the leading '['
230 if (PL_strcmp(values[0]+1, token)) {
231 break; // it's wrong token
233 return true;
235 } while (reader.NextLine());
236 return false;
239 static bool UnloadPluginsASAP()
241 return (Preferences::GetUint(kPrefUnloadPluginTimeoutSecs, kDefaultPluginUnloadingTimeout) == 0);
244 nsPluginHost::nsPluginHost()
245 // No need to initialize members to nullptr, false etc because this class
246 // has a zeroing operator new.
248 // Bump the pluginchanged epoch on startup. This insures content gets a
249 // good plugin list the first time it requests it. Normally we'd just
250 // init this to 1, but due to the unique nature of our ctor we need to do
251 // this manually.
252 if (XRE_GetProcessType() == GeckoProcessType_Default) {
253 IncrementChromeEpoch();
256 // check to see if pref is set at startup to let plugins take over in
257 // full page mode for certain image mime types that we handle internally
258 mOverrideInternalTypes =
259 Preferences::GetBool("plugin.override_internal_types", false);
261 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
262 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
264 Preferences::AddStrongObserver(this, "plugin.disable");
265 Preferences::AddStrongObserver(this, "plugins.click_to_play");
267 nsCOMPtr<nsIObserverService> obsService =
268 mozilla::services::GetObserverService();
269 if (obsService) {
270 obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
271 obsService->AddObserver(this, "blocklist-updated", false);
272 #ifdef MOZ_WIDGET_ANDROID
273 obsService->AddObserver(this, "application-foreground", false);
274 obsService->AddObserver(this, "application-background", false);
275 #endif
278 #ifdef PLUGIN_LOGGING
279 nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
280 nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
281 nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
283 PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
284 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
285 PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
287 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
288 PR_LogFlush();
289 #endif
292 nsPluginHost::~nsPluginHost()
294 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
296 UnloadPlugins();
297 sInst = nullptr;
300 NS_IMPL_ISUPPORTS(nsPluginHost,
301 nsIPluginHost,
302 nsIObserver,
303 nsITimerCallback,
304 nsISupportsWeakReference)
306 already_AddRefed<nsPluginHost>
307 nsPluginHost::GetInst()
309 if (!sInst) {
310 sInst = new nsPluginHost();
311 if (!sInst)
312 return nullptr;
313 NS_ADDREF(sInst);
316 nsRefPtr<nsPluginHost> inst = sInst;
317 return inst.forget();
320 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
322 if (!aPluginTag || !aPluginTag->mPlugin) {
323 return false;
326 if (aPluginTag->mContentProcessRunningCount) {
327 return true;
330 for (uint32_t i = 0; i < mInstances.Length(); i++) {
331 nsNPAPIPluginInstance *instance = mInstances[i].get();
332 if (instance &&
333 instance->GetPlugin() == aPluginTag->mPlugin &&
334 instance->IsRunning()) {
335 return true;
339 return false;
342 nsresult nsPluginHost::ReloadPlugins()
344 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
345 ("nsPluginHost::ReloadPlugins Begin\n"));
347 nsresult rv = NS_OK;
349 // this will create the initial plugin list out of cache
350 // if it was not created yet
351 if (!mPluginsLoaded)
352 return LoadPlugins();
354 // we are re-scanning plugins. New plugins may have been added, also some
355 // plugins may have been removed, so we should probably shut everything down
356 // but don't touch running (active and not stopped) plugins
358 // check if plugins changed, no need to do anything else
359 // if no changes to plugins have been made
360 // false instructs not to touch the plugin list, just to
361 // look for possible changes
362 bool pluginschanged = true;
363 FindPlugins(false, &pluginschanged);
365 // if no changed detected, return an appropriate error code
366 if (!pluginschanged)
367 return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
369 // shutdown plugins and kill the list if there are no running plugins
370 nsRefPtr<nsPluginTag> prev;
371 nsRefPtr<nsPluginTag> next;
373 for (nsRefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
374 next = p->mNext;
376 // only remove our plugin from the list if it's not running.
377 if (!IsRunningPlugin(p)) {
378 if (p == mPlugins)
379 mPlugins = next;
380 else
381 prev->mNext = next;
383 p->mNext = nullptr;
385 // attempt to unload plugins whenever they are removed from the list
386 p->TryUnloadPlugin(false);
388 p = next;
389 continue;
392 prev = p;
393 p = next;
396 // set flags
397 mPluginsLoaded = false;
399 // load them again
400 rv = LoadPlugins();
402 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
403 ("nsPluginHost::ReloadPlugins End\n"));
405 return rv;
408 #define NS_RETURN_UASTRING_SIZE 128
410 nsresult nsPluginHost::UserAgent(const char **retstring)
412 static char resultString[NS_RETURN_UASTRING_SIZE];
413 nsresult res;
415 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
416 if (NS_FAILED(res))
417 return res;
419 nsAutoCString uaString;
420 res = http->GetUserAgent(uaString);
422 if (NS_SUCCEEDED(res)) {
423 if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
424 PL_strcpy(resultString, uaString.get());
425 } else {
426 // Copy as much of UA string as we can (terminate at right-most space).
427 PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
428 for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
429 if (i == 0) {
430 resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
432 else if (resultString[i] == ' ') {
433 resultString[i] = '\0';
434 break;
438 *retstring = resultString;
440 else {
441 *retstring = nullptr;
444 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
446 return res;
449 nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
450 const char* url,
451 const char* target,
452 nsNPAPIPluginStreamListener* streamListener,
453 const char* altHost,
454 const char* referrer,
455 bool forceJSEnabled)
457 return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
458 url, target, streamListener, altHost, referrer,
459 forceJSEnabled, 0, nullptr);
462 nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
463 const char* url,
464 const char* target,
465 nsNPAPIPluginStreamListener* streamListener,
466 const char* altHost,
467 const char* referrer,
468 bool forceJSEnabled,
469 uint32_t getHeadersLength,
470 const char* getHeaders)
472 // we can only send a stream back to the plugin (as specified by a
473 // null target) if we also have a nsNPAPIPluginStreamListener to talk to
474 if (!target && !streamListener)
475 return NS_ERROR_ILLEGAL_VALUE;
477 nsresult rv = DoURLLoadSecurityCheck(pluginInst, url);
478 if (NS_FAILED(rv))
479 return rv;
481 if (target) {
482 nsRefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
483 if (owner) {
484 if ((0 == PL_strcmp(target, "newwindow")) ||
485 (0 == PL_strcmp(target, "_new")))
486 target = "_blank";
487 else if (0 == PL_strcmp(target, "_current"))
488 target = "_self";
490 rv = owner->GetURL(url, target, nullptr, nullptr, 0);
494 if (streamListener)
495 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
496 streamListener, nullptr,
497 getHeaders, getHeadersLength);
499 return rv;
502 nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
503 const char* url,
504 uint32_t postDataLen,
505 const char* postData,
506 bool isFile,
507 const char* target,
508 nsNPAPIPluginStreamListener* streamListener,
509 const char* altHost,
510 const char* referrer,
511 bool forceJSEnabled,
512 uint32_t postHeadersLength,
513 const char* postHeaders)
515 nsresult rv;
517 // we can only send a stream back to the plugin (as specified
518 // by a null target) if we also have a nsNPAPIPluginStreamListener
519 // to talk to also
520 if (!target && !streamListener)
521 return NS_ERROR_ILLEGAL_VALUE;
523 nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
525 rv = DoURLLoadSecurityCheck(instance, url);
526 if (NS_FAILED(rv))
527 return rv;
529 nsCOMPtr<nsIInputStream> postStream;
530 if (isFile) {
531 nsCOMPtr<nsIFile> file;
532 rv = CreateTempFileToPost(postData, getter_AddRefs(file));
533 if (NS_FAILED(rv))
534 return rv;
536 nsCOMPtr<nsIInputStream> fileStream;
537 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream),
538 file,
539 PR_RDONLY,
540 0600,
541 nsIFileInputStream::DELETE_ON_CLOSE |
542 nsIFileInputStream::CLOSE_ON_EOF);
543 if (NS_FAILED(rv))
544 return rv;
546 rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192);
547 if (NS_FAILED(rv))
548 return rv;
549 } else {
550 char *dataToPost;
551 uint32_t newDataToPostLen;
552 ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
553 if (!dataToPost)
554 return NS_ERROR_UNEXPECTED;
556 nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
557 if (!sis) {
558 NS_Free(dataToPost);
559 return rv;
562 // data allocated by ParsePostBufferToFixHeaders() is managed and
563 // freed by the string stream.
564 postDataLen = newDataToPostLen;
565 sis->AdoptData(dataToPost, postDataLen);
566 postStream = sis;
569 if (target) {
570 nsRefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
571 if (owner) {
572 if ((0 == PL_strcmp(target, "newwindow")) ||
573 (0 == PL_strcmp(target, "_new"))) {
574 target = "_blank";
575 } else if (0 == PL_strcmp(target, "_current")) {
576 target = "_self";
578 rv = owner->GetURL(url, target, postStream,
579 (void*)postHeaders, postHeadersLength);
583 // if we don't have a target, just create a stream.
584 if (streamListener)
585 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
586 streamListener,
587 postStream, postHeaders, postHeadersLength);
589 return rv;
592 /* This method queries the prefs for proxy information.
593 * It has been tested and is known to work in the following three cases
594 * when no proxy host or port is specified
595 * when only the proxy host is specified
596 * when only the proxy port is specified
597 * This method conforms to the return code specified in
598 * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
599 * with the exception that multiple values are not implemented.
602 nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
604 if (!url || !result) {
605 return NS_ERROR_INVALID_ARG;
607 nsresult res;
609 nsCOMPtr<nsIURI> uriIn;
610 nsCOMPtr<nsIProtocolProxyService> proxyService;
611 nsCOMPtr<nsIProtocolProxyService2> proxyService2;
612 nsCOMPtr<nsIIOService> ioService;
614 proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
615 if (NS_FAILED(res) || !proxyService)
616 return res;
618 proxyService2 = do_QueryInterface(proxyService, &res);
619 if (NS_FAILED(res) || !proxyService2)
620 return res;
622 ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
623 if (NS_FAILED(res) || !ioService)
624 return res;
626 // make an nsURI from the argument url
627 res = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
628 if (NS_FAILED(res))
629 return res;
631 nsCOMPtr<nsIProxyInfo> pi;
633 // Remove this with bug 778201
634 res = proxyService2->DeprecatedBlockingResolve(uriIn, 0, getter_AddRefs(pi));
635 if (NS_FAILED(res))
636 return res;
638 nsAutoCString host, type;
639 int32_t port = -1;
641 // These won't fail, and even if they do... we'll be ok.
642 if (pi) {
643 pi->GetType(type);
644 pi->GetHost(host);
645 pi->GetPort(&port);
648 if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
649 *result = PL_strdup("DIRECT");
650 } else if (type.EqualsLiteral("http")) {
651 *result = PR_smprintf("PROXY %s:%d", host.get(), port);
652 } else if (type.EqualsLiteral("socks4")) {
653 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
654 } else if (type.EqualsLiteral("socks")) {
655 // XXX - this is socks5, but there is no API for us to tell the
656 // plugin that fact. SOCKS for now, in case the proxy server
657 // speaks SOCKS4 as well. See bug 78176
658 // For a long time this was returning an http proxy type, so
659 // very little is probably broken by this
660 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
661 } else {
662 NS_ASSERTION(false, "Unknown proxy type!");
663 *result = PL_strdup("DIRECT");
666 if (nullptr == *result)
667 res = NS_ERROR_OUT_OF_MEMORY;
669 return res;
672 nsresult nsPluginHost::Init()
674 return NS_OK;
677 nsresult nsPluginHost::UnloadPlugins()
679 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
681 if (!mPluginsLoaded)
682 return NS_OK;
684 // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
685 // for those plugins who want it
686 DestroyRunningInstances(nullptr);
688 nsPluginTag *pluginTag;
689 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
690 pluginTag->TryUnloadPlugin(true);
693 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
694 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
695 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
697 // Lets remove any of the temporary files that we created.
698 if (sPluginTempDir) {
699 sPluginTempDir->Remove(true);
700 NS_RELEASE(sPluginTempDir);
703 #ifdef XP_WIN
704 if (mPrivateDirServiceProvider) {
705 nsCOMPtr<nsIDirectoryService> dirService =
706 do_GetService(kDirectoryServiceContractID);
707 if (dirService)
708 dirService->UnregisterProvider(mPrivateDirServiceProvider);
709 mPrivateDirServiceProvider = nullptr;
711 #endif /* XP_WIN */
713 mPluginsLoaded = false;
715 return NS_OK;
718 void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
720 bool hasInstance = false;
721 for (uint32_t i = 0; i < mInstances.Length(); i++) {
722 if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
723 hasInstance = true;
724 break;
728 // We have some options for unloading plugins if they have no instances.
730 // Unloading plugins immediately can be bad - some plugins retain state
731 // between instances even when there are none. This is largely limited to
732 // going from one page to another, so state is retained without an instance
733 // for only a very short period of time. In order to allow this to work
734 // we don't unload plugins immediately by default. This is supported
735 // via a hidden user pref though.
737 // Another reason not to unload immediately is that loading is expensive,
738 // and it is better to leave popular plugins loaded.
740 // Our default behavior is to try to unload a plugin after a pref-controlled
741 // delay once its last instance is destroyed. This seems like a reasonable
742 // compromise that allows us to reclaim memory while allowing short state
743 // retention and avoid perf hits for loading popular plugins.
744 if (!hasInstance) {
745 if (UnloadPluginsASAP()) {
746 aPluginTag->TryUnloadPlugin(false);
747 } else {
748 if (aPluginTag->mUnloadTimer) {
749 aPluginTag->mUnloadTimer->Cancel();
750 } else {
751 aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
753 uint32_t unloadTimeout = Preferences::GetUint(kPrefUnloadPluginTimeoutSecs,
754 kDefaultPluginUnloadingTimeout);
755 aPluginTag->mUnloadTimer->InitWithCallback(this,
756 1000 * unloadTimeout,
757 nsITimer::TYPE_ONE_SHOT);
762 nsresult
763 nsPluginHost::GetPluginTempDir(nsIFile **aDir)
765 if (!sPluginTempDir) {
766 nsCOMPtr<nsIFile> tmpDir;
767 nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
768 getter_AddRefs(tmpDir));
769 NS_ENSURE_SUCCESS(rv, rv);
771 rv = tmpDir->AppendNative(kPluginTmpDirName);
773 // make it unique, and mode == 0700, not world-readable
774 rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
775 NS_ENSURE_SUCCESS(rv, rv);
777 tmpDir.swap(sPluginTempDir);
780 return sPluginTempDir->Clone(aDir);
783 nsresult
784 nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
785 nsObjectLoadingContent *aContent,
786 nsPluginInstanceOwner** aOwner)
788 NS_ENSURE_ARG_POINTER(aOwner);
790 #ifdef PLUGIN_LOGGING
791 nsAutoCString urlSpec;
792 if (aURL)
793 aURL->GetAsciiSpec(urlSpec);
795 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
796 ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
797 aMimeType, urlSpec.get()));
799 PR_LogFlush();
800 #endif
802 if (!aMimeType) {
803 NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
804 return NS_ERROR_FAILURE;
807 nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
808 if (!instanceOwner) {
809 return NS_ERROR_OUT_OF_MEMORY;
812 nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
813 nsresult rv = instanceOwner->Init(ourContent);
814 if (NS_FAILED(rv)) {
815 return rv;
818 nsPluginTagType tagType;
819 rv = instanceOwner->GetTagType(&tagType);
820 if (NS_FAILED(rv)) {
821 instanceOwner->Destroy();
822 return rv;
825 if (tagType != nsPluginTagType_Embed &&
826 tagType != nsPluginTagType_Applet &&
827 tagType != nsPluginTagType_Object) {
828 instanceOwner->Destroy();
829 return NS_ERROR_FAILURE;
832 rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
833 if (NS_FAILED(rv)) {
834 instanceOwner->Destroy();
835 return NS_ERROR_FAILURE;
837 const bool isAsyncInit = (rv == NS_PLUGIN_INIT_PENDING);
839 nsRefPtr<nsNPAPIPluginInstance> instance;
840 rv = instanceOwner->GetInstance(getter_AddRefs(instance));
841 if (NS_FAILED(rv)) {
842 instanceOwner->Destroy();
843 return rv;
846 // Async init plugins will initiate their own widget creation.
847 if (!isAsyncInit && instance) {
848 CreateWidget(instanceOwner);
851 // At this point we consider instantiation to be successful. Do not return an error.
852 instanceOwner.forget(aOwner);
854 #ifdef PLUGIN_LOGGING
855 nsAutoCString urlSpec2;
856 if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
858 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
859 ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
860 aMimeType, rv, urlSpec2.get()));
862 PR_LogFlush();
863 #endif
865 return NS_OK;
868 nsPluginTag*
869 nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
871 nsPluginTag* pluginTag;
872 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
873 if (pluginTag->mLibrary == aLibrary) {
874 return pluginTag;
877 return nullptr;
880 nsPluginTag*
881 nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
883 nsPluginTag* pluginTag;
884 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
885 if (pluginTag->mPlugin == aPlugin) {
886 return pluginTag;
889 // a plugin should never exist without a corresponding tag
890 NS_ERROR("TagForPlugin has failed");
891 return nullptr;
894 nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
895 nsIURI *aURL,
896 nsPluginInstanceOwner *aOwner)
898 NS_ENSURE_ARG_POINTER(aOwner);
900 nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
901 if (NS_SUCCEEDED(rv)) {
902 return rv;
905 // If we failed to load a plugin instance we'll try again after
906 // reloading our plugin list. Only do that once per document to
907 // avoid redundant high resource usage on pages with multiple
908 // unkown instance types. We'll do that by caching the document.
909 nsCOMPtr<nsIDocument> document;
910 aOwner->GetDocument(getter_AddRefs(document));
912 nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
913 if (document == currentdocument) {
914 return rv;
917 mCurrentDocument = do_GetWeakReference(document);
919 // Don't try to set up an instance again if nothing changed.
920 if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
921 return rv;
924 return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
927 nsresult
928 nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
929 nsIURI *aURL,
930 nsPluginInstanceOwner *aOwner)
932 #ifdef PLUGIN_LOGGING
933 nsAutoCString urlSpec;
934 if (aURL != nullptr) aURL->GetSpec(urlSpec);
936 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
937 ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
938 aMimeType, aOwner, urlSpec.get()));
940 PR_LogFlush();
941 #endif
943 nsRefPtr<nsNPAPIPlugin> plugin;
944 GetPlugin(aMimeType, getter_AddRefs(plugin));
945 if (!plugin) {
946 return NS_ERROR_FAILURE;
949 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
951 NS_ASSERTION(pluginTag, "Must have plugin tag here!");
953 #if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
954 if (pluginTag->mIsFlashPlugin) {
955 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->mVersion);
957 #endif
959 nsRefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
961 // This will create the owning reference. The connection must be made between the
962 // instance and the instance owner before initialization. Plugins can call into
963 // the browser during initialization.
964 aOwner->SetInstance(instance.get());
966 // Add the instance to the instances list before we call NPP_New so that
967 // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
968 mInstances.AppendElement(instance.get());
970 // this should not addref the instance or owner
971 // except in some cases not Java, see bug 140931
972 // our COM pointer will free the peer
973 nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
974 if (NS_FAILED(rv)) {
975 mInstances.RemoveElement(instance.get());
976 aOwner->SetInstance(nullptr);
977 return rv;
980 // Cancel the plugin unload timer since we are creating
981 // an instance for it.
982 if (pluginTag->mUnloadTimer) {
983 pluginTag->mUnloadTimer->Cancel();
986 #ifdef PLUGIN_LOGGING
987 nsAutoCString urlSpec2;
988 if (aURL)
989 aURL->GetSpec(urlSpec2);
991 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
992 ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
993 aMimeType, rv, aOwner, urlSpec2.get()));
995 PR_LogFlush();
996 #endif
998 return rv;
1001 bool
1002 nsPluginHost::PluginExistsForType(const char* aMimeType)
1004 nsPluginTag *plugin = FindPluginForType(aMimeType, false);
1005 return nullptr != plugin;
1008 NS_IMETHODIMP
1009 nsPluginHost::GetPluginTagForType(const nsACString& aMimeType,
1010 nsIPluginTag** aResult)
1012 nsPluginTag* plugin = FindPluginForType(aMimeType.Data(), true);
1013 if (!plugin) {
1014 plugin = FindPluginForType(aMimeType.Data(), false);
1016 if (!plugin) {
1017 return NS_ERROR_NOT_AVAILABLE;
1019 NS_ADDREF(*aResult = plugin);
1020 return NS_OK;
1023 NS_IMETHODIMP
1024 nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
1026 nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
1027 if (!plugin) {
1028 plugin = FindPluginForType(aMimeType.Data(), false);
1030 if (!plugin) {
1031 return NS_ERROR_UNEXPECTED;
1034 return plugin->GetEnabledState(aResult);
1037 NS_IMETHODIMP
1038 nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
1040 nsPluginTag *plugin = FindPluginForType(aMimeType, true);
1041 if (!plugin) {
1042 plugin = FindPluginForType(aMimeType, false);
1044 if (!plugin) {
1045 return NS_ERROR_FAILURE;
1048 *aState = plugin->GetBlocklistState();
1049 return NS_OK;
1052 NS_IMETHODIMP
1053 nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
1055 aPermissionString.Truncate();
1056 uint32_t blocklistState;
1057 nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
1058 NS_ENSURE_SUCCESS(rv, rv);
1059 nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
1060 if (!tag) {
1061 tag = FindPluginForType(aMimeType.Data(), false);
1063 if (!tag) {
1064 return NS_ERROR_FAILURE;
1067 if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
1068 blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
1069 aPermissionString.AssignLiteral("plugin-vulnerable:");
1071 else {
1072 aPermissionString.AssignLiteral("plugin:");
1075 aPermissionString.Append(tag->GetNiceFileName());
1077 return NS_OK;
1080 // check comma delimitered extensions
1081 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
1083 if (!aExtensionList || !aExtension)
1084 return -1;
1086 const char *pExt = aExtensionList;
1087 const char *pComma = strchr(pExt, ',');
1088 if (!pComma)
1089 return PL_strcasecmp(pExt, aExtension);
1091 int extlen = strlen(aExtension);
1092 while (pComma) {
1093 int length = pComma - pExt;
1094 if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
1095 return 0;
1096 pComma++;
1097 pExt = pComma;
1098 pComma = strchr(pExt, ',');
1101 // the last one
1102 return PL_strcasecmp(pExt, aExtension);
1105 nsresult
1106 nsPluginHost::IsPluginEnabledForExtension(const char* aExtension,
1107 const char* &aMimeType)
1109 nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
1110 if (plugin)
1111 return NS_OK;
1113 return NS_ERROR_FAILURE;
1116 void
1117 nsPluginHost::GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray)
1119 aPluginArray.Clear();
1121 LoadPlugins();
1123 nsPluginTag* plugin = mPlugins;
1124 while (plugin != nullptr) {
1125 if (plugin->IsEnabled()) {
1126 aPluginArray.AppendElement(plugin);
1128 plugin = plugin->mNext;
1132 NS_IMETHODIMP
1133 nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
1135 LoadPlugins();
1137 uint32_t count = 0;
1138 nsRefPtr<nsPluginTag> plugin = mPlugins;
1139 while (plugin != nullptr) {
1140 count++;
1141 plugin = plugin->mNext;
1144 *aResults = static_cast<nsIPluginTag**>
1145 (nsMemory::Alloc(count * sizeof(**aResults)));
1146 if (!*aResults)
1147 return NS_ERROR_OUT_OF_MEMORY;
1149 *aPluginCount = count;
1151 plugin = mPlugins;
1152 for (uint32_t i = 0; i < count; i++) {
1153 (*aResults)[i] = plugin;
1154 NS_ADDREF((*aResults)[i]);
1155 plugin = plugin->mNext;
1158 return NS_OK;
1161 nsPluginTag*
1162 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
1164 // We prefer the plugin with the highest version number.
1165 /// XXX(johns): This seems to assume the only time multiple plugins will have
1166 /// the same MIME type is if they're multiple versions of the same
1167 /// plugin -- but since plugin filenames and pretty names can both
1168 /// update, it's probably less arbitrary than just going at it
1169 /// alphabetically.
1171 if (matches.IsEmpty()) {
1172 return nullptr;
1175 nsPluginTag *preferredPlugin = matches[0];
1176 for (unsigned int i = 1; i < matches.Length(); i++) {
1177 if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) {
1178 preferredPlugin = matches[i];
1182 return preferredPlugin;
1185 nsPluginTag*
1186 nsPluginHost::FindPluginForType(const char* aMimeType,
1187 bool aCheckEnabled)
1189 if (!aMimeType) {
1190 return nullptr;
1193 LoadPlugins();
1195 InfallibleTArray<nsPluginTag*> matchingPlugins;
1197 nsPluginTag *plugin = mPlugins;
1198 while (plugin) {
1199 if (!aCheckEnabled || plugin->IsActive()) {
1200 int32_t mimeCount = plugin->mMimeTypes.Length();
1201 for (int32_t i = 0; i < mimeCount; i++) {
1202 if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) {
1203 matchingPlugins.AppendElement(plugin);
1204 break;
1208 plugin = plugin->mNext;
1211 return FindPreferredPlugin(matchingPlugins);
1214 nsPluginTag*
1215 nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
1216 const char*& aMimeType)
1218 if (!aExtension) {
1219 return nullptr;
1222 LoadPlugins();
1224 InfallibleTArray<nsPluginTag*> matchingPlugins;
1226 nsPluginTag *plugin = mPlugins;
1227 while (plugin) {
1228 if (plugin->IsActive()) {
1229 int32_t variants = plugin->mExtensions.Length();
1230 for (int32_t i = 0; i < variants; i++) {
1231 // mExtensionsArray[cnt] is a list of extensions separated by commas
1232 if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) {
1233 matchingPlugins.AppendElement(plugin);
1234 break;
1238 plugin = plugin->mNext;
1241 nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
1242 if (!preferredPlugin) {
1243 return nullptr;
1246 int32_t variants = preferredPlugin->mExtensions.Length();
1247 for (int32_t i = 0; i < variants; i++) {
1248 // mExtensionsArray[cnt] is a list of extensions separated by commas
1249 if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) {
1250 aMimeType = preferredPlugin->mMimeTypes[i].get();
1251 break;
1255 return preferredPlugin;
1258 static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
1259 nsNPAPIPlugin **aOutNPAPIPlugin)
1261 // If this is an in-process plugin we'll need to load it here if we haven't already.
1262 if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
1263 if (aPluginTag->mFullPath.IsEmpty())
1264 return NS_ERROR_FAILURE;
1265 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
1266 file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
1267 nsPluginFile pluginFile(file);
1268 PRLibrary* pluginLibrary = nullptr;
1270 if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
1271 return NS_ERROR_FAILURE;
1273 aPluginTag->mLibrary = pluginLibrary;
1276 nsresult rv;
1277 rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
1279 return rv;
1282 nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
1284 nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
1285 if (!plugin) {
1286 nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
1287 if (NS_FAILED(rv)) {
1288 return rv;
1290 aPluginTag->mPlugin = plugin;
1292 return NS_OK;
1295 nsresult
1296 nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
1298 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
1300 // If plugins haven't been scanned yet, do so now
1301 LoadPlugins();
1303 nsPluginTag* pluginTag = PluginWithId(aPluginId);
1304 if (pluginTag) {
1305 nsresult rv = EnsurePluginLoaded(pluginTag);
1306 if (NS_FAILED(rv)) {
1307 return rv;
1310 // We only get here if a content process doesn't have a PluginModuleParent
1311 // for the given plugin already. Therefore, this counter is counting the
1312 // number of outstanding PluginModuleParents for the plugin, excluding the
1313 // one from the chrome process.
1314 pluginTag->mContentProcessRunningCount++;
1315 NS_ADDREF(*aPlugin = pluginTag->mPlugin);
1316 return NS_OK;
1319 return NS_ERROR_FAILURE;
1322 class nsPluginUnloadRunnable : public nsRunnable
1324 public:
1325 explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
1327 NS_IMETHOD Run()
1329 nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
1330 if (!host) {
1331 return NS_OK;
1333 nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
1334 if (!pluginTag) {
1335 return NS_OK;
1338 MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
1339 pluginTag->mContentProcessRunningCount--;
1341 if (!pluginTag->mContentProcessRunningCount) {
1342 if (!host->IsRunningPlugin(pluginTag)) {
1343 pluginTag->TryUnloadPlugin(false);
1346 return NS_OK;
1349 protected:
1350 uint32_t mPluginId;
1353 void
1354 nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
1356 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
1358 // This is called in response to a message from the plugin. Don't unload the
1359 // plugin until the message handler is off the stack.
1360 nsRefPtr<nsPluginUnloadRunnable> runnable =
1361 new nsPluginUnloadRunnable(aPluginId);
1362 NS_DispatchToMainThread(runnable);
1365 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
1367 nsresult rv = NS_ERROR_FAILURE;
1368 *aPlugin = nullptr;
1370 if (!aMimeType)
1371 return NS_ERROR_ILLEGAL_VALUE;
1373 // If plugins haven't been scanned yet, do so now
1374 LoadPlugins();
1376 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
1377 if (pluginTag) {
1378 rv = NS_OK;
1379 PLUGIN_LOG(PLUGIN_LOG_BASIC,
1380 ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
1381 aMimeType, pluginTag->mFileName.get()));
1383 #ifdef DEBUG
1384 if (aMimeType && !pluginTag->mFileName.IsEmpty())
1385 printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
1386 #endif
1388 rv = EnsurePluginLoaded(pluginTag);
1389 if (NS_FAILED(rv)) {
1390 return rv;
1393 NS_ADDREF(*aPlugin = pluginTag->mPlugin);
1394 return NS_OK;
1397 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
1398 ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
1399 aMimeType, rv, *aPlugin,
1400 (pluginTag ? pluginTag->mFileName.get() : "(not found)")));
1402 return rv;
1405 // Normalize 'host' to ACE.
1406 nsresult
1407 nsPluginHost::NormalizeHostname(nsCString& host)
1409 if (IsASCII(host)) {
1410 ToLowerCase(host);
1411 return NS_OK;
1414 if (!mIDNService) {
1415 nsresult rv;
1416 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
1417 NS_ENSURE_SUCCESS(rv, rv);
1420 return mIDNService->ConvertUTF8toACE(host, host);
1423 // Enumerate a 'sites' array returned by GetSitesWithData and determine if
1424 // any of them have a base domain in common with 'domain'; if so, append them
1425 // to the 'result' array. If 'firstMatchOnly' is true, return after finding the
1426 // first match.
1427 nsresult
1428 nsPluginHost::EnumerateSiteData(const nsACString& domain,
1429 const InfallibleTArray<nsCString>& sites,
1430 InfallibleTArray<nsCString>& result,
1431 bool firstMatchOnly)
1433 NS_ASSERTION(!domain.IsVoid(), "null domain string");
1435 nsresult rv;
1436 if (!mTLDService) {
1437 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
1438 NS_ENSURE_SUCCESS(rv, rv);
1441 // Get the base domain from the domain.
1442 nsCString baseDomain;
1443 rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
1444 bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
1445 if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1446 // The base domain is the site itself. However, we must be careful to
1447 // normalize.
1448 baseDomain = domain;
1449 rv = NormalizeHostname(baseDomain);
1450 NS_ENSURE_SUCCESS(rv, rv);
1451 } else if (NS_FAILED(rv)) {
1452 return rv;
1455 // Enumerate the array of sites with data.
1456 for (uint32_t i = 0; i < sites.Length(); ++i) {
1457 const nsCString& site = sites[i];
1459 // Check if the site is an IP address.
1460 bool siteIsIP =
1461 site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
1462 if (siteIsIP != isIP)
1463 continue;
1465 nsCString siteBaseDomain;
1466 if (siteIsIP) {
1467 // Strip the '[]'.
1468 siteBaseDomain = Substring(site, 1, site.Length() - 2);
1469 } else {
1470 // Determine the base domain of the site.
1471 rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
1472 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1473 // The base domain is the site itself. However, we must be careful to
1474 // normalize.
1475 siteBaseDomain = site;
1476 rv = NormalizeHostname(siteBaseDomain);
1477 NS_ENSURE_SUCCESS(rv, rv);
1478 } else if (NS_FAILED(rv)) {
1479 return rv;
1483 // At this point, we can do an exact comparison of the two domains.
1484 if (baseDomain != siteBaseDomain) {
1485 continue;
1488 // Append the site to the result array.
1489 result.AppendElement(site);
1491 // If we're supposed to return early, do so.
1492 if (firstMatchOnly) {
1493 break;
1497 return NS_OK;
1500 NS_IMETHODIMP
1501 nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType,
1502 bool ignoreCTP,
1503 const nsACString& redirectURL)
1505 nsAutoCString mt(mimeType);
1506 nsAutoCString url(redirectURL);
1507 if (url.Length() == 0) {
1508 // using default play preview iframe URL, if redirectURL is not specified
1509 url.AssignLiteral("data:application/x-moz-playpreview;,");
1510 url.Append(mimeType);
1513 nsRefPtr<nsPluginPlayPreviewInfo> playPreview =
1514 new nsPluginPlayPreviewInfo(mt.get(), ignoreCTP, url.get());
1515 mPlayPreviewMimeTypes.AppendElement(playPreview);
1516 return NS_OK;
1519 NS_IMETHODIMP
1520 nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType)
1522 nsAutoCString mimeTypeToRemove(mimeType);
1523 for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0; i--) {
1524 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i - 1];
1525 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToRemove.get()) == 0) {
1526 mPlayPreviewMimeTypes.RemoveElementAt(i - 1);
1527 break;
1530 return NS_OK;
1533 NS_IMETHODIMP
1534 nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType,
1535 nsIPluginPlayPreviewInfo** aResult)
1537 nsAutoCString mimeTypeToFind(mimeType);
1538 for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) {
1539 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i];
1540 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToFind.get()) == 0) {
1541 *aResult = new nsPluginPlayPreviewInfo(pp.get());
1542 NS_ADDREF(*aResult);
1543 return NS_OK;
1546 *aResult = nullptr;
1547 return NS_ERROR_NOT_AVAILABLE;
1550 NS_IMETHODIMP
1551 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
1552 uint64_t flags, int64_t maxAge)
1554 // maxAge must be either a nonnegative integer or -1.
1555 NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
1557 // Caller may give us a tag object that is no longer live.
1558 if (!IsLiveTag(plugin)) {
1559 return NS_ERROR_NOT_AVAILABLE;
1562 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1564 // We only ensure support for clearing Flash site data for now.
1565 // We will also attempt to clear data for any plugin that happens
1566 // to be loaded already.
1567 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1568 return NS_ERROR_FAILURE;
1571 // Make sure the plugin is loaded.
1572 nsresult rv = EnsurePluginLoaded(tag);
1573 if (NS_FAILED(rv)) {
1574 return rv;
1577 PluginLibrary* library = tag->mPlugin->GetLibrary();
1579 // If 'domain' is the null string, clear everything.
1580 if (domain.IsVoid()) {
1581 return library->NPP_ClearSiteData(nullptr, flags, maxAge);
1584 // Get the list of sites from the plugin.
1585 InfallibleTArray<nsCString> sites;
1586 rv = library->NPP_GetSitesWithData(sites);
1587 NS_ENSURE_SUCCESS(rv, rv);
1589 // Enumerate the sites and build a list of matches.
1590 InfallibleTArray<nsCString> matches;
1591 rv = EnumerateSiteData(domain, sites, matches, false);
1592 NS_ENSURE_SUCCESS(rv, rv);
1594 // Clear the matches.
1595 for (uint32_t i = 0; i < matches.Length(); ++i) {
1596 const nsCString& match = matches[i];
1597 rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
1598 NS_ENSURE_SUCCESS(rv, rv);
1601 return NS_OK;
1604 NS_IMETHODIMP
1605 nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
1606 bool* result)
1608 // Caller may give us a tag object that is no longer live.
1609 if (!IsLiveTag(plugin)) {
1610 return NS_ERROR_NOT_AVAILABLE;
1613 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1615 // We only ensure support for clearing Flash site data for now.
1616 // We will also attempt to clear data for any plugin that happens
1617 // to be loaded already.
1618 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1619 return NS_ERROR_FAILURE;
1622 // Make sure the plugin is loaded.
1623 nsresult rv = EnsurePluginLoaded(tag);
1624 if (NS_FAILED(rv)) {
1625 return rv;
1628 PluginLibrary* library = tag->mPlugin->GetLibrary();
1630 // Get the list of sites from the plugin.
1631 InfallibleTArray<nsCString> sites;
1632 rv = library->NPP_GetSitesWithData(sites);
1633 NS_ENSURE_SUCCESS(rv, rv);
1635 // If there's no data, we're done.
1636 if (sites.IsEmpty()) {
1637 *result = false;
1638 return NS_OK;
1641 // If 'domain' is the null string, and there's data for at least one site,
1642 // we're done.
1643 if (domain.IsVoid()) {
1644 *result = true;
1645 return NS_OK;
1648 // Enumerate the sites and determine if there's a match.
1649 InfallibleTArray<nsCString> matches;
1650 rv = EnumerateSiteData(domain, sites, matches, true);
1651 NS_ENSURE_SUCCESS(rv, rv);
1653 *result = !matches.IsEmpty();
1654 return NS_OK;
1657 bool nsPluginHost::IsJavaMIMEType(const char* aType)
1659 // The java mime pref may well not be one of these,
1660 // e.g. application/x-java-test used in the test suite
1661 nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
1662 return aType &&
1663 (javaMIME.EqualsIgnoreCase(aType) ||
1664 (0 == PL_strncasecmp(aType, "application/x-java-vm",
1665 sizeof("application/x-java-vm") - 1)) ||
1666 (0 == PL_strncasecmp(aType, "application/x-java-applet",
1667 sizeof("application/x-java-applet") - 1)) ||
1668 (0 == PL_strncasecmp(aType, "application/x-java-bean",
1669 sizeof("application/x-java-bean") - 1)));
1672 // Check whether or not a tag is a live, valid tag, and that it's loaded.
1673 bool
1674 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
1676 nsPluginTag* tag;
1677 for (tag = mPlugins; tag; tag = tag->mNext) {
1678 if (tag == aPluginTag) {
1679 return true;
1682 return false;
1685 nsPluginTag*
1686 nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
1688 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1689 if (tag->HasSameNameAndMimes(aPluginTag)) {
1690 return tag;
1693 return nullptr;
1696 nsPluginTag*
1697 nsPluginHost::FirstPluginWithPath(const nsCString& path)
1699 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1700 if (tag->mFullPath.Equals(path)) {
1701 return tag;
1704 return nullptr;
1707 nsPluginTag*
1708 nsPluginHost::PluginWithId(uint32_t aId)
1710 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1711 if (tag->mId == aId) {
1712 return tag;
1715 return nullptr;
1718 namespace {
1720 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
1722 PRTime fileModTime = 0;
1724 #if defined(XP_MACOSX)
1725 // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
1726 // is a much better guide to when it was last modified than the date of
1727 // its package directory. See bug 313700.
1728 nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
1729 if (localFileMac) {
1730 localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
1731 } else {
1732 localfile->GetLastModifiedTime(&fileModTime);
1734 #else
1735 localfile->GetLastModifiedTime(&fileModTime);
1736 #endif
1738 return fileModTime;
1741 bool
1742 GetPluginIsFromExtension(const nsCOMPtr<nsIFile>& pluginFile,
1743 const nsCOMArray<nsIFile>& extensionDirs)
1745 for (uint32_t i = 0; i < extensionDirs.Length(); ++i) {
1746 bool contains;
1747 if (NS_FAILED(extensionDirs[i]->Contains(pluginFile, &contains)) || !contains) {
1748 continue;
1751 return true;
1754 return false;
1757 void
1758 GetExtensionDirectories(nsCOMArray<nsIFile>& dirs)
1760 nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1761 if (!dirService) {
1762 return;
1765 nsCOMPtr<nsISimpleEnumerator> list;
1766 nsresult rv = dirService->Get(XRE_EXTENSIONS_DIR_LIST,
1767 NS_GET_IID(nsISimpleEnumerator),
1768 getter_AddRefs(list));
1769 if (NS_FAILED(rv)) {
1770 return;
1773 bool more;
1774 while (NS_SUCCEEDED(list->HasMoreElements(&more)) && more) {
1775 nsCOMPtr<nsISupports> next;
1776 if (NS_FAILED(list->GetNext(getter_AddRefs(next)))) {
1777 break;
1779 nsCOMPtr<nsIFile> file = do_QueryInterface(next);
1780 if (file) {
1781 file->Normalize();
1782 dirs.AppendElement(file);
1787 struct CompareFilesByTime
1789 bool
1790 LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
1792 return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
1795 bool
1796 Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
1798 return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
1802 } // anonymous namespace
1804 void
1805 nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
1807 aPluginTag->mNext = mPlugins;
1808 mPlugins = aPluginTag;
1810 if (aPluginTag->IsActive()) {
1811 nsAdoptingCString disableFullPage =
1812 Preferences::GetCString(kPrefDisableFullPage);
1813 for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
1814 if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
1815 RegisterWithCategoryManager(aPluginTag->mMimeTypes[i],
1816 ePluginRegister);
1822 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
1824 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
1825 bool aCreatePluginList,
1826 bool *aPluginsChanged)
1828 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
1830 NS_ENSURE_ARG_POINTER(aPluginsChanged);
1831 nsresult rv;
1833 *aPluginsChanged = false;
1835 #ifdef PLUGIN_LOGGING
1836 nsAutoCString dirPath;
1837 pluginsDir->GetNativePath(dirPath);
1838 PLUGIN_LOG(PLUGIN_LOG_BASIC,
1839 ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
1840 #endif
1842 nsCOMPtr<nsISimpleEnumerator> iter;
1843 rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
1844 if (NS_FAILED(rv))
1845 return rv;
1847 nsAutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
1849 bool hasMore;
1850 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
1851 nsCOMPtr<nsISupports> supports;
1852 rv = iter->GetNext(getter_AddRefs(supports));
1853 if (NS_FAILED(rv))
1854 continue;
1855 nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
1856 if (NS_FAILED(rv))
1857 continue;
1859 // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
1860 // See bug 197855.
1861 dirEntry->Normalize();
1863 if (nsPluginsDir::IsPluginFile(dirEntry)) {
1864 pluginFiles.AppendElement(dirEntry);
1868 pluginFiles.Sort(CompareFilesByTime());
1870 nsCOMArray<nsIFile> extensionDirs;
1871 GetExtensionDirectories(extensionDirs);
1873 bool warnOutdated = false;
1875 for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
1876 nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
1878 nsString utf16FilePath;
1879 rv = localfile->GetPath(utf16FilePath);
1880 if (NS_FAILED(rv))
1881 continue;
1883 const int64_t fileModTime = GetPluginLastModifiedTime(localfile);
1884 const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
1886 // Look for it in our cache
1887 NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
1888 nsRefPtr<nsPluginTag> pluginTag;
1889 RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
1891 bool seenBefore = false;
1893 if (pluginTag) {
1894 seenBefore = true;
1895 // If plugin changed, delete cachedPluginTag and don't use cache
1896 if (fileModTime != pluginTag->mLastModifiedTime) {
1897 // Plugins has changed. Don't use cached plugin info.
1898 pluginTag = nullptr;
1900 // plugin file changed, flag this fact
1901 *aPluginsChanged = true;
1904 // If we're not creating a list and we already know something changed then
1905 // we're done.
1906 if (!aCreatePluginList) {
1907 if (*aPluginsChanged) {
1908 return NS_OK;
1910 continue;
1914 bool isKnownInvalidPlugin = false;
1915 for (nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
1916 invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
1917 // If already marked as invalid, ignore it
1918 if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
1919 invalidPlugins->mLastModifiedTime == fileModTime) {
1920 if (aCreatePluginList) {
1921 invalidPlugins->mSeen = true;
1923 isKnownInvalidPlugin = true;
1924 break;
1927 if (isKnownInvalidPlugin) {
1928 continue;
1931 // if it is not found in cache info list or has been changed, create a new one
1932 if (!pluginTag) {
1933 nsPluginFile pluginFile(localfile);
1935 // create a tag describing this plugin.
1936 PRLibrary *library = nullptr;
1937 nsPluginInfo info;
1938 memset(&info, 0, sizeof(info));
1939 nsresult res;
1940 // Opening a block for the telemetry AutoTimer
1942 Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
1943 res = pluginFile.GetPluginInfo(info, &library);
1945 // if we don't have mime type don't proceed, this is not a plugin
1946 if (NS_FAILED(res) || !info.fMimeTypeArray) {
1947 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
1948 fileModTime);
1949 pluginFile.FreePluginInfo(info);
1951 if (aCreatePluginList) {
1952 invalidTag->mSeen = true;
1954 invalidTag->mNext = mInvalidPlugins;
1955 if (mInvalidPlugins) {
1956 mInvalidPlugins->mPrev = invalidTag;
1958 mInvalidPlugins = invalidTag;
1960 // Mark aPluginsChanged so pluginreg is rewritten
1961 *aPluginsChanged = true;
1962 continue;
1965 pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
1966 pluginFile.FreePluginInfo(info);
1967 if (!pluginTag)
1968 return NS_ERROR_OUT_OF_MEMORY;
1970 pluginTag->mLibrary = library;
1971 uint32_t state = pluginTag->GetBlocklistState();
1973 // If the blocklist says it is risky and we have never seen this
1974 // plugin before, then disable it.
1975 // If the blocklist says this is an outdated plugin, warn about
1976 // outdated plugins.
1977 if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
1978 pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
1980 if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
1981 warnOutdated = true;
1984 // Plugin unloading is tag-based. If we created a new tag and loaded
1985 // the library in the process then we want to attempt to unload it here.
1986 // Only do this if the pref is set for aggressive unloading.
1987 if (UnloadPluginsASAP()) {
1988 pluginTag->TryUnloadPlugin(false);
1992 // do it if we still want it
1993 if (!seenBefore) {
1994 // We have a valid new plugin so report that plugins have changed.
1995 *aPluginsChanged = true;
1998 // Avoid adding different versions of the same plugin if they are running
1999 // in-process, otherwise we risk undefined behaviour.
2000 if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
2001 if (HaveSamePlugin(pluginTag)) {
2002 continue;
2006 // Don't add the same plugin again if it hasn't changed
2007 if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
2008 if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
2009 continue;
2013 // If we're not creating a plugin list, simply looking for changes,
2014 // then we're done.
2015 if (!aCreatePluginList) {
2016 return NS_OK;
2019 AddPluginTag(pluginTag);
2022 if (warnOutdated) {
2023 Preferences::SetBool("plugins.update.notifyUser", true);
2026 return NS_OK;
2029 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
2030 bool aCreatePluginList,
2031 bool *aPluginsChanged)
2033 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2035 bool hasMore;
2036 while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
2037 nsCOMPtr<nsISupports> supports;
2038 nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
2039 if (NS_FAILED(rv))
2040 continue;
2041 nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
2042 if (NS_FAILED(rv))
2043 continue;
2045 // don't pass aPluginsChanged directly to prevent it from been reset
2046 bool pluginschanged = false;
2047 ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
2049 if (pluginschanged)
2050 *aPluginsChanged = true;
2052 // if changes are detected and we are not creating the list, do not proceed
2053 if (!aCreatePluginList && *aPluginsChanged)
2054 break;
2056 return NS_OK;
2059 void
2060 nsPluginHost::IncrementChromeEpoch()
2062 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2063 mPluginEpoch++;
2066 uint32_t
2067 nsPluginHost::ChromeEpoch()
2069 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2070 return mPluginEpoch;
2073 uint32_t
2074 nsPluginHost::ChromeEpochForContent()
2076 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
2077 return mPluginEpoch;
2080 void
2081 nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
2083 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
2084 mPluginEpoch = aEpoch;
2087 nsresult nsPluginHost::LoadPlugins()
2089 #ifdef ANDROID
2090 if (XRE_GetProcessType() == GeckoProcessType_Content) {
2091 return NS_OK;
2093 #endif
2094 // do not do anything if it is already done
2095 // use ReloadPlugins() to enforce loading
2096 if (mPluginsLoaded)
2097 return NS_OK;
2099 if (mPluginsDisabled)
2100 return NS_OK;
2102 bool pluginschanged;
2103 nsresult rv = FindPlugins(true, &pluginschanged);
2104 if (NS_FAILED(rv))
2105 return rv;
2107 // only if plugins have changed will we notify plugin-change observers
2108 if (pluginschanged) {
2109 if (XRE_GetProcessType() == GeckoProcessType_Default) {
2110 IncrementChromeEpoch();
2113 nsCOMPtr<nsIObserverService> obsService =
2114 mozilla::services::GetObserverService();
2115 if (obsService)
2116 obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
2119 return NS_OK;
2122 nsresult
2123 nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
2125 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
2127 dom::ContentChild* cp = dom::ContentChild::GetSingleton();
2128 nsTArray<PluginTag> plugins;
2129 uint32_t parentEpoch;
2130 if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
2131 return NS_ERROR_NOT_AVAILABLE;
2134 if (parentEpoch != ChromeEpochForContent()) {
2135 SetChromeEpochForContent(parentEpoch);
2136 *aPluginsChanged = true;
2137 if (!aCreatePluginList) {
2138 return NS_OK;
2141 for (size_t i = 0; i < plugins.Length(); i++) {
2142 PluginTag& tag = plugins[i];
2144 // Don't add the same plugin again.
2145 if (PluginWithId(tag.id())) {
2146 continue;
2149 nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
2150 tag.name().get(),
2151 tag.description().get(),
2152 tag.filename().get(),
2153 "", // aFullPath
2154 tag.version().get(),
2155 nsTArray<nsCString>(tag.mimeTypes()),
2156 nsTArray<nsCString>(tag.mimeDescriptions()),
2157 nsTArray<nsCString>(tag.extensions()),
2158 tag.isJavaPlugin(),
2159 tag.isFlashPlugin(),
2160 tag.lastModifiedTime(),
2161 tag.isFromExtension());
2162 AddPluginTag(pluginTag);
2166 mPluginsLoaded = true;
2167 return NS_OK;
2170 // if aCreatePluginList is false we will just scan for plugins
2171 // and see if any changes have been made to the plugins.
2172 // This is needed in ReloadPlugins to prevent possible recursive reloads
2173 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
2175 Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
2177 NS_ENSURE_ARG_POINTER(aPluginsChanged);
2179 *aPluginsChanged = false;
2181 if (XRE_GetProcessType() == GeckoProcessType_Content) {
2182 return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
2185 nsresult rv;
2187 // Read cached plugins info. If the profile isn't yet available then don't
2188 // scan for plugins
2189 if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
2190 return NS_OK;
2192 #ifdef XP_WIN
2193 // Failure here is not a show-stopper so just warn.
2194 rv = EnsurePrivateDirServiceProvider();
2195 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
2196 #endif /* XP_WIN */
2198 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
2199 if (NS_FAILED(rv))
2200 return rv;
2202 nsCOMPtr<nsISimpleEnumerator> dirList;
2204 // Scan plugins directories;
2205 // don't pass aPluginsChanged directly, to prevent its
2206 // possible reset in subsequent ScanPluginsDirectory calls
2207 bool pluginschanged = false;
2209 // Scan the app-defined list of plugin dirs.
2210 rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
2211 if (NS_SUCCEEDED(rv)) {
2212 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2214 if (pluginschanged)
2215 *aPluginsChanged = true;
2217 // if we are just looking for possible changes,
2218 // no need to proceed if changes are detected
2219 if (!aCreatePluginList && *aPluginsChanged) {
2220 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2221 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2222 return NS_OK;
2224 } else {
2225 #ifdef ANDROID
2226 LOG("getting plugins dir failed");
2227 #endif
2230 mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
2231 // the rest is optional
2233 #ifdef XP_WIN
2234 bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
2236 // Now lets scan any PLID directories
2237 if (bScanPLIDs && mPrivateDirServiceProvider) {
2238 rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
2239 if (NS_SUCCEEDED(rv)) {
2240 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2242 if (pluginschanged)
2243 *aPluginsChanged = true;
2245 // if we are just looking for possible changes,
2246 // no need to proceed if changes are detected
2247 if (!aCreatePluginList && *aPluginsChanged) {
2248 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2249 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2250 return NS_OK;
2256 // Scan the installation paths of our popular plugins if the prefs are enabled
2258 // This table controls the order of scanning
2259 const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY,
2260 NS_WIN_QUICKTIME_SCAN_KEY,
2261 NS_WIN_WMP_SCAN_KEY};
2263 uint32_t size = sizeof(prefs) / sizeof(prefs[0]);
2265 for (uint32_t i = 0; i < size; i+=1) {
2266 nsCOMPtr<nsIFile> dirToScan;
2267 bool bExists;
2268 if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
2269 dirToScan &&
2270 NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
2271 bExists) {
2273 ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
2275 if (pluginschanged)
2276 *aPluginsChanged = true;
2278 // if we are just looking for possible changes,
2279 // no need to proceed if changes are detected
2280 if (!aCreatePluginList && *aPluginsChanged) {
2281 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2282 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2283 return NS_OK;
2287 #endif
2289 // We should also consider plugins to have changed if any plugins have been removed.
2290 // We'll know if any were removed if they weren't taken out of the cached plugins list
2291 // during our scan, thus we can assume something was removed if the cached plugins list
2292 // contains anything.
2293 if (!*aPluginsChanged && mCachedPlugins) {
2294 *aPluginsChanged = true;
2297 // Remove unseen invalid plugins
2298 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2299 while (invalidPlugins) {
2300 if (!invalidPlugins->mSeen) {
2301 nsRefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
2303 if (invalidPlugin->mPrev) {
2304 invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
2306 else {
2307 mInvalidPlugins = invalidPlugin->mNext;
2309 if (invalidPlugin->mNext) {
2310 invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
2313 invalidPlugins = invalidPlugin->mNext;
2315 invalidPlugin->mPrev = nullptr;
2316 invalidPlugin->mNext = nullptr;
2318 else {
2319 invalidPlugins->mSeen = false;
2320 invalidPlugins = invalidPlugins->mNext;
2324 // if we are not creating the list, there is no need to proceed
2325 if (!aCreatePluginList) {
2326 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2327 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2328 return NS_OK;
2331 // if we are creating the list, it is already done;
2332 // update the plugins info cache if changes are detected
2333 if (*aPluginsChanged)
2334 WritePluginInfo();
2336 // No more need for cached plugins. Clear it up.
2337 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2338 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2340 return NS_OK;
2343 bool
2344 mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
2345 nsTArray<PluginTag>* aPlugins,
2346 uint32_t* aNewPluginEpoch)
2348 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2350 nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
2351 host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
2352 return true;
2355 void
2356 nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
2357 nsTArray<PluginTag>* aPlugins,
2358 uint32_t* aNewPluginEpoch)
2360 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2362 // Load plugins so that the epoch is correct.
2363 LoadPlugins();
2365 *aNewPluginEpoch = ChromeEpoch();
2366 if (aPluginEpoch == ChromeEpoch()) {
2367 return;
2370 nsTArray<nsRefPtr<nsPluginTag>> plugins;
2371 GetPlugins(plugins);
2373 for (size_t i = 0; i < plugins.Length(); i++) {
2374 nsRefPtr<nsPluginTag> tag = plugins[i];
2376 if (!nsNPAPIPlugin::RunPluginOOP(tag)) {
2377 // Don't expose non-OOP plugins to content processes since we have no way
2378 // to bridge them over.
2379 continue;
2382 aPlugins->AppendElement(PluginTag(tag->mId,
2383 tag->mName,
2384 tag->mDescription,
2385 tag->mMimeTypes,
2386 tag->mMimeDescriptions,
2387 tag->mExtensions,
2388 tag->mIsJavaPlugin,
2389 tag->mIsFlashPlugin,
2390 tag->mFileName,
2391 tag->mVersion,
2392 tag->mLastModifiedTime,
2393 tag->IsFromExtension()));
2397 nsresult
2398 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
2400 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2402 ReadPluginInfo();
2403 WritePluginInfo();
2404 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2405 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2407 if (!aPluginTag) {
2408 return NS_OK;
2411 // Update types with category manager
2412 nsAdoptingCString disableFullPage =
2413 Preferences::GetCString(kPrefDisableFullPage);
2414 for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
2415 nsRegisterType shouldRegister;
2417 if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
2418 shouldRegister = ePluginUnregister;
2419 } else {
2420 nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
2421 true);
2422 shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
2425 RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister);
2428 nsCOMPtr<nsIObserverService> obsService =
2429 mozilla::services::GetObserverService();
2430 if (obsService)
2431 obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
2433 // Reload instances if needed
2434 if (aPluginTag->IsActive()) {
2435 return NS_OK;
2438 return NS_OK;
2441 /* static */ bool
2442 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
2444 nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
2445 if (!whitelist.Length()) {
2446 return true;
2448 nsDependentCString wrap(aMimeType);
2449 return IsTypeInList(wrap, whitelist);
2452 void
2453 nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
2454 nsRegisterType aType)
2456 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2457 ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
2458 aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
2460 nsCOMPtr<nsICategoryManager> catMan =
2461 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
2462 if (!catMan) {
2463 return;
2466 const char *contractId =
2467 "@mozilla.org/content/plugin/document-loader-factory;1";
2469 if (aType == ePluginRegister) {
2470 catMan->AddCategoryEntry("Gecko-Content-Viewers",
2471 aMimeType.get(),
2472 contractId,
2473 false, /* persist: broken by bug 193031 */
2474 mOverrideInternalTypes,
2475 nullptr);
2476 } else {
2477 // Only delete the entry if a plugin registered for it
2478 nsXPIDLCString value;
2479 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
2480 aMimeType.get(),
2481 getter_Copies(value));
2482 if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
2483 catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
2484 aMimeType.get(),
2485 true);
2490 nsresult
2491 nsPluginHost::WritePluginInfo()
2493 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2495 nsresult rv = NS_OK;
2496 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2497 if (NS_FAILED(rv))
2498 return rv;
2500 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2501 getter_AddRefs(mPluginRegFile));
2503 if (!mPluginRegFile)
2504 return NS_ERROR_FAILURE;
2506 PRFileDesc* fd = nullptr;
2508 nsCOMPtr<nsIFile> pluginReg;
2510 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2511 if (NS_FAILED(rv))
2512 return rv;
2514 nsAutoCString filename(kPluginRegistryFilename);
2515 filename.AppendLiteral(".tmp");
2516 rv = pluginReg->AppendNative(filename);
2517 if (NS_FAILED(rv))
2518 return rv;
2520 rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
2521 if (NS_FAILED(rv))
2522 return rv;
2524 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
2525 if (!runtime) {
2526 return NS_ERROR_FAILURE;
2529 nsAutoCString arch;
2530 rv = runtime->GetXPCOMABI(arch);
2531 if (NS_FAILED(rv)) {
2532 return rv;
2535 PR_fprintf(fd, "Generated File. Do not edit.\n");
2537 PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n",
2538 PLUGIN_REGISTRY_FIELD_DELIMITER,
2539 kPluginRegistryVersion,
2540 PLUGIN_REGISTRY_FIELD_DELIMITER,
2541 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2542 PLUGIN_REGISTRY_FIELD_DELIMITER,
2543 arch.get(),
2544 PLUGIN_REGISTRY_FIELD_DELIMITER,
2545 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2547 // Store all plugins in the mPlugins list - all plugins currently in use.
2548 PR_fprintf(fd, "\n[PLUGINS]\n");
2550 for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
2551 // store each plugin info into the registry
2552 // filename & fullpath are on separate line
2553 // because they can contain field delimiter char
2554 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
2555 (tag->mFileName.get()),
2556 PLUGIN_REGISTRY_FIELD_DELIMITER,
2557 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2558 (tag->mFullPath.get()),
2559 PLUGIN_REGISTRY_FIELD_DELIMITER,
2560 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2561 (tag->mVersion.get()),
2562 PLUGIN_REGISTRY_FIELD_DELIMITER,
2563 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2565 // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
2566 PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
2567 tag->mLastModifiedTime,
2568 PLUGIN_REGISTRY_FIELD_DELIMITER,
2569 false, // did store whether or not to unload in-process plugins
2570 PLUGIN_REGISTRY_FIELD_DELIMITER,
2571 0, // legacy field for flags
2572 PLUGIN_REGISTRY_FIELD_DELIMITER,
2573 tag->IsFromExtension(),
2574 PLUGIN_REGISTRY_FIELD_DELIMITER,
2575 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2577 //description, name & mtypecount are on separate line
2578 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
2579 (tag->mDescription.get()),
2580 PLUGIN_REGISTRY_FIELD_DELIMITER,
2581 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2582 (tag->mName.get()),
2583 PLUGIN_REGISTRY_FIELD_DELIMITER,
2584 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2585 tag->mMimeTypes.Length());
2587 // Add in each mimetype this plugin supports
2588 for (uint32_t i = 0; i < tag->mMimeTypes.Length(); i++) {
2589 PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
2590 i,PLUGIN_REGISTRY_FIELD_DELIMITER,
2591 (tag->mMimeTypes[i].get()),
2592 PLUGIN_REGISTRY_FIELD_DELIMITER,
2593 (tag->mMimeDescriptions[i].get()),
2594 PLUGIN_REGISTRY_FIELD_DELIMITER,
2595 (tag->mExtensions[i].get()),
2596 PLUGIN_REGISTRY_FIELD_DELIMITER,
2597 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2601 PR_fprintf(fd, "\n[INVALID]\n");
2603 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2604 while (invalidPlugins) {
2605 // fullPath
2606 PR_fprintf(fd, "%s%c%c\n",
2607 (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
2608 PLUGIN_REGISTRY_FIELD_DELIMITER,
2609 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2611 // lastModifiedTimeStamp
2612 PR_fprintf(fd, "%lld%c%c\n",
2613 invalidPlugins->mLastModifiedTime,
2614 PLUGIN_REGISTRY_FIELD_DELIMITER,
2615 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2617 invalidPlugins = invalidPlugins->mNext;
2620 PRStatus prrc;
2621 prrc = PR_Close(fd);
2622 if (prrc != PR_SUCCESS) {
2623 // we should obtain a refined value based on prrc;
2624 rv = NS_ERROR_FAILURE;
2625 MOZ_ASSERT(false, "PR_Close() failed.");
2626 return rv;
2628 nsCOMPtr<nsIFile> parent;
2629 rv = pluginReg->GetParent(getter_AddRefs(parent));
2630 NS_ENSURE_SUCCESS(rv, rv);
2631 rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
2632 return rv;
2635 nsresult
2636 nsPluginHost::ReadPluginInfo()
2638 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
2640 const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
2641 const long PLUGIN_REG_MAX_MIMETYPES = 1000;
2643 // we need to import the legacy flags from the plugin registry once
2644 const bool pluginStateImported =
2645 Preferences::GetDefaultBool("plugin.importedState", false);
2647 nsresult rv;
2649 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2650 if (NS_FAILED(rv))
2651 return rv;
2653 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2654 getter_AddRefs(mPluginRegFile));
2656 if (!mPluginRegFile) {
2657 // There is no profile yet, this will tell us if there is going to be one
2658 // in the future.
2659 directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
2660 getter_AddRefs(mPluginRegFile));
2661 if (!mPluginRegFile)
2662 return NS_ERROR_FAILURE;
2663 else
2664 return NS_ERROR_NOT_AVAILABLE;
2667 PRFileDesc* fd = nullptr;
2669 nsCOMPtr<nsIFile> pluginReg;
2671 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2672 if (NS_FAILED(rv))
2673 return rv;
2675 rv = pluginReg->AppendNative(kPluginRegistryFilename);
2676 if (NS_FAILED(rv))
2677 return rv;
2679 int64_t fileSize;
2680 rv = pluginReg->GetFileSize(&fileSize);
2681 if (NS_FAILED(rv))
2682 return rv;
2684 if (fileSize > INT32_MAX) {
2685 return NS_ERROR_FAILURE;
2687 int32_t flen = int32_t(fileSize);
2688 if (flen == 0) {
2689 NS_WARNING("Plugins Registry Empty!");
2690 return NS_OK; // ERROR CONDITION
2693 nsPluginManifestLineReader reader;
2694 char* registry = reader.Init(flen);
2695 if (!registry)
2696 return NS_ERROR_OUT_OF_MEMORY;
2698 rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
2699 if (NS_FAILED(rv))
2700 return rv;
2702 // set rv to return an error on goto out
2703 rv = NS_ERROR_FAILURE;
2705 int32_t bread = PR_Read(fd, registry, flen);
2707 PRStatus prrc;
2708 prrc = PR_Close(fd);
2709 if (prrc != PR_SUCCESS) {
2710 // Strange error: this is one of those "Should not happen" error.
2711 // we may want to report something more refined than NS_ERROR_FAILURE.
2712 MOZ_ASSERT(false, "PR_Close() failed.");
2713 return rv;
2716 if (flen > bread)
2717 return rv;
2719 if (!ReadSectionHeader(reader, "HEADER"))
2720 return rv;;
2722 if (!reader.NextLine())
2723 return rv;
2725 char* values[6];
2727 // VersionLiteral, kPluginRegistryVersion
2728 if (2 != reader.ParseLine(values, 2))
2729 return rv;
2731 // VersionLiteral
2732 if (PL_strcmp(values[0], "Version"))
2733 return rv;
2735 // kPluginRegistryVersion
2736 int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
2737 mozilla::Version version(values[1]);
2738 // If this is a registry from some future version then don't attempt to read it
2739 if (vdiff > 0)
2740 return rv;
2741 // If this is a registry from before the minimum then don't attempt to read it
2742 if (version < kMinimumRegistryVersion)
2743 return rv;
2745 // Registry v0.10 and upwards includes the plugin version field
2746 bool regHasVersion = (version >= "0.10");
2748 // Registry v0.13 and upwards includes the architecture
2749 if (version >= "0.13") {
2750 char* archValues[6];
2752 if (!reader.NextLine()) {
2753 return rv;
2756 // ArchLiteral, Architecture
2757 if (2 != reader.ParseLine(archValues, 2)) {
2758 return rv;
2761 // ArchLiteral
2762 if (PL_strcmp(archValues[0], "Arch")) {
2763 return rv;
2766 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
2767 if (!runtime) {
2768 return rv;
2771 nsAutoCString arch;
2772 if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
2773 return rv;
2776 // If this is a registry from a different architecture then don't attempt to read it
2777 if (PL_strcmp(archValues[1], arch.get())) {
2778 return rv;
2782 // Registry v0.13 and upwards includes the list of invalid plugins
2783 const bool hasInvalidPlugins = (version >= "0.13");
2785 // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
2786 const bool hasValidFlags = (version < "0.16");
2788 // Registry v0.17 and upwards store whether the plugin comes from an XPI.
2789 const bool hasFromExtension = (version >= "0.17");
2791 #if defined(XP_MACOSX)
2792 const bool hasFullPathInFileNameField = false;
2793 #else
2794 const bool hasFullPathInFileNameField = (version < "0.11");
2795 #endif
2797 if (!ReadSectionHeader(reader, "PLUGINS"))
2798 return rv;
2800 while (reader.NextLine()) {
2801 const char *filename;
2802 const char *fullpath;
2803 nsAutoCString derivedFileName;
2805 if (hasInvalidPlugins && *reader.LinePtr() == '[') {
2806 break;
2809 if (hasFullPathInFileNameField) {
2810 fullpath = reader.LinePtr();
2811 if (!reader.NextLine())
2812 return rv;
2813 // try to derive a file name from the full path
2814 if (fullpath) {
2815 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
2816 file->InitWithNativePath(nsDependentCString(fullpath));
2817 file->GetNativeLeafName(derivedFileName);
2818 filename = derivedFileName.get();
2819 } else {
2820 filename = nullptr;
2823 // skip the next line, useless in this version
2824 if (!reader.NextLine())
2825 return rv;
2826 } else {
2827 filename = reader.LinePtr();
2828 if (!reader.NextLine())
2829 return rv;
2831 fullpath = reader.LinePtr();
2832 if (!reader.NextLine())
2833 return rv;
2836 const char *version;
2837 if (regHasVersion) {
2838 version = reader.LinePtr();
2839 if (!reader.NextLine())
2840 return rv;
2841 } else {
2842 version = "0";
2845 // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
2846 const int count = hasFromExtension ? 4 : 3;
2847 if (reader.ParseLine(values, count) != count)
2848 return rv;
2850 // If this is an old plugin registry mark this plugin tag to be refreshed
2851 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
2852 uint32_t tagflag = atoi(values[2]);
2853 bool fromExtension = false;
2854 if (hasFromExtension) {
2855 fromExtension = atoi(values[3]);
2857 if (!reader.NextLine())
2858 return rv;
2860 char *description = reader.LinePtr();
2861 if (!reader.NextLine())
2862 return rv;
2864 #if MOZ_WIDGET_ANDROID
2865 // Flash on Android does not populate the version field, but it is tacked on to the description.
2866 // For example, "Shockwave Flash 11.1 r115"
2867 if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) {
2868 version = &description[16];
2870 #endif
2872 const char *name = reader.LinePtr();
2873 if (!reader.NextLine())
2874 return rv;
2876 long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
2877 if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
2878 mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
2879 return NS_ERROR_FAILURE;
2882 char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
2883 char **mimetypes;
2884 char **mimedescriptions;
2885 char **extensions;
2886 char **heapalloced = 0;
2887 if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
2888 heapalloced = new char *[mimetypecount * 3];
2889 mimetypes = heapalloced;
2890 } else {
2891 mimetypes = stackalloced;
2893 mimedescriptions = mimetypes + mimetypecount;
2894 extensions = mimedescriptions + mimetypecount;
2896 int mtr = 0; //mimetype read
2897 for (; mtr < mimetypecount; mtr++) {
2898 if (!reader.NextLine())
2899 break;
2901 //line number|mimetype|description|extension
2902 if (4 != reader.ParseLine(values, 4))
2903 break;
2904 int line = atoi(values[0]);
2905 if (line != mtr)
2906 break;
2907 mimetypes[mtr] = values[1];
2908 mimedescriptions[mtr] = values[2];
2909 extensions[mtr] = values[3];
2912 if (mtr != mimetypecount) {
2913 if (heapalloced) {
2914 delete [] heapalloced;
2916 return rv;
2919 nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
2920 description,
2921 filename,
2922 fullpath,
2923 version,
2924 (const char* const*)mimetypes,
2925 (const char* const*)mimedescriptions,
2926 (const char* const*)extensions,
2927 mimetypecount, lastmod, fromExtension, true);
2928 if (heapalloced)
2929 delete [] heapalloced;
2931 // Import flags from registry into prefs for old registry versions
2932 if (hasValidFlags && !pluginStateImported) {
2933 tag->ImportFlagsToPrefs(tagflag);
2936 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
2937 ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
2938 tag->mNext = mCachedPlugins;
2939 mCachedPlugins = tag;
2942 // On Android we always want to try to load a plugin again (Flash). Bug 935676.
2943 #ifndef MOZ_WIDGET_ANDROID
2944 if (hasInvalidPlugins) {
2945 if (!ReadSectionHeader(reader, "INVALID")) {
2946 return rv;
2949 while (reader.NextLine()) {
2950 const char *fullpath = reader.LinePtr();
2951 if (!reader.NextLine()) {
2952 return rv;
2955 const char *lastModifiedTimeStamp = reader.LinePtr();
2956 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
2958 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
2960 invalidTag->mNext = mInvalidPlugins;
2961 if (mInvalidPlugins) {
2962 mInvalidPlugins->mPrev = invalidTag;
2964 mInvalidPlugins = invalidTag;
2967 #endif
2969 // flip the pref so we don't import the legacy flags again
2970 Preferences::SetBool("plugin.importedState", true);
2972 return NS_OK;
2975 void
2976 nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
2978 nsRefPtr<nsPluginTag> prev;
2979 nsRefPtr<nsPluginTag> tag = mCachedPlugins;
2980 while (tag)
2982 if (tag->mFullPath.Equals(filePath)) {
2983 // Found it. Remove it from our list
2984 if (prev)
2985 prev->mNext = tag->mNext;
2986 else
2987 mCachedPlugins = tag->mNext;
2988 tag->mNext = nullptr;
2989 *result = tag;
2990 NS_ADDREF(*result);
2991 break;
2993 prev = tag;
2994 tag = tag->mNext;
2998 #ifdef XP_WIN
2999 nsresult
3000 nsPluginHost::EnsurePrivateDirServiceProvider()
3002 if (!mPrivateDirServiceProvider) {
3003 nsresult rv;
3004 mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
3005 if (!mPrivateDirServiceProvider)
3006 return NS_ERROR_OUT_OF_MEMORY;
3007 nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
3008 if (NS_FAILED(rv))
3009 return rv;
3010 rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
3011 if (NS_FAILED(rv))
3012 return rv;
3014 return NS_OK;
3016 #endif /* XP_WIN */
3018 nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
3019 nsNPAPIPluginInstance *aInstance,
3020 nsNPAPIPluginStreamListener* aListener,
3021 nsIInputStream *aPostStream,
3022 const char *aHeadersData,
3023 uint32_t aHeadersDataLen)
3025 nsCOMPtr<nsIURI> url;
3026 nsAutoString absUrl;
3027 nsresult rv;
3029 if (aURL.Length() <= 0)
3030 return NS_OK;
3032 // get the base URI for the plugin to create an absolute url
3033 // in case aURL is relative
3034 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
3035 if (owner) {
3036 rv = NS_MakeAbsoluteURI(absUrl, aURL, nsCOMPtr<nsIURI>(owner->GetBaseURI()));
3039 if (absUrl.IsEmpty())
3040 absUrl.Assign(aURL);
3042 rv = NS_NewURI(getter_AddRefs(url), absUrl);
3043 if (NS_FAILED(rv))
3044 return rv;
3046 nsCOMPtr<nsIDOMElement> element;
3047 nsCOMPtr<nsIDocument> doc;
3048 if (owner) {
3049 owner->GetDOMElement(getter_AddRefs(element));
3050 owner->GetDocument(getter_AddRefs(doc));
3052 nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
3054 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
3055 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
3056 url,
3057 principal,
3058 element,
3059 EmptyCString(), //mime guess
3060 nullptr, //extra
3061 &shouldLoad);
3062 if (NS_FAILED(rv))
3063 return rv;
3064 if (NS_CP_REJECTED(shouldLoad)) {
3065 // Disallowed by content policy
3066 return NS_ERROR_CONTENT_BLOCKED;
3069 nsRefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
3070 if (!listenerPeer)
3071 return NS_ERROR_OUT_OF_MEMORY;
3073 rv = listenerPeer->Initialize(url, aInstance, aListener);
3074 if (NS_FAILED(rv))
3075 return rv;
3077 // @arg loadgroup:
3078 // do not add this internal plugin's channel on the
3079 // load group otherwise this channel could be canceled
3080 // form |nsDocShell::OnLinkClickSync| bug 166613
3081 nsCOMPtr<nsIChannel> channel;
3082 nsCOMPtr<nsINode> requestingNode(do_QueryInterface(element));
3083 if (requestingNode) {
3084 rv = NS_NewChannel(getter_AddRefs(channel),
3085 url,
3086 requestingNode,
3087 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
3088 nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
3089 nullptr, // aLoadGroup
3090 listenerPeer);
3092 else {
3093 // in this else branch we really don't know where the load is coming
3094 // from and in fact should use something better than just using
3095 // a nullPrincipal as the loadingPrincipal.
3096 principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
3097 NS_ENSURE_SUCCESS(rv, rv);
3098 rv = NS_NewChannel(getter_AddRefs(channel),
3099 url,
3100 principal,
3101 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
3102 nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
3103 nullptr, // aLoadGroup
3104 listenerPeer);
3107 if (NS_FAILED(rv))
3108 return rv;
3110 if (doc) {
3111 // And if it's a script allow it to execute against the
3112 // document's script context.
3113 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
3114 if (scriptChannel) {
3115 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
3116 // Plug-ins seem to depend on javascript: URIs running synchronously
3117 scriptChannel->SetExecuteAsync(false);
3121 // deal with headers and post data
3122 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
3123 if (httpChannel) {
3124 if (!aPostStream) {
3125 // Only set the Referer header for GET requests because IIS throws
3126 // errors about malformed requests if we include it in POSTs. See
3127 // bug 724465.
3128 nsCOMPtr<nsIURI> referer;
3129 net::ReferrerPolicy referrerPolicy = net::RP_Default;
3131 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
3132 if (olc)
3133 olc->GetSrcURI(getter_AddRefs(referer));
3136 if (!referer) {
3137 if (!doc) {
3138 return NS_ERROR_FAILURE;
3140 referer = doc->GetDocumentURI();
3141 referrerPolicy = doc->GetReferrerPolicy();
3144 rv = httpChannel->SetReferrerWithPolicy(referer, referrerPolicy);
3145 NS_ENSURE_SUCCESS(rv,rv);
3148 if (aPostStream) {
3149 // XXX it's a bit of a hack to rewind the postdata stream
3150 // here but it has to be done in case the post data is
3151 // being reused multiple times.
3152 nsCOMPtr<nsISeekableStream>
3153 postDataSeekable(do_QueryInterface(aPostStream));
3154 if (postDataSeekable)
3155 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
3157 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
3158 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
3160 uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
3163 if (aHeadersData) {
3164 rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
3165 NS_ENSURE_SUCCESS(rv,rv);
3168 rv = channel->AsyncOpen(listenerPeer, nullptr);
3169 if (NS_SUCCEEDED(rv))
3170 listenerPeer->TrackRequest(channel);
3171 return rv;
3174 // Called by GetURL and PostURL
3175 nsresult
3176 nsPluginHost::DoURLLoadSecurityCheck(nsNPAPIPluginInstance *aInstance,
3177 const char* aURL)
3179 if (!aURL || *aURL == '\0')
3180 return NS_OK;
3182 // get the base URI for the plugin element
3183 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
3184 if (!owner)
3185 return NS_ERROR_FAILURE;
3187 nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
3188 if (!baseURI)
3189 return NS_ERROR_FAILURE;
3191 // Create an absolute URL for the target in case the target is relative
3192 nsCOMPtr<nsIURI> targetURL;
3193 NS_NewURI(getter_AddRefs(targetURL), aURL, baseURI);
3194 if (!targetURL)
3195 return NS_ERROR_FAILURE;
3197 nsCOMPtr<nsIDocument> doc;
3198 owner->GetDocument(getter_AddRefs(doc));
3199 if (!doc)
3200 return NS_ERROR_FAILURE;
3202 nsresult rv;
3203 nsCOMPtr<nsIScriptSecurityManager> secMan(
3204 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
3205 if (NS_FAILED(rv))
3206 return rv;
3208 return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL,
3209 nsIScriptSecurityManager::STANDARD);
3213 nsresult
3214 nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
3215 uint32_t aHeadersDataLen,
3216 nsIChannel *aGenericChannel)
3218 nsresult rv = NS_OK;
3220 nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
3221 if (!aChannel) {
3222 return NS_ERROR_NULL_POINTER;
3225 // used during the manipulation of the String from the aHeadersData
3226 nsAutoCString headersString;
3227 nsAutoCString oneHeader;
3228 nsAutoCString headerName;
3229 nsAutoCString headerValue;
3230 int32_t crlf = 0;
3231 int32_t colon = 0;
3233 // Turn the char * buffer into an nsString.
3234 headersString = aHeadersData;
3236 // Iterate over the nsString: for each "\r\n" delimited chunk,
3237 // add the value as a header to the nsIHTTPChannel
3238 while (true) {
3239 crlf = headersString.Find("\r\n", true);
3240 if (-1 == crlf) {
3241 rv = NS_OK;
3242 return rv;
3244 headersString.Mid(oneHeader, 0, crlf);
3245 headersString.Cut(0, crlf + 2);
3246 oneHeader.StripWhitespace();
3247 colon = oneHeader.Find(":");
3248 if (-1 == colon) {
3249 rv = NS_ERROR_NULL_POINTER;
3250 return rv;
3252 oneHeader.Left(headerName, colon);
3253 colon++;
3254 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
3256 // FINALLY: we can set the header!
3258 rv = aChannel->SetRequestHeader(headerName, headerValue, true);
3259 if (NS_FAILED(rv)) {
3260 rv = NS_ERROR_NULL_POINTER;
3261 return rv;
3264 return rv;
3267 nsresult
3268 nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
3270 if (PluginDestructionGuard::DelayDestroy(aInstance)) {
3271 return NS_OK;
3274 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3275 ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
3277 if (aInstance->HasStartedDestroying()) {
3278 return NS_OK;
3281 Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
3282 aInstance->Stop();
3284 // if the instance does not want to be 'cached' just remove it
3285 bool doCache = aInstance->ShouldCache();
3286 if (doCache) {
3287 // try to get the max cached instances from a pref or use default
3288 uint32_t cachedInstanceLimit =
3289 Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
3290 DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
3291 if (StoppedInstanceCount() >= cachedInstanceLimit) {
3292 nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
3293 if (oldestInstance) {
3294 nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
3295 oldestInstance->Destroy();
3296 mInstances.RemoveElement(oldestInstance);
3297 // TODO: Remove this check once bug 752422 was investigated
3298 if (pluginTag) {
3299 OnPluginInstanceDestroyed(pluginTag);
3303 } else {
3304 nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
3305 aInstance->Destroy();
3306 mInstances.RemoveElement(aInstance);
3307 // TODO: Remove this check once bug 752422 was investigated
3308 if (pluginTag) {
3309 OnPluginInstanceDestroyed(pluginTag);
3313 return NS_OK;
3316 nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
3317 nsNPAPIPluginInstance* aInstance,
3318 nsIStreamListener **aStreamListener)
3320 NS_ENSURE_ARG_POINTER(aURI);
3321 NS_ENSURE_ARG_POINTER(aStreamListener);
3323 nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
3324 nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
3325 if (NS_FAILED(rv)) {
3326 return rv;
3329 listener.forget(aStreamListener);
3331 return NS_OK;
3334 void nsPluginHost::CreateWidget(nsPluginInstanceOwner* aOwner)
3336 aOwner->CreateWidget();
3338 // If we've got a native window, the let the plugin know about it.
3339 aOwner->CallSetWindow();
3342 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
3343 const char *aTopic,
3344 const char16_t *someData)
3346 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
3347 OnShutdown();
3348 UnloadPlugins();
3349 sInst->Release();
3351 if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
3352 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
3353 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
3354 // Unload or load plugins as needed
3355 if (mPluginsDisabled) {
3356 UnloadPlugins();
3357 } else {
3358 LoadPlugins();
3361 if (!strcmp("blocklist-updated", aTopic)) {
3362 nsPluginTag* plugin = mPlugins;
3363 while (plugin) {
3364 plugin->InvalidateBlocklistState();
3365 plugin = plugin->mNext;
3368 #ifdef MOZ_WIDGET_ANDROID
3369 if (!strcmp("application-background", aTopic)) {
3370 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3371 mInstances[i]->NotifyForeground(false);
3374 if (!strcmp("application-foreground", aTopic)) {
3375 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3376 if (mInstances[i]->IsOnScreen())
3377 mInstances[i]->NotifyForeground(true);
3380 if (!strcmp("memory-pressure", aTopic)) {
3381 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3382 mInstances[i]->MemoryPressure();
3385 #endif
3386 return NS_OK;
3389 nsresult
3390 nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
3391 char **outPostData, uint32_t *outPostDataLen)
3393 if (!inPostData || !outPostData || !outPostDataLen)
3394 return NS_ERROR_NULL_POINTER;
3396 *outPostData = 0;
3397 *outPostDataLen = 0;
3399 const char CR = '\r';
3400 const char LF = '\n';
3401 const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
3402 const char ContentLenHeader[] = "Content-length";
3404 nsAutoTArray<const char*, 8> singleLF;
3405 const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
3406 const char *pSod = 0; // pointer to start of data in inPostData
3407 const char *pEoh = 0; // pointer to end of headers in inPostData
3408 const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
3409 if (*inPostData == LF) {
3410 // If no custom headers are required, simply add a blank
3411 // line ('\n') to the beginning of the file or buffer.
3412 // so *inPostData == '\n' is valid
3413 pSod = inPostData + 1;
3414 } else {
3415 const char *s = inPostData; //tmp pointer to sourse inPostData
3416 while (s < pEod) {
3417 if (!pSCntlh &&
3418 (*s == 'C' || *s == 'c') &&
3419 (s + sizeof(ContentLenHeader) - 1 < pEod) &&
3420 (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
3422 // lets assume this is ContentLenHeader for now
3423 const char *p = pSCntlh = s;
3424 p += sizeof(ContentLenHeader) - 1;
3425 // search for first CR or LF == end of ContentLenHeader
3426 for (; p < pEod; p++) {
3427 if (*p == CR || *p == LF) {
3428 // got delimiter,
3429 // one more check; if previous char is a digit
3430 // most likely pSCntlh points to the start of ContentLenHeader
3431 if (*(p-1) >= '0' && *(p-1) <= '9') {
3432 s = p;
3434 break; //for loop
3437 if (pSCntlh == s) { // curret ptr is the same
3438 pSCntlh = 0; // that was not ContentLenHeader
3439 break; // there is nothing to parse, break *WHILE LOOP* here
3443 if (*s == CR) {
3444 if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
3445 ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
3446 !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
3448 s += sizeof(CRLFCRLF)-1;
3449 pEoh = pSod = s; // data stars here
3450 break;
3452 } else if (*s == LF) {
3453 if (*(s-1) != CR) {
3454 singleLF.AppendElement(s);
3456 if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
3457 s++;
3458 singleLF.AppendElement(s);
3459 s++;
3460 pEoh = pSod = s; // data stars here
3461 break;
3464 s++;
3468 // deal with output buffer
3469 if (!pSod) { // lets assume whole buffer is a data
3470 pSod = inPostData;
3473 uint32_t newBufferLen = 0;
3474 uint32_t dataLen = pEod - pSod;
3475 uint32_t headersLen = pEoh ? pSod - inPostData : 0;
3477 char *p; // tmp ptr into new output buf
3478 if (headersLen) { // we got a headers
3479 // this function does not make any assumption on correctness
3480 // of ContentLenHeader value in this case.
3482 newBufferLen = dataLen + headersLen;
3483 // in case there were single LFs in headers
3484 // reserve an extra space for CR will be added before each single LF
3485 int cntSingleLF = singleLF.Length();
3486 newBufferLen += cntSingleLF;
3488 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
3489 return NS_ERROR_OUT_OF_MEMORY;
3491 // deal with single LF
3492 const char *s = inPostData;
3493 if (cntSingleLF) {
3494 for (int i=0; i<cntSingleLF; i++) {
3495 const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
3496 int n = plf - s; // bytes to copy
3497 if (n) { // for '\n\n' there is nothing to memcpy
3498 memcpy(p, s, n);
3499 p += n;
3501 *p++ = CR;
3502 s = plf;
3503 *p++ = *s++;
3506 // are we done with headers?
3507 headersLen = pEoh - s;
3508 if (headersLen) { // not yet
3509 memcpy(p, s, headersLen); // copy the rest
3510 p += headersLen;
3512 } else if (dataLen) { // no ContentLenHeader is found but there is a data
3513 // make new output buffer big enough
3514 // to keep ContentLenHeader+value followed by data
3515 uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
3516 newBufferLen = dataLen + l;
3517 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
3518 return NS_ERROR_OUT_OF_MEMORY;
3519 headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
3520 if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
3521 nsMemory::Free(p);
3522 *outPostData = 0;
3523 return NS_ERROR_FAILURE;
3525 p += headersLen;
3526 newBufferLen = headersLen + dataLen;
3528 // at this point we've done with headers.
3529 // there is a possibility that input buffer has only headers info in it
3530 // which already parsed and copied into output buffer.
3531 // copy the data
3532 if (dataLen) {
3533 memcpy(p, pSod, dataLen);
3536 *outPostDataLen = newBufferLen;
3538 return NS_OK;
3541 nsresult
3542 nsPluginHost::CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile)
3544 nsresult rv;
3545 int64_t fileSize;
3546 nsAutoCString filename;
3548 // stat file == get size & convert file:///c:/ to c: if needed
3549 nsCOMPtr<nsIFile> inFile;
3550 rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL),
3551 getter_AddRefs(inFile));
3552 if (NS_FAILED(rv)) {
3553 nsCOMPtr<nsIFile> localFile;
3554 rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false,
3555 getter_AddRefs(localFile));
3556 if (NS_FAILED(rv)) return rv;
3557 inFile = localFile;
3559 rv = inFile->GetFileSize(&fileSize);
3560 if (NS_FAILED(rv)) return rv;
3561 rv = inFile->GetNativePath(filename);
3562 if (NS_FAILED(rv)) return rv;
3564 if (fileSize != 0) {
3565 nsCOMPtr<nsIInputStream> inStream;
3566 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
3567 if (NS_FAILED(rv)) return rv;
3569 // Create a temporary file to write the http Content-length:
3570 // %ld\r\n\" header and "\r\n" == end of headers for post data to
3572 nsCOMPtr<nsIFile> tempFile;
3573 rv = GetPluginTempDir(getter_AddRefs(tempFile));
3574 if (NS_FAILED(rv))
3575 return rv;
3577 nsAutoCString inFileName;
3578 inFile->GetNativeLeafName(inFileName);
3579 // XXX hack around bug 70083
3580 inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
3581 rv = tempFile->AppendNative(inFileName);
3583 if (NS_FAILED(rv))
3584 return rv;
3586 // make it unique, and mode == 0600, not world-readable
3587 rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
3588 if (NS_FAILED(rv))
3589 return rv;
3591 nsCOMPtr<nsIOutputStream> outStream;
3592 if (NS_SUCCEEDED(rv)) {
3593 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
3594 tempFile,
3595 (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
3596 0600); // 600 so others can't read our form data
3598 NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
3599 if (NS_FAILED(rv))
3600 return rv;
3602 char buf[1024];
3603 uint32_t br, bw;
3604 bool firstRead = true;
3605 while (1) {
3606 // Read() mallocs if buffer is null
3607 rv = inStream->Read(buf, 1024, &br);
3608 if (NS_FAILED(rv) || (int32_t)br <= 0)
3609 break;
3610 if (firstRead) {
3611 //"For protocols in which the headers must be distinguished from the body,
3612 // such as HTTP, the buffer or file should contain the headers, followed by
3613 // a blank line, then the body. If no custom headers are required, simply
3614 // add a blank line ('\n') to the beginning of the file or buffer.
3616 char *parsedBuf;
3617 // assuming first 1K (or what we got) has all headers in,
3618 // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders()
3619 ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
3620 rv = outStream->Write(parsedBuf, bw, &br);
3621 nsMemory::Free(parsedBuf);
3622 if (NS_FAILED(rv) || (bw != br))
3623 break;
3625 firstRead = false;
3626 continue;
3628 bw = br;
3629 rv = outStream->Write(buf, bw, &br);
3630 if (NS_FAILED(rv) || (bw != br))
3631 break;
3634 inStream->Close();
3635 outStream->Close();
3636 if (NS_SUCCEEDED(rv))
3637 tempFile.forget(aTmpFile);
3639 return rv;
3642 nsresult
3643 nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
3645 return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
3648 nsresult
3649 nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
3650 const char** aPluginName)
3652 nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
3653 if (!instance)
3654 return NS_ERROR_FAILURE;
3656 nsNPAPIPlugin* plugin = instance->GetPlugin();
3657 if (!plugin)
3658 return NS_ERROR_FAILURE;
3660 *aPluginName = TagForPlugin(plugin)->mName.get();
3662 return NS_OK;
3665 nsresult
3666 nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
3667 nsIPluginTag **aPluginTag)
3669 NS_ENSURE_ARG_POINTER(aPluginInstance);
3670 NS_ENSURE_ARG_POINTER(aPluginTag);
3672 nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
3673 if (!plugin)
3674 return NS_ERROR_FAILURE;
3676 *aPluginTag = TagForPlugin(plugin);
3678 NS_ADDREF(*aPluginTag);
3679 return NS_OK;
3682 NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
3684 nsRefPtr<nsPluginTag> pluginTag = mPlugins;
3685 while (pluginTag) {
3686 if (pluginTag->mUnloadTimer == timer) {
3687 if (!IsRunningPlugin(pluginTag)) {
3688 pluginTag->TryUnloadPlugin(false);
3690 return NS_OK;
3692 pluginTag = pluginTag->mNext;
3695 return NS_ERROR_FAILURE;
3698 #ifdef XP_WIN
3699 // Re-enable any top level browser windows that were disabled by modal dialogs
3700 // displayed by the crashed plugin.
3701 static void
3702 CheckForDisabledWindows()
3704 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
3705 if (!wm)
3706 return;
3708 nsCOMPtr<nsISimpleEnumerator> windowList;
3709 wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
3710 if (!windowList)
3711 return;
3713 bool haveWindows;
3714 do {
3715 windowList->HasMoreElements(&haveWindows);
3716 if (!haveWindows)
3717 return;
3719 nsCOMPtr<nsISupports> supportsWindow;
3720 windowList->GetNext(getter_AddRefs(supportsWindow));
3721 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
3722 if (baseWin) {
3723 nsCOMPtr<nsIWidget> widget;
3724 baseWin->GetMainWidget(getter_AddRefs(widget));
3725 if (widget && !widget->GetParent() &&
3726 widget->IsVisible() &&
3727 !widget->IsEnabled()) {
3728 nsIWidget* child = widget->GetFirstChild();
3729 bool enable = true;
3730 while (child) {
3731 if (child->WindowType() == eWindowType_dialog) {
3732 enable = false;
3733 break;
3735 child = child->GetNextSibling();
3737 if (enable) {
3738 widget->Enable(true);
3742 } while (haveWindows);
3744 #endif
3746 void
3747 nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
3748 const nsAString& pluginDumpID,
3749 const nsAString& browserDumpID)
3751 nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
3753 // Notify the app's observer that a plugin crashed so it can submit
3754 // a crashreport.
3755 bool submittedCrashReport = false;
3756 nsCOMPtr<nsIObserverService> obsService =
3757 mozilla::services::GetObserverService();
3758 nsCOMPtr<nsIWritablePropertyBag2> propbag =
3759 do_CreateInstance("@mozilla.org/hash-property-bag;1");
3760 if (obsService && propbag) {
3761 propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
3762 pluginDumpID);
3763 propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
3764 browserDumpID);
3765 propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3766 submittedCrashReport);
3767 obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
3768 // see if an observer submitted a crash report.
3769 propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3770 &submittedCrashReport);
3773 // Invalidate each nsPluginInstanceTag for the crashed plugin
3775 for (uint32_t i = mInstances.Length(); i > 0; i--) {
3776 nsNPAPIPluginInstance* instance = mInstances[i - 1];
3777 if (instance->GetPlugin() == aPlugin) {
3778 // notify the content node (nsIObjectLoadingContent) that the
3779 // plugin has crashed
3780 nsCOMPtr<nsIDOMElement> domElement;
3781 instance->GetDOMElement(getter_AddRefs(domElement));
3782 nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
3783 if (objectContent) {
3784 objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
3785 submittedCrashReport);
3788 instance->Destroy();
3789 mInstances.RemoveElement(instance);
3790 OnPluginInstanceDestroyed(crashedPluginTag);
3794 // Only after all instances have been invalidated is it safe to null
3795 // out nsPluginTag.mPlugin. The next time we try to create an
3796 // instance of this plugin we reload it (launch a new plugin process).
3798 crashedPluginTag->mPlugin = nullptr;
3799 crashedPluginTag->mContentProcessRunningCount = 0;
3801 #ifdef XP_WIN
3802 CheckForDisabledWindows();
3803 #endif
3806 nsNPAPIPluginInstance*
3807 nsPluginHost::FindInstance(const char *mimetype)
3809 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3810 nsNPAPIPluginInstance* instance = mInstances[i];
3812 const char* mt;
3813 nsresult rv = instance->GetMIMEType(&mt);
3814 if (NS_FAILED(rv))
3815 continue;
3817 if (PL_strcasecmp(mt, mimetype) == 0)
3818 return instance;
3821 return nullptr;
3824 nsNPAPIPluginInstance*
3825 nsPluginHost::FindOldestStoppedInstance()
3827 nsNPAPIPluginInstance *oldestInstance = nullptr;
3828 TimeStamp oldestTime = TimeStamp::Now();
3829 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3830 nsNPAPIPluginInstance *instance = mInstances[i];
3831 if (instance->IsRunning())
3832 continue;
3834 TimeStamp time = instance->StopTime();
3835 if (time < oldestTime) {
3836 oldestTime = time;
3837 oldestInstance = instance;
3841 return oldestInstance;
3844 uint32_t
3845 nsPluginHost::StoppedInstanceCount()
3847 uint32_t stoppedCount = 0;
3848 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3849 nsNPAPIPluginInstance *instance = mInstances[i];
3850 if (!instance->IsRunning())
3851 stoppedCount++;
3853 return stoppedCount;
3856 nsTArray< nsRefPtr<nsNPAPIPluginInstance> >*
3857 nsPluginHost::InstanceArray()
3859 return &mInstances;
3862 void
3863 nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
3865 for (int32_t i = mInstances.Length(); i > 0; i--) {
3866 nsNPAPIPluginInstance *instance = mInstances[i - 1];
3867 if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
3868 instance->SetWindow(nullptr);
3869 instance->Stop();
3871 // Get rid of all the instances without the possibility of caching.
3872 nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
3873 instance->SetWindow(nullptr);
3875 nsCOMPtr<nsIDOMElement> domElement;
3876 instance->GetDOMElement(getter_AddRefs(domElement));
3877 nsCOMPtr<nsIObjectLoadingContent> objectContent =
3878 do_QueryInterface(domElement);
3880 instance->Destroy();
3882 mInstances.RemoveElement(instance);
3883 OnPluginInstanceDestroyed(pluginTag);
3885 // Notify owning content that we destroyed its plugin out from under it
3886 if (objectContent) {
3887 objectContent->PluginDestroyed();
3893 // Runnable that does an async destroy of a plugin.
3895 class nsPluginDestroyRunnable : public nsRunnable,
3896 public PRCList
3898 public:
3899 explicit nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
3900 : mInstance(aInstance)
3902 PR_INIT_CLIST(this);
3903 PR_APPEND_LINK(this, &sRunnableListHead);
3906 virtual ~nsPluginDestroyRunnable()
3908 PR_REMOVE_LINK(this);
3911 NS_IMETHOD Run()
3913 nsRefPtr<nsNPAPIPluginInstance> instance;
3915 // Null out mInstance to make sure this code in another runnable
3916 // will do the right thing even if someone was holding on to this
3917 // runnable longer than we expect.
3918 instance.swap(mInstance);
3920 if (PluginDestructionGuard::DelayDestroy(instance)) {
3921 // It's still not safe to destroy the plugin, it's now up to the
3922 // outermost guard on the stack to take care of the destruction.
3923 return NS_OK;
3926 nsPluginDestroyRunnable *r =
3927 static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
3929 while (r != &sRunnableListHead) {
3930 if (r != this && r->mInstance == instance) {
3931 // There's another runnable scheduled to tear down
3932 // instance. Let it do the job.
3933 return NS_OK;
3935 r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
3938 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3939 ("Doing delayed destroy of instance %p\n", instance.get()));
3941 nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
3942 if (host)
3943 host->StopPluginInstance(instance);
3945 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3946 ("Done with delayed destroy of instance %p\n", instance.get()));
3948 return NS_OK;
3951 protected:
3952 nsRefPtr<nsNPAPIPluginInstance> mInstance;
3954 static PRCList sRunnableListHead;
3957 PRCList nsPluginDestroyRunnable::sRunnableListHead =
3958 PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
3960 PRCList PluginDestructionGuard::sListHead =
3961 PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
3963 PluginDestructionGuard::PluginDestructionGuard(nsNPAPIPluginInstance *aInstance)
3964 : mInstance(aInstance)
3966 Init();
3969 PluginDestructionGuard::PluginDestructionGuard(PluginAsyncSurrogate *aSurrogate)
3970 : mInstance(static_cast<nsNPAPIPluginInstance*>(aSurrogate->GetNPP()->ndata))
3972 InitAsync();
3975 PluginDestructionGuard::PluginDestructionGuard(NPP npp)
3976 : mInstance(npp ? static_cast<nsNPAPIPluginInstance*>(npp->ndata) : nullptr)
3978 Init();
3981 PluginDestructionGuard::~PluginDestructionGuard()
3983 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
3985 PR_REMOVE_LINK(this);
3987 if (mDelayedDestroy) {
3988 // We've attempted to destroy the plugin instance we're holding on
3989 // to while we were guarding it. Do the actual destroy now, off of
3990 // a runnable.
3991 nsRefPtr<nsPluginDestroyRunnable> evt =
3992 new nsPluginDestroyRunnable(mInstance);
3994 NS_DispatchToMainThread(evt);
3998 // static
3999 bool
4000 PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
4002 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
4003 NS_ASSERTION(aInstance, "Uh, I need an instance!");
4005 // Find the first guard on the stack and make it do a delayed
4006 // destroy upon destruction.
4008 PluginDestructionGuard *g =
4009 static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
4011 while (g != &sListHead) {
4012 if (g->mInstance == aInstance) {
4013 g->mDelayedDestroy = true;
4015 return true;
4017 g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
4020 return false;