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