Bug 110894 - Use favicons on webpage shortcuts in Windows. r=bbondy

This commit is contained in:
Parth Mudgal 2012-07-21 16:07:26 -04:00
parent 86fe2dfee8
commit 18ade5e188
8 changed files with 647 additions and 457 deletions

View File

@ -24,6 +24,8 @@
#include "nsThreadUtils.h"
#include "mozilla/LazyIdleThread.h"
#include "WinUtils.h"
// The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes.
#define DEFAULT_THREAD_TIMEOUT_MS 30000
@ -41,10 +43,6 @@ bool JumpListBuilder::sBuildingList = false;
const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
NS_IMPL_ISUPPORTS2(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
NS_IMPL_ISUPPORTS1(AsyncFaviconDataReady, nsIFaviconDataCallback)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncWriteIconToDisk, nsIRunnable)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteIconFromDisk, nsIRunnable)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
JumpListBuilder::JumpListBuilder() :
mMaxItems(0),
@ -182,7 +180,7 @@ nsresult JumpListBuilder::RemoveIconCacheForItems(nsIMutableArray *items)
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRunnable> event
= new AsyncDeleteIconFromDisk(NS_ConvertUTF8toUTF16(spec));
= new mozilla::widget::AsyncDeleteIconFromDisk(NS_ConvertUTF8toUTF16(spec));
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
// The shortcut was generated from an IShellLinkW so IShellLinkW can
@ -207,7 +205,8 @@ nsresult JumpListBuilder::RemoveIconCacheForAllItems()
nsresult rv = NS_GetSpecialDirectory("ProfLDS",
getter_AddRefs(jumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
rv = jumpListCacheDir->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir));
rv = jumpListCacheDir->AppendNative(nsDependentCString(
mozilla::widget::FaviconHelper::kJumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISimpleEnumerator> entries;
rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
@ -509,234 +508,14 @@ NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject,
bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
if (!enabled) {
nsCOMPtr<nsIRunnable> event = new AsyncDeleteAllFaviconsFromDisk();
nsCOMPtr<nsIRunnable> event =
new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
}
return NS_OK;
}
AsyncFaviconDataReady::AsyncFaviconDataReady(nsIURI *aNewURI,
nsCOMPtr<nsIThread> &aIOThread)
: mNewURI(aNewURI),
mIOThread(aIOThread)
{
}
NS_IMETHODIMP
AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI,
PRUint32 aDataLen,
const PRUint8 *aData,
const nsACString &aMimeType)
{
if (!aDataLen || !aData) {
return NS_OK;
}
nsCOMPtr<nsIFile> icoFile;
nsresult rv = JumpListShortcut::GetOutputIconPath(mNewURI, icoFile);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
rv = icoFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
// Allocate a new buffer that we own and can use out of line in
// another thread. Copy the favicon raw data into it.
const fallible_t fallible = fallible_t();
PRUint8 *data = new (fallible) PRUint8[aDataLen];
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(data, aData, aDataLen);
//AsyncWriteIconToDisk takes ownership of the heap allocated buffer.
nsCOMPtr<nsIRunnable> event = new AsyncWriteIconToDisk(path, aMimeType,
data,
aDataLen);
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
// Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in
AsyncWriteIconToDisk::AsyncWriteIconToDisk(const nsAString &aIconPath,
const nsACString &aMimeTypeOfInputData,
PRUint8 *aBuffer,
PRUint32 aBufferLength)
: mIconPath(aIconPath),
mMimeTypeOfInputData(aMimeTypeOfInputData),
mBuffer(aBuffer),
mBufferLength(aBufferLength)
{
}
NS_IMETHODIMP AsyncWriteIconToDisk::Run()
{
NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
// Convert the obtained favicon data to an input stream
nsCOMPtr<nsIInputStream> stream;
nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
reinterpret_cast<const char*>(mBuffer.get()),
mBufferLength,
NS_ASSIGNMENT_DEPEND);
NS_ENSURE_SUCCESS(rv, rv);
// Decode the image from the format it was returned to us in (probably PNG)
nsCOMPtr<imgIContainer> container;
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
rv = imgtool->DecodeImageData(stream, mMimeTypeOfInputData,
getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
// Get the recommended icon width and height, or if failure to obtain
// these settings, fall back to 16x16 ICOs. These values can be different
// if the user has a different DPI setting other than 100%.
// Windows would scale the 16x16 icon themselves, but it's better
// we let our ICO encoder do it.
PRInt32 systemIconWidth = GetSystemMetrics(SM_CXSMICON);
PRInt32 systemIconHeight = GetSystemMetrics(SM_CYSMICON);
if (systemIconWidth == 0 || systemIconHeight == 0) {
systemIconWidth = 16;
systemIconHeight = 16;
}
// Scale the image to the needed size and in ICO format
mMimeTypeOfInputData.AssignLiteral("image/vnd.microsoft.icon");
nsCOMPtr<nsIInputStream> iconStream;
rv = imgtool->EncodeScaledImage(container, mMimeTypeOfInputData,
systemIconWidth,
systemIconHeight,
EmptyString(),
getter_AddRefs(iconStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> icoFile
= do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
rv = icoFile->InitWithPath(mIconPath);
// Setup the output stream for the ICO file on disk
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile);
NS_ENSURE_SUCCESS(rv, rv);
// Obtain the ICO buffer size from the re-encoded ICO stream
PRUint32 bufSize;
rv = iconStream->Available(&bufSize);
NS_ENSURE_SUCCESS(rv, rv);
// Setup a buffered output stream from the stream object
// so that we can simply use WriteFrom with the stream object
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream, bufSize);
NS_ENSURE_SUCCESS(rv, rv);
// Write out the icon stream to disk and make sure we wrote everything
PRUint32 wrote;
rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote);
NS_ASSERTION(bufSize == wrote, "Icon wrote size should be equal to requested write size");
// Cleanup
bufferedOutputStream->Close();
outputStream->Close();
return rv;
}
AsyncWriteIconToDisk::~AsyncWriteIconToDisk()
{
}
AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath)
: mIconPath(aIconPath)
{
}
NS_IMETHODIMP AsyncDeleteIconFromDisk::Run()
{
// Construct the parent path of the passed in path
nsCOMPtr<nsIFile> icoFile = do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
nsresult rv = icoFile->InitWithPath(mIconPath);
NS_ENSURE_SUCCESS(rv, rv);
// Check if the cached ICO file exists
bool exists;
rv = icoFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
// Check that we aren't deleting some arbitrary file that is not an icon
if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) {
// Check if the cached ICO file exists
bool exists;
if (NS_FAILED(icoFile->Exists(&exists)) || !exists)
return NS_ERROR_FAILURE;
// We found an ICO file that exists, so we should remove it
icoFile->Remove(false);
}
return NS_OK;
}
AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk()
{
}
AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk()
{
}
NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run()
{
// Construct the path of our jump list cache
nsCOMPtr<nsIFile> jumpListCacheDir;
nsresult rv = NS_GetSpecialDirectory("ProfLDS",
getter_AddRefs(jumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
rv = jumpListCacheDir->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISimpleEnumerator> entries;
rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
// Loop through each directory entry and remove all ICO files found
do {
bool hasMore = false;
if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore)
break;
nsCOMPtr<nsISupports> supp;
if (NS_FAILED(entries->GetNext(getter_AddRefs(supp))))
break;
nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
nsAutoString path;
if (NS_FAILED(currFile->GetPath(path)))
continue;
PRInt32 len = path.Length();
if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
// Check if the cached ICO file exists
bool exists;
if (NS_FAILED(currFile->Exists(&exists)) || !exists)
continue;
// We found an ICO file that exists, so we should remove it
currFile->Remove(false);
}
} while(true);
return NS_OK;
}
AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk()
{
}
} // namespace widget
} // namespace mozilla

