Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / plugin / base / src / nsPluginHostImpl.cpp
blob8857b626fc31b14d396d282e99d564698e9b545d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Sean Echevarria <sean@beatnik.com>
24 * HÃ¥kan Waara <hwaara@chello.se>
25 * Josh Aas <josh@mozilla.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 /* nsPluginHostImpl.cpp - bulk of code for managing plugins */
43 #include "nscore.h"
44 #include "nsPluginHostImpl.h"
46 #include <stdio.h>
47 #include "prio.h"
48 #include "prmem.h"
49 #include "nsNPAPIPlugin.h"
50 #include "nsNPAPIPluginStreamListener.h"
51 #include "nsPluginInstancePeer.h"
52 #include "nsIPlugin.h"
53 #include "nsIPluginInstanceInternal.h"
54 #ifdef OJI
55 #include "nsIJVMPlugin.h"
56 #include "nsIJVMPluginInstance.h"
57 #include "nsIJVMManager.h"
58 #endif
59 #include "nsIPluginStreamListener.h"
60 #include "nsIHTTPHeaderListener.h"
61 #include "nsIHttpHeaderVisitor.h"
62 #include "nsIObserverService.h"
63 #include "nsIHttpProtocolHandler.h"
64 #include "nsIHttpChannel.h"
65 #include "nsIHttpChannelInternal.h"
66 #include "nsIUploadChannel.h"
67 #include "nsIByteRangeRequest.h"
68 #include "nsIStreamListener.h"
69 #include "nsIInputStream.h"
70 #include "nsIOutputStream.h"
71 #include "nsIURL.h"
72 #include "nsXPIDLString.h"
73 #include "nsReadableUtils.h"
74 #include "nsIProtocolProxyService.h"
75 #include "nsIStreamConverterService.h"
76 #include "nsIFile.h"
77 #include "nsIInputStream.h"
78 #include "nsIIOService.h"
79 #include "nsIURL.h"
80 #include "nsIChannel.h"
81 #include "nsISeekableStream.h"
82 #include "nsNetUtil.h"
83 #include "nsIProgressEventSink.h"
84 #include "nsIDocument.h"
85 #include "nsIScriptablePlugin.h"
86 #include "nsICachingChannel.h"
87 #include "nsHashtable.h"
88 #include "nsIProxyInfo.h"
89 #include "nsObsoleteModuleLoading.h"
90 #include "nsIComponentRegistrar.h"
91 #include "nsPluginLogging.h"
92 #include "nsIPrefBranch2.h"
93 #include "nsIScriptChannel.h"
94 #include "nsPrintfCString.h"
95 #include "nsIBlocklistService.h"
96 #include "nsVersionComparator.h"
98 // Friggin' X11 has to "#define None". Lame!
99 #ifdef None
100 #undef None
101 #endif
103 #ifdef CursorShape
104 #undef CursorShape /*X.h defines it as 0,
105 qnamespace.h makes an enum type by that name
107 #endif
109 //#include "nsIRegistry.h"
110 #include "nsEnumeratorUtils.h"
111 #include "nsXPCOM.h"
112 #include "nsXPCOMCID.h"
113 #include "nsICategoryManager.h"
114 #include "nsISupportsPrimitives.h"
115 // for the dialog
116 #include "nsIStringBundle.h"
117 #include "nsIWindowWatcher.h"
118 #include "nsPIDOMWindow.h"
120 #include "nsIScriptGlobalObject.h"
121 #include "nsIScriptGlobalObjectOwner.h"
122 #include "nsIPrincipal.h"
124 #include "nsIServiceManager.h"
125 #include "nsNetCID.h"
126 #include "nsICookieService.h"
127 #include "nsIDOMPlugin.h"
128 #include "nsIDOMMimeType.h"
129 #include "nsMimeTypes.h"
130 #include "prprf.h"
131 #include "nsThreadUtils.h"
132 #include "nsIInputStreamTee.h"
133 #include "nsIInterfaceInfoManager.h"
134 #include "xptinfo.h"
136 #if defined(XP_WIN)
137 #include "windows.h"
138 #include "winbase.h"
139 #endif
141 #include "nsIMIMEService.h"
142 #include "nsCExternalHandlerService.h"
143 #include "nsILocalFile.h"
144 #include "nsIFileChannel.h"
146 #include "nsPluginSafety.h"
148 #include "nsICharsetConverterManager.h"
149 #include "nsIPlatformCharset.h"
151 #include "nsIDirectoryService.h"
152 #include "nsDirectoryServiceDefs.h"
153 #include "nsAppDirectoryServiceDefs.h"
154 #include "nsIFile.h"
155 #include "nsPluginDirServiceProvider.h"
156 #include "nsInt64.h"
157 #include "nsPluginError.h"
159 #include "nsUnicharUtils.h"
160 #include "nsPluginManifestLineReader.h"
162 #include "imgILoader.h"
163 #include "nsDefaultPlugin.h"
164 #include "nsWeakReference.h"
165 #include "nsIDOMElement.h"
166 #include "nsIDOMHTMLObjectElement.h"
167 #include "nsIDOMHTMLEmbedElement.h"
168 #include "nsIPresShell.h"
169 #include "nsPresContext.h"
170 #include "nsIWebNavigation.h"
171 #include "nsISupportsArray.h"
172 #include "nsIDocShell.h"
173 #include "nsPluginNativeWindow.h"
174 #include "nsIScriptSecurityManager.h"
175 #include "nsIContentPolicy.h"
176 #include "nsContentPolicyUtils.h"
177 #include "nsContentErrors.h"
179 #if defined(XP_UNIX) && defined(MOZ_WIDGET_GTK2) & defined(MOZ_X11)
180 #include <gdk/gdkx.h> // for GDK_DISPLAY()
181 #endif
183 #ifdef XP_MACOSX
184 #include <Carbon/Carbon.h> // for ::UseInputWindow()
185 #include <mach-o/loader.h>
186 #include <mach-o/fat.h>
187 #endif
189 #ifdef XP_OS2
190 #include "nsILegacyPluginWrapperOS2.h"
191 #endif
193 // this is the name of the directory which will be created
194 // to cache temporary files.
195 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
197 // Version of cached plugin info
198 // 0.01 first implementation
199 // 0.02 added caching of CanUnload to fix bug 105935
200 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
201 // 0.04 added new mime entry point on Mac, bug 113464
202 // 0.05 added new entry point check for the default plugin, bug 132430
203 // 0.06 strip off suffixes in mime description strings, bug 53895
204 // 0.07 changed nsIRegistry to flat file support for caching plugins info
205 // 0.08 mime entry point on MachO, bug 137535
206 // 0.09 the file encoding is changed to UTF-8, bug 420285
207 // 0.10 added plugin versions on appropriate platforms, bug 427743
208 // The current plugin registry version (and the maximum version we know how to read)
209 static const char *kPluginRegistryVersion = "0.10";
210 // The minimum registry version we know how to read
211 static const char *kMinimumRegistryVersion = "0.9";
213 // CID's && IID's
214 static NS_DEFINE_IID(kIPluginInstanceIID, NS_IPLUGININSTANCE_IID);
215 static NS_DEFINE_CID(kPluginCID, NS_PLUGIN_CID);
216 static NS_DEFINE_IID(kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
217 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
218 // for the dialog
219 static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); // needed for NS_TRY_SAFE_CALL
221 // Registry keys for caching plugin info
222 static const char kPluginsRootKey[] = "software/plugins";
223 static const char kPluginsNameKey[] = "name";
224 static const char kPluginsDescKey[] = "description";
225 static const char kPluginsFilenameKey[] = "filename";
226 static const char kPluginsFullpathKey[] = "fullpath";
227 static const char kPluginsModTimeKey[] = "lastModTimeStamp";
228 static const char kPluginsCanUnload[] = "canUnload";
229 static const char kPluginsVersionKey[] = "version";
230 static const char kPluginsMimeTypeKey[] = "mimetype";
231 static const char kPluginsMimeDescKey[] = "description";
232 static const char kPluginsMimeExtKey[] = "extension";
234 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
236 #ifdef PLUGIN_LOGGING
237 PRLogModuleInfo* nsPluginLogging::gNPNLog = nsnull;
238 PRLogModuleInfo* nsPluginLogging::gNPPLog = nsnull;
239 PRLogModuleInfo* nsPluginLogging::gPluginLog = nsnull;
240 #endif
242 #define BRAND_PROPERTIES_URL "chrome://branding/locale/brand.properties"
243 #define PLUGIN_PROPERTIES_URL "chrome://global/locale/downloadProgress.properties"
244 #define PLUGIN_REGIONAL_URL "chrome://global-region/locale/region.properties"
246 // #defines for reading prefs and extra search plugin paths from windows registry
247 #define _MAXKEYVALUE_ 8196
248 #define _NS_PREF_COMMON_PLUGIN_REG_KEY_ "browser.plugins.registry_plugins_folder_key_location"
249 #define _NS_COMMON_PLUGIN_KEY_NAME_ "Plugins Folders"
251 // #defines for plugin cache and prefs
252 #define NS_PREF_MAX_NUM_CACHED_PLUGINS "browser.plugins.max_num_cached_plugins"
253 #define DEFAULT_NUMBER_OF_STOPPED_PLUGINS 10
255 #define MAGIC_REQUEST_CONTEXT 0x01020304
257 nsresult PostPluginUnloadEvent(PRLibrary * aLibrary);
259 static nsActivePluginList *gActivePluginList;
261 #ifdef CALL_SAFETY_ON
262 PRBool gSkipPluginSafeCalls = PR_FALSE;
263 #endif
265 nsIFile *nsPluginHostImpl::sPluginTempDir;
266 nsPluginHostImpl *nsPluginHostImpl::sInst;
268 // flat file reg funcs
269 static
270 PRBool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token)
272 do {
273 if (*reader.LinePtr() == '[') {
274 char* p = reader.LinePtr() + (reader.LineLength() - 1);
275 if (*p != ']')
276 break;
277 *p = 0;
279 char* values[1];
280 if (1 != reader.ParseLine(values, 1))
281 break;
282 // ignore the leading '['
283 if (PL_strcmp(values[0]+1, token)) {
284 break; // it's wrong token
286 return PR_TRUE;
288 } while (reader.NextLine());
289 return PR_FALSE;
292 // Little helper struct to asynchronously reframe any presentations (embedded)
293 // or reload any documents (full-page), that contained plugins
294 // which were shutdown as a result of a plugins.refresh(1)
295 class nsPluginDocReframeEvent: public nsRunnable {
296 public:
297 nsPluginDocReframeEvent(nsISupportsArray* aDocs) { mDocs = aDocs; }
299 NS_DECL_NSIRUNNABLE
301 nsCOMPtr<nsISupportsArray> mDocs;
304 NS_IMETHODIMP nsPluginDocReframeEvent::Run() {
305 NS_ENSURE_TRUE(mDocs, NS_ERROR_FAILURE);
307 PRUint32 c;
308 mDocs->Count(&c);
310 // for each document (which previously had a running instance), tell
311 // the frame constructor to rebuild
312 for (PRUint32 i = 0; i < c; i++) {
313 nsCOMPtr<nsIDocument> doc (do_QueryElementAt(mDocs, i));
314 if (doc) {
315 nsIPresShell *shell = doc->GetPrimaryShell();
317 // if this document has a presentation shell, then it has frames and can be reframed
318 if (shell) {
319 /* A reframe will cause a fresh object frame, instance owner, and instance
320 * to be created. Reframing of the entire document is necessary as we may have
321 * recently found new plugins and we want a shot at trying to use them instead
322 * of leaving alternate renderings.
323 * We do not want to completely reload all the documents that had running plugins
324 * because we could possibly trigger a script to run in the unload event handler
325 * which may want to access our defunct plugin and cause us to crash.
328 shell->ReconstructFrames(); // causes reframe of document
329 } else { // no pres shell --> full-page plugin
331 NS_NOTREACHED("all plugins should have a pres shell!");
337 return mDocs->Clear();
340 nsActivePlugin::nsActivePlugin(nsPluginTag* aPluginTag,
341 nsIPluginInstance* aInstance,
342 const char * url,
343 PRBool aDefaultPlugin,
344 nsIPluginInstancePeer* peer)
346 mNext = nsnull;
347 mPeer = nsnull;
348 mPluginTag = aPluginTag;
350 mURL = PL_strdup(url);
351 mInstance = aInstance;
352 if (aInstance && peer) {
353 mPeer = peer;
354 NS_ADDREF(mPeer);
355 NS_ADDREF(aInstance);
357 mXPConnected = PR_FALSE;
358 mDefaultPlugin = aDefaultPlugin;
359 mStopped = PR_FALSE;
360 mllStopTime = LL_ZERO;
363 nsActivePlugin::~nsActivePlugin()
365 mPluginTag = nsnull;
366 if (mInstance) {
367 if (mPeer) {
368 nsresult rv = NS_OK;
369 nsCOMPtr<nsPIPluginInstancePeer> peer(do_QueryInterface(mPeer));
370 nsCOMPtr<nsIPluginInstanceOwner> owner;
371 rv = peer->GetOwner(getter_AddRefs(owner));
372 if (owner)
373 owner->SetInstance(nsnull);
376 // now check for cached plugins because they haven't had nsIPluginInstance::Destroy()
377 // called yet. For non-cached plugins, nsIPluginInstance::Destroy() is called
378 // in either nsObjectFrame::Destroy() or nsActivePluginList::stopRunning()
379 PRBool doCache = PR_TRUE;
380 mInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
381 if (doCache)
382 mInstance->Destroy();
384 NS_RELEASE(mInstance);
385 NS_IF_RELEASE(mPeer);
387 PL_strfree(mURL);
390 void nsActivePlugin::setStopped(PRBool stopped)
392 mStopped = stopped;
393 if (mStopped) // plugin instance is told to stop
394 mllStopTime = PR_Now();
395 else
396 mllStopTime = LL_ZERO;
399 nsActivePluginList::nsActivePluginList()
401 mFirst = nsnull;
402 mLast = nsnull;
403 mCount = 0;
406 nsActivePluginList::~nsActivePluginList()
408 if (mFirst == nsnull)
409 return;
410 shut();
413 void nsActivePluginList::shut()
415 if (!mFirst)
416 return;
418 for (nsActivePlugin * plugin = mFirst; plugin != nsnull;) {
419 nsActivePlugin * next = plugin->mNext;
420 remove(plugin);
421 plugin = next;
423 mFirst = nsnull;
424 mLast = nsnull;
427 PRInt32 nsActivePluginList::add(nsActivePlugin * plugin)
429 if (!mFirst) {
430 mFirst = plugin;
431 mLast = plugin;
432 mFirst->mNext = nsnull;
434 else {
435 mLast->mNext = plugin;
436 mLast = plugin;
438 mLast->mNext = nsnull;
439 mCount++;
440 return mCount;
443 PRBool nsActivePluginList::IsLastInstance(nsActivePlugin * plugin)
445 if (!plugin)
446 return PR_FALSE;
448 if (!plugin->mPluginTag)
449 return PR_FALSE;
451 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
452 if ((p->mPluginTag == plugin->mPluginTag) && (p != plugin))
453 return PR_FALSE;
455 return PR_TRUE;
458 PRBool nsActivePluginList::remove(nsActivePlugin * plugin)
460 if (mFirst == nsnull)
461 return PR_FALSE;
463 nsActivePlugin * prev = nsnull;
464 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
465 if (p == plugin) {
466 PRBool lastInstance = IsLastInstance(p);
468 if (p == mFirst)
469 mFirst = p->mNext;
470 else
471 prev->mNext = p->mNext;
473 if ((prev != nsnull) && (prev->mNext == nsnull))
474 mLast = prev;
476 // see if this is going to be the last instance of a plugin
477 // if so we should perform nsIPlugin::Shutdown and unload the library
478 // by calling nsPluginTag::TryUnloadPlugin()
479 if (lastInstance) {
480 // cache some things as we are going to destroy it right now
481 nsPluginTag *pluginTag = p->mPluginTag;
483 delete p; // plugin instance is destroyed here
485 if (pluginTag)
486 pluginTag->TryUnloadPlugin();
487 else
488 NS_ASSERTION(pluginTag, "pluginTag was not set, plugin not shutdown");
491 else
492 delete p;
494 mCount--;
495 return PR_TRUE;
497 prev = p;
499 return PR_FALSE;
502 // This method terminates all running instances of plugins and collects their
503 // documents to be returned through an array. This method is used
504 // when we are shutting down or when a plugins.refresh(1) happens.
505 // If aPluginTag is given, then only that plugin is terminated
506 void nsActivePluginList::stopRunning(nsISupportsArray* aReloadDocs,
507 nsPluginTag* aPluginTag)
509 if (mFirst == nsnull)
510 return;
512 PRBool doCallSetWindowAfterDestroy = PR_FALSE;
514 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
515 if (!p->mStopped && p->mInstance &&
516 (!aPluginTag || aPluginTag == p->mPluginTag)) {
517 // then determine if the plugin wants Destroy to be called after
518 // Set Window. This is for bug 50547.
519 p->mInstance->GetValue(nsPluginInstanceVariable_CallSetWindowAfterDestroyBool,
520 (void *) &doCallSetWindowAfterDestroy);
521 if (doCallSetWindowAfterDestroy) {
522 p->mInstance->Stop();
523 p->mInstance->Destroy();
524 p->mInstance->SetWindow(nsnull);
526 else {
527 p->mInstance->SetWindow(nsnull);
528 p->mInstance->Stop();
529 p->mInstance->Destroy();
531 doCallSetWindowAfterDestroy = PR_FALSE;
532 p->setStopped(PR_TRUE);
534 // If we've been passed an array to return, lets collect all our documents,
535 // removing duplicates. These will be reframed (embedded) or reloaded (full-page) later
536 // to kickstart our instances.
537 if (aReloadDocs && p->mPeer) {
538 nsCOMPtr<nsPIPluginInstancePeer> peer(do_QueryInterface(p->mPeer));
539 nsCOMPtr<nsIPluginInstanceOwner> owner;
540 peer->GetOwner(getter_AddRefs(owner));
541 if (owner) {
542 nsCOMPtr<nsIDocument> doc;
543 owner->GetDocument(getter_AddRefs(doc));
544 if (doc && aReloadDocs->IndexOf(doc) == -1) // don't allow for duplicates
545 aReloadDocs->AppendElement(doc);
552 void nsActivePluginList::removeAllStopped()
554 if (mFirst == nsnull)
555 return;
557 nsActivePlugin * next = nsnull;
559 for (nsActivePlugin * p = mFirst; p != nsnull;) {
560 next = p->mNext;
562 if (p->mStopped)
563 remove(p);
565 p = next;
567 return;
570 nsActivePlugin * nsActivePluginList::find(nsIPluginInstance* instance)
572 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
573 if (p->mInstance == instance) {
574 #ifdef NS_DEBUG
575 PRBool doCache = PR_TRUE;
576 p->mInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
577 NS_ASSERTION(!p->mStopped || doCache, "This plugin is not supposed to be cached!");
578 #endif
579 return p;
582 return nsnull;
585 nsActivePlugin * nsActivePluginList::find(const char * mimetype)
587 PRBool defaultplugin = (PL_strcmp(mimetype, "*") == 0);
589 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
590 // give it some special treatment for the default plugin first
591 // because we cannot tell the default plugin by asking peer for a mime type
592 if (defaultplugin && p->mDefaultPlugin)
593 return p;
595 if (!p->mPeer)
596 continue;
598 nsMIMEType mt;
600 nsresult res = p->mPeer->GetMIMEType(&mt);
602 if (NS_FAILED(res))
603 continue;
605 if (PL_strcasecmp(mt, mimetype) == 0) {
606 #ifdef NS_DEBUG
607 PRBool doCache = PR_TRUE;
608 p->mInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
609 NS_ASSERTION(!p->mStopped || doCache, "This plugin is not supposed to be cached!");
610 #endif
611 return p;
614 return nsnull;
617 nsActivePlugin * nsActivePluginList::findStopped(const char * url)
619 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
620 if (!PL_strcmp(url, p->mURL) && p->mStopped) {
621 #ifdef NS_DEBUG
622 PRBool doCache = PR_TRUE;
623 p->mInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
624 NS_ASSERTION(doCache, "This plugin is not supposed to be cached!");
625 #endif
626 return p;
629 return nsnull;
632 PRUint32 nsActivePluginList::getStoppedCount()
634 PRUint32 stoppedCount = 0;
635 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
636 if (p->mStopped)
637 stoppedCount++;
639 return stoppedCount;
642 nsActivePlugin * nsActivePluginList::findOldestStopped()
644 nsActivePlugin * res = nsnull;
645 PRInt64 llTime = LL_MAXINT;
646 for (nsActivePlugin * p = mFirst; p != nsnull; p = p->mNext) {
647 if (!p->mStopped)
648 continue;
650 if (LL_CMP(p->mllStopTime, <, llTime)) {
651 llTime = p->mllStopTime;
652 res = p;
656 #ifdef NS_DEBUG
657 if (res) {
658 PRBool doCache = PR_TRUE;
659 res->mInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
660 NS_ASSERTION(doCache, "This plugin is not supposed to be cached!");
662 #endif
664 return res;
667 inline char* new_str(const char* str)
669 if (str == nsnull)
670 return nsnull;
672 char* result = new char[strlen(str) + 1];
673 if (result != nsnull)
674 return strcpy(result, str);
675 return result;
678 nsPluginTag::nsPluginTag(nsPluginTag* aPluginTag)
679 : mPluginHost(nsnull),
680 mName(aPluginTag->mName),
681 mDescription(aPluginTag->mDescription),
682 mVariants(aPluginTag->mVariants),
683 mMimeTypeArray(nsnull),
684 mMimeDescriptionArray(aPluginTag->mMimeDescriptionArray),
685 mExtensionsArray(nsnull),
686 mLibrary(nsnull),
687 mEntryPoint(nsnull),
688 mCanUnloadLibrary(PR_TRUE),
689 mXPConnected(PR_FALSE),
690 mIsJavaPlugin(aPluginTag->mIsJavaPlugin),
691 mIsNPRuntimeEnabledJavaPlugin(aPluginTag->mIsNPRuntimeEnabledJavaPlugin),
692 mFileName(aPluginTag->mFileName),
693 mFullPath(aPluginTag->mFullPath),
694 mVersion(aPluginTag->mVersion),
695 mLastModifiedTime(0),
696 mFlags(NS_PLUGIN_FLAG_ENABLED)
698 if (aPluginTag->mMimeTypeArray != nsnull) {
699 mMimeTypeArray = new char*[mVariants];
700 for (int i = 0; i < mVariants; i++)
701 mMimeTypeArray[i] = new_str(aPluginTag->mMimeTypeArray[i]);
704 if (aPluginTag->mExtensionsArray != nsnull) {
705 mExtensionsArray = new char*[mVariants];
706 for (int i = 0; i < mVariants; i++)
707 mExtensionsArray[i] = new_str(aPluginTag->mExtensionsArray[i]);
711 nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo)
712 : mPluginHost(nsnull),
713 mName(aPluginInfo->fName),
714 mDescription(aPluginInfo->fDescription),
715 mVariants(aPluginInfo->fVariantCount),
716 mMimeTypeArray(nsnull),
717 mExtensionsArray(nsnull),
718 mLibrary(nsnull),
719 mEntryPoint(nsnull),
720 #ifdef XP_MACOSX
721 mCanUnloadLibrary(!aPluginInfo->fBundle),
722 #else
723 mCanUnloadLibrary(PR_TRUE),
724 #endif
725 mXPConnected(PR_FALSE),
726 mIsJavaPlugin(PR_FALSE),
727 mIsNPRuntimeEnabledJavaPlugin(PR_FALSE),
728 mFileName(aPluginInfo->fFileName),
729 mFullPath(aPluginInfo->fFullPath),
730 mVersion(aPluginInfo->fVersion),
731 mLastModifiedTime(0),
732 mFlags(NS_PLUGIN_FLAG_ENABLED)
734 if (aPluginInfo->fMimeTypeArray != nsnull) {
735 mMimeTypeArray = new char*[mVariants];
736 for (int i = 0; i < mVariants; i++) {
737 if (mIsJavaPlugin && aPluginInfo->fMimeTypeArray[i] &&
738 strcmp(aPluginInfo->fMimeTypeArray[i],
739 "application/x-java-vm-npruntime") == 0) {
740 mIsNPRuntimeEnabledJavaPlugin = PR_TRUE;
742 // Stop processing here, any mimetypes after the magic "I'm a
743 // NPRuntime enabled Java plugin" mimetype will be ignored.
744 mVariants = i;
746 break;
749 mMimeTypeArray[i] = new_str(aPluginInfo->fMimeTypeArray[i]);
750 if (nsPluginHostImpl::IsJavaMIMEType(mMimeTypeArray[i]))
751 mIsJavaPlugin = PR_TRUE;
755 if (aPluginInfo->fMimeDescriptionArray != nsnull) {
756 for (int i = 0; i < mVariants; i++) {
757 // we should cut off the list of suffixes which the mime
758 // description string may have, see bug 53895
759 // it is usually in form "some description (*.sf1, *.sf2)"
760 // so we can search for the opening round bracket
761 char cur = '\0';
762 char pre = '\0';
763 char * p = PL_strrchr(aPluginInfo->fMimeDescriptionArray[i], '(');
764 if (p && (p != aPluginInfo->fMimeDescriptionArray[i])) {
765 if ((p - 1) && *(p - 1) == ' ') {
766 pre = *(p - 1);
767 *(p - 1) = '\0';
768 } else {
769 cur = *p;
770 *p = '\0';
774 mMimeDescriptionArray.AppendElement(
775 aPluginInfo->fMimeDescriptionArray[i]);
776 // restore the original string
777 if (cur != '\0')
778 *p = cur;
779 if (pre != '\0')
780 *(p - 1) = pre;
782 } else {
783 mMimeDescriptionArray.SetLength(mVariants);
786 if (aPluginInfo->fExtensionArray != nsnull) {
787 mExtensionsArray = new char*[mVariants];
788 for (int i = 0; i < mVariants; i++)
789 mExtensionsArray[i] = new_str(aPluginInfo->fExtensionArray[i]);
792 EnsureMembersAreUTF8();
795 nsPluginTag::nsPluginTag(const char* aName,
796 const char* aDescription,
797 const char* aFileName,
798 const char* aFullPath,
799 const char* aVersion,
800 const char* const* aMimeTypes,
801 const char* const* aMimeDescriptions,
802 const char* const* aExtensions,
803 PRInt32 aVariants,
804 PRInt64 aLastModifiedTime,
805 PRBool aCanUnload,
806 PRBool aArgsAreUTF8)
807 : mPluginHost(nsnull),
808 mName(aName),
809 mDescription(aDescription),
810 mVariants(aVariants),
811 mMimeTypeArray(nsnull),
812 mExtensionsArray(nsnull),
813 mLibrary(nsnull),
814 mEntryPoint(nsnull),
815 mCanUnloadLibrary(aCanUnload),
816 mXPConnected(PR_FALSE),
817 mIsJavaPlugin(PR_FALSE),
818 mIsNPRuntimeEnabledJavaPlugin(PR_FALSE),
819 mFileName(aFileName),
820 mFullPath(aFullPath),
821 mVersion(aVersion),
822 mLastModifiedTime(aLastModifiedTime),
823 mFlags(0) // Caller will read in our flags from cache
825 if (aVariants) {
826 mMimeTypeArray = new char*[mVariants];
827 mExtensionsArray = new char*[mVariants];
829 for (PRInt32 i = 0; i < aVariants; ++i) {
830 if (mIsJavaPlugin && aMimeTypes[i] &&
831 strcmp(aMimeTypes[i], "application/x-java-vm-npruntime") == 0) {
832 mIsNPRuntimeEnabledJavaPlugin = PR_TRUE;
834 // Stop processing here, any mimetypes after the magic "I'm a
835 // NPRuntime enabled Java plugin" mimetype will be ignored.
836 mVariants = i;
838 break;
841 mMimeTypeArray[i] = new_str(aMimeTypes[i]);
842 mMimeDescriptionArray.AppendElement(aMimeDescriptions[i]);
843 mExtensionsArray[i] = new_str(aExtensions[i]);
844 if (nsPluginHostImpl::IsJavaMIMEType(mMimeTypeArray[i]))
845 mIsJavaPlugin = PR_TRUE;
849 if (!aArgsAreUTF8)
850 EnsureMembersAreUTF8();
853 nsPluginTag::~nsPluginTag()
855 TryUnloadPlugin(PR_TRUE);
857 // Remove mime types added to the category manager
858 // only if we were made 'active' by setting the host
859 if (mPluginHost) {
860 RegisterWithCategoryManager(PR_FALSE, nsPluginTag::ePluginUnregister);
863 if (mMimeTypeArray) {
864 for (int i = 0; i < mVariants; i++)
865 delete[] mMimeTypeArray[i];
867 delete[] (mMimeTypeArray);
868 mMimeTypeArray = nsnull;
871 if (mExtensionsArray) {
872 for (int i = 0; i < mVariants; i++)
873 delete[] mExtensionsArray[i];
875 delete[] (mExtensionsArray);
876 mExtensionsArray = nsnull;
880 NS_IMPL_ISUPPORTS1(nsPluginTag, nsIPluginTag)
882 static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
883 nsAFlatCString& aString)
885 nsresult rv;
887 PRInt32 numberOfBytes = aString.Length();
888 PRInt32 outUnicodeLen;
889 nsAutoString buffer;
890 rv = aUnicodeDecoder->GetMaxLength(aString.get(), numberOfBytes,
891 &outUnicodeLen);
892 NS_ENSURE_SUCCESS(rv, rv);
893 if (!EnsureStringLength(buffer, outUnicodeLen))
894 return NS_ERROR_OUT_OF_MEMORY;
895 rv = aUnicodeDecoder->Convert(aString.get(), &numberOfBytes,
896 buffer.BeginWriting(), &outUnicodeLen);
897 NS_ENSURE_SUCCESS(rv, rv);
898 buffer.SetLength(outUnicodeLen);
899 CopyUTF16toUTF8(buffer, aString);
901 return NS_OK;
904 nsresult nsPluginTag::EnsureMembersAreUTF8()
906 nsresult rv;
908 nsCOMPtr<nsIPlatformCharset> pcs =
909 do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
910 NS_ENSURE_SUCCESS(rv, rv);
911 nsCOMPtr<nsIUnicodeDecoder> decoder;
912 nsCOMPtr<nsICharsetConverterManager> ccm =
913 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
914 NS_ENSURE_SUCCESS(rv, rv);
916 nsCAutoString charset;
917 rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
918 NS_ENSURE_SUCCESS(rv, rv);
919 if (!charset.LowerCaseEqualsLiteral("utf-8")) {
920 rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
921 NS_ENSURE_SUCCESS(rv, rv);
923 ConvertToUTF8(decoder, mFileName);
924 ConvertToUTF8(decoder, mFullPath);
927 // The description of the plug-in and the various MIME type descriptions
928 // should be encoded in the standard plain text file encoding for this system.
929 // XXX should we add kPlatformCharsetSel_PluginResource?
930 rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
931 NS_ENSURE_SUCCESS(rv, rv);
932 if (!charset.LowerCaseEqualsLiteral("utf-8")) {
933 rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
934 NS_ENSURE_SUCCESS(rv, rv);
936 ConvertToUTF8(decoder, mName);
937 ConvertToUTF8(decoder, mDescription);
938 for (PRUint32 i = 0; i < mMimeDescriptionArray.Length(); ++i) {
939 ConvertToUTF8(decoder, mMimeDescriptionArray[i]);
942 return NS_OK;
945 void nsPluginTag::SetHost(nsPluginHostImpl * aHost)
947 mPluginHost = aHost;
950 NS_IMETHODIMP
951 nsPluginTag::GetDescription(nsACString& aDescription)
953 aDescription = mDescription;
954 return NS_OK;
957 NS_IMETHODIMP
958 nsPluginTag::GetFilename(nsACString& aFileName)
960 aFileName = mFileName;
961 return NS_OK;
964 NS_IMETHODIMP
965 nsPluginTag::GetVersion(nsACString& aVersion)
967 aVersion = mVersion;
968 return NS_OK;
971 NS_IMETHODIMP
972 nsPluginTag::GetName(nsACString& aName)
974 aName = mName;
975 return NS_OK;
978 NS_IMETHODIMP
979 nsPluginTag::GetDisabled(PRBool* aDisabled)
981 *aDisabled = !HasFlag(NS_PLUGIN_FLAG_ENABLED);
982 return NS_OK;
985 NS_IMETHODIMP
986 nsPluginTag::SetDisabled(PRBool aDisabled)
988 if (HasFlag(NS_PLUGIN_FLAG_ENABLED) == !aDisabled)
989 return NS_OK;
991 if (mIsJavaPlugin) {
992 nsresult rv;
993 nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
994 NS_ENSURE_SUCCESS(rv, rv);
996 PRBool javaEnabled;
997 rv = pref->GetBoolPref("security.enable_java", &javaEnabled);
998 NS_ENSURE_SUCCESS(rv, rv);
1000 if (javaEnabled == aDisabled)
1001 return pref->SetBoolPref("security.enable_java", !aDisabled);
1004 if (aDisabled)
1005 UnMark(NS_PLUGIN_FLAG_ENABLED);
1006 else
1007 Mark(NS_PLUGIN_FLAG_ENABLED);
1009 mPluginHost->UpdatePluginInfo(this);
1010 return NS_OK;
1013 NS_IMETHODIMP
1014 nsPluginTag::GetBlocklisted(PRBool* aBlocklisted)
1016 *aBlocklisted = HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED);
1017 return NS_OK;
1020 NS_IMETHODIMP
1021 nsPluginTag::SetBlocklisted(PRBool aBlocklisted)
1023 if (HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED) == aBlocklisted)
1024 return NS_OK;
1026 if (aBlocklisted)
1027 Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
1028 else
1029 UnMark(NS_PLUGIN_FLAG_BLOCKLISTED);
1031 mPluginHost->UpdatePluginInfo(nsnull);
1032 return NS_OK;
1035 // helper struct for asynchronous handeling of plugin unloading
1036 class nsPluginUnloadEvent : public nsRunnable {
1037 public:
1038 nsPluginUnloadEvent(PRLibrary* aLibrary)
1039 : mLibrary(aLibrary)
1042 NS_DECL_NSIRUNNABLE
1044 PRLibrary* mLibrary;
1047 NS_IMETHODIMP nsPluginUnloadEvent::Run()
1049 if (mLibrary) {
1050 // put our unload call in a saftey wrapper
1051 NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(mLibrary), nsnull, nsnull);
1052 } else {
1053 NS_WARNING("missing library from nsPluginUnloadEvent");
1055 return NS_OK;
1058 // unload plugin asynchronously if possible, otherwise just unload now
1059 nsresult PostPluginUnloadEvent(PRLibrary* aLibrary)
1061 nsCOMPtr<nsIRunnable> ev = new nsPluginUnloadEvent(aLibrary);
1062 if (ev && NS_SUCCEEDED(NS_DispatchToCurrentThread(ev)))
1063 return NS_OK;
1065 // failure case
1066 NS_TRY_SAFE_CALL_VOID(PR_UnloadLibrary(aLibrary), nsnull, nsnull);
1068 return NS_ERROR_FAILURE;
1071 void nsPluginTag::TryUnloadPlugin(PRBool aForceShutdown)
1073 PRBool isXPCOM = PR_FALSE;
1074 if (!(mFlags & NS_PLUGIN_FLAG_OLDSCHOOL))
1075 isXPCOM = PR_TRUE;
1077 if (isXPCOM && !aForceShutdown) return;
1079 if (mEntryPoint) {
1080 mEntryPoint->Shutdown();
1081 mEntryPoint->Release();
1082 mEntryPoint = nsnull;
1085 // before we unload check if we are allowed to, see bug #61388
1086 // also, never unload an XPCOM plugin library
1087 if (mLibrary && mCanUnloadLibrary && !isXPCOM) {
1088 // NPAPI plugins can be unloaded now if they don't use XPConnect
1089 if (!mXPConnected)
1090 // unload the plugin asynchronously by posting a PLEvent
1091 PostPluginUnloadEvent(mLibrary);
1092 else {
1093 // add library to the unused library list to handle it later
1094 if (mPluginHost)
1095 mPluginHost->AddUnusedLibrary(mLibrary);
1099 // we should zero it anyway, it is going to be unloaded by
1100 // CleanUnsedLibraries before we need to call the library
1101 // again so the calling code should not be fooled and reload
1102 // the library fresh
1103 mLibrary = nsnull;
1106 PRBool nsPluginTag::Equals(nsPluginTag *aPluginTag)
1108 NS_ENSURE_TRUE(aPluginTag, PR_FALSE);
1110 if ((!mName.Equals(aPluginTag->mName)) ||
1111 (!mDescription.Equals(aPluginTag->mDescription)) ||
1112 (mVariants != aPluginTag->mVariants))
1113 return PR_FALSE;
1115 if (mVariants && mMimeTypeArray && aPluginTag->mMimeTypeArray) {
1116 for (PRInt32 i = 0; i < mVariants; i++) {
1117 if (PL_strcmp(mMimeTypeArray[i], aPluginTag->mMimeTypeArray[i]) != 0)
1118 return PR_FALSE;
1121 return PR_TRUE;
1124 class nsPluginStreamListenerPeer;
1126 class nsPluginStreamInfo : public nsINPAPIPluginStreamInfo
1128 public:
1129 nsPluginStreamInfo();
1130 virtual ~nsPluginStreamInfo();
1132 NS_DECL_ISUPPORTS
1134 // nsINPAPIPluginStreamInfo interface
1136 NS_IMETHOD
1137 GetContentType(nsMIMEType* result);
1139 NS_IMETHOD
1140 IsSeekable(PRBool* result);
1142 NS_IMETHOD
1143 GetLength(PRUint32* result);
1145 NS_IMETHOD
1146 GetLastModified(PRUint32* result);
1148 NS_IMETHOD
1149 GetURL(const char** result);
1151 NS_IMETHOD
1152 RequestRead(nsByteRange* rangeList);
1154 NS_IMETHOD
1155 GetStreamOffset(PRInt32 *result);
1157 NS_IMETHOD
1158 SetStreamOffset(PRInt32 result);
1160 // local methods
1162 void
1163 SetContentType(const nsMIMEType contentType);
1165 void
1166 SetSeekable(const PRBool seekable);
1168 void
1169 SetLength(const PRUint32 length);
1171 void
1172 SetLastModified(const PRUint32 modified);
1174 void
1175 SetURL(const char* url);
1177 void
1178 SetPluginInstance(nsIPluginInstance * aPluginInstance);
1180 void
1181 SetPluginStreamListenerPeer(nsPluginStreamListenerPeer * aPluginStreamListenerPeer);
1183 void
1184 MakeByteRangeString(nsByteRange* aRangeList, nsACString &string, PRInt32 *numRequests);
1186 PRBool
1187 UseExistingPluginCacheFile(nsPluginStreamInfo* psi);
1189 void
1190 SetStreamComplete(const PRBool complete);
1192 void
1193 SetRequest(nsIRequest *request)
1195 mRequest = request;
1198 private:
1200 char* mContentType;
1201 char* mURL;
1202 PRBool mSeekable;
1203 PRUint32 mLength;
1204 PRUint32 mModified;
1205 nsIPluginInstance * mPluginInstance;
1206 nsPluginStreamListenerPeer * mPluginStreamListenerPeer;
1207 PRInt32 mStreamOffset;
1208 PRBool mStreamComplete;
1211 class nsPluginStreamListenerPeer : public nsIStreamListener,
1212 public nsIProgressEventSink,
1213 public nsIHttpHeaderVisitor,
1214 public nsSupportsWeakReference
1216 public:
1217 nsPluginStreamListenerPeer();
1218 virtual ~nsPluginStreamListenerPeer();
1220 NS_DECL_ISUPPORTS
1221 NS_DECL_NSIPROGRESSEVENTSINK
1222 NS_DECL_NSIREQUESTOBSERVER
1223 NS_DECL_NSISTREAMLISTENER
1224 NS_DECL_NSIHTTPHEADERVISITOR
1226 // Called by GetURL and PostURL (via NewStream)
1227 nsresult Initialize(nsIURI *aURL,
1228 nsIPluginInstance *aInstance,
1229 nsIPluginStreamListener *aListener,
1230 PRInt32 requestCount = 1);
1232 nsresult InitializeEmbedded(nsIURI *aURL,
1233 nsIPluginInstance* aInstance,
1234 nsIPluginInstanceOwner *aOwner = nsnull,
1235 nsIPluginHost *aHost = nsnull);
1237 nsresult InitializeFullPage(nsIPluginInstance *aInstance);
1239 nsresult OnFileAvailable(nsIFile* aFile);
1241 nsresult ServeStreamAsFile(nsIRequest *request, nsISupports *ctxt);
1243 nsIPluginInstance *GetPluginInstance() { return mInstance; }
1245 private:
1246 nsresult SetUpCache(nsIURI* aURL); // todo: see about removing this...
1247 nsresult SetUpStreamListener(nsIRequest* request, nsIURI* aURL);
1248 nsresult SetupPluginCacheFile(nsIChannel* channel);
1250 nsIURI *mURL;
1251 nsIPluginInstanceOwner *mOwner;
1252 nsIPluginInstance *mInstance;
1253 nsIPluginStreamListener *mPStreamListener;
1254 nsRefPtr<nsPluginStreamInfo> mPluginStreamInfo;
1256 // Set to PR_TRUE if we request failed (like with a HTTP response of 404)
1257 PRPackedBool mRequestFailed;
1260 * Set to PR_TRUE after nsIPluginInstancePeer::OnStartBinding() has
1261 * been called. Checked in ::OnStopRequest so we can call the
1262 * plugin's OnStartBinding if, for some reason, it has not already
1263 * been called.
1265 PRPackedBool mStartBinding;
1266 PRPackedBool mHaveFiredOnStartRequest;
1267 // these get passed to the plugin stream listener
1268 char *mMIMEType;
1269 PRUint32 mLength;
1270 nsPluginStreamType mStreamType;
1271 nsIPluginHost *mHost;
1273 // local cached file, we save the content into local cache if browser cache is not available,
1274 // or plugin asks stream as file and it expects file extension until bug 90558 got fixed
1275 nsIFile *mLocalCachedFile;
1276 nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
1277 nsHashtable *mDataForwardToRequest;
1279 public:
1280 PRBool mAbort;
1281 PRInt32 mPendingRequests;
1282 nsWeakPtr mWeakPtrChannelCallbacks;
1283 nsWeakPtr mWeakPtrChannelLoadGroup;
1286 class nsPluginByteRangeStreamListener : public nsIStreamListener {
1287 public:
1288 nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
1289 virtual ~nsPluginByteRangeStreamListener();
1291 // nsISupports
1292 NS_DECL_ISUPPORTS
1294 // nsIRequestObserver methods:
1295 NS_DECL_NSIREQUESTOBSERVER
1297 // nsIStreamListener methods:
1298 NS_DECL_NSISTREAMLISTENER
1300 private:
1301 nsCOMPtr<nsIStreamListener> mStreamConverter;
1302 nsWeakPtr mWeakPtrPluginStreamListenerPeer;
1303 PRBool mRemoveMagicNumber;
1306 nsPluginStreamInfo::nsPluginStreamInfo()
1308 mPluginInstance = nsnull;
1309 mPluginStreamListenerPeer = nsnull;
1311 mContentType = nsnull;
1312 mURL = nsnull;
1313 mSeekable = PR_FALSE;
1314 mLength = 0;
1315 mModified = 0;
1316 mStreamOffset = 0;
1317 mStreamComplete = PR_FALSE;
1320 nsPluginStreamInfo::~nsPluginStreamInfo()
1322 if (mContentType)
1323 PL_strfree(mContentType);
1324 if (mURL)
1325 PL_strfree(mURL);
1327 NS_IF_RELEASE(mPluginInstance);
1330 NS_IMPL_ISUPPORTS2(nsPluginStreamInfo, nsIPluginStreamInfo,
1331 nsINPAPIPluginStreamInfo)
1333 NS_IMETHODIMP
1334 nsPluginStreamInfo::GetContentType(nsMIMEType* result)
1336 *result = mContentType;
1337 return NS_OK;
1340 NS_IMETHODIMP
1341 nsPluginStreamInfo::IsSeekable(PRBool* result)
1343 *result = mSeekable;
1344 return NS_OK;
1347 NS_IMETHODIMP
1348 nsPluginStreamInfo::GetLength(PRUint32* result)
1350 *result = mLength;
1351 return NS_OK;
1354 NS_IMETHODIMP
1355 nsPluginStreamInfo::GetLastModified(PRUint32* result)
1357 *result = mModified;
1358 return NS_OK;
1361 NS_IMETHODIMP
1362 nsPluginStreamInfo::GetURL(const char** result)
1364 *result = mURL;
1365 return NS_OK;
1368 void
1369 nsPluginStreamInfo::MakeByteRangeString(nsByteRange* aRangeList, nsACString &rangeRequest, PRInt32 *numRequests)
1371 rangeRequest.Truncate();
1372 *numRequests = 0;
1373 //the string should look like this: bytes=500-700,601-999
1374 if (!aRangeList)
1375 return;
1377 PRInt32 requestCnt = 0;
1378 nsCAutoString string("bytes=");
1380 for (nsByteRange * range = aRangeList; range != nsnull; range = range->next) {
1381 // XXX zero length?
1382 if (!range->length)
1383 continue;
1385 // XXX needs to be fixed for negative offsets
1386 string.AppendInt(range->offset);
1387 string.Append("-");
1388 string.AppendInt(range->offset + range->length - 1);
1389 if (range->next)
1390 string += ",";
1392 requestCnt++;
1395 // get rid of possible trailing comma
1396 string.Trim(",", PR_FALSE);
1398 rangeRequest = string;
1399 *numRequests = requestCnt;
1400 return;
1403 NS_IMETHODIMP
1404 nsPluginStreamInfo::RequestRead(nsByteRange* rangeList)
1406 nsCAutoString rangeString;
1407 PRInt32 numRequests;
1409 //first of all lets see if mPluginStreamListenerPeer is still alive
1410 nsCOMPtr<nsISupportsWeakReference> suppWeakRef(
1411 do_QueryInterface((nsISupportsWeakReference *)(mPluginStreamListenerPeer)));
1412 if (!suppWeakRef)
1413 return NS_ERROR_FAILURE;
1415 nsCOMPtr<nsIWeakReference> pWeakRefPluginStreamListenerPeer =
1416 do_GetWeakReference(suppWeakRef);
1417 if (!pWeakRefPluginStreamListenerPeer)
1418 return NS_ERROR_FAILURE;
1420 MakeByteRangeString(rangeList, rangeString, &numRequests);
1422 if (numRequests == 0)
1423 return NS_ERROR_FAILURE;
1425 nsresult rv = NS_OK;
1426 nsCOMPtr<nsIURI> url;
1428 rv = NS_NewURI(getter_AddRefs(url), nsDependentCString(mURL));
1430 nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mPluginStreamListenerPeer->mWeakPtrChannelCallbacks);
1431 nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mPluginStreamListenerPeer->mWeakPtrChannelLoadGroup);
1432 nsCOMPtr<nsIChannel> channel;
1433 rv = NS_NewChannel(getter_AddRefs(channel), url, nsnull, loadGroup, callbacks);
1434 if (NS_FAILED(rv))
1435 return rv;
1437 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1438 if (!httpChannel)
1439 return NS_ERROR_FAILURE;
1441 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
1443 mPluginStreamListenerPeer->mAbort = PR_TRUE; // instruct old stream listener to cancel
1444 // the request on the next ODA.
1446 nsCOMPtr<nsIStreamListener> converter;
1448 if (numRequests == 1) {
1449 converter = mPluginStreamListenerPeer;
1451 // set current stream offset equal to the first offset in the range list
1452 // it will work for single byte range request
1453 // for multy range we'll reset it in ODA
1454 SetStreamOffset(rangeList->offset);
1455 } else {
1456 nsPluginByteRangeStreamListener *brrListener =
1457 new nsPluginByteRangeStreamListener(pWeakRefPluginStreamListenerPeer);
1458 if (brrListener)
1459 converter = brrListener;
1460 else
1461 return NS_ERROR_OUT_OF_MEMORY;
1464 mPluginStreamListenerPeer->mPendingRequests += numRequests;
1466 nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
1467 if (NS_FAILED(rv))
1468 return rv;
1469 rv = container->SetData(MAGIC_REQUEST_CONTEXT);
1470 if (NS_FAILED(rv))
1471 return rv;
1473 return channel->AsyncOpen(converter, container);
1476 NS_IMETHODIMP
1477 nsPluginStreamInfo::GetStreamOffset(PRInt32 *result)
1479 *result = mStreamOffset;
1480 return NS_OK;
1483 NS_IMETHODIMP
1484 nsPluginStreamInfo::SetStreamOffset(PRInt32 offset)
1486 mStreamOffset = offset;
1487 return NS_OK;
1490 void
1491 nsPluginStreamInfo::SetContentType(const nsMIMEType contentType)
1493 if (mContentType != nsnull)
1494 PL_strfree(mContentType);
1496 mContentType = PL_strdup(contentType);
1499 void
1500 nsPluginStreamInfo::SetSeekable(const PRBool seekable)
1502 mSeekable = seekable;
1505 void
1506 nsPluginStreamInfo::SetLength(const PRUint32 length)
1508 mLength = length;
1511 void
1512 nsPluginStreamInfo::SetLastModified(const PRUint32 modified)
1514 mModified = modified;
1517 void
1518 nsPluginStreamInfo::SetURL(const char* url)
1520 if (mURL)
1521 PL_strfree(mURL);
1523 mURL = PL_strdup(url);
1526 void
1527 nsPluginStreamInfo::SetPluginInstance(nsIPluginInstance * aPluginInstance)
1529 NS_IF_ADDREF(mPluginInstance = aPluginInstance);
1532 void
1533 nsPluginStreamInfo::SetPluginStreamListenerPeer(nsPluginStreamListenerPeer * aPluginStreamListenerPeer)
1535 // not addref'd - nsPluginStreamInfo is owned by mPluginStreamListenerPeer
1536 mPluginStreamListenerPeer = aPluginStreamListenerPeer;
1539 class nsPluginCacheListener : public nsIStreamListener
1541 public:
1542 nsPluginCacheListener(nsPluginStreamListenerPeer* aListener);
1543 virtual ~nsPluginCacheListener();
1545 NS_DECL_ISUPPORTS
1547 NS_DECL_NSIREQUESTOBSERVER
1548 NS_DECL_NSISTREAMLISTENER
1550 private:
1551 nsPluginStreamListenerPeer* mListener;
1554 nsPluginCacheListener::nsPluginCacheListener(nsPluginStreamListenerPeer* aListener)
1556 mListener = aListener;
1557 NS_ADDREF(mListener);
1560 nsPluginCacheListener::~nsPluginCacheListener()
1562 NS_IF_RELEASE(mListener);
1565 NS_IMPL_ISUPPORTS1(nsPluginCacheListener, nsIStreamListener)
1567 NS_IMETHODIMP
1568 nsPluginCacheListener::OnStartRequest(nsIRequest *request, nsISupports* ctxt)
1570 return NS_OK;
1573 NS_IMETHODIMP
1574 nsPluginCacheListener::OnDataAvailable(nsIRequest *request, nsISupports* ctxt,
1575 nsIInputStream* aIStream,
1576 PRUint32 sourceOffset,
1577 PRUint32 aLength)
1580 PRUint32 readlen;
1581 char* buffer = (char*) PR_Malloc(aLength);
1583 // if we don't read from the stream, OnStopRequest will never be called
1584 if (!buffer)
1585 return NS_ERROR_OUT_OF_MEMORY;
1587 nsresult rv = aIStream->Read(buffer, aLength, &readlen);
1589 NS_ASSERTION(aLength == readlen, "nsCacheListener->OnDataAvailable: "
1590 "readlen != aLength");
1592 PR_Free(buffer);
1593 return rv;
1596 NS_IMETHODIMP
1597 nsPluginCacheListener::OnStopRequest(nsIRequest *request,
1598 nsISupports* aContext,
1599 nsresult aStatus)
1601 return NS_OK;
1604 nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
1606 mURL = nsnull;
1607 mOwner = nsnull;
1608 mInstance = nsnull;
1609 mPStreamListener = nsnull;
1610 mHost = nsnull;
1611 mStreamType = nsPluginStreamType_Normal;
1612 mStartBinding = PR_FALSE;
1613 mAbort = PR_FALSE;
1614 mRequestFailed = PR_FALSE;
1616 mPendingRequests = 0;
1617 mHaveFiredOnStartRequest = PR_FALSE;
1618 mDataForwardToRequest = nsnull;
1619 mLocalCachedFile = nsnull;
1622 nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
1624 #ifdef PLUGIN_LOGGING
1625 nsCAutoString urlSpec;
1626 if (mURL != nsnull) (void)mURL->GetSpec(urlSpec);
1628 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
1629 ("nsPluginStreamListenerPeer::dtor this=%p, url=%s%c",this, urlSpec.get(), mLocalCachedFile?',':'\n'));
1630 #endif
1632 NS_IF_RELEASE(mURL);
1633 NS_IF_RELEASE(mOwner);
1634 NS_IF_RELEASE(mInstance);
1635 NS_IF_RELEASE(mPStreamListener);
1636 NS_IF_RELEASE(mHost);
1638 // close FD of mFileCacheOutputStream if it's still open
1639 // or we won't be able to remove the cache file
1640 if (mFileCacheOutputStream)
1641 mFileCacheOutputStream = nsnull;
1643 // if we have mLocalCachedFile lets release it
1644 // and it'll be fiscally remove if refcnt == 1
1645 if (mLocalCachedFile) {
1646 nsrefcnt refcnt;
1647 NS_RELEASE2(mLocalCachedFile, refcnt);
1649 #ifdef PLUGIN_LOGGING
1650 nsCAutoString filePath;
1651 mLocalCachedFile->GetNativePath(filePath);
1653 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
1654 ("LocalyCachedFile=%s has %d refcnt and will %s be deleted now\n",filePath.get(),refcnt,refcnt==1?"":"NOT"));
1655 #endif
1657 if (refcnt == 1) {
1658 mLocalCachedFile->Remove(PR_FALSE);
1659 NS_RELEASE(mLocalCachedFile);
1663 delete mDataForwardToRequest;
1666 NS_IMPL_ISUPPORTS4(nsPluginStreamListenerPeer,
1667 nsIStreamListener,
1668 nsIRequestObserver,
1669 nsIHttpHeaderVisitor,
1670 nsISupportsWeakReference)
1672 // Called as a result of GetURL and PostURL
1673 nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
1674 nsIPluginInstance *aInstance,
1675 nsIPluginStreamListener* aListener,
1676 PRInt32 requestCount)
1678 #ifdef PLUGIN_LOGGING
1679 nsCAutoString urlSpec;
1680 if (aURL != nsnull) (void)aURL->GetAsciiSpec(urlSpec);
1682 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
1683 ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
1685 PR_LogFlush();
1686 #endif
1688 mURL = aURL;
1689 NS_ADDREF(mURL);
1691 mInstance = aInstance;
1692 NS_ADDREF(mInstance);
1694 mPStreamListener = aListener;
1695 NS_ADDREF(mPStreamListener);
1697 mPluginStreamInfo = new nsPluginStreamInfo();
1698 if (!mPluginStreamInfo)
1699 return NS_ERROR_OUT_OF_MEMORY;
1701 mPluginStreamInfo->SetPluginInstance(aInstance);
1702 mPluginStreamInfo->SetPluginStreamListenerPeer(this);
1704 mPendingRequests = requestCount;
1706 mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
1707 if (!mDataForwardToRequest)
1708 return NS_ERROR_FAILURE;
1710 return NS_OK;
1714 /* Called by NewEmbeddedPluginStream() - if this is called, we weren't
1715 * able to load the plugin, so we need to load it later once we figure
1716 * out the mimetype. In order to load it later, we need the plugin
1717 * host and instance owner.
1719 nsresult nsPluginStreamListenerPeer::InitializeEmbedded(nsIURI *aURL,
1720 nsIPluginInstance* aInstance,
1721 nsIPluginInstanceOwner *aOwner,
1722 nsIPluginHost *aHost)
1724 #ifdef PLUGIN_LOGGING
1725 nsCAutoString urlSpec;
1726 (void)aURL->GetSpec(urlSpec);
1728 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
1729 ("nsPluginStreamListenerPeer::InitializeEmbedded url=%s\n", urlSpec.get()));
1731 PR_LogFlush();
1732 #endif
1734 mURL = aURL;
1735 NS_ADDREF(mURL);
1737 if (aInstance) {
1738 NS_ASSERTION(mInstance == nsnull, "nsPluginStreamListenerPeer::InitializeEmbedded mInstance != nsnull");
1739 mInstance = aInstance;
1740 NS_ADDREF(mInstance);
1741 } else {
1742 mOwner = aOwner;
1743 NS_IF_ADDREF(mOwner);
1745 mHost = aHost;
1746 NS_IF_ADDREF(mHost);
1749 mPluginStreamInfo = new nsPluginStreamInfo();
1750 if (!mPluginStreamInfo)
1751 return NS_ERROR_OUT_OF_MEMORY;
1753 mPluginStreamInfo->SetPluginInstance(aInstance);
1754 mPluginStreamInfo->SetPluginStreamListenerPeer(this);
1756 mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
1757 if (!mDataForwardToRequest)
1758 return NS_ERROR_FAILURE;
1760 return NS_OK;
1764 // Called by NewFullPagePluginStream()
1765 nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIPluginInstance *aInstance)
1767 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
1768 ("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
1770 NS_ASSERTION(mInstance == nsnull, "nsPluginStreamListenerPeer::InitializeFullPage mInstance != nsnull");
1771 mInstance = aInstance;
1772 NS_ADDREF(mInstance);
1774 mPluginStreamInfo = new nsPluginStreamInfo();
1775 if (!mPluginStreamInfo)
1776 return NS_ERROR_OUT_OF_MEMORY;
1778 mPluginStreamInfo->SetPluginInstance(aInstance);
1779 mPluginStreamInfo->SetPluginStreamListenerPeer(this);
1781 mDataForwardToRequest = new nsHashtable(16, PR_FALSE);
1782 if (!mDataForwardToRequest)
1783 return NS_ERROR_FAILURE;
1785 return NS_OK;
1788 // SetupPluginCacheFile is called if we have to save the stream to disk.
1789 // the most likely cause for this is either there is no disk cache available
1790 // or the stream is coming from a https server.
1792 // These files will be deleted when the host is destroyed.
1794 // TODO? What if we fill up the the dest dir?
1795 nsresult
1796 nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
1798 nsresult rv = NS_OK;
1799 // lets try to reused a file if we already have in the local plugin cache
1800 // we loop through all of active plugins
1801 // and call |nsPluginStreamInfo::UseExistingPluginCacheFile()| on opened stream
1802 // will return RP_TRUE if file exisrs
1803 // and some conditions are matched, in this case that file will be use
1804 // in |::OnFileAvailable()| calls w/o rewriting the file again.
1805 // The file will be deleted in |nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer|
1806 PRBool useExistingCacheFile = PR_FALSE;
1807 nsActivePlugin *pActivePlugins = gActivePluginList->mFirst;
1808 while (pActivePlugins && pActivePlugins->mStreams && !useExistingCacheFile) {
1809 // most recent streams are at the end of list
1810 PRInt32 cnt;
1811 pActivePlugins->mStreams->Count((PRUint32*)&cnt);
1812 while (--cnt >= 0 && !useExistingCacheFile) {
1813 nsPluginStreamListenerPeer *lp =
1814 reinterpret_cast<nsPluginStreamListenerPeer *>(pActivePlugins->mStreams->ElementAt(cnt));
1815 if (lp) {
1816 if (lp->mLocalCachedFile &&
1817 lp->mPluginStreamInfo &&
1818 (useExistingCacheFile =
1819 lp->mPluginStreamInfo->UseExistingPluginCacheFile(mPluginStreamInfo))) {
1820 NS_ADDREF(mLocalCachedFile = lp->mLocalCachedFile);
1822 NS_RELEASE(lp);
1825 pActivePlugins = pActivePlugins->mNext;
1828 if (!useExistingCacheFile) {
1829 nsCOMPtr<nsIFile> pluginTmp;
1830 rv = nsPluginHostImpl::GetPluginTempDir(getter_AddRefs(pluginTmp));
1831 if (NS_FAILED(rv)) {
1832 return rv;
1835 // Get the filename from the channel
1836 nsCOMPtr<nsIURI> uri;
1837 rv = channel->GetURI(getter_AddRefs(uri));
1838 if (NS_FAILED(rv)) return rv;
1840 nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
1841 if (!url)
1842 return NS_ERROR_FAILURE;
1844 nsCAutoString filename;
1845 url->GetFileName(filename);
1846 if (NS_FAILED(rv))
1847 return rv;
1849 // Create a file to save our stream into. Should we scramble the name?
1850 filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
1851 rv = pluginTmp->AppendNative(filename);
1852 if (NS_FAILED(rv))
1853 return rv;
1855 // Yes, make it unique.
1856 rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
1857 if (NS_FAILED(rv))
1858 return rv;
1860 // create a file output stream to write to...
1861 nsCOMPtr<nsIOutputStream> outstream;
1862 rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
1863 if (NS_FAILED(rv))
1864 return rv;
1866 // save the file.
1867 CallQueryInterface(pluginTmp, &mLocalCachedFile); // no need to check return value, just addref
1868 // add one extra refcnt, we can use NS_RELEASE2(mLocalCachedFile...) in dtor
1869 // to remove this file when refcnt == 1
1870 NS_ADDREF(mLocalCachedFile);
1873 // add this listenerPeer to list of stream peers for this instance
1874 // it'll delay release of listenerPeer until nsActivePlugin::~nsActivePlugin
1875 // and the temp file is going to stay alive until then
1876 pActivePlugins = gActivePluginList->find(mInstance);
1877 if (pActivePlugins) {
1878 if (!pActivePlugins->mStreams &&
1879 (NS_FAILED(rv = NS_NewISupportsArray(getter_AddRefs(pActivePlugins->mStreams))))) {
1880 return rv;
1883 nsISupports* supports = static_cast<nsISupports*>((static_cast<nsIStreamListener*>(this)));
1884 pActivePlugins->mStreams->AppendElement(supports);
1887 return rv;
1890 NS_IMETHODIMP
1891 nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
1892 nsISupports* aContext)
1894 nsresult rv = NS_OK;
1896 if (mHaveFiredOnStartRequest) {
1897 return NS_OK;
1900 mHaveFiredOnStartRequest = PR_TRUE;
1902 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
1903 NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
1905 // deal with 404 (Not Found) HTTP response,
1906 // just return, this causes the request to be ignored.
1907 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1908 if (httpChannel) {
1909 PRUint32 responseCode = 0;
1910 rv = httpChannel->GetResponseStatus(&responseCode);
1911 if (NS_FAILED(rv)) {
1912 // NPP_Notify() will be called from OnStopRequest
1913 // in nsNPAPIPluginStreamListener::CleanUpStream
1914 // return error will cancel this request
1915 // ...and we also need to tell the plugin that
1916 mRequestFailed = PR_TRUE;
1917 return NS_ERROR_FAILURE;
1920 if (responseCode > 206) { // not normal
1921 PRBool bWantsAllNetworkStreams = PR_FALSE;
1922 mInstance->GetValue(nsPluginInstanceVariable_WantsAllNetworkStreams,
1923 (void *)&bWantsAllNetworkStreams);
1924 if (!bWantsAllNetworkStreams) {
1925 mRequestFailed = PR_TRUE;
1926 return NS_ERROR_FAILURE;
1931 // do a little sanity check to make sure our frame isn't gone
1932 // by getting the tag type and checking for an error, we can determine if
1933 // the frame is gone
1934 if (mOwner) {
1935 nsCOMPtr<nsIPluginTagInfo2> pti2 = do_QueryInterface(mOwner);
1936 NS_ENSURE_TRUE(pti2, NS_ERROR_FAILURE);
1937 nsPluginTagType tagType;
1938 if (NS_FAILED(pti2->GetTagType(&tagType)))
1939 return NS_ERROR_FAILURE; // something happened to our object frame, so bail!
1942 // Get the notification callbacks from the channel and save it as
1943 // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
1944 // we'll create channel for byte range request.
1945 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1946 channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1947 if (callbacks)
1948 mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
1950 nsCOMPtr<nsILoadGroup> loadGroup;
1951 channel->GetLoadGroup(getter_AddRefs(loadGroup));
1952 if (loadGroup)
1953 mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
1955 PRInt32 length;
1956 rv = channel->GetContentLength(&length);
1958 // it's possible for the server to not send a Content-Length.
1959 // we should still work in this case.
1960 if (NS_FAILED(rv) || length == -1) {
1961 // check out if this is file channel
1962 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
1963 if (fileChannel) {
1964 // file does not exist
1965 mRequestFailed = PR_TRUE;
1966 return NS_ERROR_FAILURE;
1968 mPluginStreamInfo->SetLength(PRUint32(0));
1970 else {
1971 mPluginStreamInfo->SetLength(length);
1974 mPluginStreamInfo->SetRequest(request);
1976 nsCAutoString aContentType; // XXX but we already got the type above!
1977 rv = channel->GetContentType(aContentType);
1978 if (NS_FAILED(rv))
1979 return rv;
1981 nsCOMPtr<nsIURI> aURL;
1982 rv = channel->GetURI(getter_AddRefs(aURL));
1983 if (NS_FAILED(rv))
1984 return rv;
1986 nsCAutoString urlSpec;
1987 aURL->GetSpec(urlSpec);
1988 mPluginStreamInfo->SetURL(urlSpec.get());
1990 if (!aContentType.IsEmpty())
1991 mPluginStreamInfo->SetContentType(aContentType.get());
1993 #ifdef PLUGIN_LOGGING
1994 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
1995 ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
1996 this, request, aContentType.get(), urlSpec.get()));
1998 PR_LogFlush();
1999 #endif
2001 nsPluginWindow *window = nsnull;
2003 // if we don't have an nsIPluginInstance (mInstance), it means
2004 // we weren't able to load a plugin previously because we
2005 // didn't have the mimetype. Now that we do (aContentType),
2006 // we'll try again with SetUpPluginInstance()
2007 // which is called by InstantiateEmbeddedPlugin()
2008 // NOTE: we don't want to try again if we didn't get the MIME type this time
2010 if (!mInstance && mOwner && !aContentType.IsEmpty()) {
2011 mOwner->GetInstance(mInstance);
2012 mOwner->GetWindow(window);
2013 if (!mInstance && mHost && window) {
2014 // determine if we need to try embedded again. FullPage takes a different code path
2015 nsPluginMode mode;
2016 mOwner->GetMode(&mode);
2017 if (mode == nsPluginMode_Embedded)
2018 rv = mHost->InstantiateEmbeddedPlugin(aContentType.get(), aURL, mOwner);
2019 else
2020 rv = mHost->SetUpPluginInstance(aContentType.get(), aURL, mOwner);
2022 if (NS_OK == rv) {
2023 // GetInstance() adds a ref
2024 mOwner->GetInstance(mInstance);
2025 if (mInstance) {
2026 mInstance->Start();
2027 mOwner->CreateWidget();
2028 // If we've got a native window, the let the plugin know about it.
2029 if (window->window) {
2030 nsCOMPtr<nsIPluginInstance> inst = mInstance;
2031 ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
2038 // Set up the stream listener...
2039 rv = SetUpStreamListener(request, aURL);
2040 if (NS_FAILED(rv)) return rv;
2042 return rv;
2045 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
2046 nsISupports* aContext,
2047 PRUint64 aProgress,
2048 PRUint64 aProgressMax)
2050 nsresult rv = NS_OK;
2051 return rv;
2054 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
2055 nsISupports* aContext,
2056 nsresult aStatus,
2057 const PRUnichar* aStatusArg)
2059 return NS_OK;
2062 class nsPRUintKey : public nsHashKey {
2063 protected:
2064 PRUint32 mKey;
2065 public:
2066 nsPRUintKey(PRUint32 key) : mKey(key) {}
2068 PRUint32 HashCode(void) const {
2069 return mKey;
2072 PRBool Equals(const nsHashKey *aKey) const {
2073 return mKey == ((const nsPRUintKey*)aKey)->mKey;
2075 nsHashKey *Clone() const {
2076 return new nsPRUintKey(mKey);
2078 PRUint32 GetValue() { return mKey; }
2081 NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
2082 nsISupports* aContext,
2083 nsIInputStream *aIStream,
2084 PRUint32 sourceOffset,
2085 PRUint32 aLength)
2087 if (mRequestFailed)
2088 return NS_ERROR_FAILURE;
2090 if (mAbort) {
2091 PRUint32 magicNumber = 0; // set it to something that is not the magic number.
2092 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
2093 if (container)
2094 container->GetData(&magicNumber);
2096 if (magicNumber != MAGIC_REQUEST_CONTEXT) {
2097 // this is not one of our range requests
2098 mAbort = PR_FALSE;
2099 return NS_BINDING_ABORTED;
2103 nsresult rv = NS_OK;
2105 if (!mPStreamListener || !mPluginStreamInfo)
2106 return NS_ERROR_FAILURE;
2108 mPluginStreamInfo->SetRequest(request);
2110 const char * url = nsnull;
2111 mPluginStreamInfo->GetURL(&url);
2113 PLUGIN_LOG(PLUGIN_LOG_NOISY,
2114 ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%d, length=%d, url=%s\n",
2115 this, request, sourceOffset, aLength, url ? url : "no url set"));
2117 // if the plugin has requested an AsFileOnly stream, then don't
2118 // call OnDataAvailable
2119 if (mStreamType != nsPluginStreamType_AsFileOnly) {
2120 // get the absolute offset of the request, if one exists.
2121 nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
2122 if (brr) {
2123 if (!mDataForwardToRequest)
2124 return NS_ERROR_FAILURE;
2126 PRInt64 absoluteOffset64 = LL_ZERO;
2127 brr->GetStartRange(&absoluteOffset64);
2129 // XXX handle 64-bit for real
2130 PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64);
2132 // we need to track how much data we have forwarded to the
2133 // plugin.
2135 // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
2137 // Why couldn't this be tracked on the plugin info, and not in a
2138 // *hash table*?
2139 nsPRUintKey key(absoluteOffset);
2140 PRInt32 amtForwardToPlugin =
2141 NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
2142 mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
2144 mPluginStreamInfo->SetStreamOffset(absoluteOffset + amtForwardToPlugin);
2147 nsCOMPtr<nsIInputStream> stream = aIStream;
2149 // if we are caching the file ourselves to disk, we want to 'tee' off
2150 // the data as the plugin read from the stream. We do this by the magic
2151 // of an input stream tee.
2153 if (mFileCacheOutputStream) {
2154 rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
2155 if (NS_FAILED(rv))
2156 return rv;
2159 rv = mPStreamListener->OnDataAvailable(mPluginStreamInfo,
2160 stream,
2161 aLength);
2163 // if a plugin returns an error, the peer must kill the stream
2164 // else the stream and PluginStreamListener leak
2165 if (NS_FAILED(rv))
2166 request->Cancel(rv);
2168 else
2170 // if we don't read from the stream, OnStopRequest will never be called
2171 char* buffer = new char[aLength];
2172 PRUint32 amountRead, amountWrote = 0;
2173 rv = aIStream->Read(buffer, aLength, &amountRead);
2175 // if we are caching this to disk ourselves, lets write the bytes out.
2176 if (mFileCacheOutputStream) {
2177 while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
2178 rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote);
2181 delete [] buffer;
2183 return rv;
2186 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
2187 nsISupports* aContext,
2188 nsresult aStatus)
2190 nsresult rv = NS_OK;
2192 PLUGIN_LOG(PLUGIN_LOG_NOISY,
2193 ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
2194 this, aStatus, request));
2196 // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
2197 nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
2198 if (brr) {
2199 PRInt64 absoluteOffset64 = LL_ZERO;
2200 brr->GetStartRange(&absoluteOffset64);
2201 // XXX support 64-bit offsets
2202 PRInt32 absoluteOffset = (PRInt32)nsInt64(absoluteOffset64);
2204 nsPRUintKey key(absoluteOffset);
2206 // remove the request from our data forwarding count hash.
2207 (void) mDataForwardToRequest->Remove(&key);
2210 PLUGIN_LOG(PLUGIN_LOG_NOISY,
2211 (" ::OnStopRequest for ByteRangeRequest Started=%d\n",
2212 absoluteOffset));
2213 } else {
2214 // if this is not byte range request and
2215 // if we are writting the stream to disk ourselves,
2216 // close & tear it down here
2217 mFileCacheOutputStream = nsnull;
2220 // if we still have pending stuff to do, lets not close the plugin socket.
2221 if (--mPendingRequests > 0)
2222 return NS_OK;
2224 // we keep our connections around...
2225 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
2226 if (container) {
2227 PRUint32 magicNumber = 0; // set it to something that is not the magic number.
2228 container->GetData(&magicNumber);
2229 if (magicNumber == MAGIC_REQUEST_CONTEXT) {
2230 // this is one of our range requests
2231 return NS_OK;
2235 if (!mPStreamListener)
2236 return NS_ERROR_FAILURE;
2238 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
2239 if (!channel)
2240 return NS_ERROR_FAILURE;
2241 // Set the content type to ensure we don't pass null to the plugin
2242 nsCAutoString aContentType;
2243 rv = channel->GetContentType(aContentType);
2244 if (NS_FAILED(rv) && !mRequestFailed)
2245 return rv;
2247 if (!aContentType.IsEmpty())
2248 mPluginStreamInfo->SetContentType(aContentType.get());
2250 // set error status if stream failed so we notify the plugin
2251 if (mRequestFailed)
2252 aStatus = NS_ERROR_FAILURE;
2254 if (NS_FAILED(aStatus)) {
2255 // on error status cleanup the stream
2256 // and return w/o OnFileAvailable()
2257 mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
2258 return NS_OK;
2261 // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
2262 if (mStreamType >= nsPluginStreamType_AsFile) {
2263 nsCOMPtr<nsIFile> localFile = do_QueryInterface(mLocalCachedFile);
2264 if (!localFile) {
2265 nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
2266 if (cacheChannel) {
2267 cacheChannel->GetCacheFile(getter_AddRefs(localFile));
2268 } else {
2269 // see if it is a file channel.
2270 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
2271 if (fileChannel) {
2272 fileChannel->GetFile(getter_AddRefs(localFile));
2277 if (localFile) {
2278 OnFileAvailable(localFile);
2282 if (mStartBinding) {
2283 // On start binding has been called
2284 mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
2285 } else {
2286 // OnStartBinding hasn't been called, so complete the action.
2287 mPStreamListener->OnStartBinding(mPluginStreamInfo);
2288 mPStreamListener->OnStopBinding(mPluginStreamInfo, aStatus);
2291 if (NS_SUCCEEDED(aStatus))
2292 mPluginStreamInfo->SetStreamComplete(PR_TRUE);
2294 return NS_OK;
2297 // private methods for nsPluginStreamListenerPeer
2298 nsresult nsPluginStreamListenerPeer::SetUpCache(nsIURI* aURL)
2300 nsPluginCacheListener* cacheListener = new nsPluginCacheListener(this);
2301 // XXX: Null LoadGroup?
2302 return NS_OpenURI(cacheListener, nsnull, aURL, nsnull);
2305 nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
2306 nsIURI* aURL)
2308 nsresult rv = NS_OK;
2310 // If we don't yet have a stream listener, we need to get
2311 // one from the plugin.
2312 // NOTE: this should only happen when a stream was NOT created
2313 // with GetURL or PostURL (i.e. it's the initial stream we
2314 // send to the plugin as determined by the SRC or DATA attribute)
2315 if (mPStreamListener == nsnull && mInstance != nsnull)
2316 rv = mInstance->NewStream(&mPStreamListener);
2318 if (rv != NS_OK)
2319 return rv;
2321 if (mPStreamListener == nsnull)
2322 return NS_ERROR_NULL_POINTER;
2324 PRBool useLocalCache = PR_FALSE;
2326 // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
2327 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
2328 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
2331 * Assumption
2332 * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
2333 * called, all the headers have been read.
2335 if (httpChannel) {
2336 // Reassemble the HTTP response status line and provide it to our
2337 // listener. Would be nice if we could get the raw status line,
2338 // but nsIHttpChannel doesn't currently provide that.
2339 nsCOMPtr<nsIHTTPHeaderListener> listener =
2340 do_QueryInterface(mPStreamListener);
2341 if (listener) {
2342 // Status code: required; the status line isn't useful without it.
2343 PRUint32 statusNum;
2344 if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) &&
2345 statusNum < 1000) {
2346 // HTTP version: provide if available. Defaults to empty string.
2347 nsCString ver;
2348 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
2349 do_QueryInterface(channel);
2350 if (httpChannelInternal) {
2351 PRUint32 major, minor;
2352 if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major,
2353 &minor))) {
2354 ver = nsPrintfCString("/%lu.%lu", major, minor);
2358 // Status text: provide if available. Defaults to "OK".
2359 nsCString statusText;
2360 if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
2361 statusText = "OK";
2364 // Assemble everything and pass to listener.
2365 nsPrintfCString status(100, "HTTP%s %lu %s", ver.get(), statusNum,
2366 statusText.get());
2367 listener->StatusLine(status.get());
2371 // Also provide all HTTP response headers to our listener.
2372 httpChannel->VisitResponseHeaders(this);
2374 PRBool bSeekable = PR_FALSE;
2375 // first we look for a content-encoding header. If we find one, we tell the
2376 // plugin that stream is not seekable, because the plugin always sees
2377 // uncompressed data, so it can't make meaningful range requests on a
2378 // compressed entity. Also, we force the plugin to use
2379 // nsPluginStreamType_AsFile stream type and we have to save decompressed
2380 // file into local plugin cache, because necko cache contains original
2381 // compressed file.
2382 nsCAutoString contentEncoding;
2383 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
2384 contentEncoding))) {
2385 useLocalCache = PR_TRUE;
2386 } else {
2387 // set seekability (seekable if the stream has a known length and if the
2388 // http server accepts byte ranges).
2389 PRUint32 length;
2390 mPluginStreamInfo->GetLength(&length);
2391 if (length) {
2392 nsCAutoString range;
2393 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
2394 range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) {
2395 bSeekable = PR_TRUE;
2396 // nsPluginStreamInfo.mSeekable intitialized by PR_FALSE in ctor of nsPluginStreamInfo
2397 // so we reset it only here.
2398 mPluginStreamInfo->SetSeekable(bSeekable);
2403 // we require a content len
2404 // get Last-Modified header for plugin info
2405 nsCAutoString lastModified;
2406 if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) &&
2407 !lastModified.IsEmpty()) {
2408 PRTime time64;
2409 PR_ParseTimeString(lastModified.get(), PR_TRUE, &time64); //convert string time to integer time
2411 // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
2412 double fpTime;
2413 LL_L2D(fpTime, time64);
2414 mPluginStreamInfo->SetLastModified((PRUint32)(fpTime * 1e-6 + 0.5));
2418 rv = mPStreamListener->OnStartBinding(mPluginStreamInfo);
2420 mStartBinding = PR_TRUE;
2422 if (NS_FAILED(rv))
2423 return rv;
2425 mPStreamListener->GetStreamType(&mStreamType);
2427 if (!useLocalCache && mStreamType >= nsPluginStreamType_AsFile) {
2428 // check it out if this is not a file channel.
2429 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
2430 if (!fileChannel) {
2431 // and browser cache is not available
2432 nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
2433 if (!(cacheChannel && (NS_SUCCEEDED(cacheChannel->SetCacheAsFile(PR_TRUE))))) {
2434 useLocalCache = PR_TRUE;
2439 if (useLocalCache) {
2440 SetupPluginCacheFile(channel);
2443 return NS_OK;
2446 nsresult
2447 nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
2449 nsresult rv;
2450 if (!mPStreamListener)
2451 return NS_ERROR_FAILURE;
2453 nsCAutoString path;
2454 rv = aFile->GetNativePath(path);
2455 if (NS_FAILED(rv)) return rv;
2457 if (path.IsEmpty()) {
2458 NS_WARNING("empty path");
2459 return NS_OK;
2462 rv = mPStreamListener->OnFileAvailable(mPluginStreamInfo, path.get());
2463 return rv;
2466 NS_IMETHODIMP
2467 nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
2469 nsCOMPtr<nsIHTTPHeaderListener> listener = do_QueryInterface(mPStreamListener);
2470 if (!listener)
2471 return NS_ERROR_FAILURE;
2473 return listener->NewResponseHeader(PromiseFlatCString(header).get(),
2474 PromiseFlatCString(value).get());
2477 nsPluginHostImpl::nsPluginHostImpl()
2479 mPluginsLoaded = PR_FALSE;
2480 mDontShowBadPluginMessage = PR_FALSE;
2481 mIsDestroyed = PR_FALSE;
2482 mOverrideInternalTypes = PR_FALSE;
2483 mAllowAlienStarHandler = PR_FALSE;
2484 mUnusedLibraries.Clear();
2485 mDefaultPluginDisabled = PR_FALSE;
2486 mJavaEnabled = PR_TRUE;
2488 gActivePluginList = &mActivePluginList;
2490 // check to see if pref is set at startup to let plugins take over in
2491 // full page mode for certain image mime types that we handle internally
2492 mPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
2493 if (mPrefService) {
2494 PRBool tmp;
2495 nsresult rv = mPrefService->GetBoolPref("plugin.override_internal_types",
2496 &tmp);
2497 if (NS_SUCCEEDED(rv)) {
2498 mOverrideInternalTypes = tmp;
2501 rv = mPrefService->GetBoolPref("plugin.allow_alien_star_handler", &tmp);
2502 if (NS_SUCCEEDED(rv)) {
2503 mAllowAlienStarHandler = tmp;
2506 rv = mPrefService->GetBoolPref("plugin.default_plugin_disabled", &tmp);
2507 if (NS_SUCCEEDED(rv)) {
2508 mDefaultPluginDisabled = tmp;
2511 #ifdef WINCE
2512 mDefaultPluginDisabled = PR_TRUE;
2513 #endif
2515 rv = mPrefService->GetBoolPref("security.enable_java", &tmp);
2516 if (NS_SUCCEEDED(rv)) {
2517 mJavaEnabled = tmp;
2521 nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1");
2522 if (obsService)
2524 obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
2527 #ifdef PLUGIN_LOGGING
2528 nsPluginLogging::gNPNLog = PR_NewLogModule(NPN_LOG_NAME);
2529 nsPluginLogging::gNPPLog = PR_NewLogModule(NPP_LOG_NAME);
2530 nsPluginLogging::gPluginLog = PR_NewLogModule(PLUGIN_LOG_NAME);
2532 PR_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
2533 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHostImpl::ctor)\n"));
2534 PR_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
2536 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHostImpl::ctor\n"));
2537 PR_LogFlush();
2538 #endif
2539 mCachedPlugins = nsnull;
2542 nsPluginHostImpl::~nsPluginHostImpl()
2544 PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHostImpl::dtor\n"));
2546 Destroy();
2547 sInst = nsnull;
2550 NS_IMPL_ISUPPORTS8(nsPluginHostImpl,
2551 nsIPluginManager,
2552 nsIPluginManager2,
2553 nsIPluginHost,
2554 nsIFileUtilities,
2555 nsICookieStorage,
2556 nsIObserver,
2557 nsPIPluginHost,
2558 nsISupportsWeakReference)
2560 nsPluginHostImpl*
2561 nsPluginHostImpl::GetInst()
2563 if (!sInst) {
2564 sInst = new nsPluginHostImpl();
2565 if (!sInst)
2566 return nsnull;
2567 NS_ADDREF(sInst);
2569 // Must call this after the refcount is already 1!
2570 if (NS_FAILED(sInst->AddPrefObserver())) {
2571 NS_RELEASE(sInst);
2572 return nsnull;
2576 NS_ADDREF(sInst);
2577 return sInst;
2580 // static
2581 const char *
2582 nsPluginHostImpl::GetPluginName(nsIPluginInstance *aPluginInstance)
2584 nsActivePlugin *plugin =
2585 gActivePluginList ? gActivePluginList->find(aPluginInstance) : nsnull;
2587 if (plugin && plugin->mPluginTag)
2588 return plugin->mPluginTag->mName.get();
2590 return nsnull;
2593 NS_IMETHODIMP nsPluginHostImpl::GetValue(nsPluginManagerVariable aVariable, void *aValue)
2595 nsresult rv = NS_OK;
2597 NS_ENSURE_ARG_POINTER(aValue);
2599 #if defined(XP_UNIX) && !defined(XP_MACOSX) && defined(MOZ_X11)
2600 if (nsPluginManagerVariable_XDisplay == aVariable) {
2601 Display** value = reinterpret_cast<Display**>(aValue);
2602 #if defined (MOZ_WIDGET_GTK2)
2603 *value = GDK_DISPLAY();
2604 #endif
2605 if (!(*value))
2606 return NS_ERROR_FAILURE;
2608 #endif
2609 if (nsPluginManagerVariable_SupportsXEmbed == aVariable) {
2610 #ifdef MOZ_WIDGET_GTK2
2611 *(NPBool*)aValue = PR_TRUE;
2612 #else
2613 *(NPBool*)aValue = PR_FALSE;
2614 #endif
2616 return rv;
2619 PRBool nsPluginHostImpl::IsRunningPlugin(nsPluginTag * plugin)
2621 if (!plugin)
2622 return PR_FALSE;
2624 // we can check for mLibrary to be non-zero and then querry nsIPluginInstancePeer
2625 // in nsActivePluginList to see if plugin with matching mime type is not stopped
2626 if (!plugin->mLibrary)
2627 return PR_FALSE;
2629 for (int i = 0; i < plugin->mVariants; i++) {
2630 nsActivePlugin * p = mActivePluginList.find(plugin->mMimeTypeArray[i]);
2631 if (p && !p->mStopped)
2632 return PR_TRUE;
2635 return PR_FALSE;
2638 nsresult nsPluginHostImpl::ReloadPlugins(PRBool reloadPages)
2640 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2641 ("nsPluginHostImpl::ReloadPlugins Begin reloadPages=%d, active_instance_count=%d\n",
2642 reloadPages, mActivePluginList.mCount));
2644 nsresult rv = NS_OK;
2646 // this will create the initial plugin list out of cache
2647 // if it was not created yet
2648 if (!mPluginsLoaded)
2649 return LoadPlugins();
2651 // we are re-scanning plugins. New plugins may have been added, also some
2652 // plugins may have been removed, so we should probably shut everything down
2653 // but don't touch running (active and not stopped) plugins
2655 // check if plugins changed, no need to do anything else
2656 // if no changes to plugins have been made
2657 // PR_FALSE instructs not to touch the plugin list, just to
2658 // look for possible changes
2659 PRBool pluginschanged = PR_TRUE;
2660 FindPlugins(PR_FALSE, &pluginschanged);
2662 // if no changed detected, return an appropriate error code
2663 if (!pluginschanged)
2664 return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED;
2666 nsCOMPtr<nsISupportsArray> instsToReload;
2668 if (reloadPages) {
2669 NS_NewISupportsArray(getter_AddRefs(instsToReload));
2671 // Then stop any running plugin instances but hold on to the documents in the array
2672 // We are going to need to restart the instances in these documents later
2673 mActivePluginList.stopRunning(instsToReload, nsnull);
2676 // clean active plugin list
2677 mActivePluginList.removeAllStopped();
2679 // shutdown plugins and kill the list if there are no running plugins
2680 nsRefPtr<nsPluginTag> prev;
2681 nsRefPtr<nsPluginTag> next;
2683 for (nsRefPtr<nsPluginTag> p = mPlugins; p != nsnull;) {
2684 next = p->mNext;
2686 // XXX only remove our plugin from the list if it's not running and not
2687 // an XPCOM plugin. XPCOM plugins do not get a call to nsIPlugin::Shutdown
2688 // if plugins are reloaded. This also fixes a crash on UNIX where the call
2689 // to shutdown would break the ProxyJNI connection to the JRE after a reload.
2690 // see bug 86591
2691 if (!IsRunningPlugin(p) && (!p->mEntryPoint || p->HasFlag(NS_PLUGIN_FLAG_OLDSCHOOL))) {
2692 if (p == mPlugins)
2693 mPlugins = next;
2694 else
2695 prev->mNext = next;
2697 p->mNext = nsnull;
2698 p = next;
2699 continue;
2702 prev = p;
2703 p = next;
2706 // set flags
2707 mPluginsLoaded = PR_FALSE;
2709 // load them again
2710 rv = LoadPlugins();
2712 // If we have shut down any plugin instances, we've now got to restart them.
2713 // Post an event to do the rest as we are going to be destroying the frame tree and we also want
2714 // any posted unload events to finish
2715 PRUint32 c;
2716 if (reloadPages &&
2717 instsToReload &&
2718 NS_SUCCEEDED(instsToReload->Count(&c)) &&
2719 c > 0) {
2720 nsCOMPtr<nsIRunnable> ev = new nsPluginDocReframeEvent(instsToReload);
2721 if (ev)
2722 NS_DispatchToCurrentThread(ev);
2725 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
2726 ("nsPluginHostImpl::ReloadPlugins End active_instance_count=%d\n",
2727 mActivePluginList.mCount));
2729 return rv;
2732 #define NS_RETURN_UASTRING_SIZE 128
2734 nsresult nsPluginHostImpl::UserAgent(const char **retstring)
2736 static char resultString[NS_RETURN_UASTRING_SIZE];
2737 nsresult res;
2739 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
2740 if (NS_FAILED(res))
2741 return res;
2743 nsCAutoString uaString;
2744 res = http->GetUserAgent(uaString);
2746 if (NS_SUCCEEDED(res)) {
2747 if (NS_RETURN_UASTRING_SIZE > uaString.Length()) {
2748 PL_strcpy(resultString, uaString.get());
2749 } else {
2750 // Copy as much of UA string as we can (terminate at right-most space).
2751 PL_strncpy(resultString, uaString.get(), NS_RETURN_UASTRING_SIZE);
2752 for (int i = NS_RETURN_UASTRING_SIZE - 1; i >= 0; i--) {
2753 if (i == 0) {
2754 resultString[NS_RETURN_UASTRING_SIZE - 1] = '\0';
2756 else if (resultString[i] == ' ') {
2757 resultString[i] = '\0';
2758 break;
2762 *retstring = resultString;
2764 else {
2765 *retstring = nsnull;
2768 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHostImpl::UserAgent return=%s\n", *retstring));
2770 return res;
2773 nsresult nsPluginHostImpl::GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt)
2775 nsresult rv;
2776 nsCOMPtr<nsIPrompt> prompt;
2777 nsCOMPtr<nsIWindowWatcher> wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
2779 if (wwatch) {
2780 nsCOMPtr<nsIDOMWindow> domWindow;
2781 if (aOwner) {
2782 nsCOMPtr<nsIDocument> document;
2783 aOwner->GetDocument(getter_AddRefs(document));
2784 if (document) {
2785 domWindow = document->GetWindow();
2789 if (!domWindow) {
2790 wwatch->GetWindowByName(NS_LITERAL_STRING("_content").get(), nsnull, getter_AddRefs(domWindow));
2792 rv = wwatch->GetNewPrompter(domWindow, getter_AddRefs(prompt));
2795 NS_IF_ADDREF(*aPrompt = prompt);
2796 return rv;
2799 NS_IMETHODIMP nsPluginHostImpl::GetURL(nsISupports* pluginInst,
2800 const char* url,
2801 const char* target,
2802 nsIPluginStreamListener* streamListener,
2803 const char* altHost,
2804 const char* referrer,
2805 PRBool forceJSEnabled)
2807 return GetURLWithHeaders(pluginInst, url, target, streamListener,
2808 altHost, referrer, forceJSEnabled, nsnull, nsnull);
2811 NS_IMETHODIMP nsPluginHostImpl::GetURLWithHeaders(nsISupports* pluginInst,
2812 const char* url,
2813 const char* target,
2814 nsIPluginStreamListener* streamListener,
2815 const char* altHost,
2816 const char* referrer,
2817 PRBool forceJSEnabled,
2818 PRUint32 getHeadersLength,
2819 const char* getHeaders)
2821 nsAutoString string;
2822 string.AssignWithConversion(url);
2824 // we can only send a stream back to the plugin (as specified by a
2825 // null target) if we also have a nsIPluginStreamListener to talk to
2826 if (!target && !streamListener)
2827 return NS_ERROR_ILLEGAL_VALUE;
2829 nsresult rv;
2830 nsCOMPtr<nsIPluginInstance> instance = do_QueryInterface(pluginInst, &rv);
2831 if (NS_SUCCEEDED(rv))
2832 rv = DoURLLoadSecurityCheck(instance, url);
2834 if (NS_SUCCEEDED(rv)) {
2835 if (target) {
2836 nsCOMPtr<nsIPluginInstancePeer> peer;
2837 rv = instance->GetPeer(getter_AddRefs(peer));
2838 if (NS_SUCCEEDED(rv) && peer) {
2839 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
2840 nsCOMPtr<nsIPluginInstanceOwner> owner;
2841 rv = privpeer->GetOwner(getter_AddRefs(owner));
2842 if (owner) {
2843 if ((0 == PL_strcmp(target, "newwindow")) ||
2844 (0 == PL_strcmp(target, "_new")))
2845 target = "_blank";
2846 else if (0 == PL_strcmp(target, "_current"))
2847 target = "_self";
2849 rv = owner->GetURL(url, target, nsnull, 0, (void *) getHeaders, getHeadersLength);
2854 if (streamListener) {
2855 rv = NewPluginURLStream(string, instance, streamListener, nsnull,
2856 PR_FALSE, nsnull, getHeaders, getHeadersLength);
2860 return rv;
2863 NS_IMETHODIMP nsPluginHostImpl::PostURL(nsISupports* pluginInst,
2864 const char* url,
2865 PRUint32 postDataLen,
2866 const char* postData,
2867 PRBool isFile,
2868 const char* target,
2869 nsIPluginStreamListener* streamListener,
2870 const char* altHost,
2871 const char* referrer,
2872 PRBool forceJSEnabled,
2873 PRUint32 postHeadersLength,
2874 const char* postHeaders)
2876 nsAutoString string; string.AssignWithConversion(url);
2877 nsresult rv;
2879 // we can only send a stream back to the plugin (as specified
2880 // by a null target) if we also have a nsIPluginStreamListener
2881 // to talk to also
2882 if (!target && !streamListener)
2883 return NS_ERROR_ILLEGAL_VALUE;
2885 nsCOMPtr<nsIPluginInstance> instance = do_QueryInterface(pluginInst, &rv);
2886 if (NS_SUCCEEDED(rv))
2887 rv = DoURLLoadSecurityCheck(instance, url);
2889 if (NS_SUCCEEDED(rv)) {
2890 char *dataToPost;
2891 if (isFile) {
2892 rv = CreateTmpFileToPost(postData, &dataToPost);
2893 if (NS_FAILED(rv) || !dataToPost)
2894 return rv;
2895 } else {
2896 PRUint32 newDataToPostLen;
2897 ParsePostBufferToFixHeaders(postData, postDataLen, &dataToPost, &newDataToPostLen);
2898 if (!dataToPost)
2899 return NS_ERROR_UNEXPECTED;
2901 // we use nsIStringInputStream::adoptDataa()
2902 // in NS_NewPluginPostDataStream to set the stream
2903 // all new data alloced in ParsePostBufferToFixHeaders()
2904 // well be nsMemory::Free()d on destroy the stream
2905 postDataLen = newDataToPostLen;
2908 if (target) {
2909 nsCOMPtr<nsIPluginInstancePeer> peer;
2910 rv = instance->GetPeer(getter_AddRefs(peer));
2911 if (NS_SUCCEEDED(rv) && peer) {
2912 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
2913 nsCOMPtr<nsIPluginInstanceOwner> owner;
2914 rv = privpeer->GetOwner(getter_AddRefs(owner));
2915 if (owner) {
2916 if (!target) {
2917 target = "_self";
2919 else {
2920 if ((0 == PL_strcmp(target, "newwindow")) ||
2921 (0 == PL_strcmp(target, "_new"))) {
2922 target = "_blank";
2924 else if (0 == PL_strcmp(target, "_current")) {
2925 target = "_self";
2928 rv = owner->GetURL(url, target, (void*)dataToPost, postDataLen,
2929 (void*)postHeaders, postHeadersLength, isFile);
2934 // if we don't have a target, just create a stream. This does
2935 // NS_OpenURI()!
2936 if (streamListener)
2937 rv = NewPluginURLStream(string, instance, streamListener,
2938 (const char*)dataToPost, isFile, postDataLen,
2939 postHeaders, postHeadersLength);
2940 if (isFile)
2941 NS_Free(dataToPost);
2944 return rv;
2947 NS_IMETHODIMP nsPluginHostImpl::RegisterPlugin(REFNSIID aCID,
2948 const char* aPluginName,
2949 const char* aDescription,
2950 const char** aMimeTypes,
2951 const char** aMimeDescriptions,
2952 const char** aFileExtensions,
2953 PRInt32 aCount)
2955 return NS_ERROR_NOT_IMPLEMENTED;
2958 NS_IMETHODIMP nsPluginHostImpl::UnregisterPlugin(REFNSIID aCID)
2960 return NS_ERROR_NOT_IMPLEMENTED;
2963 NS_IMETHODIMP nsPluginHostImpl::BeginWaitCursor(void)
2965 return NS_ERROR_NOT_IMPLEMENTED;
2968 NS_IMETHODIMP nsPluginHostImpl::EndWaitCursor(void)
2970 return NS_ERROR_NOT_IMPLEMENTED;
2973 NS_IMETHODIMP nsPluginHostImpl::SupportsURLProtocol(const char* protocol, PRBool *result)
2975 return NS_ERROR_NOT_IMPLEMENTED;
2978 NS_IMETHODIMP nsPluginHostImpl::NotifyStatusChange(nsIPlugin* plugin, nsresult errorStatus)
2980 return NS_ERROR_NOT_IMPLEMENTED;
2984 /* This method queries the prefs for proxy information.
2985 * It has been tested and is known to work in the following three cases
2986 * when no proxy host or port is specified
2987 * when only the proxy host is specified
2988 * when only the proxy port is specified
2989 * This method conforms to the return code specified in
2990 * http://developer.netscape.com/docs/manuals/proxy/adminnt/autoconf.htm#1020923
2991 * with the exception that multiple values are not implemented.
2994 NS_IMETHODIMP nsPluginHostImpl::FindProxyForURL(const char* url, char* *result)
2996 if (!url || !result) {
2997 return NS_ERROR_INVALID_ARG;
2999 nsresult res;
3001 nsCOMPtr<nsIURI> uriIn;
3002 nsCOMPtr<nsIProtocolProxyService> proxyService;
3003 nsCOMPtr<nsIIOService> ioService;
3005 proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
3006 if (NS_FAILED(res) || !proxyService)
3007 return res;
3009 ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
3010 if (NS_FAILED(res) || !ioService)
3011 return res;
3013 // make an nsURI from the argument url
3014 res = ioService->NewURI(nsDependentCString(url), nsnull, nsnull, getter_AddRefs(uriIn));
3015 if (NS_FAILED(res))
3016 return res;
3018 nsCOMPtr<nsIProxyInfo> pi;
3020 res = proxyService->Resolve(uriIn, 0, getter_AddRefs(pi));
3021 if (NS_FAILED(res))
3022 return res;
3024 nsCAutoString host, type;
3025 PRInt32 port = -1;
3027 // These won't fail, and even if they do... we'll be ok.
3028 if (pi) {
3029 pi->GetType(type);
3030 pi->GetHost(host);
3031 pi->GetPort(&port);
3034 if (!pi || host.IsEmpty() || port <= 0 || host.EqualsLiteral("direct")) {
3035 *result = PL_strdup("DIRECT");
3036 } else if (type.EqualsLiteral("http")) {
3037 *result = PR_smprintf("PROXY %s:%d", host.get(), port);
3038 } else if (type.EqualsLiteral("socks4")) {
3039 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
3040 } else if (type.EqualsLiteral("socks")) {
3041 // XXX - this is socks5, but there is no API for us to tell the
3042 // plugin that fact. SOCKS for now, in case the proxy server
3043 // speaks SOCKS4 as well. See bug 78176
3044 // For a long time this was returning an http proxy type, so
3045 // very little is probably broken by this
3046 *result = PR_smprintf("SOCKS %s:%d", host.get(), port);
3047 } else {
3048 NS_ASSERTION(PR_FALSE, "Unknown proxy type!");
3049 *result = PL_strdup("DIRECT");
3052 if (nsnull == *result)
3053 res = NS_ERROR_OUT_OF_MEMORY;
3055 return res;
3058 NS_IMETHODIMP nsPluginHostImpl::RegisterWindow(nsIEventHandler* handler, nsPluginPlatformWindowRef window)
3060 return NS_ERROR_NOT_IMPLEMENTED;
3063 NS_IMETHODIMP nsPluginHostImpl::UnregisterWindow(nsIEventHandler* handler, nsPluginPlatformWindowRef window)
3065 return NS_ERROR_NOT_IMPLEMENTED;
3068 NS_IMETHODIMP nsPluginHostImpl::AllocateMenuID(nsIEventHandler* handler, PRBool isSubmenu, PRInt16 *result)
3070 return NS_ERROR_NOT_IMPLEMENTED;
3073 NS_IMETHODIMP nsPluginHostImpl::DeallocateMenuID(nsIEventHandler* handler, PRInt16 menuID)
3075 return NS_ERROR_NOT_IMPLEMENTED;
3078 NS_IMETHODIMP nsPluginHostImpl::HasAllocatedMenuID(nsIEventHandler* handler, PRInt16 menuID, PRBool *result)
3080 return NS_ERROR_NOT_IMPLEMENTED;
3083 NS_IMETHODIMP nsPluginHostImpl::ProcessNextEvent(PRBool *bEventHandled)
3085 return NS_ERROR_NOT_IMPLEMENTED;
3088 NS_IMETHODIMP nsPluginHostImpl::CreateInstance(nsISupports *aOuter,
3089 REFNSIID aIID,
3090 void **aResult)
3092 NS_NOTREACHED("how'd I get here?");
3093 return NS_ERROR_UNEXPECTED;
3096 NS_IMETHODIMP nsPluginHostImpl::LockFactory(PRBool aLock)
3098 NS_NOTREACHED("how'd I get here?");
3099 return NS_ERROR_UNEXPECTED;
3102 NS_IMETHODIMP nsPluginHostImpl::Init(void)
3104 return NS_OK;
3107 NS_IMETHODIMP nsPluginHostImpl::Destroy(void)
3109 PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsPluginHostImpl::Destroy Called\n"));
3111 if (mIsDestroyed)
3112 return NS_OK;
3114 mIsDestroyed = PR_TRUE;
3116 // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow
3117 // for those plugins who want it
3118 mActivePluginList.stopRunning(nsnull, nsnull);
3120 // at this point nsIPlugin::Shutdown calls will be performed if needed
3121 mActivePluginList.shut();
3123 if (mPluginPath) {
3124 PR_Free(mPluginPath);
3125 mPluginPath = nsnull;
3128 while (mPlugins) {
3129 nsRefPtr<nsPluginTag> temp = mPlugins->mNext;
3130 // while walking through the list of the plugins see if we still have anything
3131 // to shutdown some plugins may have never created an instance but still expect
3132 // the shutdown call see bugzilla bug 73071
3133 // with current logic, no need to do anything special as nsIPlugin::Shutdown
3134 // will be performed in the destructor
3135 mPlugins->mNext = nsnull;
3136 mPlugins = temp;
3139 // Delete any remaining cached plugins list
3140 mCachedPlugins = nsnull;
3142 // Lets remove any of the temporary files that we created.
3143 if (sPluginTempDir) {
3144 sPluginTempDir->Remove(PR_TRUE);
3145 NS_RELEASE(sPluginTempDir);
3148 #ifdef XP_WIN
3149 if (mPrivateDirServiceProvider) {
3150 nsCOMPtr<nsIDirectoryService> dirService =
3151 do_GetService(kDirectoryServiceContractID);
3152 if (dirService)
3153 dirService->UnregisterProvider(mPrivateDirServiceProvider);
3154 mPrivateDirServiceProvider = nsnull;
3156 #endif /* XP_WIN */
3158 nsCOMPtr<nsIPrefBranch2> prefBranch(do_QueryInterface(mPrefService));
3159 if (prefBranch)
3160 prefBranch->RemoveObserver("security.enable_java", this);
3161 mPrefService = nsnull; // release prefs service to avoid leaks!
3163 return NS_OK;
3166 void nsPluginHostImpl::UnloadUnusedLibraries()
3168 // unload any remaining plugin libraries from memory
3169 for (PRInt32 i = 0; i < mUnusedLibraries.Count(); i++) {
3170 PRLibrary * library = (PRLibrary *)mUnusedLibraries[i];
3171 if (library)
3172 PostPluginUnloadEvent(library);
3174 mUnusedLibraries.Clear();
3177 nsresult
3178 nsPluginHostImpl::GetPluginTempDir(nsIFile **aDir)
3180 if (!sPluginTempDir) {
3181 nsCOMPtr<nsIFile> tmpDir;
3182 nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
3183 getter_AddRefs(tmpDir));
3184 NS_ENSURE_SUCCESS(rv, rv);
3186 rv = tmpDir->AppendNative(kPluginTmpDirName);
3188 // make it unique, and mode == 0700, not world-readable
3189 rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
3190 NS_ENSURE_SUCCESS(rv, rv);
3192 tmpDir.swap(sPluginTempDir);
3195 return sPluginTempDir->Clone(aDir);
3198 NS_IMETHODIMP nsPluginHostImpl::InstantiatePluginForChannel(nsIChannel* aChannel,
3199 nsIPluginInstanceOwner* aOwner,
3200 nsIStreamListener** aListener)
3202 NS_PRECONDITION(aChannel && aOwner,
3203 "Invalid arguments to InstantiatePluginForChannel");
3204 nsCOMPtr<nsIURI> uri;
3205 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
3206 if (NS_FAILED(rv))
3207 return rv;
3209 #ifdef PLUGIN_LOGGING
3210 if (PR_LOG_TEST(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL)) {
3211 nsCAutoString urlSpec;
3212 uri->GetAsciiSpec(urlSpec);
3214 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
3215 ("nsPluginHostImpl::InstantiatePluginForChannel Begin owner=%p, url=%s\n",
3216 aOwner, urlSpec.get()));
3218 PR_LogFlush();
3220 #endif
3222 // XXX do we need to look for stopped plugins, like InstantiateEmbeddedPlugin
3223 // does?
3225 return NewEmbeddedPluginStreamListener(uri, aOwner, nsnull, aListener);
3228 // Called by nsPluginInstanceOwner (nsObjectFrame.cpp - embedded case)
3229 NS_IMETHODIMP nsPluginHostImpl::InstantiateEmbeddedPlugin(const char *aMimeType,
3230 nsIURI* aURL,
3231 nsIPluginInstanceOwner *aOwner)
3233 NS_ENSURE_ARG_POINTER(aOwner);
3235 #ifdef PLUGIN_LOGGING
3236 nsCAutoString urlSpec;
3237 if (aURL)
3238 aURL->GetAsciiSpec(urlSpec);
3240 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
3241 ("nsPluginHostImpl::InstantiateEmbeddedPlugin Begin mime=%s, owner=%p, url=%s\n",
3242 aMimeType, aOwner, urlSpec.get()));
3244 PR_LogFlush();
3245 #endif
3247 nsresult rv;
3248 nsIPluginInstance *instance = nsnull;
3249 nsCOMPtr<nsIPluginTagInfo2> pti2;
3250 nsPluginTagType tagType;
3252 rv = aOwner->QueryInterface(kIPluginTagInfo2IID, getter_AddRefs(pti2));
3254 if (rv != NS_OK)
3255 return rv;
3257 rv = pti2->GetTagType(&tagType);
3259 if ((rv != NS_OK) || !((tagType == nsPluginTagType_Embed)
3260 || (tagType == nsPluginTagType_Applet)
3261 || (tagType == nsPluginTagType_Object))) {
3262 return rv;
3265 // Security checks
3266 // Can't do security checks without a URI - hopefully the plugin will take
3267 // care of that
3268 if (aURL) {
3269 nsCOMPtr<nsIScriptSecurityManager> secMan =
3270 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
3271 if (NS_FAILED(rv))
3272 return rv; // Better fail if we can't do security checks
3274 nsCOMPtr<nsIDocument> doc;
3275 aOwner->GetDocument(getter_AddRefs(doc));
3276 if (!doc)
3277 return NS_ERROR_NULL_POINTER;
3279 rv = secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), aURL, 0);
3280 if (NS_FAILED(rv))
3281 return rv;
3283 nsCOMPtr<nsIDOMElement> elem;
3284 pti2->GetDOMElement(getter_AddRefs(elem));
3286 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit
3287 nsresult rv =
3288 NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
3289 aURL,
3290 doc->NodePrincipal(),
3291 elem,
3292 nsDependentCString(aMimeType ? aMimeType : ""),
3293 nsnull, //extra
3294 &shouldLoad);
3295 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))
3296 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
3299 // Look for even disabled plugins, because if the plugin for this type is
3300 // disabled, we don't want to go on and end up in SetUpDefaultPluginInstance.
3301 nsPluginTag* pluginTag = FindPluginForType(aMimeType, PR_FALSE);
3302 if (pluginTag) {
3303 if (!pluginTag->IsEnabled())
3304 return NS_ERROR_NOT_AVAILABLE;
3305 } else if (!mJavaEnabled && IsJavaMIMEType(aMimeType)) {
3306 // Even if we had no Java plugin, if mJavaEnabled is false we should throw
3307 // here for Java types. Note that we only need to do this for the case
3308 // when pluginTag is null; if we had a pluginTag, it would have its
3309 // NS_PLUGIN_FLAG_ENABLED set the right way.
3310 return NS_ERROR_NOT_AVAILABLE;
3313 PRBool isJava = pluginTag && pluginTag->mIsJavaPlugin;
3315 // Determine if the scheme of this URL is one we can handle internaly because we should
3316 // only open the initial stream if it's one that we can handle internally. Otherwise
3317 // |NS_OpenURI| in |InstantiateEmbeddedPlugin| may open up a OS protocal registered helper app
3318 PRBool bCanHandleInternally = PR_FALSE;
3319 nsCAutoString scheme;
3320 if (aURL && NS_SUCCEEDED(aURL->GetScheme(scheme))) {
3321 nsCAutoString contractID(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX);
3322 contractID += scheme;
3323 ToLowerCase(contractID);
3324 nsCOMPtr<nsIProtocolHandler> handler = do_GetService(contractID.get());
3325 if (handler)
3326 bCanHandleInternally = PR_TRUE;
3329 if (FindStoppedPluginForURL(aURL, aOwner) == NS_OK) {
3331 PLUGIN_LOG(PLUGIN_LOG_NOISY,
3332 ("nsPluginHostImpl::InstantiateEmbeddedPlugin FoundStopped mime=%s\n", aMimeType));
3334 aOwner->GetInstance(instance);
3335 if (!isJava && bCanHandleInternally)
3336 rv = NewEmbeddedPluginStream(aURL, aOwner, instance);
3338 // notify Java DOM component
3339 nsresult res;
3340 nsCOMPtr<nsIPluginInstanceOwner> javaDOM =
3341 do_GetService("@mozilla.org/blackwood/java-dom;1", &res);
3342 if (NS_SUCCEEDED(res) && javaDOM)
3343 javaDOM->SetInstance(instance);
3345 NS_IF_RELEASE(instance);
3346 return NS_OK;
3349 // if we don't have a MIME type at this point, we still have one more chance by
3350 // opening the stream and seeing if the server hands one back
3351 if (!aMimeType)
3352 return bCanHandleInternally ? NewEmbeddedPluginStream(aURL, aOwner, nsnull) : NS_ERROR_FAILURE;
3354 rv = SetUpPluginInstance(aMimeType, aURL, aOwner);
3356 if (rv == NS_OK) {
3357 rv = aOwner->GetInstance(instance);
3358 } else {
3359 /* If we are here, it's time to either show the default plugin
3360 * or return failure so layout will replace us.
3362 * Currently, the default plugin is shown for all EMBED and APPLET
3363 * tags and also any OBJECT tag that has a PLUGINURL PARAM tag name.
3366 PRBool bHasPluginURL = PR_FALSE;
3367 nsCOMPtr<nsIPluginTagInfo2> pti2(do_QueryInterface(aOwner));
3369 if (pti2) {
3370 const char *value;
3371 bHasPluginURL = NS_SUCCEEDED(pti2->GetParameter("PLUGINURL", &value));
3374 // if we didn't find a pluginURL param on the object tag,
3375 // there's nothing more to do here
3376 if (nsPluginTagType_Object == tagType && !bHasPluginURL)
3377 return rv;
3379 if (NS_FAILED(SetUpDefaultPluginInstance(aMimeType, aURL, aOwner)))
3380 return NS_ERROR_FAILURE;
3382 if (NS_FAILED(aOwner->GetInstance(instance)))
3383 return NS_ERROR_FAILURE;
3385 rv = NS_OK;
3388 // if we have a failure error, it means we found a plugin for the mimetype,
3389 // but we had a problem with the entry point
3390 if (rv == NS_ERROR_FAILURE)
3391 return rv;
3393 // if we are here then we have loaded a plugin for this mimetype
3394 // and it could be the Default plugin
3396 nsPluginWindow *window = nsnull;
3398 //we got a plugin built, now stream
3399 aOwner->GetWindow(window);
3401 if (instance) {
3402 instance->Start();
3403 aOwner->CreateWidget();
3405 // If we've got a native window, the let the plugin know about it.
3406 if (window->window) {
3407 nsCOMPtr<nsIPluginInstance> inst = instance;
3408 ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
3411 // create an initial stream with data
3412 // don't make the stream if it's a java applet or we don't have SRC or DATA attribute
3413 PRBool havedata = PR_FALSE;
3415 nsCOMPtr<nsIPluginTagInfo> pti(do_QueryInterface(aOwner, &rv));
3417 if (pti) {
3418 const char *value;
3419 havedata = NS_SUCCEEDED(pti->GetAttribute("SRC", &value));
3420 // no need to check for "data" as it would have been converted to "src"
3423 if (havedata && !isJava && bCanHandleInternally)
3424 rv = NewEmbeddedPluginStream(aURL, aOwner, instance);
3426 // notify Java DOM component
3427 nsresult res;
3428 nsCOMPtr<nsIPluginInstanceOwner> javaDOM =
3429 do_GetService("@mozilla.org/blackwood/java-dom;1", &res);
3430 if (NS_SUCCEEDED(res) && javaDOM)
3431 javaDOM->SetInstance(instance);
3433 NS_RELEASE(instance);
3436 #ifdef PLUGIN_LOGGING
3437 nsCAutoString urlSpec2;
3438 if (aURL != nsnull) (void)aURL->GetAsciiSpec(urlSpec2);
3440 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
3441 ("nsPluginHostImpl::InstantiateEmbeddedPlugin Finished mime=%s, rv=%d, owner=%p, url=%s\n",
3442 aMimeType, rv, aOwner, urlSpec2.get()));
3444 PR_LogFlush();
3445 #endif
3447 return rv;
3450 // Called by full-page case
3451 NS_IMETHODIMP nsPluginHostImpl::InstantiateFullPagePlugin(const char *aMimeType,
3452 nsIURI* aURI,
3453 nsIStreamListener *&aStreamListener,
3454 nsIPluginInstanceOwner *aOwner)
3456 #ifdef PLUGIN_LOGGING
3457 nsCAutoString urlSpec;
3458 aURI->GetSpec(urlSpec);
3459 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3460 ("nsPluginHostImpl::InstantiateFullPagePlugin Begin mime=%s, owner=%p, url=%s\n",
3461 aMimeType, aOwner, urlSpec.get()));
3462 #endif
3464 if (FindStoppedPluginForURL(aURI, aOwner) == NS_OK) {
3465 PLUGIN_LOG(PLUGIN_LOG_NOISY,
3466 ("nsPluginHostImpl::InstantiateFullPagePlugin FoundStopped mime=%s\n",aMimeType));
3468 nsIPluginInstance* instance;
3469 aOwner->GetInstance(instance);
3470 nsPluginTag* pluginTag = FindPluginForType(aMimeType, PR_TRUE);
3471 if (!pluginTag || !pluginTag->mIsJavaPlugin)
3472 NewFullPagePluginStream(aStreamListener, instance);
3473 NS_IF_RELEASE(instance);
3474 return NS_OK;
3477 nsresult rv = SetUpPluginInstance(aMimeType, aURI, aOwner);
3479 if (NS_OK == rv) {
3480 nsCOMPtr<nsIPluginInstance> instance;
3481 nsPluginWindow * win = nsnull;
3483 aOwner->GetInstance(*getter_AddRefs(instance));
3484 aOwner->GetWindow(win);
3486 if (win && instance) {
3487 instance->Start();
3488 aOwner->CreateWidget();
3490 // If we've got a native window, the let the plugin know about it.
3491 nsPluginNativeWindow * window = (nsPluginNativeWindow *)win;
3492 if (window->window)
3493 window->CallSetWindow(instance);
3495 rv = NewFullPagePluginStream(aStreamListener, instance);
3497 // If we've got a native window, the let the plugin know about it.
3498 if (window->window)
3499 window->CallSetWindow(instance);
3503 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3504 ("nsPluginHostImpl::InstantiateFullPagePlugin End mime=%s, rv=%d, owner=%p, url=%s\n",
3505 aMimeType, rv, aOwner, urlSpec.get()));
3507 return rv;
3510 nsresult nsPluginHostImpl::FindStoppedPluginForURL(nsIURI* aURL,
3511 nsIPluginInstanceOwner *aOwner)
3513 nsCAutoString url;
3514 if (!aURL)
3515 return NS_ERROR_FAILURE;
3517 aURL->GetAsciiSpec(url);
3519 nsActivePlugin * plugin = mActivePluginList.findStopped(url.get());
3521 if (plugin && plugin->mStopped) {
3522 nsIPluginInstance* instance = plugin->mInstance;
3523 nsPluginWindow *window = nsnull;
3524 aOwner->GetWindow(window);
3526 aOwner->SetInstance(instance);
3528 // we have to reset the owner and instance in the plugin instance peer
3529 //instance->GetPeer(&peer);
3530 ((nsPluginInstancePeerImpl*)plugin->mPeer)->SetOwner(aOwner);
3532 instance->Start();
3533 aOwner->CreateWidget();
3535 // If we've got a native window, the let the plugin know about it.
3536 if (window->window) {
3537 nsCOMPtr<nsIPluginInstance> inst = instance;
3538 ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
3541 plugin->setStopped(PR_FALSE);
3542 return NS_OK;
3544 return NS_ERROR_FAILURE;
3547 nsresult nsPluginHostImpl::AddInstanceToActiveList(nsCOMPtr<nsIPlugin> aPlugin,
3548 nsIPluginInstance* aInstance,
3549 nsIURI* aURL,
3550 PRBool aDefaultPlugin,
3551 nsIPluginInstancePeer* peer)
3554 nsCAutoString url;
3555 // It's OK to not have a URL here, as is the case with the dummy
3556 // Java plugin. In that case simply use an empty string...
3557 if (aURL)
3558 aURL->GetSpec(url);
3560 // let's find the corresponding plugin tag by matching nsIPlugin pointer
3561 // it's legal for XPCOM plugins not to have nsIPlugin implemented but
3562 // this is OK, we don't need the plugin tag for XPCOM plugins. It is going
3563 // to be used later when we decide whether or not we should delay unloading
3564 // NPAPI dll from memory, and XPCOM dlls will stay in memory anyway.
3565 nsPluginTag * pluginTag = nsnull;
3566 if (aPlugin) {
3567 for (pluginTag = mPlugins; pluginTag != nsnull; pluginTag = pluginTag->mNext) {
3568 if (pluginTag->mEntryPoint == aPlugin)
3569 break;
3571 NS_ASSERTION(pluginTag, "Plugin tag not found");
3574 nsActivePlugin * plugin = new nsActivePlugin(pluginTag, aInstance, url.get(), aDefaultPlugin, peer);
3576 if (!plugin)
3577 return NS_ERROR_OUT_OF_MEMORY;
3579 mActivePluginList.add(plugin);
3580 return NS_OK;
3583 void
3584 nsPluginTag::RegisterWithCategoryManager(PRBool aOverrideInternalTypes,
3585 nsPluginTag::nsRegisterType aType)
3587 if (!mMimeTypeArray)
3588 return;
3590 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
3591 ("nsPluginTag::RegisterWithCategoryManager plugin=%s, removing = %s\n",
3592 mFileName.get(), aType == ePluginUnregister ? "yes" : "no"));
3594 nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
3595 if (!catMan)
3596 return;
3598 const char *contractId = "@mozilla.org/content/plugin/document-loader-factory;1";
3600 nsCOMPtr<nsIPrefBranch> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
3601 if (!psvc)
3602 return; // NS_ERROR_OUT_OF_MEMORY
3604 // A preference controls whether or not the full page plugin is disabled for
3605 // a particular type. The string must be in the form:
3606 // type1,type2,type3,type4
3607 // Note: need an actual interface to control this and subsequent disabling
3608 // (and other plugin host settings) so applications can reliably disable
3609 // plugins - without relying on implementation details such as prefs/category
3610 // manager entries.
3611 nsXPIDLCString overrideTypes;
3612 psvc->GetCharPref("plugin.disable_full_page_plugin_for_types", getter_Copies(overrideTypes));
3613 nsCAutoString overrideTypesFormatted;
3614 overrideTypesFormatted.Assign(',');
3615 overrideTypesFormatted += overrideTypes;
3616 overrideTypesFormatted.Append(',');
3618 nsACString::const_iterator start, end;
3619 for (int i = 0; i < mVariants; i++) {
3620 if (aType == ePluginUnregister) {
3621 nsXPIDLCString value;
3622 if (NS_SUCCEEDED(catMan->GetCategoryEntry("Gecko-Content-Viewers",
3623 mMimeTypeArray[i],
3624 getter_Copies(value)))) {
3625 // Only delete the entry if a plugin registered for it
3626 if (strcmp(value, contractId) == 0) {
3627 catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
3628 mMimeTypeArray[i],
3629 PR_TRUE);
3632 } else {
3633 overrideTypesFormatted.BeginReading(start);
3634 overrideTypesFormatted.EndReading(end);
3636 nsDependentCString mimeType(mMimeTypeArray[i]);
3637 nsCAutoString commaSeparated;
3638 commaSeparated.Assign(',');
3639 commaSeparated += mimeType;
3640 commaSeparated.Append(',');
3641 if (!FindInReadable(commaSeparated, start, end)) {
3642 catMan->AddCategoryEntry("Gecko-Content-Viewers",
3643 mMimeTypeArray[i],
3644 contractId,
3645 PR_FALSE, /* persist: broken by bug 193031 */
3646 aOverrideInternalTypes, /* replace if we're told to */
3647 nsnull);
3651 PLUGIN_LOG(PLUGIN_LOG_NOISY,
3652 ("nsPluginTag::RegisterWithCategoryManager mime=%s, plugin=%s\n",
3653 mMimeTypeArray[i], mFileName.get()));
3657 NS_IMETHODIMP nsPluginHostImpl::SetUpPluginInstance(const char *aMimeType,
3658 nsIURI *aURL,
3659 nsIPluginInstanceOwner *aOwner)
3661 nsresult rv = NS_OK;
3663 rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
3665 // if we fail, refresh plugin list just in case the plugin has been
3666 // just added and try to instantiate plugin instance again, see bug 143178
3667 if (NS_FAILED(rv)) {
3668 // we should also make sure not to do this more than once per page
3669 // so if there are a few embed tags with unknown plugins,
3670 // we don't get unnecessary overhead
3671 // let's cache document to decide whether this is the same page or not
3672 nsCOMPtr<nsIDocument> document;
3673 if (aOwner)
3674 aOwner->GetDocument(getter_AddRefs(document));
3676 nsCOMPtr<nsIDocument> currentdocument = do_QueryReferent(mCurrentDocument);
3677 if (document == currentdocument)
3678 return rv;
3680 mCurrentDocument = do_GetWeakReference(document);
3682 // ReloadPlugins will do the job smartly: nothing will be done
3683 // if no changes detected, in such a case just return
3684 if (NS_ERROR_PLUGINS_PLUGINSNOTCHANGED == ReloadPlugins(PR_FALSE))
3685 return rv;
3687 // other failure return codes may be not fatal, so we can still try
3688 rv = TrySetUpPluginInstance(aMimeType, aURL, aOwner);
3691 return rv;
3694 NS_IMETHODIMP
3695 nsPluginHostImpl::TrySetUpPluginInstance(const char *aMimeType,
3696 nsIURI *aURL,
3697 nsIPluginInstanceOwner *aOwner)
3699 #ifdef PLUGIN_LOGGING
3700 nsCAutoString urlSpec;
3701 if (aURL != nsnull) (void)aURL->GetSpec(urlSpec);
3703 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
3704 ("nsPluginHostImpl::TrySetupPluginInstance Begin mime=%s, owner=%p, url=%s\n",
3705 aMimeType, aOwner, urlSpec.get()));
3707 PR_LogFlush();
3708 #endif
3711 nsresult result = NS_ERROR_FAILURE;
3712 nsCOMPtr<nsIPluginInstance> instance;
3713 nsCOMPtr<nsIPlugin> plugin;
3714 const char* mimetype = nsnull;
3716 // if don't have a mimetype or no plugin can handle this mimetype
3717 // check by file extension
3718 nsPluginTag* pluginTag = FindPluginForType(aMimeType, PR_TRUE);
3719 if (!pluginTag) {
3720 nsCOMPtr<nsIURL> url = do_QueryInterface(aURL);
3721 if (!url) return NS_ERROR_FAILURE;
3723 nsCAutoString fileExtension;
3724 url->GetFileExtension(fileExtension);
3726 // if we don't have an extension or no plugin for this extension,
3727 // return failure as there is nothing more we can do
3728 if (fileExtension.IsEmpty() ||
3729 !(pluginTag = FindPluginEnabledForExtension(fileExtension.get(),
3730 mimetype))) {
3731 return NS_ERROR_FAILURE;
3734 else
3735 mimetype = aMimeType;
3737 NS_ASSERTION(pluginTag, "Must have plugin tag here!");
3738 PRBool isJavaPlugin = pluginTag->mIsJavaPlugin;
3740 if (isJavaPlugin && !pluginTag->mIsNPRuntimeEnabledJavaPlugin) {
3741 #if !defined(OJI) && defined(XP_MACOSX)
3742 // The MRJ plugin hangs if you try to load it with OJI disabled,
3743 // don't even try to go there.
3744 return NS_ERROR_FAILURE;
3745 #endif
3747 // We must make sure LiveConnect is started, if needed.
3748 nsCOMPtr<nsIDocument> document;
3749 aOwner->GetDocument(getter_AddRefs(document));
3750 if (document) {
3751 nsCOMPtr<nsPIDOMWindow> window =
3752 do_QueryInterface(document->GetScriptGlobalObject());
3754 if (window) {
3755 window->InitJavaProperties();
3759 #if defined(OJI) && ((defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_OS2))
3760 // This is a work-around on Unix for a LiveConnect problem (bug
3761 // 83698).
3762 // The problem:
3763 // The proxy JNI needs to be created by the browser. If it is
3764 // created by someone else (e.g., a plugin) on a different thread,
3765 // the proxy JNI will not work, and break LiveConnect. Currently,
3766 // on Unix, when instantiating a Java plugin instance (by calling
3767 // InstantiateEmbeddedPlugin() next), Java plugin will create the
3768 // proxy JNI if it is not created yet. If that happens,
3769 // LiveConnect will be broken. Before lazy start JVM was
3770 // implemented, since at this point the browser already created
3771 // the proxy JNI during startup, the problem did not happen.
3772 // But after the lazy start was implemented, at this point the
3773 // proxy JNI was not created yet, so the Java plugin created the
3774 // proxy JNI, and broke liveConnect.
3775 // On Windows and Mac, Java plugin does not create the proxy JNI,
3776 // but lets the browser to create it. Hence this is a Unix-only
3777 // problem.
3779 // The work-around:
3780 // The root cause of the problem is in Java plugin's Unix
3781 // implementation, which should not create the proxy JNI. As a
3782 // work-around, here we make sure the proxy JNI has been created
3783 // by the browser, before plugin gets a chance.
3786 // If Java is installed, get proxy JNI.
3787 nsCOMPtr<nsIJVMManager> jvmManager = do_GetService(nsIJVMManager::GetCID(),
3788 &result);
3789 if (NS_SUCCEEDED(result)) {
3790 JNIEnv* proxyEnv;
3791 // Get proxy JNI, if not created yet, create it.
3792 jvmManager->GetProxyJNI(&proxyEnv);
3794 #endif
3797 nsCAutoString contractID(
3798 NS_LITERAL_CSTRING(NS_INLINE_PLUGIN_CONTRACTID_PREFIX) +
3799 nsDependentCString(mimetype));
3801 GetPluginFactory(mimetype, getter_AddRefs(plugin));
3803 instance = do_CreateInstance(contractID.get(), &result);
3805 // couldn't create an XPCOM plugin, try to create wrapper for a
3806 // legacy plugin
3807 if (NS_FAILED(result)) {
3808 if (plugin) {
3809 #ifdef XP_WIN
3810 static BOOL firstJavaPlugin = FALSE;
3811 BOOL restoreOrigDir = FALSE;
3812 char origDir[_MAX_PATH];
3813 if (isJavaPlugin && !firstJavaPlugin) {
3814 DWORD dw = ::GetCurrentDirectory(_MAX_PATH, origDir);
3815 NS_ASSERTION(dw <= _MAX_PATH, "Falied to obtain the current directory, which may leads to incorrect class laoding");
3816 nsCOMPtr<nsIFile> binDirectory;
3817 result = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
3818 getter_AddRefs(binDirectory));
3820 if (NS_SUCCEEDED(result)) {
3821 nsCAutoString path;
3822 binDirectory->GetNativePath(path);
3823 restoreOrigDir = ::SetCurrentDirectory(path.get());
3826 #endif
3827 result = plugin->CreateInstance(NULL, kIPluginInstanceIID, (void **)getter_AddRefs(instance));
3829 #ifdef XP_WIN
3830 if (!firstJavaPlugin && restoreOrigDir) {
3831 BOOL bCheck = ::SetCurrentDirectory(origDir);
3832 NS_ASSERTION(bCheck, " Error restoring driectoy");
3833 firstJavaPlugin = TRUE;
3835 #endif
3838 if (NS_FAILED(result)) {
3839 nsCOMPtr<nsIPlugin> bwPlugin =
3840 do_GetService("@mozilla.org/blackwood/pluglet-engine;1", &result);
3841 if (NS_SUCCEEDED(result)) {
3842 result = bwPlugin->CreatePluginInstance(NULL,
3843 kIPluginInstanceIID,
3844 aMimeType,
3845 (void **)getter_AddRefs(instance));
3850 // neither an XPCOM or legacy plugin could be instantiated,
3851 // so return the failure
3852 if (NS_FAILED(result))
3853 return result;
3855 // it is adreffed here
3856 aOwner->SetInstance(instance);
3858 nsRefPtr<nsPluginInstancePeerImpl> peer = new nsPluginInstancePeerImpl();
3859 if (!peer)
3860 return NS_ERROR_OUT_OF_MEMORY;
3862 // set up the peer for the instance
3863 peer->Initialize(aOwner, mimetype);
3865 result = instance->Initialize(peer); // this should addref the peer but not the instance or owner
3866 if (NS_FAILED(result)) // except in some cases not Java, see bug 140931
3867 return result; // our COM pointer will free the peer
3869 // instance and peer will be addreffed here
3870 result = AddInstanceToActiveList(plugin, instance, aURL, PR_FALSE, peer);
3872 #ifdef PLUGIN_LOGGING
3873 nsCAutoString urlSpec2;
3874 if (aURL)
3875 aURL->GetSpec(urlSpec2);
3877 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
3878 ("nsPluginHostImpl::TrySetupPluginInstance Finished mime=%s, rv=%d, owner=%p, url=%s\n",
3879 aMimeType, result, aOwner, urlSpec2.get()));
3881 PR_LogFlush();
3882 #endif
3884 return result;
3887 nsresult
3888 nsPluginHostImpl::SetUpDefaultPluginInstance(const char *aMimeType,
3889 nsIURI *aURL,
3890 nsIPluginInstanceOwner *aOwner)
3892 if (mDefaultPluginDisabled) {
3893 // The default plugin is disabled, don't load it.
3895 return NS_OK;
3898 nsCOMPtr<nsIPluginInstance> instance;
3899 nsCOMPtr<nsIPlugin> plugin = NULL;
3900 const char* mimetype = aMimeType;
3902 if (!aURL)
3903 return NS_ERROR_FAILURE;
3905 GetPluginFactory("*", getter_AddRefs(plugin));
3907 nsresult result;
3908 instance = do_CreateInstance(NS_INLINE_PLUGIN_CONTRACTID_PREFIX "*",
3909 &result);
3911 // couldn't create an XPCOM plugin, try to create wrapper for a
3912 // legacy plugin
3913 if (NS_FAILED(result)) {
3914 if (plugin)
3915 result = plugin->CreateInstance(NULL, kIPluginInstanceIID,
3916 getter_AddRefs(instance));
3919 // neither an XPCOM or legacy plugin could be instantiated, so
3920 // return the failure
3921 if (NS_FAILED(result))
3922 return result;
3924 // it is adreffed here
3925 aOwner->SetInstance(instance);
3927 nsRefPtr<nsPluginInstancePeerImpl> peer = new nsPluginInstancePeerImpl();
3928 if (!peer)
3929 return NS_ERROR_OUT_OF_MEMORY;
3931 // if we don't have a mimetype, check by file extension
3932 nsXPIDLCString mt;
3933 if (!mimetype || !*mimetype) {
3934 nsresult res = NS_OK;
3935 nsCOMPtr<nsIMIMEService> ms (do_GetService(NS_MIMESERVICE_CONTRACTID, &res));
3936 if (NS_SUCCEEDED(res)) {
3937 res = ms->GetTypeFromURI(aURL, mt);
3938 if (NS_SUCCEEDED(res))
3939 mimetype = mt.get();
3943 // set up the peer for the instance
3944 peer->Initialize(aOwner, mimetype);
3946 // this should addref the peer but not the instance or owner except
3947 // in some cases not Java, see bug 140931 our COM pointer will free
3948 // the peer
3949 result = instance->Initialize(peer);
3950 if (NS_FAILED(result))
3951 return result;
3953 // instance and peer will be addreffed here
3954 result = AddInstanceToActiveList(plugin, instance, aURL, PR_TRUE, peer);
3956 return result;
3959 NS_IMETHODIMP
3960 nsPluginHostImpl::IsPluginEnabledForType(const char* aMimeType)
3962 // Pass PR_FALSE as the second arg so we can return NS_ERROR_PLUGIN_DISABLED
3963 // for disabled plug-ins.
3964 nsPluginTag *plugin = FindPluginForType(aMimeType, PR_FALSE);
3965 if (!plugin)
3966 return NS_ERROR_FAILURE;
3968 if (!plugin->IsEnabled()) {
3969 if (plugin->HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED))
3970 return NS_ERROR_PLUGIN_BLOCKLISTED;
3971 else
3972 return NS_ERROR_PLUGIN_DISABLED;
3975 return NS_OK;
3978 // check comma delimitered extensions
3979 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
3981 if (!aExtensionList || !aExtension)
3982 return -1;
3984 const char *pExt = aExtensionList;
3985 const char *pComma = strchr(pExt, ',');
3986 if (!pComma)
3987 return PL_strcasecmp(pExt, aExtension);
3989 int extlen = strlen(aExtension);
3990 while (pComma) {
3991 int length = pComma - pExt;
3992 if (length == extlen && 0 == PL_strncasecmp(aExtension, pExt, length))
3993 return 0;
3994 pComma++;
3995 pExt = pComma;
3996 pComma = strchr(pExt, ',');
3999 // the last one
4000 return PL_strcasecmp(pExt, aExtension);
4003 NS_IMETHODIMP
4004 nsPluginHostImpl::IsPluginEnabledForExtension(const char* aExtension,
4005 const char* &aMimeType)
4007 nsPluginTag *plugin = FindPluginEnabledForExtension(aExtension, aMimeType);
4008 return plugin ? NS_OK : NS_ERROR_FAILURE;
4011 class DOMMimeTypeImpl : public nsIDOMMimeType {
4012 public:
4013 NS_DECL_ISUPPORTS
4015 DOMMimeTypeImpl(nsPluginTag* aTag, PRUint32 aMimeTypeIndex)
4017 if (!aTag)
4018 return;
4019 CopyUTF8toUTF16(aTag->mMimeDescriptionArray[aMimeTypeIndex], mDescription);
4020 if (aTag->mExtensionsArray)
4021 CopyUTF8toUTF16(aTag->mExtensionsArray[aMimeTypeIndex], mSuffixes);
4022 if (aTag->mMimeTypeArray)
4023 CopyUTF8toUTF16(aTag->mMimeTypeArray[aMimeTypeIndex], mType);
4026 virtual ~DOMMimeTypeImpl() {
4029 NS_METHOD GetDescription(nsAString& aDescription)
4031 aDescription.Assign(mDescription);
4032 return NS_OK;
4035 NS_METHOD GetEnabledPlugin(nsIDOMPlugin** aEnabledPlugin)
4037 // this has to be implemented by the DOM version.
4038 *aEnabledPlugin = nsnull;
4039 return NS_OK;
4042 NS_METHOD GetSuffixes(nsAString& aSuffixes)
4044 aSuffixes.Assign(mSuffixes);
4045 return NS_OK;
4048 NS_METHOD GetType(nsAString& aType)
4050 aType.Assign(mType);
4051 return NS_OK;
4054 private:
4055 nsString mDescription;
4056 nsString mSuffixes;
4057 nsString mType;
4060 NS_IMPL_ISUPPORTS1(DOMMimeTypeImpl, nsIDOMMimeType)
4062 class DOMPluginImpl : public nsIDOMPlugin {
4063 public:
4064 NS_DECL_ISUPPORTS
4066 DOMPluginImpl(nsPluginTag* aPluginTag) : mPluginTag(aPluginTag)
4070 virtual ~DOMPluginImpl() {
4073 NS_METHOD GetDescription(nsAString& aDescription)
4075 CopyUTF8toUTF16(mPluginTag.mDescription, aDescription);
4076 return NS_OK;
4079 NS_METHOD GetFilename(nsAString& aFilename)
4081 PRBool bShowPath;
4082 nsCOMPtr<nsIPrefBranch> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
4083 if (prefService &&
4084 NS_SUCCEEDED(prefService->GetBoolPref("plugin.expose_full_path",&bShowPath)) &&
4085 bShowPath) {
4086 // only show the full path if people have set the pref,
4087 // the default should not reveal path information (bug 88183)
4088 #if defined(XP_MACOSX)
4089 CopyUTF8toUTF16(mPluginTag.mFullPath, aFilename);
4090 #else
4091 CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
4092 #endif
4093 return NS_OK;
4096 nsAutoString spec;
4097 if (!mPluginTag.mFullPath.IsEmpty()) {
4098 #if !defined(XP_MACOSX)
4099 NS_ERROR("Only MAC should be using nsPluginTag::mFullPath!");
4100 #endif
4101 CopyUTF8toUTF16(mPluginTag.mFullPath, spec);
4102 } else {
4103 CopyUTF8toUTF16(mPluginTag.mFileName, spec);
4106 nsCString leafName;
4107 nsCOMPtr<nsILocalFile> pluginPath;
4108 NS_NewLocalFile(spec, PR_TRUE, getter_AddRefs(pluginPath));
4110 return pluginPath->GetLeafName(aFilename);
4113 NS_METHOD GetName(nsAString& aName)
4115 CopyUTF8toUTF16(mPluginTag.mName, aName);
4116 return NS_OK;
4119 NS_METHOD GetLength(PRUint32* aLength)
4121 *aLength = mPluginTag.mVariants;
4122 return NS_OK;
4125 NS_METHOD Item(PRUint32 aIndex, nsIDOMMimeType** aReturn)
4127 nsIDOMMimeType* mimeType = new DOMMimeTypeImpl(&mPluginTag, aIndex);
4128 NS_IF_ADDREF(mimeType);
4129 *aReturn = mimeType;
4130 return NS_OK;
4133 NS_METHOD NamedItem(const nsAString& aName, nsIDOMMimeType** aReturn)
4135 for (int i = mPluginTag.mVariants - 1; i >= 0; --i) {
4136 if (aName.Equals(NS_ConvertUTF8toUTF16(mPluginTag.mMimeTypeArray[i])))
4137 return Item(i, aReturn);
4139 return NS_OK;
4142 private:
4143 nsPluginTag mPluginTag;
4146 NS_IMPL_ISUPPORTS1(DOMPluginImpl, nsIDOMPlugin)
4148 NS_IMETHODIMP
4149 nsPluginHostImpl::GetPluginCount(PRUint32* aPluginCount)
4151 LoadPlugins();
4153 PRUint32 count = 0;
4155 nsPluginTag* plugin = mPlugins;
4156 while (plugin != nsnull) {
4157 if (plugin->IsEnabled()) {
4158 ++count;
4160 plugin = plugin->mNext;
4163 *aPluginCount = count;
4165 return NS_OK;
4168 NS_IMETHODIMP
4169 nsPluginHostImpl::GetPlugins(PRUint32 aPluginCount, nsIDOMPlugin** aPluginArray)
4171 LoadPlugins();
4173 nsPluginTag* plugin = mPlugins;
4174 for (PRUint32 i = 0; i < aPluginCount && plugin; plugin = plugin->mNext) {
4175 if (plugin->IsEnabled()) {
4176 nsIDOMPlugin* domPlugin = new DOMPluginImpl(plugin);
4177 NS_IF_ADDREF(domPlugin);
4178 aPluginArray[i++] = domPlugin;
4182 return NS_OK;
4185 NS_IMETHODIMP
4186 nsPluginHostImpl::GetPluginTags(PRUint32* aPluginCount, nsIPluginTag*** aResults)
4188 LoadPlugins();
4190 PRUint32 count = 0;
4191 nsRefPtr<nsPluginTag> plugin = mPlugins;
4192 while (plugin != nsnull) {
4193 count++;
4194 plugin = plugin->mNext;
4197 *aResults = static_cast<nsIPluginTag**>
4198 (nsMemory::Alloc(count * sizeof(**aResults)));
4199 if (!*aResults)
4200 return NS_ERROR_OUT_OF_MEMORY;
4202 *aPluginCount = count;
4204 plugin = mPlugins;
4205 PRUint32 i;
4206 for (i = 0; i < count; i++) {
4207 (*aResults)[i] = plugin;
4208 NS_ADDREF((*aResults)[i]);
4209 plugin = plugin->mNext;
4212 return NS_OK;
4215 nsPluginTag*
4216 nsPluginHostImpl::FindPluginForType(const char* aMimeType,
4217 PRBool aCheckEnabled)
4219 nsPluginTag *plugins = nsnull;
4220 PRInt32 variants, cnt;
4222 LoadPlugins();
4224 // if we have a mimetype passed in, search the mPlugins
4225 // linked list for a match
4226 if (nsnull != aMimeType) {
4227 plugins = mPlugins;
4229 while (nsnull != plugins) {
4230 variants = plugins->mVariants;
4231 for (cnt = 0; cnt < variants; cnt++) {
4232 if ((!aCheckEnabled || plugins->IsEnabled()) &&
4233 plugins->mMimeTypeArray[cnt] &&
4234 (0 == PL_strcasecmp(plugins->mMimeTypeArray[cnt], aMimeType))) {
4235 return plugins;
4239 plugins = plugins->mNext;
4243 return nsnull;
4246 nsPluginTag*
4247 nsPluginHostImpl::FindPluginEnabledForExtension(const char* aExtension,
4248 const char*& aMimeType)
4250 nsPluginTag *plugins = nsnull;
4251 PRInt32 variants, cnt;
4253 LoadPlugins();
4255 // if we have a mimetype passed in, search the mPlugins linked
4256 // list for a match
4257 if (aExtension) {
4258 plugins = mPlugins;
4259 while (plugins) {
4260 variants = plugins->mVariants;
4261 if (plugins->mExtensionsArray) {
4262 for (cnt = 0; cnt < variants; cnt++) {
4263 // mExtensionsArray[cnt] is a list of extensions separated
4264 // by commas
4265 if (plugins->IsEnabled() &&
4266 0 == CompareExtensions(plugins->mExtensionsArray[cnt], aExtension)) {
4267 aMimeType = plugins->mMimeTypeArray[cnt];
4268 return plugins;
4273 plugins = plugins->mNext;
4277 return nsnull;
4280 #if defined(XP_MACOSX)
4281 /* The following code examines the format of a Mac OS X binary, and determines whether it
4282 * is compatible with the current executable.
4285 #include <sys/stat.h>
4286 #include <sys/fcntl.h>
4287 #include <unistd.h>
4289 static inline PRBool is_directory(const char* path)
4291 struct stat sb;
4292 return ::stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
4295 static inline PRBool is_symbolic_link(const char* path)
4297 struct stat sb;
4298 return ::lstat(path, &sb) == 0 && S_ISLNK(sb.st_mode);
4301 static int open_executable(const char* path)
4303 int fd = 0;
4304 char resolvedPath[PATH_MAX] = "\0";
4306 // If the root of the bundle as referred to by path is a symbolic link,
4307 // CFBundleCopyExecutableURL will not return an absolute URL, but will
4308 // instead only return the executable name, such as "MRJPlugin". Work
4309 // around that by always using a fully-resolved absolute pathname.
4310 if (is_symbolic_link(path)) {
4311 path = realpath(path, resolvedPath);
4312 if (!path)
4313 return fd;
4316 // if this is a directory, it must be a bundle, so get the true path using CFBundle...
4317 if (is_directory(path)) {
4318 CFBundleRef bundle = NULL;
4319 CFStringRef pathRef = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
4320 if (pathRef) {
4321 CFURLRef bundleURL = CFURLCreateWithFileSystemPath(NULL, pathRef, kCFURLPOSIXPathStyle, true);
4322 CFRelease(pathRef);
4323 if (bundleURL != NULL) {
4324 bundle = CFBundleCreate(NULL, bundleURL);
4325 CFRelease(bundleURL);
4326 if (bundle) {
4327 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle);
4328 if (executableURL) {
4329 pathRef = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
4330 CFRelease(executableURL);
4331 if (pathRef) {
4332 CFIndex bufferSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pathRef), kCFStringEncodingUTF8) + 1;
4333 char* executablePath = new char[bufferSize];
4334 if (executablePath && CFStringGetCString(pathRef, executablePath, bufferSize, kCFStringEncodingUTF8)) {
4335 fd = open(executablePath, O_RDONLY, 0);
4336 delete[] executablePath;
4338 CFRelease(pathRef);
4341 CFRelease(bundle);
4345 } else {
4346 fd = open(path, O_RDONLY, 0);
4348 return fd;
4351 static PRBool IsCompatibleExecutable(const char* path)
4353 int fd = open_executable(path);
4354 if (fd > 0) {
4355 // Check the executable header to see if it is something we can use. Currently
4356 // we can only use 32-bit mach-o and FAT binaries (FAT headers are always big
4357 // endian, so we do network-to-host swap on the bytes from disk).
4358 // Note: We assume FAT binaries contain the right arch. Maybe fix that later.
4359 UInt32 magic;
4360 ssize_t n = read(fd, &magic, sizeof(magic));
4361 close(fd);
4362 if (n == sizeof(magic)) {
4363 if ((magic == MH_MAGIC) || (PR_ntohl(magic) == FAT_MAGIC))
4364 return PR_TRUE;
4367 return PR_FALSE;
4370 #else
4372 inline PRBool IsCompatibleExecutable(const char* path) { return PR_TRUE; }
4374 #endif
4376 static nsresult ConvertToNative(nsIUnicodeEncoder *aEncoder,
4377 const nsACString& aUTF8String,
4378 nsACString& aNativeString)
4380 NS_ConvertUTF8toUTF16 utf16(aUTF8String);
4381 PRInt32 len = utf16.Length();
4382 PRInt32 outLen;
4383 nsresult rv = aEncoder->GetMaxLength(utf16.get(), len, &outLen);
4384 NS_ENSURE_SUCCESS(rv, rv);
4385 if (!EnsureStringLength(aNativeString, outLen))
4386 return NS_ERROR_OUT_OF_MEMORY;
4387 rv = aEncoder->Convert(utf16.get(), &len,
4388 aNativeString.BeginWriting(), &outLen);
4389 NS_ENSURE_SUCCESS(rv, rv);
4390 aNativeString.SetLength(outLen);
4391 return NS_OK;
4394 static nsresult CreateNPAPIPlugin(nsIServiceManagerObsolete* aServiceManager,
4395 const nsPluginTag *aPluginTag,
4396 nsIPlugin **aOutNPAPIPlugnin)
4398 nsresult rv;
4399 nsCOMPtr <nsIPlatformCharset> pcs =
4400 do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
4401 NS_ENSURE_SUCCESS(rv, rv);
4403 nsCAutoString charset;
4404 rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
4405 NS_ENSURE_SUCCESS(rv, rv);
4407 nsCAutoString fileName, fullPath;
4408 if (!charset.LowerCaseEqualsLiteral("utf-8")) {
4409 nsCOMPtr<nsIUnicodeEncoder> encoder;
4410 nsCOMPtr<nsICharsetConverterManager> ccm =
4411 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
4412 NS_ENSURE_SUCCESS(rv, rv);
4413 rv = ccm->GetUnicodeEncoderRaw(charset.get(), getter_AddRefs(encoder));
4414 NS_ENSURE_SUCCESS(rv, rv);
4415 rv = ConvertToNative(encoder, aPluginTag->mFileName, fileName);
4416 NS_ENSURE_SUCCESS(rv, rv);
4417 rv = ConvertToNative(encoder, aPluginTag->mFullPath, fullPath);
4418 NS_ENSURE_SUCCESS(rv, rv);
4419 } else {
4420 fileName = aPluginTag->mFileName;
4421 fullPath = aPluginTag->mFullPath;
4424 return nsNPAPIPlugin::CreatePlugin(fileName.get(),
4425 fullPath.get(),
4426 aPluginTag->mLibrary,
4427 aOutNPAPIPlugnin);
4430 NS_IMETHODIMP nsPluginHostImpl::GetPluginFactory(const char *aMimeType, nsIPlugin** aPlugin)
4432 nsresult rv = NS_ERROR_FAILURE;
4433 *aPlugin = NULL;
4435 if (!aMimeType)
4436 return NS_ERROR_ILLEGAL_VALUE;
4438 // If plugins haven't been scanned yet, do so now
4439 LoadPlugins();
4441 nsPluginTag* pluginTag = FindPluginForType(aMimeType, PR_TRUE);
4442 if (pluginTag) {
4443 rv = NS_OK;
4444 PLUGIN_LOG(PLUGIN_LOG_BASIC,
4445 ("nsPluginHostImpl::GetPluginFactory Begin mime=%s, plugin=%s\n",
4446 aMimeType, pluginTag->mFileName.get()));
4448 #ifdef NS_DEBUG
4449 if (aMimeType && !pluginTag->mFileName.IsEmpty())
4450 printf("For %s found plugin %s\n", aMimeType, pluginTag->mFileName.get());
4451 #endif
4453 if (!pluginTag->mLibrary) { // if we haven't done this yet
4454 nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1");
4455 #if !defined(XP_MACOSX)
4456 file->InitWithPath(NS_ConvertUTF8toUTF16(pluginTag->mFileName));
4457 #else
4458 if (pluginTag->mFullPath.IsEmpty())
4459 return NS_ERROR_FAILURE;
4460 file->InitWithPath(NS_ConvertUTF8toUTF16(pluginTag->mFullPath));
4461 #endif
4462 nsPluginFile pluginFile(file);
4463 PRLibrary* pluginLibrary = NULL;
4465 if (pluginFile.LoadPlugin(pluginLibrary) != NS_OK || pluginLibrary == NULL)
4466 return NS_ERROR_FAILURE;
4468 // remove from unused lib list, if it is there
4469 if (mUnusedLibraries.IndexOf(pluginLibrary) > -1)
4470 mUnusedLibraries.RemoveElement(pluginLibrary);
4472 pluginTag->mLibrary = pluginLibrary;
4475 nsIPlugin* plugin = pluginTag->mEntryPoint;
4476 if (!plugin) {
4477 // nsIPlugin* of xpcom plugins can be found thru a call to
4478 // nsIComponentManager::GetClassObjectByContractID()
4479 nsCAutoString contractID(
4480 NS_LITERAL_CSTRING(NS_INLINE_PLUGIN_CONTRACTID_PREFIX) +
4481 nsDependentCString(aMimeType));
4482 nsresult rv = CallGetClassObject(contractID.get(), &plugin);
4483 if (NS_SUCCEEDED(rv) && plugin) {
4484 // plugin is already addref'd
4485 pluginTag->mEntryPoint = plugin;
4486 plugin->Initialize();
4490 if (!plugin) {
4491 // No, this is not a leak. GetGlobalServiceManager() doesn't
4492 // addref the pointer on the way out. It probably should.
4493 nsIServiceManagerObsolete* serviceManager;
4494 nsServiceManager::GetGlobalServiceManager((nsIServiceManager**)&serviceManager);
4496 // need to get the plugin factory from this plugin.
4497 nsFactoryProc nsGetFactory = nsnull;
4498 #ifdef XP_OS2
4499 nsGetFactory = (nsFactoryProc) PR_FindFunctionSymbol(pluginTag->mLibrary, "_NSGetFactory");
4500 #else
4501 nsGetFactory = (nsFactoryProc) PR_FindFunctionSymbol(pluginTag->mLibrary, "NSGetFactory");
4502 #endif
4503 if (nsGetFactory && IsCompatibleExecutable(pluginTag->mFullPath.get())) {
4504 // XPCOM-style plugins (or at least the OJI one) cause crashes on
4505 // on windows GCC builds, so we're just turning them off for now.
4506 #if !defined(XP_WIN) || !defined(__GNUC__)
4507 rv = nsGetFactory(serviceManager, kPluginCID, nsnull, nsnull, // XXX fix ClassName/ContractID
4508 (nsIFactory**)&pluginTag->mEntryPoint);
4509 plugin = pluginTag->mEntryPoint;
4510 if (plugin)
4511 plugin->Initialize();
4512 #endif
4514 #ifdef XP_OS2
4515 // on OS2, first check if this might be legacy XPCOM module.
4516 else if (PR_FindSymbol(pluginTag->mLibrary, "NSGetFactory") &&
4517 IsCompatibleExecutable(pluginTag->mFullPath.get())) {
4518 // Possibly a legacy XPCOM module. We'll need to create a calling
4519 // vtable/calling convention wrapper for it.
4520 nsCOMPtr<nsILegacyPluginWrapperOS2> wrapper =
4521 do_GetService(NS_LEGACY_PLUGIN_WRAPPER_CONTRACTID, &rv);
4522 if (NS_SUCCEEDED(rv)) {
4523 rv = wrapper->GetFactory(serviceManager, kPluginCID, nsnull, nsnull,
4524 pluginTag->mLibrary, &pluginTag->mEntryPoint);
4525 plugin = pluginTag->mEntryPoint;
4526 if (plugin)
4527 plugin->Initialize();
4530 #endif
4531 else {
4532 // Now lets try to get the entry point from a 4.x plugin
4533 rv = CreateNPAPIPlugin(serviceManager, pluginTag, &plugin);
4534 if (NS_SUCCEEDED(rv))
4535 pluginTag->mEntryPoint = plugin;
4536 pluginTag->Mark(NS_PLUGIN_FLAG_OLDSCHOOL);
4537 // no need to initialize, already done by CreatePlugin()
4541 #if defined (XP_MACOSX)
4542 /* Flash 6.0 r50 and older on Mac has a bug which calls ::UseInputWindow(NULL, true)
4543 which turn off all our inline IME. Turn it back after the plugin
4544 initializtion and hope that future versions will be fixed. See bug 159016
4546 if (StringBeginsWith(pluginTag->mDescription,
4547 NS_LITERAL_CSTRING("Shockwave Flash 6.0"),
4548 nsCaseInsensitiveCStringComparator()) &&
4549 pluginTag->mDescription.Length() > 21) {
4550 int ver = atoi(pluginTag->mDescription.get() + 21);
4551 if (ver && ver <= 50)
4552 ::UseInputWindow(NULL, false);
4554 #endif
4556 if (plugin) {
4557 *aPlugin = plugin;
4558 plugin->AddRef();
4559 return NS_OK;
4563 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
4564 ("nsPluginHostImpl::GetPluginFactory End mime=%s, rv=%d, plugin=%p name=%s\n",
4565 aMimeType, rv, *aPlugin,
4566 (pluginTag ? pluginTag->mFileName.get() : "(not found)")));
4568 return rv;
4571 // XXX called from ScanPluginsDirectory only when told to filter
4572 // currently 'unwanted' plugins are Java, and all other plugins except
4573 // Acrobat, Flash, Quicktime and Shockwave
4574 static PRBool isUnwantedPlugin(nsPluginTag * tag)
4576 if (tag->mFileName.IsEmpty())
4577 return PR_TRUE;
4579 for (PRInt32 i = 0; i < tag->mVariants; ++i) {
4580 if (nsnull == PL_strcasecmp(tag->mMimeTypeArray[i], "application/pdf"))
4581 return PR_FALSE;
4583 if (nsnull == PL_strcasecmp(tag->mMimeTypeArray[i], "application/x-shockwave-flash"))
4584 return PR_FALSE;
4586 if (nsnull == PL_strcasecmp(tag->mMimeTypeArray[i],"application/x-director"))
4587 return PR_FALSE;
4590 // On Windows, we also want to include the Quicktime plugin from the 4.x directory
4591 // But because it spans several DLL's, the best check for now is by filename
4592 if (tag->mFileName.Find("npqtplugin", PR_TRUE, 0, -1) != kNotFound)
4593 return PR_FALSE;
4595 return PR_TRUE;
4598 PRBool nsPluginHostImpl::IsJavaMIMEType(const char* aType)
4600 return aType &&
4601 ((0 == PL_strncasecmp(aType, "application/x-java-vm",
4602 sizeof("application/x-java-vm") - 1)) ||
4603 (0 == PL_strncasecmp(aType, "application/x-java-applet",
4604 sizeof("application/x-java-applet") - 1)) ||
4605 (0 == PL_strncasecmp(aType, "application/x-java-bean",
4606 sizeof("application/x-java-bean") - 1)));
4609 nsPluginTag * nsPluginHostImpl::HaveSamePlugin(nsPluginTag * aPluginTag)
4611 for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
4612 if (tag->Equals(aPluginTag))
4613 return tag;
4615 return nsnull;
4618 PRBool nsPluginHostImpl::IsDuplicatePlugin(nsPluginTag * aPluginTag)
4620 nsPluginTag * tag = HaveSamePlugin(aPluginTag);
4621 if (tag) {
4622 // if we got the same plugin, check the full path to see if this is a dup;
4624 // mFileName contains full path on Windows and Unix and leaf name on Mac
4625 // if those are not equal, we have the same plugin with different path,
4626 // i.e. duplicate, return true
4627 if (!tag->mFileName.Equals(aPluginTag->mFileName))
4628 return PR_TRUE;
4630 // if they are equal, compare mFullPath fields just in case
4631 // mFileName contained leaf name only, and if not equal, return true
4632 if (!tag->mFullPath.Equals(aPluginTag->mFullPath))
4633 return PR_TRUE;
4636 // we do not have it at all, return false
4637 return PR_FALSE;
4640 // Structure for collecting plugin files found during directory scanning
4641 struct pluginFileinDirectory
4643 nsString mFilename;
4644 PRInt64 mModTime;
4646 pluginFileinDirectory()
4648 mModTime = LL_ZERO;
4652 // QuickSort callback for comparing the modification time of two files
4653 // if the times are the same, compare the filenames
4654 static int ComparePluginFileInDirectory (const void *v1, const void *v2, void *)
4656 const pluginFileinDirectory* pfd1 = static_cast<const pluginFileinDirectory*>(v1);
4657 const pluginFileinDirectory* pfd2 = static_cast<const pluginFileinDirectory*>(v2);
4659 PRInt32 result = 0;
4660 if (LL_EQ(pfd1->mModTime, pfd2->mModTime))
4661 result = Compare(pfd1->mFilename, pfd2->mFilename, nsCaseInsensitiveStringComparator());
4662 else if (LL_CMP(pfd1->mModTime, >, pfd2->mModTime))
4663 result = -1;
4664 else
4665 result = 1;
4667 return result;
4670 typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
4672 static nsresult FixUpPluginInfo(nsPluginInfo &aInfo, nsPluginFile &aPluginFile)
4674 #ifndef XP_WIN
4675 return NS_OK;
4676 #endif
4678 for (PRUint32 i = 0; i < aInfo.fVariantCount; i++) {
4679 if (PL_strcmp(aInfo.fMimeTypeArray[i], "*"))
4680 continue;
4682 // we got "*" type
4683 // check if this is an alien plugin (not our default plugin)
4684 // by trying to find a special entry point
4685 PRLibrary *library = nsnull;
4686 if (NS_FAILED(aPluginFile.LoadPlugin(library)) || !library)
4687 return NS_ERROR_FAILURE;
4689 NP_GETMIMEDESCRIPTION pf = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(library, "NP_GetMIMEDescription");
4691 if (pf) {
4692 // if we found it, this is the default plugin, return
4693 char * mimedescription = pf();
4694 if (!PL_strncmp(mimedescription, NS_PLUGIN_DEFAULT_MIME_DESCRIPTION, 1))
4695 return NS_OK;
4698 // if we are here that means we have an alien plugin
4699 // which wants to take over "*" type
4701 // change its "*" mime type to "[*]"
4702 PL_strfree(aInfo.fMimeTypeArray[i]);
4703 aInfo.fMimeTypeArray[i] = PL_strdup("[*]");
4705 // continue the loop?
4707 return NS_OK;
4710 /* Helper class which automatically deallocates a nsVoidArray of
4711 * pluginFileinDirectories when the array goes out of scope.
4713 class nsAutoPluginFileDeleter
4715 public:
4716 nsAutoPluginFileDeleter (nsAutoVoidArray& aPluginFiles)
4717 :mPluginFiles(aPluginFiles)
4720 ~nsAutoPluginFileDeleter()
4722 for (PRInt32 i = 0; i < mPluginFiles.Count(); ++i) {
4723 pluginFileinDirectory* pfd = static_cast<pluginFileinDirectory*>(mPluginFiles[i]);
4724 delete pfd;
4727 protected:
4728 // A reference to the array for which to perform deallocation.
4729 nsAutoVoidArray& mPluginFiles;
4732 nsresult nsPluginHostImpl::ScanPluginsDirectory(nsIFile * pluginsDir,
4733 nsIComponentManager * compManager,
4734 PRBool aCreatePluginList,
4735 PRBool * aPluginsChanged,
4736 PRBool checkForUnwantedPlugins)
4738 NS_ENSURE_ARG_POINTER(aPluginsChanged);
4739 nsresult rv;
4741 *aPluginsChanged = PR_FALSE;
4743 #ifdef PLUGIN_LOGGING
4744 nsCAutoString dirPath;
4745 pluginsDir->GetNativePath(dirPath);
4746 PLUGIN_LOG(PLUGIN_LOG_BASIC,
4747 ("nsPluginHostImpl::ScanPluginsDirectory dir=%s\n", dirPath.get()));
4748 #endif
4750 nsCOMPtr<nsISimpleEnumerator> iter;
4751 rv = pluginsDir->GetDirectoryEntries(getter_AddRefs(iter));
4752 if (NS_FAILED(rv))
4753 return rv;
4755 // Collect all the files in this directory in a void array we can sort later
4756 nsAutoVoidArray pluginFilesArray; // array for sorting files in this directory
4758 // Setup the helper which will cleanup the array.
4759 nsAutoPluginFileDeleter pluginFileArrayDeleter(pluginFilesArray);
4761 PRBool hasMore;
4762 while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
4763 nsCOMPtr<nsISupports> supports;
4764 rv = iter->GetNext(getter_AddRefs(supports));
4765 if (NS_FAILED(rv))
4766 continue;
4767 nsCOMPtr<nsILocalFile> dirEntry(do_QueryInterface(supports, &rv));
4768 if (NS_FAILED(rv))
4769 continue;
4771 // Sun's JRE 1.3.1 plugin must have symbolic links resolved or else it'll crash.
4772 // See bug 197855.
4773 dirEntry->Normalize();
4775 nsAutoString filePath;
4776 rv = dirEntry->GetPath(filePath);
4777 if (NS_FAILED(rv))
4778 continue;
4780 if (nsPluginsDir::IsPluginFile(dirEntry)) {
4781 pluginFileinDirectory * item = new pluginFileinDirectory();
4782 if (!item)
4783 return NS_ERROR_OUT_OF_MEMORY;
4785 // Get file mod time
4786 PRInt64 fileModTime = LL_ZERO;
4787 dirEntry->GetLastModifiedTime(&fileModTime);
4789 item->mModTime = fileModTime;
4790 item->mFilename = filePath;
4791 pluginFilesArray.AppendElement(item);
4793 } // end round of up of plugin files
4795 // now sort the array by file modification time or by filename, if equal
4796 // put newer plugins first to weed out dups and catch upgrades, see bug 119966
4797 pluginFilesArray.Sort(ComparePluginFileInDirectory, nsnull);
4799 // finally, go through the array, looking at each entry and continue processing it
4800 for (PRInt32 i = 0; i < pluginFilesArray.Count(); i++) {
4801 pluginFileinDirectory* pfd = static_cast<pluginFileinDirectory*>(pluginFilesArray[i]);
4802 nsCOMPtr <nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
4803 nsCOMPtr <nsILocalFile> localfile = do_QueryInterface(file);
4804 localfile->InitWithPath(pfd->mFilename);
4805 PRInt64 fileModTime = pfd->mModTime;
4807 // Look for it in our cache
4808 nsRefPtr<nsPluginTag> pluginTag;
4809 RemoveCachedPluginsInfo(NS_ConvertUTF16toUTF8(pfd->mFilename).get(),
4810 getter_AddRefs(pluginTag));
4812 PRBool enabled = PR_TRUE;
4813 PRBool seenBefore = PR_FALSE;
4814 if (pluginTag) {
4815 seenBefore = PR_TRUE;
4816 // If plugin changed, delete cachedPluginTag and don't use cache
4817 if (LL_NE(fileModTime, pluginTag->mLastModifiedTime)) {
4818 // Plugins has changed. Don't use cached plugin info.
4819 enabled = (pluginTag->Flags() & NS_PLUGIN_FLAG_ENABLED) != 0;
4820 pluginTag = nsnull;
4822 // plugin file changed, flag this fact
4823 *aPluginsChanged = PR_TRUE;
4825 else {
4826 // if it is unwanted plugin we are checking for, get it back to the cache info list
4827 // if this is a duplicate plugin, too place it back in the cache info list marking unwantedness
4828 if ((checkForUnwantedPlugins && isUnwantedPlugin(pluginTag)) ||
4829 IsDuplicatePlugin(pluginTag)) {
4830 if (!pluginTag->HasFlag(NS_PLUGIN_FLAG_UNWANTED)) {
4831 // Plugin switched from wanted to unwanted
4832 *aPluginsChanged = PR_TRUE;
4834 pluginTag->Mark(NS_PLUGIN_FLAG_UNWANTED);
4835 pluginTag->mNext = mCachedPlugins;
4836 mCachedPlugins = pluginTag;
4837 } else if (pluginTag->HasFlag(NS_PLUGIN_FLAG_UNWANTED)) {
4838 pluginTag->UnMark(NS_PLUGIN_FLAG_UNWANTED);
4839 // Plugin switched from unwanted to wanted
4840 *aPluginsChanged = PR_TRUE;
4844 else {
4845 // plugin file was added, flag this fact
4846 *aPluginsChanged = PR_TRUE;
4849 // if we are not creating the list, just continue the loop
4850 // no need to proceed if changes are detected
4851 if (!aCreatePluginList) {
4852 if (*aPluginsChanged)
4853 return NS_OK;
4854 else
4855 continue;
4858 // if it is not found in cache info list or has been changed, create a new one
4859 if (!pluginTag) {
4860 nsPluginFile pluginFile(file);
4861 PRLibrary* pluginLibrary = nsnull;
4863 // load the plugin's library so we can ask it some questions, but not for Windows
4864 #ifndef XP_WIN
4865 if (pluginFile.LoadPlugin(pluginLibrary) != NS_OK || pluginLibrary == nsnull)
4866 continue;
4867 #endif
4869 // create a tag describing this plugin.
4870 nsPluginInfo info;
4871 memset(&info, 0, sizeof(info));
4872 nsresult res = pluginFile.GetPluginInfo(info);
4873 // if we don't have mime type don't proceed, this is not a plugin
4874 if (NS_FAILED(res) || !info.fMimeTypeArray) {
4875 pluginFile.FreePluginInfo(info);
4876 continue;
4879 // Check for any potential '*' mime type handlers which are not our
4880 // own default plugin and disable them as they will break the plugin
4881 // finder service, see Bugzilla bug 132430
4882 if (!mAllowAlienStarHandler)
4883 FixUpPluginInfo(info, pluginFile);
4885 pluginTag = new nsPluginTag(&info);
4886 pluginFile.FreePluginInfo(info);
4888 if (pluginTag == nsnull)
4889 return NS_ERROR_OUT_OF_MEMORY;
4891 pluginTag->mLibrary = pluginLibrary;
4892 pluginTag->mLastModifiedTime = fileModTime;
4894 nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
4895 if (blocklist) {
4896 PRUint32 state;
4897 rv = blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
4898 EmptyString(), &state);
4900 if (NS_SUCCEEDED(rv)) {
4901 // If the blocklist says so then block the plugin. If the blocklist says
4902 // it is risky and we have never seen this plugin before then disable it
4903 if (state == nsIBlocklistService::STATE_BLOCKED)
4904 pluginTag->Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
4905 else if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore)
4906 enabled = PR_FALSE;
4910 if (!enabled || (pluginTag->mIsJavaPlugin && !mJavaEnabled))
4911 pluginTag->UnMark(NS_PLUGIN_FLAG_ENABLED);
4913 // if this is unwanted plugin we are checkin for, or this is a duplicate plugin,
4914 // add it to our cache info list so we can cache the unwantedness of this plugin
4915 // when we sync cached plugins to registry
4916 NS_ASSERTION(!pluginTag->HasFlag(NS_PLUGIN_FLAG_UNWANTED),
4917 "Brand-new tags should not be unwanted");
4918 if ((checkForUnwantedPlugins && isUnwantedPlugin(pluginTag)) ||
4919 IsDuplicatePlugin(pluginTag)) {
4920 pluginTag->Mark(NS_PLUGIN_FLAG_UNWANTED);
4921 pluginTag->mNext = mCachedPlugins;
4922 mCachedPlugins = pluginTag;
4926 // set the flag that we want to add this plugin to the list for now
4927 // and see if it remains after we check several reasons not to do so
4928 PRBool bAddIt = PR_TRUE;
4930 // check if this is a specific plugin we don't want
4931 if (checkForUnwantedPlugins && isUnwantedPlugin(pluginTag))
4932 bAddIt = PR_FALSE;
4934 // check if we already have this plugin in the list which
4935 // is possible if we do refresh
4936 if (bAddIt) {
4937 if (HaveSamePlugin(pluginTag)) {
4938 // we cannot get here if the plugin has just been added
4939 // and thus |pluginTag| is not from cache, because otherwise
4940 // it would not be present in the list;
4941 // so there is no need to delete |pluginTag| -- it _is_ from the cache info list.
4942 bAddIt = PR_FALSE;
4946 // do it if we still want it
4947 if (bAddIt) {
4948 pluginTag->SetHost(this);
4949 pluginTag->mNext = mPlugins;
4950 mPlugins = pluginTag;
4952 if (pluginTag->IsEnabled())
4953 pluginTag->RegisterWithCategoryManager(mOverrideInternalTypes);
4955 else if (!pluginTag->HasFlag(NS_PLUGIN_FLAG_UNWANTED)) {
4956 // we don't need it, delete it;
4957 // but don't delete unwanted plugins since they are cached
4958 // in the cache info list and will be deleted later
4959 pluginTag = nsnull;
4962 return NS_OK;
4965 nsresult nsPluginHostImpl::ScanPluginsDirectoryList(nsISimpleEnumerator * dirEnum,
4966 nsIComponentManager * compManager,
4967 PRBool aCreatePluginList,
4968 PRBool * aPluginsChanged,
4969 PRBool checkForUnwantedPlugins)
4971 PRBool hasMore;
4972 while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
4973 nsCOMPtr<nsISupports> supports;
4974 nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
4975 if (NS_FAILED(rv))
4976 continue;
4977 nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
4978 if (NS_FAILED(rv))
4979 continue;
4981 // don't pass aPluginsChanged directly to prevent it from been reset
4982 PRBool pluginschanged = PR_FALSE;
4983 ScanPluginsDirectory(nextDir, compManager, aCreatePluginList, &pluginschanged, checkForUnwantedPlugins);
4985 if (pluginschanged)
4986 *aPluginsChanged = PR_TRUE;
4988 // if changes are detected and we are not creating the list, do not proceed
4989 if (!aCreatePluginList && *aPluginsChanged)
4990 break;
4992 return NS_OK;
4995 NS_IMETHODIMP nsPluginHostImpl::LoadPlugins()
4997 // do not do anything if it is already done
4998 // use ReloadPlugins() to enforce loading
4999 if (mPluginsLoaded)
5000 return NS_OK;
5002 PRBool pluginschanged;
5003 nsresult rv = FindPlugins(PR_TRUE, &pluginschanged);
5004 if (NS_FAILED(rv))
5005 return rv;
5007 // only if plugins have changed will we ask XPTI to refresh
5008 if (pluginschanged) {
5009 // rescan XPTI to catch any newly installed interfaces
5010 nsCOMPtr<nsIInterfaceInfoManager>
5011 iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
5013 if (iim)
5014 iim->AutoRegisterInterfaces();
5016 nsCOMPtr<nsIObserverService>
5017 obsService(do_GetService("@mozilla.org/observer-service;1"));
5018 if (obsService)
5019 obsService->NotifyObservers(nsnull, "plugins-list-updated", nsnull);
5022 return NS_OK;
5025 #include "nsITimelineService.h"
5027 // if aCreatePluginList is false we will just scan for plugins
5028 // and see if any changes have been made to the plugins.
5029 // This is needed in ReloadPlugins to prevent possible recursive reloads
5030 nsresult nsPluginHostImpl::FindPlugins(PRBool aCreatePluginList, PRBool * aPluginsChanged)
5032 // let's start timing if we are only really creating the plugin list
5033 if (aCreatePluginList) {
5034 NS_TIMELINE_START_TIMER("LoadPlugins");
5037 #ifdef CALL_SAFETY_ON
5038 // check preferences on whether or not we want to try safe calls to plugins
5039 NS_INIT_PLUGIN_SAFE_CALLS;
5040 #endif
5042 NS_ENSURE_ARG_POINTER(aPluginsChanged);
5044 *aPluginsChanged = PR_FALSE;
5045 nsresult rv;
5047 // Read cached plugins info
5048 ReadPluginInfo();
5050 nsCOMPtr<nsIComponentManager> compManager;
5051 NS_GetComponentManager(getter_AddRefs(compManager));
5052 if (compManager)
5053 LoadXPCOMPlugins(compManager);
5055 #ifdef XP_WIN
5056 // Failure here is not a show-stopper so just warn.
5057 rv = EnsurePrivateDirServiceProvider();
5058 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register dir service provider.");
5059 #endif /* XP_WIN */
5061 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
5062 if (NS_FAILED(rv))
5063 return rv;
5065 nsCOMPtr<nsISimpleEnumerator> dirList;
5067 // Scan plugins directories;
5068 // don't pass aPluginsChanged directly, to prevent its
5069 // possible reset in subsequent ScanPluginsDirectory calls
5070 PRBool pluginschanged = PR_FALSE;
5072 // Scan the app-defined list of plugin dirs.
5073 rv = dirService->Get(NS_APP_PLUGINS_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dirList));
5074 if (NS_SUCCEEDED(rv)) {
5075 ScanPluginsDirectoryList(dirList, compManager, aCreatePluginList, &pluginschanged);
5077 if (pluginschanged)
5078 *aPluginsChanged = PR_TRUE;
5080 // if we are just looking for possible changes,
5081 // no need to proceed if changes are detected
5082 if (!aCreatePluginList && *aPluginsChanged) {
5083 mCachedPlugins = nsnull;
5084 return NS_OK;
5088 mPluginsLoaded = PR_TRUE; // at this point 'some' plugins have been loaded,
5089 // the rest is optional
5091 #ifdef XP_WIN
5092 PRBool bScanPLIDs = PR_FALSE;
5094 if (mPrefService)
5095 mPrefService->GetBoolPref("plugin.scan.plid.all", &bScanPLIDs);
5097 // Now lets scan any PLID directories
5098 if (bScanPLIDs && mPrivateDirServiceProvider) {
5099 rv = mPrivateDirServiceProvider->GetPLIDDirectories(getter_AddRefs(dirList));
5100 if (NS_SUCCEEDED(rv)) {
5101 ScanPluginsDirectoryList(dirList, compManager, aCreatePluginList, &pluginschanged);
5103 if (pluginschanged)
5104 *aPluginsChanged = PR_TRUE;
5106 // if we are just looking for possible changes,
5107 // no need to proceed if changes are detected
5108 if (!aCreatePluginList && *aPluginsChanged) {
5109 mCachedPlugins = nsnull;
5110 return NS_OK;
5116 // Scan the installation paths of our popular plugins if the prefs are enabled
5118 // This table controls the order of scanning
5119 const char* const prefs[] = {NS_WIN_JRE_SCAN_KEY, nsnull,
5120 NS_WIN_ACROBAT_SCAN_KEY, nsnull,
5121 NS_WIN_QUICKTIME_SCAN_KEY, nsnull,
5122 NS_WIN_WMP_SCAN_KEY, nsnull,
5123 NS_WIN_4DOTX_SCAN_KEY, "1" /* second column is flag for 4.x folder */ };
5125 PRUint32 size = sizeof(prefs) / sizeof(prefs[0]);
5127 for (PRUint32 i = 0; i < size; i+=2) {
5128 nsCOMPtr<nsIFile> dirToScan;
5129 PRBool bExists;
5130 if (NS_SUCCEEDED(dirService->Get(prefs[i], NS_GET_IID(nsIFile), getter_AddRefs(dirToScan))) &&
5131 dirToScan &&
5132 NS_SUCCEEDED(dirToScan->Exists(&bExists)) &&
5133 bExists) {
5135 PRBool bFilterUnwanted = PR_FALSE;
5137 // 4.x plugins folder stuff:
5138 // Normally we "filter" the 4.x folder through |IsUnwantedPlugin|
5139 // Check for a pref to see if we want to scan the entire 4.x plugins folder
5140 if (prefs[i+1]) {
5141 PRBool bScanEverything;
5142 bFilterUnwanted = PR_TRUE; // default to filter 4.x folder
5143 if (mPrefService &&
5144 NS_SUCCEEDED(mPrefService->GetBoolPref(prefs[i], &bScanEverything)) &&
5145 bScanEverything)
5146 bFilterUnwanted = PR_FALSE;
5149 ScanPluginsDirectory(dirToScan, compManager, aCreatePluginList, &pluginschanged, bFilterUnwanted);
5151 if (pluginschanged)
5152 *aPluginsChanged = PR_TRUE;
5154 // if we are just looking for possible changes,
5155 // no need to proceed if changes are detected
5156 if (!aCreatePluginList && *aPluginsChanged) {
5157 mCachedPlugins = nsnull;
5158 return NS_OK;
5162 #endif
5164 // if get to this point and did not detect changes in plugins
5165 // that means no plugins got updated or added
5166 // let's see if plugins have been removed
5167 if (!*aPluginsChanged) {
5168 // count plugins remained in cache, if there are some, that means some plugins were removed;
5169 // while counting, we should ignore unwanted plugins which are also present in cache
5170 PRUint32 cachecount = 0;
5171 for (nsPluginTag * cachetag = mCachedPlugins; cachetag; cachetag = cachetag->mNext) {
5172 if (!cachetag->HasFlag(NS_PLUGIN_FLAG_UNWANTED))
5173 cachecount++;
5175 // if there is something left in cache, some plugins got removed from the directory
5176 // and therefor their info did not get removed from the cache info list during directory scan;
5177 // flag this fact
5178 if (cachecount > 0)
5179 *aPluginsChanged = PR_TRUE;
5182 // if we are not creating the list, there is no need to proceed
5183 if (!aCreatePluginList) {
5184 mCachedPlugins = nsnull;
5185 return NS_OK;
5188 // if we are creating the list, it is already done;
5189 // update the plugins info cache if changes are detected
5190 if (*aPluginsChanged)
5191 WritePluginInfo();
5193 // No more need for cached plugins. Clear it up.
5194 mCachedPlugins = nsnull;
5197 * XXX Big time hack alert!!!!
5198 * Because Real Player 8 installs in the components folder, we must have this one off
5199 * scan for nppl3260.dll because XPCOM has shut off nsGetFactory type plugins.
5200 * When we stop supporting Real 8 or they fix their installer, this can go away.
5202 if (aCreatePluginList)
5203 ScanForRealInComponentsFolder(compManager);
5205 // reverse our list of plugins
5206 nsRefPtr<nsPluginTag> next;
5207 nsRefPtr<nsPluginTag> prev;
5208 for (nsRefPtr<nsPluginTag> cur = mPlugins; cur; cur = next) {
5209 next = cur->mNext;
5210 cur->mNext = prev;
5211 prev = cur;
5214 mPlugins = prev;
5216 NS_TIMELINE_STOP_TIMER("LoadPlugins");
5217 NS_TIMELINE_MARK_TIMER("LoadPlugins");
5219 return NS_OK;
5222 nsresult
5223 nsPluginHostImpl::LoadXPCOMPlugins(nsIComponentManager* aComponentManager)
5225 // the component reg is a flat file now see 48888
5226 // we have to reimplement this method if we need it
5228 // The "new style" XPCOM plugins have their information stored in
5229 // the component registry, under the key
5231 // nsIRegistry::Common/software/plugins
5233 // Enumerate through that list now, creating an nsPluginTag for
5234 // each.
5236 return NS_OK;
5239 nsresult
5240 nsPluginHostImpl::UpdatePluginInfo(nsPluginTag* aPluginTag)
5242 ReadPluginInfo();
5243 WritePluginInfo();
5244 mCachedPlugins = nsnull;
5246 if (!aPluginTag || aPluginTag->IsEnabled())
5247 return NS_OK;
5249 nsCOMPtr<nsISupportsArray> instsToReload;
5250 NS_NewISupportsArray(getter_AddRefs(instsToReload));
5251 mActivePluginList.stopRunning(instsToReload, aPluginTag);
5252 mActivePluginList.removeAllStopped();
5254 PRUint32 c;
5255 if (instsToReload && NS_SUCCEEDED(instsToReload->Count(&c)) && c > 0) {
5256 nsCOMPtr<nsIRunnable> ev = new nsPluginDocReframeEvent(instsToReload);
5257 if (ev)
5258 NS_DispatchToCurrentThread(ev);
5261 return NS_OK;
5264 nsresult
5265 nsPluginHostImpl::WritePluginInfo()
5268 nsresult rv = NS_OK;
5269 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
5270 if (NS_FAILED(rv))
5271 return rv;
5273 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
5274 getter_AddRefs(mPluginRegFile));
5276 if (!mPluginRegFile)
5277 return NS_ERROR_FAILURE;
5279 PRFileDesc* fd = nsnull;
5281 nsCOMPtr<nsIFile> pluginReg;
5283 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
5284 if (NS_FAILED(rv))
5285 return rv;
5287 rv = pluginReg->AppendNative(kPluginRegistryFilename);
5288 if (NS_FAILED(rv))
5289 return rv;
5291 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(pluginReg, &rv);
5292 if (NS_FAILED(rv))
5293 return rv;
5295 rv = localFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
5296 if (NS_FAILED(rv))
5297 return rv;
5299 PR_fprintf(fd, "Generated File. Do not edit.\n");
5301 PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\n",
5302 PLUGIN_REGISTRY_FIELD_DELIMITER,
5303 kPluginRegistryVersion,
5304 PLUGIN_REGISTRY_FIELD_DELIMITER,
5305 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
5307 // Store all plugins in the mPlugins list - all plugins currently in use.
5308 PR_fprintf(fd, "\n[PLUGINS]\n");
5310 nsPluginTag *taglist[] = {mPlugins, mCachedPlugins};
5311 for (int i=0; i<(int)(sizeof(taglist)/sizeof(nsPluginTag *)); i++) {
5312 for (nsPluginTag *tag = taglist[i]; tag; tag=tag->mNext) {
5313 // from mCachedPlugins list write down only unwanted plugins
5314 if ((taglist[i] == mCachedPlugins) && !tag->HasFlag(NS_PLUGIN_FLAG_UNWANTED))
5315 continue;
5316 // store each plugin info into the registry
5317 // filename & fullpath are on separate line
5318 // because they can contain field delimiter char
5319 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%s%c%c\n",
5320 (!tag->mFileName.IsEmpty() ? tag->mFileName.get() : ""),
5321 PLUGIN_REGISTRY_FIELD_DELIMITER,
5322 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
5323 (!tag->mFullPath.IsEmpty() ? tag->mFullPath.get() : ""),
5324 PLUGIN_REGISTRY_FIELD_DELIMITER,
5325 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
5326 (!tag->mVersion.IsEmpty() ? tag->mVersion.get() : ""),
5327 PLUGIN_REGISTRY_FIELD_DELIMITER,
5328 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
5330 // lastModifiedTimeStamp|canUnload|tag->mFlags
5331 PR_fprintf(fd, "%lld%c%d%c%lu%c%c\n",
5332 tag->mLastModifiedTime,
5333 PLUGIN_REGISTRY_FIELD_DELIMITER,
5334 tag->mCanUnloadLibrary,
5335 PLUGIN_REGISTRY_FIELD_DELIMITER,
5336 tag->Flags(),
5337 PLUGIN_REGISTRY_FIELD_DELIMITER,
5338 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
5340 //description, name & mtypecount are on separate line
5341 PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
5342 (!tag->mDescription.IsEmpty() ? tag->mDescription.get() : ""),
5343 PLUGIN_REGISTRY_FIELD_DELIMITER,
5344 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
5345 (!tag->mName.IsEmpty() ? tag->mName.get() : ""),
5346 PLUGIN_REGISTRY_FIELD_DELIMITER,
5347 PLUGIN_REGISTRY_END_OF_LINE_MARKER,
5348 tag->mVariants + (tag->mIsNPRuntimeEnabledJavaPlugin ? 1 : 0));
5350 // Add in each mimetype this plugin supports
5351 for (int i=0; i<tag->mVariants; i++) {
5352 PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
5353 i,PLUGIN_REGISTRY_FIELD_DELIMITER,
5354 (tag->mMimeTypeArray && tag->mMimeTypeArray[i] ? tag->mMimeTypeArray[i] : ""),
5355 PLUGIN_REGISTRY_FIELD_DELIMITER,
5356 (!tag->mMimeDescriptionArray[i].IsEmpty() ? tag->mMimeDescriptionArray[i].get() : ""),
5357 PLUGIN_REGISTRY_FIELD_DELIMITER,
5358 (tag->mExtensionsArray && tag->mExtensionsArray[i] ? tag->mExtensionsArray[i] : ""),
5359 PLUGIN_REGISTRY_FIELD_DELIMITER,
5360 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
5363 if (tag->mIsNPRuntimeEnabledJavaPlugin) {
5364 PR_fprintf(fd, "%d%c%s%c%s%c%s%c%c\n",
5365 tag->mVariants, PLUGIN_REGISTRY_FIELD_DELIMITER,
5366 "application/x-java-vm-npruntime",
5367 PLUGIN_REGISTRY_FIELD_DELIMITER,
5369 PLUGIN_REGISTRY_FIELD_DELIMITER,
5371 PLUGIN_REGISTRY_FIELD_DELIMITER,
5372 PLUGIN_REGISTRY_END_OF_LINE_MARKER);
5377 if (fd)
5378 PR_Close(fd);
5379 return NS_OK;
5382 #define PLUGIN_REG_MIMETYPES_ARRAY_SIZE 12
5383 nsresult
5384 nsPluginHostImpl::ReadPluginInfo()
5386 nsresult rv;
5388 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
5389 if (NS_FAILED(rv))
5390 return rv;
5392 directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
5393 getter_AddRefs(mPluginRegFile));
5395 if (!mPluginRegFile)
5396 return NS_ERROR_FAILURE;
5398 PRFileDesc* fd = nsnull;
5400 nsCOMPtr<nsIFile> pluginReg;
5402 rv = mPluginRegFile->Clone(getter_AddRefs(pluginReg));
5403 if (NS_FAILED(rv))
5404 return rv;
5406 rv = pluginReg->AppendNative(kPluginRegistryFilename);
5407 if (NS_FAILED(rv))
5408 return rv;
5410 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(pluginReg, &rv);
5411 if (NS_FAILED(rv))
5412 return rv;
5414 PRInt64 fileSize;
5415 rv = localFile->GetFileSize(&fileSize);
5416 if (NS_FAILED(rv))
5417 return rv;
5419 PRInt32 flen = nsInt64(fileSize);
5420 if (flen == 0) {
5421 NS_WARNING("Plugins Registry Empty!");
5422 return NS_OK; // ERROR CONDITION
5425 nsPluginManifestLineReader reader;
5426 char* registry = reader.Init(flen);
5427 if (!registry)
5428 return NS_ERROR_OUT_OF_MEMORY;
5430 rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
5431 if (NS_FAILED(rv))
5432 return rv;
5434 // set rv to return an error on goto out
5435 rv = NS_ERROR_FAILURE;
5437 PRInt32 bread = PR_Read(fd, registry, flen);
5438 PR_Close(fd);
5440 if (flen > bread)
5441 return rv;
5443 if (!ReadSectionHeader(reader, "HEADER"))
5444 return rv;;
5446 if (!reader.NextLine())
5447 return rv;
5449 char* values[6];
5451 // VersionLiteral, kPluginRegistryVersion
5452 if (2 != reader.ParseLine(values, 2))
5453 return rv;
5455 // VersionLiteral
5456 if (PL_strcmp(values[0], "Version"))
5457 return rv;
5459 // kPluginRegistryVersion
5460 PRInt32 vdiff = NS_CompareVersions(values[1], kPluginRegistryVersion);
5461 // If this is a registry from some future version then don't attempt to read it
5462 if (vdiff > 0)
5463 return rv;
5464 // If this is a registry from before the minimum then don't attempt to read it
5465 if (NS_CompareVersions(values[1], kMinimumRegistryVersion) < 0)
5466 return rv;
5468 // Registry v0.10 and upwards includes the plugin version field
5469 PRBool regHasVersion = NS_CompareVersions(values[1], "0.10") >= 0;
5471 if (!ReadSectionHeader(reader, "PLUGINS"))
5472 return rv;
5474 while (reader.NextLine()) {
5475 char *filename = reader.LinePtr();
5476 if (!reader.NextLine())
5477 return rv;
5479 char *fullpath = reader.LinePtr();
5480 if (!reader.NextLine())
5481 return rv;
5483 char *version;
5484 if (regHasVersion) {
5485 version = reader.LinePtr();
5486 if (!reader.NextLine())
5487 return rv;
5488 } else {
5489 version = "0";
5492 // lastModifiedTimeStamp|canUnload|tag.mFlag
5493 if (reader.ParseLine(values, 3) != 3)
5494 return rv;
5496 // If this is an old plugin registry mark this plugin tag to be refreshed
5497 PRInt64 lastmod = vdiff == 0 ? nsCRT::atoll(values[0]) : -1;
5498 PRBool canunload = atoi(values[1]);
5499 PRUint32 tagflag = atoi(values[2]);
5500 if (!reader.NextLine())
5501 return rv;
5503 char *description = reader.LinePtr();
5504 if (!reader.NextLine())
5505 return rv;
5507 char *name = reader.LinePtr();
5508 if (!reader.NextLine())
5509 return rv;
5511 int mimetypecount = atoi(reader.LinePtr());
5513 char *stackalloced[PLUGIN_REG_MIMETYPES_ARRAY_SIZE * 3];
5514 char **mimetypes;
5515 char **mimedescriptions;
5516 char **extensions;
5517 char **heapalloced = 0;
5518 if (mimetypecount > PLUGIN_REG_MIMETYPES_ARRAY_SIZE - 1) {
5519 heapalloced = new char *[mimetypecount * 3];
5520 mimetypes = heapalloced;
5521 } else {
5522 mimetypes = stackalloced;
5524 mimedescriptions = mimetypes + mimetypecount;
5525 extensions = mimedescriptions + mimetypecount;
5527 int mtr = 0; //mimetype read
5528 for (; mtr < mimetypecount; mtr++) {
5529 if (!reader.NextLine())
5530 break;
5532 //line number|mimetype|description|extension
5533 if (4 != reader.ParseLine(values, 4))
5534 break;
5535 int line = atoi(values[0]);
5536 if (line != mtr)
5537 break;
5538 mimetypes[mtr] = values[1];
5539 mimedescriptions[mtr] = values[2];
5540 extensions[mtr] = values[3];
5543 if (mtr != mimetypecount) {
5544 if (heapalloced) {
5545 delete [] heapalloced;
5547 return rv;
5550 nsRefPtr<nsPluginTag> tag = new nsPluginTag(name,
5551 description,
5552 filename,
5553 (*fullpath ? fullpath : 0), // we have to pass 0 prt if it's empty str
5554 version,
5555 (const char* const*)mimetypes,
5556 (const char* const*)mimedescriptions,
5557 (const char* const*)extensions,
5558 mimetypecount, lastmod, canunload, PR_TRUE);
5559 if (heapalloced)
5560 delete [] heapalloced;
5562 if (!tag)
5563 continue;
5565 // Mark plugin as loaded from cache
5566 tag->Mark(tagflag | NS_PLUGIN_FLAG_FROMCACHE);
5567 if (tag->mIsJavaPlugin) {
5568 if (mJavaEnabled)
5569 tag->Mark(NS_PLUGIN_FLAG_ENABLED);
5570 else
5571 tag->UnMark(NS_PLUGIN_FLAG_ENABLED);
5573 PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
5574 ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
5575 tag->mNext = mCachedPlugins;
5576 mCachedPlugins = tag;
5579 return NS_OK;
5582 void
5583 nsPluginHostImpl::RemoveCachedPluginsInfo(const char *filename, nsPluginTag **result)
5585 nsRefPtr<nsPluginTag> prev;
5586 nsRefPtr<nsPluginTag> tag = mCachedPlugins;
5587 while (tag)
5589 // compare filename or else the mFullPath if it exists. Mac seems to use
5590 // mFullPath for fullpath and mFileName for just the leafname of fullpath.
5591 // On win and unix, mFullPath is never used and mFileName is contains the
5592 // full pathname. All this should move to using nsIFile.
5593 if (tag->mFileName.Equals(filename) || tag->mFullPath.Equals(filename)) {
5594 // Found it. Remove it from our list
5595 if (prev)
5596 prev->mNext = tag->mNext;
5597 else
5598 mCachedPlugins = tag->mNext;
5599 tag->mNext = nsnull;
5600 *result = tag;
5601 NS_ADDREF(*result);
5602 break;
5604 prev = tag;
5605 tag = tag->mNext;
5609 #ifdef XP_WIN
5610 nsresult
5611 nsPluginHostImpl::EnsurePrivateDirServiceProvider()
5613 if (!mPrivateDirServiceProvider) {
5614 nsresult rv;
5615 mPrivateDirServiceProvider = new nsPluginDirServiceProvider();
5616 if (!mPrivateDirServiceProvider)
5617 return NS_ERROR_OUT_OF_MEMORY;
5618 nsCOMPtr<nsIDirectoryService> dirService(do_GetService(kDirectoryServiceContractID, &rv));
5619 if (NS_FAILED(rv))
5620 return rv;
5621 rv = dirService->RegisterProvider(mPrivateDirServiceProvider);
5622 if (NS_FAILED(rv))
5623 return rv;
5625 return NS_OK;
5627 #endif /* XP_WIN */
5629 // Called by GetURL and PostURL
5630 NS_IMETHODIMP nsPluginHostImpl::NewPluginURLStream(const nsString& aURL,
5631 nsIPluginInstance *aInstance,
5632 nsIPluginStreamListener* aListener,
5633 const char *aPostData,
5634 PRBool aIsFile,
5635 PRUint32 aPostDataLen,
5636 const char *aHeadersData,
5637 PRUint32 aHeadersDataLen)
5639 nsCOMPtr<nsIURI> url;
5640 nsAutoString absUrl;
5641 nsresult rv;
5643 if (aURL.Length() <= 0)
5644 return NS_OK;
5646 // get the full URL of the document that the plugin is embedded
5647 // in to create an absolute url in case aURL is relative
5648 nsCOMPtr<nsIDocument> doc;
5649 nsCOMPtr<nsIPluginInstancePeer> peer;
5650 nsCOMPtr<nsIPluginInstanceOwner> owner;
5651 rv = aInstance->GetPeer(getter_AddRefs(peer));
5652 if (NS_SUCCEEDED(rv) && peer) {
5653 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
5654 rv = privpeer->GetOwner(getter_AddRefs(owner));
5655 if (owner) {
5656 rv = owner->GetDocument(getter_AddRefs(doc));
5657 if (NS_SUCCEEDED(rv) && doc) {
5658 // Create an absolute URL
5659 rv = NS_MakeAbsoluteURI(absUrl, aURL, doc->GetBaseURI());
5664 if (absUrl.IsEmpty())
5665 absUrl.Assign(aURL);
5667 rv = NS_NewURI(getter_AddRefs(url), absUrl);
5669 if (NS_SUCCEEDED(rv)) {
5670 nsCOMPtr<nsIPluginTagInfo2> pti2 = do_QueryInterface(owner);
5671 nsCOMPtr<nsIDOMElement> element;
5672 if (pti2)
5673 pti2->GetDOMElement(getter_AddRefs(element));
5675 PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
5676 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT_SUBREQUEST,
5677 url,
5678 (doc ? doc->NodePrincipal() : nsnull),
5679 element,
5680 EmptyCString(), //mime guess
5681 nsnull, //extra
5682 &shouldLoad);
5683 if (NS_FAILED(rv)) return rv;
5684 if (NS_CP_REJECTED(shouldLoad)) {
5685 // Disallowed by content policy
5686 return NS_ERROR_CONTENT_BLOCKED;
5689 nsPluginStreamListenerPeer *listenerPeer = new nsPluginStreamListenerPeer;
5690 if (listenerPeer == NULL)
5691 return NS_ERROR_OUT_OF_MEMORY;
5693 NS_ADDREF(listenerPeer);
5694 rv = listenerPeer->Initialize(url, aInstance, aListener);
5696 if (NS_SUCCEEDED(rv)) {
5697 nsCOMPtr<nsIInterfaceRequestor> callbacks;
5698 if (doc) {
5699 // Get the script global object owner and use that as the
5700 // notification callback.
5701 nsIScriptGlobalObject* global = doc->GetScriptGlobalObject();
5702 if (global) {
5703 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(global);
5704 callbacks = do_QueryInterface(webNav);
5708 nsCOMPtr<nsIChannel> channel;
5710 rv = NS_NewChannel(getter_AddRefs(channel), url, nsnull,
5711 nsnull, /* do not add this internal plugin's channel
5712 on the load group otherwise this channel could be canceled
5713 form |nsWebShell::OnLinkClickSync| bug 166613 */
5714 callbacks);
5715 if (NS_FAILED(rv))
5716 return rv;
5718 if (doc) {
5719 // Set the owner of channel to the document principal...
5720 channel->SetOwner(doc->NodePrincipal());
5722 // And if it's a script allow it to execute against the
5723 // document's script context.
5724 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(channel));
5725 if (scriptChannel) {
5726 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
5727 // Plug-ins seem to depend on javascript: URIs running synchronously
5728 scriptChannel->SetExecuteAsync(PR_FALSE);
5732 // deal with headers and post data
5733 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
5734 if (httpChannel) {
5735 if (aPostData) {
5737 nsCOMPtr<nsIInputStream> postDataStream;
5738 rv = NS_NewPluginPostDataStream(getter_AddRefs(postDataStream), (const char*)aPostData,
5739 aPostDataLen, aIsFile);
5741 if (!postDataStream) {
5742 NS_RELEASE(aInstance);
5743 return NS_ERROR_UNEXPECTED;
5746 // XXX it's a bit of a hack to rewind the postdata stream
5747 // here but it has to be done in case the post data is
5748 // being reused multiple times.
5749 nsCOMPtr<nsISeekableStream>
5750 postDataSeekable(do_QueryInterface(postDataStream));
5751 if (postDataSeekable)
5752 postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
5754 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
5755 NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
5757 uploadChannel->SetUploadStream(postDataStream, EmptyCString(), -1);
5760 if (aHeadersData)
5761 rv = AddHeadersToChannel(aHeadersData, aHeadersDataLen, httpChannel);
5763 rv = channel->AsyncOpen(listenerPeer, nsnull);
5765 NS_RELEASE(listenerPeer);
5767 return rv;
5770 // Called by GetURL and PostURL
5771 nsresult
5772 nsPluginHostImpl::DoURLLoadSecurityCheck(nsIPluginInstance *aInstance,
5773 const char* aURL)
5775 nsresult rv;
5777 if (!aURL || *aURL == '\0')
5778 return NS_OK;
5780 // get the URL of the document that loaded the plugin
5781 nsCOMPtr<nsIDocument> doc;
5782 nsCOMPtr<nsIPluginInstancePeer> peer;
5783 rv = aInstance->GetPeer(getter_AddRefs(peer));
5784 if (NS_FAILED(rv) || !peer)
5785 return NS_ERROR_FAILURE;
5787 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
5788 nsCOMPtr<nsIPluginInstanceOwner> owner;
5789 rv = privpeer->GetOwner(getter_AddRefs(owner));
5790 if (!owner)
5791 return NS_ERROR_FAILURE;
5793 rv = owner->GetDocument(getter_AddRefs(doc));
5794 if (!doc)
5795 return NS_ERROR_FAILURE;
5797 // Create an absolute URL for the target in case the target is relative
5798 nsCOMPtr<nsIURI> targetURL;
5799 rv = NS_NewURI(getter_AddRefs(targetURL), aURL, doc->GetBaseURI());
5801 if (!targetURL)
5802 return NS_ERROR_FAILURE;
5804 nsCOMPtr<nsIScriptSecurityManager> secMan(
5805 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
5806 if (NS_FAILED(rv))
5807 return rv;
5809 return secMan->CheckLoadURIWithPrincipal(doc->NodePrincipal(), targetURL,
5810 nsIScriptSecurityManager::STANDARD);
5814 NS_IMETHODIMP
5815 nsPluginHostImpl::AddHeadersToChannel(const char *aHeadersData,
5816 PRUint32 aHeadersDataLen,
5817 nsIChannel *aGenericChannel)
5819 nsresult rv = NS_OK;
5821 nsCOMPtr<nsIHttpChannel> aChannel = do_QueryInterface(aGenericChannel);
5822 if (!aChannel) {
5823 return NS_ERROR_NULL_POINTER;
5826 // used during the manipulation of the String from the aHeadersData
5827 nsCAutoString headersString;
5828 nsCAutoString oneHeader;
5829 nsCAutoString headerName;
5830 nsCAutoString headerValue;
5831 PRInt32 crlf = 0;
5832 PRInt32 colon = 0;
5834 // Turn the char * buffer into an nsString.
5835 headersString = aHeadersData;
5837 // Iterate over the nsString: for each "\r\n" delimited chunk,
5838 // add the value as a header to the nsIHTTPChannel
5839 while (PR_TRUE) {
5840 crlf = headersString.Find("\r\n", PR_TRUE);
5841 if (-1 == crlf) {
5842 rv = NS_OK;
5843 return rv;
5845 headersString.Mid(oneHeader, 0, crlf);
5846 headersString.Cut(0, crlf + 2);
5847 oneHeader.StripWhitespace();
5848 colon = oneHeader.Find(":");
5849 if (-1 == colon) {
5850 rv = NS_ERROR_NULL_POINTER;
5851 return rv;
5853 oneHeader.Left(headerName, colon);
5854 colon++;
5855 oneHeader.Mid(headerValue, colon, oneHeader.Length() - colon);
5857 // FINALLY: we can set the header!
5859 rv = aChannel->SetRequestHeader(headerName, headerValue, PR_TRUE);
5860 if (NS_FAILED(rv)) {
5861 rv = NS_ERROR_NULL_POINTER;
5862 return rv;
5865 return rv;
5868 NS_IMETHODIMP
5869 nsPluginHostImpl::StopPluginInstance(nsIPluginInstance* aInstance)
5871 if (PluginDestructionGuard::DelayDestroy(aInstance)) {
5872 return NS_OK;
5875 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
5876 ("nsPluginHostImpl::StopPluginInstance called instance=%p\n",aInstance));
5878 nsActivePlugin * plugin = mActivePluginList.find(aInstance);
5880 if (plugin) {
5881 plugin->setStopped(PR_TRUE); // be sure we set the "stop" bit
5883 // if the plugin does not want to be 'cached' just remove it
5884 PRBool doCache = PR_TRUE;
5885 aInstance->GetValue(nsPluginInstanceVariable_DoCacheBool, (void *) &doCache);
5887 if (!doCache) {
5888 PRLibrary * library = nsnull;
5889 if (plugin->mPluginTag)
5890 library = plugin->mPluginTag->mLibrary;
5892 mActivePluginList.remove(plugin);
5893 } else {
5894 // if it is allowed to be cached simply stop it, but first we should check
5895 // if we haven't exceeded the maximum allowed number of cached instances
5897 // try to get the max cached plugins from a pref or use default
5898 PRUint32 max_num;
5899 nsresult rv = NS_ERROR_FAILURE;
5900 if (mPrefService)
5901 rv = mPrefService->GetIntPref(NS_PREF_MAX_NUM_CACHED_PLUGINS, (int*)&max_num);
5902 if (NS_FAILED(rv))
5903 max_num = DEFAULT_NUMBER_OF_STOPPED_PLUGINS;
5905 if (mActivePluginList.getStoppedCount() >= max_num) {
5906 nsActivePlugin * oldest = mActivePluginList.findOldestStopped();
5907 if (oldest != nsnull)
5908 mActivePluginList.remove(oldest);
5912 return NS_OK;
5915 nsresult nsPluginHostImpl::NewEmbeddedPluginStreamListener(nsIURI* aURL,
5916 nsIPluginInstanceOwner *aOwner,
5917 nsIPluginInstance* aInstance,
5918 nsIStreamListener** aListener)
5920 if (!aURL)
5921 return NS_OK;
5923 nsRefPtr<nsPluginStreamListenerPeer> listener =
5924 new nsPluginStreamListenerPeer();
5925 if (listener == nsnull)
5926 return NS_ERROR_OUT_OF_MEMORY;
5928 nsresult rv;
5930 // if we have an instance, everything has been set up
5931 // if we only have an owner, then we need to pass it in
5932 // so the listener can set up the instance later after
5933 // we've determined the mimetype of the stream
5934 if (aInstance != nsnull)
5935 rv = listener->InitializeEmbedded(aURL, aInstance);
5936 else if (aOwner != nsnull)
5937 rv = listener->InitializeEmbedded(aURL, nsnull, aOwner, this);
5938 else
5939 rv = NS_ERROR_ILLEGAL_VALUE;
5940 if (NS_SUCCEEDED(rv))
5941 NS_ADDREF(*aListener = listener);
5943 return rv;
5946 // Called by InstantiateEmbeddedPlugin()
5947 nsresult nsPluginHostImpl::NewEmbeddedPluginStream(nsIURI* aURL,
5948 nsIPluginInstanceOwner *aOwner,
5949 nsIPluginInstance* aInstance)
5951 nsCOMPtr<nsIStreamListener> listener;
5952 nsresult rv = NewEmbeddedPluginStreamListener(aURL, aOwner, aInstance,
5953 getter_AddRefs(listener));
5954 if (NS_SUCCEEDED(rv)) {
5955 nsCOMPtr<nsIDocument> doc;
5956 nsCOMPtr<nsILoadGroup> loadGroup;
5957 if (aOwner) {
5958 rv = aOwner->GetDocument(getter_AddRefs(doc));
5959 if (NS_SUCCEEDED(rv) && doc) {
5960 loadGroup = doc->GetDocumentLoadGroup();
5963 nsCOMPtr<nsIChannel> channel;
5964 rv = NS_NewChannel(getter_AddRefs(channel), aURL, nsnull, loadGroup, nsnull);
5965 if (NS_SUCCEEDED(rv)) {
5966 // if this is http channel, set referrer, some servers are configured
5967 // to reject requests without referrer set, see bug 157796
5968 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
5969 if (httpChannel && doc)
5970 httpChannel->SetReferrer(doc->GetDocumentURI());
5972 rv = channel->AsyncOpen(listener, nsnull);
5973 if (NS_SUCCEEDED(rv))
5974 return NS_OK;
5978 return rv;
5981 // Called by InstantiateFullPagePlugin()
5982 nsresult nsPluginHostImpl::NewFullPagePluginStream(nsIStreamListener *&aStreamListener,
5983 nsIPluginInstance *aInstance)
5985 nsPluginStreamListenerPeer *listener = new nsPluginStreamListenerPeer();
5986 if (!listener)
5987 return NS_ERROR_OUT_OF_MEMORY;
5989 nsresult rv;
5991 rv = listener->InitializeFullPage(aInstance);
5993 aStreamListener = listener;
5994 NS_ADDREF(listener);
5996 // add peer to list of stream peers for this instance
5997 nsActivePlugin * p = mActivePluginList.find(aInstance);
5998 if (p) {
5999 if (!p->mStreams && (NS_FAILED(rv = NS_NewISupportsArray(getter_AddRefs(p->mStreams)))))
6000 return rv;
6001 p->mStreams->AppendElement(aStreamListener);
6004 return rv;
6008 // nsIFileUtilities interface
6009 NS_IMETHODIMP nsPluginHostImpl::GetProgramPath(const char* *result)
6011 nsresult rv;
6012 NS_ENSURE_ARG_POINTER(result);
6013 *result = nsnull;
6015 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
6016 if (NS_FAILED(rv))
6017 return rv;
6018 nsCOMPtr<nsILocalFile> programDir;
6019 rv = dirService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(programDir));
6020 if (NS_FAILED(rv))
6021 return rv;
6023 nsCAutoString temp;
6024 rv = programDir->GetNativePath(temp);
6025 *result = ToNewCString(temp);
6026 return rv;
6029 NS_IMETHODIMP nsPluginHostImpl::GetTempDirPath(const char* *result)
6031 nsresult rv;
6032 NS_ENSURE_ARG_POINTER(result);
6033 *result = nsnull;
6035 nsCOMPtr<nsIProperties> dirService(do_GetService(kDirectoryServiceContractID, &rv));
6036 if (NS_FAILED(rv))
6037 return rv;
6038 nsCOMPtr<nsILocalFile> tempDir;
6039 rv = dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(tempDir));
6040 if (NS_FAILED(rv))
6041 return rv;
6043 nsCAutoString temp;
6044 rv = tempDir->GetNativePath(temp);
6045 *result = ToNewCString(temp);
6046 return rv;
6049 NS_IMETHODIMP nsPluginHostImpl::NewTempFileName(const char* prefix, PRUint32 bufLen, char* resultBuf)
6051 return NS_ERROR_NOT_IMPLEMENTED;
6054 // nsICookieStorage interface
6056 NS_IMETHODIMP nsPluginHostImpl::GetCookie(const char* inCookieURL, void* inOutCookieBuffer, PRUint32& inOutCookieSize)
6058 nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
6059 nsXPIDLCString cookieString;
6060 PRUint32 cookieStringLen = 0;
6061 nsCOMPtr<nsIURI> uriIn;
6063 if (!inCookieURL || (0 >= inOutCookieSize)) {
6064 return NS_ERROR_INVALID_ARG;
6067 nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
6069 if (NS_FAILED(rv) || !ioService)
6070 return rv;
6072 nsCOMPtr<nsICookieService> cookieService =
6073 do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
6075 if (NS_FAILED(rv) || !cookieService)
6076 return NS_ERROR_INVALID_ARG;
6078 // make an nsURI from the argument url
6079 rv = ioService->NewURI(nsDependentCString(inCookieURL), nsnull, nsnull, getter_AddRefs(uriIn));
6080 if (NS_FAILED(rv))
6081 return rv;
6083 rv = cookieService->GetCookieString(uriIn, nsnull, getter_Copies(cookieString));
6085 if (NS_FAILED(rv) || !cookieString ||
6086 (inOutCookieSize <= (cookieStringLen = PL_strlen(cookieString.get())))) {
6087 return NS_ERROR_FAILURE;
6090 PL_strcpy((char *) inOutCookieBuffer, cookieString.get());
6091 inOutCookieSize = cookieStringLen;
6092 rv = NS_OK;
6094 return rv;
6097 NS_IMETHODIMP nsPluginHostImpl::SetCookie(const char* inCookieURL, const void* inCookieBuffer, PRUint32 inCookieSize)
6099 nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
6100 nsCOMPtr<nsIURI> uriIn;
6102 if (!inCookieURL || !inCookieBuffer ||
6103 (0 >= inCookieSize)) {
6104 return NS_ERROR_INVALID_ARG;
6107 nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
6109 if (NS_FAILED(rv) || !ioService)
6110 return rv;
6112 nsCOMPtr<nsICookieService> cookieService =
6113 do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
6115 if (NS_FAILED(rv) || !cookieService)
6116 return NS_ERROR_FAILURE;
6118 // make an nsURI from the argument url
6119 rv = ioService->NewURI(nsDependentCString(inCookieURL), nsnull, nsnull, getter_AddRefs(uriIn));
6120 if (NS_FAILED(rv))
6121 return NS_ERROR_FAILURE;
6123 nsCOMPtr<nsIPrompt> prompt;
6124 GetPrompt(nsnull, getter_AddRefs(prompt));
6126 char * cookie = (char *)inCookieBuffer;
6127 char c = cookie[inCookieSize];
6128 cookie[inCookieSize] = '\0';
6129 rv = cookieService->SetCookieString(uriIn, prompt, cookie, nsnull);
6130 cookie[inCookieSize] = c;
6132 return rv;
6135 NS_IMETHODIMP nsPluginHostImpl::Observe(nsISupports *aSubject,
6136 const char *aTopic,
6137 const PRUnichar *someData)
6139 if (!nsCRT::strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
6140 OnShutdown();
6141 Destroy();
6142 UnloadUnusedLibraries();
6143 sInst->Release();
6145 if (!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
6146 NS_ASSERTION(someData &&
6147 nsDependentString(someData).EqualsLiteral("security.enable_java"),
6148 "Unexpected pref");
6149 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
6150 NS_ASSERTION(branch, "Not a pref branch?");
6151 PRBool enabled;
6152 if (NS_FAILED(branch->GetBoolPref("security.enable_java", &enabled)))
6153 enabled = PR_TRUE;
6155 if (enabled != mJavaEnabled) {
6156 mJavaEnabled = enabled;
6157 // We want to keep the java PluginTag around so we'll know that it's
6158 // actually disabled, not just not present. So just manually mark it as
6159 // disabled so at least FindPluginForType/Extension doesn't return
6160 // anything.
6161 for (nsPluginTag* cur = mPlugins; cur; cur = cur->mNext) {
6162 if (cur->mIsJavaPlugin)
6163 cur->SetDisabled(!mJavaEnabled);
6167 return NS_OK;
6170 NS_IMETHODIMP
6171 nsPluginHostImpl::HandleBadPlugin(PRLibrary* aLibrary, nsIPluginInstance *aInstance)
6173 // the |aLibrary| parameter is not needed anymore, after we added |aInstance| which
6174 // can also be used to look up the plugin name, but we cannot get rid of it because
6175 // the |nsIPluginHost| interface is deprecated which in fact means 'frozen'
6177 nsresult rv = NS_OK;
6179 NS_ASSERTION(PR_FALSE, "Plugin performed illegal operation");
6181 if (mDontShowBadPluginMessage)
6182 return rv;
6184 nsCOMPtr<nsIPluginInstanceOwner> owner;
6186 if (aInstance) {
6187 nsCOMPtr<nsIPluginInstancePeer> peer;
6188 rv = aInstance->GetPeer(getter_AddRefs(peer));
6189 if (NS_SUCCEEDED(rv) && peer) {
6190 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
6191 privpeer->GetOwner(getter_AddRefs(owner));
6195 nsCOMPtr<nsIPrompt> prompt;
6196 GetPrompt(owner, getter_AddRefs(prompt));
6197 if (prompt) {
6198 nsCOMPtr<nsIStringBundleService> strings(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
6199 if (NS_FAILED(rv))
6200 return rv;
6202 nsCOMPtr<nsIStringBundle> bundle;
6203 rv = strings->CreateBundle(BRAND_PROPERTIES_URL, getter_AddRefs(bundle));
6204 if (NS_FAILED(rv))
6205 return rv;
6207 nsXPIDLString brandName;
6208 if (NS_FAILED(rv = bundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
6209 getter_Copies(brandName))))
6210 return rv;
6212 rv = strings->CreateBundle(PLUGIN_PROPERTIES_URL, getter_AddRefs(bundle));
6213 if (NS_FAILED(rv))
6214 return rv;
6216 nsXPIDLString title, message, checkboxMessage;
6217 if (NS_FAILED(rv = bundle->GetStringFromName(NS_LITERAL_STRING("BadPluginTitle").get(),
6218 getter_Copies(title))))
6219 return rv;
6221 const PRUnichar *formatStrings[] = { brandName.get() };
6222 if (NS_FAILED(rv = bundle->FormatStringFromName(NS_LITERAL_STRING("BadPluginMessage").get(),
6223 formatStrings, 1, getter_Copies(message))))
6224 return rv;
6226 if (NS_FAILED(rv = bundle->GetStringFromName(NS_LITERAL_STRING("BadPluginCheckboxMessage").get(),
6227 getter_Copies(checkboxMessage))))
6228 return rv;
6230 // add plugin name to the message
6231 nsCString pluginname;
6232 nsActivePlugin * p = mActivePluginList.find(aInstance);
6233 if (p) {
6234 nsPluginTag * tag = p->mPluginTag;
6235 if (tag) {
6236 if (!tag->mName.IsEmpty())
6237 pluginname = tag->mName;
6238 else
6239 pluginname = tag->mFileName;
6243 NS_ConvertUTF8toUTF16 msg(pluginname);
6244 msg.AppendLiteral("\n\n");
6245 msg.Append(message);
6247 PRInt32 buttonPressed;
6248 PRBool checkboxState = PR_FALSE;
6249 rv = prompt->ConfirmEx(title, msg.get(),
6250 nsIPrompt::BUTTON_TITLE_OK * nsIPrompt::BUTTON_POS_0,
6251 nsnull, nsnull, nsnull,
6252 checkboxMessage, &checkboxState, &buttonPressed);
6255 if (NS_SUCCEEDED(rv) && checkboxState)
6256 mDontShowBadPluginMessage = PR_TRUE;
6259 return rv;
6262 // nsPIPluginHost interface
6264 NS_IMETHODIMP
6265 nsPluginHostImpl::SetIsScriptableInstance(nsIPluginInstance * aPluginInstance, PRBool aScriptable)
6267 nsActivePlugin * p = mActivePluginList.find(aPluginInstance);
6268 if (p == nsnull)
6269 return NS_ERROR_FAILURE;
6271 p->mXPConnected = aScriptable;
6272 if (p->mPluginTag)
6273 p->mPluginTag->mXPConnected = aScriptable;
6275 return NS_OK;
6278 NS_IMETHODIMP
6279 nsPluginHostImpl::ParsePostBufferToFixHeaders(
6280 const char *inPostData, PRUint32 inPostDataLen,
6281 char **outPostData, PRUint32 *outPostDataLen)
6283 if (!inPostData || !outPostData || !outPostDataLen)
6284 return NS_ERROR_NULL_POINTER;
6286 *outPostData = 0;
6287 *outPostDataLen = 0;
6289 const char CR = '\r';
6290 const char LF = '\n';
6291 const char CRLFCRLF[] = {CR,LF,CR,LF,'\0'}; // C string"\r\n\r\n"
6292 const char ContentLenHeader[] = "Content-length";
6294 nsAutoVoidArray singleLF;
6295 const char *pSCntlh = 0;// pointer to start of ContentLenHeader in inPostData
6296 const char *pSod = 0; // pointer to start of data in inPostData
6297 const char *pEoh = 0; // pointer to end of headers in inPostData
6298 const char *pEod = inPostData + inPostDataLen; // pointer to end of inPostData
6299 if (*inPostData == LF) {
6300 // from 4.x spec http://developer.netscape.com/docs/manuals/communicator/plugin/pgfn2.htm#1007754
6301 // If no custom headers are required, simply add a blank
6302 // line ('\n') to the beginning of the file or buffer.
6303 // so *inPostData == '\n' is valid
6304 pSod = inPostData + 1;
6305 } else {
6306 const char *s = inPostData; //tmp pointer to sourse inPostData
6307 while (s < pEod) {
6308 if (!pSCntlh &&
6309 (*s == 'C' || *s == 'c') &&
6310 (s + sizeof(ContentLenHeader) - 1 < pEod) &&
6311 (!PL_strncasecmp(s, ContentLenHeader, sizeof(ContentLenHeader) - 1)))
6313 // lets assume this is ContentLenHeader for now
6314 const char *p = pSCntlh = s;
6315 p += sizeof(ContentLenHeader) - 1;
6316 // search for first CR or LF == end of ContentLenHeader
6317 for (; p < pEod; p++) {
6318 if (*p == CR || *p == LF) {
6319 // got delimiter,
6320 // one more check; if previous char is a digit
6321 // most likely pSCntlh points to the start of ContentLenHeader
6322 if (*(p-1) >= '0' && *(p-1) <= '9') {
6323 s = p;
6325 break; //for loop
6328 if (pSCntlh == s) { // curret ptr is the same
6329 pSCntlh = 0; // that was not ContentLenHeader
6330 break; // there is nothing to parse, break *WHILE LOOP* here
6334 if (*s == CR) {
6335 if (pSCntlh && // only if ContentLenHeader is found we are looking for end of headers
6336 ((s + sizeof(CRLFCRLF)-1) <= pEod) &&
6337 !memcmp(s, CRLFCRLF, sizeof(CRLFCRLF)-1))
6339 s += sizeof(CRLFCRLF)-1;
6340 pEoh = pSod = s; // data stars here
6341 break;
6343 } else if (*s == LF) {
6344 if (*(s-1) != CR) {
6345 singleLF.AppendElement((void*)s);
6347 if (pSCntlh && (s+1 < pEod) && (*(s+1) == LF)) {
6348 s++;
6349 singleLF.AppendElement((void*)s);
6350 s++;
6351 pEoh = pSod = s; // data stars here
6352 break;
6355 s++;
6359 // deal with output buffer
6360 if (!pSod) { // lets assume whole buffer is a data
6361 pSod = inPostData;
6364 PRUint32 newBufferLen = 0;
6365 PRUint32 dataLen = pEod - pSod;
6366 PRUint32 headersLen = pEoh ? pSod - inPostData : 0;
6368 char *p; // tmp ptr into new output buf
6369 if (headersLen) { // we got a headers
6370 // this function does not make any assumption on correctness
6371 // of ContentLenHeader value in this case.
6373 newBufferLen = dataLen + headersLen;
6374 // in case there were single LFs in headers
6375 // reserve an extra space for CR will be added before each single LF
6376 int cntSingleLF = singleLF.Count();
6377 newBufferLen += cntSingleLF;
6379 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
6380 return NS_ERROR_OUT_OF_MEMORY;
6382 // deal with single LF
6383 const char *s = inPostData;
6384 if (cntSingleLF) {
6385 for (int i=0; i<cntSingleLF; i++) {
6386 const char *plf = (const char*) singleLF.ElementAt(i); // ptr to single LF in headers
6387 int n = plf - s; // bytes to copy
6388 if (n) { // for '\n\n' there is nothing to memcpy
6389 memcpy(p, s, n);
6390 p += n;
6392 *p++ = CR;
6393 s = plf;
6394 *p++ = *s++;
6397 // are we done with headers?
6398 headersLen = pEoh - s;
6399 if (headersLen) { // not yet
6400 memcpy(p, s, headersLen); // copy the rest
6401 p += headersLen;
6403 } else if (dataLen) { // no ContentLenHeader is found but there is a data
6404 // make new output buffer big enough
6405 // to keep ContentLenHeader+value followed by data
6406 PRUint32 l = sizeof(ContentLenHeader) + sizeof(CRLFCRLF) + 32;
6407 newBufferLen = dataLen + l;
6408 if (!(*outPostData = p = (char*)nsMemory::Alloc(newBufferLen)))
6409 return NS_ERROR_OUT_OF_MEMORY;
6410 headersLen = PR_snprintf(p, l,"%s: %ld%s", ContentLenHeader, dataLen, CRLFCRLF);
6411 if (headersLen == l) { // if PR_snprintf has ate all extra space consider this as an error
6412 nsMemory::Free(p);
6413 *outPostData = 0;
6414 return NS_ERROR_FAILURE;
6416 p += headersLen;
6417 newBufferLen = headersLen + dataLen;
6419 // at this point we've done with headers.
6420 // there is a possibility that input buffer has only headers info in it
6421 // which already parsed and copied into output buffer.
6422 // copy the data
6423 if (dataLen) {
6424 memcpy(p, pSod, dataLen);
6427 *outPostDataLen = newBufferLen;
6429 return NS_OK;
6432 NS_IMETHODIMP
6433 nsPluginHostImpl::CreateTmpFileToPost(const char *postDataURL, char **pTmpFileName)
6435 *pTmpFileName = 0;
6436 nsresult rv;
6437 PRInt64 fileSize;
6438 nsCAutoString filename;
6440 // stat file == get size & convert file:///c:/ to c: if needed
6441 nsCOMPtr<nsIFile> inFile;
6442 rv = NS_GetFileFromURLSpec(nsDependentCString(postDataURL),
6443 getter_AddRefs(inFile));
6444 if (NS_FAILED(rv)) {
6445 nsCOMPtr<nsILocalFile> localFile;
6446 rv = NS_NewNativeLocalFile(nsDependentCString(postDataURL), PR_FALSE,
6447 getter_AddRefs(localFile));
6448 if (NS_FAILED(rv)) return rv;
6449 inFile = localFile;
6451 rv = inFile->GetFileSize(&fileSize);
6452 if (NS_FAILED(rv)) return rv;
6453 rv = inFile->GetNativePath(filename);
6454 if (NS_FAILED(rv)) return rv;
6456 if (!LL_IS_ZERO(fileSize)) {
6457 nsCOMPtr<nsIInputStream> inStream;
6458 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), inFile);
6459 if (NS_FAILED(rv)) return rv;
6461 // Create a temporary file to write the http Content-length:
6462 // %ld\r\n\" header and "\r\n" == end of headers for post data to
6464 nsCOMPtr<nsIFile> tempFile;
6465 rv = GetPluginTempDir(getter_AddRefs(tempFile));
6466 if (NS_FAILED(rv))
6467 return rv;
6469 nsCAutoString inFileName;
6470 inFile->GetNativeLeafName(inFileName);
6471 // XXX hack around bug 70083
6472 inFileName.Insert(NS_LITERAL_CSTRING("post-"), 0);
6473 rv = tempFile->AppendNative(inFileName);
6475 if (NS_FAILED(rv))
6476 return rv;
6478 // make it unique, and mode == 0600, not world-readable
6479 rv = tempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
6480 if (NS_FAILED(rv))
6481 return rv;
6483 nsCOMPtr<nsIOutputStream> outStream;
6484 if (NS_SUCCEEDED(rv)) {
6485 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
6486 tempFile,
6487 (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE),
6488 0600); // 600 so others can't read our form data
6490 NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
6491 if (NS_FAILED(rv))
6492 return rv;
6494 char buf[1024];
6495 PRUint32 br, bw;
6496 PRBool firstRead = PR_TRUE;
6497 while (1) {
6498 // Read() mallocs if buffer is null
6499 rv = inStream->Read(buf, 1024, &br);
6500 if (NS_FAILED(rv) || (PRInt32)br <= 0)
6501 break;
6502 if (firstRead) {
6503 // according to the 4.x spec
6504 // http://developer.netscape.com/docs/manuals/communicator/plugin/pgfn2.htm#1007707
6505 //"For protocols in which the headers must be distinguished from the body,
6506 // such as HTTP, the buffer or file should contain the headers, followed by
6507 // a blank line, then the body. If no custom headers are required, simply
6508 // add a blank line ('\n') to the beginning of the file or buffer.
6510 char *parsedBuf;
6511 // assuming first 1K (or what we got) has all headers in,
6512 // lets parse it through nsPluginHostImpl::ParsePostBufferToFixHeaders()
6513 ParsePostBufferToFixHeaders((const char *)buf, br, &parsedBuf, &bw);
6514 rv = outStream->Write(parsedBuf, bw, &br);
6515 nsMemory::Free(parsedBuf);
6516 if (NS_FAILED(rv) || (bw != br))
6517 break;
6519 firstRead = PR_FALSE;
6520 continue;
6522 bw = br;
6523 rv = outStream->Write(buf, bw, &br);
6524 if (NS_FAILED(rv) || (bw != br))
6525 break;
6528 inStream->Close();
6529 outStream->Close();
6530 if (NS_SUCCEEDED(rv)) {
6531 nsCAutoString path;
6532 if (NS_SUCCEEDED(tempFile->GetNativePath(path)))
6533 *pTmpFileName = ToNewCString(path);
6536 return rv;
6539 NS_IMETHODIMP
6540 nsPluginHostImpl::NewPluginNativeWindow(nsPluginNativeWindow ** aPluginNativeWindow)
6542 return PLUG_NewPluginNativeWindow(aPluginNativeWindow);
6545 NS_IMETHODIMP
6546 nsPluginHostImpl::DeletePluginNativeWindow(nsPluginNativeWindow * aPluginNativeWindow)
6548 return PLUG_DeletePluginNativeWindow(aPluginNativeWindow);
6551 NS_IMETHODIMP
6552 nsPluginHostImpl::InstantiateDummyJavaPlugin(nsIPluginInstanceOwner *aOwner)
6554 // Pass PR_FALSE as the second arg, we want the answer to be the
6555 // same here whether the Java plugin is enabled or not.
6556 nsPluginTag *plugin = FindPluginForType("application/x-java-vm", PR_FALSE);
6558 if (!plugin || !plugin->mIsNPRuntimeEnabledJavaPlugin) {
6559 // No NPRuntime enabled Java plugin found, no point in
6560 // instantiating a dummy plugin then.
6562 return NS_OK;
6565 nsresult rv = SetUpPluginInstance("application/x-java-vm", nsnull, aOwner);
6566 NS_ENSURE_SUCCESS(rv, rv);
6568 nsCOMPtr<nsIPluginInstance> instance;
6569 aOwner->GetInstance(*getter_AddRefs(instance));
6571 nsCOMPtr<nsIPluginInstanceInternal> plugin_internal =
6572 do_QueryInterface(instance);
6574 if (!plugin_internal) {
6575 return NS_OK;
6578 plugin_internal->DefineJavaProperties();
6580 return NS_OK;
6583 NS_IMETHODIMP
6584 nsPluginHostImpl::GetPluginName(nsIPluginInstance *aPluginInstance,
6585 const char** aPluginName)
6587 *aPluginName = GetPluginName(aPluginInstance);
6588 return NS_OK;
6591 // end of nsPIPluginHost implementation
6593 nsresult
6594 nsPluginHostImpl::ScanForRealInComponentsFolder(nsIComponentManager * aCompManager)
6596 nsresult rv = NS_OK;
6598 #ifdef XP_WIN
6600 // First, lets check if we already have Real. No point in doing this if it's installed correctly
6601 if (NS_SUCCEEDED(IsPluginEnabledForType("audio/x-pn-realaudio-plugin")))
6602 return rv;
6604 // Next, maybe the pref wants to override
6605 PRBool bSkipRealPlayerHack = PR_FALSE;
6606 if (!mPrefService ||
6607 (NS_SUCCEEDED(mPrefService->GetBoolPref("plugin.skip_real_player_hack", &bSkipRealPlayerHack)) &&
6608 bSkipRealPlayerHack))
6609 return rv;
6611 // now we need the XPCOM components folder
6612 nsCOMPtr<nsIFile> RealPlugin;
6613 if (NS_FAILED(NS_GetSpecialDirectory(NS_XPCOM_COMPONENT_DIR, getter_AddRefs(RealPlugin))) || !RealPlugin)
6614 return rv;
6616 // make sure the file is actually there
6617 RealPlugin->AppendNative(nsDependentCString("nppl3260.dll"));
6618 PRBool exists;
6619 nsCAutoString filePath;
6620 RealPlugin->Exists(&exists);
6621 if (!exists || NS_FAILED(RealPlugin->GetNativePath(filePath)))
6622 return rv;
6624 // now make sure it's a plugin
6625 nsCOMPtr<nsILocalFile> localfile;
6626 NS_NewNativeLocalFile(filePath,
6627 PR_TRUE,
6628 getter_AddRefs(localfile));
6630 if (!nsPluginsDir::IsPluginFile(localfile))
6631 return rv;
6633 // try to get the mime info and descriptions out of the plugin
6634 nsPluginFile pluginFile(localfile);
6635 nsPluginInfo info;
6636 memset(&info, 0, sizeof(info));
6637 if (NS_FAILED(pluginFile.GetPluginInfo(info)))
6638 return rv;
6640 nsCOMPtr<nsIComponentManager> compManager;
6641 NS_GetComponentManager(getter_AddRefs(compManager));
6643 // finally, create our "plugin tag" and add it to the list
6644 if (info.fMimeTypeArray) {
6645 nsRefPtr<nsPluginTag> pluginTag = new nsPluginTag(&info);
6646 if (pluginTag) {
6647 pluginTag->SetHost(this);
6648 pluginTag->mNext = mPlugins;
6649 mPlugins = pluginTag;
6651 // last thing we need is to register this plugin with layout so it can be used in full-page mode
6652 if (pluginTag->IsEnabled())
6653 pluginTag->RegisterWithCategoryManager(mOverrideInternalTypes);
6657 // free allocated strings in GetPluginInfo
6658 pluginFile.FreePluginInfo(info);
6660 #endif
6662 return rv;
6665 nsresult nsPluginHostImpl::AddUnusedLibrary(PRLibrary * aLibrary)
6667 if (mUnusedLibraries.IndexOf(aLibrary) == -1) // don't add duplicates
6668 mUnusedLibraries.AppendElement(aLibrary);
6670 return NS_OK;
6673 nsresult nsPluginHostImpl::AddPrefObserver()
6675 nsCOMPtr<nsIPrefBranch2> prefBranch(do_QueryInterface(mPrefService));
6676 NS_ENSURE_TRUE(prefBranch, NS_ERROR_UNEXPECTED);
6678 return prefBranch->AddObserver("security.enable_java", this, PR_TRUE);
6681 nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
6682 nsISupports* aContext)
6684 if (!mInstance)
6685 return NS_ERROR_FAILURE;
6687 // mInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
6688 mInstance->Stop();
6689 mInstance->Start();
6690 nsCOMPtr<nsIPluginInstancePeer> peer;
6691 mInstance->GetPeer(getter_AddRefs(peer));
6692 if (peer) {
6693 nsCOMPtr<nsPIPluginInstancePeer> privpeer(do_QueryInterface(peer));
6694 nsCOMPtr<nsIPluginInstanceOwner> owner;
6695 privpeer->GetOwner(getter_AddRefs(owner));
6696 if (owner) {
6697 nsPluginWindow *window = nsnull;
6698 owner->GetWindow(window);
6699 #if defined (MOZ_WIDGET_GTK2)
6700 // Should call GetPluginPort() here.
6701 // This part is copied from nsPluginInstanceOwner::GetPluginPort().
6702 nsCOMPtr<nsIWidget> widget;
6703 ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
6704 if (widget) {
6705 window->window = (nsPluginPort*) widget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
6707 #endif
6708 if (window->window)
6710 nsCOMPtr<nsIPluginInstance> inst = mInstance;
6711 ((nsPluginNativeWindow*)window)->CallSetWindow(inst);
6716 mPluginStreamInfo->SetSeekable(0);
6717 mPStreamListener->OnStartBinding(mPluginStreamInfo);
6718 mPluginStreamInfo->SetStreamOffset(0);
6720 // force the plugin use stream as file
6721 mStreamType = nsPluginStreamType_AsFile;
6723 // then check it out if browser cache is not available
6724 nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
6725 if (!(cacheChannel && (NS_SUCCEEDED(cacheChannel->SetCacheAsFile(PR_TRUE))))) {
6726 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
6727 if (channel) {
6728 SetupPluginCacheFile(channel);
6732 // unset mPendingRequests
6733 mPendingRequests = 0;
6735 return NS_OK;
6738 NS_IMPL_ISUPPORTS1(nsPluginByteRangeStreamListener, nsIStreamListener)
6739 nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
6741 mWeakPtrPluginStreamListenerPeer = aWeakPtr;
6742 mRemoveMagicNumber = PR_FALSE;
6745 nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
6747 mStreamConverter = 0;
6748 mWeakPtrPluginStreamListenerPeer = 0;
6751 NS_IMETHODIMP
6752 nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
6754 nsresult rv;
6756 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
6757 if (!finalStreamListener)
6758 return NS_ERROR_FAILURE;
6760 nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
6761 if (NS_SUCCEEDED(rv)) {
6762 rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
6763 "*/*",
6764 finalStreamListener,
6765 nsnull,
6766 getter_AddRefs(mStreamConverter));
6767 if (NS_SUCCEEDED(rv)) {
6768 rv = mStreamConverter->OnStartRequest(request, ctxt);
6769 if (NS_SUCCEEDED(rv))
6770 return rv;
6773 mStreamConverter = 0;
6775 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
6776 if (!httpChannel) {
6777 return NS_ERROR_FAILURE;
6780 PRUint32 responseCode = 0;
6781 rv = httpChannel->GetResponseStatus(&responseCode);
6782 if (NS_FAILED(rv)) {
6783 return NS_ERROR_FAILURE;
6786 // get nsPluginStreamListenerPeer* ptr from finalStreamListener
6787 nsPluginStreamListenerPeer *pslp =
6788 reinterpret_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
6790 if (responseCode != 200) {
6791 PRBool bWantsAllNetworkStreams = PR_FALSE;
6792 pslp->GetPluginInstance()->
6793 GetValue(nsPluginInstanceVariable_WantsAllNetworkStreams,
6794 (void *)&bWantsAllNetworkStreams);
6795 if (!bWantsAllNetworkStreams){
6796 return NS_ERROR_FAILURE;
6800 // if server cannot continue with byte range (206 status) and sending us whole object (200 status)
6801 // reset this seekable stream & try serve it to plugin instance as a file
6802 mStreamConverter = finalStreamListener;
6803 mRemoveMagicNumber = PR_TRUE;
6805 rv = pslp->ServeStreamAsFile(request, ctxt);
6806 return rv;
6809 NS_IMETHODIMP
6810 nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
6811 nsresult status)
6813 if (!mStreamConverter)
6814 return NS_ERROR_FAILURE;
6816 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
6817 if (!finalStreamListener)
6818 return NS_ERROR_FAILURE;
6820 if (mRemoveMagicNumber) {
6821 // remove magic number from container
6822 nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt);
6823 if (container) {
6824 PRUint32 magicNumber = 0;
6825 container->GetData(&magicNumber);
6826 if (magicNumber == MAGIC_REQUEST_CONTEXT) {
6827 // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest()
6828 // set it to something that is not the magic number.
6829 container->SetData(0);
6831 } else {
6832 NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
6836 return mStreamConverter->OnStopRequest(request, ctxt, status);
6839 NS_IMETHODIMP
6840 nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
6841 nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
6843 if (!mStreamConverter)
6844 return NS_ERROR_FAILURE;
6846 nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
6847 if (!finalStreamListener)
6848 return NS_ERROR_FAILURE;
6850 return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
6853 PRBool
6854 nsPluginStreamInfo::UseExistingPluginCacheFile(nsPluginStreamInfo* psi)
6857 NS_ENSURE_ARG_POINTER(psi);
6859 if ( psi->mLength == mLength &&
6860 psi->mModified == mModified &&
6861 mStreamComplete &&
6862 !PL_strcmp(psi->mURL, mURL))
6864 return PR_TRUE;
6866 return PR_FALSE;
6869 void
6870 nsPluginStreamInfo::SetStreamComplete(const PRBool complete)
6872 mStreamComplete = complete;
6874 if (complete) {
6875 // We're done, release the request.
6876 SetRequest(nsnull);
6880 // Runnable that does an async destroy of a plugin.
6882 class nsPluginDestroyRunnable : public nsRunnable,
6883 public PRCList
6885 public:
6886 nsPluginDestroyRunnable(nsIPluginInstance *aInstance)
6887 : mInstance(aInstance)
6889 PR_INIT_CLIST(this);
6890 PR_APPEND_LINK(this, &sRunnableListHead);
6893 virtual ~nsPluginDestroyRunnable()
6895 PR_REMOVE_LINK(this);
6898 NS_IMETHOD Run()
6900 nsCOMPtr<nsIPluginInstance> instance;
6902 // Null out mInstance to make sure this code in another runnable
6903 // will do the right thing even if someone was holding on to this
6904 // runnable longer than we expect.
6905 instance.swap(mInstance);
6907 if (PluginDestructionGuard::DelayDestroy(instance)) {
6908 // It's still not safe to destroy the plugin, it's now up to the
6909 // outermost guard on the stack to take care of the destruction.
6910 return NS_OK;
6913 nsPluginDestroyRunnable *r =
6914 static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(&sRunnableListHead));
6916 while (r != &sRunnableListHead) {
6917 if (r != this && r->mInstance == instance) {
6918 // There's another runnable scheduled to tear down
6919 // instance. Let it do the job.
6920 return NS_OK;
6922 r = static_cast<nsPluginDestroyRunnable*>(PR_NEXT_LINK(r));
6925 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
6926 ("Doing delayed destroy of instance %p\n", instance.get()));
6928 instance->Stop();
6930 nsRefPtr<nsPluginHostImpl> host = nsPluginHostImpl::GetInst();
6932 if (host)
6933 host->StopPluginInstance(instance);
6935 PLUGIN_LOG(PLUGIN_LOG_NORMAL,
6936 ("Done with delayed destroy of instance %p\n", instance.get()));
6938 return NS_OK;
6941 protected:
6942 nsCOMPtr<nsIPluginInstance> mInstance;
6944 static PRCList sRunnableListHead;
6947 PRCList nsPluginDestroyRunnable::sRunnableListHead =
6948 PR_INIT_STATIC_CLIST(&nsPluginDestroyRunnable::sRunnableListHead);
6950 PRCList PluginDestructionGuard::sListHead =
6951 PR_INIT_STATIC_CLIST(&PluginDestructionGuard::sListHead);
6953 PluginDestructionGuard::~PluginDestructionGuard()
6955 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
6957 PR_REMOVE_LINK(this);
6959 if (mDelayedDestroy) {
6960 // We've attempted to destroy the plugin instance we're holding on
6961 // to while we were guarding it. Do the actual destroy now, off of
6962 // a runnable.
6963 nsRefPtr<nsPluginDestroyRunnable> evt =
6964 new nsPluginDestroyRunnable(mInstance);
6966 NS_DispatchToMainThread(evt);
6970 // static
6971 PRBool
6972 PluginDestructionGuard::DelayDestroy(nsIPluginInstance *aInstance)
6974 NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread");
6975 NS_ASSERTION(aInstance, "Uh, I need an instance!");
6977 // Find the first guard on the stack and make it do a delayed
6978 // destroy upon destruction.
6980 PluginDestructionGuard *g =
6981 static_cast<PluginDestructionGuard*>(PR_LIST_HEAD(&sListHead));
6983 while (g != &sListHead) {
6984 if (g->mInstance == aInstance) {
6985 g->mDelayedDestroy = PR_TRUE;
6987 return PR_TRUE;
6989 g = static_cast<PluginDestructionGuard*>(PR_NEXT_LINK(g));
6992 return PR_FALSE;