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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Sean Echevarria <sean@beatnik.com>
24 : * HÃ¥kan Waara <hwaara@chello.se>
25 : * Josh Aas <josh@mozilla.com>
26 : *
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.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsPluginTags.h"
42 :
43 : #include "prlink.h"
44 : #include "plstr.h"
45 : #include "nsIPluginInstanceOwner.h"
46 : #include "nsIDocument.h"
47 : #include "nsServiceManagerUtils.h"
48 : #include "nsIPrefService.h"
49 : #include "nsIPrefBranch.h"
50 : #include "nsPluginsDir.h"
51 : #include "nsPluginHost.h"
52 : #include "nsIUnicodeDecoder.h"
53 : #include "nsIPlatformCharset.h"
54 : #include "nsICharsetConverterManager.h"
55 : #include "nsPluginLogging.h"
56 : #include "nsICategoryManager.h"
57 : #include "nsNPAPIPlugin.h"
58 : #include "mozilla/TimeStamp.h"
59 :
60 : using mozilla::TimeStamp;
61 :
62 : inline char* new_str(const char* str)
63 : {
64 : if (str == nsnull)
65 : return nsnull;
66 :
67 : char* result = new char[strlen(str) + 1];
68 : if (result != nsnull)
69 : return strcpy(result, str);
70 : return result;
71 : }
72 :
73 : /* nsPluginTag */
74 :
75 0 : nsPluginTag::nsPluginTag(nsPluginTag* aPluginTag)
76 : : mPluginHost(nsnull),
77 : mName(aPluginTag->mName),
78 : mDescription(aPluginTag->mDescription),
79 : mMimeTypes(aPluginTag->mMimeTypes),
80 : mMimeDescriptions(aPluginTag->mMimeDescriptions),
81 : mExtensions(aPluginTag->mExtensions),
82 : mLibrary(nsnull),
83 : mIsJavaPlugin(aPluginTag->mIsJavaPlugin),
84 : mIsNPRuntimeEnabledJavaPlugin(aPluginTag->mIsNPRuntimeEnabledJavaPlugin),
85 : mIsFlashPlugin(aPluginTag->mIsFlashPlugin),
86 : mFileName(aPluginTag->mFileName),
87 : mFullPath(aPluginTag->mFullPath),
88 : mVersion(aPluginTag->mVersion),
89 : mLastModifiedTime(0),
90 0 : mFlags(NS_PLUGIN_FLAG_ENABLED)
91 : {
92 0 : }
93 :
94 173 : nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo)
95 : : mPluginHost(nsnull),
96 : mName(aPluginInfo->fName),
97 : mDescription(aPluginInfo->fDescription),
98 : mLibrary(nsnull),
99 : mIsJavaPlugin(false),
100 : mIsNPRuntimeEnabledJavaPlugin(false),
101 : mIsFlashPlugin(false),
102 : mFileName(aPluginInfo->fFileName),
103 : mFullPath(aPluginInfo->fFullPath),
104 : mVersion(aPluginInfo->fVersion),
105 : mLastModifiedTime(0),
106 173 : mFlags(NS_PLUGIN_FLAG_ENABLED)
107 : {
108 : InitMime(aPluginInfo->fMimeTypeArray,
109 : aPluginInfo->fMimeDescriptionArray,
110 : aPluginInfo->fExtensionArray,
111 173 : aPluginInfo->fVariantCount);
112 173 : EnsureMembersAreUTF8();
113 173 : }
114 :
115 5 : nsPluginTag::nsPluginTag(const char* aName,
116 : const char* aDescription,
117 : const char* aFileName,
118 : const char* aFullPath,
119 : const char* aVersion,
120 : const char* const* aMimeTypes,
121 : const char* const* aMimeDescriptions,
122 : const char* const* aExtensions,
123 : PRInt32 aVariants,
124 : PRInt64 aLastModifiedTime,
125 : bool aArgsAreUTF8)
126 : : mPluginHost(nsnull),
127 : mName(aName),
128 : mDescription(aDescription),
129 : mLibrary(nsnull),
130 : mIsJavaPlugin(false),
131 : mIsNPRuntimeEnabledJavaPlugin(false),
132 : mIsFlashPlugin(false),
133 : mFileName(aFileName),
134 : mFullPath(aFullPath),
135 : mVersion(aVersion),
136 : mLastModifiedTime(aLastModifiedTime),
137 5 : mFlags(0) // Caller will read in our flags from cache
138 : {
139 5 : InitMime(aMimeTypes, aMimeDescriptions, aExtensions, static_cast<PRUint32>(aVariants));
140 5 : if (!aArgsAreUTF8)
141 0 : EnsureMembersAreUTF8();
142 5 : }
143 :
144 534 : nsPluginTag::~nsPluginTag()
145 : {
146 178 : NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
147 712 : }
148 :
149 13405 : NS_IMPL_ISUPPORTS1(nsPluginTag, nsIPluginTag)
150 :
151 178 : void nsPluginTag::InitMime(const char* const* aMimeTypes,
152 : const char* const* aMimeDescriptions,
153 : const char* const* aExtensions,
154 : PRUint32 aVariantCount)
155 : {
156 178 : if (!aMimeTypes) {
157 0 : return;
158 : }
159 :
160 356 : for (PRUint32 i = 0; i < aVariantCount; i++) {
161 178 : if (!aMimeTypes[i]) {
162 0 : continue;
163 : }
164 :
165 : // If we already marked this as a Java plugin, a later MIME type will tell
166 : // us if it is npruntime-enabled.
167 178 : if (mIsJavaPlugin) {
168 0 : if (strcmp(aMimeTypes[i], "application/x-java-vm-npruntime") == 0) {
169 : // This "magic MIME type" should not be exposed, but is just a signal
170 : // to the browser that this is new-style java.
171 : // Don't add it or its associated information to our arrays.
172 0 : mIsNPRuntimeEnabledJavaPlugin = true;
173 0 : continue;
174 : }
175 : }
176 :
177 : // Look for certain special plugins.
178 178 : if (nsPluginHost::IsJavaMIMEType(aMimeTypes[i])) {
179 0 : mIsJavaPlugin = true;
180 178 : } else if (strcmp(aMimeTypes[i], "application/x-shockwave-flash") == 0) {
181 0 : mIsFlashPlugin = true;
182 : }
183 :
184 : // Fill in our MIME type array.
185 178 : mMimeTypes.AppendElement(nsCString(aMimeTypes[i]));
186 :
187 : // Now fill in the MIME descriptions.
188 178 : if (aMimeDescriptions && aMimeDescriptions[i]) {
189 : // we should cut off the list of suffixes which the mime
190 : // description string may have, see bug 53895
191 : // it is usually in form "some description (*.sf1, *.sf2)"
192 : // so we can search for the opening round bracket
193 178 : char cur = '\0';
194 178 : char pre = '\0';
195 178 : char * p = PL_strrchr(aMimeDescriptions[i], '(');
196 178 : if (p && (p != aMimeDescriptions[i])) {
197 0 : if ((p - 1) && *(p - 1) == ' ') {
198 0 : pre = *(p - 1);
199 0 : *(p - 1) = '\0';
200 : } else {
201 0 : cur = *p;
202 0 : *p = '\0';
203 : }
204 : }
205 178 : mMimeDescriptions.AppendElement(nsCString(aMimeDescriptions[i]));
206 : // restore the original string
207 178 : if (cur != '\0') {
208 0 : *p = cur;
209 : }
210 178 : if (pre != '\0') {
211 0 : *(p - 1) = pre;
212 178 : }
213 : } else {
214 0 : mMimeDescriptions.AppendElement(nsCString());
215 : }
216 :
217 : // Now fill in the extensions.
218 178 : if (aExtensions && aExtensions[i]) {
219 178 : mExtensions.AppendElement(nsCString(aExtensions[i]));
220 : } else {
221 0 : mExtensions.AppendElement(nsCString());
222 : }
223 : }
224 : }
225 :
226 : #if !defined(XP_WIN) && !defined(XP_MACOSX)
227 0 : static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
228 : nsAFlatCString& aString)
229 : {
230 0 : PRInt32 numberOfBytes = aString.Length();
231 : PRInt32 outUnicodeLen;
232 0 : nsAutoString buffer;
233 : nsresult rv = aUnicodeDecoder->GetMaxLength(aString.get(), numberOfBytes,
234 0 : &outUnicodeLen);
235 0 : NS_ENSURE_SUCCESS(rv, rv);
236 0 : if (!EnsureStringLength(buffer, outUnicodeLen))
237 0 : return NS_ERROR_OUT_OF_MEMORY;
238 : rv = aUnicodeDecoder->Convert(aString.get(), &numberOfBytes,
239 0 : buffer.BeginWriting(), &outUnicodeLen);
240 0 : NS_ENSURE_SUCCESS(rv, rv);
241 0 : buffer.SetLength(outUnicodeLen);
242 0 : CopyUTF16toUTF8(buffer, aString);
243 :
244 0 : return NS_OK;
245 : }
246 : #endif
247 :
248 173 : nsresult nsPluginTag::EnsureMembersAreUTF8()
249 : {
250 : #if defined(XP_WIN) || defined(XP_MACOSX)
251 : return NS_OK;
252 : #else
253 : nsresult rv;
254 :
255 : nsCOMPtr<nsIPlatformCharset> pcs =
256 346 : do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
257 173 : NS_ENSURE_SUCCESS(rv, rv);
258 346 : nsCOMPtr<nsIUnicodeDecoder> decoder;
259 : nsCOMPtr<nsICharsetConverterManager> ccm =
260 346 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
261 173 : NS_ENSURE_SUCCESS(rv, rv);
262 :
263 346 : nsCAutoString charset;
264 173 : rv = pcs->GetCharset(kPlatformCharsetSel_FileName, charset);
265 173 : NS_ENSURE_SUCCESS(rv, rv);
266 173 : if (!charset.LowerCaseEqualsLiteral("utf-8")) {
267 0 : rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
268 0 : NS_ENSURE_SUCCESS(rv, rv);
269 :
270 0 : ConvertToUTF8(decoder, mFileName);
271 0 : ConvertToUTF8(decoder, mFullPath);
272 : }
273 :
274 : // The description of the plug-in and the various MIME type descriptions
275 : // should be encoded in the standard plain text file encoding for this system.
276 : // XXX should we add kPlatformCharsetSel_PluginResource?
277 173 : rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
278 173 : NS_ENSURE_SUCCESS(rv, rv);
279 173 : if (!charset.LowerCaseEqualsLiteral("utf-8")) {
280 0 : rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
281 0 : NS_ENSURE_SUCCESS(rv, rv);
282 :
283 0 : ConvertToUTF8(decoder, mName);
284 0 : ConvertToUTF8(decoder, mDescription);
285 0 : for (PRUint32 i = 0; i < mMimeDescriptions.Length(); ++i) {
286 0 : ConvertToUTF8(decoder, mMimeDescriptions[i]);
287 : }
288 : }
289 173 : return NS_OK;
290 : #endif
291 : }
292 :
293 173 : void nsPluginTag::SetHost(nsPluginHost * aHost)
294 : {
295 173 : mPluginHost = aHost;
296 173 : }
297 :
298 : NS_IMETHODIMP
299 2925 : nsPluginTag::GetDescription(nsACString& aDescription)
300 : {
301 2925 : aDescription = mDescription;
302 2925 : return NS_OK;
303 : }
304 :
305 : NS_IMETHODIMP
306 4 : nsPluginTag::GetFilename(nsACString& aFileName)
307 : {
308 4 : aFileName = mFileName;
309 4 : return NS_OK;
310 : }
311 :
312 : NS_IMETHODIMP
313 15 : nsPluginTag::GetFullpath(nsACString& aFullPath)
314 : {
315 15 : aFullPath = mFullPath;
316 15 : return NS_OK;
317 : }
318 :
319 : NS_IMETHODIMP
320 7 : nsPluginTag::GetVersion(nsACString& aVersion)
321 : {
322 7 : aVersion = mVersion;
323 7 : return NS_OK;
324 : }
325 :
326 : NS_IMETHODIMP
327 4430 : nsPluginTag::GetName(nsACString& aName)
328 : {
329 4430 : aName = mName;
330 4430 : return NS_OK;
331 : }
332 :
333 : NS_IMETHODIMP
334 44 : nsPluginTag::GetDisabled(bool* aDisabled)
335 : {
336 44 : *aDisabled = !HasFlag(NS_PLUGIN_FLAG_ENABLED);
337 44 : return NS_OK;
338 : }
339 :
340 : NS_IMETHODIMP
341 4 : nsPluginTag::SetDisabled(bool aDisabled)
342 : {
343 4 : if (HasFlag(NS_PLUGIN_FLAG_ENABLED) == !aDisabled)
344 0 : return NS_OK;
345 :
346 4 : if (aDisabled)
347 2 : UnMark(NS_PLUGIN_FLAG_ENABLED);
348 : else
349 2 : Mark(NS_PLUGIN_FLAG_ENABLED);
350 :
351 4 : mPluginHost->UpdatePluginInfo(this);
352 4 : return NS_OK;
353 : }
354 :
355 : NS_IMETHODIMP
356 35 : nsPluginTag::GetBlocklisted(bool* aBlocklisted)
357 : {
358 35 : *aBlocklisted = HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED);
359 35 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 4 : nsPluginTag::SetBlocklisted(bool aBlocklisted)
364 : {
365 4 : if (HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED) == aBlocklisted)
366 4 : return NS_OK;
367 :
368 0 : if (aBlocklisted)
369 0 : Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
370 : else
371 0 : UnMark(NS_PLUGIN_FLAG_BLOCKLISTED);
372 :
373 0 : mPluginHost->UpdatePluginInfo(nsnull);
374 0 : return NS_OK;
375 : }
376 :
377 : void
378 176 : nsPluginTag::RegisterWithCategoryManager(bool aOverrideInternalTypes,
379 : nsPluginTag::nsRegisterType aType)
380 : {
381 176 : PLUGIN_LOG(PLUGIN_LOG_NORMAL,
382 : ("nsPluginTag::RegisterWithCategoryManager plugin=%s, removing = %s\n",
383 : mFileName.get(), aType == ePluginUnregister ? "yes" : "no"));
384 :
385 352 : nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
386 176 : if (!catMan)
387 : return;
388 :
389 176 : const char *contractId = "@mozilla.org/content/plugin/document-loader-factory;1";
390 :
391 352 : nsCOMPtr<nsIPrefBranch> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
392 176 : if (!psvc)
393 : return; // NS_ERROR_OUT_OF_MEMORY
394 :
395 : // A preference controls whether or not the full page plugin is disabled for
396 : // a particular type. The string must be in the form:
397 : // type1,type2,type3,type4
398 : // Note: need an actual interface to control this and subsequent disabling
399 : // (and other plugin host settings) so applications can reliably disable
400 : // plugins - without relying on implementation details such as prefs/category
401 : // manager entries.
402 528 : nsXPIDLCString overrideTypes;
403 352 : nsCAutoString overrideTypesFormatted;
404 176 : if (aType != ePluginUnregister) {
405 174 : psvc->GetCharPref("plugin.disable_full_page_plugin_for_types", getter_Copies(overrideTypes));
406 174 : overrideTypesFormatted.Assign(',');
407 174 : overrideTypesFormatted += overrideTypes;
408 174 : overrideTypesFormatted.Append(',');
409 : }
410 :
411 176 : nsACString::const_iterator start, end;
412 352 : for (PRUint32 i = 0; i < mMimeTypes.Length(); i++) {
413 176 : if (aType == ePluginUnregister) {
414 4 : nsXPIDLCString value;
415 2 : if (NS_SUCCEEDED(catMan->GetCategoryEntry("Gecko-Content-Viewers",
416 : mMimeTypes[i].get(),
417 : getter_Copies(value)))) {
418 : // Only delete the entry if a plugin registered for it
419 2 : if (strcmp(value, contractId) == 0) {
420 2 : catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
421 2 : mMimeTypes[i].get(),
422 4 : true);
423 : }
424 : }
425 : } else {
426 174 : overrideTypesFormatted.BeginReading(start);
427 174 : overrideTypesFormatted.EndReading(end);
428 :
429 348 : nsCAutoString commaSeparated;
430 174 : commaSeparated.Assign(',');
431 174 : commaSeparated += mMimeTypes[i];
432 174 : commaSeparated.Append(',');
433 174 : if (!FindInReadable(commaSeparated, start, end)) {
434 174 : catMan->AddCategoryEntry("Gecko-Content-Viewers",
435 174 : mMimeTypes[i].get(),
436 : contractId,
437 : false, /* persist: broken by bug 193031 */
438 : aOverrideInternalTypes, /* replace if we're told to */
439 348 : nsnull);
440 : }
441 : }
442 :
443 176 : PLUGIN_LOG(PLUGIN_LOG_NOISY,
444 : ("nsPluginTag::RegisterWithCategoryManager mime=%s, plugin=%s\n",
445 : mMimeTypes[i].get(), mFileName.get()));
446 : }
447 : }
448 :
449 7 : void nsPluginTag::Mark(PRUint32 mask)
450 : {
451 7 : bool wasEnabled = IsEnabled();
452 7 : mFlags |= mask;
453 : // Update entries in the category manager if necessary.
454 7 : if (mPluginHost && wasEnabled != IsEnabled()) {
455 2 : if (wasEnabled)
456 0 : RegisterWithCategoryManager(false, nsPluginTag::ePluginUnregister);
457 : else
458 2 : RegisterWithCategoryManager(false, nsPluginTag::ePluginRegister);
459 : }
460 7 : }
461 :
462 3 : void nsPluginTag::UnMark(PRUint32 mask)
463 : {
464 3 : bool wasEnabled = IsEnabled();
465 3 : mFlags &= ~mask;
466 : // Update entries in the category manager if necessary.
467 3 : if (mPluginHost && wasEnabled != IsEnabled()) {
468 2 : if (wasEnabled)
469 2 : RegisterWithCategoryManager(false, nsPluginTag::ePluginUnregister);
470 : else
471 0 : RegisterWithCategoryManager(false, nsPluginTag::ePluginRegister);
472 : }
473 3 : }
474 :
475 720 : bool nsPluginTag::HasFlag(PRUint32 flag)
476 : {
477 720 : return (mFlags & flag) != 0;
478 : }
479 :
480 163 : PRUint32 nsPluginTag::Flags()
481 : {
482 163 : return mFlags;
483 : }
484 :
485 234 : bool nsPluginTag::IsEnabled()
486 : {
487 234 : return HasFlag(NS_PLUGIN_FLAG_ENABLED) && !HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED);
488 : }
489 :
490 0 : bool nsPluginTag::Equals(nsPluginTag *aPluginTag)
491 : {
492 0 : NS_ENSURE_TRUE(aPluginTag, false);
493 :
494 0 : if ((!mName.Equals(aPluginTag->mName)) ||
495 0 : (!mDescription.Equals(aPluginTag->mDescription)) ||
496 0 : (mMimeTypes.Length() != aPluginTag->mMimeTypes.Length())) {
497 0 : return false;
498 : }
499 :
500 0 : for (PRUint32 i = 0; i < mMimeTypes.Length(); i++) {
501 0 : if (!mMimeTypes[i].Equals(aPluginTag->mMimeTypes[i])) {
502 0 : return false;
503 : }
504 : }
505 :
506 0 : return true;
507 : }
508 :
509 173 : void nsPluginTag::TryUnloadPlugin(bool inShutdown)
510 : {
511 : // We never want to send NPP_Shutdown to an in-process plugin unless
512 : // this process is shutting down.
513 173 : if (mLibrary && !inShutdown) {
514 0 : return;
515 : }
516 :
517 173 : if (mEntryPoint) {
518 0 : mEntryPoint->Shutdown();
519 0 : mEntryPoint = nsnull;
520 : }
521 : }
|