Bug 1663784 - Add Windows platform support for IconLoader. r=mhowell

Depends on D89972

Differential Revision: https://phabricator.services.mozilla.com/D90007
This commit is contained in:
Mike Conley 2020-09-30 15:15:01 +00:00
Родитель 5a0c26e13e
Коммит 455e99b3e0
5 изменённых файлов: 219 добавлений и 4 удалений

Просмотреть файл

@ -27,11 +27,16 @@ IconLoader::IconLoader(Helper* aHelper, nsINode* aContent,
mLoadedIcon(false),
mHelper(aHelper) {}
IconLoader::~IconLoader() {
IconLoader::~IconLoader() { Destroy(); }
void IconLoader::Destroy() {
if (mIconRequest) {
mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
mIconRequest = nullptr;
}
if (mHelper) {
mHelper = nullptr;
}
}
nsresult IconLoader::LoadIcon(nsIURI* aIconURI, bool aIsInternalIcon) {

Просмотреть файл

@ -0,0 +1,70 @@
/* -*- 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 Windows.
*/
#include "gfxPlatform.h"
#include "imgIContainer.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
#include "mozilla/dom/Document.h"
#include "nsContentUtils.h"
#include "nsIContent.h"
#include "nsNameSpaceManager.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsToolkit.h"
#include "nsWindowGfx.h"
#include "IconLoaderHelperWin.h"
using namespace mozilla;
using mozilla::gfx::SourceSurface;
using mozilla::widget::IconLoader;
using mozilla::widget::IconLoaderListenerWin;
namespace mozilla::widget {
IconLoaderHelperWin::IconLoaderHelperWin(IconLoaderListenerWin* aListener)
: mLoadListener(aListener) {
MOZ_ASSERT(aListener);
}
IconLoaderHelperWin::~IconLoaderHelperWin() { Destroy(); }
nsresult IconLoaderHelperWin::OnComplete(imgIContainer* aImage,
const nsIntRect& aRect) {
NS_ENSURE_ARG_POINTER(aImage);
nsresult rv = nsWindowGfx::CreateIcon(
aImage, false, LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon),
&mNativeIconImage);
NS_ENSURE_SUCCESS(rv, rv);
mLoadListener->OnComplete();
return NS_OK;
}
HICON IconLoaderHelperWin::GetNativeIconImage() {
if (mNativeIconImage) {
return mNativeIconImage;
}
return ::LoadIcon(::GetModuleHandle(NULL), IDI_APPLICATION);
}
void IconLoaderHelperWin::Destroy() {
if (mNativeIconImage) {
::DestroyIcon(mNativeIconImage);
mNativeIconImage = nullptr;
}
if (mLoadListener) {
mLoadListener = nullptr;
}
}
} // namespace mozilla::widget

Просмотреть файл

@ -0,0 +1,63 @@
/* -*- 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/. */
#ifndef mozilla_widget_IconLoaderHelperWin_h
#define mozilla_widget_IconLoaderHelperWin_h
#include "mozilla/widget/IconLoader.h"
namespace mozilla::widget {
/**
* Classes that want to hear about when icons load should subclass
* IconLoaderListenerWin, and implement the OnComplete() method,
* which will be called once the load of the icon has completed.
*/
class IconLoaderListenerWin {
public:
IconLoaderListenerWin() = default;
NS_INLINE_DECL_REFCOUNTING(mozilla::widget::IconLoaderListenerWin)
virtual nsresult OnComplete() = 0;
protected:
virtual ~IconLoaderListenerWin() = default;
};
/**
* This is a Helper used with mozilla::widget::IconLoader that implements the
* Windows-specific functionality for converting a loaded icon into an HICON.
*/
class IconLoaderHelperWin final : public mozilla::widget::IconLoader::Helper {
public:
explicit IconLoaderHelperWin(
mozilla::widget::IconLoaderListenerWin* aLoadListener);
nsresult OnComplete(imgIContainer* aImage, const nsIntRect& aRect) override;
/**
* IconLoaderHelperWin will default the HICON returned by GetNativeIconImage
* to the application icon. Once the load of the icon by IconLoader has
* completed, GetNativeIconImage will return the loaded icon.
*
* Note that IconLoaderHelperWin owns this HICON. If you don't need it to hold
* onto the HICON anymore, call Destroy on it to deallocate. The
* IconLoaderHelperWin destructor will also deallocate the HICON if necessary.
*/
HICON GetNativeIconImage();
void Destroy();
protected:
~IconLoaderHelperWin();
private:
RefPtr<mozilla::widget::IconLoaderListenerWin> mLoadListener;
HICON mNativeIconImage;
};
} // namespace mozilla::widget
#endif // mozilla_widget_IconLoaderHelperWin_h

Просмотреть файл

@ -11,26 +11,32 @@
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "nsComputedDOMStyle.h"
#include "nsIContentPolicy.h"
#include "nsMenuFrame.h"
#include "nsMenuPopupFrame.h"
#include "nsXULPopupManager.h"
#include "IconLoaderHelperWin.h"
namespace mozilla::widget {
using mozilla::LinkedListElement;
using mozilla::dom::Element;
class StatusBarEntry final : public LinkedListElement<RefPtr<StatusBarEntry>> {
class StatusBarEntry final : public LinkedListElement<RefPtr<StatusBarEntry>>,
public mozilla::widget::IconLoaderListenerWin {
public:
explicit StatusBarEntry(Element* aMenu);
nsresult Init();
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
const Element* GetMenu() { return mMenu; };
NS_INLINE_DECL_REFCOUNTING(StatusBarEntry)
nsresult OnComplete();
private:
~StatusBarEntry();
RefPtr<mozilla::widget::IconLoader> mIconLoader;
RefPtr<mozilla::widget::IconLoaderHelperWin> mIconLoaderHelper;
RefPtr<Element> mMenu;
NOTIFYICONDATAW mIconData;
boolean mInitted;
@ -50,6 +56,7 @@ StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) {
/* uVersion */ {NOTIFYICON_VERSION},
/* szInfoTitle */ L"",
/* dwInfoFlags */ 0};
MOZ_ASSERT(mMenu);
}
StatusBarEntry::~StatusBarEntry() {
@ -61,6 +68,57 @@ StatusBarEntry::~StatusBarEntry() {
}
nsresult StatusBarEntry::Init() {
MOZ_ASSERT(NS_IsMainThread());
// First, look at the content node's "image" attribute.
nsAutoString imageURIString;
bool hasImageAttr =
mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::image, imageURIString);
nsresult rv;
RefPtr<ComputedStyle> sc;
nsCOMPtr<nsIURI> iconURI;
if (!hasImageAttr) {
// If the content node has no "image" attribute, get the
// "list-style-image" property from CSS.
RefPtr<mozilla::dom::Document> document = mMenu->GetComposedDoc();
if (!document) {
return NS_ERROR_FAILURE;
}
sc = nsComputedDOMStyle::GetComputedStyle(mMenu, nullptr);
if (!sc) {
return NS_ERROR_FAILURE;
}
iconURI = sc->StyleList()->GetListStyleImageURI();
} else {
uint64_t dummy = 0;
nsContentPolicyType policyType;
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mMenu->NodePrincipal();
nsContentUtils::GetContentPolicyTypeForUIImageLoading(
mMenu, getter_AddRefs(triggeringPrincipal), policyType, &dummy);
if (policyType != nsIContentPolicy::TYPE_INTERNAL_IMAGE) {
return NS_ERROR_ILLEGAL_VALUE;
}
// If this menu item shouldn't have an icon, the string will be empty,
// and NS_NewURI will fail.
rv = NS_NewURI(getter_AddRefs(iconURI), imageURIString);
if (NS_FAILED(rv)) return rv;
}
mIconLoaderHelper = new IconLoaderHelperWin(this);
nsIntRect rect;
mIconLoader = new IconLoader(mIconLoaderHelper, mMenu, rect);
if (!mIconLoader || !mIconLoaderHelper) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (iconURI) {
rv = mIconLoader->LoadIcon(iconURI);
}
HWND iconWindow;
NS_ENSURE_TRUE(iconWindow = ::CreateWindowExW(
/* extended style */ 0,
@ -76,7 +134,7 @@ nsresult StatusBarEntry::Init() {
::SetWindowLongPtr(iconWindow, GWLP_USERDATA, (LONG_PTR)this);
mIconData.hWnd = iconWindow;
mIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), IDI_APPLICATION);
mIconData.hIcon = mIconLoaderHelper->GetNativeIconImage();
nsAutoString labelAttr;
mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::label, labelAttr);
@ -93,6 +151,23 @@ nsresult StatusBarEntry::Init() {
return NS_OK;
}
nsresult StatusBarEntry::OnComplete() {
RefPtr<StatusBarEntry> kungFuDeathGrip = this;
mIconData.hIcon = mIconLoaderHelper->GetNativeIconImage();
::Shell_NotifyIconW(NIM_MODIFY, &mIconData);
// To simplify things, we won't react to CSS changes to update the icon
// with this implementation. We can get rid of the IconLoader and Helper
// at this point, which will also free the allocated HICON.
mIconLoaderHelper->Destroy();
mIconLoader->ReleaseJSObjects();
mIconLoader->Destroy();
mIconLoader = nullptr;
mIconLoaderHelper = nullptr;
return NS_OK;
}
LRESULT StatusBarEntry::OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
if (msg == WM_USER && (lp == WM_LBUTTONUP || lp == WM_RBUTTONUP)) {
nsMenuFrame* menu = do_QueryFrame(mMenu->GetPrimaryFrame());

Просмотреть файл

@ -48,6 +48,7 @@ UNIFIED_SOURCES += [
'AudioSession.cpp',
'CompositorWidgetChild.cpp',
'GfxInfo.cpp',
'IconLoaderHelperWin.cpp',
'IEnumFE.cpp',
'IMMHandler.cpp',
'InkCollector.cpp',
@ -155,6 +156,7 @@ if CONFIG['MOZ_ENABLE_SKIA_PDF']:
LOCAL_INCLUDES += [
'/layout/forms',
'/layout/generic',
'/layout/style',
'/layout/xul',
'/toolkit/xre',
'/widget',