View File

@ -55,65 +55,6 @@ private:
friend class WinTaskbar;
};
class AsyncFaviconDataReady MOZ_FINAL : public nsIFaviconDataCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONDATACALLBACK
AsyncFaviconDataReady(nsIURI *aNewURI, nsCOMPtr<nsIThread> &aIOThread);
private:
nsCOMPtr<nsIURI> mNewURI;
nsCOMPtr<nsIThread> mIOThread;
};
/**
* Asynchronously tries add the list to the build
*/
class AsyncWriteIconToDisk : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
// Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in
AsyncWriteIconToDisk(const nsAString &aIconPath,
const nsACString &aMimeTypeOfInputData,
PRUint8 *aData,
PRUint32 aDataLen);
virtual ~AsyncWriteIconToDisk();
private:
nsAutoString mIconPath;
nsCAutoString mMimeTypeOfInputData;
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferLength;
};
class AsyncDeleteIconFromDisk : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
AsyncDeleteIconFromDisk(const nsAString &aIconPath);
virtual ~AsyncDeleteIconFromDisk();
private:
nsAutoString mIconPath;
};
class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
AsyncDeleteAllFaviconsFromDisk();
virtual ~AsyncDeleteAllFaviconsFromDisk();
};
} // namespace widget
} // namespace mozilla

View File

