dom/plugins/base/nsPluginHost.cpp

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

mercurial