1 /* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora, Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "PluginDatabase.h" 29 30 #include "Frame.h" 31 #include "KURL.h" 32 #include "PluginPackage.h" 33 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 34 #include "FileSystem.h" 35 #endif 36 #include <stdlib.h> 37 #include <wtf/text/CString.h> 38 39 #if PLATFORM(ANDROID) 40 #include "JavaSharedClient.h" 41 #include "PluginClient.h" 42 #endif 43 44 namespace WebCore { 45 46 typedef HashMap<String, RefPtr<PluginPackage> > PluginPackageByNameMap; 47 48 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 49 static const size_t maximumPersistentPluginMetadataCacheSize = 32768; 50 51 static bool gPersistentPluginMetadataCacheIsEnabled; 52 53 String& persistentPluginMetadataCachePath() 54 { 55 DEFINE_STATIC_LOCAL(String, cachePath, ()); 56 return cachePath; 57 } 58 #endif 59 60 PluginDatabase::PluginDatabase() 61 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 62 : m_persistentMetadataCacheIsLoaded(false) 63 #endif 64 { 65 } 66 67 PluginDatabase* PluginDatabase::installedPlugins(bool populate) 68 { 69 static PluginDatabase* plugins = 0; 70 71 if (!plugins) { 72 plugins = new PluginDatabase; 73 74 if (populate) { 75 plugins->setPluginDirectories(PluginDatabase::defaultPluginDirectories()); 76 plugins->refresh(); 77 } 78 } 79 80 return plugins; 81 } 82 83 bool PluginDatabase::isMIMETypeRegistered(const String& mimeType) 84 { 85 if (mimeType.isNull()) 86 return false; 87 if (m_registeredMIMETypes.contains(mimeType)) 88 return true; 89 // No plugin was found, try refreshing the database and searching again 90 return (refresh() && m_registeredMIMETypes.contains(mimeType)); 91 } 92 93 void PluginDatabase::addExtraPluginDirectory(const String& directory) 94 { 95 m_pluginDirectories.append(directory); 96 refresh(); 97 } 98 99 bool PluginDatabase::refresh() 100 { 101 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 102 if (!m_persistentMetadataCacheIsLoaded) 103 loadPersistentMetadataCache(); 104 #endif 105 bool pluginSetChanged = false; 106 107 if (!m_plugins.isEmpty()) { 108 PluginSet pluginsToUnload; 109 getDeletedPlugins(pluginsToUnload); 110 111 // Unload plugins 112 PluginSet::const_iterator end = pluginsToUnload.end(); 113 for (PluginSet::const_iterator it = pluginsToUnload.begin(); it != end; ++it) 114 remove(it->get()); 115 116 pluginSetChanged = !pluginsToUnload.isEmpty(); 117 } 118 119 HashSet<String> paths; 120 getPluginPathsInDirectories(paths); 121 122 HashMap<String, time_t> pathsWithTimes; 123 124 // We should only skip unchanged files if we didn't remove any plugins above. If we did remove 125 // any plugins, we need to look at every plugin file so that, e.g., if the user has two versions 126 // of RealPlayer installed and just removed the newer one, we'll pick up the older one. 127 bool shouldSkipUnchangedFiles = !pluginSetChanged; 128 129 HashSet<String>::const_iterator pathsEnd = paths.end(); 130 for (HashSet<String>::const_iterator it = paths.begin(); it != pathsEnd; ++it) { 131 time_t lastModified; 132 if (!getFileModificationTime(*it, lastModified)) 133 continue; 134 135 pathsWithTimes.add(*it, lastModified); 136 137 // If the path's timestamp hasn't changed since the last time we ran refresh(), we don't have to do anything. 138 if (shouldSkipUnchangedFiles && m_pluginPathsWithTimes.get(*it) == lastModified) 139 continue; 140 141 if (RefPtr<PluginPackage> oldPackage = m_pluginsByPath.get(*it)) { 142 ASSERT(!shouldSkipUnchangedFiles || oldPackage->lastModified() != lastModified); 143 remove(oldPackage.get()); 144 } 145 146 RefPtr<PluginPackage> package = PluginPackage::createPackage(*it, lastModified); 147 if (package && add(package.release())) 148 pluginSetChanged = true; 149 } 150 151 // Cache all the paths we found with their timestamps for next time. 152 pathsWithTimes.swap(m_pluginPathsWithTimes); 153 154 if (!pluginSetChanged) 155 return false; 156 157 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 158 updatePersistentMetadataCache(); 159 #endif 160 161 m_registeredMIMETypes.clear(); 162 163 // Register plug-in MIME types 164 PluginSet::const_iterator end = m_plugins.end(); 165 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { 166 // Get MIME types 167 MIMEToDescriptionsMap::const_iterator map_it = (*it)->mimeToDescriptions().begin(); 168 MIMEToDescriptionsMap::const_iterator map_end = (*it)->mimeToDescriptions().end(); 169 for (; map_it != map_end; ++map_it) 170 m_registeredMIMETypes.add(map_it->first); 171 } 172 173 return true; 174 } 175 176 Vector<PluginPackage*> PluginDatabase::plugins() const 177 { 178 Vector<PluginPackage*> result; 179 180 PluginSet::const_iterator end = m_plugins.end(); 181 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) 182 result.append((*it).get()); 183 184 return result; 185 } 186 187 int PluginDatabase::preferredPluginCompare(const void* a, const void* b) 188 { 189 PluginPackage* pluginA = *static_cast<PluginPackage* const*>(a); 190 PluginPackage* pluginB = *static_cast<PluginPackage* const*>(b); 191 192 return pluginA->compare(*pluginB); 193 } 194 195 PluginPackage* PluginDatabase::pluginForMIMEType(const String& mimeType) 196 { 197 if (mimeType.isEmpty()) 198 return 0; 199 200 String key = mimeType.lower(); 201 PluginSet::const_iterator end = m_plugins.end(); 202 PluginPackage* preferredPlugin = m_preferredPlugins.get(key).get(); 203 if (preferredPlugin 204 && preferredPlugin->isEnabled() 205 && preferredPlugin->mimeToDescriptions().contains(key)) { 206 return preferredPlugin; 207 } 208 209 Vector<PluginPackage*, 2> pluginChoices; 210 211 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { 212 PluginPackage* plugin = (*it).get(); 213 214 if (!plugin->isEnabled()) 215 continue; 216 217 if (plugin->mimeToDescriptions().contains(key)) { 218 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 219 if (!plugin->ensurePluginLoaded()) 220 continue; 221 #endif 222 pluginChoices.append(plugin); 223 } 224 } 225 226 if (pluginChoices.isEmpty()) 227 return 0; 228 229 qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); 230 231 return pluginChoices[0]; 232 } 233 234 String PluginDatabase::MIMETypeForExtension(const String& extension) const 235 { 236 if (extension.isEmpty()) 237 return String(); 238 239 PluginSet::const_iterator end = m_plugins.end(); 240 String mimeType; 241 Vector<PluginPackage*, 2> pluginChoices; 242 HashMap<PluginPackage*, String> mimeTypeForPlugin; 243 244 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { 245 if (!(*it)->isEnabled()) 246 continue; 247 248 MIMEToExtensionsMap::const_iterator mime_end = (*it)->mimeToExtensions().end(); 249 250 for (MIMEToExtensionsMap::const_iterator mime_it = (*it)->mimeToExtensions().begin(); mime_it != mime_end; ++mime_it) { 251 mimeType = mime_it->first; 252 PluginPackage* preferredPlugin = m_preferredPlugins.get(mimeType).get(); 253 const Vector<String>& extensions = mime_it->second; 254 bool foundMapping = false; 255 for (unsigned i = 0; i < extensions.size(); i++) { 256 if (equalIgnoringCase(extensions[i], extension)) { 257 PluginPackage* plugin = (*it).get(); 258 259 if (preferredPlugin && PluginPackage::equal(*plugin, *preferredPlugin)) 260 return mimeType; 261 262 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 263 if (!plugin->ensurePluginLoaded()) 264 continue; 265 #endif 266 pluginChoices.append(plugin); 267 mimeTypeForPlugin.add(plugin, mimeType); 268 foundMapping = true; 269 break; 270 } 271 } 272 if (foundMapping) 273 break; 274 } 275 } 276 277 if (pluginChoices.isEmpty()) 278 return String(); 279 280 qsort(pluginChoices.data(), pluginChoices.size(), sizeof(PluginPackage*), PluginDatabase::preferredPluginCompare); 281 282 return mimeTypeForPlugin.get(pluginChoices[0]); 283 } 284 285 PluginPackage* PluginDatabase::findPlugin(const KURL& url, String& mimeType) 286 { 287 if (!mimeType.isEmpty()) 288 return pluginForMIMEType(mimeType); 289 290 String filename = url.lastPathComponent(); 291 if (filename.endsWith("/")) 292 return 0; 293 294 int extensionPos = filename.reverseFind('.'); 295 if (extensionPos == -1) 296 return 0; 297 298 String mimeTypeForExtension = MIMETypeForExtension(filename.substring(extensionPos + 1)); 299 PluginPackage* plugin = pluginForMIMEType(mimeTypeForExtension); 300 if (!plugin) { 301 // FIXME: if no plugin could be found, query Windows for the mime type 302 // corresponding to the extension. 303 return 0; 304 } 305 306 mimeType = mimeTypeForExtension; 307 return plugin; 308 } 309 310 void PluginDatabase::setPreferredPluginForMIMEType(const String& mimeType, PluginPackage* plugin) 311 { 312 if (!plugin || plugin->mimeToExtensions().contains(mimeType)) 313 m_preferredPlugins.set(mimeType.lower(), plugin); 314 } 315 316 void PluginDatabase::getDeletedPlugins(PluginSet& plugins) const 317 { 318 PluginSet::const_iterator end = m_plugins.end(); 319 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { 320 if (!fileExists((*it)->path())) 321 plugins.add(*it); 322 } 323 } 324 325 bool PluginDatabase::add(PassRefPtr<PluginPackage> prpPackage) 326 { 327 ASSERT_ARG(prpPackage, prpPackage); 328 329 RefPtr<PluginPackage> package = prpPackage; 330 331 if (!m_plugins.add(package).second) 332 return false; 333 334 m_pluginsByPath.add(package->path(), package); 335 return true; 336 } 337 338 void PluginDatabase::remove(PluginPackage* package) 339 { 340 MIMEToExtensionsMap::const_iterator it = package->mimeToExtensions().begin(); 341 MIMEToExtensionsMap::const_iterator end = package->mimeToExtensions().end(); 342 for ( ; it != end; ++it) { 343 PluginPackageByNameMap::iterator packageInMap = m_preferredPlugins.find(it->first); 344 if (packageInMap != m_preferredPlugins.end() && packageInMap->second == package) 345 m_preferredPlugins.remove(packageInMap); 346 } 347 348 m_plugins.remove(package); 349 m_pluginsByPath.remove(package->path()); 350 } 351 352 void PluginDatabase::clear() 353 { 354 m_plugins.clear(); 355 m_pluginsByPath.clear(); 356 m_pluginPathsWithTimes.clear(); 357 m_registeredMIMETypes.clear(); 358 m_preferredPlugins.clear(); 359 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 360 m_persistentMetadataCacheIsLoaded = false; 361 #endif 362 } 363 364 #if (!OS(WINCE)) && (!OS(SYMBIAN)) && (!OS(WINDOWS) || !ENABLE(NETSCAPE_PLUGIN_API)) 365 // For Safari/Win the following three methods are implemented 366 // in PluginDatabaseWin.cpp, but if we can use WebCore constructs 367 // for the logic we should perhaps move it here under XP_WIN? 368 369 Vector<String> PluginDatabase::defaultPluginDirectories() 370 { 371 Vector<String> paths; 372 373 // Add paths specific to each platform 374 #if defined(XP_UNIX) 375 String userPluginPath = homeDirectoryPath(); 376 userPluginPath.append(String("/.mozilla/plugins")); 377 paths.append(userPluginPath); 378 379 userPluginPath = homeDirectoryPath(); 380 userPluginPath.append(String("/.netscape/plugins")); 381 paths.append(userPluginPath); 382 383 paths.append("/usr/lib/browser/plugins"); 384 paths.append("/usr/local/lib/mozilla/plugins"); 385 paths.append("/usr/lib/firefox/plugins"); 386 paths.append("/usr/lib64/browser-plugins"); 387 paths.append("/usr/lib/browser-plugins"); 388 paths.append("/usr/lib/mozilla/plugins"); 389 paths.append("/usr/local/netscape/plugins"); 390 paths.append("/opt/mozilla/plugins"); 391 paths.append("/opt/mozilla/lib/plugins"); 392 paths.append("/opt/netscape/plugins"); 393 paths.append("/opt/netscape/communicator/plugins"); 394 paths.append("/usr/lib/netscape/plugins"); 395 paths.append("/usr/lib/netscape/plugins-libc5"); 396 paths.append("/usr/lib/netscape/plugins-libc6"); 397 paths.append("/usr/lib64/netscape/plugins"); 398 paths.append("/usr/lib64/mozilla/plugins"); 399 paths.append("/usr/lib/nsbrowser/plugins"); 400 paths.append("/usr/lib64/nsbrowser/plugins"); 401 402 String mozHome(getenv("MOZILLA_HOME")); 403 mozHome.append("/plugins"); 404 paths.append(mozHome); 405 406 Vector<String> mozPaths; 407 String mozPath(getenv("MOZ_PLUGIN_PATH")); 408 mozPath.split(UChar(':'), /* allowEmptyEntries */ false, mozPaths); 409 paths.append(mozPaths); 410 #elif defined(XP_MACOSX) 411 String userPluginPath = homeDirectoryPath(); 412 userPluginPath.append(String("/Library/Internet Plug-Ins")); 413 paths.append(userPluginPath); 414 paths.append("/Library/Internet Plug-Ins"); 415 #elif defined(XP_WIN) 416 String userPluginPath = homeDirectoryPath(); 417 userPluginPath.append(String("\\Application Data\\Mozilla\\plugins")); 418 paths.append(userPluginPath); 419 #endif 420 421 // Add paths specific to each port 422 #if PLATFORM(QT) 423 Vector<String> qtPaths; 424 String qtPath(qgetenv("QTWEBKIT_PLUGIN_PATH").constData()); 425 qtPath.split(UChar(':'), /* allowEmptyEntries */ false, qtPaths); 426 paths.append(qtPaths); 427 #endif 428 429 #if PLATFORM(ANDROID) 430 if (android::JavaSharedClient::GetPluginClient()) 431 return android::JavaSharedClient::GetPluginClient()->getPluginDirectories(); 432 #endif 433 434 return paths; 435 } 436 437 bool PluginDatabase::isPreferredPluginDirectory(const String& path) 438 { 439 String preferredPath = homeDirectoryPath(); 440 441 #if defined(XP_UNIX) 442 preferredPath.append(String("/.mozilla/plugins")); 443 #elif defined(XP_MACOSX) 444 preferredPath.append(String("/Library/Internet Plug-Ins")); 445 #elif defined(XP_WIN) 446 preferredPath.append(String("\\Application Data\\Mozilla\\plugins")); 447 #endif 448 449 // TODO: We should normalize the path before doing a comparison. 450 return path == preferredPath; 451 } 452 453 void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const 454 { 455 // FIXME: This should be a case insensitive set. 456 HashSet<String> uniqueFilenames; 457 458 #if defined(XP_UNIX) || defined(ANDROID) 459 String fileNameFilter("*.so"); 460 #else 461 String fileNameFilter(""); 462 #endif 463 464 Vector<String>::const_iterator dirsEnd = m_pluginDirectories.end(); 465 for (Vector<String>::const_iterator dIt = m_pluginDirectories.begin(); dIt != dirsEnd; ++dIt) { 466 Vector<String> pluginPaths = listDirectory(*dIt, fileNameFilter); 467 Vector<String>::const_iterator pluginsEnd = pluginPaths.end(); 468 for (Vector<String>::const_iterator pIt = pluginPaths.begin(); pIt != pluginsEnd; ++pIt) { 469 if (!fileExists(*pIt)) 470 continue; 471 472 paths.add(*pIt); 473 } 474 } 475 } 476 477 #endif // !OS(SYMBIAN) && !OS(WINDOWS) 478 479 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE) 480 481 static void fillBufferWithContentsOfFile(PlatformFileHandle file, Vector<char>& buffer) 482 { 483 size_t bufferSize = 0; 484 size_t bufferCapacity = 1024; 485 buffer.resize(bufferCapacity); 486 487 do { 488 bufferSize += readFromFile(file, buffer.data() + bufferSize, bufferCapacity - bufferSize); 489 if (bufferSize == bufferCapacity) { 490 if (bufferCapacity < maximumPersistentPluginMetadataCacheSize) { 491 bufferCapacity *= 2; 492 buffer.resize(bufferCapacity); 493 } else { 494 buffer.clear(); 495 return; 496 } 497 } else 498 break; 499 } while (true); 500 501 buffer.shrink(bufferSize); 502 } 503 504 static bool readUTF8String(String& resultString, char*& start, const char* end) 505 { 506 if (start >= end) 507 return false; 508 509 int len = strlen(start); 510 resultString = String::fromUTF8(start, len); 511 start += len + 1; 512 513 return true; 514 } 515 516 static bool readTime(time_t& resultTime, char*& start, const char* end) 517 { 518 if (start + sizeof(time_t) >= end) 519 return false; 520 521 resultTime = *reinterpret_cast_ptr<time_t*>(start); 522 start += sizeof(time_t); 523 524 return true; 525 } 526 527 static const char schemaVersion = '1'; 528 static const char persistentPluginMetadataCacheFilename[] = "PluginMetadataCache.bin"; 529 530 void PluginDatabase::loadPersistentMetadataCache() 531 { 532 if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) 533 return; 534 535 PlatformFileHandle file; 536 String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); 537 file = openFile(absoluteCachePath, OpenForRead); 538 539 if (!isHandleValid(file)) 540 return; 541 542 // Mark cache as loaded regardless of success or failure. If 543 // there's error in the cache, we won't try to load it anymore. 544 m_persistentMetadataCacheIsLoaded = true; 545 546 Vector<char> fileContents; 547 fillBufferWithContentsOfFile(file, fileContents); 548 closeFile(file); 549 550 if (fileContents.size() < 2 || fileContents.first() != schemaVersion || fileContents.last() != '\0') { 551 LOG_ERROR("Unable to read plugin metadata cache: corrupt schema"); 552 deleteFile(absoluteCachePath); 553 return; 554 } 555 556 char* bufferPos = fileContents.data() + 1; 557 char* end = fileContents.data() + fileContents.size(); 558 559 PluginSet cachedPlugins; 560 HashMap<String, time_t> cachedPluginPathsWithTimes; 561 HashMap<String, RefPtr<PluginPackage> > cachedPluginsByPath; 562 563 while (bufferPos < end) { 564 String path; 565 time_t lastModified; 566 String name; 567 String desc; 568 String mimeDesc; 569 if (!(readUTF8String(path, bufferPos, end) 570 && readTime(lastModified, bufferPos, end) 571 && readUTF8String(name, bufferPos, end) 572 && readUTF8String(desc, bufferPos, end) 573 && readUTF8String(mimeDesc, bufferPos, end))) { 574 LOG_ERROR("Unable to read plugin metadata cache: corrupt data"); 575 deleteFile(absoluteCachePath); 576 return; 577 } 578 579 // Skip metadata that points to plugins from directories that 580 // are not part of plugin directory list anymore. 581 String pluginDirectoryName = directoryName(path); 582 if (m_pluginDirectories.find(pluginDirectoryName) == WTF::notFound) 583 continue; 584 585 RefPtr<PluginPackage> package = PluginPackage::createPackageFromCache(path, lastModified, name, desc, mimeDesc); 586 587 if (package && cachedPlugins.add(package).second) { 588 cachedPluginPathsWithTimes.add(package->path(), package->lastModified()); 589 cachedPluginsByPath.add(package->path(), package); 590 } 591 } 592 593 m_plugins.swap(cachedPlugins); 594 m_pluginsByPath.swap(cachedPluginsByPath); 595 m_pluginPathsWithTimes.swap(cachedPluginPathsWithTimes); 596 } 597 598 static bool writeUTF8String(PlatformFileHandle file, const String& string) 599 { 600 CString utf8String = string.utf8(); 601 int length = utf8String.length() + 1; 602 return writeToFile(file, utf8String.data(), length) == length; 603 } 604 605 static bool writeTime(PlatformFileHandle file, const time_t& time) 606 { 607 return writeToFile(file, reinterpret_cast<const char*>(&time), sizeof(time_t)) == sizeof(time_t); 608 } 609 610 void PluginDatabase::updatePersistentMetadataCache() 611 { 612 if (!isPersistentMetadataCacheEnabled() || persistentMetadataCachePath().isEmpty()) 613 return; 614 615 makeAllDirectories(persistentMetadataCachePath()); 616 String absoluteCachePath = pathByAppendingComponent(persistentMetadataCachePath(), persistentPluginMetadataCacheFilename); 617 deleteFile(absoluteCachePath); 618 619 if (m_plugins.isEmpty()) 620 return; 621 622 PlatformFileHandle file; 623 file = openFile(absoluteCachePath, OpenForWrite); 624 625 if (!isHandleValid(file)) { 626 LOG_ERROR("Unable to open plugin metadata cache for saving"); 627 return; 628 } 629 630 char localSchemaVersion = schemaVersion; 631 if (writeToFile(file, &localSchemaVersion, 1) != 1) { 632 LOG_ERROR("Unable to write plugin metadata cache schema"); 633 closeFile(file); 634 deleteFile(absoluteCachePath); 635 return; 636 } 637 638 PluginSet::const_iterator end = m_plugins.end(); 639 for (PluginSet::const_iterator it = m_plugins.begin(); it != end; ++it) { 640 if (!(writeUTF8String(file, (*it)->path()) 641 && writeTime(file, (*it)->lastModified()) 642 && writeUTF8String(file, (*it)->name()) 643 && writeUTF8String(file, (*it)->description()) 644 && writeUTF8String(file, (*it)->fullMIMEDescription()))) { 645 LOG_ERROR("Unable to write plugin metadata to cache"); 646 closeFile(file); 647 deleteFile(absoluteCachePath); 648 return; 649 } 650 } 651 652 closeFile(file); 653 } 654 655 bool PluginDatabase::isPersistentMetadataCacheEnabled() 656 { 657 return gPersistentPluginMetadataCacheIsEnabled; 658 } 659 660 void PluginDatabase::setPersistentMetadataCacheEnabled(bool isEnabled) 661 { 662 gPersistentPluginMetadataCacheIsEnabled = isEnabled; 663 } 664 665 String PluginDatabase::persistentMetadataCachePath() 666 { 667 return WebCore::persistentPluginMetadataCachePath(); 668 } 669 670 void PluginDatabase::setPersistentMetadataCachePath(const String& persistentMetadataCachePath) 671 { 672 WebCore::persistentPluginMetadataCachePath() = persistentMetadataCachePath; 673 } 674 #endif 675 } 676