summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/IconLoaderHelperCocoa.mm
blob: 0d379b8e9f0ca6f9deac2b95e7d6e6a19e2faad7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* -*- 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 "gfxPlatform.h"
#include "imgIContainer.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
#include "mozilla/dom/Document.h"
#include "nsCocoaUtils.h"
#include "nsContentUtils.h"
#include "nsIContent.h"
#include "nsNameSpaceManager.h"
#include "nsNetUtil.h"
#include "nsObjCExceptions.h"
#include "nsThreadUtils.h"
#include "nsToolkit.h"
#include "IconLoaderHelperCocoa.h"

using namespace mozilla;

using mozilla::gfx::SourceSurface;
using mozilla::widget::IconLoaderListenerCocoa;

namespace mozilla::widget {

NS_IMPL_ISUPPORTS0(IconLoaderHelperCocoa)

IconLoaderHelperCocoa::IconLoaderHelperCocoa(IconLoaderListenerCocoa* aListener,
                                             uint32_t aIconHeight, uint32_t aIconWidth,
                                             CGFloat aScaleFactor)
    : mLoadListener(aListener),
      mIconHeight(aIconHeight),
      mIconWidth(aIconWidth),
      mScaleFactor(aScaleFactor) {
  // Placeholder icon, which will later be replaced.
  mNativeIconImage = [[NSImage alloc] initWithSize:NSMakeSize(mIconHeight, mIconWidth)];
  MOZ_ASSERT(aListener);
}

IconLoaderHelperCocoa::~IconLoaderHelperCocoa() { Destroy(); }

nsresult IconLoaderHelperCocoa::OnComplete(imgIContainer* aImage, const nsIntRect& aRect) {
  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT

  NS_ENSURE_ARG_POINTER(aImage);

  bool isEntirelyBlack = false;
  NSImage* newImage = nil;
  nsresult rv;
  if (mScaleFactor != 0.0f) {
    rv = nsCocoaUtils::CreateNSImageFromImageContainer(aImage, imgIContainer::FRAME_CURRENT,
                                                       &newImage, mScaleFactor, &isEntirelyBlack);
  } else {
    rv = nsCocoaUtils::CreateDualRepresentationNSImageFromImageContainer(
        aImage, imgIContainer::FRAME_CURRENT, &newImage, &isEntirelyBlack);
  }

  if (NS_FAILED(rv) || !newImage) {
    mNativeIconImage = nil;
    [newImage release];
    return NS_ERROR_FAILURE;
  }

  NSSize requestedSize = NSMakeSize(mIconWidth, mIconHeight);

  int32_t origWidth = 0, origHeight = 0;
  aImage->GetWidth(&origWidth);
  aImage->GetHeight(&origHeight);

  bool createSubImage =
      !(aRect.x == 0 && aRect.y == 0 && aRect.width == origWidth && aRect.height == origHeight);

  if (createSubImage) {
    // If aRect is set using CSS, we need to slice a piece out of the
    // overall image to use as the icon.
    NSImage* subImage =
        [NSImage imageWithSize:requestedSize
                       flipped:NO
                drawingHandler:^BOOL(NSRect subImageRect) {
                  [newImage drawInRect:NSMakeRect(0, 0, mIconWidth, mIconHeight)
                              fromRect:NSMakeRect(aRect.x, aRect.y, aRect.width, aRect.height)
                             operation:NSCompositingOperationCopy
                              fraction:1.0f];
                  return YES;
                }];
    [newImage release];
    newImage = [subImage retain];
    subImage = nil;
  }

  // If all the color channels in the image are black, treat the image as a
  // template. This will cause macOS to use the image's alpha channel as a mask
  // and it will fill it with a color that looks good in the context that it's
  // used in. For example, for regular menu items, the image will be black, but
  // when the menu item is hovered (and its background is blue), it will be
  // filled with white.
  [newImage setTemplate:isEntirelyBlack];

  [newImage setSize:requestedSize];

  NSImage* placeholderImage = mNativeIconImage;
  mNativeIconImage = newImage;
  [placeholderImage release];
  placeholderImage = nil;

  mLoadListener->OnComplete();

  return NS_OK;
  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT
}

NSImage* IconLoaderHelperCocoa::GetNativeIconImage() { return mNativeIconImage; }

void IconLoaderHelperCocoa::Destroy() {
  if (mNativeIconImage) {
    [mNativeIconImage release];
    mNativeIconImage = nil;
  }
}

}  // namespace mozilla::widget