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/. */
5
6/*
7 * Retrieves and displays icons on the macOS Touch Bar.
8 */
9
10#include "nsTouchBarInputIcon.h"
11
12#include "MOZIconHelper.h"
13#include "mozilla/dom/Document.h"
14#include "nsCocoaUtils.h"
15#include "nsComputedDOMStyle.h"
16#include "nsContentUtils.h"
17#include "nsGkAtoms.h"
18#include "nsINode.h"
19#include "nsNameSpaceManager.h"
20#include "nsObjCExceptions.h"
21
22using namespace mozilla;
23using mozilla::widget::IconLoader;
24
25static const uint32_t kIconHeight = 16;
26static const CGFloat kHiDPIScalingFactor = 2.0f;
27
28nsTouchBarInputIcon::nsTouchBarInputIcon(RefPtr<Document> aDocument, TouchBarInput* aInput,
29                                         NSTouchBarItem* aItem)
30    : mDocument(aDocument), mSetIcon(false), mButton(nil), mShareScrubber(nil), mPopoverItem(nil) {
31  if ([[aInput nativeIdentifier] isEqualToString:[TouchBarInput shareScrubberIdentifier]]) {
32    mShareScrubber = (NSSharingServicePickerTouchBarItem*)aItem;
33  } else if ([aInput baseType] == TouchBarInputBaseType::kPopover) {
34    mPopoverItem = (NSPopoverTouchBarItem*)aItem;
35  } else if ([aInput baseType] == TouchBarInputBaseType::kButton ||
36             [aInput baseType] == TouchBarInputBaseType::kMainButton) {
37    mButton = (NSButton*)[aItem view];
38  } else {
39    NS_ERROR("Incompatible Touch Bar input passed to nsTouchBarInputIcon.");
40  }
41  aInput = nil;
42  MOZ_COUNT_CTOR(nsTouchBarInputIcon);
43}
44
45nsTouchBarInputIcon::~nsTouchBarInputIcon() {
46  Destroy();
47  MOZ_COUNT_DTOR(nsTouchBarInputIcon);
48}
49
50// Called from nsTouchBar's destructor, to prevent us from outliving it
51// (as might otherwise happen if calls to our imgINotificationObserver methods
52// are still outstanding).  nsTouchBar owns our mTouchBarInput.
53void nsTouchBarInputIcon::Destroy() {
54  ReleaseJSObjects();
55  if (mIconLoader) {
56    mIconLoader->Destroy();
57    mIconLoader = nullptr;
58  }
59
60  mButton = nil;
61  mShareScrubber = nil;
62  mPopoverItem = nil;
63}
64
65nsresult nsTouchBarInputIcon::SetupIcon(nsCOMPtr<nsIURI> aIconURI) {
66  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
67
68  // We might not have a document if the Touch Bar tries to update when the main
69  // window is closed.
70  if (!mDocument) {
71    return NS_OK;
72  }
73
74  if (!(mButton || mShareScrubber || mPopoverItem)) {
75    NS_ERROR("No Touch Bar input provided.");
76    return NS_ERROR_FAILURE;
77  }
78
79  if (!mIconLoader) {
80    mIconLoader = new IconLoader(this);
81  }
82
83  if (!mSetIcon) {
84    // Load placeholder icon.
85    NSSize iconSize = NSMakeSize(kIconHeight, kIconHeight);
86    NSImage* placeholder = [MOZIconHelper placeholderIconWithSize:iconSize];
87    [mButton setImage:placeholder];
88    [mShareScrubber setButtonImage:placeholder];
89    [mPopoverItem setCollapsedRepresentationImage:placeholder];
90  }
91
92  nsresult rv = mIconLoader->LoadIcon(aIconURI, mDocument, true /* aIsInternalIcon */);
93  if (NS_FAILED(rv)) {
94    // There is no icon for this menu item, as an error occurred while loading it.
95    // An icon might have been set earlier or the place holder icon may have
96    // been set.  Clear it.
97    [mButton setImage:nil];
98    [mShareScrubber setButtonImage:nil];
99    [mPopoverItem setCollapsedRepresentationImage:nil];
100  }
101
102  mSetIcon = true;
103
104  return rv;
105
106  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
107}
108
109void nsTouchBarInputIcon::ReleaseJSObjects() { mDocument = nil; }
110
111//
112// mozilla::widget::IconLoader::Listener
113//
114
115nsresult nsTouchBarInputIcon::OnComplete(imgIContainer* aImage) {
116  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
117
118  // We ask only for the HiDPI images since all Touch Bars are Retina
119  // displays and we have no need for icons @1x.
120  NSImage* image = [MOZIconHelper iconImageFromImageContainer:aImage
121                                                     withSize:NSMakeSize(kIconHeight, kIconHeight)
122                                                computedStyle:nullptr
123                                                      subrect:mImageRegionRect
124                                                  scaleFactor:kHiDPIScalingFactor];
125  [mButton setImage:image];
126  [mShareScrubber setButtonImage:image];
127  [mPopoverItem setCollapsedRepresentationImage:image];
128
129  mIconLoader->Destroy();
130  return NS_OK;
131
132  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
133}
134