@ -23,8 +23,6 @@
namespace mozilla {
namespace widget {
const char JumpListItem::kJumpListCacheDir[] = "jumpListCache";
// ISUPPORTS Impl's
NS_IMPL_ISUPPORTS1(JumpListItem,
nsIJumpListItem)
@ -122,7 +120,7 @@ NS_IMETHODIMP JumpListLink::GetUriHash(nsACString& aUriHash)
if (!mURI)
return NS_ERROR_NOT_AVAILABLE;
return JumpListItem::HashURI(mCryptoHash, mURI, aUriHash);
return mozilla::widget::FaviconHelper::HashURI(mCryptoHash, mURI, aUriHash);
}
/* boolean compareHash(in nsIURI uri); */
@ -139,9 +137,9 @@ NS_IMETHODIMP JumpListLink::CompareHash(nsIURI *aUri, bool *aResult)
nsCAutoString hash1, hash2;
rv = JumpListItem::HashURI(mCryptoHash, mURI, hash1);
rv = mozilla::widget::FaviconHelper::HashURI(mCryptoHash, mURI, hash1);
NS_ENSURE_SUCCESS(rv, rv);
rv = JumpListItem::HashURI(mCryptoHash, aUri, hash2);
rv = mozilla::widget::FaviconHelper::HashURI(mCryptoHash, aUri, hash2);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = hash1.Equals(hash2);
@ -321,135 +319,6 @@ nsresult JumpListSeparator::GetSeparator(nsRefPtr<IShellLinkW>& aShellLink)
return NS_OK;
}
// Obtains the jump list 'ICO cache timeout in seconds' pref
static PRInt32 GetICOCacheSecondsTimeout() {
// Only obtain the setting at most once from the pref service.
// In the rare case that 2 threads call this at the same
// time it is no harm and we will simply obtain the pref twice.
// None of the taskbar list prefs are currently updated via a
// pref observer so I think this should suffice.
const PRInt32 kSecondsPerDay = 86400;
static bool alreadyObtained = false;
static PRInt32 icoReCacheSecondsTimeout = kSecondsPerDay;
if (alreadyObtained) {
return icoReCacheSecondsTimeout;
}
// Obtain the pref
const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
icoReCacheSecondsTimeout = Preferences::GetInt(PREF_ICOTIMEOUT,
kSecondsPerDay);
alreadyObtained = true;
return icoReCacheSecondsTimeout;
}
// (static) If the data is available, will return the path on disk where
// the favicon for page aFaviconPageURI is stored. If the favicon does not
// exist, or its cache is expired, this method will kick off an async request
// for the icon so that next time the method is called it will be available.
nsresult JumpListShortcut::ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
nsString &aICOFilePath,
nsCOMPtr<nsIThread> &aIOThread)
{
// Obtain the ICO file path
nsCOMPtr<nsIFile> icoFile;
nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile);
NS_ENSURE_SUCCESS(rv, rv);
// Check if the cached ICO file already exists
bool exists;
rv = icoFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
// Obtain the file's last modification date in seconds
PRInt64 fileModTime = LL_ZERO;
rv = icoFile->GetLastModifiedTime(&fileModTime);
fileModTime /= PR_MSEC_PER_SEC;
PRInt32 icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
PRInt64 nowTime = PR_Now() / PRInt64(PR_USEC_PER_SEC);
// If the last mod call failed or the icon is old then re-cache it
// This check is in case the favicon of a page changes
// the next time we try to build the jump list, the data will be available.
if (NS_FAILED(rv) ||
(nowTime - fileModTime) > icoReCacheSecondsTimeout) {
CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread);
return NS_ERROR_NOT_AVAILABLE;
}
} else {
// The file does not exist yet, obtain it async from the favicon service so that
// the next time we try to build the jump list it'll be available.
CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread);
return NS_ERROR_NOT_AVAILABLE;
}
// The icoFile is filled with a path that exists, get its path
rv = icoFile->GetPath(aICOFilePath);
return rv;
}
// (static) Obtains the ICO file for the favicon at page aFaviconPageURI
// If successful, the file path on disk is in the format:
// <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
nsresult JumpListShortcut::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> &aICOFile)
{
// Hash the input URI and replace any / with _
nsCAutoString inputURIHash;
nsCOMPtr<nsICryptoHash> cryptoHash;
nsresult rv = JumpListItem::HashURI(cryptoHash, aFaviconPageURI,
inputURIHash);
NS_ENSURE_SUCCESS(rv, rv);
char* cur = inputURIHash.BeginWriting();
char* end = inputURIHash.EndWriting();
for (; cur < end; ++cur) {
if ('/' == *cur) {
*cur = '_';
}
}
// Obtain the local profile directory and construct the output icon file path
rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = aICOFile->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
// Try to create the directory if it's not there yet
rv = aICOFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return rv;
}
// Append the icon extension
inputURIHash.Append(".ico");
rv = aICOFile->AppendNative(inputURIHash);
return rv;
}
// (static) Asynchronously creates a cached ICO file on disk for the favicon of
// page aFaviconPageURI and stores it to disk at the path of aICOFile.
nsresult
JumpListShortcut::CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> aICOFile,
nsCOMPtr<nsIThread> &aIOThread)
{
// Obtain the favicon service and get the favicon for the specified page
nsCOMPtr<mozIAsyncFavicons> favIconSvc(
do_GetService("@mozilla.org/browser/favicon-service;1"));
NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
nsCOMPtr<nsIFaviconDataCallback> callback =
new AsyncFaviconDataReady(aFaviconPageURI, aIOThread);
favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback);
return NS_OK;
}
// (static) Creates a ShellLink that encapsulate a shortcut to local apps.
nsresult JumpListShortcut::GetShellLink(nsCOMPtr<nsIJumpListItem>& item,
nsRefPtr<IShellLinkW>& aShellLink,
@ -544,7 +413,10 @@ nsresult JumpListShortcut::GetShellLink(nsCOMPtr<nsIJumpListItem>& item,
if (useUriIcon) {
nsString icoFilePath;
rv = ObtainCachedIconFile(iconUri, icoFilePath, aIOThread);
rv = mozilla::widget::FaviconHelper::ObtainCachedIconFile(iconUri,
icoFilePath,
aIOThread,
false);
if (NS_SUCCEEDED(rv)) {
// Always use the first icon in the ICO file
// our encoded icon only has 1 resource
@ -577,7 +449,7 @@ static nsresult IsPathInOurIconCache(nsCOMPtr<nsIJumpListShortcut>& aShortcut,
nsCOMPtr<nsIFile> jumpListCache;
nsresult rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCache));
NS_ENSURE_SUCCESS(rv, rv);
rv = jumpListCache->AppendNative(nsDependentCString(JumpListItem::kJumpListCacheDir));
rv = jumpListCache->AppendNative(nsDependentCString(FaviconHelper::kJumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString jumpListCachePath;
rv = jumpListCache->GetPath(jumpListCachePath);
@ -772,33 +644,6 @@ bool JumpListShortcut::ExecutableExists(nsCOMPtr<nsILocalHandlerApp>& handlerApp
return false;
}
// (static) Helper method which will hash a URI
nsresult JumpListItem::HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash,
nsIURI *aUri, nsACString& aUriHash)
{
if (!aUri)
return NS_ERROR_INVALID_ARG;
nsCAutoString spec;
nsresult rv = aUri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
if (!aCryptoHash) {
aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = aCryptoHash->Init(nsICryptoHash::MD5);
NS_ENSURE_SUCCESS(rv, rv);
rv = aCryptoHash->Update(reinterpret_cast<const PRUint8*>(spec.BeginReading()),
spec.Length());
NS_ENSURE_SUCCESS(rv, rv);
rv = aCryptoHash->Finish(true, aUriHash);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
} // namespace widget
} // namespace mozilla

View File

@ -48,8 +48,6 @@ protected:
short Type() { return mItemType; }
short mItemType;
static nsresult HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash,
nsIURI *aUri, nsACString& aUriHash);
};
class JumpListSeparator : public JumpListItem, public nsIJumpListSeparator

View File

@ -11,9 +11,33 @@
#include "nsIDOMMouseEvent.h"
#include "mozilla/Preferences.h"
#include "nsString.h"
#include "nsDirectoryServiceUtils.h"
#include "imgIContainer.h"
#include "imgITools.h"
#include "nsStringStream.h"
#include "nsNetUtil.h"
#include "mozIAsyncFavicons.h"
#include "nsIIconURI.h"
#include "nsIDownloader.h"
#include "nsINetUtil.h"
#include "nsIChannel.h"
#include "nsIObserver.h"
namespace mozilla {
namespace widget {
NS_IMPL_ISUPPORTS1(myDownloadObserver, nsIDownloadObserver)
NS_IMPL_ISUPPORTS1(AsyncFaviconDataReady, nsIFaviconDataCallback)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncWriteIconToDisk, nsIRunnable)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteIconFromDisk, nsIRunnable)
NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
// SHCreateItemFromParsingName is only available on vista and up.
WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = nsnull;
@ -367,6 +391,471 @@ WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc,
return sCreateItemFromParsingName(pszPath, pbc, riid, ppv);
}
/************************************************************************/
/* Constructs as AsyncFaviconDataReady Object
/* @param aIOThread : the thread which performs the action
/* @param aURLShortcut : Differentiates between (false)Jumplistcache and (true)Shortcutcache
/************************************************************************/
AsyncFaviconDataReady::AsyncFaviconDataReady(nsIURI *aNewURI,
nsCOMPtr<nsIThread> &aIOThread,
const bool aURLShortcut):
mNewURI(aNewURI),
mIOThread(aIOThread),
mURLShortcut(aURLShortcut)
{
}
NS_IMETHODIMP
myDownloadObserver::OnDownloadComplete(nsIDownloader *downloader,
nsIRequest *request,
nsISupports *ctxt,
nsresult status,
nsIFile *result)
{
return NS_OK;
}
nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void)
{
if (!mURLShortcut) {
return NS_OK;
}
nsCOMPtr<nsIFile> icoFile;
nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> mozIconURI;
rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<myDownloadObserver> downloadObserver = new myDownloadObserver;
nsCOMPtr<nsIStreamListener> listener;
rv = NS_NewDownloader(getter_AddRefs(listener), downloadObserver, icoFile);
NS_ENSURE_SUCCESS(rv, rv);
channel->AsyncOpen(listener, NULL);
return NS_OK;
}
NS_IMETHODIMP
AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI,
PRUint32 aDataLen,
const PRUint8 *aData,
const nsACString &aMimeType)
{
if (!aDataLen || !aData) {
if (mURLShortcut) {
OnFaviconDataNotAvailable();
}
return NS_OK;
}
nsCOMPtr<nsIFile> icoFile;
nsresult rv = FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
rv = icoFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
// Allocate a new buffer that we own and can use out of line in
// another thread. Copy the favicon raw data into it.
const fallible_t fallible = fallible_t();
PRUint8 *data = new (fallible) PRUint8[aDataLen];
if (!data) {
return NS_ERROR_OUT_OF_MEMORY;
}
memcpy(data, aData, aDataLen);
//AsyncWriteIconToDisk takes ownership of the heap allocated buffer.
nsCOMPtr<nsIRunnable> event = new AsyncWriteIconToDisk(path, aMimeType,
data,
aDataLen,
mURLShortcut);
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
// Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in
AsyncWriteIconToDisk::AsyncWriteIconToDisk(const nsAString &aIconPath,
const nsACString &aMimeTypeOfInputData,
PRUint8 *aBuffer,
PRUint32 aBufferLength,
const bool aURLShortcut):
mIconPath(aIconPath),
mMimeTypeOfInputData(aMimeTypeOfInputData),
mBuffer(aBuffer),
mBufferLength(aBufferLength),
mURLShortcut(aURLShortcut)
{
}
NS_IMETHODIMP AsyncWriteIconToDisk::Run()
{
NS_PRECONDITION(!NS_IsMainThread(), "Should not be called on the main thread.");
// Convert the obtained favicon data to an input stream
nsCOMPtr<nsIInputStream> stream;
nsresult rv =
NS_NewByteInputStream(getter_AddRefs(stream),
reinterpret_cast<const char*>(mBuffer.get()),
mBufferLength,
NS_ASSIGNMENT_DEPEND);
NS_ENSURE_SUCCESS(rv, rv);
// Decode the image from the format it was returned to us in (probably PNG)
nsCOMPtr<imgIContainer> container;
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
rv = imgtool->DecodeImageData(stream, mMimeTypeOfInputData,
getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
// Get the recommended icon width and height, or if failure to obtain
// these settings, fall back to 16x16 ICOs. These values can be different
// if the user has a different DPI setting other than 100%.
// Windows would scale the 16x16 icon themselves, but it's better
// we let our ICO encoder do it.
nsCOMPtr<nsIInputStream> iconStream;
if (!mURLShortcut) {
PRInt32 systemIconWidth = GetSystemMetrics(SM_CXSMICON);
PRInt32 systemIconHeight = GetSystemMetrics(SM_CYSMICON);
if ((systemIconWidth == 0 || systemIconHeight == 0)) {
systemIconWidth = 16;
systemIconHeight = 16;
}
// Scale the image to the needed size and in ICO format
mMimeTypeOfInputData.AssignLiteral("image/vnd.microsoft.icon");
rv = imgtool->EncodeScaledImage(container, mMimeTypeOfInputData,
systemIconWidth,
systemIconHeight,
EmptyString(),
getter_AddRefs(iconStream));
} else {
mMimeTypeOfInputData.AssignLiteral("image/vnd.microsoft.icon");
rv = imgtool->EncodeImage(container,
mMimeTypeOfInputData,
NS_LITERAL_STRING("format=bmp;bpp=32"),
getter_AddRefs(iconStream));
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> icoFile
= do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
rv = icoFile->InitWithPath(mIconPath);
// Setup the output stream for the ICO file on disk
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), icoFile);
NS_ENSURE_SUCCESS(rv, rv);
// Obtain the ICO buffer size from the re-encoded ICO stream
PRUint32 bufSize;
rv = iconStream->Available(&bufSize);
NS_ENSURE_SUCCESS(rv, rv);
// Setup a buffered output stream from the stream object
// so that we can simply use WriteFrom with the stream object
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream, bufSize);
NS_ENSURE_SUCCESS(rv, rv);
// Write out the icon stream to disk and make sure we wrote everything
PRUint32 wrote;
rv = bufferedOutputStream->WriteFrom(iconStream, bufSize, &wrote);
NS_ASSERTION(bufSize == wrote,
"Icon wrote size should be equal to requested write size");
// Cleanup
bufferedOutputStream->Close();
outputStream->Close();
if (mURLShortcut) {
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0);
}
return rv;
}
AsyncWriteIconToDisk::~AsyncWriteIconToDisk()
{
}
AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath)
: mIconPath(aIconPath)
{
}
NS_IMETHODIMP AsyncDeleteIconFromDisk::Run()
{
// Construct the parent path of the passed in path
nsCOMPtr<nsIFile> icoFile = do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
nsresult rv = icoFile->InitWithPath(mIconPath);
NS_ENSURE_SUCCESS(rv, rv);
// Check if the cached ICO file exists
bool exists;
rv = icoFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
// Check that we aren't deleting some arbitrary file that is not an icon
if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) {
// Check if the cached ICO file exists
bool exists;
if (NS_FAILED(icoFile->Exists(&exists)) || !exists)
return NS_ERROR_FAILURE;
// We found an ICO file that exists, so we should remove it
icoFile->Remove(false);
}
return NS_OK;
}
AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk()
{
}
AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk()
{
}
NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run()
{
// Construct the path of our jump list cache
nsCOMPtr<nsIFile> jumpListCacheDir;
nsresult rv = NS_GetSpecialDirectory("ProfLDS",
getter_AddRefs(jumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
rv = jumpListCacheDir->AppendNative(
nsDependentCString(FaviconHelper::kJumpListCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISimpleEnumerator> entries;
rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
// Loop through each directory entry and remove all ICO files found
do {
bool hasMore = false;
if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore)
break;
nsCOMPtr<nsISupports> supp;
if (NS_FAILED(entries->GetNext(getter_AddRefs(supp))))
break;
nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
nsAutoString path;
if (NS_FAILED(currFile->GetPath(path)))
continue;
PRInt32 len = path.Length();
if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
// Check if the cached ICO file exists
bool exists;
if (NS_FAILED(currFile->Exists(&exists)) || !exists)
continue;
// We found an ICO file that exists, so we should remove it
currFile->Remove(false);
}
} while(true);
return NS_OK;
}
AsyncDeleteAllFaviconsFromDisk::~AsyncDeleteAllFaviconsFromDisk()
{
}
/*
* (static) If the data is available, will return the path on disk where
* the favicon for page aFaviconPageURI is stored. If the favicon does not
* exist, or its cache is expired, this method will kick off an async request
* for the icon so that next time the method is called it will be available.
* @param aFaviconPageURI The URI of the page to obtain
* @param aICOFilePath The path of the icon file
* @param aIOThread The thread to perform the Fetch on
* @param aURLShortcut to distinguish between jumplistcache(false) and shortcutcache(true)
*/
nsresult FaviconHelper::ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
nsString &aICOFilePath,
nsCOMPtr<nsIThread> &aIOThread,
bool aURLShortcut)
{
// Obtain the ICO file path
nsCOMPtr<nsIFile> icoFile;
nsresult rv = GetOutputIconPath(aFaviconPageURI, icoFile, aURLShortcut);
NS_ENSURE_SUCCESS(rv, rv);
// Check if the cached ICO file already exists
bool exists;
rv = icoFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
// Obtain the file's last modification date in seconds
PRInt64 fileModTime = LL_ZERO;
rv = icoFile->GetLastModifiedTime(&fileModTime);
fileModTime /= PR_MSEC_PER_SEC;
PRInt32 icoReCacheSecondsTimeout = GetICOCacheSecondsTimeout();
PRInt64 nowTime = PR_Now() / PRInt64(PR_USEC_PER_SEC);
// If the last mod call failed or the icon is old then re-cache it
// This check is in case the favicon of a page changes
// the next time we try to build the jump list, the data will be available.
if (NS_FAILED(rv) ||
(nowTime - fileModTime) > icoReCacheSecondsTimeout) {
CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut);
return NS_ERROR_NOT_AVAILABLE;
}
} else {
// The file does not exist yet, obtain it async from the favicon service so that
// the next time we try to build the jump list it'll be available.
CacheIconFileFromFaviconURIAsync(aFaviconPageURI, icoFile, aIOThread, aURLShortcut);
return NS_ERROR_NOT_AVAILABLE;
}
// The icoFile is filled with a path that exists, get its path
rv = icoFile->GetPath(aICOFilePath);
return rv;
}
nsresult FaviconHelper::HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash,
nsIURI *aUri,
nsACString& aUriHash)
{
if (!aUri)
return NS_ERROR_INVALID_ARG;
nsCAutoString spec;
nsresult rv = aUri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
if (!aCryptoHash) {
aCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = aCryptoHash->Init(nsICryptoHash::MD5);
NS_ENSURE_SUCCESS(rv, rv);
rv = aCryptoHash->Update(reinterpret_cast<const PRUint8*>(spec.BeginReading()),
spec.Length());
NS_ENSURE_SUCCESS(rv, rv);
rv = aCryptoHash->Finish(true, aUriHash);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// (static) Obtains the ICO file for the favicon at page aFaviconPageURI
// If successful, the file path on disk is in the format:
// <ProfLDS>\jumpListCache\<hash(aFaviconPageURI)>.ico
nsresult FaviconHelper::GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> &aICOFile,
bool aURLShortcut)
{
// Hash the input URI and replace any / with _
nsCAutoString inputURIHash;
nsCOMPtr<nsICryptoHash> cryptoHash;
nsresult rv = HashURI(cryptoHash, aFaviconPageURI,
inputURIHash);
NS_ENSURE_SUCCESS(rv, rv);
char* cur = inputURIHash.BeginWriting();
char* end = inputURIHash.EndWriting();
for (; cur < end; ++cur) {
if ('/' == *cur) {
*cur = '_';
}
}
// Obtain the local profile directory and construct the output icon file path
rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(aICOFile));
NS_ENSURE_SUCCESS(rv, rv);
if (!aURLShortcut)
rv = aICOFile->AppendNative(nsDependentCString(kJumpListCacheDir));
else
rv = aICOFile->AppendNative(nsDependentCString(kShortcutCacheDir));
NS_ENSURE_SUCCESS(rv, rv);
// Try to create the directory if it's not there yet
rv = aICOFile->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return rv;
}
// Append the icon extension
inputURIHash.Append(".ico");
rv = aICOFile->AppendNative(inputURIHash);
return rv;
}
// (static) Asynchronously creates a cached ICO file on disk for the favicon of
// page aFaviconPageURI and stores it to disk at the path of aICOFile.
nsresult
FaviconHelper::CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> aICOFile,
nsCOMPtr<nsIThread> &aIOThread,
bool aURLShortcut)
{
// Obtain the favicon service and get the favicon for the specified page
nsCOMPtr<mozIAsyncFavicons> favIconSvc(
do_GetService("@mozilla.org/browser/favicon-service;1"));
NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
nsCOMPtr<nsIFaviconDataCallback> callback =
new mozilla::widget::AsyncFaviconDataReady(aFaviconPageURI,
aIOThread,
aURLShortcut);
favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback);
return NS_OK;
}
// Obtains the jump list 'ICO cache timeout in seconds' pref
PRInt32 FaviconHelper::GetICOCacheSecondsTimeout() {
// Only obtain the setting at most once from the pref service.
// In the rare case that 2 threads call this at the same
// time it is no harm and we will simply obtain the pref twice.
// None of the taskbar list prefs are currently updated via a
// pref observer so I think this should suffice.
const PRInt32 kSecondsPerDay = 86400;
static bool alreadyObtained = false;
static PRInt32 icoReCacheSecondsTimeout = kSecondsPerDay;
if (alreadyObtained) {
return icoReCacheSecondsTimeout;
}
// Obtain the pref
const char PREF_ICOTIMEOUT[] = "browser.taskbar.lists.icoTimeoutInSeconds";
icoReCacheSecondsTimeout = Preferences::GetInt(PREF_ICOTIMEOUT,
kSecondsPerDay);
alreadyObtained = true;
return icoReCacheSecondsTimeout;
}
/* static */
bool
WinUtils::GetShellItemPath(IShellItem* aItem,

View File

@ -12,11 +12,24 @@
#include "nsAutoPtr.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsICryptoHash.h"
#include "nsIFaviconService.h"
#include "nsIDownloader.h"
class nsWindow;
namespace mozilla {
namespace widget {
class myDownloadObserver: public nsIDownloadObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOWNLOADOBSERVER
};
class WinUtils {
public:
enum WinVersion {
@ -203,6 +216,99 @@ private:
static bool VistaCreateItemFromParsingNameInit();
};
class AsyncFaviconDataReady : public nsIFaviconDataCallback
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONDATACALLBACK
AsyncFaviconDataReady(nsIURI *aNewURI,
nsCOMPtr<nsIThread> &aIOThread,
const bool aURLShortcut);
nsresult OnFaviconDataNotAvailable(void);
private:
nsCOMPtr<nsIURI> mNewURI;
nsCOMPtr<nsIThread> mIOThread;
const bool mURLShortcut;
};
/**
* Asynchronously tries add the list to the build
*/
class AsyncWriteIconToDisk : public nsIRunnable
{
public:
const bool mURLShortcut;
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
// Warning: AsyncWriteIconToDisk assumes ownership of the aData buffer passed in
AsyncWriteIconToDisk(const nsAString &aIconPath,
const nsACString &aMimeTypeOfInputData,
PRUint8 *aData,
PRUint32 aDataLen,
const bool aURLShortcut);
virtual ~AsyncWriteIconToDisk();
private:
nsAutoString mIconPath;
nsCAutoString mMimeTypeOfInputData;
nsAutoArrayPtr<PRUint8> mBuffer;
PRUint32 mBufferLength;
};
class AsyncDeleteIconFromDisk : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
AsyncDeleteIconFromDisk(const nsAString &aIconPath);
virtual ~AsyncDeleteIconFromDisk();
private:
nsAutoString mIconPath;
};
class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
AsyncDeleteAllFaviconsFromDisk();
virtual ~AsyncDeleteAllFaviconsFromDisk();
};
class FaviconHelper
{
public:
static const char kJumpListCacheDir[];
static const char kShortcutCacheDir[];
static nsresult ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
nsString &aICOFilePath,
nsCOMPtr<nsIThread> &aIOThread,
bool aURLShortcut);
static nsresult HashURI(nsCOMPtr<nsICryptoHash> &aCryptoHash,
nsIURI *aUri,
nsACString& aUriHash);
static nsresult GetOutputIconPath(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> &aICOFile,
bool aURLShortcut);
static nsresult
CacheIconFileFromFaviconURIAsync(nsCOMPtr<nsIURI> aFaviconPageURI,
nsCOMPtr<nsIFile> aICOFile,
nsCOMPtr<nsIThread> &aIOThread,
bool aURLShortcut);
static PRInt32 GetICOCacheSecondsTimeout();
};
} // namespace widget
} // namespace mozilla

