diff options
Diffstat (limited to 'widget/cocoa/nsMenuItemIconX.mm')
-rw-r--r-- | widget/cocoa/nsMenuItemIconX.mm | 466 |
1 files changed, 0 insertions, 466 deletions
diff --git a/widget/cocoa/nsMenuItemIconX.mm b/widget/cocoa/nsMenuItemIconX.mm deleted file mode 100644 index 7589c279e5..0000000000 --- a/widget/cocoa/nsMenuItemIconX.mm +++ /dev/null @@ -1,466 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * Retrieves and displays icons in native menu items on Mac OS X. - */ - -/* exception_defines.h defines 'try' to 'if (true)' which breaks objective-c - exceptions and produces errors like: error: unexpected '@' in program'. - If we define __EXCEPTIONS exception_defines.h will avoid doing this. - - See bug 666609 for more information. - - We use <limits> to get the libstdc++ version. */ -#include <limits> -#if __GLIBCXX__ <= 20070719 -#ifndef __EXCEPTIONS -#define __EXCEPTIONS -#endif -#endif - -#include "nsMenuItemIconX.h" -#include "nsObjCExceptions.h" -#include "nsIContent.h" -#include "nsIDocument.h" -#include "nsNameSpaceManager.h" -#include "nsGkAtoms.h" -#include "nsIDOMElement.h" -#include "nsICSSDeclaration.h" -#include "nsIDOMCSSValue.h" -#include "nsIDOMCSSPrimitiveValue.h" -#include "nsIDOMRect.h" -#include "nsThreadUtils.h" -#include "nsToolkit.h" -#include "nsNetUtil.h" -#include "imgLoader.h" -#include "imgRequestProxy.h" -#include "nsMenuItemX.h" -#include "gfxPlatform.h" -#include "imgIContainer.h" -#include "nsCocoaUtils.h" -#include "nsContentUtils.h" -#include "nsIContentPolicy.h" - -using mozilla::dom::Element; -using mozilla::gfx::SourceSurface; - -static const uint32_t kIconWidth = 16; -static const uint32_t kIconHeight = 16; - -typedef NS_STDCALL_FUNCPROTO(nsresult, GetRectSideMethod, nsIDOMRect, - GetBottom, (nsIDOMCSSPrimitiveValue**)); - -NS_IMPL_ISUPPORTS(nsMenuItemIconX, imgINotificationObserver) - -nsMenuItemIconX::nsMenuItemIconX(nsMenuObjectX* aMenuItem, - nsIContent* aContent, - NSMenuItem* aNativeMenuItem) -: mContent(aContent) -, mMenuObject(aMenuItem) -, mLoadedIcon(false) -, mSetIcon(false) -, mNativeMenuItem(aNativeMenuItem) -{ - // printf("Creating icon for menu item %d, menu %d, native item is %d\n", aMenuItem, aMenu, aNativeMenuItem); -} - -nsMenuItemIconX::~nsMenuItemIconX() -{ - if (mIconRequest) - mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); -} - -// Called from mMenuObjectX's destructor, to prevent us from outliving it -// (as might otherwise happen if calls to our imgINotificationObserver methods -// are still outstanding). mMenuObjectX owns our nNativeMenuItem. -void nsMenuItemIconX::Destroy() -{ - if (mIconRequest) { - mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - mMenuObject = nullptr; - mNativeMenuItem = nil; -} - -nsresult -nsMenuItemIconX::SetupIcon() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Still don't have one, then something is wrong, get out of here. - if (!mNativeMenuItem) { - NS_ERROR("No native menu item"); - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIURI> iconURI; - nsresult rv = GetIconURI(getter_AddRefs(iconURI)); - if (NS_FAILED(rv)) { - // There is no icon for this menu item. An icon might have been set - // earlier. Clear it. - [mNativeMenuItem setImage:nil]; - - return NS_OK; - } - - rv = LoadIcon(iconURI); - if (NS_FAILED(rv)) { - // There is no icon for this menu item, as an error occurred while loading it. - // An icon might have been set earlier or the place holder icon may have - // been set. Clear it. - [mNativeMenuItem setImage:nil]; - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -static int32_t -GetDOMRectSide(nsIDOMRect* aRect, GetRectSideMethod aMethod) -{ - nsCOMPtr<nsIDOMCSSPrimitiveValue> dimensionValue; - (aRect->*aMethod)(getter_AddRefs(dimensionValue)); - if (!dimensionValue) - return -1; - - uint16_t primitiveType; - nsresult rv = dimensionValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv) || primitiveType != nsIDOMCSSPrimitiveValue::CSS_PX) - return -1; - - float dimension = 0; - rv = dimensionValue->GetFloatValue(nsIDOMCSSPrimitiveValue::CSS_PX, - &dimension); - if (NS_FAILED(rv)) - return -1; - - return NSToIntRound(dimension); -} - -nsresult -nsMenuItemIconX::GetIconURI(nsIURI** aIconURI) -{ - if (!mMenuObject) - return NS_ERROR_FAILURE; - - // Mac native menu items support having both a checkmark and an icon - // simultaneously, but this is unheard of in the cross-platform toolkit, - // seemingly because the win32 theme is unable to cope with both at once. - // The downside is that it's possible to get a menu item marked with a - // native checkmark and a checkmark for an icon. Head off that possibility - // by pretending that no icon exists if this is a checkable menu item. - if (mMenuObject->MenuObjectType() == eMenuItemObjectType) { - nsMenuItemX* menuItem = static_cast<nsMenuItemX*>(mMenuObject); - if (menuItem->GetMenuItemType() != eRegularMenuItemType) - return NS_ERROR_FAILURE; - } - - if (!mContent) - return NS_ERROR_FAILURE; - - // First, look at the content node's "image" attribute. - nsAutoString imageURIString; - bool hasImageAttr = mContent->GetAttr(kNameSpaceID_None, - nsGkAtoms::image, - imageURIString); - - nsresult rv; - nsCOMPtr<nsIDOMCSSValue> cssValue; - nsCOMPtr<nsICSSDeclaration> cssStyleDecl; - nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue; - uint16_t primitiveType; - if (!hasImageAttr) { - // If the content node has no "image" attribute, get the - // "list-style-image" property from CSS. - nsCOMPtr<nsIDocument> document = mContent->GetComposedDoc(); - if (!document) - return NS_ERROR_FAILURE; - - nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow(); - if (!window) - return NS_ERROR_FAILURE; - - nsCOMPtr<Element> domElement = do_QueryInterface(mContent); - if (!domElement) - return NS_ERROR_FAILURE; - - ErrorResult dummy; - cssStyleDecl = window->GetComputedStyle(*domElement, EmptyString(), dummy); - dummy.SuppressException(); - if (!cssStyleDecl) - return NS_ERROR_FAILURE; - - NS_NAMED_LITERAL_STRING(listStyleImage, "list-style-image"); - rv = cssStyleDecl->GetPropertyCSSValue(listStyleImage, - getter_AddRefs(cssValue)); - if (NS_FAILED(rv)) return rv; - - primitiveValue = do_QueryInterface(cssValue); - if (!primitiveValue) return NS_ERROR_FAILURE; - - rv = primitiveValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv)) return rv; - if (primitiveType != nsIDOMCSSPrimitiveValue::CSS_URI) - return NS_ERROR_FAILURE; - - rv = primitiveValue->GetStringValue(imageURIString); - if (NS_FAILED(rv)) return rv; - } - - // Empty the mImageRegionRect initially as the image region CSS could - // have been changed and now have an error or have been removed since the - // last GetIconURI call. - mImageRegionRect.SetEmpty(); - - // If this menu item shouldn't have an icon, the string will be empty, - // and NS_NewURI will fail. - nsCOMPtr<nsIURI> iconURI; - rv = NS_NewURI(getter_AddRefs(iconURI), imageURIString); - if (NS_FAILED(rv)) return rv; - - *aIconURI = iconURI; - NS_ADDREF(*aIconURI); - - if (!hasImageAttr) { - // Check if the icon has a specified image region so that it can be - // cropped appropriately before being displayed. - NS_NAMED_LITERAL_STRING(imageRegion, "-moz-image-region"); - rv = cssStyleDecl->GetPropertyCSSValue(imageRegion, - getter_AddRefs(cssValue)); - // Just return NS_OK if there if there is a failure due to no - // moz-image region specified so the whole icon will be drawn anyway. - if (NS_FAILED(rv)) return NS_OK; - - primitiveValue = do_QueryInterface(cssValue); - if (!primitiveValue) return NS_OK; - - rv = primitiveValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv)) return NS_OK; - if (primitiveType != nsIDOMCSSPrimitiveValue::CSS_RECT) - return NS_OK; - - nsCOMPtr<nsIDOMRect> imageRegionRect; - rv = primitiveValue->GetRectValue(getter_AddRefs(imageRegionRect)); - if (NS_FAILED(rv)) return NS_OK; - - if (imageRegionRect) { - // Return NS_ERROR_FAILURE if the image region is invalid so the image - // is not drawn, and behavior is similar to XUL menus. - int32_t bottom = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetBottom); - int32_t right = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetRight); - int32_t top = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetTop); - int32_t left = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetLeft); - - if (top < 0 || left < 0 || bottom <= top || right <= left) - return NS_ERROR_FAILURE; - - mImageRegionRect.SetRect(left, top, right - left, bottom - top); - } - } - - return NS_OK; -} - -nsresult -nsMenuItemIconX::LoadIcon(nsIURI* aIconURI) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mIconRequest) { - // Another icon request is already in flight. Kill it. - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - - mLoadedIcon = false; - - if (!mContent) return NS_ERROR_FAILURE; - - nsCOMPtr<nsIDocument> document = mContent->OwnerDoc(); - - nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup(); - if (!loadGroup) return NS_ERROR_FAILURE; - - RefPtr<imgLoader> loader = nsContentUtils::GetImgLoaderForDocument(document); - if (!loader) return NS_ERROR_FAILURE; - - if (!mSetIcon) { - // Set a completely transparent 16x16 image as the icon on this menu item - // as a placeholder. This keeps the menu item text displayed in the same - // position that it will be displayed when the real icon is loaded, and - // prevents it from jumping around or looking misaligned. - - static bool sInitializedPlaceholder; - static NSImage* sPlaceholderIconImage; - if (!sInitializedPlaceholder) { - sInitializedPlaceholder = true; - - // Note that we only create the one and reuse it forever, so this is not a leak. - sPlaceholderIconImage = [[NSImage alloc] initWithSize:NSMakeSize(kIconWidth, kIconHeight)]; - } - - if (!sPlaceholderIconImage) return NS_ERROR_FAILURE; - - if (mNativeMenuItem) - [mNativeMenuItem setImage:sPlaceholderIconImage]; - } - - nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr, - mozilla::net::RP_Default, - nullptr, loadGroup, this, - nullptr, nullptr, nsIRequest::LOAD_NORMAL, nullptr, - nsIContentPolicy::TYPE_INTERNAL_IMAGE, EmptyString(), - getter_AddRefs(mIconRequest)); - if (NS_FAILED(rv)) return rv; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// -// imgINotificationObserver -// - -NS_IMETHODIMP -nsMenuItemIconX::Notify(imgIRequest* aRequest, - int32_t aType, - const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - // Make sure the image loaded successfully. - uint32_t status = imgIRequest::STATUS_ERROR; - if (NS_FAILED(aRequest->GetImageStatus(&status)) || - (status & imgIRequest::STATUS_ERROR)) { - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - return NS_ERROR_FAILURE; - } - - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - MOZ_ASSERT(image); - - // Ask the image to decode at its intrinsic size. - int32_t width = 0, height = 0; - image->GetWidth(&width); - image->GetHeight(&height); - image->RequestDecodeForSize(nsIntSize(width, height), imgIContainer::FLAG_NONE); - } - - if (aType == imgINotificationObserver::FRAME_COMPLETE) { - return OnFrameComplete(aRequest); - } - - if (aType == imgINotificationObserver::DECODE_COMPLETE) { - if (mIconRequest && mIconRequest == aRequest) { - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - } - - return NS_OK; -} - -nsresult -nsMenuItemIconX::OnFrameComplete(imgIRequest* aRequest) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (aRequest != mIconRequest) - return NS_ERROR_FAILURE; - - // Only support one frame. - if (mLoadedIcon) - return NS_OK; - - if (!mNativeMenuItem) - return NS_ERROR_FAILURE; - - nsCOMPtr<imgIContainer> imageContainer; - aRequest->GetImage(getter_AddRefs(imageContainer)); - if (!imageContainer) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - int32_t origWidth = 0, origHeight = 0; - imageContainer->GetWidth(&origWidth); - imageContainer->GetHeight(&origHeight); - - // If the image region is invalid, don't draw the image to almost match - // the behavior of other platforms. - if (!mImageRegionRect.IsEmpty() && - (mImageRegionRect.XMost() > origWidth || - mImageRegionRect.YMost() > origHeight)) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - if (mImageRegionRect.IsEmpty()) { - mImageRegionRect.SetRect(0, 0, origWidth, origHeight); - } - - RefPtr<SourceSurface> surface = - imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - if (!surface) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - CGImageRef origImage = NULL; - nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &origImage); - if (NS_FAILED(rv) || !origImage) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - bool createSubImage = !(mImageRegionRect.x == 0 && mImageRegionRect.y == 0 && - mImageRegionRect.width == origWidth && mImageRegionRect.height == origHeight); - - CGImageRef finalImage = origImage; - if (createSubImage) { - // if mImageRegionRect is set using CSS, we need to slice a piece out of the overall - // image to use as the icon - finalImage = ::CGImageCreateWithImageInRect(origImage, - ::CGRectMake(mImageRegionRect.x, - mImageRegionRect.y, - mImageRegionRect.width, - mImageRegionRect.height)); - ::CGImageRelease(origImage); - if (!finalImage) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - } - - NSImage *newImage = nil; - rv = nsCocoaUtils::CreateNSImageFromCGImage(finalImage, &newImage); - if (NS_FAILED(rv) || !newImage) { - [mNativeMenuItem setImage:nil]; - ::CGImageRelease(finalImage); - return NS_ERROR_FAILURE; - } - - [newImage setSize:NSMakeSize(kIconWidth, kIconHeight)]; - [mNativeMenuItem setImage:newImage]; - - [newImage release]; - ::CGImageRelease(finalImage); - - mLoadedIcon = true; - mSetIcon = true; - - if (mMenuObject) { - mMenuObject->IconUpdated(); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} |