Bug 1909163 - make select dropdowns more properly tabspecific, r=emilio
[gecko.git] / widget / cocoa / nsMenuItemIconX.mm
blob6749d787de09b11abf5802ac1f30536776a16791
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7  * Retrieves and displays icons in native menu items on Mac OS X.
8  */
10 /* exception_defines.h defines 'try' to 'if (true)' which breaks objective-c
11    exceptions and produces errors like: error: unexpected '@' in program'.
12    If we define __EXCEPTIONS exception_defines.h will avoid doing this.
14    See bug 666609 for more information.
16    We use <limits> to get the libstdc++ version. */
17 #include <limits>
18 #if __GLIBCXX__ <= 20070719
19 #  ifndef __EXCEPTIONS
20 #    define __EXCEPTIONS
21 #  endif
22 #endif
24 #include "MOZIconHelper.h"
25 #include "mozilla/dom/Document.h"
26 #include "mozilla/dom/DocumentInlines.h"
27 #include "nsCocoaUtils.h"
28 #include "nsComputedDOMStyle.h"
29 #include "nsContentUtils.h"
30 #include "nsGkAtoms.h"
31 #include "nsIContent.h"
32 #include "nsIContentPolicy.h"
33 #include "nsMenuItemX.h"
34 #include "nsMenuItemIconX.h"
35 #include "nsNameSpaceManager.h"
36 #include "nsObjCExceptions.h"
38 using namespace mozilla;
40 using mozilla::dom::Element;
41 using mozilla::widget::IconLoader;
43 static const uint32_t kIconSize = 16;
45 nsMenuItemIconX::nsMenuItemIconX(Listener* aListener) : mListener(aListener) {
46   MOZ_COUNT_CTOR(nsMenuItemIconX);
49 nsMenuItemIconX::~nsMenuItemIconX() {
50   if (mIconLoader) {
51     mIconLoader->Destroy();
52   }
53   if (mIconImage) {
54     [mIconImage release];
55     mIconImage = nil;
56   }
57   MOZ_COUNT_DTOR(nsMenuItemIconX);
60 void nsMenuItemIconX::SetupIcon(nsIContent* aContent) {
61   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
63   bool shouldHaveIcon = StartIconLoad(aContent);
64   if (!shouldHaveIcon) {
65     // There is no icon for this menu item, as an error occurred while loading
66     // it. An icon might have been set earlier or the place holder icon may have
67     // been set.  Clear it.
68     if (mIconImage) {
69       [mIconImage release];
70       mIconImage = nil;
71     }
72     return;
73   }
75   if (!mIconImage) {
76     // Set a placeholder icon, so that the menuitem reserves space for the icon
77     // during the load and there is no sudden shift once the icon finishes
78     // loading.
79     NSSize iconSize = NSMakeSize(kIconSize, kIconSize);
80     mIconImage = [[MOZIconHelper placeholderIconWithSize:iconSize] retain];
81   }
83   NS_OBJC_END_TRY_ABORT_BLOCK;
86 bool nsMenuItemIconX::StartIconLoad(nsIContent* aContent) {
87   RefPtr<nsIURI> iconURI = GetIconURI(aContent);
88   if (!iconURI) {
89     return false;
90   }
92   if (!mIconLoader) {
93     mIconLoader = new IconLoader(this);
94   }
96   nsresult rv = mIconLoader->LoadIcon(iconURI, aContent);
97   return NS_SUCCEEDED(rv);
100 already_AddRefed<nsIURI> nsMenuItemIconX::GetIconURI(nsIContent* aContent) {
101   // First, look at the content node's "image" attribute.
102   nsAutoString imageURIString;
103   bool hasImageAttr =
104       aContent->IsElement() &&
105       aContent->AsElement()->GetAttr(nsGkAtoms::image, imageURIString);
107   if (hasImageAttr) {
108     // Use the URL from the image attribute.
109     // If this menu item shouldn't have an icon, the string will be empty,
110     // and NS_NewURI will fail.
111     RefPtr<nsIURI> iconURI;
112     nsresult rv = NS_NewURI(getter_AddRefs(iconURI), imageURIString);
113     if (NS_FAILED(rv)) {
114       return nullptr;
115     }
116     return iconURI.forget();
117   }
119   // If the content node has no "image" attribute, get the
120   // "list-style-image" property from CSS.
121   RefPtr<mozilla::dom::Document> document = aContent->GetComposedDoc();
122   if (!document || !aContent->IsElement()) {
123     return nullptr;
124   }
126   RefPtr<const ComputedStyle> sc =
127       nsComputedDOMStyle::GetComputedStyle(aContent->AsElement());
128   if (!sc) {
129     return nullptr;
130   }
132   RefPtr<nsIURI> iconURI = sc->StyleList()->GetListStyleImageURI();
133   if (!iconURI) {
134     return nullptr;
135   }
137   mComputedStyle = std::move(sc);
138   mPresContext = document->GetPresContext();
140   return iconURI.forget();
144 // mozilla::widget::IconLoader::Listener
147 nsresult nsMenuItemIconX::OnComplete(imgIContainer* aImage) {
148   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
150   if (mIconImage) {
151     [mIconImage release];
152     mIconImage = nil;
153   }
154   RefPtr<nsPresContext> pc = mPresContext.get();
155   mIconImage = [[MOZIconHelper
156       iconImageFromImageContainer:aImage
157                          withSize:NSMakeSize(kIconSize, kIconSize)
158                       presContext:pc
159                     computedStyle:mComputedStyle
160                       scaleFactor:0.0f] retain];
161   mComputedStyle = nullptr;
162   mPresContext = nullptr;
164   if (mListener) {
165     mListener->IconUpdated();
166   }
168   mIconLoader->Destroy();
170   return NS_OK;
172   NS_OBJC_END_TRY_ABORT_BLOCK;