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