Bug 882339 - Cache the blocklist state on plugin tags to avoid querying the blocklist...
[gecko.git] / dom / plugins / base / nsPluginHost.cpp
blob09f9975a80d033ee9e328d608a0d0288d34419c4
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 "nsIComponentManager.h"
16 #include "nsNPAPIPlugin.h"
17 #include "nsNPAPIPluginStreamListener.h"
18 #include "nsNPAPIPluginInstance.h"
19 #include "nsPluginInstanceOwner.h"
20 #include "nsObjectLoadingContent.h"
21 #include "nsIHTTPHeaderListener.h"
22 #include "nsIHttpHeaderVisitor.h"
23 #include "nsIObserverService.h"
24 #include "nsIHttpProtocolHandler.h"
25 #include "nsIHttpChannel.h"
26 #include "nsIHttpChannelInternal.h"
27 #include "nsIUploadChannel.h"
28 #include "nsIByteRangeRequest.h"
29 #include "nsIStreamListener.h"
30 #include "nsIInputStream.h"
31 #include "nsIOutputStream.h"
32 #include "nsIURL.h"
33 #include "nsTArray.h"
34 #include "nsReadableUtils.h"
35 #include "nsIProtocolProxyService2.h"
36 #include "nsIStreamConverterService.h"
37 #include "nsIFile.h"
38 #if defined(XP_MACOSX)
39 #include "nsILocalFileMac.h"
40 #endif
41 #include "nsIInputStream.h"
42 #include "nsIIOService.h"
43 #include "nsIURL.h"
44 #include "nsIChannel.h"
45 #include "nsISeekableStream.h"
46 #include "nsNetUtil.h"
47 #include "nsIProgressEventSink.h"
48 #include "nsIDocument.h"
49 #include "nsICachingChannel.h"
50 #include "nsHashtable.h"
51 #include "nsIProxyInfo.h"
52 #include "nsPluginLogging.h"
53 #include "nsIScriptChannel.h"
54 #include "nsIBlocklistService.h"
55 #include "nsVersionComparator.h"
56 #include "nsIObjectLoadingContent.h"
57 #include "nsIWritablePropertyBag2.h"
58 #include "nsICategoryManager.h"
59 #include "nsPluginStreamListenerPeer.h"
60 #include "mozilla/Preferences.h"
62 #include "nsEnumeratorUtils.h"
63 #include "nsXPCOM.h"
64 #include "nsXPCOMCID.h"
65 #include "nsISupportsPrimitives.h"
67 #include "nsXULAppAPI.h"
68 #include "nsIXULRuntime.h"
70 // for the dialog
71 #include "nsIWindowWatcher.h"
72 #include "nsIDOMWindow.h"
74 #include "nsIScriptGlobalObject.h"
75 #include "nsIScriptGlobalObjectOwner.h"
76 #include "nsIPrincipal.h"
78 #include "nsNetCID.h"
79 #include "nsIDOMPlugin.h"
80 #include "nsIDOMMimeType.h"
81 #include "nsMimeTypes.h"
82 #include "prprf.h"
83 #include "nsThreadUtils.h"
84 #include "nsIInputStreamTee.h"
85 #include "nsIInterfaceInfoManager.h"
86 #include "xptinfo.h"
88 #include "nsIMIMEService.h"
89 #include "nsCExternalHandlerService.h"
90 #include "nsIFileChannel.h"
92 #include "nsICharsetConverterManager.h"
93 #include "nsIPlatformCharset.h"
95 #include "nsIDirectoryService.h"
96 #include "nsDirectoryServiceDefs.h"
97 #include "nsXULAppAPI.h"
98 #include "nsAppDirectoryServiceDefs.h"
99 #include "nsPluginDirServiceProvider.h"
100 #include "nsError.h"
102 #include "nsUnicharUtils.h"
103 #include "nsPluginManifestLineReader.h"
105 #include "nsIWeakReferenceUtils.h"
106 #include "nsIDOMElement.h"
107 #include "nsIDOMHTMLObjectElement.h"
108 #include "nsIDOMHTMLEmbedElement.h"
109 #include "nsIPresShell.h"
110 #include "nsIWebNavigation.h"
111 #include "nsIDocShell.h"
112 #include "nsPluginNativeWindow.h"
113 #include "nsIScriptSecurityManager.h"
114 #include "nsIContentPolicy.h"
115 #include "nsContentPolicyUtils.h"
116 #include "mozilla/TimeStamp.h"
117 #include "mozilla/Telemetry.h"
118 #include "nsIImageLoadingContent.h"
119 #include "mozilla/Preferences.h"
120 #include "nsVersionComparator.h"
122 #if defined(XP_WIN)
123 #include "nsIWindowMediator.h"
124 #include "nsIBaseWindow.h"
125 #include "windows.h"
126 #include "winbase.h"
127 #endif
129 #ifdef ANDROID
130 #include <android/log.h>
131 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
132 #endif
134 #if MOZ_CRASHREPORTER
135 #include "nsExceptionHandler.h"
136 #endif
138 using namespace mozilla;
139 using mozilla::TimeStamp;
141 // Null out a strong ref to a linked list iteratively to avoid
142 // exhausting the stack (bug 486349).
143 #define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
145 while (list_) { \
146 type_ temp = list_->mNext_; \
147 list_->mNext_ = nullptr; \
148 list_ = temp; \
152 // this is the name of the directory which will be created
153 // to cache temporary files.
154 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
156 static const char *kPrefWhitelist = "plugin.allowed_types";
157 static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
159 // Version of cached plugin info
160 // 0.01 first implementation
161 // 0.02 added caching of CanUnload to fix bug 105935
162 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
163 // 0.04 added new mime entry point on Mac, bug 113464
164 // 0.05 added new entry point check for the default plugin, bug 132430
165 // 0.06 strip off suffixes in mime description strings, bug 53895
166 // 0.07 changed nsIRegistry to flat file support for caching plugins info
167 // 0.08 mime entry point on MachO, bug 137535
168 // 0.09 the file encoding is changed to UTF-8, bug 420285
169 // 0.10 added plugin versions on appropriate platforms, bug 427743
170 // 0.11 file name and full path fields now store expected values on all platforms, bug 488181
171 // 0.12 force refresh due to quicktime pdf claim fix, bug 611197
172 // 0.13 add architecture and list of invalid plugins, bug 616271
173 // 0.14 force refresh due to locale comparison fix, bug 611296
174 // 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
175 // 0.16 version bump to avoid importing the plugin flags in newer versions
176 // The current plugin registry version (and the maximum version we know how to read)
177 static const char *kPluginRegistryVersion = "0.16";
178 // The minimum registry version we know how to read
179 static const char *kMinimumRegistryVersion = "0.9";
181 static NS_DEFINE_IID(kIPluginTagInfoIID, NS_IPLUGINTAGINFO_IID);
182 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
184 // Registry keys for caching plugin info
185 static const char kPluginsRootKey[] = "software/plugins";
186 static const char kPluginsNameKey[] = "name";
187 static const char kPluginsDescKey[] = "description";
188 static const char kPluginsFilenameKey[] = "filename";
189 static const char kPluginsFullpathKey[] = "fullpath";
190 static const char kPluginsModTimeKey[] = "lastModTimeStamp";
191 static const char kPluginsCanUnload[] = "canUnload";
192 static const char kPluginsVersionKey[] = "version";
193 static const char kPluginsMimeTypeKey[] = "mimetype";
194 static const char kPluginsMimeDescKey[] = "description";
195 static const char kPluginsMimeExtKey[] = "extension";
197 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
199 #ifdef PLUGIN_LOGGING
200 PRLogModuleInfo* nsPluginLogging::gNPNLog = nullptr;
201 PRLogModuleInfo* nsPluginLogging::gNPPLog = nullptr;
202 PRLogModuleInfo* nsPluginLogging::gPluginLog = nullptr;
203 #endif
205 // #defines for plugin cache and prefs
206 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
207 // Raise this from '10' to '50' to work around a bug in Apple's current Java
208 // plugins on OS X Lion and SnowLeopard. See bug 705931.
209 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
211 nsIFile *nsPluginHost::sPluginTempDir;
212 nsPluginHost *nsPluginHost::sInst;
214 NS_IMPL_ISUPPORTS0(nsInvalidPluginTag)
216 nsInvalidPluginTag::nsInvalidPluginTag(const char* aFullPath, int64_t aLastModifiedTime)
217 : mFullPath(aFullPath),
218 mLastModifiedTime(aLastModifiedTime),
219 mSeen(false)
222 nsInvalidPluginTag::~nsInvalidPluginTag()
225 // Helper to check for a MIME in a comma-delimited preference
226 static bool
227 IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
229 nsAutoCString searchStr;
230 searchStr.Assign(',');
231 searchStr.Append(aTypeList);
232 searchStr.Append(',');
234 nsACString::const_iterator start, end;
236 searchStr.BeginReading(start);
237 searchStr.EndReading(end);
239 nsAutoCString commaSeparated;
240 commaSeparated.Assign(',');
241 commaSeparated += aMimeType;
242 commaSeparated.Append(',');
244 return FindInReadable(commaSeparated, start, end);
247 // flat file reg funcs
248 static
249 bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
251 do {
252 if (*reader.LinePtr() == '[') {
253 char* p = reader.LinePtr() + (reader.LineLength() - 1);
254 if (*p != ']')
255 break;
256 *p = 0;
258 char* values[1];
259 if (1 != reader.ParseLine(values, 1))
260 break;
261 // ignore the leading '['
262 if (PL_strcmp(values[0]+1, token)) {
263 break; // it's wrong token
265 return true;
267 } while (reader.NextLine());
268 return false;
271 static bool UnloadPluginsASAP()
273 return Preferences::GetBool("dom.ipc.plugins.unloadASAP", false);
276 nsPluginHost::nsPluginHost()
277 // No need to initialize members to nullptr, false etc because this class
278 // has a zeroing operator new.
280 // check to see if pref is set at startup to let plugins take over in
281 // full page mode for certain image mime types that we handle internally
282 mOverrideInternalTypes =
283 Preferences::GetBool("plugin.override_internal_types", false);
285 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
286 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
288 Preferences::AddStrongObserver(this, "plugin.disable");
289 Preferences::AddStrongObserver(this, "plugins.click_to_play");
291 nsCOMPtr<nsIObserverService> obsService =
292 mozilla::services::GetObserverService();
293 if (obsService) {
294 obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
295 obsService->AddObserver(this, "blocklist-updated", false);
296 #ifdef MOZ_WIDGET_ANDROID
297 obsService->AddObserver(this, "application-foreground", false);
298 obsService->AddObserver(this, "application-background", false);
299 #endif
302 #ifdef PLUGIN_LOGGING
303 nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
304 nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
305 nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
307 PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
308 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
309 PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
311 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
312 PR_LogFlush();
313 #endif
316 nsPluginHost::~nsPluginHost()
318 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
320 UnloadPlugins();
321 sInst = nullptr;
324 NS_IMPL_ISUPPORTS4(nsPluginHost,
325 nsIPluginHost,
326 nsIObserver,
327 nsITimerCallback,
328 nsISupportsWeakReference)
330 already_AddRefed<nsPluginHost>
331 nsPluginHost::GetInst()
333 if (!sInst) {
334 sInst = new nsPluginHost();
335 if (!sInst)
336 return nullptr;
337 NS_ADDREF(sInst);
340 nsRefPtr<nsPluginHost> inst = sInst;
341 return inst.forget();
344 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
346 if (!aPluginTag || !aPluginTag->mPlugin) {
347 return false;
350 for (uint32_t i = 0; i < mInstances.Length(); i++) {
351 nsNPAPIPluginInstance *instance = mInstances[i].get();
352 if (instance &&
353 instance->GetPlugin() == aPluginTag->mPlugin &&
354 instance->IsRunning()) {
355 return true;
359 return false;
362 nsresult nsPluginHost::ReloadPlugins()
364 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
365 ("nsPluginHost::ReloadPlugins Begin\n"));
367 nsresult rv = NS_OK;
369 // this will create the initial plugin list out of cache
370 // if it was not created yet
371 if (!mPluginsLoaded)
372 return LoadPlugins();
374 // we are re-scanning plugins. New plugins may have been added, also some
375 // plugins may have been removed, so we should probably shut everything down
376 // but don't touch running (active and not stopped) plugins
378 // check if plugins changed, no need to do anything else
379 // if no changes to plugins have been made
380 // false instructs not to touch the plugin list, just to
381 // look for possible changes
382 bool pluginschanged = true;
383 FindPlugins(false, &pluginschanged);
385 // if no changed detected, return an appropriate error code
386 if (!pluginschanged)
387 return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
389 // shutdown plugins and kill the list if there are no running plugins
390 nsRefPtr<nsPluginTag> prev;
391 nsRefPtr<nsPluginTag> next;
393 for (nsRefPtr<nsPluginTag> p = mPlugins; p != nullptr;) {
394 next = p->mNext;
396 // only remove our plugin from the list if it's not running.
397 if (!IsRunningPlugin(p)) {
398 if (p == mPlugins)
399 mPlugins = next;
400 else
401 prev->mNext = next;
403 p->mNext = nullptr;
405 // attempt to unload plugins whenever they are removed from the list
406 p->TryUnloadPlugin(false);
408 p = next;
409 continue;
412 prev = p;
413 p = next;
416 // set flags
417 mPluginsLoaded = false;
419 // load them again
420 rv = LoadPlugins();
422 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
423 ("nsPluginHost::ReloadPlugins End\n"));
425 return rv;
428 #define NS_RETURN_UASTRING_SIZE 128
430 nsresult nsPluginHost::UserAgent(const char **retstring)
432 static char resultString[NS_RETURN_UASTRING_SIZE];
433 nsresult res;
435 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
436 if (NS_FAILED(res))
437 return res;
439 nsAutoCString uaString;
440 res = http->GetUserAgent(uaString);
442 if (NS_SUCCEEDED(res)) {
443 if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
444 PL_strcpy(resultString, uaString.get());
445 } else {
446 // Copy as much of UA string as we can (terminate at right-most space).
447 PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
448 for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
449 if (i == 0) {
450 resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
452 else if (resultString[i] == ' ') {
453 resultString[i] = '\0';
454 break;
458 *retstring = resultString;
460 else {
461 *retstring = nullptr;
464 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UserAgent return=%s\n", *retstring));
466 return res;
469 nsresult nsPluginHost::GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt)
471 nsresult rv;
472 nsCOMPtr<nsIPrompt> prompt;
473 nsCOMPtr<nsIWindowWatcher> wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
475 if (wwatch) {
476 nsCOMPtr<nsIDOMWindow> domWindow;
477 if (aOwner) {
478 nsCOMPtr<nsIDocument> document;
479 aOwner->GetDocument(getter_AddRefs(document));
480 if (document) {
481 domWindow = document->GetWindow();
485 if (!domWindow) {
486 wwatch->GetWindowByName(NS_LITERAL_STRING("_content").get(), nullptr, getter_AddRefs(domWindow));
488 rv = wwatch->GetNewPrompter(domWindow, getter_AddRefs(prompt));
491 NS_IF_ADDREF(*aPrompt = prompt);
492 return rv;
495 nsresult nsPluginHost::GetURL(nsISupports* pluginInst,
496 const char* url,
497 const char* target,
498 nsNPAPIPluginStreamListener* streamListener,
499 const char* altHost,
500 const char* referrer,
501 bool forceJSEnabled)
503 return GetURLWithHeaders(static_cast<nsNPAPIPluginInstance*>(pluginInst),
504 url, target, streamListener, altHost, referrer,
505 forceJSEnabled, 0, nullptr);
508 nsresult nsPluginHost::GetURLWithHeaders(nsNPAPIPluginInstance* pluginInst,
509 const char* url,
510 const char* target,
511 nsNPAPIPluginStreamListener* streamListener,
512 const char* altHost,
513 const char* referrer,
514 bool forceJSEnabled,
515 uint32_t getHeadersLength,
516 const char* getHeaders)
518 // we can only send a stream back to the plugin (as specified by a
519 // null target) if we also have a nsNPAPIPluginStreamListener to talk to
520 if (!target && !streamListener)
521 return NS_ERROR_ILLEGAL_VALUE;
523 nsresult rv = DoURLLoadSecurityCheck(pluginInst, url);
524 if (NS_FAILED(rv))
525 return rv;
527 if (target) {
528 nsRefPtr<nsPluginInstanceOwner> owner = pluginInst->GetOwner();
529 if (owner) {
530 if ((0 == PL_strcmp(target, "newwindow")) ||
531 (0 == PL_strcmp(target, "_new")))
532 target = "_blank";
533 else if (0 == PL_strcmp(target, "_current"))
534 target = "_self";
536 rv = owner->GetURL(url, target, nullptr, nullptr, 0);
540 if (streamListener)
541 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), pluginInst,
542 streamListener, nullptr,
543 getHeaders, getHeadersLength);
545 return rv;
548 nsresult nsPluginHost::PostURL(nsISupports* pluginInst,
549 const char* url,
550 uint32_t postDataLen,
551 const char* postData,
552 bool isFile,
553 const char* target,
554 nsNPAPIPluginStreamListener* streamListener,
555 const char* altHost,
556 const char* referrer,
557 bool forceJSEnabled,
558 uint32_t postHeadersLength,
559 const char* postHeaders)
561 nsresult rv;
563 // we can only send a stream back to the plugin (as specified
564 // by a null target) if we also have a nsNPAPIPluginStreamListener
565 // to talk to also
566 if (!target && !streamListener)
567 return NS_ERROR_ILLEGAL_VALUE;
569 nsNPAPIPluginInstance* instance = static_cast<nsNPAPIPluginInstance*>(pluginInst);
571 rv = DoURLLoadSecurityCheck(instance, url);
572 if (NS_FAILED(rv))
573 return rv;
575 nsCOMPtr<nsIInputStream> postStream;
576 if (isFile) {
577 nsCOMPtr<nsIFile> file;
578 rv = CreateTempFileToPost(postData, getter_AddRefs(file));
579 if (NS_FAILED(rv))
580 return rv;
582 nsCOMPtr<nsIInputStream> fileStream;
583 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream),
584 file,
585 PR_RDONLY,
586 0600,
587 nsIFileInputStream::DELETE_ON_CLOSE |
588 nsIFileInputStream::CLOSE_ON_EOF);
589 if (NS_FAILED(rv))
590 return rv;
592 rv = NS_NewBufferedInputStream(getter_AddRefs(postStream), fileStream, 8192);
593 if (NS_FAILED(rv))
594 return rv;
595 } else {
596 char *dataToPost;
597 uint32_t newDataToPostLen;
598 ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
599 if (!dataToPost)
600 return NS_ERROR_UNEXPECTED;
602 nsCOMPtr<nsIStringInputStream> sis = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
603 if (!sis) {
604 NS_Free(dataToPost);
605 return rv;
608 // data allocated by ParsePostBufferToFixHeaders() is managed and
609 // freed by the string stream.
610 postDataLen = newDataToPostLen;
611 sis->AdoptData(dataToPost, postDataLen);
612 postStream = sis;
615 if (target) {
616 nsRefPtr<nsPluginInstanceOwner> owner = instance->GetOwner();
617 if (owner) {
618 if ((0 == PL_strcmp(target, "newwindow")) ||
619 (0 == PL_strcmp(target, "_new"))) {
620 target = "_blank";
621 } else if (0 == PL_strcmp(target, "_current")) {
622 target = "_self";
624 rv = owner->GetURL(url, target, postStream,
625 (void*)postHeaders, postHeadersLength);
629 // if we don't have a target, just create a stream. This does
630 // NS_OpenURI()!
631 if (streamListener)
632 rv = NewPluginURLStream(NS_ConvertUTF8toUTF16(url), instance,
633 streamListener,
634 postStream, postHeaders, postHeadersLength);
636 return rv;
639 /* This method queries the prefs for proxy information.
640 * It has been tested and is known to work in the following three cases
641 * when no proxy host or port is specified
642 * when only the proxy host is specified
643 * when only the proxy port is specified
644 * This method conforms to the return code specified in
645 * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
646 * with the exception that multiple values are not implemented.
649 nsresult nsPluginHost::FindProxyForURL(const char* url, char* *result)
651 if (!url || !result) {
652 return NS_ERROR_INVALID_ARG;
654 nsresult res;
656 nsCOMPtr<nsIURI> uriIn;
657 nsCOMPtr<nsIProtocolProxyService> proxyService;
658 nsCOMPtr<nsIProtocolProxyService2> proxyService2;
659 nsCOMPtr<nsIIOService> ioService;
661 proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
662 if (NS_FAILED(res) || !proxyService)
663 return res;
665 proxyService2 = do_QueryInterface(proxyService, &res);
666 if (NS_FAILED(res) || !proxyService2)
667 return res;
669 ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
670 if (NS_FAILED(res) || !ioService)
671 return res;
673 // make an nsURI from the argument url
674 res = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
675 if (NS_FAILED(res))
676 return res;
678 nsCOMPtr<nsIProxyInfo> pi;
680 // Remove this with bug 778201
681 res = proxyService2->DeprecatedBlockingResolve(uriIn, 0, getter_AddRefs(pi));
682 if (NS_FAILED(res))
683 return res;
685 nsAutoCString host, type;
686 int32_t port = -1;
688 // These won't fail, and even if they do... we'll be ok.
689 if (pi) {
690 pi->GetType(type);
691 pi->GetHost(host);
692 pi->GetPort(&port);
695 if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
696 *result = PL_strdup("DIRECT");
697 } else if (type.EqualsLiteral("http")) {
698 *result = PR_smprintf("PROXY %s:%d", host.get(), port);
699 } else if (type.EqualsLiteral("socks4")) {
700 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
701 } else if (type.EqualsLiteral("socks")) {
702 // XXX - this is socks5, but there is no API for us to tell the
703 // plugin that fact. SOCKS for now, in case the proxy server
704 // speaks SOCKS4 as well. See bug 78176
705 // For a long time this was returning an http proxy type, so
706 // very little is probably broken by this
707 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
708 } else {
709 NS_ASSERTION(false, "Unknown proxy type!");
710 *result = PL_strdup("DIRECT");
713 if (nullptr == *result)
714 res = NS_ERROR_OUT_OF_MEMORY;
716 return res;
719 nsresult nsPluginHost::Init()
721 return NS_OK;
724 nsresult nsPluginHost::UnloadPlugins()
726 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHost::UnloadPlugins Called\n"));
728 if (!mPluginsLoaded)
729 return NS_OK;
731 // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
732 // for those plugins who want it
733 DestroyRunningInstances(nullptr);
735 nsPluginTag *pluginTag;
736 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
737 pluginTag->TryUnloadPlugin(true);
740 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mPlugins, mNext);
741 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
742 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
744 // Lets remove any of the temporary files that we created.
745 if (sPluginTempDir) {
746 sPluginTempDir->Remove(true);
747 NS_RELEASE(sPluginTempDir);
750 #ifdef XP_WIN
751 if (mPrivateDirServiceProvider) {
752 nsCOMPtr<nsIDirectoryService> dirService =
753 do_GetService(kDirectoryServiceContractID);
754 if (dirService)
755 dirService->UnregisterProvider(mPrivateDirServiceProvider);
756 mPrivateDirServiceProvider = nullptr;
758 #endif /* XP_WIN */
760 mPluginsLoaded = false;
762 return NS_OK;
765 void nsPluginHost::OnPluginInstanceDestroyed(nsPluginTag* aPluginTag)
767 bool hasInstance = false;
768 for (uint32_t i = 0; i < mInstances.Length(); i++) {
769 if (TagForPlugin(mInstances[i]->GetPlugin()) == aPluginTag) {
770 hasInstance = true;
771 break;
775 // We have some options for unloading plugins if they have no instances.
777 // Unloading plugins immediately can be bad - some plugins retain state
778 // between instances even when there are none. This is largely limited to
779 // going from one page to another, so state is retained without an instance
780 // for only a very short period of time. In order to allow this to work
781 // we don't unload plugins immediately by default. This is supported
782 // via a hidden user pref though.
784 // Another reason not to unload immediately is that loading is expensive,
785 // and it is better to leave popular plugins loaded.
787 // Our default behavior is to try to unload a plugin three minutes after
788 // its last instance is destroyed. This seems like a reasonable compromise
789 // that allows us to reclaim memory while allowing short state retention
790 // and avoid perf hits for loading popular plugins.
791 if (!hasInstance) {
792 if (UnloadPluginsASAP()) {
793 aPluginTag->TryUnloadPlugin(false);
794 } else {
795 if (aPluginTag->mUnloadTimer) {
796 aPluginTag->mUnloadTimer->Cancel();
797 } else {
798 aPluginTag->mUnloadTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
800 aPluginTag->mUnloadTimer->InitWithCallback(this, 1000 * 60 * 3, nsITimer::TYPE_ONE_SHOT);
805 nsresult
806 nsPluginHost::GetPluginTempDir(nsIFile **aDir)
808 if (!sPluginTempDir) {
809 nsCOMPtr<nsIFile> tmpDir;
810 nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
811 getter_AddRefs(tmpDir));
812 NS_ENSURE_SUCCESS(rv, rv);
814 rv = tmpDir->AppendNative(kPluginTmpDirName);
816 // make it unique, and mode == 0700, not world-readable
817 rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
818 NS_ENSURE_SUCCESS(rv, rv);
820 tmpDir.swap(sPluginTempDir);
823 return sPluginTempDir->Clone(aDir);
826 nsresult
827 nsPluginHost::InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
828 nsObjectLoadingContent *aContent,
829 nsPluginInstanceOwner** aOwner)
831 NS_ENSURE_ARG_POINTER(aOwner);
833 #ifdef PLUGIN_LOGGING
834 nsAutoCString urlSpec;
835 if (aURL)
836 aURL->GetAsciiSpec(urlSpec);
838 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
839 ("nsPluginHost::InstantiatePlugin Begin mime=%s, url=%s\n",
840 aMimeType, urlSpec.get()));
842 PR_LogFlush();
843 #endif
845 if (!aMimeType) {
846 NS_NOTREACHED("Attempting to spawn a plugin with no mime type");
847 return NS_ERROR_FAILURE;
850 nsRefPtr<nsPluginInstanceOwner> instanceOwner = new nsPluginInstanceOwner();
851 if (!instanceOwner) {
852 return NS_ERROR_OUT_OF_MEMORY;
855 nsCOMPtr<nsIContent> ourContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(aContent));
856 nsresult rv = instanceOwner->Init(ourContent);
857 if (NS_FAILED(rv)) {
858 return rv;
861 nsCOMPtr<nsIPluginTagInfo> pti;
862 rv = instanceOwner->QueryInterface(kIPluginTagInfoIID, getter_AddRefs(pti));
863 if (NS_FAILED(rv)) {
864 return rv;
867 nsPluginTagType tagType;
868 rv = pti->GetTagType(&tagType);
869 if (NS_FAILED(rv)) {
870 return rv;
873 if (tagType != nsPluginTagType_Embed &&
874 tagType != nsPluginTagType_Applet &&
875 tagType != nsPluginTagType_Object) {
876 return NS_ERROR_FAILURE;
879 rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
880 if (NS_FAILED(rv)) {
881 return NS_ERROR_FAILURE;
884 nsRefPtr<nsNPAPIPluginInstance> instance;
885 rv = instanceOwner->GetInstance(getter_AddRefs(instance));
886 if (NS_FAILED(rv)) {
887 return rv;
890 if (instance) {
891 instanceOwner->CreateWidget();
893 // If we've got a native window, the let the plugin know about it.
894 instanceOwner->CallSetWindow();
897 // At this point we consider instantiation to be successful. Do not return an error.
898 instanceOwner.forget(aOwner);
900 #ifdef PLUGIN_LOGGING
901 nsAutoCString urlSpec2;
902 if (aURL != nullptr) aURL->GetAsciiSpec(urlSpec2);
904 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
905 ("nsPluginHost::InstantiatePlugin Finished mime=%s, rv=%d, url=%s\n",
906 aMimeType, rv, urlSpec2.get()));
908 PR_LogFlush();
909 #endif
911 return NS_OK;
914 nsPluginTag*
915 nsPluginHost::FindTagForLibrary(PRLibrary* aLibrary)
917 nsPluginTag* pluginTag;
918 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
919 if (pluginTag->mLibrary == aLibrary) {
920 return pluginTag;
923 return nullptr;
926 nsPluginTag*
927 nsPluginHost::TagForPlugin(nsNPAPIPlugin* aPlugin)
929 nsPluginTag* pluginTag;
930 for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) {
931 if (pluginTag->mPlugin == aPlugin) {
932 return pluginTag;
935 // a plugin should never exist without a corresponding tag
936 NS_ERROR("TagForPlugin has failed");
937 return nullptr;
940 nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType,
941 nsIURI *aURL,
942 nsPluginInstanceOwner *aOwner)
944 NS_ENSURE_ARG_POINTER(aOwner);
946 nsresult rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
947 if (NS_SUCCEEDED(rv)) {
948 return rv;
951 // If we failed to load a plugin instance we'll try again after
952 // reloading our plugin list. Only do that once per document to
953 // avoid redundant high resource usage on pages with multiple
954 // unkown instance types. We'll do that by caching the document.
955 nsCOMPtr<nsIDocument> document;
956 aOwner->GetDocument(getter_AddRefs(document));
958 nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
959 if (document == currentdocument) {
960 return rv;
963 mCurrentDocument = do_GetWeakReference(document);
965 // Don't try to set up an instance again if nothing changed.
966 if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
967 return rv;
970 return TrySetUpPluginInstance(aMimeType, aURL, aOwner);
973 nsresult
974 nsPluginHost::TrySetUpPluginInstance(const char *aMimeType,
975 nsIURI *aURL,
976 nsPluginInstanceOwner *aOwner)
978 #ifdef PLUGIN_LOGGING
979 nsAutoCString urlSpec;
980 if (aURL != nullptr) aURL->GetSpec(urlSpec);
982 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
983 ("nsPluginHost::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
984 aMimeType, aOwner, urlSpec.get()));
986 PR_LogFlush();
987 #endif
989 nsRefPtr<nsNPAPIPlugin> plugin;
990 GetPlugin(aMimeType, getter_AddRefs(plugin));
991 if (!plugin) {
992 return NS_ERROR_FAILURE;
995 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
997 NS_ASSERTION(pluginTag, "Must have plugin tag here!");
999 #if defined(MOZ_WIDGET_ANDROID) && defined(MOZ_CRASHREPORTER)
1000 if (pluginTag->mIsFlashPlugin) {
1001 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FlashVersion"), pluginTag->mVersion);
1003 #endif
1005 nsRefPtr<nsNPAPIPluginInstance> instance = new nsNPAPIPluginInstance();
1007 // This will create the owning reference. The connection must be made between the
1008 // instance and the instance owner before initialization. Plugins can call into
1009 // the browser during initialization.
1010 aOwner->SetInstance(instance.get());
1012 // Add the instance to the instances list before we call NPP_New so that
1013 // it is "in play" before NPP_New happens. Take it out if NPP_New fails.
1014 mInstances.AppendElement(instance.get());
1016 // this should not addref the instance or owner
1017 // except in some cases not Java, see bug 140931
1018 // our COM pointer will free the peer
1019 nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
1020 if (NS_FAILED(rv)) {
1021 mInstances.RemoveElement(instance.get());
1022 aOwner->SetInstance(nullptr);
1023 return rv;
1026 // Cancel the plugin unload timer since we are creating
1027 // an instance for it.
1028 if (pluginTag->mUnloadTimer) {
1029 pluginTag->mUnloadTimer->Cancel();
1032 #ifdef PLUGIN_LOGGING
1033 nsAutoCString urlSpec2;
1034 if (aURL)
1035 aURL->GetSpec(urlSpec2);
1037 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
1038 ("nsPluginHost::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
1039 aMimeType, rv, aOwner, urlSpec2.get()));
1041 PR_LogFlush();
1042 #endif
1044 return rv;
1047 bool
1048 nsPluginHost::PluginExistsForType(const char* aMimeType)
1050 nsPluginTag *plugin = FindPluginForType(aMimeType, false);
1051 return nullptr != plugin;
1054 NS_IMETHODIMP
1055 nsPluginHost::GetStateForType(const nsACString &aMimeType, uint32_t* aResult)
1057 nsPluginTag *plugin = FindPluginForType(aMimeType.Data(), true);
1058 if (!plugin) {
1059 return NS_ERROR_UNEXPECTED;
1062 return plugin->GetEnabledState(aResult);
1065 nsresult
1066 nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
1068 nsPluginTag *plugin = FindPluginForType(aMimeType, true);
1069 if (!plugin) {
1070 plugin = FindPluginForType(aMimeType, false);
1072 if (!plugin) {
1073 return NS_ERROR_FAILURE;
1076 *aState = plugin->GetBlocklistState();
1077 return NS_OK;
1080 NS_IMETHODIMP
1081 nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
1083 aPermissionString.Truncate();
1084 uint32_t blocklistState;
1085 nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
1086 NS_ENSURE_SUCCESS(rv, rv);
1087 nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
1088 if (!tag) {
1089 tag = FindPluginForType(aMimeType.Data(), false);
1091 if (!tag) {
1092 return NS_ERROR_FAILURE;
1095 if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
1096 blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
1097 aPermissionString.AssignLiteral("plugin-vulnerable:");
1099 else {
1100 aPermissionString.AssignLiteral("plugin:");
1103 aPermissionString.Append(tag->GetNiceFileName());
1105 return NS_OK;
1108 // check comma delimitered extensions
1109 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
1111 if (!aExtensionList || !aExtension)
1112 return -1;
1114 const char *pExt = aExtensionList;
1115 const char *pComma = strchr(pExt, ',');
1116 if (!pComma)
1117 return PL_strcasecmp(pExt, aExtension);
1119 int extlen = strlen(aExtension);
1120 while (pComma) {
1121 int length = pComma - pExt;
1122 if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
1123 return 0;
1124 pComma++;
1125 pExt = pComma;
1126 pComma = strchr(pExt, ',');
1129 // the last one
1130 return PL_strcasecmp(pExt, aExtension);
1133 nsresult
1134 nsPluginHost::IsPluginEnabledForExtension(const char* aExtension,
1135 const char* &aMimeType)
1137 nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
1138 if (plugin)
1139 return NS_OK;
1141 return NS_ERROR_FAILURE;
1144 class DOMPluginImpl : public nsIDOMPlugin {
1145 public:
1146 NS_DECL_ISUPPORTS
1148 DOMPluginImpl(nsPluginTag* aPluginTag) : mPluginTag(aPluginTag)
1152 virtual ~DOMPluginImpl() {
1155 NS_METHOD GetDescription(nsAString& aDescription)
1157 CopyUTF8toUTF16(mPluginTag.mDescription, aDescription);
1158 return NS_OK;
1161 NS_METHOD GetFilename(nsAString& aFilename)
1163 CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
1164 return NS_OK;
1167 NS_METHOD GetVersion(nsAString& aVersion)
1169 CopyUTF8toUTF16(mPluginTag.mVersion, aVersion);
1170 return NS_OK;
1173 NS_METHOD GetName(nsAString& aName)
1175 CopyUTF8toUTF16(mPluginTag.mName, aName);
1176 return NS_OK;
1179 NS_METHOD GetLength(uint32_t* aLength)
1181 *aLength = mPluginTag.mMimeTypes.Length();
1182 return NS_OK;
1185 NS_METHOD Item(uint32_t aIndex, nsIDOMMimeType** aReturn)
1187 nsIDOMMimeType* mimeType = new DOMMimeTypeImpl(&mPluginTag, aIndex);
1188 NS_IF_ADDREF(mimeType);
1189 *aReturn = mimeType;
1190 return NS_OK;
1193 NS_METHOD NamedItem(const nsAString& aName, nsIDOMMimeType** aReturn)
1195 for (int i = mPluginTag.mMimeTypes.Length() - 1; i >= 0; --i) {
1196 if (aName.Equals(NS_ConvertUTF8toUTF16(mPluginTag.mMimeTypes[i])))
1197 return Item(i, aReturn);
1199 return NS_OK;
1202 private:
1203 nsPluginTag mPluginTag;
1206 NS_IMPL_ISUPPORTS1(DOMPluginImpl, nsIDOMPlugin)
1208 nsresult
1209 nsPluginHost::GetPluginCount(uint32_t* aPluginCount)
1211 LoadPlugins();
1213 uint32_t count = 0;
1215 nsPluginTag* plugin = mPlugins;
1216 while (plugin != nullptr) {
1217 if (plugin->IsActive()) {
1218 ++count;
1220 plugin = plugin->mNext;
1223 *aPluginCount = count;
1225 return NS_OK;
1228 nsresult
1229 nsPluginHost::GetPlugins(uint32_t aPluginCount, nsIDOMPlugin** aPluginArray)
1231 LoadPlugins();
1233 nsPluginTag* plugin = mPlugins;
1234 for (uint32_t i = 0; i < aPluginCount && plugin; plugin = plugin->mNext) {
1235 if (plugin->IsActive()) {
1236 nsIDOMPlugin* domPlugin = new DOMPluginImpl(plugin);
1237 NS_IF_ADDREF(domPlugin);
1238 aPluginArray[i++] = domPlugin;
1242 return NS_OK;
1245 NS_IMETHODIMP
1246 nsPluginHost::GetPluginTags(uint32_t* aPluginCount, nsIPluginTag*** aResults)
1248 LoadPlugins();
1250 uint32_t count = 0;
1251 nsRefPtr<nsPluginTag> plugin = mPlugins;
1252 while (plugin != nullptr) {
1253 count++;
1254 plugin = plugin->mNext;
1257 *aResults = static_cast<nsIPluginTag**>
1258 (nsMemory::Alloc(count * sizeof(**aResults)));
1259 if (!*aResults)
1260 return NS_ERROR_OUT_OF_MEMORY;
1262 *aPluginCount = count;
1264 plugin = mPlugins;
1265 for (uint32_t i = 0; i < count; i++) {
1266 (*aResults)[i] = plugin;
1267 NS_ADDREF((*aResults)[i]);
1268 plugin = plugin->mNext;
1271 return NS_OK;
1274 nsPluginTag*
1275 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
1277 // We prefer the plugin with the highest version number.
1278 /// XXX(johns): This seems to assume the only time multiple plugins will have
1279 /// the same MIME type is if they're multiple versions of the same
1280 /// plugin -- but since plugin filenames and pretty names can both
1281 /// update, it's probably less arbitrary than just going at it
1282 /// alphabetically.
1284 if (matches.IsEmpty()) {
1285 return nullptr;
1288 nsPluginTag *preferredPlugin = matches[0];
1289 for (unsigned int i = 1; i < matches.Length(); i++) {
1290 if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) {
1291 preferredPlugin = matches[i];
1295 return preferredPlugin;
1298 nsPluginTag*
1299 nsPluginHost::FindPluginForType(const char* aMimeType,
1300 bool aCheckEnabled)
1302 if (!aMimeType) {
1303 return nullptr;
1306 LoadPlugins();
1308 InfallibleTArray<nsPluginTag*> matchingPlugins;
1310 nsPluginTag *plugin = mPlugins;
1311 while (plugin) {
1312 if (!aCheckEnabled || plugin->IsActive()) {
1313 int32_t mimeCount = plugin->mMimeTypes.Length();
1314 for (int32_t i = 0; i < mimeCount; i++) {
1315 if (0 == PL_strcasecmp(plugin->mMimeTypes[i].get(), aMimeType)) {
1316 matchingPlugins.AppendElement(plugin);
1317 break;
1321 plugin = plugin->mNext;
1324 return FindPreferredPlugin(matchingPlugins);
1327 nsPluginTag*
1328 nsPluginHost::FindPluginEnabledForExtension(const char* aExtension,
1329 const char*& aMimeType)
1331 if (!aExtension) {
1332 return nullptr;
1335 LoadPlugins();
1337 InfallibleTArray<nsPluginTag*> matchingPlugins;
1339 nsPluginTag *plugin = mPlugins;
1340 while (plugin) {
1341 if (plugin->IsActive()) {
1342 int32_t variants = plugin->mExtensions.Length();
1343 for (int32_t i = 0; i < variants; i++) {
1344 // mExtensionsArray[cnt] is a list of extensions separated by commas
1345 if (0 == CompareExtensions(plugin->mExtensions[i].get(), aExtension)) {
1346 matchingPlugins.AppendElement(plugin);
1347 break;
1351 plugin = plugin->mNext;
1354 nsPluginTag *preferredPlugin = FindPreferredPlugin(matchingPlugins);
1355 if (!preferredPlugin) {
1356 return nullptr;
1359 int32_t variants = preferredPlugin->mExtensions.Length();
1360 for (int32_t i = 0; i < variants; i++) {
1361 // mExtensionsArray[cnt] is a list of extensions separated by commas
1362 if (0 == CompareExtensions(preferredPlugin->mExtensions[i].get(), aExtension)) {
1363 aMimeType = preferredPlugin->mMimeTypes[i].get();
1364 break;
1368 return preferredPlugin;
1371 static nsresult CreateNPAPIPlugin(nsPluginTag *aPluginTag,
1372 nsNPAPIPlugin **aOutNPAPIPlugin)
1374 // If this is an in-process plugin we'll need to load it here if we haven't already.
1375 if (!nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
1376 if (aPluginTag->mFullPath.IsEmpty())
1377 return NS_ERROR_FAILURE;
1378 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
1379 file->InitWithPath(NS_ConvertUTF8toUTF16(aPluginTag->mFullPath));
1380 nsPluginFile pluginFile(file);
1381 PRLibrary* pluginLibrary = NULL;
1383 if (NS_FAILED(pluginFile.LoadPlugin(&pluginLibrary)) || !pluginLibrary)
1384 return NS_ERROR_FAILURE;
1386 aPluginTag->mLibrary = pluginLibrary;
1389 nsresult rv;
1390 rv = nsNPAPIPlugin::CreatePlugin(aPluginTag, aOutNPAPIPlugin);
1392 return rv;
1395 nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag)
1397 nsRefPtr<nsNPAPIPlugin> plugin = aPluginTag->mPlugin;
1398 if (!plugin) {
1399 nsresult rv = CreateNPAPIPlugin(aPluginTag, getter_AddRefs(plugin));
1400 if (NS_FAILED(rv)) {
1401 return rv;
1403 aPluginTag->mPlugin = plugin;
1405 return NS_OK;
1408 nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
1410 nsresult rv = NS_ERROR_FAILURE;
1411 *aPlugin = NULL;
1413 if (!aMimeType)
1414 return NS_ERROR_ILLEGAL_VALUE;
1416 // If plugins haven't been scanned yet, do so now
1417 LoadPlugins();
1419 nsPluginTag* pluginTag = FindPluginForType(aMimeType, true);
1420 if (pluginTag) {
1421 rv = NS_OK;
1422 PLUGIN_LOG(PLUGIN_LOG_BASIC,
1423 ("nsPluginHost::GetPlugin Begin mime=%s, plugin=%s\n",
1424 aMimeType, pluginTag->mFileName.get()));
1426 #ifdef DEBUG
1427 if (aMimeType && !pluginTag->mFileName.IsEmpty())
1428 printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
1429 #endif
1431 rv = EnsurePluginLoaded(pluginTag);
1432 if (NS_FAILED(rv)) {
1433 return rv;
1436 NS_ADDREF(*aPlugin = pluginTag->mPlugin);
1437 return NS_OK;
1440 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
1441 ("nsPluginHost::GetPlugin End mime=%s, rv=%d, plugin=%p name=%s\n",
1442 aMimeType, rv, *aPlugin,
1443 (pluginTag ? pluginTag->mFileName.get() : "(not found)")));
1445 return rv;
1448 // Normalize 'host' to ACE.
1449 nsresult
1450 nsPluginHost::NormalizeHostname(nsCString& host)
1452 if (IsASCII(host)) {
1453 ToLowerCase(host);
1454 return NS_OK;
1457 if (!mIDNService) {
1458 nsresult rv;
1459 mIDNService = do_GetService(NS_IDNSERVICE_CONTRACTID, &rv);
1460 NS_ENSURE_SUCCESS(rv, rv);
1463 return mIDNService->ConvertUTF8toACE(host, host);
1466 // Enumerate a 'sites' array returned by GetSitesWithData and determine if
1467 // any of them have a base domain in common with 'domain'; if so, append them
1468 // to the 'result' array. If 'firstMatchOnly' is true, return after finding the
1469 // first match.
1470 nsresult
1471 nsPluginHost::EnumerateSiteData(const nsACString& domain,
1472 const InfallibleTArray<nsCString>& sites,
1473 InfallibleTArray<nsCString>& result,
1474 bool firstMatchOnly)
1476 NS_ASSERTION(!domain.IsVoid(), "null domain string");
1478 nsresult rv;
1479 if (!mTLDService) {
1480 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
1481 NS_ENSURE_SUCCESS(rv, rv);
1484 // Get the base domain from the domain.
1485 nsCString baseDomain;
1486 rv = mTLDService->GetBaseDomainFromHost(domain, 0, baseDomain);
1487 bool isIP = rv == NS_ERROR_HOST_IS_IP_ADDRESS;
1488 if (isIP || rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1489 // The base domain is the site itself. However, we must be careful to
1490 // normalize.
1491 baseDomain = domain;
1492 rv = NormalizeHostname(baseDomain);
1493 NS_ENSURE_SUCCESS(rv, rv);
1494 } else if (NS_FAILED(rv)) {
1495 return rv;
1498 // Enumerate the array of sites with data.
1499 for (uint32_t i = 0; i < sites.Length(); ++i) {
1500 const nsCString& site = sites[i];
1502 // Check if the site is an IP address.
1503 bool siteIsIP =
1504 site.Length() >= 2 && site.First() == '[' && site.Last() == ']';
1505 if (siteIsIP != isIP)
1506 continue;
1508 nsCString siteBaseDomain;
1509 if (siteIsIP) {
1510 // Strip the '[]'.
1511 siteBaseDomain = Substring(site, 1, site.Length() - 2);
1512 } else {
1513 // Determine the base domain of the site.
1514 rv = mTLDService->GetBaseDomainFromHost(site, 0, siteBaseDomain);
1515 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
1516 // The base domain is the site itself. However, we must be careful to
1517 // normalize.
1518 siteBaseDomain = site;
1519 rv = NormalizeHostname(siteBaseDomain);
1520 NS_ENSURE_SUCCESS(rv, rv);
1521 } else if (NS_FAILED(rv)) {
1522 return rv;
1526 // At this point, we can do an exact comparison of the two domains.
1527 if (baseDomain != siteBaseDomain) {
1528 continue;
1531 // Append the site to the result array.
1532 result.AppendElement(site);
1534 // If we're supposed to return early, do so.
1535 if (firstMatchOnly) {
1536 break;
1540 return NS_OK;
1543 NS_IMETHODIMP
1544 nsPluginHost::RegisterPlayPreviewMimeType(const nsACString& mimeType,
1545 bool ignoreCTP,
1546 const nsACString& redirectURL)
1548 nsAutoCString mt(mimeType);
1549 nsAutoCString url(redirectURL);
1550 if (url.Length() == 0) {
1551 // using default play preview iframe URL, if redirectURL is not specified
1552 url.Assign("data:application/x-moz-playpreview;,");
1553 url.Append(mimeType);
1556 nsRefPtr<nsPluginPlayPreviewInfo> playPreview =
1557 new nsPluginPlayPreviewInfo(mt.get(), ignoreCTP, url.get());
1558 mPlayPreviewMimeTypes.AppendElement(playPreview);
1559 return NS_OK;
1562 NS_IMETHODIMP
1563 nsPluginHost::UnregisterPlayPreviewMimeType(const nsACString& mimeType)
1565 nsAutoCString mimeTypeToRemove(mimeType);
1566 for (uint32_t i = mPlayPreviewMimeTypes.Length(); i > 0; i--) {
1567 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i - 1];
1568 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToRemove.get()) == 0) {
1569 mPlayPreviewMimeTypes.RemoveElementAt(i - 1);
1570 break;
1573 return NS_OK;
1576 NS_IMETHODIMP
1577 nsPluginHost::GetPlayPreviewInfo(const nsACString& mimeType,
1578 nsIPluginPlayPreviewInfo** aResult)
1580 nsAutoCString mimeTypeToFind(mimeType);
1581 for (uint32_t i = 0; i < mPlayPreviewMimeTypes.Length(); i++) {
1582 nsRefPtr<nsPluginPlayPreviewInfo> pp = mPlayPreviewMimeTypes[i];
1583 if (PL_strcasecmp(pp.get()->mMimeType.get(), mimeTypeToFind.get()) == 0) {
1584 *aResult = new nsPluginPlayPreviewInfo(pp.get());
1585 NS_ADDREF(*aResult);
1586 return NS_OK;
1589 *aResult = nullptr;
1590 return NS_ERROR_NOT_AVAILABLE;
1593 NS_IMETHODIMP
1594 nsPluginHost::ClearSiteData(nsIPluginTag* plugin, const nsACString& domain,
1595 uint64_t flags, int64_t maxAge)
1597 // maxAge must be either a nonnegative integer or -1.
1598 NS_ENSURE_ARG(maxAge >= 0 || maxAge == -1);
1600 // Caller may give us a tag object that is no longer live.
1601 if (!IsLiveTag(plugin)) {
1602 return NS_ERROR_NOT_AVAILABLE;
1605 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1607 // We only ensure support for clearing Flash site data for now.
1608 // We will also attempt to clear data for any plugin that happens
1609 // to be loaded already.
1610 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1611 return NS_ERROR_FAILURE;
1614 // Make sure the plugin is loaded.
1615 nsresult rv = EnsurePluginLoaded(tag);
1616 if (NS_FAILED(rv)) {
1617 return rv;
1620 PluginLibrary* library = tag->mPlugin->GetLibrary();
1622 // If 'domain' is the null string, clear everything.
1623 if (domain.IsVoid()) {
1624 return library->NPP_ClearSiteData(NULL, flags, maxAge);
1627 // Get the list of sites from the plugin.
1628 InfallibleTArray<nsCString> sites;
1629 rv = library->NPP_GetSitesWithData(sites);
1630 NS_ENSURE_SUCCESS(rv, rv);
1632 // Enumerate the sites and build a list of matches.
1633 InfallibleTArray<nsCString> matches;
1634 rv = EnumerateSiteData(domain, sites, matches, false);
1635 NS_ENSURE_SUCCESS(rv, rv);
1637 // Clear the matches.
1638 for (uint32_t i = 0; i < matches.Length(); ++i) {
1639 const nsCString& match = matches[i];
1640 rv = library->NPP_ClearSiteData(match.get(), flags, maxAge);
1641 NS_ENSURE_SUCCESS(rv, rv);
1644 return NS_OK;
1647 NS_IMETHODIMP
1648 nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
1649 bool* result)
1651 // Caller may give us a tag object that is no longer live.
1652 if (!IsLiveTag(plugin)) {
1653 return NS_ERROR_NOT_AVAILABLE;
1656 nsPluginTag* tag = static_cast<nsPluginTag*>(plugin);
1658 // We only ensure support for clearing Flash site data for now.
1659 // We will also attempt to clear data for any plugin that happens
1660 // to be loaded already.
1661 if (!tag->mIsFlashPlugin && !tag->mPlugin) {
1662 return NS_ERROR_FAILURE;
1665 // Make sure the plugin is loaded.
1666 nsresult rv = EnsurePluginLoaded(tag);
1667 if (NS_FAILED(rv)) {
1668 return rv;
1671 PluginLibrary* library = tag->mPlugin->GetLibrary();
1673 // Get the list of sites from the plugin.
1674 InfallibleTArray<nsCString> sites;
1675 rv = library->NPP_GetSitesWithData(sites);
1676 NS_ENSURE_SUCCESS(rv, rv);
1678 // If there's no data, we're done.
1679 if (sites.IsEmpty()) {
1680 *result = false;
1681 return NS_OK;
1684 // If 'domain' is the null string, and there's data for at least one site,
1685 // we're done.
1686 if (domain.IsVoid()) {
1687 *result = true;
1688 return NS_OK;
1691 // Enumerate the sites and determine if there's a match.
1692 InfallibleTArray<nsCString> matches;
1693 rv = EnumerateSiteData(domain, sites, matches, true);
1694 NS_ENSURE_SUCCESS(rv, rv);
1696 *result = !matches.IsEmpty();
1697 return NS_OK;
1700 bool nsPluginHost::IsJavaMIMEType(const char* aType)
1702 return aType &&
1703 ((0 == PL_strncasecmp(aType, "application/x-java-vm",
1704 sizeof("application/x-java-vm") - 1)) ||
1705 (0 == PL_strncasecmp(aType, "application/x-java-applet",
1706 sizeof("application/x-java-applet") - 1)) ||
1707 (0 == PL_strncasecmp(aType, "application/x-java-bean",
1708 sizeof("application/x-java-bean") - 1)));
1711 // Check whether or not a tag is a live, valid tag, and that it's loaded.
1712 bool
1713 nsPluginHost::IsLiveTag(nsIPluginTag* aPluginTag)
1715 nsPluginTag* tag;
1716 for (tag = mPlugins; tag; tag = tag->mNext) {
1717 if (tag == aPluginTag) {
1718 return true;
1721 return false;
1724 nsPluginTag*
1725 nsPluginHost::HaveSamePlugin(const nsPluginTag* aPluginTag)
1727 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1728 if (tag->HasSameNameAndMimes(aPluginTag)) {
1729 return tag;
1732 return nullptr;
1735 nsPluginTag*
1736 nsPluginHost::FirstPluginWithPath(const nsCString& path)
1738 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
1739 if (tag->mFullPath.Equals(path)) {
1740 return tag;
1743 return nullptr;
1746 namespace {
1748 int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
1750 PRTime fileModTime = 0;
1752 #if defined(XP_MACOSX)
1753 // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
1754 // is a much better guide to when it was last modified than the date of
1755 // its package directory. See bug 313700.
1756 nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
1757 if (localFileMac) {
1758 localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
1759 } else {
1760 localfile->GetLastModifiedTime(&fileModTime);
1762 #else
1763 localfile->GetLastModifiedTime(&fileModTime);
1764 #endif
1766 return fileModTime;
1769 struct CompareFilesByTime
1771 bool
1772 LessThan(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
1774 return GetPluginLastModifiedTime(a) < GetPluginLastModifiedTime(b);
1777 bool
1778 Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
1780 return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
1784 } // anonymous namespace
1786 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
1788 nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
1789 bool aCreatePluginList,
1790 bool *aPluginsChanged)
1792 NS_ENSURE_ARG_POINTER(aPluginsChanged);
1793 nsresult rv;
1795 *aPluginsChanged = false;
1797 #ifdef PLUGIN_LOGGING
1798 nsAutoCString dirPath;
1799 pluginsDir->GetNativePath(dirPath);
1800 PLUGIN_LOG(PLUGIN_LOG_BASIC,
1801 ("nsPluginHost::ScanPluginsDirectory dir=%s\n", dirPath.get()));
1802 #endif
1804 nsCOMPtr<nsISimpleEnumerator> iter;
1805 rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
1806 if (NS_FAILED(rv))
1807 return rv;
1809 nsAutoTArray<nsCOMPtr<nsIFile>, 6> pluginFiles;
1811 bool hasMore;
1812 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
1813 nsCOMPtr<nsISupports> supports;
1814 rv = iter->GetNext(getter_AddRefs(supports));
1815 if (NS_FAILED(rv))
1816 continue;
1817 nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
1818 if (NS_FAILED(rv))
1819 continue;
1821 // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
1822 // See bug 197855.
1823 dirEntry->Normalize();
1825 if (nsPluginsDir::IsPluginFile(dirEntry)) {
1826 pluginFiles.AppendElement(dirEntry);
1830 pluginFiles.Sort(CompareFilesByTime());
1832 bool warnOutdated = false;
1834 for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
1835 nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
1837 nsString utf16FilePath;
1838 rv = localfile->GetPath(utf16FilePath);
1839 if (NS_FAILED(rv))
1840 continue;
1842 int64_t fileModTime = GetPluginLastModifiedTime(localfile);
1844 // Look for it in our cache
1845 NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
1846 nsRefPtr<nsPluginTag> pluginTag;
1847 RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
1849 bool seenBefore = false;
1851 if (pluginTag) {
1852 seenBefore = true;
1853 // If plugin changed, delete cachedPluginTag and don't use cache
1854 if (fileModTime != pluginTag->mLastModifiedTime) {
1855 // Plugins has changed. Don't use cached plugin info.
1856 pluginTag = nullptr;
1858 // plugin file changed, flag this fact
1859 *aPluginsChanged = true;
1862 // If we're not creating a list and we already know something changed then
1863 // we're done.
1864 if (!aCreatePluginList) {
1865 if (*aPluginsChanged) {
1866 return NS_OK;
1868 continue;
1872 bool isKnownInvalidPlugin = false;
1873 for (nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
1874 invalidPlugins; invalidPlugins = invalidPlugins->mNext) {
1875 // If already marked as invalid, ignore it
1876 if (invalidPlugins->mFullPath.Equals(filePath.get()) &&
1877 invalidPlugins->mLastModifiedTime == fileModTime) {
1878 if (aCreatePluginList) {
1879 invalidPlugins->mSeen = true;
1881 isKnownInvalidPlugin = true;
1882 break;
1885 if (isKnownInvalidPlugin) {
1886 continue;
1889 // if it is not found in cache info list or has been changed, create a new one
1890 if (!pluginTag) {
1891 nsPluginFile pluginFile(localfile);
1893 // create a tag describing this plugin.
1894 PRLibrary *library = nullptr;
1895 nsPluginInfo info;
1896 memset(&info, 0, sizeof(info));
1897 nsresult res;
1898 // Opening a block for the telemetry AutoTimer
1900 Telemetry::AutoTimer<Telemetry::PLUGIN_LOAD_METADATA> telemetry;
1901 res = pluginFile.GetPluginInfo(info, &library);
1903 // if we don't have mime type don't proceed, this is not a plugin
1904 if (NS_FAILED(res) || !info.fMimeTypeArray) {
1905 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(filePath.get(),
1906 fileModTime);
1907 pluginFile.FreePluginInfo(info);
1909 if (aCreatePluginList) {
1910 invalidTag->mSeen = true;
1912 invalidTag->mNext = mInvalidPlugins;
1913 if (mInvalidPlugins) {
1914 mInvalidPlugins->mPrev = invalidTag;
1916 mInvalidPlugins = invalidTag;
1918 // Mark aPluginsChanged so pluginreg is rewritten
1919 *aPluginsChanged = true;
1920 continue;
1923 pluginTag = new nsPluginTag(&info);
1924 pluginFile.FreePluginInfo(info);
1925 if (!pluginTag)
1926 return NS_ERROR_OUT_OF_MEMORY;
1928 pluginTag->mLibrary = library;
1929 pluginTag->mLastModifiedTime = fileModTime;
1930 uint32_t state = pluginTag->GetBlocklistState();
1932 // If the blocklist says it is risky and we have never seen this
1933 // plugin before, then disable it.
1934 // If the blocklist says this is an outdated plugin, warn about
1935 // outdated plugins.
1936 if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
1937 pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
1939 if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
1940 warnOutdated = true;
1943 // Plugin unloading is tag-based. If we created a new tag and loaded
1944 // the library in the process then we want to attempt to unload it here.
1945 // Only do this if the pref is set for aggressive unloading.
1946 if (UnloadPluginsASAP()) {
1947 pluginTag->TryUnloadPlugin(false);
1951 // do it if we still want it
1952 if (!seenBefore) {
1953 // We have a valid new plugin so report that plugins have changed.
1954 *aPluginsChanged = true;
1957 // Avoid adding different versions of the same plugin if they are running
1958 // in-process, otherwise we risk undefined behaviour.
1959 if (!nsNPAPIPlugin::RunPluginOOP(pluginTag)) {
1960 if (HaveSamePlugin(pluginTag)) {
1961 continue;
1965 // Don't add the same plugin again if it hasn't changed
1966 if (nsPluginTag* duplicate = FirstPluginWithPath(pluginTag->mFullPath)) {
1967 if (pluginTag->mLastModifiedTime == duplicate->mLastModifiedTime) {
1968 continue;
1972 // If we're not creating a plugin list, simply looking for changes,
1973 // then we're done.
1974 if (!aCreatePluginList) {
1975 return NS_OK;
1978 // Add plugin tags such that the list is ordered by modification date,
1979 // newest to oldest. This is ugly, it'd be easier with just about anything
1980 // other than a single-directional linked list.
1981 if (mPlugins) {
1982 nsPluginTag *prev = nullptr;
1983 nsPluginTag *next = mPlugins;
1984 while (next) {
1985 if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
1986 pluginTag->mNext = next;
1987 if (prev) {
1988 prev->mNext = pluginTag;
1989 } else {
1990 mPlugins = pluginTag;
1992 break;
1994 prev = next;
1995 next = prev->mNext;
1996 if (!next) {
1997 prev->mNext = pluginTag;
2000 } else {
2001 mPlugins = pluginTag;
2004 if (pluginTag->IsActive()) {
2005 nsAdoptingCString disableFullPage =
2006 Preferences::GetCString(kPrefDisableFullPage);
2007 for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
2008 if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
2009 RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
2010 ePluginRegister);
2016 if (warnOutdated) {
2017 Preferences::SetBool("plugins.update.notifyUser", true);
2020 return NS_OK;
2023 nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
2024 bool aCreatePluginList,
2025 bool *aPluginsChanged)
2027 bool hasMore;
2028 while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
2029 nsCOMPtr<nsISupports> supports;
2030 nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
2031 if (NS_FAILED(rv))
2032 continue;
2033 nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
2034 if (NS_FAILED(rv))
2035 continue;
2037 // don't pass aPluginsChanged directly to prevent it from been reset
2038 bool pluginschanged = false;
2039 ScanPluginsDirectory(nextDir, aCreatePluginList, &pluginschanged);
2041 if (pluginschanged)
2042 *aPluginsChanged = true;
2044 // if changes are detected and we are not creating the list, do not proceed
2045 if (!aCreatePluginList && *aPluginsChanged)
2046 break;
2048 return NS_OK;
2051 nsresult nsPluginHost::LoadPlugins()
2053 #ifdef ANDROID
2054 if (XRE_GetProcessType() == GeckoProcessType_Content) {
2055 return NS_OK;
2057 #endif
2058 // do not do anything if it is already done
2059 // use ReloadPlugins() to enforce loading
2060 if (mPluginsLoaded)
2061 return NS_OK;
2063 if (mPluginsDisabled)
2064 return NS_OK;
2066 bool pluginschanged;
2067 nsresult rv = FindPlugins(true, &pluginschanged);
2068 if (NS_FAILED(rv))
2069 return rv;
2071 // only if plugins have changed will we notify plugin-change observers
2072 if (pluginschanged) {
2073 nsCOMPtr<nsIObserverService> obsService =
2074 mozilla::services::GetObserverService();
2075 if (obsService)
2076 obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
2079 return NS_OK;
2082 // if aCreatePluginList is false we will just scan for plugins
2083 // and see if any changes have been made to the plugins.
2084 // This is needed in ReloadPlugins to prevent possible recursive reloads
2085 nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
2087 Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
2089 NS_ENSURE_ARG_POINTER(aPluginsChanged);
2091 *aPluginsChanged = false;
2092 nsresult rv;
2094 // Read cached plugins info. If the profile isn't yet available then don't
2095 // scan for plugins
2096 if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
2097 return NS_OK;
2099 #ifdef XP_WIN
2100 // Failure here is not a show-stopper so just warn.
2101 rv = EnsurePrivateDirServiceProvider();
2102 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
2103 #endif /* XP_WIN */
2105 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
2106 if (NS_FAILED(rv))
2107 return rv;
2109 nsCOMPtr<nsISimpleEnumerator> dirList;
2111 // Scan plugins directories;
2112 // don't pass aPluginsChanged directly, to prevent its
2113 // possible reset in subsequent ScanPluginsDirectory calls
2114 bool pluginschanged = false;
2116 // Scan the app-defined list of plugin dirs.
2117 rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
2118 if (NS_SUCCEEDED(rv)) {
2119 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2121 if (pluginschanged)
2122 *aPluginsChanged = true;
2124 // if we are just looking for possible changes,
2125 // no need to proceed if changes are detected
2126 if (!aCreatePluginList && *aPluginsChanged) {
2127 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2128 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2129 return NS_OK;
2131 } else {
2132 #ifdef ANDROID
2133 LOG("getting plugins dir failed");
2134 #endif
2137 mPluginsLoaded = true; // at this point 'some' plugins have been loaded,
2138 // the rest is optional
2140 #ifdef XP_WIN
2141 bool bScanPLIDs = Preferences::GetBool("plugin.scan.plid.all", false);
2143 // Now lets scan any PLID directories
2144 if (bScanPLIDs && mPrivateDirServiceProvider) {
2145 rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
2146 if (NS_SUCCEEDED(rv)) {
2147 ScanPluginsDirectoryList(dirList, aCreatePluginList, &pluginschanged);
2149 if (pluginschanged)
2150 *aPluginsChanged = true;
2152 // if we are just looking for possible changes,
2153 // no need to proceed if changes are detected
2154 if (!aCreatePluginList && *aPluginsChanged) {
2155 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2156 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2157 return NS_OK;
2163 // Scan the installation paths of our popular plugins if the prefs are enabled
2165 // This table controls the order of scanning
2166 const char* const prefs[] = {NS_WIN_ACROBAT_SCAN_KEY,
2167 NS_WIN_QUICKTIME_SCAN_KEY,
2168 NS_WIN_WMP_SCAN_KEY};
2170 uint32_t size = sizeof(prefs) / sizeof(prefs[0]);
2172 for (uint32_t i = 0; i < size; i+=1) {
2173 nsCOMPtr<nsIFile> dirToScan;
2174 bool bExists;
2175 if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
2176 dirToScan &&
2177 NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
2178 bExists) {
2180 ScanPluginsDirectory(dirToScan, aCreatePluginList, &pluginschanged);
2182 if (pluginschanged)
2183 *aPluginsChanged = true;
2185 // if we are just looking for possible changes,
2186 // no need to proceed if changes are detected
2187 if (!aCreatePluginList && *aPluginsChanged) {
2188 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2189 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2190 return NS_OK;
2194 #endif
2196 // We should also consider plugins to have changed if any plugins have been removed.
2197 // We'll know if any were removed if they weren't taken out of the cached plugins list
2198 // during our scan, thus we can assume something was removed if the cached plugins list
2199 // contains anything.
2200 if (!*aPluginsChanged && mCachedPlugins) {
2201 *aPluginsChanged = true;
2204 // Remove unseen invalid plugins
2205 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2206 while (invalidPlugins) {
2207 if (!invalidPlugins->mSeen) {
2208 nsRefPtr<nsInvalidPluginTag> invalidPlugin = invalidPlugins;
2210 if (invalidPlugin->mPrev) {
2211 invalidPlugin->mPrev->mNext = invalidPlugin->mNext;
2213 else {
2214 mInvalidPlugins = invalidPlugin->mNext;
2216 if (invalidPlugin->mNext) {
2217 invalidPlugin->mNext->mPrev = invalidPlugin->mPrev;
2220 invalidPlugins = invalidPlugin->mNext;
2222 invalidPlugin->mPrev = NULL;
2223 invalidPlugin->mNext = NULL;
2225 else {
2226 invalidPlugins->mSeen = false;
2227 invalidPlugins = invalidPlugins->mNext;
2231 // if we are not creating the list, there is no need to proceed
2232 if (!aCreatePluginList) {
2233 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2234 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2235 return NS_OK;
2238 // if we are creating the list, it is already done;
2239 // update the plugins info cache if changes are detected
2240 if (*aPluginsChanged)
2241 WritePluginInfo();
2243 // No more need for cached plugins. Clear it up.
2244 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2245 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2247 return NS_OK;
2250 nsresult
2251 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
2253 ReadPluginInfo();
2254 WritePluginInfo();
2255 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
2256 NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
2258 if (!aPluginTag) {
2259 return NS_OK;
2262 // Update types with category manager
2263 nsAdoptingCString disableFullPage =
2264 Preferences::GetCString(kPrefDisableFullPage);
2265 for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
2266 nsRegisterType shouldRegister;
2268 if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
2269 shouldRegister = ePluginUnregister;
2270 } else {
2271 nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
2272 true);
2273 shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
2276 RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister);
2279 nsCOMPtr<nsIObserverService> obsService =
2280 mozilla::services::GetObserverService();
2281 if (obsService)
2282 obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
2284 // Reload instances if needed
2285 if (aPluginTag->IsActive()) {
2286 return NS_OK;
2289 return NS_OK;
2292 /* static */ bool
2293 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
2295 nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
2296 if (!whitelist.Length()) {
2297 return true;
2299 nsDependentCString wrap(aMimeType);
2300 return IsTypeInList(wrap, whitelist);
2303 void
2304 nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
2305 nsRegisterType aType)
2307 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2308 ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
2309 aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
2311 nsCOMPtr<nsICategoryManager> catMan =
2312 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
2313 if (!catMan) {
2314 return;
2317 const char *contractId =
2318 "@mozilla.org/content/plugin/document-loader-factory;1";
2320 if (aType == ePluginRegister) {
2321 catMan->AddCategoryEntry("Gecko-Content-Viewers",
2322 aMimeType.get(),
2323 contractId,
2324 false, /* persist: broken by bug 193031 */
2325 mOverrideInternalTypes,
2326 nullptr);
2327 } else {
2328 // Only delete the entry if a plugin registered for it
2329 nsXPIDLCString value;
2330 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
2331 aMimeType.get(),
2332 getter_Copies(value));
2333 if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
2334 catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
2335 aMimeType.get(),
2336 true);
2341 nsresult
2342 nsPluginHost::WritePluginInfo()
2345 nsresult rv = NS_OK;
2346 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2347 if (NS_FAILED(rv))
2348 return rv;
2350 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2351 getter_AddRefs(mPluginRegFile));
2353 if (!mPluginRegFile)
2354 return NS_ERROR_FAILURE;
2356 PRFileDesc* fd = nullptr;
2358 nsCOMPtr<nsIFile> pluginReg;
2360 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2361 if (NS_FAILED(rv))
2362 return rv;
2364 nsAutoCString filename(kPluginRegistryFilename);
2365 filename.Append(".tmp");
2366 rv = pluginReg->AppendNative(filename);
2367 if (NS_FAILED(rv))
2368 return rv;
2370 rv = pluginReg->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
2371 if (NS_FAILED(rv))
2372 return rv;
2374 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
2375 if (!runtime) {
2376 return NS_ERROR_FAILURE;
2379 nsAutoCString arch;
2380 rv = runtime->GetXPCOMABI(arch);
2381 if (NS_FAILED(rv)) {
2382 return rv;
2385 PR_fprintf(fd, "Generated File. Do not edit.\n");
2387 PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n",
2388 PLUGIN_REGISTRY_FIELD_DELIMITER,
2389 kPluginRegistryVersion,
2390 PLUGIN_REGISTRY_FIELD_DELIMITER,
2391 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2392 PLUGIN_REGISTRY_FIELD_DELIMITER,
2393 arch.get(),
2394 PLUGIN_REGISTRY_FIELD_DELIMITER,
2395 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2397 // Store all plugins in the mPlugins list - all plugins currently in use.
2398 PR_fprintf(fd, "\n[PLUGINS]\n");
2400 for (nsPluginTag *tag = mPlugins; tag; tag = tag->mNext) {
2401 // store each plugin info into the registry
2402 // filename & fullpath are on separate line
2403 // because they can contain field delimiter char
2404 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
2405 (tag->mFileName.get()),
2406 PLUGIN_REGISTRY_FIELD_DELIMITER,
2407 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2408 (tag->mFullPath.get()),
2409 PLUGIN_REGISTRY_FIELD_DELIMITER,
2410 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2411 (tag->mVersion.get()),
2412 PLUGIN_REGISTRY_FIELD_DELIMITER,
2413 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2415 // lastModifiedTimeStamp|canUnload|tag->mFlags
2416 PR_fprintf(fd, "%lld%c%d%c%lu%c%c\n",
2417 tag->mLastModifiedTime,
2418 PLUGIN_REGISTRY_FIELD_DELIMITER,
2419 false, // did store whether or not to unload in-process plugins
2420 PLUGIN_REGISTRY_FIELD_DELIMITER,
2421 0, // legacy field for flags
2422 PLUGIN_REGISTRY_FIELD_DELIMITER,
2423 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2425 //description, name & mtypecount are on separate line
2426 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
2427 (tag->mDescription.get()),
2428 PLUGIN_REGISTRY_FIELD_DELIMITER,
2429 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2430 (tag->mName.get()),
2431 PLUGIN_REGISTRY_FIELD_DELIMITER,
2432 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
2433 tag->mMimeTypes.Length());
2435 // Add in each mimetype this plugin supports
2436 for (uint32_t i = 0; i < tag->mMimeTypes.Length(); i++) {
2437 PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
2438 i,PLUGIN_REGISTRY_FIELD_DELIMITER,
2439 (tag->mMimeTypes[i].get()),
2440 PLUGIN_REGISTRY_FIELD_DELIMITER,
2441 (tag->mMimeDescriptions[i].get()),
2442 PLUGIN_REGISTRY_FIELD_DELIMITER,
2443 (tag->mExtensions[i].get()),
2444 PLUGIN_REGISTRY_FIELD_DELIMITER,
2445 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2449 PR_fprintf(fd, "\n[INVALID]\n");
2451 nsRefPtr<nsInvalidPluginTag> invalidPlugins = mInvalidPlugins;
2452 while (invalidPlugins) {
2453 // fullPath
2454 PR_fprintf(fd, "%s%c%c\n",
2455 (!invalidPlugins->mFullPath.IsEmpty() ? invalidPlugins->mFullPath.get() : ""),
2456 PLUGIN_REGISTRY_FIELD_DELIMITER,
2457 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2459 // lastModifiedTimeStamp
2460 PR_fprintf(fd, "%lld%c%c\n",
2461 invalidPlugins->mLastModifiedTime,
2462 PLUGIN_REGISTRY_FIELD_DELIMITER,
2463 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
2465 invalidPlugins = invalidPlugins->mNext;
2468 PR_Close(fd);
2469 nsCOMPtr<nsIFile> parent;
2470 rv = pluginReg->GetParent(getter_AddRefs(parent));
2471 NS_ENSURE_SUCCESS(rv, rv);
2472 rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
2473 return rv;
2476 nsresult
2477 nsPluginHost::ReadPluginInfo()
2479 const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
2480 const long PLUGIN_REG_MAX_MIMETYPES = 1000;
2482 // we need to import the legacy flags from the plugin registry once
2483 const bool pluginStateImported =
2484 Preferences::GetDefaultBool("plugin.importedState", false);
2486 nsresult rv;
2488 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
2489 if (NS_FAILED(rv))
2490 return rv;
2492 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
2493 getter_AddRefs(mPluginRegFile));
2495 if (!mPluginRegFile) {
2496 // There is no profile yet, this will tell us if there is going to be one
2497 // in the future.
2498 directoryService->Get(NS_APP_PROFILE_DIR_STARTUP, NS_GET_IID(nsIFile),
2499 getter_AddRefs(mPluginRegFile));
2500 if (!mPluginRegFile)
2501 return NS_ERROR_FAILURE;
2502 else
2503 return NS_ERROR_NOT_AVAILABLE;
2506 PRFileDesc* fd = nullptr;
2508 nsCOMPtr<nsIFile> pluginReg;
2510 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
2511 if (NS_FAILED(rv))
2512 return rv;
2514 rv = pluginReg->AppendNative(kPluginRegistryFilename);
2515 if (NS_FAILED(rv))
2516 return rv;
2518 int64_t fileSize;
2519 rv = pluginReg->GetFileSize(&fileSize);
2520 if (NS_FAILED(rv))
2521 return rv;
2523 if (fileSize > INT32_MAX) {
2524 return NS_ERROR_FAILURE;
2526 int32_t flen = int32_t(fileSize);
2527 if (flen == 0) {
2528 NS_WARNING("Plugins Registry Empty!");
2529 return NS_OK; // ERROR CONDITION
2532 nsPluginManifestLineReader reader;
2533 char* registry = reader.Init(flen);
2534 if (!registry)
2535 return NS_ERROR_OUT_OF_MEMORY;
2537 rv = pluginReg->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
2538 if (NS_FAILED(rv))
2539 return rv;
2541 // set rv to return an error on goto out
2542 rv = NS_ERROR_FAILURE;
2544 int32_t bread = PR_Read(fd, registry, flen);
2545 PR_Close(fd);
2547 if (flen > bread)
2548 return rv;
2550 if (!ReadSectionHeader(reader, "HEADER"))
2551 return rv;;
2553 if (!reader.NextLine())
2554 return rv;
2556 char* values[6];
2558 // VersionLiteral, kPluginRegistryVersion
2559 if (2 != reader.ParseLine(values, 2))
2560 return rv;
2562 // VersionLiteral
2563 if (PL_strcmp(values[0], "Version"))
2564 return rv;
2566 // kPluginRegistryVersion
2567 int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
2568 mozilla::Version version(values[1]);
2569 // If this is a registry from some future version then don't attempt to read it
2570 if (vdiff > 0)
2571 return rv;
2572 // If this is a registry from before the minimum then don't attempt to read it
2573 if (version < kMinimumRegistryVersion)
2574 return rv;
2576 // Registry v0.10 and upwards includes the plugin version field
2577 bool regHasVersion = (version >= "0.10");
2579 // Registry v0.13 and upwards includes the architecture
2580 if (version >= "0.13") {
2581 char* archValues[6];
2583 if (!reader.NextLine()) {
2584 return rv;
2587 // ArchLiteral, Architecture
2588 if (2 != reader.ParseLine(archValues, 2)) {
2589 return rv;
2592 // ArchLiteral
2593 if (PL_strcmp(archValues[0], "Arch")) {
2594 return rv;
2597 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
2598 if (!runtime) {
2599 return rv;
2602 nsAutoCString arch;
2603 if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
2604 return rv;
2607 // If this is a registry from a different architecture then don't attempt to read it
2608 if (PL_strcmp(archValues[1], arch.get())) {
2609 return rv;
2613 // Registry v0.13 and upwards includes the list of invalid plugins
2614 bool hasInvalidPlugins = (version >= "0.13");
2616 // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
2617 const bool hasValidFlags = (version < "0.16");
2619 if (!ReadSectionHeader(reader, "PLUGINS"))
2620 return rv;
2622 #if defined(XP_MACOSX)
2623 bool hasFullPathInFileNameField = false;
2624 #else
2625 bool hasFullPathInFileNameField = (version < "0.11");
2626 #endif
2628 while (reader.NextLine()) {
2629 const char *filename;
2630 const char *fullpath;
2631 nsAutoCString derivedFileName;
2633 if (hasInvalidPlugins && *reader.LinePtr() == '[') {
2634 break;
2637 if (hasFullPathInFileNameField) {
2638 fullpath = reader.LinePtr();
2639 if (!reader.NextLine())
2640 return rv;
2641 // try to derive a file name from the full path
2642 if (fullpath) {
2643 nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
2644 file->InitWithNativePath(nsDependentCString(fullpath));
2645 file->GetNativeLeafName(derivedFileName);
2646 filename = derivedFileName.get();
2647 } else {
2648 filename = NULL;
2651 // skip the next line, useless in this version
2652 if (!reader.NextLine())
2653 return rv;
2654 } else {
2655 filename = reader.LinePtr();
2656 if (!reader.NextLine())
2657 return rv;
2659 fullpath = reader.LinePtr();
2660 if (!reader.NextLine())
2661 return rv;
2664 const char *version;
2665 if (regHasVersion) {
2666 version = reader.LinePtr();
2667 if (!reader.NextLine())
2668 return rv;
2669 } else {
2670 version = "0";
2673 // lastModifiedTimeStamp|canUnload|tag.mFlag
2674 if (reader.ParseLine(values, 3) != 3)
2675 return rv;
2677 // If this is an old plugin registry mark this plugin tag to be refreshed
2678 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
2679 uint32_t tagflag = atoi(values[2]);
2680 if (!reader.NextLine())
2681 return rv;
2683 char *description = reader.LinePtr();
2684 if (!reader.NextLine())
2685 return rv;
2687 #if MOZ_WIDGET_ANDROID
2688 // Flash on Android does not populate the version field, but it is tacked on to the description.
2689 // For example, "Shockwave Flash 11.1 r115"
2690 if (PL_strncmp("Shockwave Flash ", description, 16) == 0 && description[16]) {
2691 version = &description[16];
2693 #endif
2695 const char *name = reader.LinePtr();
2696 if (!reader.NextLine())
2697 return rv;
2699 long mimetypecount = std::strtol(reader.LinePtr(), nullptr, 10);
2700 if (mimetypecount == LONG_MAX || mimetypecount == LONG_MIN ||
2701 mimetypecount >= PLUGIN_REG_MAX_MIMETYPES || mimetypecount < 0) {
2702 return NS_ERROR_FAILURE;
2705 char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
2706 char **mimetypes;
2707 char **mimedescriptions;
2708 char **extensions;
2709 char **heapalloced = 0;
2710 if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
2711 heapalloced = new char *[mimetypecount * 3];
2712 mimetypes = heapalloced;
2713 } else {
2714 mimetypes = stackalloced;
2716 mimedescriptions = mimetypes + mimetypecount;
2717 extensions = mimedescriptions + mimetypecount;
2719 int mtr = 0; //mimetype read
2720 for (; mtr < mimetypecount; mtr++) {
2721 if (!reader.NextLine())
2722 break;
2724 //line number|mimetype|description|extension
2725 if (4 != reader.ParseLine(values, 4))
2726 break;
2727 int line = atoi(values[0]);
2728 if (line != mtr)
2729 break;
2730 mimetypes[mtr] = values[1];
2731 mimedescriptions[mtr] = values[2];
2732 extensions[mtr] = values[3];
2735 if (mtr != mimetypecount) {
2736 if (heapalloced) {
2737 delete [] heapalloced;
2739 return rv;
2742 nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
2743 description,
2744 filename,
2745 fullpath,
2746 version,
2747 (const char* const*)mimetypes,
2748 (const char* const*)mimedescriptions,
2749 (const char* const*)extensions,
2750 mimetypecount, lastmod, true);
2751 if (heapalloced)
2752 delete [] heapalloced;
2754 if (!tag)
2755 continue;
2757 // Import flags from registry into prefs for old registry versions
2758 if (hasValidFlags && !pluginStateImported) {
2759 tag->ImportFlagsToPrefs(tagflag);
2762 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
2763 ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
2764 tag->mNext = mCachedPlugins;
2765 mCachedPlugins = tag;
2768 if (hasInvalidPlugins) {
2769 if (!ReadSectionHeader(reader, "INVALID")) {
2770 return rv;
2773 while (reader.NextLine()) {
2774 const char *fullpath = reader.LinePtr();
2775 if (!reader.NextLine()) {
2776 return rv;
2779 const char *lastModifiedTimeStamp = reader.LinePtr();
2780 int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
2782 nsRefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
2784 invalidTag->mNext = mInvalidPlugins;
2785 if (mInvalidPlugins) {
2786 mInvalidPlugins->mPrev = invalidTag;
2788 mInvalidPlugins = invalidTag;
2792 // flip the pref so we don't import the legacy flags again
2793 Preferences::SetBool("plugin.importedState", true);
2795 return NS_OK;
2798 void
2799 nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
2801 nsRefPtr<nsPluginTag> prev;
2802 nsRefPtr<nsPluginTag> tag = mCachedPlugins;
2803 while (tag)
2805 if (tag->mFullPath.Equals(filePath)) {
2806 // Found it. Remove it from our list
2807 if (prev)
2808 prev->mNext = tag->mNext;
2809 else
2810 mCachedPlugins = tag->mNext;
2811 tag->mNext = nullptr;
2812 *result = tag;
2813 NS_ADDREF(*result);
2814 break;
2816 prev = tag;
2817 tag = tag->mNext;
2821 #ifdef XP_WIN
2822 nsresult
2823 nsPluginHost::EnsurePrivateDirServiceProvider()
2825 if (!mPrivateDirServiceProvider) {
2826 nsresult rv;
2827 mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
2828 if (!mPrivateDirServiceProvider)
2829 return NS_ERROR_OUT_OF_MEMORY;
2830 nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
2831 if (NS_FAILED(rv))
2832 return rv;
2833 rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
2834 if (NS_FAILED(rv))
2835 return rv;
2837 return NS_OK;
2839 #endif /* XP_WIN */
2841 nsresult nsPluginHost::NewPluginURLStream(const nsString& aURL,
2842 nsNPAPIPluginInstance *aInstance,
2843 nsNPAPIPluginStreamListener* aListener,
2844 nsIInputStream *aPostStream,
2845 const char *aHeadersData,
2846 uint32_t aHeadersDataLen)
2848 nsCOMPtr<nsIURI> url;
2849 nsAutoString absUrl;
2850 nsresult rv;
2852 if (aURL.Length() <= 0)
2853 return NS_OK;
2855 // get the base URI for the plugin to create an absolute url
2856 // in case aURL is relative
2857 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
2858 if (owner) {
2859 rv = NS_MakeAbsoluteURI(absUrl, aURL, nsCOMPtr<nsIURI>(owner->GetBaseURI()));
2862 if (absUrl.IsEmpty())
2863 absUrl.Assign(aURL);
2865 rv = NS_NewURI(getter_AddRefs(url), absUrl);
2866 if (NS_FAILED(rv))
2867 return rv;
2869 nsCOMPtr<nsIDOMElement> element;
2870 nsCOMPtr<nsIDocument> doc;
2871 if (owner) {
2872 owner->GetDOMElement(getter_AddRefs(element));
2873 owner->GetDocument(getter_AddRefs(doc));
2876 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
2877 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
2878 url,
2879 (doc ? doc->NodePrincipal() : nullptr),
2880 element,
2881 EmptyCString(), //mime guess
2882 nullptr, //extra
2883 &shouldLoad);
2884 if (NS_FAILED(rv))
2885 return rv;
2886 if (NS_CP_REJECTED(shouldLoad)) {
2887 // Disallowed by content policy
2888 return NS_ERROR_CONTENT_BLOCKED;
2891 nsRefPtr<nsPluginStreamListenerPeer> listenerPeer = new nsPluginStreamListenerPeer();
2892 if (!listenerPeer)
2893 return NS_ERROR_OUT_OF_MEMORY;
2895 rv = listenerPeer->Initialize(url, aInstance, aListener);
2896 if (NS_FAILED(rv))
2897 return rv;
2899 nsCOMPtr<nsIChannel> channel;
2900 rv = NS_NewChannel(getter_AddRefs(channel), url, nullptr,
2901 nullptr, /* do not add this internal plugin's channel
2902 on the load group otherwise this channel could be canceled
2903 form |nsDocShell::OnLinkClickSync| bug 166613 */
2904 listenerPeer);
2905 if (NS_FAILED(rv))
2906 return rv;
2908 if (doc) {
2909 // Set the owner of channel to the document principal...
2910 channel->SetOwner(doc->NodePrincipal());
2912 // And if it's a script allow it to execute against the
2913 // document's script context.
2914 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
2915 if (scriptChannel) {
2916 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
2917 // Plug-ins seem to depend on javascript: URIs running synchronously
2918 scriptChannel->SetExecuteAsync(false);
2922 // deal with headers and post data
2923 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
2924 if (httpChannel) {
2925 if (!aPostStream) {
2926 // Only set the Referer header for GET requests because IIS throws
2927 // errors about malformed requests if we include it in POSTs. See
2928 // bug 724465.
2929 nsCOMPtr<nsIURI> referer;
2931 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(element);
2932 if (olc)
2933 olc->GetSrcURI(getter_AddRefs(referer));
2936 if (!referer) {
2937 if (!doc) {
2938 return NS_ERROR_FAILURE;
2940 referer = doc->GetDocumentURI();
2943 rv = httpChannel->SetReferrer(referer);
2944 NS_ENSURE_SUCCESS(rv,rv);
2947 if (aPostStream) {
2948 // XXX it's a bit of a hack to rewind the postdata stream
2949 // here but it has to be done in case the post data is
2950 // being reused multiple times.
2951 nsCOMPtr<nsISeekableStream>
2952 postDataSeekable(do_QueryInterface(aPostStream));
2953 if (postDataSeekable)
2954 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2956 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
2957 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
2959 uploadChannel->SetUploadStream(aPostStream, EmptyCString(), -1);
2962 if (aHeadersData) {
2963 rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
2964 NS_ENSURE_SUCCESS(rv,rv);
2967 rv = channel->AsyncOpen(listenerPeer, nullptr);
2968 if (NS_SUCCEEDED(rv))
2969 listenerPeer->TrackRequest(channel);
2970 return rv;
2973 // Called by GetURL and PostURL
2974 nsresult
2975 nsPluginHost::DoURLLoadSecurityCheck(nsNPAPIPluginInstance *aInstance,
2976 const char* aURL)
2978 if (!aURL || *aURL == '\0')
2979 return NS_OK;
2981 // get the base URI for the plugin element
2982 nsRefPtr<nsPluginInstanceOwner> owner = aInstance->GetOwner();
2983 if (!owner)
2984 return NS_ERROR_FAILURE;
2986 nsCOMPtr<nsIURI> baseURI = owner->GetBaseURI();
2987 if (!baseURI)
2988 return NS_ERROR_FAILURE;
2990 // Create an absolute URL for the target in case the target is relative
2991 nsCOMPtr<nsIURI> targetURL;
2992 NS_NewURI(getter_AddRefs(targetURL), aURL, baseURI);
2993 if (!targetURL)
2994 return NS_ERROR_FAILURE;
2996 nsCOMPtr<nsIDocument> doc;
2997 owner->GetDocument(getter_AddRefs(doc));
2998 if (!doc)
2999 return NS_ERROR_FAILURE;
3001 nsresult rv;
3002 nsCOMPtr<nsIScriptSecurityManager> secMan(
3003 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
3004 if (NS_FAILED(rv))
3005 return rv;
3007 return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL,
3008 nsIScriptSecurityManager::STANDARD);
3012 nsresult
3013 nsPluginHost::AddHeadersToChannel(const char *aHeadersData,
3014 uint32_t aHeadersDataLen,
3015 nsIChannel *aGenericChannel)
3017 nsresult rv = NS_OK;
3019 nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
3020 if (!aChannel) {
3021 return NS_ERROR_NULL_POINTER;
3024 // used during the manipulation of the String from the aHeadersData
3025 nsAutoCString headersString;
3026 nsAutoCString oneHeader;
3027 nsAutoCString headerName;
3028 nsAutoCString headerValue;
3029 int32_t crlf = 0;
3030 int32_t colon = 0;
3032 // Turn the char * buffer into an nsString.
3033 headersString = aHeadersData;
3035 // Iterate over the nsString: for each "\r\n" delimited chunk,
3036 // add the value as a header to the nsIHTTPChannel
3037 while (true) {
3038 crlf = headersString.Find("\r\n", true);
3039 if (-1 == crlf) {
3040 rv = NS_OK;
3041 return rv;
3043 headersString.Mid(oneHeader, 0, crlf);
3044 headersString.Cut(0, crlf + 2);
3045 oneHeader.StripWhitespace();
3046 colon = oneHeader.Find(":");
3047 if (-1 == colon) {
3048 rv = NS_ERROR_NULL_POINTER;
3049 return rv;
3051 oneHeader.Left(headerName, colon);
3052 colon++;
3053 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
3055 // FINALLY: we can set the header!
3057 rv = aChannel->SetRequestHeader(headerName, headerValue, true);
3058 if (NS_FAILED(rv)) {
3059 rv = NS_ERROR_NULL_POINTER;
3060 return rv;
3063 return rv;
3066 nsresult
3067 nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
3069 if (PluginDestructionGuard::DelayDestroy(aInstance)) {
3070 return NS_OK;
3073 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3074 ("nsPluginHost::StopPluginInstance called instance=%p\n",aInstance));
3076 if (aInstance->HasStartedDestroying()) {
3077 return NS_OK;
3080 Telemetry::AutoTimer<Telemetry::PLUGIN_SHUTDOWN_MS> timer;
3081 aInstance->Stop();
3083 // if the instance does not want to be 'cached' just remove it
3084 bool doCache = aInstance->ShouldCache();
3085 if (doCache) {
3086 // try to get the max cached instances from a pref or use default
3087 uint32_t cachedInstanceLimit =
3088 Preferences::GetUint(NS_PREF_MAX_NUM_CACHED_INSTANCES,
3089 DEFAULT_NUMBER_OF_STOPPED_INSTANCES);
3090 if (StoppedInstanceCount() >= cachedInstanceLimit) {
3091 nsNPAPIPluginInstance *oldestInstance = FindOldestStoppedInstance();
3092 if (oldestInstance) {
3093 nsPluginTag* pluginTag = TagForPlugin(oldestInstance->GetPlugin());
3094 oldestInstance->Destroy();
3095 mInstances.RemoveElement(oldestInstance);
3096 // TODO: Remove this check once bug 752422 was investigated
3097 if (pluginTag) {
3098 OnPluginInstanceDestroyed(pluginTag);
3102 } else {
3103 nsPluginTag* pluginTag = TagForPlugin(aInstance->GetPlugin());
3104 aInstance->Destroy();
3105 mInstances.RemoveElement(aInstance);
3106 // TODO: Remove this check once bug 752422 was investigated
3107 if (pluginTag) {
3108 OnPluginInstanceDestroyed(pluginTag);
3112 return NS_OK;
3115 nsresult nsPluginHost::NewPluginStreamListener(nsIURI* aURI,
3116 nsNPAPIPluginInstance* aInstance,
3117 nsIStreamListener **aStreamListener)
3119 NS_ENSURE_ARG_POINTER(aURI);
3120 NS_ENSURE_ARG_POINTER(aStreamListener);
3122 nsRefPtr<nsPluginStreamListenerPeer> listener = new nsPluginStreamListenerPeer();
3123 nsresult rv = listener->Initialize(aURI, aInstance, nullptr);
3124 if (NS_FAILED(rv)) {
3125 return rv;
3128 listener.forget(aStreamListener);
3130 return NS_OK;
3133 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
3134 const char *aTopic,
3135 const PRUnichar *someData)
3137 if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
3138 OnShutdown();
3139 UnloadPlugins();
3140 sInst->Release();
3142 if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
3143 mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
3144 mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
3145 // Unload or load plugins as needed
3146 if (mPluginsDisabled) {
3147 UnloadPlugins();
3148 } else {
3149 LoadPlugins();
3152 if (!strcmp("blocklist-updated", aTopic)) {
3153 nsPluginTag* plugin = mPlugins;
3154 while (plugin) {
3155 plugin->InvalidateBlocklistState();
3156 plugin = plugin->mNext;
3159 #ifdef MOZ_WIDGET_ANDROID
3160 if (!strcmp("application-background", aTopic)) {
3161 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3162 mInstances[i]->NotifyForeground(false);
3165 if (!strcmp("application-foreground", aTopic)) {
3166 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3167 if (mInstances[i]->IsOnScreen())
3168 mInstances[i]->NotifyForeground(true);
3171 if (!strcmp("memory-pressure", aTopic)) {
3172 for(uint32_t i = 0; i < mInstances.Length(); i++) {
3173 mInstances[i]->MemoryPressure();
3176 #endif
3177 return NS_OK;
3180 nsresult
3181 nsPluginHost::ParsePostBufferToFixHeaders(const char *inPostData, uint32_t inPostDataLen,
3182 char **outPostData, uint32_t *outPostDataLen)
3184 if (!inPostData || !outPostData || !outPostDataLen)
3185 return NS_ERROR_NULL_POINTER;
3187 *outPostData = 0;
3188 *outPostDataLen = 0;
3190 const char CR = '\r';
3191 const char LF = '\n';
3192 const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
3193 const char ContentLenHeader[] = "Content-length";
3195 nsAutoTArray<const char*, 8> singleLF;
3196 const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
3197 const char *pSod = 0; // pointer to start of data in inPostData
3198 const char *pEoh = 0; // pointer to end of headers in inPostData
3199 const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
3200 if (*inPostData == LF) {
3201 // If no custom headers are required, simply add a blank
3202 // line ('\n') to the beginning of the file or buffer.
3203 // so *inPostData == '\n' is valid
3204 pSod = inPostData + 1;
3205 } else {
3206 const char *s = inPostData; //tmp pointer to sourse inPostData
3207 while (s < pEod) {
3208 if (!pSCntlh &&
3209 (*s == 'C' || *s == 'c') &&
3210 (s + sizeof(ContentLenHeader) - 1 < pEod) &&
3211 (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
3213 // lets assume this is ContentLenHeader for now
3214 const char *p = pSCntlh = s;
3215 p += sizeof(ContentLenHeader) - 1;
3216 // search for first CR or LF == end of ContentLenHeader
3217 for (; p < pEod; p++) {
3218 if (*p == CR || *p == LF) {
3219 // got delimiter,
3220 // one more check; if previous char is a digit
3221 // most likely pSCntlh points to the start of ContentLenHeader
3222 if (*(p-1) >= '0' && *(p-1) <= '9') {
3223 s = p;
3225 break; //for loop
3228 if (pSCntlh == s) { // curret ptr is the same
3229 pSCntlh = 0; // that was not ContentLenHeader
3230 break; // there is nothing to parse, break *WHILE LOOP* here
3234 if (*s == CR) {
3235 if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
3236 ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
3237 !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
3239 s += sizeof(CRLFCRLF)-1;
3240 pEoh = pSod = s; // data stars here
3241 break;
3243 } else if (*s == LF) {
3244 if (*(s-1) != CR) {
3245 singleLF.AppendElement(s);
3247 if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
3248 s++;
3249 singleLF.AppendElement(s);
3250 s++;
3251 pEoh = pSod = s; // data stars here
3252 break;
3255 s++;
3259 // deal with output buffer
3260 if (!pSod) { // lets assume whole buffer is a data
3261 pSod = inPostData;
3264 uint32_t newBufferLen = 0;
3265 uint32_t dataLen = pEod - pSod;
3266 uint32_t headersLen = pEoh ? pSod - inPostData : 0;
3268 char *p; // tmp ptr into new output buf
3269 if (headersLen) { // we got a headers
3270 // this function does not make any assumption on correctness
3271 // of ContentLenHeader value in this case.
3273 newBufferLen = dataLen + headersLen;
3274 // in case there were single LFs in headers
3275 // reserve an extra space for CR will be added before each single LF
3276 int cntSingleLF = singleLF.Length();
3277 newBufferLen += cntSingleLF;
3279 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
3280 return NS_ERROR_OUT_OF_MEMORY;
3282 // deal with single LF
3283 const char *s = inPostData;
3284 if (cntSingleLF) {
3285 for (int i=0; i<cntSingleLF; i++) {
3286 const char *plf = singleLF.ElementAt(i); // ptr to single LF in headers
3287 int n = plf - s; // bytes to copy
3288 if (n) { // for '\n\n' there is nothing to memcpy
3289 memcpy(p, s, n);
3290 p += n;
3292 *p++ = CR;
3293 s = plf;
3294 *p++ = *s++;
3297 // are we done with headers?
3298 headersLen = pEoh - s;
3299 if (headersLen) { // not yet
3300 memcpy(p, s, headersLen); // copy the rest
3301 p += headersLen;
3303 } else if (dataLen) { // no ContentLenHeader is found but there is a data
3304 // make new output buffer big enough
3305 // to keep ContentLenHeader+value followed by data
3306 uint32_t l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
3307 newBufferLen = dataLen + l;
3308 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
3309 return NS_ERROR_OUT_OF_MEMORY;
3310 headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
3311 if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
3312 nsMemory::Free(p);
3313 *outPostData = 0;
3314 return NS_ERROR_FAILURE;
3316 p += headersLen;
3317 newBufferLen = headersLen + dataLen;
3319 // at this point we've done with headers.
3320 // there is a possibility that input buffer has only headers info in it
3321 // which already parsed and copied into output buffer.
3322 // copy the data
3323 if (dataLen) {
3324 memcpy(p, pSod, dataLen);
3327 *outPostDataLen = newBufferLen;
3329 return NS_OK;
3332 nsresult
3333 nsPluginHost::CreateTempFileToPost(const char *aPostDataURL, nsIFile **aTmpFile)
3335 nsresult rv;
3336 int64_t fileSize;
3337 nsAutoCString filename;
3339 // stat file == get size & convert file:///c:/ to c: if needed
3340 nsCOMPtr<nsIFile> inFile;
3341 rv = NS_GetFileFromURLSpec(nsDependentCString(aPostDataURL),
3342 getter_AddRefs(inFile));
3343 if (NS_FAILED(rv)) {
3344 nsCOMPtr<nsIFile> localFile;
3345 rv = NS_NewNativeLocalFile(nsDependentCString(aPostDataURL), false,
3346 getter_AddRefs(localFile));
3347 if (NS_FAILED(rv)) return rv;
3348 inFile = localFile;
3350 rv = inFile->GetFileSize(&fileSize);
3351 if (NS_FAILED(rv)) return rv;
3352 rv = inFile->GetNativePath(filename);
3353 if (NS_FAILED(rv)) return rv;
3355 if (fileSize != 0) {
3356 nsCOMPtr<nsIInputStream> inStream;
3357 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
3358 if (NS_FAILED(rv)) return rv;
3360 // Create a temporary file to write the http Content-length:
3361 // %ld\r\n\" header and "\r\n" == end of headers for post data to
3363 nsCOMPtr<nsIFile> tempFile;
3364 rv = GetPluginTempDir(getter_AddRefs(tempFile));
3365 if (NS_FAILED(rv))
3366 return rv;
3368 nsAutoCString inFileName;
3369 inFile->GetNativeLeafName(inFileName);
3370 // XXX hack around bug 70083
3371 inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
3372 rv = tempFile->AppendNative(inFileName);
3374 if (NS_FAILED(rv))
3375 return rv;
3377 // make it unique, and mode == 0600, not world-readable
3378 rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
3379 if (NS_FAILED(rv))
3380 return rv;
3382 nsCOMPtr<nsIOutputStream> outStream;
3383 if (NS_SUCCEEDED(rv)) {
3384 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
3385 tempFile,
3386 (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
3387 0600); // 600 so others can't read our form data
3389 NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
3390 if (NS_FAILED(rv))
3391 return rv;
3393 char buf[1024];
3394 uint32_t br, bw;
3395 bool firstRead = true;
3396 while (1) {
3397 // Read() mallocs if buffer is null
3398 rv = inStream->Read(buf, 1024, &br);
3399 if (NS_FAILED(rv) || (int32_t)br <= 0)
3400 break;
3401 if (firstRead) {
3402 //"For protocols in which the headers must be distinguished from the body,
3403 // such as HTTP, the buffer or file should contain the headers, followed by
3404 // a blank line, then the body. If no custom headers are required, simply
3405 // add a blank line ('\n') to the beginning of the file or buffer.
3407 char *parsedBuf;
3408 // assuming first 1K (or what we got) has all headers in,
3409 // lets parse it through nsPluginHost::ParsePostBufferToFixHeaders()
3410 ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
3411 rv = outStream->Write(parsedBuf, bw, &br);
3412 nsMemory::Free(parsedBuf);
3413 if (NS_FAILED(rv) || (bw != br))
3414 break;
3416 firstRead = false;
3417 continue;
3419 bw = br;
3420 rv = outStream->Write(buf, bw, &br);
3421 if (NS_FAILED(rv) || (bw != br))
3422 break;
3425 inStream->Close();
3426 outStream->Close();
3427 if (NS_SUCCEEDED(rv))
3428 *aTmpFile = tempFile.forget().get();
3430 return rv;
3433 nsresult
3434 nsPluginHost::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
3436 return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
3439 nsresult
3440 nsPluginHost::GetPluginName(nsNPAPIPluginInstance *aPluginInstance,
3441 const char** aPluginName)
3443 nsNPAPIPluginInstance *instance = static_cast<nsNPAPIPluginInstance*>(aPluginInstance);
3444 if (!instance)
3445 return NS_ERROR_FAILURE;
3447 nsNPAPIPlugin* plugin = instance->GetPlugin();
3448 if (!plugin)
3449 return NS_ERROR_FAILURE;
3451 *aPluginName = TagForPlugin(plugin)->mName.get();
3453 return NS_OK;
3456 nsresult
3457 nsPluginHost::GetPluginTagForInstance(nsNPAPIPluginInstance *aPluginInstance,
3458 nsIPluginTag **aPluginTag)
3460 NS_ENSURE_ARG_POINTER(aPluginInstance);
3461 NS_ENSURE_ARG_POINTER(aPluginTag);
3463 nsNPAPIPlugin *plugin = aPluginInstance->GetPlugin();
3464 if (!plugin)
3465 return NS_ERROR_FAILURE;
3467 *aPluginTag = TagForPlugin(plugin);
3469 NS_ADDREF(*aPluginTag);
3470 return NS_OK;
3473 NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
3475 nsRefPtr<nsPluginTag> pluginTag = mPlugins;
3476 while (pluginTag) {
3477 if (pluginTag->mUnloadTimer == timer) {
3478 if (!IsRunningPlugin(pluginTag)) {
3479 pluginTag->TryUnloadPlugin(false);
3481 return NS_OK;
3483 pluginTag = pluginTag->mNext;
3486 return NS_ERROR_FAILURE;
3489 #ifdef XP_WIN
3490 // Re-enable any top level browser windows that were disabled by modal dialogs
3491 // displayed by the crashed plugin.
3492 static void
3493 CheckForDisabledWindows()
3495 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
3496 if (!wm)
3497 return;
3499 nsCOMPtr<nsISimpleEnumerator> windowList;
3500 wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
3501 if (!windowList)
3502 return;
3504 bool haveWindows;
3505 do {
3506 windowList->HasMoreElements(&haveWindows);
3507 if (!haveWindows)
3508 return;
3510 nsCOMPtr<nsISupports> supportsWindow;
3511 windowList->GetNext(getter_AddRefs(supportsWindow));
3512 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
3513 if (baseWin) {
3514 nsCOMPtr<nsIWidget> widget;
3515 baseWin->GetMainWidget(getter_AddRefs(widget));
3516 if (widget && !widget->GetParent() &&
3517 widget->IsVisible() &&
3518 !widget->IsEnabled()) {
3519 nsIWidget* child = widget->GetFirstChild();
3520 bool enable = true;
3521 while (child) {
3522 nsWindowType aType;
3523 if (NS_SUCCEEDED(child->GetWindowType(aType)) &&
3524 aType == eWindowType_dialog) {
3525 enable = false;
3526 break;
3528 child = child->GetNextSibling();
3530 if (enable) {
3531 widget->Enable(true);
3535 } while (haveWindows);
3537 #endif
3539 void
3540 nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin,
3541 const nsAString& pluginDumpID,
3542 const nsAString& browserDumpID)
3544 nsPluginTag* crashedPluginTag = TagForPlugin(aPlugin);
3546 // Notify the app's observer that a plugin crashed so it can submit
3547 // a crashreport.
3548 bool submittedCrashReport = false;
3549 nsCOMPtr<nsIObserverService> obsService =
3550 mozilla::services::GetObserverService();
3551 nsCOMPtr<nsIWritablePropertyBag2> propbag =
3552 do_CreateInstance("@mozilla.org/hash-property-bag;1");
3553 if (obsService && propbag) {
3554 propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"),
3555 pluginDumpID);
3556 propbag->SetPropertyAsAString(NS_LITERAL_STRING("browserDumpID"),
3557 browserDumpID);
3558 propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3559 submittedCrashReport);
3560 obsService->NotifyObservers(propbag, "plugin-crashed", nullptr);
3561 // see if an observer submitted a crash report.
3562 propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"),
3563 &submittedCrashReport);
3566 // Invalidate each nsPluginInstanceTag for the crashed plugin
3568 for (uint32_t i = mInstances.Length(); i > 0; i--) {
3569 nsNPAPIPluginInstance* instance = mInstances[i - 1];
3570 if (instance->GetPlugin() == aPlugin) {
3571 // notify the content node (nsIObjectLoadingContent) that the
3572 // plugin has crashed
3573 nsCOMPtr<nsIDOMElement> domElement;
3574 instance->GetDOMElement(getter_AddRefs(domElement));
3575 nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
3576 if (objectContent) {
3577 objectContent->PluginCrashed(crashedPluginTag, pluginDumpID, browserDumpID,
3578 submittedCrashReport);
3581 instance->Destroy();
3582 mInstances.RemoveElement(instance);
3583 OnPluginInstanceDestroyed(crashedPluginTag);
3587 // Only after all instances have been invalidated is it safe to null
3588 // out nsPluginTag.mPlugin. The next time we try to create an
3589 // instance of this plugin we reload it (launch a new plugin process).
3591 crashedPluginTag->mPlugin = nullptr;
3593 #ifdef XP_WIN
3594 CheckForDisabledWindows();
3595 #endif
3598 nsNPAPIPluginInstance*
3599 nsPluginHost::FindInstance(const char *mimetype)
3601 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3602 nsNPAPIPluginInstance* instance = mInstances[i];
3604 const char* mt;
3605 nsresult rv = instance->GetMIMEType(&mt);
3606 if (NS_FAILED(rv))
3607 continue;
3609 if (PL_strcasecmp(mt, mimetype) == 0)
3610 return instance;
3613 return nullptr;
3616 nsNPAPIPluginInstance*
3617 nsPluginHost::FindOldestStoppedInstance()
3619 nsNPAPIPluginInstance *oldestInstance = nullptr;
3620 TimeStamp oldestTime = TimeStamp::Now();
3621 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3622 nsNPAPIPluginInstance *instance = mInstances[i];
3623 if (instance->IsRunning())
3624 continue;
3626 TimeStamp time = instance->StopTime();
3627 if (time < oldestTime) {
3628 oldestTime = time;
3629 oldestInstance = instance;
3633 return oldestInstance;
3636 uint32_t
3637 nsPluginHost::StoppedInstanceCount()
3639 uint32_t stoppedCount = 0;
3640 for (uint32_t i = 0; i < mInstances.Length(); i++) {
3641 nsNPAPIPluginInstance *instance = mInstances[i];
3642 if (!instance->IsRunning())
3643 stoppedCount++;
3645 return stoppedCount;
3648 nsTArray< nsRefPtr<nsNPAPIPluginInstance> >*
3649 nsPluginHost::InstanceArray()
3651 return &mInstances;
3654 void
3655 nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag)
3657 for (int32_t i = mInstances.Length(); i > 0; i--) {
3658 nsNPAPIPluginInstance *instance = mInstances[i - 1];
3659 if (instance->IsRunning() && (!aPluginTag || aPluginTag == TagForPlugin(instance->GetPlugin()))) {
3660 instance->SetWindow(nullptr);
3661 instance->Stop();
3663 // Get rid of all the instances without the possibility of caching.
3664 nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin());
3665 instance->SetWindow(nullptr);
3667 nsCOMPtr<nsIDOMElement> domElement;
3668 instance->GetDOMElement(getter_AddRefs(domElement));
3669 nsCOMPtr<nsIObjectLoadingContent> objectContent =
3670 do_QueryInterface(domElement);
3672 instance->Destroy();
3674 mInstances.RemoveElement(instance);
3675 OnPluginInstanceDestroyed(pluginTag);
3677 // Notify owning content that we destroyed its plugin out from under it
3678 if (objectContent) {
3679 objectContent->PluginDestroyed();
3685 // Runnable that does an async destroy of a plugin.
3687 class nsPluginDestroyRunnable : public nsRunnable,
3688 public PRCList
3690 public:
3691 nsPluginDestroyRunnable(nsNPAPIPluginInstance *aInstance)
3692 : mInstance(aInstance)
3694 PR_INIT_CLIST(this);
3695 PR_APPEND_LINK(this, &sRunnableListHead);
3698 virtual ~nsPluginDestroyRunnable()
3700 PR_REMOVE_LINK(this);
3703 NS_IMETHOD Run()
3705 nsRefPtr<nsNPAPIPluginInstance> instance;
3707 // Null out mInstance to make sure this code in another runnable
3708 // will do the right thing even if someone was holding on to this
3709 // runnable longer than we expect.
3710 instance.swap(mInstance);
3712 if (PluginDestructionGuard::DelayDestroy(instance)) {
3713 // It's still not safe to destroy the plugin, it's now up to the
3714 // outermost guard on the stack to take care of the destruction.
3715 return NS_OK;
3718 nsPluginDestroyRunnable *r =
3719 static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
3721 while (r != &sRunnableListHead) {
3722 if (r != this && r->mInstance == instance) {
3723 // There's another runnable scheduled to tear down
3724 // instance. Let it do the job.
3725 return NS_OK;
3727 r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
3730 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3731 ("Doing delayed destroy of instance %p\n", instance.get()));
3733 nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
3734 if (host)
3735 host->StopPluginInstance(instance);
3737 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3738 ("Done with delayed destroy of instance %p\n", instance.get()));
3740 return NS_OK;
3743 protected:
3744 nsRefPtr<nsNPAPIPluginInstance> mInstance;
3746 static PRCList sRunnableListHead;
3749 PRCList nsPluginDestroyRunnable::sRunnableListHead =
3750 PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
3752 PRCList PluginDestructionGuard::sListHead =
3753 PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
3755 PluginDestructionGuard::~PluginDestructionGuard()
3757 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
3759 PR_REMOVE_LINK(this);
3761 if (mDelayedDestroy) {
3762 // We've attempted to destroy the plugin instance we're holding on
3763 // to while we were guarding it. Do the actual destroy now, off of
3764 // a runnable.
3765 nsRefPtr<nsPluginDestroyRunnable> evt =
3766 new nsPluginDestroyRunnable(mInstance);
3768 NS_DispatchToMainThread(evt);
3772 // static
3773 bool
3774 PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
3776 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
3777 NS_ASSERTION(aInstance, "Uh, I need an instance!");
3779 // Find the first guard on the stack and make it do a delayed
3780 // destroy upon destruction.
3782 PluginDestructionGuard *g =
3783 static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
3785 while (g != &sListHead) {
3786 if (g->mInstance == aInstance) {
3787 g->mDelayedDestroy = true;
3789 return true;
3791 g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
3794 return false;