add the Carbon icon decoder back, alongside the Cocoa icon decoder. Use Carbon decoder for Carbon builds, Cocoa for Cocoa builds. We can't use the Cocoa API from Carbon due to runtime conflicts (that aren't completely resolved by calling NSApplicationLoad). Also, removing call to NSApplicationLoad. b=339389 r=mento sr=stuart

This commit is contained in:
joshmoz%gmail.com 2006-05-30 15:54:28 +00:00
parent 22cab1ed59
commit c35313a483
4 changed files with 678 additions and 10 deletions

View File

@ -122,7 +122,7 @@ ifeq ($(OS_ARCH),WINNT)
OS_LIBS += $(call EXPAND_LIBNAME,shell32 gdi32 comctl32)
endif
ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
OS_LIBS += -framework Cocoa
endif

View File

@ -53,7 +53,11 @@ REQUIRES = xpcom \
exthandler \
$(NULL)
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CMMSRCS = nsIconChannel.mm
else
CPPSRCS = nsIconChannel.cpp
endif
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1

View File

@ -0,0 +1,673 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Brian Ryner.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Scott MacGregor <mscott@mozilla.org>
* Robert John Churchill <rjc@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIconChannel.h"
#include "nsIIconURI.h"
#include "nsIServiceManager.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsXPIDLString.h"
#include "nsMimeTypes.h"
#include "nsMemory.h"
#include "nsIStringStream.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsIMIMEService.h"
#include "nsCExternalHandlerService.h"
#include "plstr.h"
#include "nsILocalFileMac.h"
#include "nsIFileURL.h"
#include "nsInt64.h"
#include <Icons.h>
#include <Files.h>
#include <Folders.h>
#include <Icons.h>
#include <Quickdraw.h>
#include <MacMemory.h>
// nsIconChannel methods
nsIconChannel::nsIconChannel()
{
}
nsIconChannel::~nsIconChannel()
{}
NS_IMPL_THREADSAFE_ISUPPORTS4(nsIconChannel,
nsIChannel,
nsIRequest,
nsIRequestObserver,
nsIStreamListener)
nsresult nsIconChannel::Init(nsIURI* uri)
{
NS_ASSERTION(uri, "no uri");
mUrl = uri;
nsresult rv;
mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// nsIRequest methods:
NS_IMETHODIMP nsIconChannel::GetName(nsACString &result)
{
return mUrl->GetSpec(result);
}
NS_IMETHODIMP nsIconChannel::IsPending(PRBool *result)
{
return mPump->IsPending(result);
}
NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status)
{
return mPump->GetStatus(status);
}
NS_IMETHODIMP nsIconChannel::Cancel(nsresult status)
{
return mPump->Cancel(status);
}
NS_IMETHODIMP nsIconChannel::Suspend(void)
{
return mPump->Suspend();
}
NS_IMETHODIMP nsIconChannel::Resume(void)
{
return mPump->Resume();
}
// nsIRequestObserver methods
NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
if (mListener)
return mListener->OnStartRequest(this, aContext);
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
{
if (mListener) {
mListener->OnStopRequest(this, aContext, aStatus);
mListener = nsnull;
}
// Remove from load group
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, aStatus);
return NS_OK;
}
// nsIStreamListener methods
NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aStream,
PRUint32 aOffset,
PRUint32 aCount)
{
if (mListener)
return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIChannel methods:
NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI)
{
*aURI = mOriginalURI ? mOriginalURI : mUrl;
NS_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI)
{
mOriginalURI = aURI;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI)
{
*aURI = mUrl;
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsIconChannel::Open(nsIInputStream **_retval)
{
return MakeInputStream(_retval, PR_FALSE);
}
nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, PRUint32 * aDesiredImageSize, nsACString &aContentType, nsACString &aFileExtension)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
NS_ENSURE_SUCCESS(rv, rv);
iconURI->GetImageSize(aDesiredImageSize);
iconURI->GetContentType(aContentType);
iconURI->GetFileExtension(aFileExtension);
nsCOMPtr<nsIURI> fileURI;
rv = iconURI->GetIconFile(getter_AddRefs(fileURI));
if (NS_FAILED(rv) || !fileURI) return NS_OK;
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv);
if (NS_FAILED(rv) || !fileURL) return NS_OK;
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file));
if (NS_FAILED(rv) || !file) return NS_OK;
nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(file, &rv));
if (NS_FAILED(rv) || !localFileMac) return NS_OK;
*aLocalFile = file;
NS_IF_ADDREF(*aLocalFile);
return NS_OK;
}
nsresult
GetLockedIconData(IconFamilyHandle iconFamilyH, PRUint32 iconType,
Handle iconDataH, PRUint32 *iconDataSize)
{
*iconDataSize = 0;
if (::GetIconFamilyData(iconFamilyH, iconType, iconDataH) != noErr)
return(NS_ERROR_FAILURE);
*iconDataSize = (PRUint32)::GetHandleSize(iconDataH);
if (*iconDataSize > 0)
{
::HLock(iconDataH);
}
return(NS_OK);
}
nsresult
GetLockedIcons(IconFamilyHandle icnFamily, PRUint32 desiredImageSize,
Handle iconH, PRUint32 *dataCount, PRBool *isIndexedData,
Handle iconMaskH, PRUint32 *maskCount)
{
// note: this code could be improved by:
//
// o adding support for icon scaling, i.e. if we want a
// 32x32 icon but can only get a 16x16 icon (or vice versa),
// scale the pixels appropriately
//
// o adding support for Mac OS X "huge" 128x128 icons with alpha
// which is also tricky as the alpha data defines the mask for the icon
*dataCount = *maskCount = 0L;
// make sure icon/mask handles are unlocked
// so that they can move in memory
HUnlock(iconH);
HUnlock(iconMaskH);
// Note: Always try and get 32bit non-indexed icons first
// so that we don't have to worry about color palette issues
nsresult rv = GetLockedIconData(icnFamily, (desiredImageSize > 16) ?
kLarge32BitData : kSmall32BitData,
iconH, dataCount);
if (NS_SUCCEEDED(rv) && (*dataCount > 0))
{
*isIndexedData = PR_FALSE;
}
else
{
// if couldn't get a 32bit non-indexed icon,
// then try getting an 8-bit icon
rv = GetLockedIconData(icnFamily, (desiredImageSize > 16) ?
kLarge8BitData : kSmall8BitData,
iconH, dataCount);
if (NS_SUCCEEDED(rv) && (*dataCount > 0))
{
*isIndexedData = PR_TRUE;
}
}
// if we have an icon, try getting a mask too
if (NS_SUCCEEDED(rv) && (*dataCount > 0))
{
// moz-icons are RGB_A1, so get 1-bit icon mask
rv = GetLockedIconData(icnFamily, (desiredImageSize > 16) ?
kLarge1BitMask : kSmall1BitMask,
iconMaskH, maskCount);
if (NS_FAILED(rv) || (*maskCount == 0))
{
// if we can't get a mask, the file's BNDL might be
// messed up, etc. Let's just fake a 1-bit mask
// which will blit the entire icon... its not perfect,
// but its better than no icon at all
*maskCount = (desiredImageSize > 16) ? 256 : 64;
rv = NS_OK;
}
}
return(rv);
}
NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
nsCOMPtr<nsIInputStream> inStream;
nsresult rv = MakeInputStream(getter_AddRefs(inStream), PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
// Init our stream pump
rv = mPump->Init(inStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = mPump->AsyncRead(this, ctxt);
if (NS_SUCCEEDED(rv))
{
// Store our real listener
mListener = aListener;
// Add ourself to the load group, if available
if (mLoadGroup)
mLoadGroup->AddRequest(this, nsnull);
}
return rv;
}
nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, PRBool nonBlocking)
{
nsXPIDLCString contentType;
nsCAutoString fileExt;
nsCOMPtr<nsIFile> fileloc; // file we want an icon for
PRUint32 desiredImageSize;
nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(fileloc), &desiredImageSize, contentType, fileExt);
NS_ENSURE_SUCCESS(rv, rv);
// ensure that we DO NOT resolve aliases, very important for file views
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(fileloc);
if (localFile)
localFile->SetFollowLinks(PR_FALSE);
PRBool fileExists = PR_FALSE;
if (fileloc)
localFile->Exists(&fileExists);
IconRef icnRef = nsnull;
if (fileExists)
{
// if the file exists, first try getting icons via Icon Services
nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(fileloc, &rv));
NS_ENSURE_SUCCESS(rv, rv);
FSSpec spec;
if (NS_FAILED(localFileMac->GetFSSpec(&spec)))
return NS_ERROR_FAILURE;
SInt16 label;
if (::GetIconRefFromFile (&spec, &icnRef, &label) != noErr)
return NS_ERROR_FAILURE;
}
// note: once we have an IconRef,
// we MUST release it before exiting this method!
// start with zero-sized icons which ::GetIconFamilyData will resize
PRUint32 dataCount = 0L, maskCount = 0L;
Handle iconH = ::NewHandle(dataCount);
Handle iconMaskH = ::NewHandle(maskCount);
if (!iconH || !iconMaskH)
{
// sigh; REALLY low-mem, bail
if (iconH) ::DisposeHandle(iconH);
if (iconMaskH) ::DisposeHandle(iconMaskH);
if (fileExists) ::ReleaseIconRef(icnRef);
return(NS_ERROR_OUT_OF_MEMORY);
}
PRUint8 *iconBitmapData = nsnull, *maskBitmapData = nsnull;
PRBool isIndexedData = PR_FALSE;
IconFamilyHandle icnFamily;
if (fileExists && (::IconRefToIconFamily(icnRef,
kSelectorAllAvailableData, &icnFamily) == noErr))
{
GetLockedIcons(icnFamily, desiredImageSize, iconH, &dataCount,
&isIndexedData, iconMaskH, &maskCount);
if (dataCount > 0)
{
iconBitmapData = (PRUint8 *)*iconH;
if (maskCount > 0) maskBitmapData = (PRUint8 *)*iconMaskH;
}
::DisposeHandle((Handle)icnFamily);
}
::ReleaseIconRef(icnRef);
icnRef = nsnull;
if (!dataCount)
{
// if file didn't exist, or couldn't get an appropriate
// icon resource, then try again by mimetype mapping
nsCOMPtr<nsIMIMEService> mimeService (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// if we were given an explicit content type, use it....
nsCOMPtr<nsIMIMEInfo> mimeInfo;
if (mimeService && (!contentType.IsEmpty() || !fileExt.IsEmpty()))
{
mimeService->GetFromTypeAndExtension(contentType,
fileExt,
getter_AddRefs(mimeInfo));
}
// if we don't have enough info to fetch an application icon, bail
if (!mimeInfo)
{
if (iconH) ::DisposeHandle(iconH);
if (iconMaskH) ::DisposeHandle(iconMaskH);
return NS_ERROR_FAILURE;
}
// get the mac creator and file type for this mime object
PRUint32 macType;
PRUint32 macCreator;
mimeInfo->GetMacType(&macType);
mimeInfo->GetMacCreator(&macCreator);
if (::GetIconRef(kOnSystemDisk, macCreator, macType, &icnRef) != noErr)
{
if (iconH) ::DisposeHandle(iconH);
if (iconMaskH) ::DisposeHandle(iconMaskH);
return(NS_ERROR_FAILURE);
}
if (::IconRefToIconFamily(icnRef, kSelectorAllAvailableData, &icnFamily) == noErr)
{
GetLockedIcons(icnFamily, desiredImageSize, iconH, &dataCount,
&isIndexedData, iconMaskH, &maskCount);
if (dataCount > 0)
{
iconBitmapData = (PRUint8 *)*iconH;
if (maskCount > 0) maskBitmapData = (PRUint8 *)*iconMaskH;
}
::DisposeHandle((Handle)icnFamily);
}
::ReleaseIconRef(icnRef);
icnRef = nsnull;
}
// note: we must have icon data, but it is OK to not
// have maskBitmapData (we fake a mask in that case)
if (!iconBitmapData)
{
if (iconH) DisposeHandle(iconH);
if (iconMaskH) DisposeHandle(iconMaskH);
return(NS_ERROR_FAILURE);
}
PRUint32 numPixelsInRow = (desiredImageSize > 16) ? 32 : 16;
PRUint8 *bitmapData = (PRUint8 *)iconBitmapData;
nsCString iconBuffer;
iconBuffer.Assign((char) numPixelsInRow);
iconBuffer.Append((char) numPixelsInRow);
iconBuffer.Append((char) 1); // alpha bits per pixel
CTabHandle cTabHandle = nsnull;
CTabPtr colTable = nsnull;
if (isIndexedData)
{
// only need the CTable if we have an palette-indexed icon
cTabHandle = ::GetCTable(72);
if (!cTabHandle)
{
if (iconH) ::DisposeHandle(iconH);
if (iconMaskH) ::DisposeHandle(iconMaskH);
return NS_ERROR_FAILURE;
}
::HLock((Handle) cTabHandle);
colTable = *cTabHandle;
}
RGBColor rgbCol;
PRUint8 redValue, greenValue, blueValue;
PRUint32 index = 0L;
while (index < dataCount)
{
if (!isIndexedData)
{
// 32 bit icon data
iconBuffer.Append((char) bitmapData[index++]);
iconBuffer.Append((char) bitmapData[index++]);
iconBuffer.Append((char) bitmapData[index++]);
iconBuffer.Append((char) bitmapData[index++]);
}
else
{
// each byte in bitmapData needs to be converted from an 8 bit system color into
// 24 bit RGB data which our special icon image decoder can understand.
ColorSpec colSpec = colTable->ctTable[ bitmapData[index]];
rgbCol = colSpec.rgb;
redValue = rgbCol.red & 0xff;
greenValue = rgbCol.green & 0xff;
blueValue = rgbCol.blue & 0xff;
iconBuffer.Append((char) 0); // alpha channel byte
iconBuffer.Append((char) redValue);
iconBuffer.Append((char) greenValue);
iconBuffer.Append((char) blueValue);
index++;
}
}
if (cTabHandle)
{
::HUnlock((Handle) cTabHandle);
::DisposeCTable(cTabHandle);
}
::DisposeHandle(iconH);
iconH = nsnull;
bitmapData = (PRUint8 *)maskBitmapData;
if (maskBitmapData)
{
// skip over ICN# data to get to mask
// which is half way into data
index = maskCount/2;
while (index < maskCount)
{
iconBuffer.Append((char) bitmapData[index]);
iconBuffer.Append((char) bitmapData[index + 1]);
if (numPixelsInRow == 32)
{
iconBuffer.Append((char) bitmapData[index + 2]);
iconBuffer.Append((char) bitmapData[index + 3]);
index += 4;
}
else
{
iconBuffer.Append((char) 255); // 2 bytes of padding
iconBuffer.Append((char) 255);
index += 2;
}
}
}
else
{
index = 0L;
while (index++ < maskCount)
{
// edgecase: without a mask, just blit entire icon
iconBuffer.Append((char) 255);
}
}
if (iconMaskH)
{
::DisposeHandle(iconMaskH);
iconMaskH = nsnull;
}
// Now, create a pipe and stuff our data into it
nsCOMPtr<nsIInputStream> inStream;
nsCOMPtr<nsIOutputStream> outStream;
PRUint32 iconSize = iconBuffer.Length();
rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream), iconSize, iconSize, nonBlocking);
if (NS_SUCCEEDED(rv))
{
PRUint32 written;
rv = outStream->Write(iconBuffer.get(), iconSize, &written);
if (NS_SUCCEEDED(rv))
NS_IF_ADDREF(*_retval = inStream);
}
// Drop notification callbacks to prevent cycles.
mCallbacks = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::GetLoadFlags(PRUint32 *aLoadAttributes)
{
return mPump->GetLoadFlags(aLoadAttributes);
}
NS_IMETHODIMP nsIconChannel::SetLoadFlags(PRUint32 aLoadAttributes)
{
return mPump->SetLoadFlags(aLoadAttributes);
}
NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType)
{
aContentType.AssignLiteral("image/icon");
return NS_OK;
}
NS_IMETHODIMP
nsIconChannel::SetContentType(const nsACString &aContentType)
{
//It doesn't make sense to set the content-type on this type
// of channel...
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset)
{
aContentCharset.AssignLiteral("image/icon");
return NS_OK;
}
NS_IMETHODIMP
nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
{
//It doesn't make sense to set the content-type on this type
// of channel...
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsIconChannel::GetContentLength(PRInt32 *aContentLength)
{
*aContentLength = mContentLength;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::SetContentLength(PRInt32 aContentLength)
{
NS_NOTREACHED("nsIconChannel::SetContentLength");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
NS_IF_ADDREF(*aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
mLoadGroup = aLoadGroup;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
*aNotificationCallbacks = mCallbacks.get();
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
mCallbacks = aNotificationCallbacks;
return NS_OK;
}
NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
{
*aSecurityInfo = nsnull;
return NS_OK;
}

View File

@ -49,10 +49,6 @@
#include <Carbon/Carbon.h>
extern "C" {
int NSApplicationLoad();
}
enum {
kEventClassMoz = 'MOZZ',
kEventMozNull = 0,
@ -88,11 +84,6 @@ nsAppShell::~nsAppShell()
nsresult
nsAppShell::Init()
{
// We call NSApplicationLoad() to initialize Cocoa. If we don't initialize Cocoa
// before we call into it (either through some mozilla component or a plugin)
// then we get bad app behavior like in bug 337334.
NSApplicationLoad();
// The message pump is only used for its EventRecord dispatcher. It is
// used by the transitional WaitNextEvent handler.