Bug 1371888 - cache plugin information in pluginreg.dat to avoid sync startup load, r?Mossop,florian
This changes the pluginreg.dat format to include the blocklist state.
There is now only the saved blocklist state in a plugin tag instance, rather than
looking it up from in there using the blocklist service, so it was renamed from
mCachedBlocklistState to mBlocklistState. We pass the 'right' state to the plugin
instance when the plugintag is constructed. If we don't have state, we mark it as
unblocked.
mCachedBlocklistStateChanged was never read so it's being removed.
Bug 1439519 adds a 'blocklist-loaded' notification that is fired once the blocklist is loaded.
The plugin host implementation will listen to this in the parent process and update the
blocklist state of all the plugins, and broadcast changes to the child process, just like when
we update the blocklist from the server. We now also avoid re-sending plugin content to the
content processes if the plugin state hasn't changed as a result of the blocklist having been
loaded.
Finally, because new plugins should still get an up-to-date blocklist state, and
telemetry should get up-to-date data about which plugins are and aren't enabled
once we have that data, we ensure that once we've loaded the blocklist async,
we schedule an idle task to parse it and consider it loaded.
All this means that plugin blocklist information could be mistaken between the points where
a new plugin is installed and we first run Firefox with the new plugin, and the point where
we load the blocklist. Given the trade-offs, that size of window (tiny) seems OK, also given
that there's already a much larger window in blocklist updates (which only happen once every 24h).
MozReview-Commit-ID: 1gsojRkUzTw
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -802,32 +802,28 @@ nsObjectLoadingContent::InstantiatePlugi
RefPtr<nsNPAPIPluginInstance> pluginInstance;
GetPluginInstance(getter_AddRefs(pluginInstance));
if (pluginInstance) {
nsCOMPtr<nsIPluginTag> pluginTag;
pluginHost->GetPluginTagForInstance(pluginInstance,
getter_AddRefs(pluginTag));
- nsCOMPtr<nsIBlocklistService> blocklist =
- do_GetService("@mozilla.org/extensions/blocklist;1");
- if (blocklist) {
- uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
- blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
- EmptyString(), &blockState);
- if (blockState == nsIBlocklistService::STATE_OUTDATED) {
- // Fire plugin outdated event if necessary
- LOG(("OBJLC [%p]: Dispatching plugin outdated event for content\n",
- this));
- nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
- NS_LITERAL_STRING("PluginOutdated"));
- nsresult rv = NS_DispatchToCurrentThread(ev);
- if (NS_FAILED(rv)) {
- NS_WARNING("failed to dispatch nsSimplePluginEvent");
- }
+
+ uint32_t blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
+ pluginTag->GetBlocklistState(&blockState);
+ if (blockState == nsIBlocklistService::STATE_OUTDATED) {
+ // Fire plugin outdated event if necessary
+ LOG(("OBJLC [%p]: Dispatching plugin outdated event for content\n",
+ this));
+ nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(thisContent,
+ NS_LITERAL_STRING("PluginOutdated"));
+ nsresult rv = NS_DispatchToCurrentThread(ev);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to dispatch nsSimplePluginEvent");
}
}
// If we have a URI but didn't open a channel yet (eAllowPluginSkipChannel)
// or we did load with a channel but are re-instantiating, re-open the
// channel. OpenChannel() performs security checks, and this plugin has
// already passed content policy in LoadObject.
if ((mURI && !mChannelLoaded) || (mChannelLoaded && !aIsLoading)) {
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -392,17 +392,18 @@ nsPluginArray::EnsurePlugins()
} else {
mCTPPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
}
}
}
if (mPlugins.Length() == 0 && mCTPPlugins.Length() != 0) {
nsCOMPtr<nsPluginTag> hiddenTag = new nsPluginTag("Hidden Plugin", nullptr, "dummy.plugin", nullptr, nullptr,
- nullptr, nullptr, nullptr, 0, 0, false);
+ nullptr, nullptr, nullptr, 0, 0, false,
+ nsIBlocklistService::STATE_NOT_BLOCKED);
mPlugins.AppendElement(new nsPluginElement(mWindow, hiddenTag));
}
// Alphabetize the enumeration order of non-hidden plugins to reduce
// fingerprintable entropy based on plugins' installation file times.
mPlugins.Sort();
}
// nsPluginElement implementation.
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -129,17 +129,17 @@ static const char *kPrefWhitelist = "plu
static const char *kPrefLoadInParentPrefix = "plugin.load_in_parent_process.";
static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
// How long we wait before unloading an idle plugin process.
// Defaults to 30 seconds.
static const char *kPrefUnloadPluginTimeoutSecs = "dom.ipc.plugins.unloadTimeoutSecs";
static const uint32_t kDefaultPluginUnloadingTimeout = 30;
-static const char *kPluginRegistryVersion = "0.18";
+static const char *kPluginRegistryVersion = "0.19";
static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
#define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
LazyLogModule nsPluginLogging::gPluginLog(PLUGIN_LOG_NAME);
@@ -261,17 +261,20 @@ nsPluginHost::nsPluginHost()
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
Preferences::AddStrongObserver(this, "plugin.disable");
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (obsService) {
obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
- obsService->AddObserver(this, "blocklist-updated", false);
+ if (XRE_IsParentProcess()) {
+ obsService->AddObserver(this, "blocklist-updated", false);
+ obsService->AddObserver(this, "blocklist-loaded", false);
+ }
}
#ifdef PLUGIN_LOGGING
MOZ_LOG(nsPluginLogging::gNPNLog, PLUGIN_LOG_ALWAYS,("NPN Logging Active!\n"));
MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_ALWAYS,("General Plugin Logging Active! (nsPluginHost::ctor)\n"));
MOZ_LOG(nsPluginLogging::gNPPLog, PLUGIN_LOG_ALWAYS,("NPP Logging Active!\n"));
PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n"));
@@ -1994,16 +1997,23 @@ nsresult nsPluginHost::ScanPluginsDirect
}
}
pluginFiles.Sort(CompareFilesByTime());
nsCOMArray<nsIFile> extensionDirs;
GetExtensionDirectories(extensionDirs);
+ nsCOMPtr<nsIBlocklistService> blocklist =
+ do_GetService("@mozilla.org/extensions/blocklist;1");
+
+ bool isBlocklistLoaded = false;
+ if (blocklist && NS_FAILED(blocklist->GetIsLoaded(&isBlocklistLoaded))) {
+ isBlocklistLoaded = false;
+ }
for (int32_t i = (pluginFiles.Length() - 1); i >= 0; i--) {
nsCOMPtr<nsIFile>& localfile = pluginFiles[i];
nsString utf16FilePath;
rv = localfile->GetPath(utf16FilePath);
if (NS_FAILED(rv))
continue;
@@ -2085,22 +2095,27 @@ nsresult nsPluginHost::ScanPluginsDirect
}
mInvalidPlugins = invalidTag;
// Mark aPluginsChanged so pluginreg is rewritten
*aPluginsChanged = true;
continue;
}
- pluginTag = new nsPluginTag(&info, fileModTime, fromExtension);
- pluginFile.FreePluginInfo(info);
+ uint32_t state = nsIBlocklistService::STATE_NOT_BLOCKED;
+ pluginTag = new nsPluginTag(&info, fileModTime, fromExtension, state);
pluginTag->mLibrary = library;
- uint32_t state;
- rv = pluginTag->GetBlocklistState(&state);
- NS_ENSURE_SUCCESS(rv, rv);
+ // If the blocklist is loaded, get the blocklist state now.
+ // If it isn't loaded yet, we'll update it once it loads.
+ if (isBlocklistLoaded &&
+ NS_SUCCEEDED(blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
+ EmptyString(), &state))) {
+ pluginTag->SetBlocklistState(state);
+ }
+ pluginFile.FreePluginInfo(info);
// If the blocklist says it is risky and we have never seen this
// plugin before, then disable it.
if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
pluginTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
}
// Plugin unloading is tag-based. If we created a new tag and loaded
@@ -2739,26 +2754,28 @@ nsPluginHost::WritePluginInfo()
PLUGIN_REGISTRY_END_OF_LINE_MARKER,
(tag->mFullPath.get()),
PLUGIN_REGISTRY_FIELD_DELIMITER,
PLUGIN_REGISTRY_END_OF_LINE_MARKER,
(tag->Version().get()),
PLUGIN_REGISTRY_FIELD_DELIMITER,
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
- // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension
- PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%c\n",
+ // lastModifiedTimeStamp|canUnload|tag->mFlags|fromExtension|blocklistState
+ PR_fprintf(fd, "%lld%c%d%c%lu%c%d%c%d%c%c\n",
tag->mLastModifiedTime,
PLUGIN_REGISTRY_FIELD_DELIMITER,
false, // did store whether or not to unload in-process plugins
PLUGIN_REGISTRY_FIELD_DELIMITER,
0, // legacy field for flags
PLUGIN_REGISTRY_FIELD_DELIMITER,
tag->IsFromExtension(),
PLUGIN_REGISTRY_FIELD_DELIMITER,
+ tag->BlocklistState(),
+ PLUGIN_REGISTRY_FIELD_DELIMITER,
PLUGIN_REGISTRY_END_OF_LINE_MARKER);
//description, name & mtypecount are on separate line
PR_fprintf(fd, "%s%c%c\n%s%c%c\n%d\n",
(tag->Description().get()),
PLUGIN_REGISTRY_FIELD_DELIMITER,
PLUGIN_REGISTRY_END_OF_LINE_MARKER,
(tag->Name().get()),
@@ -2969,22 +2986,23 @@ nsPluginHost::ReadPluginInfo()
if (!reader.NextLine())
return rv;
const char *version;
version = reader.LinePtr();
if (!reader.NextLine())
return rv;
- // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
- if (4 != reader.ParseLine(values, 4))
+ // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension|blocklistState
+ if (5 != reader.ParseLine(values, 5))
return rv;
int64_t lastmod = nsCRT::atoll(values[0]);
bool fromExtension = atoi(values[3]);
+ uint16_t blocklistState = atoi(values[4]);
if (!reader.NextLine())
return rv;
char *description = reader.LinePtr();
if (!reader.NextLine())
return rv;
const char *name = reader.LinePtr();
@@ -3035,17 +3053,17 @@ nsPluginHost::ReadPluginInfo()
RefPtr<nsPluginTag> tag = new nsPluginTag(name,
description,
filename,
fullpath,
version,
(const char* const*)mimetypes,
(const char* const*)mimedescriptions,
(const char* const*)extensions,
- mimetypecount, lastmod, fromExtension, true);
+ mimetypecount, lastmod, fromExtension, blocklistState, true);
delete [] heapalloced;
// Import flags from registry into prefs for old registry versions
MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->FileName().get()));
tag->mNext = mCachedPlugins;
@@ -3384,25 +3402,42 @@ NS_IMETHODIMP nsPluginHost::Observe(nsIS
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
// Unload or load plugins as needed
if (mPluginsDisabled) {
UnloadPlugins();
} else {
LoadPlugins();
}
}
- if (!strcmp("blocklist-updated", aTopic)) {
+ if (XRE_IsParentProcess() &&
+ (!strcmp("blocklist-updated", aTopic) || !strcmp("blocklist-loaded", aTopic))) {
+ nsCOMPtr<nsIBlocklistService> blocklist =
+ do_GetService("@mozilla.org/extensions/blocklist;1");
+ if (!blocklist) {
+ return NS_OK;
+ }
nsPluginTag* plugin = mPlugins;
+ bool blocklistAlteredPlugins = false;
while (plugin) {
- plugin->InvalidateBlocklistState();
+ uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
+ nsresult rv = blocklist->GetPluginBlocklistState(plugin, EmptyString(),
+ EmptyString(), &blocklistState);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t oldBlocklistState;
+ plugin->GetBlocklistState(&oldBlocklistState);
+ plugin->SetBlocklistState(blocklistState);
+ blocklistAlteredPlugins |= (oldBlocklistState != blocklistState);
plugin = plugin->mNext;
}
- // We update blocklists asynchronously by just sending a new plugin list to
- // content.
- if (XRE_IsParentProcess()) {
+ if (blocklistAlteredPlugins) {
+ // Write the changed list to disk:
+ WritePluginInfo();
+
+ // We update blocklists asynchronously by just sending a new plugin list to
+ // content.
// We'll need to repack our tags and send them to content again.
IncrementChromeEpoch();
SendPluginsToContent();
}
}
return NS_OK;
}
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -218,32 +218,32 @@ nsIInternalPluginTag::HasMimeType(const
}
/* nsPluginTag */
uint32_t nsPluginTag::sNextId;
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
int64_t aLastModifiedTime,
- bool fromExtension)
+ bool fromExtension,
+ uint32_t aBlocklistState)
: nsIInternalPluginTag(aPluginInfo->fName, aPluginInfo->fDescription,
aPluginInfo->fFileName, aPluginInfo->fVersion),
mId(sNextId++),
mContentProcessRunningCount(0),
mHadLocalInstance(false),
mLibrary(nullptr),
mIsFlashPlugin(false),
mSupportsAsyncRender(false),
mFullPath(aPluginInfo->fFullPath),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(0),
mIsSandboxLoggingEnabled(false),
- mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
- mCachedBlocklistStateValid(false),
- mIsFromExtension(fromExtension)
+ mIsFromExtension(fromExtension),
+ mBlocklistState(aBlocklistState)
{
InitMime(aPluginInfo->fMimeTypeArray,
aPluginInfo->fMimeDescriptionArray,
aPluginInfo->fExtensionArray,
aPluginInfo->fVariantCount);
InitSandboxLevel();
EnsureMembersAreUTF8();
FixupVersion();
@@ -255,31 +255,31 @@ nsPluginTag::nsPluginTag(const char* aNa
const char* aFullPath,
const char* aVersion,
const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool fromExtension,
+ uint32_t aBlocklistState,
bool aArgsAreUTF8)
: nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
mId(sNextId++),
mContentProcessRunningCount(0),
mHadLocalInstance(false),
mLibrary(nullptr),
mIsFlashPlugin(false),
mSupportsAsyncRender(false),
mFullPath(aFullPath),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(0),
mIsSandboxLoggingEnabled(false),
- mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
- mCachedBlocklistStateValid(false),
- mIsFromExtension(fromExtension)
+ mIsFromExtension(fromExtension),
+ mBlocklistState(aBlocklistState)
{
InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
static_cast<uint32_t>(aVariants));
InitSandboxLevel();
if (!aArgsAreUTF8)
EnsureMembersAreUTF8();
FixupVersion();
}
@@ -293,31 +293,30 @@ nsPluginTag::nsPluginTag(uint32_t aId,
nsTArray<nsCString> aMimeTypes,
nsTArray<nsCString> aMimeDescriptions,
nsTArray<nsCString> aExtensions,
bool aIsFlashPlugin,
bool aSupportsAsyncRender,
int64_t aLastModifiedTime,
bool aFromExtension,
int32_t aSandboxLevel,
- uint16_t aBlocklistState)
+ uint32_t aBlocklistState)
: nsIInternalPluginTag(aName, aDescription, aFileName, aVersion, aMimeTypes,
aMimeDescriptions, aExtensions),
mId(aId),
mContentProcessRunningCount(0),
mLibrary(nullptr),
mIsFlashPlugin(aIsFlashPlugin),
mSupportsAsyncRender(aSupportsAsyncRender),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(aSandboxLevel),
mIsSandboxLoggingEnabled(false),
mNiceFileName(),
- mCachedBlocklistState(aBlocklistState),
- mCachedBlocklistStateValid(true),
- mIsFromExtension(aFromExtension)
+ mIsFromExtension(aFromExtension),
+ mBlocklistState(aBlocklistState)
{
}
nsPluginTag::~nsPluginTag()
{
NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
}
@@ -543,19 +542,17 @@ nsPluginTag::GetDisabled(bool* aDisabled
{
*aDisabled = !IsEnabled();
return NS_OK;
}
bool
nsPluginTag::IsBlocklisted()
{
- uint32_t blocklistState;
- nsresult rv = GetBlocklistState(&blocklistState);
- return NS_FAILED(rv) || blocklistState == nsIBlocklistService::STATE_BLOCKED;
+ return mBlocklistState == nsIBlocklistService::STATE_BLOCKED;
}
NS_IMETHODIMP
nsPluginTag::GetBlocklisted(bool* aBlocklisted)
{
*aBlocklisted = IsBlocklisted();
return NS_OK;
}
@@ -717,58 +714,30 @@ nsPluginTag::GetNiceName(nsACString & aR
{
aResult = GetNiceFileName();
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetBlocklistState(uint32_t *aResult)
{
- // If we're in the content process, assume our cache state to always be valid,
- // as the only way it can be updated is via a plugin list push from the
- // parent process.
- if (!XRE_IsParentProcess()) {
- *aResult = mCachedBlocklistState;
- return NS_OK;
- }
-
- nsCOMPtr<nsIBlocklistService> blocklist =
- do_GetService("@mozilla.org/extensions/blocklist;1");
-
- if (!blocklist) {
- *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
- }
- // The EmptyString()s are so we use the currently running application
- // and toolkit versions
- else if (NS_FAILED(blocklist->GetPluginBlocklistState(this, EmptyString(),
- EmptyString(), aResult))) {
- *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
- }
-
- MOZ_ASSERT(*aResult <= UINT16_MAX);
- mCachedBlocklistState = (uint16_t) *aResult;
- mCachedBlocklistStateValid = true;
+ *aResult = mBlocklistState;
return NS_OK;
}
void
-nsPluginTag::SetBlocklistState(uint16_t aBlocklistState)
+nsPluginTag::SetBlocklistState(uint32_t aBlocklistState)
{
- // We should only ever call this on content processes. Any calls in the parent
- // process should route through GetBlocklistState since we'll have the
- // blocklist service there.
- MOZ_ASSERT(!XRE_IsParentProcess());
- mCachedBlocklistState = aBlocklistState;
- mCachedBlocklistStateValid = true;
+ mBlocklistState = aBlocklistState;
}
-void
-nsPluginTag::InvalidateBlocklistState()
+uint32_t
+nsPluginTag::BlocklistState()
{
- mCachedBlocklistStateValid = false;
+ return mBlocklistState;
}
NS_IMETHODIMP
nsPluginTag::GetLastModifiedTime(PRTime* aLastModifiedTime)
{
MOZ_ASSERT(aLastModifiedTime);
*aLastModifiedTime = mLastModifiedTime;
return NS_OK;
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -5,16 +5,17 @@
#ifndef nsPluginTags_h_
#define nsPluginTags_h_
#include "mozilla/Attributes.h"
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
+#include "nsIBlocklistService.h"
#include "nsIPluginTag.h"
#include "nsITimer.h"
#include "nsString.h"
class nsIURI;
struct PRLibrary;
struct nsPluginInfo;
class nsNPAPIPlugin;
@@ -103,58 +104,61 @@ public:
ePluginState_Disabled = 0,
ePluginState_Clicktoplay = 1,
ePluginState_Enabled = 2,
ePluginState_MaxValue = 3,
};
nsPluginTag(nsPluginInfo* aPluginInfo,
int64_t aLastModifiedTime,
- bool fromExtension);
+ bool fromExtension,
+ uint32_t aBlocklistState);
nsPluginTag(const char* aName,
const char* aDescription,
const char* aFileName,
const char* aFullPath,
const char* aVersion,
const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool fromExtension,
+ uint32_t aBlocklistState,
bool aArgsAreUTF8 = false);
nsPluginTag(uint32_t aId,
const char* aName,
const char* aDescription,
const char* aFileName,
const char* aFullPath,
const char* aVersion,
nsTArray<nsCString> aMimeTypes,
nsTArray<nsCString> aMimeDescriptions,
nsTArray<nsCString> aExtensions,
bool aIsFlashPlugin,
bool aSupportsAsyncRender,
int64_t aLastModifiedTime,
bool aFromExtension,
int32_t aSandboxLevel,
- uint16_t aBlocklistState);
+ uint32_t aBlocklistState);
void TryUnloadPlugin(bool inShutdown);
// plugin is enabled and not blocklisted
bool IsActive();
bool IsEnabled() override;
void SetEnabled(bool enabled);
bool IsClicktoplay();
bool IsBlocklisted();
+ uint32_t BlocklistState();
PluginState GetPluginState();
void SetPluginState(PluginState state);
- void SetBlocklistState(uint16_t aBlocklistState);
+ void SetBlocklistState(uint32_t aBlocklistState);
bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
const nsCString& GetNiceFileName() override;
bool IsFromExtension() const;
RefPtr<nsPluginTag> mNext;
uint32_t mId;
@@ -170,25 +174,22 @@ public:
bool mIsFlashPlugin;
bool mSupportsAsyncRender;
nsCString mFullPath; // UTF-8
int64_t mLastModifiedTime;
nsCOMPtr<nsITimer> mUnloadTimer;
int32_t mSandboxLevel;
bool mIsSandboxLoggingEnabled;
- void InvalidateBlocklistState();
-
private:
virtual ~nsPluginTag();
nsCString mNiceFileName; // UTF-8
- uint16_t mCachedBlocklistState;
- bool mCachedBlocklistStateValid;
bool mIsFromExtension;
+ uint32_t mBlocklistState;
void InitMime(const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
uint32_t aVariantCount);
void InitSandboxLevel();
nsresult EnsureMembersAreUTF8();
void FixupVersion();
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -372,17 +372,17 @@ PluginWrapper.prototype = {
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]);
}
return val;
},
get blocklistState() {
let { tags: [tag] } = pluginFor(this);
- return Services.blocklist.getPluginBlocklistState(tag);
+ return tag.blocklistState;
},
get blocklistURL() {
let { tags: [tag] } = pluginFor(this);
return Services.blocklist.getPluginBlocklistURL(tag);
},
get size() {
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -623,19 +623,16 @@ Blocklist.prototype = {
}
// Save current blocklist timestamp to pref.
const lastModified = request.getResponseHeader("Last-Modified") || "";
Services.prefs.setCharPref(PREF_BLOCKLIST_LAST_MODIFIED, lastModified);
var oldAddonEntries = this._addonEntries;
var oldPluginEntries = this._pluginEntries;
- this._addonEntries = [];
- this._gfxEntries = [];
- this._pluginEntries = [];
this._loadBlocklistFromXML(responseXML);
// We don't inform the users when the graphics blocklist changed at runtime.
// However addons and plugins blocking status is refreshed.
this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
try {
let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
@@ -670,23 +667,16 @@ Blocklist.prototype = {
* Finds the newest blocklist file from the application and the profile and
* load it or does nothing if neither exist.
*/
_loadBlocklist() {
this._addonEntries = [];
this._gfxEntries = [];
this._pluginEntries = [];
- if (this._isBlocklistPreloaded()) {
- Services.telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(false);
- this._loadBlocklistFromString(this._preloadedBlocklistContent);
- delete this._preloadedBlocklistContent;
- return;
- }
-
Services.telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(true);
var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
try {
this._loadBlocklistFromFile(profFile);
} catch (ex) {
LOG("Blocklist::_loadBlocklist: couldn't load file from profile, trying app dir");
try {
@@ -801,26 +791,21 @@ Blocklist.prototype = {
if (text)
this._loadBlocklistFromString(text);
},
get isLoaded() {
return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null;
},
- _isBlocklistPreloaded() {
- return this._preloadedBlocklistContent != null;
- },
-
/* Used for testing */
_clear() {
this._addonEntries = null;
this._gfxEntries = null;
this._pluginEntries = null;
- this._preloadedBlocklistContent = null;
},
async _preloadBlocklist() {
let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
try {
await this._preloadBlocklistFile(profPath);
return;
} catch (e) {
@@ -846,20 +831,25 @@ Blocklist.prototype = {
if (!gBlocklistEnabled) {
LOG("Blocklist::_preloadBlocklistFile: blocklist is disabled");
return;
}
let text = await OS.File.read(path, { encoding: "utf-8" });
- if (!this._addonEntries) {
- // Store the content only if a sync load has not been performed in the meantime.
- this._preloadedBlocklistContent = text;
- }
+ await new Promise(resolve => {
+ Services.tm.idleDispatchToMainThread(() => {
+ if (!this.isLoaded) {
+ Services.telemetry.getHistogramById("BLOCKLIST_SYNC_FILE_LOAD").add(false);
+ this._loadBlocklistFromString(text);
+ }
+ resolve();
+ });
+ });
},
_loadBlocklistFromString(text) {
try {
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = parser.parseFromString(text, "text/xml");
if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
@@ -871,16 +861,19 @@ Blocklist.prototype = {
} catch (e) {
LOG("Blocklist::_loadBlocklistFromString: Error constructing blocklist " + e);
return;
}
this._loadBlocklistFromXML(doc);
},
_loadBlocklistFromXML(doc) {
+ this._addonEntries = [];
+ this._gfxEntries = [];
+ this._pluginEntries = [];
try {
var childNodes = doc.documentElement.childNodes;
for (let element of childNodes) {
if (!(element instanceof Ci.nsIDOMElement))
continue;
switch (element.localName) {
case "emItems":
this._addonEntries = this._processItemNodes(element.childNodes, "emItem",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
@@ -2,39 +2,46 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
add_task(async function() {
let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
getService().wrappedJSObject;
let scope = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
- // sync -> async
+ // sync -> async. Check that async code doesn't try to read the file
+ // once it's already been read synchronously.
+ let read = scope.OS.File.read;
+ let triedToRead = false;
+ scope.OS.File.read = () => triedToRead = true;
blocklist._loadBlocklist();
Assert.ok(blocklist.isLoaded);
await blocklist._preloadBlocklist();
- Assert.ok(!blocklist._isBlocklistPreloaded());
+ Assert.ok(!triedToRead);
+ scope.OS.File.read = read;
blocklist._clear();
- // async -> sync
+ info("sync -> async complete");
+
+ // async first. Check that once we preload the content, that is sufficient.
await blocklist._preloadBlocklist();
- Assert.ok(!blocklist.isLoaded);
- Assert.ok(blocklist._isBlocklistPreloaded());
- blocklist._loadBlocklist();
Assert.ok(blocklist.isLoaded);
- Assert.ok(!blocklist._isBlocklistPreloaded());
+ // Calling _loadBlocklist now would just re-load the list sync.
+
+ info("async test complete");
blocklist._clear();
// async -> sync -> async
- let read = scope.OS.File.read;
scope.OS.File.read = function(...args) {
return new Promise((resolve, reject) => {
executeSoon(() => {
blocklist._loadBlocklist();
+ // Now do the async bit after all:
resolve(read(...args));
});
});
};
await blocklist._preloadBlocklist();
+ // We're mostly just checking this doesn't error out.
Assert.ok(blocklist.isLoaded);
- Assert.ok(!blocklist._isBlocklistPreloaded());
+ info("mixed async/sync test complete");
});