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