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