View File

@ -30,8 +30,14 @@
#include "nsITimer.h"
#include "nsThreadUtils.h"
#include "WinUtils.h"
#include "mozilla/LazyIdleThread.h"
using namespace mozilla;
#define DEFAULT_THREAD_TIMEOUT_MS 30000
NS_IMPL_ISUPPORTS1(nsDataObj::CStream, nsIStreamListener)
//-----------------------------------------------------------------------------
@ -345,6 +351,9 @@ nsDataObj::nsDataObj(nsIURI * uri)
: m_cRef(0), mTransferable(nsnull),
mIsAsyncMode(FALSE), mIsInOperation(FALSE)
{
mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
NS_LITERAL_CSTRING("nsDataObj"),
LazyIdleThread::ManualShutdown);
m_enumFE = new CEnumFormatEtc();
m_enumFE->AddRef();
@ -1088,10 +1097,27 @@ nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
// will need to change if we ever support iDNS
nsCAutoString asciiUrl;
LossyCopyUTF16toASCII(url, asciiUrl);
static const char* shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
static const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s in the len
const int totalLen = formatLen + asciiUrl.Length(); // we don't want a null character on the end
nsCOMPtr<nsIFile> icoFile;
nsCOMPtr<nsIURI> aUri;
NS_NewURI(getter_AddRefs(aUri), url);
nsAutoString aUriHash;
mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true);
nsresult rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true);
NS_ENSURE_SUCCESS(rv, rv);
nsCString path;
rv = icoFile->GetNativePath(path);
NS_ENSURE_SUCCESS(rv, rv);
static char* shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"
"IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
"IconIndex=0\r\n";
static const int formatLen = strlen(shortcutFormatStr) - 2*2; // don't include %s (2 times) in the len
const int totalLen = formatLen + asciiUrl.Length()
+ path.Length(); // we don't want a null character on the end
// create a global memory area and build up the file contents w/in it
HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
@ -1108,7 +1134,7 @@ nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
// terminate strings which reach the maximum size of the buffer. Since we know that the
// formatted length here is totalLen, this call to _snprintf will format the string into
// the buffer without appending the null character.
_snprintf( contents, totalLen, shortcutFormatStr, asciiUrl.get() );
_snprintf( contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get() );
::GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;

View File

@ -19,6 +19,8 @@
#include "nsCOMArray.h"
#include "nsITimer.h"
class nsIThread;
// The SDK shipping with VC11 has renamed IAsyncOperation to
// IDataObjectAsyncCapability. We try to detect this, and rename this in our
// code too to make sure that we pick the correct name when building.
@ -85,6 +87,10 @@ class nsITransferable;
class nsDataObj : public IDataObject,
public IAsyncOperation
{
protected:
nsCOMPtr<nsIThread> mIOThread;
public: // construction, destruction
nsDataObj(nsIURI *uri = nsnull);
virtual ~nsDataObj();